mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
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.
This commit is contained in:
committed by
Frantisek Hrbata
parent
1a51a0ad01
commit
66851c702e
@@ -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
|
||||
|
||||
@@ -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)
|
||||
@@ -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 <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!
|
||||
```
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "app1_main.c"
|
||||
PRIV_REQUIRES component1 component2)
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#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));
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "app2_main.c"
|
||||
PRIV_REQUIRES component1 component2 component3)
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#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));
|
||||
}
|
||||
}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "component1.c"
|
||||
INCLUDE_DIRS ".")
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "component1";
|
||||
|
||||
void component1_print_hello(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Hello from component1!");
|
||||
}
|
||||
+12
@@ -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
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "component2.c"
|
||||
INCLUDE_DIRS ".")
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "esp_log.h"
|
||||
|
||||
static const char *TAG = "component2";
|
||||
|
||||
void component2_print_hello(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Hello from component2!");
|
||||
}
|
||||
+12
@@ -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
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "component3.c"
|
||||
INCLUDE_DIRS ".")
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "esp_log.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char *TAG = "component3";
|
||||
|
||||
void component3_print_hello(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Hello from component3!");
|
||||
}
|
||||
+12
@@ -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
|
||||
Reference in New Issue
Block a user