mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 11:10:54 +03: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.
|
**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
|
## Build System v2 API Quick Reference
|
||||||
|
|
||||||
### Project Setup
|
### 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