Main Content

Deploy Component Algorithm as Component Model Library by Using CMake

This example shows how to use the CMake approach of building code from a component algorithm model for deploying 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. The example guides you through these steps:

  1. Open the model.

  2. Generate component code from the model.

  3. Implement handwritten integration code.

  4. Configure CMake for your target environment.

  5. Generate build artifacts and build integrated code.

  6. Run the executable program.

Open Model

Open example model ComponentDeploymentFcn.slx.

open_system('ComponentDeploymentFcn');

Generate Component Code from Model

  1. Configure your target platform for the code generator.

  2. Generate source code.

  3. Create a CMakeLists.txt file for the component model library.

  4. 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);

Generate Code

slbuild('ComponentDeploymentFcn', 'GenerateCodeOnly', true)
### Starting build procedure for: ComponentDeploymentFcn
### Successful completion of code generation for: ComponentDeploymentFcn

Build Summary

Top model targets built:

Model                   Action           Rebuild Reason                                    
===========================================================================================
ComponentDeploymentFcn  Code generated.  Code generation information file does not exist.  

1 of 1 models built (0 models already up to date)
Build duration: 0h 0m 40.442s

Create CMakeLists.txt File for Component Model Library

codebuild('ComponentDeploymentFcn_ert_rtw/services/lib', 'BuildMethod', 'cmake');

Package Generated Files

To relocate files to another development environment, use the packNGo utility to package generated code, build artifact, and CMakeLists.txt files into a ZIP file.

%% Optional
packNGo('ComponentDeploymentFcn_ert_rtw', 'packType', 'hierarchical', 'nestedZipFiles', false);
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 the src subfolder of your code generation folder (codeGenerationFolder/src/). 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 uint32_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

Create handwritten code that provides implementations of the target platform services in the src subfolder of your code generation folder (codeGenerationFolder/src/). For this example, you can use the code in file services.c.

open('services.c');
#include "services.h"
 
#include <stdio.h>
 
uint32_T tick = 0;
 
static real_T DataTransfer_storage = 0;
 
static real_T input_storage = 0;
static real_T NVM_storage = 100;
static real_T output_storage = 0;
 
 
/* timer services */
uint32_T get_tick_outside_CD_integrator(void) {
    printf("Service function called: get_tick_outside_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;
}
 
real_T * set_CD_integrator_DataTransfer(void) {
    printf("Service function called: set_CD_integrator_DataTransfer.\n");
    return &DataTransfer_storage;
}
 
real_T * set_ModelInitialize_DataTransfer(void) {
    printf("Service function called: set_ModelInitialize_DataTransfer.\n");
    return &DataTransfer_storage;
}
 
/* receiver services */
const real_T * get_CD_integrator_InBus_u(void) {
    printf("Service function called: get_CD_integrator_InBus_u.\n");
    return &input_storage;
}
 
const real_T * get_CD_initialize_InBus_NVM(void) {
    printf("Service function called: get_CD_initialize_InBus_NVM.\n");
    return &NVM_storage;
}
 
/* sender services */
real_T * getref_CD_accumulator_OutBus_y(void) {
    printf("Service function called: getref_CD_accumulator_OutBus_y.\n");
    return &output_storage;
}
 
real_T * getref_CD_terminate_OutBus_NVM(void) {
    printf("Service function called: getref_CD_terminate_OutBus_NVM.\n");
    return &NVM_storage;
}

CMakeLists.txt

Create a CMakeLists.txt file in your code generation folder (codeGenerationFolder). 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
    src/ComponentDeploymentFcnIntegration.c
    src/services.c)
  
# Specify that the executable target "ComponentDeploymentFcnIntegration"
# incorporating the handwritten code should be linked against the the
# "ComponentDeploymentFcn" library target containing the code
# generated from the Simulink model.
target_link_libraries(ComponentDeploymentFcnIntegration PRIVATE ComponentDeploymentFcn)

Configure CMake

Configure the CMake command and arguments for your target environment. You can use 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. In codeGenerationFolder, run these commmands:

[status1, cmdout1] = system([cmakeCommand ' -S . -B build ', cmakeArguments], '-echo');
[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)