Files
Sudeep Mohanty e364a60769 test(cmakev2): add pytest scripts for cmakev2 build system examples
Add pytest test coverage for cmakev2 build system examples that had
none.

CMakeLists.txt fixes required to enable testing:

conditional_component and plugins: added idf_build_generate_flasher_args()
since these use the low-level build API (idf_build_executable /
idf_flash_binary) which unlike idf_project_default() does not call it
automatically. Without it, flasher_args.json was missing from the build
output and pytest-embedded could not initialize the DUT.

multi_binary: both app1 and app2 were registered in the global flash
target via idf_flash_binary(...FLASH), creating a duplicate key at offset
0x10000 in the flasher_args.json generator expression and preventing the
file from being generated. Fixed by removing FLASH from app2's call so
only app1 is registered in the global flash target. idf_build_generate_flasher_args()
now produces a valid flasher_args.json with app1 as the default app binary.
The test patches the binary path to app2.bin when testing the second app.
2026-04-10 13:49:56 +02:00
..

Supported Targets ESP32 ESP32-C2 ESP32-C3 ESP32-C5 ESP32-C6 ESP32-C61 ESP32-H2 ESP32-H21 ESP32-H4 ESP32-P4 ESP32-S2 ESP32-S3 ESP32-S31

Multi-Binary Example: Multiple Apps with Different Component Sets

This example demonstrates Build System v2's capability to build multiple independent firmware binaries from a single project in one build command. Each binary has its own entry point component with different dependencies.

Overview

Two independent executables are built simultaneously:

  1. app1.bin - Links app1_main, component1, component2
  2. app2.bin - Links app2_main, component1, component2, component3

Each app has its own dedicated entry point component that explicitly declares which components it needs. This avoids complex conditional logic and makes dependencies clear.

Project Structure

All components live under components/. Each idf_build_executable call picks exactly the subset it requires.

multi_binary/
├── CMakeLists.txt
└── components/
    ├── app1_main/      # Entry point for app1
    │   ├── CMakeLists.txt
    │   └── app1_main.c
    ├── app2_main/      # Entry point for app2
    │   ├── CMakeLists.txt
    │   └── app2_main.c
    ├── component1/     # Shared component
    │   ├── CMakeLists.txt
    │   ├── component1.c
    │   └── component1.h
    ├── component2/     # Shared component
    │   ├── CMakeLists.txt
    │   ├── component2.c
    │   └── component2.h
    └── component3/     # Only linked into app2
        ├── CMakeLists.txt
        ├── component3.c
        └── component3.h

Building

Build both binaries in a single command:

cd examples/build_system/cmakev2/features/multi_binary
idf.py set-target <target> 
idf.py build

This generates both app1.bin and app2.bin in the build/ directory.

Flashing

Flash app1:

idf.py app1-flash monitor

Flash app2:

idf.py app2-flash monitor

You can switch between apps without rebuilding - just select which one to flash.

Configuration (menuconfig)

When invoking a custom menuconfig target via idf.py, you must pass --no-hints. For example, use idf.py app1-menuconfig --no-hints (plain idf.py app1-menuconfig may not work because idf.py redirects stdout by default, which breaks the curses-based menu).

Note: Although multiple binaries can be produced from a single project, each component is evaluated only once using the current configuration. If a different configuration is needed for a component, a new project must be created. You cannot use the same component with different configurations within a single project.

Expected Output

app1 Output

I (xxx) component1: Hello from component1!
I (xxx) component2: Hello from component2!

app2 Output

I (xxx) component1: Hello from component1!
I (xxx) component2: Hello from component2!
I (xxx) component3: Hello from component3!