cancel
Showing results for 
Search instead for 
Did you mean: 

How to add post build commands in VS Code

B.Montanari
ST Employee

Summary

This article explains how to add post build commands in VS Code with CMake for STM32 projects.

Introduction

When developing STM32 firmware using Visual Studio Code with the CMake extension, the build process often involves more than simply compiling source code into an executable .elf file. Post build steps are essential for preparing firmware for deployment, debugging, security, and production.

Some key post build tasks include:

  • Generating *.bin and *.hex files from the *.elf output for flashing and compatibility with various tools.
  • Signing binaries to meet security requirements, such as those for STM32N6 series devices.
  • Preparing custom loader files for external memory programming.

Furthermore, many other post build tasks can be implemented to tailor your development process in a better way.

This article provides a comprehensive guide on adding and customizing post build commands in your CMakeLists.txt file within VS Code, including practical examples designed to streamline your STM32 development workflow.

Prerequisites

Before you begin, ensure the following:

  1. STM32Cube for Visual Studio Code extension is installed. 
  2. CMake is installed and properly configured in your development environment with the CMake Tools extension.
  3. The Arm GCC toolchain (arm-none-eabi-gcc) is installed, as it includes the arm-none-eabi-objcopy tool required to generate the files.
  4. STM32CubeProgrammer is installed (required for flashing and signing).
  5. You have a CMake-based STM32 project set up in VS Code.

1. Understanding the build process

When you build an STM32 project with CMake, the default output is an .elf file (for example, project.elf). This file is a binary format that contains all the necessary information for debugging, linking, and execution. The .elf file can be used directly for programming and debugging, even without the project’s source code. However, it is not commonly used in production environments due to the presence of additional metadata that is unnecessary for flashing and may increase file size.

To prepare firmware for production, it is standard practice to convert the .elf file into simpler binary formats such as:

  • .bin: A raw binary file containing only the program data, stripped of metadata.
  • .hex: An Intel HEX file format that organizes binary data in a human-readable text format, widely accepted by flashing tools.

Common programming tools like STM32CubeProgrammer typically accept only these simpler binary formats (.bin and .hex), making conversion essential for deployment.

This conversion can be performed using the arm-none-eabi-objcopy tool, part of the Arm GCC toolchain. You can configure your CMakeLists.txt file to call this tool automatically after the build process, ensuring that .bin and .hex files are generated every time you build your project.

Pseudocode of a post build command in CMake:

add_custom_command(
    TARGET <target_name>
    POST_BUILD
    COMMAND <command_to_run>
    COMMENT "<comment_text>"
)
  • TARGET: The build target (usually your project name).
  • POST_BUILD: Specifies the command runs after the build finishes successfully.
  • COMMAND: The actual shell/console command to execute.
  • COMMENT: Descriptive message shown in build output.

2. Adding post build commands to CMakeLists.txt

Import your project and locate the CMakeLists.txt file in the root of your STM32 project.

BMontanari_0-1754074931286.png

Insert the following code snippet at the end of your CMakeLists.txt file:

# Specify the output ELF file
set(TARGET_ELF "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.elf")
# Command to generate .bin file
add_custom_command(
    TARGET ${CMAKE_PROJECT_NAME}
    POST_BUILD
    COMMAND ${CMAKE_OBJCOPY}
-O binary ${TARGET_ELF} ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin
    COMMENT "Generating .bin file"
)

# Command to generate .hex file
add_custom_command(
    TARGET ${CMAKE_PROJECT_NAME}
    POST_BUILD
    COMMAND ${CMAKE_OBJCOPY}
-O ihex ${TARGET_ELF} ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.hex
    COMMENT "Generating .hex file"
)

2.1 Explanation of the CMakeLists.txt modifications

  1. set(TARGET_ELF...)
    This line defines the path to the ELF file generated during the build process. The ${CMAKE_BINARY_DIR} variable refers to the directory where CMake stores build outputs. ${CMAKE_PROJECT_NAME} is the name of your project (defined earlier in the CMakeLists.txt file, in this case, “EXTI_ToggleLedOnIT_Init”).
  2. add_custom_command()
    This function defines a custom command to be executed during the build process.
    • TARGET ${CMAKE_PROJECT_NAME}: Associates the custom command with the build target (your STM32 project).
    • POST_BUILD: Specifies that the command should run after the build process is complete.
    • COMMAND ${CMAKE_OBJCOPY} ...: Executes the arm-none-eabi-objcopy tool to generate the desired output files.
      • -O binary: Specifies that the output format should be a binary file (.bin).
      • -O ihex: Specifies that the output format should be an Intel HEX file (.hex).
    • COMMENT: Adds a message to the build output for easier debugging and tracking.
  3. ${CMAKE_OBJCOPY}
    This variable represents the path to the arm-none-eabi-objcopy tool. It is typically defined in your toolchain file.

3. Verifying the output

Build the project by using the [Build] button at the lower left corner and after the build completes, navigate to the [build/] directory to verify that the *.elf, *.bin and *.hex files have been generated.

BMontanari_1-1754074931295.png

After generating the .bin or .hex files, you can flash them to your STM32 microcontroller using STM32CubeProgrammer to validate the code at runtime.

4. Advanced use case: binary signing for the STM32N6 series

On STM32N6 MCUs, the first stage bootloader (FSBL) must be signed so the boot ROM can execute it in a secured-locked state, and same is true for the application. More information can be found on our wiki page: Getting started with STM32N6 security - stm32mcu. However, the steps necessary to execute this process can become repetitive and time-consuming, especially when working on applications that require more than one binary. This portion of the article aims to introduce a simple post build script that allows for the entire signing process to occur after the build is performed.

4.1 Example: signing the FSBL

BMontanari_2-1754074931304.png

Since the signing tool will be called every time a new build is performed, it is mandatory to echo the "y" to replace the previous signed binary. A possible way to implement this is shown below, slightly editing the previous commands:

# Specify the output ELF file
set(TARGET_ELF "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.elf")
# Command to generate .bin file
add_custom_command(
    TARGET ${CMAKE_PROJECT_NAME}
    POST_BUILD
    COMMAND ${CMAKE_OBJCOPY}
-O binary ${TARGET_ELF} ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin
    COMMENT "Generating .bin file"
)

# Define the paths to the STM32 Signing Tool and the input/output files
set(SIGNING_TOOL "C:/Program Files/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_SigningTool_CLI.exe")

# Define the input and output files for FSBL
set(FSBL_INPUT "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin")
set(FSBL_OUTPUT "${CMAKE_BINARY_DIR}/FSBL-trusted.bin")

# Add a post-build step to sign the FSBL binary
add_custom_command(
    TARGET ${CMAKE_PROJECT_NAME}
    POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E echo y > input.txt
    COMMAND "${SIGNING_TOOL}" -bin "${FSBL_INPUT}" -nk -of 0x80000000 -t fsbl -o "${FSBL_OUTPUT}" -hv 2.3 -dump "${FSBL_OUTPUT}" < input.txt
    WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
    COMMENT "Signing FSBL binary: ${FSBL_INPUT} -> ${FSBL_OUTPUT}"
)

Similar to the FSBL signing process, the application firmware binaries can also be signed post-build to meet security requirements.

4.2 Example: signing the application firmware (Appli)

# Specify the output ELF file
set(TARGET_ELF "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.elf")
]# Command to generate .bin file
add_custom_command(
    TARGET ${CMAKE_PROJECT_NAME}
    POST_BUILD
    COMMAND ${CMAKE_OBJCOPY} -O binary ${TARGET_ELF} ${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin
    COMMENT "Generating .bin file"
)

# Define the paths to the STM32 Signing Tool and the input/output files
set(SIGNING_TOOL "C:/Program Files/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_SigningTool_CLI.exe")

# Define the input and output files for Appli
set(APPLI_INPUT "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin")
set(APPLI_OUTPUT "${CMAKE_BINARY_DIR}/Appli-trusted.bin")

# Add a post-build step to sign the Appli binary
add_custom_command(
    TARGET ${CMAKE_PROJECT_NAME}
    POST_BUILD
    # Step 1: Create a file with 'y'
    COMMAND ${CMAKE_COMMAND} -E echo y > "${CMAKE_BINARY_DIR}/input.txt"
    # Step 2: Run the signing tool, redirecting input from the file
    COMMAND "${SIGNING_TOOL}" -bin "${APPLI_INPUT}" -nk -of 0x80000000 -t fsbl -o "${APPLI_OUTPUT}" -hv 2.3 -dump "${APPLI_OUTPUT}" < "${CMAKE_BINARY_DIR}/input.txt"
    WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
    COMMENT "Signing Appli binary: ${APPLI_INPUT} -> ${APPLI_OUTPUT}"
)

4.3 Explanation of the CMakeLists.txt Modifications

  1. set(SIGNING_TOOL "...")
    This line defines the path to the STM32 signing tool executable.
    • "C:/Program Files/STMicroelectronics/STM32Cube/STM32CubeProgrammer/bin/STM32_SigningTool_CLI.exe" is the full path to the signing tool binary.
    • This tool is used to sign STM32 binaries to ensure their authenticity and integrity.
  2. set(APPLI_INPUT "...")
    This line defines the path to the input binary file that will be signed.
    • ${CMAKE_BINARY_DIR} refers to the directory where CMake places build outputs.
    • ${CMAKE_PROJECT_NAME} is the name of the current project.
    • The input file is expected to be a .bin file named after the project, for example, MyProject.bin.
  3. set(APPLI_OUTPUT "...")
    This line defines the path for the signed output binary file.
    • The signed binary will be saved as Appli-trusted.bin in the build directory.
  4. add_custom_command()
    This function adds a custom command to the build process.
    • TARGET ${CMAKE_PROJECT_NAME}: Associates this command with the build target (your STM32 project).
    • POST_BUILD: Specifies that this command runs after the build completes.
    • COMMAND "${SIGNING_TOOL}" ...: Runs the signing tool with the following options:
      • -bin ${APPLI _INPUT}: Specifies the input binary file to sign.
      • -nk: A flag for the signing tool (likely "no key" or a specific mode; refer to tool docs).
      • -of 0x80000000: Sets the offset/load address for the binary in memory.
      • -t fsbl: Specifies the binary type as FSBL (first stage bootloader).
      • -o ${APPLI _OUTPUT}: Specifies the output file path for the signed binary.
      • -hv 2.3: Specifies the hardware version or signing version.
      • -dump ${APPLI _OUTPUT}: Dumps the signed binary for verification or logging.
    • COMMENT: Adds a message to the build output to indicate the signing step, showing input and output files.

Conclusion

Post build commands are essential for automating repetitive tasks in STM32 development. By configuring them, you save time and simplify your workflow, ensuring your firmware is always ready for deployment with minimal effort.

Related links

Version history
Last update:
‎2025-08-06 1:27 AM
Updated by: