From c735d9beb9f62b09a32c20a784f0ea32d3e38cac Mon Sep 17 00:00:00 2001 From: Sudeep Mohanty Date: Tue, 27 Jan 2026 13:22:44 +0100 Subject: [PATCH] feat(cmakev2): Added import_lib_direct example for cmakev2 This commit adds a new example at examples/build_system/cmakev2/features/import_lib_direct to demonstrate the cmakev2 ability to integrate with external CMake projects easily. --- .../features/import_lib_direct/CMakeLists.txt | 11 ++ .../features/import_lib_direct/README.md | 61 ++++++++++ .../import_lib_direct/main/CMakeLists.txt | 15 +++ .../features/import_lib_direct/main/main.c | 107 ++++++++++++++++++ 4 files changed, 194 insertions(+) create mode 100644 examples/build_system/cmakev2/features/import_lib_direct/CMakeLists.txt create mode 100644 examples/build_system/cmakev2/features/import_lib_direct/README.md create mode 100644 examples/build_system/cmakev2/features/import_lib_direct/main/CMakeLists.txt create mode 100644 examples/build_system/cmakev2/features/import_lib_direct/main/main.c diff --git a/examples/build_system/cmakev2/features/import_lib_direct/CMakeLists.txt b/examples/build_system/cmakev2/features/import_lib_direct/CMakeLists.txt new file mode 100644 index 0000000000..3f8b8faa9a --- /dev/null +++ b/examples/build_system/cmakev2/features/import_lib_direct/CMakeLists.txt @@ -0,0 +1,11 @@ +# This example demonstrates a cmakev2 capability of importing an external +# C library via FetchContent, built by its own CMake, and linked directly +# to an IDF component without any IDF component wrapper for the library. + +cmake_minimum_required(VERSION 3.22) + +include($ENV{IDF_PATH}/tools/cmakev2/idf.cmake) + +project(import_lib_direct C CXX ASM) + +idf_project_default() diff --git a/examples/build_system/cmakev2/features/import_lib_direct/README.md b/examples/build_system/cmakev2/features/import_lib_direct/README.md new file mode 100644 index 0000000000..b67a697cc2 --- /dev/null +++ b/examples/build_system/cmakev2/features/import_lib_direct/README.md @@ -0,0 +1,61 @@ +| 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 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | --------- | + +# Import External C Library Directly (cmakev2) + +This example demonstrates importing an external C library that is a pure CMake project. It is downloaded at configure time, built by its own CMake, and linked directly to the IDF app without wrapping it as an IDF component. + +## Overview + +In Build System v1, integrating third-party CMake libraries required wrapping them as IDF components (see the `import_lib` example). With cmakev2, CMake's `FetchContent` module can be used to fetch and build an external CMake library (lwjson) and link the library to an IDF component using `target_link_libraries`. + +This example uses [lwjson](https://github.com/MaJerle/lwjson), a lightweight JSON parser for embedded systems. + +## Project Structure + +``` +import_lib_direct/ +├── CMakeLists.txt # Initialize the IDF project +├── README.md +└── main/ + ├── CMakeLists.txt # FetchContent for lwjson, register component, link + └── main.c # Uses lwjson API +``` + +## How It Works + +1. The **main component's CMakeLists.txt** uses `FetchContent_Declare()` and `FetchContent_MakeAvailable()` to download and build lwjson at configure time. The fetched sources land in `./build/_deps/`. + +2. After registering the main omponent with `idf_component_register`, it links the fetched library: + ```cmake + target_link_libraries(${COMPONENT_LIB} PUBLIC lwjson) + ``` + +3. **main.c** uses the lwjson API (`lwjson_init`, `lwjson_parse`, `lwjson_find`, etc.) to parse a sample JSON string and print device name, cores, features, and specs. + +## How to Use + +Build and flash the example: + +```bash +idf.py set-target +idf.py build +idf.py flash monitor +``` + +## Expected Output + +``` +I (275) import_lib_direct: lwjson library imported directly (downloaded, built, linked) without IDF component wrapper +I (275) import_lib_direct: Parsing JSON string... +I (285) import_lib_direct: Device name: ESP32 +I (285) import_lib_direct: Number of cores: 2 +I (285) import_lib_direct: Features: +I (295) import_lib_direct: - WiFi +I (295) import_lib_direct: - Bluetooth +I (295) import_lib_direct: - GPIO +I (305) import_lib_direct: Specifications: +I (305) import_lib_direct: Flash: 4MB +I (305) import_lib_direct: RAM: 520KB +I (315) import_lib_direct: Example complete! +``` \ No newline at end of file diff --git a/examples/build_system/cmakev2/features/import_lib_direct/main/CMakeLists.txt b/examples/build_system/cmakev2/features/import_lib_direct/main/CMakeLists.txt new file mode 100644 index 0000000000..1a80a6f9de --- /dev/null +++ b/examples/build_system/cmakev2/features/import_lib_direct/main/CMakeLists.txt @@ -0,0 +1,15 @@ +# Fetch the external lwjson library at configure time. +include(FetchContent) +fetchcontent_declare( + lwjson + GIT_REPOSITORY https://github.com/MaJerle/lwjson.git + GIT_TAG v1.8.1 + GIT_SHALLOW TRUE +) +fetchcontent_makeavailable(lwjson) + +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") + +# Link the fetched lwjson library to this component +target_link_libraries(${COMPONENT_LIB} PUBLIC lwjson) diff --git a/examples/build_system/cmakev2/features/import_lib_direct/main/main.c b/examples/build_system/cmakev2/features/import_lib_direct/main/main.c new file mode 100644 index 0000000000..b9a82b121d --- /dev/null +++ b/examples/build_system/cmakev2/features/import_lib_direct/main/main.c @@ -0,0 +1,107 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +/* + * This example demonstrates importing an external C library (lwjson) + * via FetchContent, built by its own CMake, and linked directly to this IDF component + * without any IDF component wrapper. + */ + +#include +#include +#include "esp_log.h" +#include "lwjson/lwjson.h" + +static const char *TAG = "import_lib_direct"; + +#define LWJSON_TOKENS 64 + +/* Sample JSON string to parse */ +static const char *sample_json = + "{" + " \"name\": \"ESP32\"," + " \"cores\": 2," + " \"features\": [\"WiFi\", \"Bluetooth\", \"GPIO\"]," + " \"specs\": {" + " \"flash\": \"4MB\"," + " \"ram\": \"520KB\"" + " }" + "}"; + +void app_main(void) +{ + ESP_LOGI(TAG, "lwjson library imported directly (downloaded, built, linked) without IDF component wrapper"); + + static lwjson_token_t tokens[LWJSON_TOKENS]; + lwjson_t lwobj; + + if (lwjson_init(&lwobj, tokens, LWJSON_TOKENS) != lwjsonOK) { + ESP_LOGE(TAG, "lwjson_init failed"); + return; + } + + if (lwjson_parse(&lwobj, sample_json) != lwjsonOK) { + ESP_LOGE(TAG, "lwjson_parse failed"); + lwjson_free(&lwobj); + return; + } + + ESP_LOGI(TAG, "Parsing JSON string..."); + + const lwjson_token_t *name_t = lwjson_find(&lwobj, "name"); + if (name_t != NULL) { + size_t len; + const char *s = lwjson_get_val_string(name_t, &len); + if (s != NULL) { + ESP_LOGI(TAG, "Device name: %.*s", (int)len, s); + } + } + + const lwjson_token_t *cores_t = lwjson_find(&lwobj, "cores"); + if (cores_t != NULL && cores_t->type == LWJSON_TYPE_NUM_INT) { + ESP_LOGI(TAG, "Number of cores: %lld", (long long)lwjson_get_val_int(cores_t)); + } + + const lwjson_token_t *features_t = lwjson_find(&lwobj, "features"); + if (features_t != NULL && features_t->type == LWJSON_TYPE_ARRAY) { + ESP_LOGI(TAG, "Features:"); + const lwjson_token_t *child = lwjson_get_first_child(features_t); + while (child != NULL) { + if (child->type == LWJSON_TYPE_STRING) { + size_t len; + const char *s = lwjson_get_val_string(child, &len); + if (s != NULL) { + ESP_LOGI(TAG, " - %.*s", (int)len, s); + } + } + child = child->next; + } + } + + const lwjson_token_t *flash_t = lwjson_find(&lwobj, "specs.flash"); + const lwjson_token_t *ram_t = lwjson_find(&lwobj, "specs.ram"); + if (flash_t != NULL || ram_t != NULL) { + ESP_LOGI(TAG, "Specifications:"); + if (flash_t != NULL) { + size_t len; + const char *s = lwjson_get_val_string(flash_t, &len); + if (s != NULL) { + ESP_LOGI(TAG, " Flash: %.*s", (int)len, s); + } + } + if (ram_t != NULL) { + size_t len; + const char *s = lwjson_get_val_string(ram_t, &len); + if (s != NULL) { + ESP_LOGI(TAG, " RAM: %.*s", (int)len, s); + } + } + } + + lwjson_free(&lwobj); + + ESP_LOGI(TAG, "Example complete!"); +}