From 66851c702e4a1ecc22be4fe8e651fa50cc02b303 Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Tue, 27 Jan 2026 11:17:25 +0100 Subject: [PATCH] feat(cmakev2): Added multi_binary example for cmakev2 This commit adds a new example at examples/build_system/cmakev2/features/multi_binary to demonstrate the cmakev2 ability to build multiple binaries in a single CMake project. --- examples/build_system/cmakev2/README.md | 15 +++ .../features/multi_binary/CMakeLists.txt | 54 +++++++++++ .../cmakev2/features/multi_binary/README.md | 92 +++++++++++++++++++ .../components/app1_main/CMakeLists.txt | 2 + .../components/app1_main/app1_main.c | 22 +++++ .../components/app2_main/CMakeLists.txt | 2 + .../components/app2_main/app2_main.c | 24 +++++ .../components/component1/CMakeLists.txt | 2 + .../components/component1/component1.c | 15 +++ .../components/component1/component1.h | 12 +++ .../components/component2/CMakeLists.txt | 2 + .../components/component2/component2.c | 15 +++ .../components/component2/component2.h | 12 +++ .../components/component3/CMakeLists.txt | 2 + .../components/component3/component3.c | 16 ++++ .../components/component3/component3.h | 12 +++ 16 files changed, 299 insertions(+) create mode 100644 examples/build_system/cmakev2/features/multi_binary/CMakeLists.txt create mode 100644 examples/build_system/cmakev2/features/multi_binary/README.md create mode 100644 examples/build_system/cmakev2/features/multi_binary/components/app1_main/CMakeLists.txt create mode 100644 examples/build_system/cmakev2/features/multi_binary/components/app1_main/app1_main.c create mode 100644 examples/build_system/cmakev2/features/multi_binary/components/app2_main/CMakeLists.txt create mode 100644 examples/build_system/cmakev2/features/multi_binary/components/app2_main/app2_main.c create mode 100644 examples/build_system/cmakev2/features/multi_binary/components/component1/CMakeLists.txt create mode 100644 examples/build_system/cmakev2/features/multi_binary/components/component1/component1.c create mode 100644 examples/build_system/cmakev2/features/multi_binary/components/component1/component1.h create mode 100644 examples/build_system/cmakev2/features/multi_binary/components/component2/CMakeLists.txt create mode 100644 examples/build_system/cmakev2/features/multi_binary/components/component2/component2.c create mode 100644 examples/build_system/cmakev2/features/multi_binary/components/component2/component2.h create mode 100644 examples/build_system/cmakev2/features/multi_binary/components/component3/CMakeLists.txt create mode 100644 examples/build_system/cmakev2/features/multi_binary/components/component3/component3.c create mode 100644 examples/build_system/cmakev2/features/multi_binary/components/component3/component3.h diff --git a/examples/build_system/cmakev2/README.md b/examples/build_system/cmakev2/README.md index 801c28ec23..aa9f9701db 100644 --- a/examples/build_system/cmakev2/README.md +++ b/examples/build_system/cmakev2/README.md @@ -167,6 +167,21 @@ Demonstrates the use of the Kconfig configuration system to conditionally includ **When to use:** When you need to build applications or components that must conditionally depend on other components based on `sdkconfig` options. +--- + +### multi_binary +**Location:** [multi_binary](./features/multi_binary/) + +Demonstrates building multiple independent firmware binaries from a single project in one build command. + +**Build:** `idf.py build` creates both `app1.bin` and `app2.bin` +**Flash App1:** `idf.py app1-flash monitor` +**Flash App2:** `idf.py app2-flash monitor` + +**Key Concepts:** `idf_build_executable()`, `idf_build_binary()`, `idf_flash_binary()`, conditional component linking, Kconfig integration, multiple entry points + +**When to use:** Creating firmware variants with different features, manufacturing test firmware, or developing modular applications with compile-time configurable behavior. + ## Build System v2 API Quick Reference ### Project Setup diff --git a/examples/build_system/cmakev2/features/multi_binary/CMakeLists.txt b/examples/build_system/cmakev2/features/multi_binary/CMakeLists.txt new file mode 100644 index 0000000000..1706a09a95 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/CMakeLists.txt @@ -0,0 +1,54 @@ +cmake_minimum_required(VERSION 3.22) + +include($ENV{IDF_PATH}/tools/cmakev2/idf.cmake) + +project(multi_binary C CXX ASM) + +# Manual initialization for fine-grained control over component inclusion +idf_project_init() + +# Build the first executable: app1 +# Links: app1_main, component1, component2 +idf_build_executable(app1.elf + COMPONENTS app1_main component1 component2) + +# Build the second executable: app2 +# Links: app2_main, component1, component2, component3 +idf_build_executable(app2.elf + COMPONENTS app2_main component1 component2 component3) + +# Generate binaries and flash targets + +# App1 binary +idf_build_binary(app1.elf + OUTPUT_FILE "${CMAKE_BINARY_DIR}/app1.bin" + TARGET app1_binary) + +idf_flash_binary(app1_binary + TARGET app1-flash + NAME "app1" + FLASH) + +# Generate metadata only for the primary app1 binary +idf_build_generate_metadata(BINARY app1_binary) + +# Create menuconfig and confserver targets for app1 binary +idf_create_menuconfig(app1.elf TARGET app1-menuconfig) +idf_create_confserver(app1.elf TARGET app1-confserver) + +# App2 binary +idf_build_binary(app2.elf + OUTPUT_FILE "${CMAKE_BINARY_DIR}/app2.bin" + TARGET app2_binary) + +idf_flash_binary(app2_binary + TARGET app2-flash + NAME "app2" + FLASH) + +# Create menuconfig and confserver targets for app2 binary +idf_create_menuconfig(app2.elf TARGET app2-menuconfig) +idf_create_confserver(app2.elf TARGET app2-confserver) + +# Make both binaries part of the default "app" target +add_custom_target(app ALL DEPENDS app1.bin app2.bin) diff --git a/examples/build_system/cmakev2/features/multi_binary/README.md b/examples/build_system/cmakev2/features/multi_binary/README.md new file mode 100644 index 0000000000..06ec856b26 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/README.md @@ -0,0 +1,92 @@ +| 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 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | + +# 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: + +```bash +cd examples/build_system/cmakev2/features/multi_binary +idf.py set-target +idf.py build +``` + +This generates both `app1.bin` and `app2.bin` in the `build/` directory. + +## Flashing + +Flash app1: +```bash +idf.py app1-flash monitor +``` + +Flash app2: +```bash +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! +``` \ No newline at end of file diff --git a/examples/build_system/cmakev2/features/multi_binary/components/app1_main/CMakeLists.txt b/examples/build_system/cmakev2/features/multi_binary/components/app1_main/CMakeLists.txt new file mode 100644 index 0000000000..089b4abe26 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/components/app1_main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "app1_main.c" + PRIV_REQUIRES component1 component2) diff --git a/examples/build_system/cmakev2/features/multi_binary/components/app1_main/app1_main.c b/examples/build_system/cmakev2/features/multi_binary/components/app1_main/app1_main.c new file mode 100644 index 0000000000..eab34da357 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/components/app1_main/app1_main.c @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "component1.h" +#include "component2.h" + +void app_main(void) +{ + component1_print_hello(); + component2_print_hello(); + + while (1) { + vTaskDelay(pdMS_TO_TICKS(10000)); + } +} diff --git a/examples/build_system/cmakev2/features/multi_binary/components/app2_main/CMakeLists.txt b/examples/build_system/cmakev2/features/multi_binary/components/app2_main/CMakeLists.txt new file mode 100644 index 0000000000..f7e79ff6b9 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/components/app2_main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "app2_main.c" + PRIV_REQUIRES component1 component2 component3) diff --git a/examples/build_system/cmakev2/features/multi_binary/components/app2_main/app2_main.c b/examples/build_system/cmakev2/features/multi_binary/components/app2_main/app2_main.c new file mode 100644 index 0000000000..c187322124 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/components/app2_main/app2_main.c @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdkconfig.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "component1.h" +#include "component2.h" +#include "component3.h" + +void app_main(void) +{ + component1_print_hello(); + component2_print_hello(); + component3_print_hello(); + + while (1) { + vTaskDelay(pdMS_TO_TICKS(10000)); + } +} diff --git a/examples/build_system/cmakev2/features/multi_binary/components/component1/CMakeLists.txt b/examples/build_system/cmakev2/features/multi_binary/components/component1/CMakeLists.txt new file mode 100644 index 0000000000..5d4ba1e466 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/components/component1/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "component1.c" + INCLUDE_DIRS ".") diff --git a/examples/build_system/cmakev2/features/multi_binary/components/component1/component1.c b/examples/build_system/cmakev2/features/multi_binary/components/component1/component1.c new file mode 100644 index 0000000000..225d0202d3 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/components/component1/component1.c @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_log.h" + +static const char *TAG = "component1"; + +void component1_print_hello(void) +{ + ESP_LOGI(TAG, "Hello from component1!"); +} diff --git a/examples/build_system/cmakev2/features/multi_binary/components/component1/component1.h b/examples/build_system/cmakev2/features/multi_binary/components/component1/component1.h new file mode 100644 index 0000000000..1ba5db88e5 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/components/component1/component1.h @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef COMPONENT1_H +#define COMPONENT1_H + +void component1_print_hello(void); + +#endif // COMPONENT1_H diff --git a/examples/build_system/cmakev2/features/multi_binary/components/component2/CMakeLists.txt b/examples/build_system/cmakev2/features/multi_binary/components/component2/CMakeLists.txt new file mode 100644 index 0000000000..2555f99474 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/components/component2/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "component2.c" + INCLUDE_DIRS ".") diff --git a/examples/build_system/cmakev2/features/multi_binary/components/component2/component2.c b/examples/build_system/cmakev2/features/multi_binary/components/component2/component2.c new file mode 100644 index 0000000000..1683975446 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/components/component2/component2.c @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_log.h" + +static const char *TAG = "component2"; + +void component2_print_hello(void) +{ + ESP_LOGI(TAG, "Hello from component2!"); +} diff --git a/examples/build_system/cmakev2/features/multi_binary/components/component2/component2.h b/examples/build_system/cmakev2/features/multi_binary/components/component2/component2.h new file mode 100644 index 0000000000..d3c2d0a5fe --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/components/component2/component2.h @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef COMPONENT2_H +#define COMPONENT2_H + +void component2_print_hello(void); + +#endif // COMPONENT2_H diff --git a/examples/build_system/cmakev2/features/multi_binary/components/component3/CMakeLists.txt b/examples/build_system/cmakev2/features/multi_binary/components/component3/CMakeLists.txt new file mode 100644 index 0000000000..5c0dfffa86 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/components/component3/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "component3.c" + INCLUDE_DIRS ".") diff --git a/examples/build_system/cmakev2/features/multi_binary/components/component3/component3.c b/examples/build_system/cmakev2/features/multi_binary/components/component3/component3.c new file mode 100644 index 0000000000..37b8528b65 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/components/component3/component3.c @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_log.h" +#include "sdkconfig.h" + +static const char *TAG = "component3"; + +void component3_print_hello(void) +{ + ESP_LOGI(TAG, "Hello from component3!"); +} diff --git a/examples/build_system/cmakev2/features/multi_binary/components/component3/component3.h b/examples/build_system/cmakev2/features/multi_binary/components/component3/component3.h new file mode 100644 index 0000000000..cd5069ae95 --- /dev/null +++ b/examples/build_system/cmakev2/features/multi_binary/components/component3/component3.h @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef COMPONENT3_H +#define COMPONENT3_H + +void component3_print_hello(void); + +#endif // COMPONENT3_H