function lMakeResult = codebuild(buildInfo_in, varargin)
% CODEBUILD compiles code specified by an RTW.BuildInfo object
% 
%   CODEBUILD(BUILDFOLDER) loads the file BUILDFOLDER/buildInfo.mat, which
%   contains a build information object that specifies source code. The
%   function compiles the source code by using build options that are also
%   specified in buildInfo.mat.
% 
%   CODEBUILD(BUILDFOLDER, name1, value1, ...) loads the file
%   BUILDFOLDER/buildInfo.mat. The function compiles the source code by
%   using build options specified by name-value pairs that override
%   corresponding options in buildInfo.mat.
% 
%   CODEBUILD(BUILDINFO, name1, value1, ...) compiles the code specified in
%   BUILDINFO, an RTW.BuildInfo object, using options specified by
%   the name-value pair arguments.
%
%   Allowed argument names:
%
%       BuildMethod - Specify a template makefile, registered Toolchain, or
%       literal string 'CMake'.
%
%       BuildVariant - Specify an enumeration for the final product type,
%       for example, STATIC_LIBRARY or STANDALONE_EXECUTABLE.
   
    
% Copyright 2012-2021 The MathWorks, Inc.

% Convert any string input to char
buildInfo = convertStringsToChars(buildInfo_in);

if ~ischar(buildInfo) && ~isa(buildInfo, 'RTW.BuildInfo')
    msg = message('coder_compile:CoderCompile:CodebuildInvalidBuildInfo');
    error(msg.Identifier, '%s', string(msg));    
end

if nargin > 1
    [varargin{:}] = convertStringsToChars(varargin{:});
end

% By default, child components inherit build options from those specified
% for the top component. In this default mode, options such as BuildMethod, 
% BuildConfiguration are always consistent across the hierarchy.
inheritBuildOptionsFromTop = true;

if length(varargin)==1 && isa(varargin{1}, 'coder.make.BuildOpts')
    % Build opts are supplied directly as an argument
    buildOptsTop = varargin{1};
else
    if ischar(buildInfo)
        % Check that the argument resolves to a file and load it
        [buildInfo, buildOptsTop] = i_loadBuildInfo(buildInfo);
        
        % Apply build options specified by the caller, if any
        specifiedBuildOptions = i_applyBuildOptsFromNameValues(buildOptsTop, varargin);
        
        % Identify build options specified by the caller that can override
        % build options in a child component
        overrideBuildOptions = ...
            setdiff(specifiedBuildOptions, coder.make.BuildOpts.NoOverrideOpts);
            
        if isempty(overrideBuildOptions)
            % If codebuild was invoked with the first argument specifying a
            % buildInfo.mat and if there are no build options that override
            % the corresponding child build option: each component in the
            % hierarchy will be built using options specific to that
            % component. The options for each component are obtained
            % directly its buildInfo.mat. In this mode, options such as
            % BuildMethod, BuildConfiguration may differ between components
            % and are not necessarily consistent across the hierarchy.
            inheritBuildOptionsFromTop = false;
        end
    else
        % Start with default build options
        buildOptsTop = coder.make.BuildOpts;
        
        % Apply build options specified by the caller
        if ~isempty(varargin)
            i_applyBuildOptsFromNameValues(buildOptsTop, varargin);
        end
    end
end

if ~isempty(buildInfo.DisallowedBuildVariants) && ...
        any(buildOptsTop.BuildVariant==buildInfo.DisallowedBuildVariants)
        msg = message('coder_compile:CoderCompile:DisallowedBuildVariant', ...
            string(buildOptsTop.BuildVariant), ...
            join(string(buildInfo.DisallowedBuildVariants), ', '));
        error(msg.Identifier, '%s', string(msg));
end
    
% If build method is empty, use default toolchain for the current platform
if isempty(buildOptsTop.BuildMethod)
    mexCompInfo = coder.make.internal.getMexCompilerInfo();
    if ~isempty(mexCompInfo)
        compilerString = mexCompInfo.compStr;
        buildOptsTop.BuildMethod = ...
            coder.make.internal.getToolchainNameFromRegistry(compilerString);
    end
end

if buildOptsTop.CopyBuildInfo
    % This transient property must be copied over
    lComponentBuildFolder = buildInfo.ComponentBuildFolder; 
    
    % Accept a small performance penalty by making a deep copy of buildInfo;
    % this ensures that the buildInfo supplied by the caller is not 
    % modified in any way
    buildInfo = coder.make.internal.cloneBuildInfo(buildInfo);
    buildInfo.ComponentBuildFolder = lComponentBuildFolder;
end

% Ensure that root folder, component build folder and relative path
% to anchor are all consistent
if isempty(buildInfo.Settings.LocalAnchorDir)
    setRootFolder(buildInfo, pwd);
end
coder.make.internal.syncRelativePathToAnchor(buildInfo)

% Load child components, including shared libraries and referenced models.
% This is an optimization so that we don't have to load and unload
% referenced buildInfo each time we call method such as getLinkFlags that
% must query them.
% TODO(g2446267) skip loading buildInfo hierarchy if building a static library.
loadChildBuildInfos(buildInfo);

% Check if the BuildMethod is CMake and issue a warning if it conflicts with 
% a registered toolchain
[isCMake, isCMakeInfo] = coder.make.internal.buildMethodIsCMake(buildOptsTop.BuildMethod);

% Convert the build method to a toolchain info or TMF properties object.
% This an expensive operation and performing it here for the top component
% avoids incurring the same expense for each child component.
[buildOptsTop.BuildMethod, toolchainInfo] = i_resolveBuildMethod...
    (isCMake, buildOptsTop.BuildMethod);

% If the toolchain supports building mex, make sure the mex options are set
buildOptsTop.MexCompInfo = i_resolveMexCompInfo...
    (isCMake, buildOptsTop.MexCompInfo, toolchainInfo);

% Set up compiler environment (for MSVC)
environmentCleanupFcn = i_setupEnvironment...
    (isCMake, buildOptsTop.MexCompInfo, buildOptsTop.BuildMethod);
    
% Map of link objects to be linked by the top component
% Skip linking them when generating CMakeLists.txt for child components  
topModelLinkObjs = buildInfo.Linkables;
if isempty(topModelLinkObjs)
    cmakeLinkObjsToSkip = containers.Map();
else
    cmakeLinkObjsToSkip = containers.Map({topModelLinkObjs.Name},...
        coder.make.internal.formatPathsForCMake({topModelLinkObjs.Path}));    
end
% Identify child components to build: components must be built
% in the same order that would apply if built directly within
% a code generation, model-reference-hierarchy context
% TODO(g2194341): test point to lock down correct order for building child components
[libsToBuild, buildTopComponent] = ...
    i_getLibsToBuild(buildInfo, buildOptsTop.ComponentsToBuild);

lRootFolder = buildInfo.Settings.LocalAnchorDir;

% Prepare to build libraries
if ~isempty(libsToBuild)
    libBuildResults = coder.make.MakeResult.empty(0, length(libsToBuild));
    
    % For the existence check, always use build results for the static
    % library (in the case of a mex target this differs from the final
    % product for each component)
    libBuildResultsForPoller = coder.make.MakeResult.empty(0, length(libsToBuild));
    
    [libsToBuildPaths, libsToBuildNames] = i_getLibPaths(libsToBuild, lRootFolder);
    lUpdatedLibPoller = coder.make.internal.UpdatedLibArchivePoller...
        (libsToBuildPaths, libsToBuildNames);
end

% Map to cache the link objects CMake target names
% The map avoids having to re-compute the target names when generating the
% CMakeLists.txt for the top level buildInfo
cmakeTargetNames = containers.Map;

for i=1:length(libsToBuild)
    libBuildInfo = libsToBuild(i).BuildInfoHandle;
    libBuildOptsOriginal = libsToBuild(i).BuildOptsHandle;

    if inheritBuildOptionsFromTop
        % Some build options from the top component override build options
        % originating from the child component
        libBuildOptsUpdated = coder.make.internal.buildOptsApplyParentOverrides...
            (libBuildOptsOriginal, buildOptsTop);
    else
        % Build options are obtained from this component's buildInfo.mat
        libBuildOptsUpdated = libBuildOptsOriginal;
    end

    if isempty(libBuildOptsUpdated.BuildName)
        libBuildOptsUpdated.BuildName = libBuildInfo.ComponentName;
    end
        
    if isCMake
        [libBuildResults(i), targetName] = coder.make.internal.writeCMakeLists...
            (libsToBuildPaths{i}, libBuildInfo, ...
            'BuildName', libBuildOptsUpdated.BuildName, ...
            'BuildVariant', libBuildOptsUpdated.BuildVariant, ...
            'BuildMethodSerializable', libBuildOptsUpdated.BuildMethodSerializable, ...
            'LinkObjsToSkip', cmakeLinkObjsToSkip, ...
            'IsTopLevel', false, ...
            'CMakePackages', libBuildOptsUpdated.CMakePackages);
        cmakeTargetNames(libsToBuild(i).Name) = targetName;
    else
        % For mex case only, perform the initial library build
        [remainderBuildOpts, remainderBuildFolder, libMakeResult] = ...
            i_mexDoLibBuild(libBuildInfo, libBuildOptsUpdated, libsToBuildPaths{i});

        % Determine "model reference target type"
        isERTSFcn = false;
        modelReferenceTargetType = coder.make.internal.getModelRefTargetType...
            (remainderBuildOpts.BuildVariant, isERTSFcn);

        
        libBuildResults(i) = RTW.genMakefileAndBuild...
            (remainderBuildFolder, libBuildInfo, remainderBuildOpts, ...
            modelReferenceTargetType);

        if ~isempty(libMakeResult)
            addChildMakeResults(libBuildResults(i), libMakeResult);
            libBuildResultsForPoller(i) = libMakeResult;
        else
            libBuildResultsForPoller(i) = libBuildResults(i);
        end
        
    end
    % Serialize the compileInfo.mat file
    if ~isempty(libBuildResults(i).CompileInfo)
        saveInfoFile(libBuildResults(i).CompileInfo);
    end
end

if ~isCMake
    % Poll for existence of updated copy of all libraries that
    % were re-compiled
    if ~isempty(libsToBuild)
        pollForUpdatedLibArchives(lUpdatedLibPoller, libBuildResultsForPoller);
    end
end

% Full path to build folder for topmost component
lBuildFolderFullPath = fullfile(lRootFolder, buildInfo.ComponentBuildFolder);

if buildTopComponent
    if isCMake
        lMakeResult = coder.make.internal.writeCMakeLists(lBuildFolderFullPath, buildInfo, ...
            'BuildName', buildOptsTop.BuildName, ...
            'BuildVariant', buildOptsTop.BuildVariant, ...
            'BuildMethodSerializable', buildOptsTop.BuildMethodSerializable, ...
            'GenerateChildComponents', true, ...
            'CMakeTargetNames', cmakeTargetNames,...
            'IsTopLevel', true,...
            'CMakePackages', buildOptsTop.CMakePackages);
        
        
        if coder.make.internal.featureOn('TargetFrameworkToolchain')
            if isCMakeInfo
                % Compile using CMake
                lMakeResult = coder.make.internal.cmakeBuild(...
                    buildOptsTop.BuildMethod,...
                    buildOptsTop.BuildName,...
                    buildOptsTop.BuildVariant,...
                    buildOptsTop.RTWVerbose);
            end
        end
    else        
        lMakeResult = i_buildTopComponent(buildInfo, buildOptsTop,lBuildFolderFullPath);
    end
else
    lMakeResult = coder.make.MakeResult('', '', []);
end
    
% Add the child make results so that lMakeResult can show the log
% for all child components as well as the top component
if ~isempty(libsToBuild)
    addChildMakeResults(lMakeResult, libBuildResults)
end

% Serialize the compileInfo.mat file
if ~isempty(lMakeResult.CompileInfo)
    saveInfoFile(lMakeResult.CompileInfo);
end

% Restore environment settings
delete(environmentCleanupFcn)

function [libsToBuildPaths, libsToBuildNames] = i_getLibPaths(libsToBuild, lRootFolder)

libsToBuildPaths = {libsToBuild.Path};
libsToBuildPaths = regexprep(libsToBuildPaths, ...
    [regexptranslate('escape', '$(START_DIR)') '(\\|\/)?'], '');
libsToBuildPaths = fullfile(lRootFolder, libsToBuildPaths);

libsToBuildNames = {libsToBuild.Name};



function i_validateParam(name, value)
% Create a buildOpts for validation purposes
persistent lBuildOpts
if isempty(lBuildOpts)
    lBuildOpts = coder.make.BuildOpts;
end
originalValue = lBuildOpts.(name);

% Validate the parameter value
lBuildOpts.(name) = value;

% Restore the original value to avoid retaining any
% handle that may prevent cleanup elsewhere
lBuildOpts.(name) = originalValue;



% Check that buildInfoFolder argument is valid and load buildInfo,
% buildOpts from the file
function [buildInfo, buildOpts] = i_loadBuildInfo(buildInfoFolder)

if isfolder(buildInfoFolder)
    buildInfoFile = fullfile(buildInfoFolder, 'buildInfo.mat');
    if ~isfile(buildInfoFile)
        msg = message('coder_compile:CoderCompile:CodebuildInvalidFolder', ...
            buildInfoFolder);
        error(msg.Identifier, '%s', string(msg));
    end
elseif isfile(buildInfoFolder)
    buildInfoFile = buildInfoFolder;
else
    msg = message('coder_compile:CoderCompile:CodebuildNonExistentFolder', ...
        buildInfoFolder);
    error(msg.Identifier, '%s', string(msg));
end

validateBuildInfoContent = true;
[buildInfo, buildOpts] = coder.make.internal.loadBuildInfo...
    (buildInfoFile, validateBuildInfoContent);
        


% Convert varargin passed to codebuild to a build options object
function specifiedBuildOptions = i_applyBuildOptsFromNameValues(buildOpts, nameValueArgs)

% The input parser is expensive to create, therefore place it in a persistent variable
persistent p

if isempty(p)
    % Obtain default parameter values from coder.make.BuildOpts
    defaultOpts = coder.make.BuildOpts;
    
    p = inputParser;

    params = {
        'ComponentsToBuild'
        'BuildActions'
        'BuildConfiguration'
        'BuildMethod'
        'BuildName'
        'BuildVariant'
        'ClientChecksum'
        'CMakePackages'
        'CoderTargetAuxMakeContent'
        'CompileErrorDiagnosticFunctions'
        'CompileProfileFcn'
        'CopyBuildInfo'
        'CustomToolchainOptions'
        'DispHook'
        'LegacyTargetLibSuffix'
        'MakefileSelfDependency'
        'ModelRefLibPathFormat'
        'PrecompiledLibSuffix'
        'RTWVerbose'
        'SuppressExe'
        'TargetLang'
        'TargetLangExt'
        'UseFileChecksums'
        'buildHookEnvVars'
        'buildHookPathPrepends'
        'checkStatusOnly'
        'forceCompile'                % Required subject to g2041630
        'generateCodeOnly'
        'isCpp'
        'isMATLABCodeGen'
        'makeCommandPrepend'
        'makefileName'
        'ncstates'
        'sysTargetFile'
        };
    
    for i=1:length(params)
        addParameter(p, params{i}, defaultOpts.(params{i}), @(x)i_validateParam(params{i}, x));
    end

end

% Clear inputParser on exiting this function. This avoids persisting any
% object handles in p.Results. This is important otherwise it prevents
% these objects from being cleaned up, even when all other handles are
% deleted.
pCleanup = onCleanup(@()parse(p));

% Parse nameValueArgs
parse(p, nameValueArgs{:});

% Apply input parser results to the build opts object
notUsingDefaults = setdiff(p.Parameters, p.UsingDefaults);
for i=1:length(notUsingDefaults)
    parameter = notUsingDefaults{i};
    buildOpts.(parameter) = p.Results.(parameter);
end

% Input parser results are now captured in a build opts object
delete(pCleanup);

% Return names of build options that were specified by the caller of codebuild
specifiedBuildOptions = nameValueArgs(1:2:end);


% Convert the build method to an object if it's a toolchain or TMF build
function [buildMethodResolved, toolchainInfo] = i_resolveBuildMethod...
    (isCMake, buildMethod_in)

buildMethodResolved = buildMethod_in;
toolchainInfo = [];
if ~isCMake
    [toolchainInfo, lTMFProperties] = ...
        coder.make.internal.resolveToolchainOrTMF(buildMethod_in);
    if ~isempty(lTMFProperties)
        buildMethodResolved = lTMFProperties;
    elseif ~isempty(toolchainInfo)
        buildMethodResolved = toolchainInfo;
    end
end


% Identify the mex options file (for toolchains that support building mex
% files)
function mexCompInfo = i_resolveMexCompInfo(isCMake, mexCompInfo_in, toolchainInfo)

mexCompInfo = [];
if ~isCMake
    if ~isempty(mexCompInfo_in)
        % If a mex options file was specified, use it
        mexCompInfo = mexCompInfo_in;
    else
        % Identify the mex options for the current toolchain
        mexCompInfo = coder.make.internal.getToolchainMexOpts(toolchainInfo);
        
        if ~isempty(mexCompInfo)
            % If default (located under matlabroot) mex options for the
            % toolchain corresponds to the currently selected mex compiler,
            % make sure we use options file for the latter. This is because
            % the user might have some customizations in the options file
            % located in prefdir.
            lMexCompInfoSelected = coder.make.internal.getMexCompilerInfo;
            if ~isempty(lMexCompInfoSelected) && ...
                    strcmp(lMexCompInfoSelected.compStr, toolchainInfo.Alias{1})
                mexCompInfo = lMexCompInfoSelected;
            end
        end        
    end
end


function environmentCleanupFcn = i_setupEnvironment...
    (isCMake, mexCompInfo, buildMethod)

environmentCleanupFcn = onCleanup(@()[]);
if coder.make.internal.featureOn('CacheVcVars') && ~isCMake && ispc && ...
        coder.make.internal.isFactoryMsvc(buildMethod)
    if ~isempty(mexCompInfo)
        % Apply environment settings for MSVC
        mexCompDetails = mexCompInfo.comp.Details;
        environmentCleanupFcn = coder.make.internal.applyMsvcEnvironment(mexCompDetails);
    end
end


% Configure buildInfo to generate mex file
function mexBuildOpts = i_configureMexBuildInfo(buildFolder, mexBuildInfo, ...
    buildOpts, modelLib)

% Deep copy buildOpts so that any changes are internal to codebuild
mexBuildOpts = copy(buildOpts);
clear buildOpts

% Create folder for mex build
mexObjFolder = fullfile(buildFolder, 'mex');
if ~isfolder(mexObjFolder)
    mkdir(mexObjFolder)
end

% Configure buildInfo for build using the mex sub-folder
mexBuildInfo.ComponentBuildFolder = mexObjFolder;
coder.make.internal.syncRelativePathToAnchor(mexBuildInfo)

% Required defines
addDefines(mexBuildInfo, 'MATLAB_MEX_FILE');
addDefines(mexBuildInfo, 'MATLAB_DEFAULT_RELEASE=R2018a') % Interleaved complex API

% Remove any source paths (all source files must have an explicit path)
mexBuildInfo.Src.Paths = [];

% Include path for mex header files
addIncludePaths(mexBuildInfo, fullfile(matlabroot, 'extern', 'include'));

% Mex build does not allow multi-tasking
mexBuildInfo.removeKeyValuePair('Tokens', {'|>MULTITASKING<|'});

% Remove any MakeArgs (e.g. USER_SRCS)
mexBuildInfo.MakeArgs = '';

% Remove build arg GENERATE_ERT_S_FUNCTION
argErtSfcn = findBuildArg(mexBuildInfo, 'GENERATE_ERT_S_FUNCTION');
if isempty(mexBuildInfo.BuildArgs)
    keys = {};
else
    keys = {mexBuildInfo.BuildArgs.Key};
end
keepIdx = ~strcmp(keys, argErtSfcn);
mexBuildInfo.BuildArgs = mexBuildInfo.BuildArgs(keepIdx);

% Remove TMF token GEN_SAMPLE_MAIN
genSampleMain = findTMFToken(mexBuildInfo, '|>GEN_SAMPLE_MAIN<|');
if isempty(mexBuildInfo.Tokens)
    keys = {};
else
    keys = {mexBuildInfo.Tokens.Key};
end
keepIdx = ~strcmp(keys, genSampleMain);
mexBuildInfo.Tokens = mexBuildInfo.Tokens(keepIdx);


% Some compiler warnings on unix must be suppressed
mexSrcFiles = getSourceFiles(mexBuildInfo, false, false);
[~, mexSrcName, mexSrcFileExt] = fileparts(mexSrcFiles{1});
if isunix && ~ismac
    % Linux platform
    isCpp = strcmp(mexSrcFileExt, '.cpp');
    if ~isCpp
        % Suppress warning about implicit declaration
        % TopTester: matlab/test/toolbox/simulink/engine_blksNS/tsfbuilder4.m
        addCompileFlags(mexBuildInfo, '-Wno-implicit-function-declaration');
    end
end

% Some compiler warnings on Windows must be suppressed
[toolchainInfo, tmfProps] = ...
    coder.make.internal.resolveToolchainOrTMF(mexBuildOpts.BuildMethod);
if contains(toolchainInfo.Alias{1}, 'Microsoft-')
    % For Microsoft compiler, suppress some warnings when compiling mex
    % source files
    %
    % 4100: unreferenced formal parameter
    % 4701: potentially uninitialized local variable, PackagedModel/tFileGenControl
    % 4189: local variable is initialized but not referenced
    % 4090: different 'modifier' qualifiers
    % 4505: unreferenced local function has been removed
    addCompileFlags(mexBuildInfo, ...
        {'/wd4701', '/wd4100', '/wd4189', '/wd4090', '/wd4505'})
end

% Mex file release version management
addSourceFiles(mexBuildInfo, fullfile([matlabroot '/extern/version/c_mexapi_version.c']));

% System libraries for mex build
mexToolChainName = coder.make.internal.getMexToolchainName(toolchainInfo);
sysLibPath = coder.make.internal.getMLSysLibPath(mexToolChainName);
addSysLibs(mexBuildInfo, {'mx', 'mex'}, sysLibPath);

% If a static library was split out, this library must be linked
if ~isempty(modelLib)
    group = coder.make.internal.BuildInfoGroup.ModelRefGroup;
    linkObj = RTW.BuildInfoLinkObj(modelLib, buildFolder, 1000, group);
    linkObj.LinkOnly = true;
    
    % Any child linkables are already included with mexBuildInfo
    loadLinkObjBuildInfo(linkObj);
    if ~isempty(linkObj.BuildInfoHandle)
        removeLinkables(linkObj.BuildInfoHandle);
    end
    
    % The linkable object must be added in front of any other linkables
    linkables = mexBuildInfo.Linkables;
    removeLinkables(mexBuildInfo);
    addLinkObjects(mexBuildInfo, [linkObj; linkables]);
end

% Name of file defining exported symbols
libName = mexBuildInfo.ComponentName;
defFile = [libName '.def'];
defFileFullPath = fullfile(buildFolder, 'mex', defFile);

if strcmp(toolchainInfo.Alias{1}, 'GNU-x')
    if ispc
        % MinGW
        addLinkFlags(mexBuildInfo, ...
            '-Wl,"$(MATLAB_ROOT)/extern/lib/win64/mingw64/exportsmexfileversion.def"');
        
        % Include dependencies such as libgcc_s_seh-1.dll in the image
        % otherwise they might not be found at runtime
        addLinkFlags(mexBuildInfo, '-static')
        
        defFileContent = '';
    else
        % GCC on Linux
        defFileContent = fileread(fullfile...
            ([matlabroot '/extern/lib/glnxa64/c_exportsmexfileversion.map']));
        % Remove comment lines
        defFileContent = strtrim(regexprep(defFileContent, '^\s*#.*', '', ...
            'dotexceptnewline', 'lineanchors'));
        
        if isempty(tmfProps)
            % The linker flag is hard-coded for template makefile but not
            % for toolchain compilation
            addLinkFlags(mexBuildInfo, [ '-Wl,--version-script,' defFile ]);
        end
    end
    
elseif any(startsWith(toolchainInfo.Alias{1}, {'Microsoft-', 'INTEL'}))
    defFileContent = '';
    addLinkFlags(mexBuildInfo, ...
                 {'/EXPORT:mexFunction', '/EXPORT:mexfilerequiredapiversion'})

    % Do not pollute the output folder with .exp, .lib files
    addLinkFlags(mexBuildInfo, ['/IMPLIB:' mexSrcName '.lib']);
        
elseif startsWith(toolchainInfo.Alias{1}, 'Apple')
        defFileContent = fileread(fullfile...
            (matlabroot, 'extern', 'lib', computer('arch'), 'c_exportsmexfileversion.map'));
        % Remove comment lines
        defFileContent = strtrim(regexprep(defFileContent, '^\s*#.*', '', ...
            'dotexceptnewline', 'lineanchors'));
        defFileContent = [defFileContent newline];
        
        if isempty(tmfProps)
            % The linker flag is hard-coded for template makefile but not
            % for toolchain compilation
            addLinkFlags(mexBuildInfo, [ '-Wl,-exported_symbols_list,' defFile ]);
        end
else
    assert(startsWith(toolchainInfo.Alias{1}, 'LCC'), 'Toolchain must be one of supported options')
    [~, lRelativePathToAnchor] = findBuildArg(mexBuildInfo, 'RELATIVE_PATH_TO_ANCHOR');
    defFileContent = ['LIBRARY ' fullfile(lRelativePathToAnchor, [mexSrcName '.' mexext]) newline];
    defFileContent = [defFileContent 'EXPORTS' newline];
    defFileContent = [defFileContent '    mexFunction' newline];
    defFileContent = [defFileContent '    mexfilerequiredapiversion' newline];
end


% Write out the .def file (if not already up to date)
if isfile(defFileFullPath)
    oldContent = fileread(defFileFullPath);
    isUpToDate = strcmp(defFileContent, oldContent);
else
    isUpToDate = false;
end
if ~isUpToDate
    fid = fopen(defFileFullPath, 'w');
    fprintf(fid, '%s', defFileContent);
    fclose(fid);
end

% Build name for the mex component
mexBuildOpts.BuildName = mexSrcName;


% Do out static library build, prior to creating mex file
function [libMakeResult, mexBuildOpts] = ...
    i_buildStaticLibPriorToMex(buildFolder, buildInfoMex, buildOpts)

% Make a copy of the buildInfo because it might be modified while building
% the static library, which would unnecessarily invalidate the checksum for
% building the mex file.
% TopTester: test/toolbox/simulink/modelref/Lean/bld/tReBuild_ChangeMatSimAndUpdate.m
buildInfoStaticLib = copy(buildInfoMex);

% Extract the mex source files from the buildInfo
mexSrcIdx = strcmp({buildInfoStaticLib.Src.Files.Group}, ...
    coder.make.internal.BuildInfoGroup.MexSourceGroup);
mexSrcFiles = buildInfoStaticLib.Src.Files(mexSrcIdx);
buildInfoStaticLib.Src.Files = buildInfoStaticLib.Src.Files(~mexSrcIdx);

% Build the static library and any non-link-only libraries sharing the same
% makefile (tAccelSfunUsingTLC)
libBuildOpts = copy(buildOpts);
libBuildOpts.BuildVariant = coder.make.enum.BuildVariant.STATIC_LIBRARY;
libMakeResult = RTW.genMakefileAndBuild(buildFolder, buildInfoStaticLib, libBuildOpts, 'SIM');
    
% Remove the non-mex source files from the mex buildInfo
nonMexSourceFiles = getSourceFiles(buildInfoMex, false, false, '', ...
    coder.make.internal.BuildInfoGroup.MexSourceGroup);
removeSourceFiles(buildInfoMex, nonMexSourceFiles);

% Restore the mex source files
buildInfoMex.Src.Files = mexSrcFiles;

if isempty(nonMexSourceFiles)
    % No source files for the main static library therefore it was not
    % created (but a non-link-only library might have been built) 
    libFinalProduct = '';
else
    libFinalProduct = libMakeResult.FinalProduct;
end

% Configure the mex buildInfo
mexBuildOpts = i_configureMexBuildInfo(buildFolder, buildInfoMex, buildOpts, ...
    libFinalProduct);



% In the case of building a mex function, do the initial library build
function  [remainderBuildOpts, remainderBuildFolder, libMakeResult] = ...
        i_mexDoLibBuild(buildInfo, buildOpts, buildFolderFullPath)

if buildOpts.BuildVariant==coder.make.enum.BuildVariant.MEX_FILE && coder.make.internal.featureOn('MexbldUseCodebuild')   
    [libMakeResult, remainderBuildOpts] = i_buildStaticLibPriorToMex...
        (buildFolderFullPath, buildInfo, buildOpts);
    
    % Final build of the mex file takes place in a sub-folder
    remainderBuildFolder = fullfile(buildFolderFullPath, 'mex');
    
    % Serialize the compileInfo.mat file
    if ~isempty(libMakeResult) && ~isempty(libMakeResult.CompileInfo)
        saveInfoFile(libMakeResult.CompileInfo);
    end    
else 
    remainderBuildFolder = buildFolderFullPath;
    remainderBuildOpts = copy(buildOpts);
    libMakeResult = [];
end


% Determine which components must be built
function [libsToBuild, buildTopComponent] = i_getLibsToBuild(buildInfo, componentsToBuild)

if isempty(componentsToBuild)
    % Building all components
    buildTopComponent = true;
    linkablesRequested = {}; % Build all libraries
else
    buildTopComponent = any(strcmp(componentsToBuild, buildInfo.ComponentName));
    linkablesRequested = setdiff(componentsToBuild, buildInfo.ComponentName, 'stable');
    if isempty(linkablesRequested)
        libsToBuild = {};
        return
    end
end

linkables = [buildInfo.LinkObj(end:-1:1); buildInfo.ModelRefs(end:-1:1)];
hasBuildInfoIdx = ~cellfun(@isempty, {linkables.BuildInfoHandle});
buildableLinkables = linkables(hasBuildInfoIdx);
if isempty(buildableLinkables)
    linkableNames = {};
else
    buildInfoHandles = [buildableLinkables.BuildInfoHandle];
    linkableNames = {buildInfoHandles(:).ComponentName};
end

% Filter according to setting of ComponentsToBuild argument
if ~isempty(componentsToBuild)
    missingComponents = setdiff(linkablesRequested, linkableNames);
    if ~isempty(missingComponents)
        missingComponents = join(string(missingComponents), ', ');
        msg = sprintf(['Cannot build components %s. The requested ' ...
            'ComponentsToBuild must each correspond to a ComponentName ' ...
            'in the buildInfo hierarchy.'], missingComponents);
        assert(false, msg);
    end
    [~, ia] = intersect(linkableNames, linkablesRequested, 'stable');
else
    % Building all components
    ia = true(size(linkableNames));
end
keepIdx = false(size(buildableLinkables));
keepIdx(ia) = true;

% Filter according to setting of RTW.BuildInfoLinkObj > NeverRebuild
neverRebuildIdx = [buildableLinkables.NeverRebuild];
keepIdx(neverRebuildIdx) = false;

libsToBuild = buildableLinkables(keepIdx);

% TODO(g2446267) remove any child linkables as not required when building 
% a static library. At present they are included in the checksum 
% incurring some performance overhead.

% Makefile build for top component
function lMakeResult = i_buildTopComponent(buildInfo, buildOptsTop,lBuildFolderFullPath)
% For mex case only, perform the initial library build
[remainderBuildOpts, remainderBuildFolder, libMakeResult] = ...
    i_mexDoLibBuild(buildInfo, buildOptsTop, lBuildFolderFullPath);

% Target type for final product
[~, argErtSfcn] = findBuildArg(buildInfo, 'GENERATE_ERT_S_FUNCTION');
isERTSFcn = strcmp(argErtSfcn, '1');
modelReferenceTargetType = coder.make.internal.getModelRefTargetType...
    (remainderBuildOpts.BuildVariant, isERTSFcn);

% Build the final product
lMakeResult = RTW.genMakefileAndBuild...
    (remainderBuildFolder, buildInfo, remainderBuildOpts, ...
     modelReferenceTargetType);

if ~isempty(libMakeResult)
    addChildMakeResults(lMakeResult, libMakeResult);
end
