%% Relocate Code to Another Development Environment with packNGo
%
% Use the code generator packNGo option to
% generate code in one location. After code generation, 
% collect the compiler command-line options, move the generated code 
% to another location, and compile the generated code in combination 
% with custom code.

%% Check Platform and Set Up Path and Folders
% For this example, you must have the gcc toolchain running on a Linux(R) platform.
% After verifying this platform, this example sets up the path, 
% folders, and files.
%
% When you rerun the example, to ease clean up of generated files, 
% the work folders (|code_generation_area_dir| and |build_area_dir|) 
% are separate from the source files (|models_dir| and |hand_code_dir|).
%
% All locations of source file and folders use paths that are relative to the 
% folder containing the example .M file.

if ismac
    % Code to run on Mac platform
	disp('Platform (Macintosh) not supported.')	
elseif ispc
    % Code to run on Windows platform
	disp('Platform (Windows) not supported.')
elseif isunix
originalFolder = pwd;
thisFilesFullName = mfilename( 'fullpath' );
thisFilesAbsolutePath = fileparts( thisFilesFullName );
modelsFolder = fullfile( thisFilesAbsolutePath, 'models_dir' );
codeGenerationRootFolder = ...
	fullfile( thisFilesAbsolutePath, 'code_generation_area_dir' );
buildAreaRootFolder = ...
	fullfile( thisFilesAbsolutePath, 'build_area_dir' );
handCodeFolder = ...
	fullfile( thisFilesAbsolutePath, 'hand_code_dir' );

%% Clean Up
% To eliminate build artifacts from previously running the example,
% this example searches for and eliminates folders containing these 
% by-products of previous runs.  

if( 0 < exist( codeGenerationRootFolder, 'dir' ) )
	clear mex;
	rmdir( codeGenerationRootFolder, 's' );
else
% No previous codeGenerationRootFolder.
end
mkdir( codeGenerationRootFolder );

if( 0 < exist( buildAreaRootFolder, 'dir' ) )
	clear mex;
	rmdir( buildAreaRootFolder, 's' );
else
% No previous buildAreaRootFolder.
end
mkdir( buildAreaRootFolder );
if( 0 < exist( modelsFolder, 'dir' ) )
	clear mex;
	rmdir( modelsFolder, 's' );
else
% No previous modelsFolder.
end
mkdir( modelsFolder );
copyfile((fullfile(matlabroot,'toolbox','rtw','rtwdemos','rtwdemo_packngo.slx')), ...
    (fullfile(modelsFolder,'rtwdemo_packngo.slx')),'f');
if( 0 < exist( handCodeFolder, 'dir' ) )
	clear mex;
	rmdir( handCodeFolder, 's' );
else
% No previous handCodeFolder.
end
mkdir( handCodeFolder );
copyfile((fullfile(matlabroot,'toolbox','rtw','rtwdemos','rtwdemo_packngo','main.c')), ...
    (fullfile(handCodeFolder,'main.c')),'f');
copyfile((fullfile(matlabroot,'toolbox','rtw','rtwdemos','rtwdemo_packngo','main.h')), ...
    (fullfile(handCodeFolder,'main.h')),'f');
directoriesToAddToMatlabPathCellStr = ...
	{ ...
	thisFilesAbsolutePath, ...
	modelsFolder ...
	};
for directoryIndex = 1 : numel( directoriesToAddToMatlabPathCellStr )
    currentDirectory = directoriesToAddToMatlabPathCellStr{ directoryIndex };
    addpath( currentDirectory );
end

%% Generate Code and Output a .zip File with packNGo
% In this example, the code generation configuration for the |rtwdemo_packngo| model 
% enables the *Generate code only* parameter (|GenCodeOnly|) 
% and enables the *Package code and artifacts* parameter (|PackageGeneratedCodeAndArtifacts|).
% These options direct the code generator to create a |packNGo| archive 
% (|.zip| file) and stop the build process without compiling the 
% generated code. Compilation occurs later with integrated 
% custom code that replaces the generated |main.c| file.

cd( codeGenerationRootFolder );
modelBaseName = 'rtwdemo_packngo';
open_system( modelBaseName );
slbuild( modelBaseName );
close_system( modelBaseName );

%% Extract Build Information from Makefile
% When using the toolchain approach for the build process, you can extract 
% build information from the generated makefile. The information includes 
% source files, dependency files, and macro definitions and values that appear 
% in the makefile. With current working folder set to the folder containing 
% the generated makefile, rtwdemo_packngo.mk, in the Command Window, type:

cd( 'rtwdemo_packngo_grt_rtw' );
system('gmake -f rtwdemo_packngo.mk info')

%%
% For information about performing a similar operation on Windows(R)
% platforms, see the |model.bat| description in <docid:rtw_ug.brufp8r-17 Manage Build Process Files>.
%

%% Move Generated Code and Custom Code to Common Location for Build Process
% To integrate the generated code and custom code, this example moves the 
% files to a common location for the build process. The archive from the packNGo  
% option is unzipped.

cd( buildAreaRootFolder );
generatedPackNGoArchiveShortName = ...
	[ modelBaseName, '.zip' ];
filesToCopyCellStr = ...
	{ ...
	fullfile( codeGenerationRootFolder, generatedPackNGoArchiveShortName ), ...
	fullfile( handCodeFolder, 'main.h' ), ...
	fullfile( handCodeFolder, 'main.c' ) ...
	};
fileCopyFunctionHandle = ...
	@( sourceFileFullName )copyfile( sourceFileFullName, buildAreaRootFolder );
for sourceFileIndex = 1 : numel( filesToCopyCellStr )
    currentSourceFile = filesToCopyCellStr{ sourceFileIndex };
    fileCopyFunctionHandle( currentSourceFile );
end
generatedPackNGoArchiveDestinationFullName = ...
	fullfile( buildAreaRootFolder, generatedPackNGoArchiveShortName );
unzip( generatedPackNGoArchiveDestinationFullName );

%% Construct the Build Command
% To compile the generated code and integrated custom code, this example
% constructs a build command for the gcc compiler and tools that:
% 
% * Compiles the generated and custom source files
% * Specifies the required folders where the preprocessor can locate
%   included header files
% * Provides the correct |-D| arguments for preprocessor macros that 
%   the generated code requires

%% Identify the Source Files for the Build Command
% The generated source files are in a folder extracted from the
% packNGo archive (|.zip| file). The file name combines the:
%
% * Model name
% * System target file (selected in the model configuration set)
% * Original code generation folder

generatedCodeSubFolderName = ...
	[ modelBaseName, '_grt_rtw' ];
generatedCodeFolderFullName = ...
	fullfile( buildAreaRootFolder, 'code_generation_area_dir', generatedCodeSubFolderName );
sharedCodeFolderFullName = fullfile( buildAreaRootFolder, 'code_generation_area_dir', 'slprj', 'grt', '_sharedutils');
sourceFileFullNamesCellStr = ...
	{ ...
	fullfile( buildAreaRootFolder, 'main.c' ), ...
	fullfile( generatedCodeFolderFullName, 'rtwdemo_packngo.c' ) ...
	};

%% Identify the Header File Folders
% The required header file folders include:
%
% * Custom header file locations
% * Generated header file locations
% * Locations that contain files that the packNGo operation copied 
% from the MATLAB installation folders to the packNGo
% archive

[ ~, matlabReleaseFolderName ] = fileparts( matlabroot() );
headerFileDirectoriesCellStr = ...
	{ ...
	buildAreaRootFolder, ...
	generatedCodeFolderFullName, ...
    sharedCodeFolderFullName, ...
	fullfile( buildAreaRootFolder, matlabReleaseFolderName, 'extern', 'include' ), ...
	fullfile( buildAreaRootFolder, matlabReleaseFolderName, 'simulink', 'include' ) ...
	};

%% Identify Required Preprocessor Macros
% There are a number of preprocessor macros that must be defined to
% guide conditional compilation in the code. The macro names and values
% can be copied from the |defines.txt| file in the generated code
% folder.

commandLineDefinesCellStr = ...
	{ ...
	'MODEL=rtwdemo_packngo', ...
	'NUMST=1', ...
	'NCSTATES=0', ...
	'HAVESTDIO', ...
	'RT', ...
	'USE_RTMODEL', ...
	'UNIX', ...
	'MAT_FILE=0', ...
	'INTEGER_CODE=0', ...
	'MT=0', ...
	'CLASSIC_INTERFACE=0', ...
	'ALLOCATIONFCN=0', ...
	'ONESTEPFCN=1', ...
	'TERMFCN=1', ...
	'MULTI_INSTANCE_CODE=0', ...
	'TID01EQ=0' ...
	};

%% Prepare Cell Array Data as Strings
% After gathering the source, header, folder, and macro information for 
% the build command into cell array data, this example constructs the 
% build command by reorganizing this data as properly-formed strings.
% The strings serve as part of a compilation command.

commandLineDefinesCompilerArgumentString = ...
	strjoin( strcat( '-D', commandLineDefinesCellStr ), ' ' );
sourceFilesCompilerArgumentString = ...
    ['"' strjoin( sourceFileFullNamesCellStr, '" "' ) '"'];
inclusionDirectoriesCompilerArgumentString = ...
    ['-I"' strjoin(headerFileDirectoriesCellStr, '" -I"') '"'];
compilationCommandString = ...
	[ ...
	'gcc', ' ', ...
	'-v', ' ', ...
	sourceFilesCompilerArgumentString, ' ', ...
	commandLineDefinesCompilerArgumentString, ' ', ...
	inclusionDirectoriesCompilerArgumentString ...
	];

%% Run the Compilation Command
% The compilation command is:

[ compilationResult, compilationOutput ] = ...
	system( compilationCommandString );

%% View the Compilation Results
% The results use operating system conventions for the return values 
% because "system(...)" returns the results from the operating system. 
% As a result, "0" indicates success, instead of "1"/"true.

assert( ( 0 == compilationResult ), compilationOutput );
disp( compilationOutput );

%% Call the Integrated, Compiled Executable
% Running the compile executable produces this output:

[ executionResult, executionOutput ] = system( './a.out' );
assert( ( 0 == executionResult ), executionOutput );
disp( executionOutput );
cd( originalFolder );

%% Considerations for Relocating Code to Other Platforms or Toolchains
% This example shows how to use packNGo to relocate code and integrate
% custom code with generated code. These techniques ease relocating generated
% code to other platforms (for example, Linux(R) to Windows(R)). The sections
% of this example that show how to construct the build command provide
% information that supports code relocation.
% 
% The build command on the target platform (for example, Microsoft Visual C/C++
% MSVC on Windows(R)) is as close as possible to the build tool flags and 
% definitions in the build command on the source platform (for example, gcc on 
% Linux(R)). Some flags and definitions are required (for example, list of source 
% and dependency files). Other flags are optional (for example, compiler 
% optimizations vary among compilers).

else
    disp('Platform not supported.')
end


%% 
% Copyright 2012-2019 The MathWorks, Inc.