feat(cmakev2): Added idf_as_lib example for cmakev2

This commit updates the examples/build_system/cmake/idf_as_lib
example for the new build system and adds the newly created example at
examples/build_system/cmakev2/features/idf_as_lib.
This commit is contained in:
Sudeep Mohanty
2026-01-23 16:42:20 +01:00
committed by Frantisek Hrbata
parent d782a2c4eb
commit 20cc771961
4 changed files with 201 additions and 0 deletions
+20
View File
@@ -77,6 +77,26 @@ LED blinking example with Kconfig configuration and Component Manager integratio
## Build System Features Examples
### idf_as_lib
**Location:** [idf_as_lib/](./features/idf_as_lib/)
Demonstrates using ESP-IDF components as a library in external projects.
```cmake
idf_build_library(idf_lib COMPONENTS spi_flash)
target_link_libraries(external_app idf_lib)
```
**Key Concepts:** `idf_build_library()`, external project integration
**When to use:** When integrating ESP-IDF components into non-IDF build systems or existing CMake projects.
**Build Note:** This example uses direct CMake commands, not `idf.py`:
```bash
cmake -G Ninja -B build -DCMAKE_TOOLCHAIN_FILE=...
ninja -C build
```
These examples demonstrate the build system capabilities and some `cmakev2` specific features.
## Build System v2 API Quick Reference
@@ -0,0 +1,55 @@
# A standard CMake project that uses ESP-IDF as a library.
# When ESP_PLATFORM is set (idf.py invocation), IDF components are bundled
# into a single library and linked into the executable. Without it the
# project builds a plain host executable with no IDF dependency.
cmake_minimum_required(VERSION 3.22)
if(ESP_PLATFORM)
include($ENV{IDF_PATH}/tools/cmakev2/idf.cmake)
endif()
project(idf_as_lib C CXX ASM)
# Single source file for both builds. IDF code paths inside main.c are
# guarded by #ifdef ESP_PLATFORM.
add_executable(${CMAKE_PROJECT_NAME}.elf main.c)
if(ESP_PLATFORM)
# Initialise the IDF build system and bundle the required components
# into a single linkable library.
idf_project_init()
# Create ESP-IDF library with specified components.
idf_build_library(idf_components COMPONENTS spi_flash esp_system)
# Apply IDF build properties to the custom executable so it receives
# include paths, compile definitions, and compile options that IDF normally adds.
idf_build_get_property(include_directories INCLUDE_DIRECTORIES GENERATOR_EXPRESSION)
target_include_directories(${CMAKE_PROJECT_NAME}.elf PRIVATE "${include_directories}")
idf_build_get_property(compile_definitions COMPILE_DEFINITIONS GENERATOR_EXPRESSION)
target_compile_definitions(${CMAKE_PROJECT_NAME}.elf PRIVATE "${compile_definitions}")
idf_build_get_compile_options(compile_options)
target_compile_options(${CMAKE_PROJECT_NAME}.elf PRIVATE "${compile_options}")
# Link the bundled IDF library. This brings in component headers,
# sdkconfig includes, and all necessary linker options.
target_link_libraries(${CMAKE_PROJECT_NAME}.elf PRIVATE idf_components)
# Binary and flash targets
idf_build_binary(${CMAKE_PROJECT_NAME}.elf
OUTPUT_FILE "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin"
TARGET ${CMAKE_PROJECT_NAME}_binary)
idf_flash_binary(${CMAKE_PROJECT_NAME}_binary
TARGET app-flash
NAME "app"
FLASH)
idf_check_binary_size(${CMAKE_PROJECT_NAME}_binary)
idf_build_generate_metadata(BINARY ${CMAKE_PROJECT_NAME}_binary)
idf_build_generate_flasher_args()
add_custom_target(app ALL DEPENDS ${CMAKE_PROJECT_NAME}_binary)
endif()
@@ -0,0 +1,55 @@
| 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 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- |
# ESP-IDF as a Library in a Standard CMake Project
This example demonstrates how to use ESP-IDF as a library inside a standard CMake project. The same source files produce either a plain host executable or an ESP-IDF firmware image depending on how the project is invoked.
## How it works
When `idf.py` is used it sets the `ESP_PLATFORM` CMake variable before invoking CMake. A single project-level CMakeLists.txt checks for this variable: if set, it initialises the IDF build system, bundles the required IDF components into one linkable library via `idf_build_library`, compiles the application sources, links them against that library, and produces the final binary and flash targets. Without `ESP_PLATFORM` the same file produces a minimal host executable with no IDF dependency.
`main.c` is the only source file. All IDF API calls inside it are guarded by `#ifdef ESP_PLATFORM`, so the same file compiles cleanly for both targets. On ESP it defines `app_main`; on the host it defines a plain `main`.
## Project Layout
```
idf_as_lib/
├── CMakeLists.txt ← Single file: host + ESP build logic
├── main.c ← One file, two entry points (host & ESP)
└── sdkconfig ← ESP-IDF configuration
```
## How to build the host app
```
cmake -B build-host .
cmake --build build-host
./build-host/idf_as_lib.elf
```
### Expected output
```
Hello from host build
Run with idf.py set-target <target> && idf.py build for ESP build.
```
## How to build the ESP-IDF app
```
idf.py set-target <target>
idf.py build flash monitor
```
### Expected output
```
Hello from ESP-IDF build
idf_lib initialized (IDF as library)
This is esp32 chip with 2 CPU core(s), WiFi/BT/BLE, silicon revision v3.0, 2MB external flash
Minimum free heap size: 303764 bytes
Restarting in 10...
...
Restarting now.
```
@@ -0,0 +1,71 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*
* Single source file for both host and ESP-IDF builds.
* - Host: plain main(), no IDF dependency.
* - ESP-IDF: app_main(), uses IDF APIs guarded by ESP_PLATFORM.
*/
#include <stdio.h>
#include <inttypes.h>
#ifdef ESP_PLATFORM
#include "esp_chip_info.h"
#include "esp_flash.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#endif
#ifdef ESP_PLATFORM
void app_main(void)
#else
int main(void)
#endif
{
printf("Hello from %s build\n",
#ifdef ESP_PLATFORM
"ESP-IDF"
#else
"host"
#endif
);
#ifdef ESP_PLATFORM
/* --- chip info --------------------------------------------------------- */
esp_chip_info_t chip_info;
uint32_t flash_size;
esp_chip_info(&chip_info);
printf("This is %s chip with %d CPU core(s), WiFi%s%s, ",
CONFIG_IDF_TARGET,
chip_info.cores,
(chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
unsigned major_rev = chip_info.revision / 100;
unsigned minor_rev = chip_info.revision % 100;
printf("silicon revision v%d.%d, ", major_rev, minor_rev);
if (esp_flash_get_size(NULL, &flash_size) == ESP_OK) {
printf("%" PRIu32 " MB %s flash\n", flash_size / (1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
}
printf("Minimum free heap size: %" PRIu32 " bytes\n", esp_get_minimum_free_heap_size());
/* --- countdown and restart -------------------------------------------- */
for (int i = 10; i >= 0; i--) {
printf("Restarting in %d...\n", i);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
printf("Restarting now.\n");
fflush(stdout);
esp_restart();
#else
printf("Run with idf.py set-target <target> && idf.py build for ESP-IDF build.\n");
return 0;
#endif
}