Deploy Component Algorithm as Component Model Library by Using CMake
Use the CMake approach to build code generated from a component algorithm model, and deploy the algorithm code as a component model library. CMake is a third-party, open-source tool for build process management. CMake uses configuration files (CMakeLists.txt
) to generate standard build files for native build environments such as, makefiles, Ninja files, or Microsoft® Visual Studio® projects. This example guides you through these steps:
Open the model.
Generate component code from the model.
Implement handwritten integration code.
Configure CMake for your target environment.
Generate build artifacts and build integrated code.
Run the executable program.
Open Model
Open example model ComponentDeploymentFcn.slx
.
open_system('ComponentDeploymentFcn');
Generate Component Code from Model
Configure your target platform for the code generator.
Generate source code.
Create a
CMakeLists.tx
t file for the component model library.Optionally, package generated code files and
CMakeLists.txt
files into a ZIP file that you can use to relocate the files.
Configure Model Target Environment Device Type
Inform the code generator about the C language constraints for your target platform by defining a workspace variable (prodHWDeviceType
) that you can use for setting model configuration parameter ProdHWDeviceType
.
switch computer('arch') case 'win64' prodHWDeviceType = 'Intel->x86-64 (Windows64)'; case 'glnxa64' prodHWDeviceType = 'Intel->x86-64 (Linux 64)'; case 'maci64' prodHWDeviceType = 'Intel->x86-64 (Mac OS X)'; case 'maca64' prodHWDeviceType = 'Apple->ARM64'; otherwise assert(false, 'Platform not supported'); end set_param('ComponentDeploymentFcn', 'ProdHWDeviceType', prodHWDeviceType); set_param('ComponentDeploymentFcn', 'ProdLongLongMode', 'on');
Generate Code and CMakeLists.txt for Component Model Library
To generate a platform-independent CMakeLists.txt
file, specify the CMake
toolchain.
set_param('ComponentDeploymentFcn', 'Toolchain', 'CMake');
Specify the creation of a ZIP file after code generation. The packNGo
function packages the generated source code and CMake configuration file in the ZIP file.
set_param('ComponentDeploymentFcn', 'PackageGeneratedCodeAndArtifacts', 'on');
Generate code.
slbuild('ComponentDeploymentFcn')
### Starting build procedure for: ComponentDeploymentFcn ### Successful completion of code generation for: ComponentDeploymentFcn Build Summary Top model targets: Model Build Reason Status Build Duration ============================================================================================================ ComponentDeploymentFcn Information cache folder or artifacts were missing. Code generated. 0h 0m 13.454s 1 of 1 models built (0 models already up to date) Build duration: 0h 0m 15.324s
In the services
subfolder beneath the build folder, the software generates a header file services.h
. The file defines interfaces to services that you must implement for an application containing the generated component.
dir(fullfile('ComponentDeploymentFcn_ert_rtw', 'services', 'services.h'))
services.h
In the services/lib
subfolder beneath the build folder, the software generates a CMakeLists.txt
file that you can use to build the generated code into a static library.
dir(fullfile('ComponentDeploymentFcn_ert_rtw', 'services', 'lib', 'CMakeLists.txt'))
CMakeLists.txt
(Optional) Relocate Generated Files to Another Development Environment
You can relocate the ZIP file, which contains the generated code, build artifact, and CMakeLists.txt
files to another development environment.
%% Optional copyfile('ComponentDeploymentFcn.zip', 'path-for-destination'); cd path-for-destination unzip('ComponentDeploymentFcn.zip');
Implement Handwritten Integration Code
main function
Implement handwritten code for a main
function in your code generation folder (codeGenerationFolder/)
. For this example, you can use the function in file ComponentDeploymentFcnIntegration.c
.
open('ComponentDeploymentFcnIntegration.c');
#include "ComponentDeploymentFcn.h" #include <stddef.h> #include <stdio.h> extern uint64_T tick; // Variable used by timer service implementation const size_t nIter = 3; int main(void) { // Run model initialize function printf("\nRunning model initialize function\n"); CD_initialize(); for (size_t k = 0; k < nIter; ++k) { // Run periodic Accumulator function at every iteration printf("\nRunning Accumulator function (iteration %llu)\n", k); CD_accumulator(); if (k == 1) { // The asynchronous Integrator function only runs when k == 1 printf("\nRunning Integrator function (iteration %llu)\n", k); CD_integrator(); } // Increment timer tick count tick++; } // Run model terminate function printf("\nRunning model terminate function\n"); CD_terminate(); }
services.c
In your code generation folder (codeGenerationFolder
/)
, create handwritten code that provides implementations of the target platform services declared in services.h
. For this example, you can use the code in file services.c
.
open('services.c');
#include "services.h" #include <stdio.h> uint64_T tick = 0; static real_T DataTransfer_storage[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; static real_T input_storage[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; static real_T NVM_storage[10] = {100, 100, 100, 100, 100, 100, 100, 100, 100, 100}; static real_T output_storage[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; /* timer services */ uint64_T get_tick_outside_tick_CD_integrator(void) { printf("Service function called: get_tick_outside_tick_CD_integrator.\n"); return tick; } /* data transfer services */ const real_T * get_CD_accumulator_DataTransfer(void) { printf("Service function called: get_CD_accumulator_DataTransfer.\n"); return &DataTransfer_storage[0]; } real_T * set_CD_integrator_DataTransfer(void) { printf("Service function called: set_CD_integrator_DataTransfer.\n"); return &DataTransfer_storage[0]; } real_T * set_CD_initialize_DataTransfer(void) { printf("Service function called: set_CD_initialize_DataTransfer.\n"); return &DataTransfer_storage[0]; } /* receiver services */ const real_T * get_CD_integrator_InBus_u(void) { printf("Service function called: get_CD_integrator_InBus_u.\n"); return &input_storage[0]; } const real_T * get_CD_initialize_InBus_NVM(void) { printf("Service function called: get_CD_initialize_InBus_NVM.\n"); return &NVM_storage[0]; } /* sender services */ real_T * getref_CD_accumulator_OutBus_y(void) { printf("Service function called: getref_CD_accumulator_OutBus_y.\n"); return &output_storage[0]; } real_T * getref_CD_terminate_OutBus_NVM(void) { printf("Service function called: getref_CD_terminate_OutBus_NVM.\n"); return &NVM_storage[0]; }
CMakeLists.txt
To produce an executable application that incorporates the handwritten main function and service implementations, and links with the library generated from the Simulink model, in your code generation folder (codeGenerationFolder
), create a handwritten CMakeLists.txt
file. The file must perform these tasks:
Import the previously generated
CMakeLists.txt
file that contains the rules to build the static library from the component code.Add a rule to build the handwritten source files
ComponentDeploymentFcnIntegration.c
andservices.c
into an executable program.Link the executable program with the static library that contains the compiled component code.
For this example, you can use the provided CMakeLists.txt
file.
cmake_minimum_required(VERSION 3.12) # Set a name for the project project(ComponentDeploymentFcnIntegration) # Use the "add_subdirectory" command to reference the CMakeLists.txt file # that was generated by codebuild for the code generated from # the Simulink model. This has defined a static library target # named "ComponentDeploymentFcn" which we will link into # the application further down. add_subdirectory(ComponentDeploymentFcn_ert_rtw/services/lib) # Use the "add_executable" command to define an executable target named # "ComponentDeploymentFcnIntegration" that is built from the hand-written # source files. add_executable(ComponentDeploymentFcnIntegration ComponentDeploymentFcnIntegration.c services.c) # Specify that the executable target "ComponentDeploymentFcnIntegration" # incorporating the handwritten code should be linked against the # "ComponentDeploymentFcn" library target containing the code # generated from the Simulink model. target_link_libraries(ComponentDeploymentFcnIntegration ComponentDeploymentFcn)
Configure CMake
Configure the CMake command and arguments for your target environment. In the MATLAB Command Window, run one of these examples.
Windows - Microsoft Visual Studio 2017 Solution
cmakeCommand = ['"', fullfile(matlabroot, 'bin', 'win64', 'cmake', 'bin', 'cmake.exe'), '"']; cmakeArguments = '-G "Visual Studio 15 2017"';
Windows - Microsoft Visual Studio 2019 Solution
cmakeCommand = ['"', fullfile(matlabroot, 'bin', 'win64', 'cmake', 'bin', 'cmake.exe'), '"']; cmakeArguments = '-G "Visual Studio 16 2019"';
Windows - Microsoft Visual Studio with NMake
[status, msvcInstall] = system('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest -property installationPath'); msvcInstall = strtrim(msvcInstall); assert(status == 0 && ~isempty(msvcInstall), 'Could not find location of Microsoft Visual Studio installation'); vcvarsAll = fullfile(msvcInstall, 'VC', 'Auxiliary', 'Build', 'vcvarsall.bat'); envSetupCommand = ['"' vcvarsAll '" amd64 && ']; cmakeCommand = [envSetupCommand, '"', fullfile(matlabroot, 'bin', 'win64', 'cmake', 'bin', 'cmake.exe'), '"']; cmakeArguments = '-G "NMake Makefiles"';
Windows - MinGW
mingwLoc = getenv('MW_MINGW64_LOC'); assert(~isempty(mingwLoc), 'Could not find location of MinGW installation'); mingwLocBin = [mingwLoc, '/bin']; envSetupCommand = ['set PATH=', mingwLocBin, ';%PATH% && ']; cmakeCommand = [envSetupCommand, '"', fullfile(matlabroot, 'bin', 'win64', 'cmake', 'bin', 'cmake.exe'), '"']; cmakeArguments = ['-G "MinGW Makefiles" ', ... '-DCMAKE_C_COMPILER="gcc.exe" ', ... '-DCMAKE_CXX_COMPILER="g++.exe"'];
Linux - GCC and GMake
cmakeCommand = ['"', fullfile(matlabroot, 'bin', 'glnxa64', 'cmake', 'bin', 'cmake'), '"']; cmakeArguments = '-G "Unix Makefiles"';
Mac - XCode with Clang
cmakeCommand = ['"', fullfile(matlabroot, 'bin', computer('arch'), 'cmake', 'bin', 'cmake'), '"']; cmakeArguments = '-G "Xcode"';
Generate Build Artifacts and Build Integrated Code
To generate required build artifacts and build the integrated code, run CMake. The CMake tool processes the CMakeLists.txt
file in the folder specified through the -S
flag, and generates the configuration files for your build system in the folder that you specify through the -B
flag. In codeGenerationFolder
, run this commmand:
[status1, cmdout1] = system([cmakeCommand ' -S . -B build ', cmakeArguments], '-echo');
Using the --build
flag, build the application.
[status2, cmdout2] = system([cmakeCommand ' --build build'], '-echo');
The location of the executable program file that the CMake generator produces depends on the CMake generator that you use.
Makefile based CMake generator - Relative to your current working folder, places the executable program file in subfolder
build
.Project-based CMake generator - Relative to your current working folder, places the executable program file in folder
build/Debug
.
Windows - Visual Studio Solution
exeLocation = fullfile('build', 'Debug', 'ComponentDeploymentFcnIntegration.exe');
Windows - NMake Makefiles or GMake Makefiles
exeLocation = fullfile('build', 'ComponentDeploymentFcnIntegration.exe');
Linux - GMake Makefiles
exeLocation = fullfile('build', 'ComponentDeploymentFcnIntegration');
Mac - Xcode project
exeLocation = fullfile('build', 'Debug', 'ComponentDeploymentFcnIntegration');
Run Executable Program
Use this command to run the compiled executable program:
[status3, cmdout3] = system(exeLocation, '-echo');
Example output:
Running model initialize function Service function called: set_ModelInitialize_DataTransfer. Service function called: get_CD_initialize_InBus_NVM. Running Accumulator function (iteration 0) Service function called: get_CD_accumulator_DataTransfer. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Running Accumulator function (iteration 1) Service function called: get_CD_accumulator_DataTransfer. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Running Integrator function (iteration 1) Service function called: set_CD_integrator_DataTransfer. Service function called: get_tick_outside_CD_integrator. Service function called: get_CD_integrator_InBus_u. Service function called: get_CD_integrator_InBus_u. Service function called: get_CD_integrator_InBus_u. Service function called: get_CD_integrator_InBus_u. Service function called: get_CD_integrator_InBus_u. Service function called: get_CD_integrator_InBus_u. Service function called: get_CD_integrator_InBus_u. Service function called: get_CD_integrator_InBus_u. Service function called: get_CD_integrator_InBus_u. Service function called: get_CD_integrator_InBus_u. Running Accumulator function (iteration 2) Service function called: get_CD_accumulator_DataTransfer. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Service function called: getref_CD_accumulator_OutBus_y. Running model terminate function Service function called: getref_CD_terminate_OutBus_NVM.
close_system('ComponentDeploymentFcn',0)