function generate_code_for_charts_and_machine(fileNameInfo,codingRebuildAll)

% Copyright 2003-2023 The MathWorks, Inc.

global gMachineInfo %#ok<GVMIS>
global gTargetInfo %#ok<GVMIS>

incCodeGenInfo = compute_inc_codegen_info(fileNameInfo,codingRebuildAll);

mainMachineId = gMachineInfo.mainMachineId;
machineName = gMachineInfo.machineName;
mainMachineName = gMachineInfo.mainMachineName;
targetName = gMachineInfo.targetName;

gMachineInfo.thirdPartyUses = {};
gMachineInfo.updateBuildInfoArgs = {};

lastException = [];

if gTargetInfo.codingSFunction
compute_autoinheritance_info();
end

for i = 1:length(gMachineInfo.charts)
chart = gMachineInfo.charts(i);
chartFileNumber = sf('get',chart,'chart.chartFileNumber');
chartName = sf('get',chart,'.name');

numSpecs = length(gMachineInfo.specializations{i});

if gTargetInfo.codingSFunction
for j = 1:numSpecs
if(~incCodeGenInfo.flags{i}(j))

success = false;
try
mainSfunName = [gMachineInfo.mainMachineName '_sfun'];
thirdPartyUses = feval(mainSfunName, 'get_third_party_uses_info', gMachineInfo.machineName, ...
chartFileNumber, gMachineInfo.specializations{i}{j});
gMachineInfo.thirdPartyUses = [gMachineInfo.thirdPartyUses thirdPartyUses];

updateBuildInfoArgs = feval(mainSfunName, 'get_updateBuildInfo_args_info', gMachineInfo.machineName, ...
chartFileNumber, gMachineInfo.specializations{i}{j});
gMachineInfo.updateBuildInfoArgs = [gMachineInfo.updateBuildInfoArgs updateBuildInfoArgs];

success = true;
catch
end

if ~success
incCodeGenInfo.flags{i}(j) = 1;
end
end
end
end

if ~any(incCodeGenInfo.flags{i})
continue;
end

compute_chart_information(chart);

if gTargetInfo.codingRTW
rtwInstanceInfo = [];
end

% if/when we support library charts, the key will need to be augmented
% to distinguish between specializations, and this flush will need to
% be moved into the spec loop below. will also need a way to distinguish
% keys from instances in different models.
if gTargetInfo.codingSFunction ...
&& isfile([fullfile(matlabroot,'toolbox','fixedpoint','fixedpoint','+fixed','+internal','clearLog') '.' mexext])
% Do NOT test for min/max logging enabled - especially
% important to flush when not enabled.
fixed.internal.clearLog(['#' Simulink.ID.getSID(idToHandle(sfroot(), chart))]);
end

%Clear any chart cache auxInfo before generating code.
sfprivate('auxInfoChartCache','clr',chart);
numSpecs = length(gMachineInfo.specializations{i});
for j = 1:numSpecs
thisSpec = gMachineInfo.specializations{i}{j};
if(~isempty(incCodeGenInfo.jitIncCodegenInfoForMachine))
jitIncCodegenInfoForSpec = incCodeGenInfo.jitIncCodegenInfoForMachine.(thisSpec);
needsPreFallback = jitIncCodegenInfoForSpec.needsPreFallback;
preFallBackReason = jitIncCodegenInfoForSpec.preFallBackReason;
else
needsPreFallback = true;
preFallBackReason = 'feature off';
end
if(~incCodeGenInfo.flags{i}(j))
continue;
end

%TLTODO: handle following block
% Let EML know what charts need to be reconsidered for its resolved functions
targetId = gTargetInfo.target;
targetName = sf('get',targetId,'.name');
targetName0 = sfprivate('get_eml_metadata_target_name',targetName);
machineName0 = sfprivate('get_eml_metadata_machine_name',machineName);
rebuildMetaData = sf('get', mainMachineId, '.eml.rebuildMetaData');
if ~isempty(rebuildMetaData) && isfield(rebuildMetaData, targetName0)
chartFiles = rebuildMetaData.(targetName0).(machineName0).rebuiltChartFiles;
if ~any(chartFiles == chartFileNumber)
chartFiles = sort([chartFiles chartFileNumber]);
rebuildMetaData.(targetName0).(machineName0).rebuiltChartFiles = chartFiles;
sf('set',mainMachineId,'.eml.rebuildMetaData', rebuildMetaData);
end
end

% Even though the incremental codegen info says we need to
% regenerate IR for this spec, it is possible this spec was
% handled as part of another chart. Hence, try to relink before
% we generate code and return early.
if gTargetInfo.codingSFunction && ~needsPreFallback
needValidateJITEngine = sf('get', chart, '.hadBreakpoints') || gTargetInfo.codingCoverage;
if Stateflow.JIT.Utils.relinkToExistingJITEngine(chart,thisSpec,needValidateJITEngine)
gMachineInfo.codingLLVM{i}(j) = 1;
continue;
end
end

display_chart_codegen_message(chart,sf('get',targetId,'target.name'));
errorsOccurred = false;
try
needsPreFallback = construct_module(chart, 0.0, thisSpec, fileNameInfo,needsPreFallback,preFallBackReason);
catch ME
errorsOccurred = true;
lastException = ME;
sfprivate('sf_display', sprintf('Error: %s\n', ME.message));
end

if ~errorsOccurred
if gTargetInfo.codingSFunction && ...
~needsPreFallback && ...
sfprivate('can_relink_to_existing_jit_engine',chart,thisSpec)
gMachineInfo.codingLLVM{i}(j) = 1;
sf('Cg','destroy_module',chart);
continue;
end

sfprivate('sf_compile_stats', 'snap', targetName, mainMachineName, machineName, chartName, 'generate_code_for_chart', true);

try
create_rapidaccel_wrapper_dir_path_just_in_time(gTargetInfo, gMachineInfo.mainMachineId, chart, thisSpec);
create_project_dir_path_just_in_time(fileNameInfo);
sf('set', chart, 'chart.codeGeneratedNow', 1);

msgString = getString(message('Stateflow:sfprivate:GeneratingFiles'));
sfprivate('sf_display', msgString);

code_chart_header_file(fileNameInfo,chart,j);
code_chart_source_file(fileNameInfo,chart,j);

spec_third_party_uses = sf('Cg','third_party_uses',chart);
gMachineInfo.thirdPartyUses = [gMachineInfo.thirdPartyUses spec_third_party_uses];

spec_updateBuildInfoArgs = sf('Cg','updateBuildInfo_args',chart);
gMachineInfo.updateBuildInfoArgs = [gMachineInfo.updateBuildInfoArgs spec_updateBuildInfoArgs];

catch ME
% Make sure we destroy the module otherwise things leak.
if ~gTargetInfo.codingRTW % We delay the destruction at the end of mdlRTWCG(). Needs to be delayed in this case since the order of deletion can be different from the order of creation.
sf('Cg','destroy_module',chart);
end
rethrow(ME);
end

if ~gTargetInfo.codingRTW
sf('Cg','destroy_module',chart);
end

sfprivate('sf_compile_stats', 'snap', targetName, mainMachineName, machineName, chartName, 'generate_code_for_chart', false);

if gTargetInfo.codingRTW
tlcFile = fileNameInfo.chartTLCFiles{i}{j};
outputFcn = fileNameInfo.chartOutputsFcns{i}{j};
initFcn = fileNameInfo.chartInitializeFcns{i}{j};

instanceInfo = get_instance_rtw_info(chart, thisSpec, tlcFile, outputFcn, initFcn, ...
spec_third_party_uses, spec_updateBuildInfoArgs);
rtwInstanceInfo = [rtwInstanceInfo instanceInfo]; %#ok<AGROW>
end
end
end

if gTargetInfo.codingRTW
sf('set', chart, 'chart.rtwInfo.instanceInfo', rtwInstanceInfo);
end
end

if gTargetInfo.codingSFunction && ~isempty(lastException)
throwAsCaller(lastException);
end

% Clear deep learning mat obj cache.
% TODO: Move it to a better place, at the exit of model update.
if gTargetInfo.codingSFunction
clear(which('coder.internal.loadCachedDeepLearningObj'));
end
% Compute if machine is fully jitted. if not we will have to run the
% makemethod in targetman
if gTargetInfo.codingSFunction
jitIncCodegenInfo = sf('get',mainMachineId,'machine.jitIncCodeGenInfo');
if(~isempty(jitIncCodegenInfo) && isfield(jitIncCodegenInfo,machineName))
jitIncCodegenInfoForMachine = jitIncCodegenInfo.(machineName);
else
jitIncCodegenInfoForMachine = [];
end

if ~isempty(gMachineInfo.charts) && ~isempty(jitIncCodegenInfoForMachine)
codegenSpecCount = 0;
foundInJitSpecCount = 0;
foundInDllSpecCount = 0;
for i = 1:length(gMachineInfo.charts)
chart = gMachineInfo.charts(i);
numSpecs = length(gMachineInfo.specializations{i});
for j=1:numSpecs
thisSpec = gMachineInfo.specializations{i}{j};
jitIncCodegenInfoForSpec = jitIncCodegenInfoForMachine.info.(thisSpec);
if(jitIncCodegenInfoForSpec.generateCode)
codegenSpecCount = codegenSpecCount+1;
break;
elseif(jitIncCodegenInfoForSpec.generateJitEngine)
if(~sfprivate('can_relink_to_existing_jit_engine',chart,thisSpec))
% must have been a late fallback
codegenSpecCount = codegenSpecCount+1;
else
foundInJitSpecCount = foundInJitSpecCount+1;
end
elseif(jitIncCodegenInfoForSpec.foundInDll)
foundInDllSpecCount = foundInDllSpecCount+1;
else
assert(jitIncCodegenInfoForSpec.foundInJit);
foundInJitSpecCount = foundInJitSpecCount+1;
end
end
end
jitIncCodegenInfo.(machineName).codegenSpecCount = codegenSpecCount;
if(jitIncCodegenInfo.(machineName).codegenSpecCount>0)
jitIncCodegenInfo.(machineName).invokeMakeMethod = true;
end
jitIncCodegenInfo.(machineName).foundInJitSpecCount = foundInJitSpecCount;
jitIncCodegenInfo.(machineName).foundInDllSpecCount = foundInDllSpecCount;
sf('set',mainMachineId,'machine.jitIncCodeGenInfo',jitIncCodegenInfo);
end

fullyJittedLibs = [];
for i = 1:length(fileNameInfo.linkMachines)
linkMachineName = fileNameInfo.linkMachines{i};
if(jitIncCodegenInfo.(linkMachineName).codegenSpecCount==0 &&...
jitIncCodegenInfo.(linkMachineName).foundInDllSpecCount==0)

fullyJittedLibs(end+1) = i; %#ok <AGROW>
end
end

fileNameInfo.linkMachines(fullyJittedLibs) = [];
fileNameInfo.linkLibFullPaths(fullyJittedLibs) = [];
fileNameInfo.linkObjListFullPaths (fullyJittedLibs) = [];
fileNameInfo.linkMachinesInlinable(fullyJittedLibs) = [];

codegenOccurred = false;
for i = 1:length(fileNameInfo.linkMachines)
linkMachineName = fileNameInfo.linkMachines{i};
if(jitIncCodegenInfo.(linkMachineName).codegenSpecCount>0)
codegenOccurred = true;
break;
end
end
if(jitIncCodegenInfo.(machineName).codegenSpecCount>0)
codegenOccurred = true;
end
if(~codegenOccurred)
return;
end
jitIncCodegenInfo.(machineName).invokeMakeMethod = true;
sf('set',mainMachineId,'machine.jitIncCodeGenInfo',jitIncCodegenInfo);
end

if gTargetInfo.codingSFunction
gMachineInfo.thirdPartyUses = unique(gMachineInfo.thirdPartyUses);
gMachineInfo.updateBuildInfoArgs = uniqueCell(gMachineInfo.updateBuildInfoArgs);
end

if ~gTargetInfo.codingHDL && ~gTargetInfo.codingPLC
oldCtxInfo = []; % Only set when we create a temporary context for machine.
if gTargetInfo.codingRTW
% Create a temporary context for machine since the current
% context is the one from RTW and we do not want to pollute it
% with machine information that are not compatible with RTW.
%
% To be consistent with other created contexts, this temporary
% context will be destroyed the next time that we create a new
% context and calling construct_module or construct_machine_module.
% Thus, we do not need to destroy the context here.

oldCtxInfo = gMachineInfo.ctxInfo;
status = construct_context_for_machine([], true);
if status ~= 0
construct_coder_error(gMachineInfo.machineId,getString(message('Stateflow:sfprivate:ConstructContextFail')),0);
end
end

try
sf('Cg','construct_machine_module',gMachineInfo.ctxInfo);
catch ME
lastException = ME;
construct_coder_error(gMachineInfo.machineId,ME.message,0);
end

if isempty(lastException)
assert(sfprivate('coder_error_count_man', 'get') == 0);
sfprivate('sf_compile_stats', 'snap', targetName, mainMachineName, machineName, 'machine', 'generate_code_for_machine', true);
create_project_dir_path_just_in_time(fileNameInfo);
fileNameInfo = code_aux_support_files(fileNameInfo);

fileNameInfo.buildInfo = create_build_info(fileNameInfo);
code_machine_header_file(fileNameInfo);
code_machine_source_file(fileNameInfo);
code_interface_and_support_files(fileNameInfo);
sfprivate('sf_compile_stats', 'snap', targetName, mainMachineName, machineName, 'machine', 'generate_code_for_machine', false);
end

if ~isempty(oldCtxInfo)
% No need to delete the temporary context for the machine
% (see the previous comment), but we need to restore the old context.
gMachineInfo.ctxInfo = oldCtxInfo;
end
end

if ~isempty(lastException)
throwAsCaller(lastException);
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function y_or_n = yes_or_no(val)
if val
y_or_n = 'Yes';
else
y_or_n = 'No';
end

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function instanceInfo = get_instance_rtw_info(chart, spec, tlcFile, outputFcn, initFcn, spec_third_party_uses, spec_updateBuildInfo_args)

instanceInfo.specialization = spec;
instanceInfo.TLCFile = tlcFile;
instanceInfo.OutputsFcn = outputFcn;
instanceInfo.InitializeFcn = initFcn;
instanceInfo.ReusableOutputs = sf('get', chart, 'chart.rtwInfo.reusableOutputs');
instanceInfo.ExpressionableInputs = sf('get', chart, 'chart.rtwInfo.expressionableInputs');
instanceInfo.Inline = yes_or_no(sf('get', chart, 'chart.rtwInfo.chartWhollyInlinable'));
instanceInfo.sfSymbols = sf('get', chart, 'chart.rtwInfo.sfSymbols');
instanceInfo.gatewayCannotBeInlinedMultipleTimes = sf('get', chart, 'chart.rtwInfo.gatewayCannotBeInlinedMultipleTimes');
instanceInfo.accessesMachineSymbols = sf('get', chart, 'chart.rtwInfo.accessesMachineSymbols');
instanceInfo.outputEventsWithMultipleCallers = sf('get', chart, 'chart.rtwInfo.outputEventsWithMultipleCallers');
instanceInfo.dworkInfo = sf('get', chart, 'chart.rtwInfo.dWorkVarInfo');
instanceInfo.dworkChecksum = CGXE.Utils.md5(rmfield(instanceInfo.dworkInfo, {'varName', 'typeSize', 'isOpaque', 'bitWidth', 'description', 'extModeUpload', 'isTestPoint', 'objName', 'path', 'resolveToSignalObject', 'srcLocation'}));
instanceInfo.requiredStackSize = sf('get', chart, 'chart.rtwInfo.requiredStackSize');
instanceInfo.thirdPartyUses = spec_third_party_uses;
instanceInfo.updateBuildInfoArgs = spec_updateBuildInfo_args;
instanceInfo.chartUsesVerify = sf('get', chart, 'chart.sfunInfo.chartUsesVerify');

function c = uniqueCell(c)
% Behaves like a UNIQUE command but on a cell array.
duplicateIdx = [];
for i = 1:numel(c)
if ~any(i == duplicateIdx)
for j = i+1:numel(c)
if isequal(c(i),c(j))
duplicateIdx = [duplicateIdx j]; %#ok<AGROW>
end
end
end
end
c(duplicateIdx) = [];