Skip to content

Reproducible builds for CMake projects migrated from the IDE

Felipe Torrezan edited this page Apr 23, 2025 · 2 revisions

Introduction

This article provides an outline for building a project originally created with the IAR Embedded Workbench IDE, when migrating to the CMake meta build system, in order to achieve the same output.

The IAR Embedded Workbench project used as an example is "Mixing C and Assembler Modules", which is available under the Product Explorer tutorials in the IDE's Information Center for Arm version 9.60.4.

Once that tutorial is completed, there should be 2 source files in the MixingCAndAssemblerModules project:

  • Fibonacci.c
  • Utilities.s

Helpful Resources

Procedure

Preparing the EWP project

Let's slightly tweak the original project so that we get its binary firmware:

  • Go to ProjectOptionsOutput Converter and enable Generate additional output setting it to Raw binary.
  • Invoke the context menu for the Build window and set the Filter Level to All.
  • Rebuild the project.
  • Inspect the command lines which were used for building the project.

Creating a CMake project

Now that we know the assembler, compiler and linker options used for building the application, let's create a CMake project from scratch using the IDE:

  • From the IDE, create a new empty CMake project (ProjectCreate New Project...Toolchain: CMake for ArmEmpty project) and save it as "MixingCandAssemblyModulesinCMake.ewp".
  • In the project folder, create a new CMakeLists.txt file and populate it with the following annotated project configuration:
# The CMake version used when creating this project
cmake_minimum_required(VERSION 4.0)

# Set CMake for cross-compiling
set(CMAKE_SYSTEM_NAME Generic)

# This will be used for setting the project's target options
set(This MixingCandAssemblyModules)

# The project name
project(${This} ASM C)

# Toolkit directory
cmake_path(GET CMAKE_C_COMPILER PARENT_PATH COMPILER_PATH)
set(TOOLKIT_DIR "${COMPILER_PATH}/..")

# Add the target executable
add_executable(${This})

# Set the target sources
target_sources(${This} PRIVATE
  Fibonacci.c
  Debug/List/Utilities.s
)

# Compiling options (covers general, C and ASM)
target_compile_options(${This} PRIVATE
  # General options
  --cpu=arm7tdmi
  --fpu=none
  # IAR C compiler options
  $<$<COMPILE_LANGUAGE:C>:
    --cpu_mode=thumb
    --dlib_config=normal
    -Ol
  >
  # IAR Assembler options
  $<$<COMPILE_LANGUAGE:ASM>:
    -s+
    -w+
  >
)

# IAR ILINK linker options
target_link_options(${This} PRIVATE
  --config=${TOOLKIT_DIR}/config/generic.icf
  --cpu=arm7tdmi
  --fpu=none
  --semihosting
  --map .
  --vfe
)

# Convert ${This}.elf to ${This}.bin
add_custom_command(
  TARGET ${This}
  POST_BUILD
  COMMAND ${CMAKE_IAR_ELFTOOL}
  --bin
  $<TARGET_FILE:${This}>
  $<TARGET_PROPERTY:${This},NAME>.bin
)
  • In the main menu, select ProjectAdd CMakeLists.txt to Project.

Note

Some default options, such as the IAR Assembler's -M<> were omitted for brevity. If desired, any omitted option can be explicitly added. For example, the omitted Assembler option can be explicitly entered using the following snippet $<$<COMPILE_LANGUAGE:ASM>:-M<$<ANGLE-R>>.

Inspecting the outputs

In the following examples, WSL/Ubuntu will be used for comparing outputs geared by simple command line tools. Feel free to make use of any other 3rd-party software of your preference that can hash and compare text files.

Comparing binaries

One easy way to compare the resulting binaries is to compare their hashes. Change to the project directory and perform:

$ find -iname "m*.bin" -exec sha256sum {} +
fa33f0347c47da2b7295b40c604768e7bd5571e46fe78f15c92b52d40f5218c7  ./.cmake_build/MixingCAndAssemblyModules.bin
fa33f0347c47da2b7295b40c604768e7bd5571e46fe78f15c92b52d40f5218c7  ./Debug/Exe/MixingCandAssemblerModules.bin

Comparing Compiler listings

For deeper inspection, it is recommended to generate the compiler's listings.

  • In the IDE project: In the project options, enable C/C++ CompilerListOutput List File with Assembler mnemonics and rebuild the project.
  • In the CMake project: append -lCN . to the C Compiler options in the CMakeLists.txt file and rebuild the project.
  $<$<COMPILE_LANGUAGE:C>:
    --cpu_mode=thumb
    --dlib_config=normal
    -Ol
    -lCN .
  >

Since we only have 1 compiler listing file (Fibonacci.lst) generated for each build, we can use the diff command to show the differences between them:

diff Debug/List/Fibonacci.lst .cmake_build/Fibonacci.lst
3c3
< # IAR ANSI C/C++ Compiler V9.60.4.438/W64 for ARM         22/Apr/2025  18:49:10
---
> # IAR ANSI C/C++ Compiler V9.60.4.438/W64 for ARM         22/Apr/2025  18:49:14
10,18c10,14
< #        -f D:\arm\tutorials\LanguageSupport\Debug\Obj\Fibonacci.o.rsp
< #        (D:\arm\tutorials\LanguageSupport\Fibonacci.c -lCN
< #        D:\arm\tutorials\LanguageSupport\Debug\List\ -o
< #        D:\arm\tutorials\LanguageSupport\Debug\Obj\ --no_cse --no_unroll
< #        --no_inline --no_code_motion --no_tbaa --no_clustering --no_scheduling
< #        --debug --endian=little --cpu=ARM7TDMI -e --fpu=None --dlib_config
< #        C:\iar\ewarm-9.60.4.11196\arm\inc\c\DLib_Config_Normal.h --cpu_mode
< #        thumb -Ol) --dependencies=n
< #        D:\arm\tutorials\LanguageSupport\Debug\Obj\Fibonacci.o.iar_deps
---
> #        --silent D:\arm\tutorials\LanguageSupport\Fibonacci.c
> #        "-DCMAKE_INTDIR=\"Debug\"" -r --cpu=arm7tdmi --fpu=none
> #        --cpu_mode=thumb --dlib_config=normal -Ol -lCN . --dependencies=ns
> #        CMakeFiles\MixingCAndAssemblyModules.dir\Debug\Fibonacci.o.d -o
> #        CMakeFiles\MixingCAndAssemblyModules.dir\Debug\Fibonacci.o
20,21c16
< #    List file         =
< #        D:\arm\tutorials\LanguageSupport\Debug\List\Fibonacci.lst
---
> #    List file         =  .\Fibonacci.lst
23c18
< #        D:\arm\tutorials\LanguageSupport\Debug\Obj\Fibonacci.o
---
> #        CMakeFiles\MixingCAndAssemblyModules.dir\Debug\Fibonacci.o

As observed, the generated assembly code was identical. The differences were limited to implicit IDE-generated options, such as the response file (-f), which we can safely omit in the CMake project, as well as differences in the path names. To minimize potential discrepancies further, you can explicitly include any additional required option(s).

Note

CMake automatically uses response files for generating build scripts when command lines exceed the operating system's size limit.

Comparing the Linker Map files

Finally let's compare both projects' map files:

$ diff Debug/List/MixingCandAsm.map .cmake_build/MixingCAndAssemblyModules.map
234,244c232,242
<                             0x413  0x42  Code  Gb  packbits_init_single.o [3]
< __iar_program_start         0x50c        Code  Gb  cstartup.o [3]
< __iar_return_from_swi       0x6a0   0x4  Code  Gb  xreturnswi.o [4]
< __iar_sh_stdout             0x488  0x40  Code  Gb  iarwstd.o [4]
< __iar_sh_write              0x4c8  0x44  Code  Gb  iarwrite.o [4]
< __iar_ttio_handles      0x10'0000   0x8  Data  Lc  XShttio.o [2]
< __low_level_init            0x5c5   0x4  Code  Gb  low_level_init.o [2]
< __vector                      0x0        Data  Gb  cstartup.o [3]
< __write                     0x454  0x10  Code  Gb  write.o [4]
< _call_main                  0x5ac        Code  Gb  cmain.o [3]
< _exit                       0x67c        Code  Gb  cexit.o [3]
---
>                             0x413  0x42  Code  Gb  packbits_init_single.o [4]
> __iar_program_start         0x50c        Code  Gb  cstartup.o [4]
> __iar_return_from_swi       0x6a0   0x4  Code  Gb  xreturnswi.o [5]
> __iar_sh_stdout             0x488  0x40  Code  Gb  iarwstd.o [5]
> __iar_sh_write              0x4c8  0x44  Code  Gb  iarwrite.o [5]
> __iar_ttio_handles      0x10'0000   0x8  Data  Lc  XShttio.o [3]
> __low_level_init            0x5c5   0x4  Code  Gb  low_level_init.o [3]
> __vector                      0x0        Data  Gb  cstartup.o [4]
> __write                     0x454  0x10  Code  Gb  write.o [5]
> _call_main                  0x5ac        Code  Gb  cmain.o [4]
> _exit                       0x67c        Code  Gb  cexit.o [4]
246c244
< exit                        0x651   0xa  Code  Gb  exit.o [2]
---
> exit                        0x651   0xa  Code  Gb  exit.o [3]
248c246
< putchar                     0x3f1  0x22  Code  Gb  putchar.o [2]
---
> putchar                     0x3f1  0x22  Code  Gb  putchar.o [3]
251,254c249,253
< [1] = D:\arm\tutorials\LanguageSupport\Debug\Obj
< [2] = dl4t_tln.a
< [3] = rt4t_al.a
< [4] = shs_l.a
---
> [1] = D:\arm\tutorials\LanguageSupport\.cmake_build\CMakeFiles\MixingCAndAssemblyModules.dir\Debug
> [2] = D:\arm\tutorials\LanguageSupport\.cmake_build\CMakeFiles\MixingCAndAssemblyModules.dir\Debug\Debug\List
> [3] = dl4t_tln.a
> [4] = rt4t_al.a
> [5] = shs_l.a

You will notice that having object files originating from two different folders affected the Entry List in the CMake project whereas such a difference was masked in the IDE project. In order to eliminate this numbering difference, copy the Debug/List/Utilities.s source file to the project folder and update target_sources() in the CMakeLists.txt accordingly.

target_sources(${This} PRIVATE
  Fibonacci.c
  Utilities.s
)

Then rebuild the project and check the map file once again to realize that the number of directories in the Entry List is now the same, differing only on their respective path names.

151c145
< D:\arm\tutorials\LanguageSupport\Debug\Obj: [1]
---
> D:\arm\tutorials\LanguageSupport\.cmake_build\CMakeFiles\MixingCAndAssemblyModules.dir\Debug: [1]
251c245
< [1] = D:\arm\tutorials\LanguageSupport\Debug\Obj
---
> [1] = D:\arm\tutorials\LanguageSupport\.cmake_build\CMakeFiles\MixingCAndAssemblyModules.dir\Debug

Summary

This article highlighted relevant hot spots to consider when migrating from a classic IDE project to a CMake-enabled project using the IAR platform.

Clone this wiki locally