function loadChildBuildInfos(buildInfo)
% loadChildBuildInfos loads buildInfos for any shared libraries and 
% (optionally) and referenced model components

% Copyright 2019-2023 The MathWorks, Inc.

% TopTester: test/toolbox/coder/compile/compile/buildInfo/tLinkObjMethods.m
% TopTester: test/toolbox/coder/compile/compile/buildInfo/tHierarchyLinkInfo.m

arguments
    buildInfo RTW.BuildInfo
end

% For each of LinkablesDirect, ensure any referenced buildInfo.mat is loaded
for i=1:length(buildInfo.LinkablesDirect)
    loadLinkObjBuildInfo(buildInfo.LinkablesDirect(i));
end

% Create checksum data to allow early return if nothing changed
if isempty(buildInfo.LinkablesDirect)
    linkablesDirectChecksum = {};
else
    linkablesDirectChecksum = {{buildInfo.LinkablesDirect.Name} ...
   [buildInfo.LinkablesDirect.BuildInfoHandle]};
end

% If LinkablesDirect are unchanged since last call to loadChildBuildInfos,
% we can return early
if isequal(buildInfo.LinkablesDirectChecksum, linkablesDirectChecksum)
    return
end

% Do a depth-first traversal of the hierarchy to identify all linkables
linkables = i_getLinkables(buildInfo.LinkablesDirect);

% Store the checksum data
buildInfo.LinkablesDirectChecksum = linkablesDirectChecksum;
      
% Transform to column vector and populate the transient BuildInfo property
buildInfo.LinkablesInternal = linkables(:);



function dupeIdx = findDuplicate(childLinkable, linkables)
        
% Check if any of the new linkable objects have matching name
nameMatchIdx = strcmp(childLinkable.Name, {linkables.Name});

% Check if any of the new linkable objects have matching path (only in
% the case where the path points at a buildInfo.mat)
pathMatchIdx = strcmp(childLinkable.Path, {linkables.Path}) & ...
    ~cellfun(@isempty, {linkables.BuildInfoHandle}) & ...
    ~[linkables.NeverRebuild];

% Index of any matching linkable object
matchIdx = nameMatchIdx | pathMatchIdx;
if any(matchIdx)
    assert(sum(matchIdx)==1, 'Must be no more than one matching linkable')
    dupeIdx = find(matchIdx, 1);
else
    dupeIdx = [];
end

        

function linkables_out = i_getLinkables(linkables_in, linkables_existing)

arguments
    % Linkables over which to loop
    linkables_in

    % Tally of existing linkables for duplicate detection
    linkables_existing = RTW.BuildInfoLinkObj.empty;
end
linkables_out = RTW.BuildInfoLinkObj.empty;

for nodeIdx = numel(linkables_in):-1:1
    % Iterate in reverse order since we will be prepending the linkables to
    % the list but we want to preserve the order.
    currentLinkable = linkables_in(nodeIdx);
    
    % Is this a duplicate?
    candidateDups = [linkables_out, linkables_existing];
    dupIdx = findDuplicate(currentLinkable,candidateDups);
    if ~isempty(dupIdx)
        % For duplicates, copy the BuildInfoHandle and do not recurse.
        currentLinkable.BuildInfoHandle = candidateDups(dupIdx).BuildInfoHandle;
        currentLinkable.BuildOptsHandle = candidateDups(dupIdx).BuildOptsHandle;
        continue;
    end
    
    % Load any referenced buildInfo.mat before inserting into the
    % list
    if isempty(currentLinkable.BuildInfoHandle) || isempty(currentLinkable.BuildOptsHandle)
        loadLinkObjBuildInfo(currentLinkable);    
    end
    buildInfoHandle = currentLinkable.BuildInfoHandle;
    childLinkables = [buildInfoHandle.LinkablesDirect];

    % Prepend the current linkable, and any of its dependencies, to the
    % list of previously identified linkables.
    linkables_out = [currentLinkable, ... 
        i_getLinkables(childLinkables, [candidateDups currentLinkable]), ... 
        linkables_out]; %#ok<AGROW>
end


% LocalWords:  linkables
