-
Notifications
You must be signed in to change notification settings - Fork 18
Reproducible builds for CMake projects migrated from the IDE
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
Let's slightly tweak the original project so that we get its binary firmware:
- Go to Project → Options → Output 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.
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 (Project → Create New Project... → Toolchain: CMake for Arm → Empty 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 Project → Add 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>>
.
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.
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
For deeper inspection, it is recommended to generate the compiler's listings.
- In the IDE project: In the project options, enable C/C++ Compiler → List → Output 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.
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
This article highlighted relevant hot spots to consider when migrating from a classic IDE project to a CMake-enabled project using the IAR platform.
This is the cmake-tutorial wiki. Back to Wiki Home
- Setting language-specific target options
- Selecting build types
- Using Ninja Multi-Config
- Filing a build log
- Multi-file compilation
- Invoking IAR binary utilities
- Use the IAR ELF Tool to convert executable targets to their binary formats