diff --git a/examples/build_system/cmakev2/README.md b/examples/build_system/cmakev2/README.md index c2a44374b0f..cc8c6dcdf8f 100644 --- a/examples/build_system/cmakev2/README.md +++ b/examples/build_system/cmakev2/README.md @@ -106,6 +106,19 @@ Demonstrates using ESP-IDF component manager with `cmakev2` **Key Concepts:** Defining component dependencies for the application using `idf_component.yml` +--- + +### import_lib +**Location:** [import_lib/](.feature/import_lib/) + +Demonstrates importing external third-party libraries using CMake's `ExternalProject` module. + +**Key Concepts:** `ExternalProject_Add()`, cross-compilation, `add_prebuilt_library()` + +**When to use:** When you need to download and build external libraries as part of your project. + +--- + ## Build System v2 API Quick Reference ### Project Setup diff --git a/examples/build_system/cmakev2/features/import_lib/CMakeLists.txt b/examples/build_system/cmakev2/features/import_lib/CMakeLists.txt new file mode 100644 index 00000000000..92937ffd783 --- /dev/null +++ b/examples/build_system/cmakev2/features/import_lib/CMakeLists.txt @@ -0,0 +1,9 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.22) + +include($ENV{IDF_PATH}/tools/cmakev2/idf.cmake) + +project(import_lib C CXX ASM) + +idf_project_default() diff --git a/examples/build_system/cmakev2/features/import_lib/README.md b/examples/build_system/cmakev2/features/import_lib/README.md new file mode 100644 index 00000000000..421700fa65a --- /dev/null +++ b/examples/build_system/cmakev2/features/import_lib/README.md @@ -0,0 +1,41 @@ +| 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 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | --------- | -------- | --------- | -------- | -------- | -------- | -------- | + +# Import Third-Party CMake Library Example + +This example demonstrates how to import third-party CMake libraries. + +## Example Flow + +[tinyxml2](https://github.com/leethomason/tinyxml2) is a small C++ XML parser. + +It is imported, without modification, into the [tinyxml2](components/tinyxml2/) component. Please refer to the component CMakeLists.txt file for the description of the process: [components/tinyxml2/CMakeLists.txt](components/tinyxml2/CMakeLists.txt). + +To demonstrate the library being used, a sample XML is embedded into the project. This sample XML is then read and parsed using `tinyxml2`. Please refer to the [main](main/) component for details. + +### Output + +``` +I (317) example: Setting up... +I (317) example: Copying sample XML to filesystem... +I (647) example: Reading XML file +I (657) example: Read XML data: + + + Tove + Jani + Reminder + Don't forget me this weekend! + + +I (667) example: Parsed XML data: + +To: Tove +From: Jani +Heading: Reminder +Body: Don't forget me this weekend! +I (677) example: Example end +``` +--- + +There is a discussion on importing third-party CMake libraries in the programming guide under `API Guides` -> `Build System` -> `Using Third-Party CMake Projects with Components` diff --git a/examples/build_system/cmakev2/features/import_lib/components/tinyxml2/CMakeLists.txt b/examples/build_system/cmakev2/features/import_lib/components/tinyxml2/CMakeLists.txt new file mode 100644 index 00000000000..5c20751bb7e --- /dev/null +++ b/examples/build_system/cmakev2/features/import_lib/components/tinyxml2/CMakeLists.txt @@ -0,0 +1,67 @@ +# This component demonstrates how to add an existing third-party library as a component +# to ESP-IDF build system. +# +# Since we are wrapping the library inside a component, +# the component has to be registered first: +idf_component_register() + +# To build a third-party library, ExternalProject CMake module can be used. +# ExternalProject offers many features which are impossible to demonstrate +# in a single example. Please refer to its documentation for more info: +# https://cmake.org/cmake/help/latest/module/ExternalProject.html +include(ExternalProject) + +# Define the location where tinyxml2 will be installed: +set(TINYXML2_INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/tinyxml2_install) + +# This function downloads the project, calls CMake to configure it, +# builds the project and installs it to the specified location: +externalproject_add(tinyxml2_proj + # Download the source code of the third party project from the following URL. + # (Two URLs are provided, the 2nd one is the mirror for Chinese users) + URL https://github.com/leethomason/tinyxml2/archive/refs/tags/9.0.0.zip + https://dl.espressif.com/dl/tinyxml2/9.0.0.zip + # (Downloading is not the only option; the library can also be located in your source tree. + # Consult ExternalProject_Add function documentation for other options.) + + # Specify arguments to be passed when running CMake for this subproject. + # Note that ExternalProject_Add also works with non-CMake projects, so this + # is just an example. + CMAKE_ARGS + # Use the same CMake toolchain file as for the main project. + -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} + # tinyxml2-specific settings: disable building everything except for the static library + -Dtinyxml2_BUILD_TESTING=FALSE + -Dtinyxml2_SHARED_LIBS=FALSE + # Pass the install directory to the subproject. + -DCMAKE_INSTALL_PREFIX= + + # These options are set so that Ninja immediately outputs + # the subproject build to the terminal. Otherwise it looks like the + # build process "hangs" while the subproject is being built. + USES_TERMINAL_DOWNLOAD TRUE + USES_TERMINAL_CONFIGURE TRUE + USES_TERMINAL_BUILD TRUE + + # Specify the installation directory for the subproject + INSTALL_DIR ${TINYXML2_INSTALL_DIR} + # Let CMake know that the library is generated by the subproject build step. + BUILD_BYPRODUCTS "${TINYXML2_INSTALL_DIR}/lib/libtinyxml2.a" +) + +# Now that the subproject build is set up, we need to consume the results +# of the build: the header file and the static library. +# To do this, define an imported CMake library: +add_prebuilt_library(tinyxml2_lib "${TINYXML2_INSTALL_DIR}/lib/libtinyxml2.a" + # tinyxml calls certain C++ support library functions (_Unwind_Resume and similar) + # so a dependency on IDF's cxx component is added here: + PRIV_REQUIRES cxx) +target_include_directories(tinyxml2_lib INTERFACE "${TINYXML2_INSTALL_DIR}/include") +add_dependencies(tinyxml2_lib tinyxml2_proj) + +# Link the imported library to the current component. +target_link_libraries(${COMPONENT_LIB} INTERFACE tinyxml2_lib) + +# To use tinyxml2 in another component, add 'tinyxml2' (the name of this component) +# to PRIV_REQUIRES or REQUIRES list its idf_component_register call. +# See ../../main/CMakeLists.txt for an example. diff --git a/examples/build_system/cmakev2/features/import_lib/main/CMakeLists.txt b/examples/build_system/cmakev2/features/import_lib/main/CMakeLists.txt new file mode 100644 index 00000000000..6e9d7d94ca9 --- /dev/null +++ b/examples/build_system/cmakev2/features/import_lib/main/CMakeLists.txt @@ -0,0 +1,7 @@ +idf_component_register(SRCS "import_lib_example_main.cpp" + INCLUDE_DIRS "." + PRIV_REQUIRES tinyxml2 fatfs) + +# Create a FAT filesystem image from the contents of data/ subdirectory, +# The image will be flashed into the 'storage' partition when 'idf.py flash' is used. +fatfs_create_spiflash_image(storage data FLASH_IN_PROJECT) diff --git a/examples/build_system/cmakev2/features/import_lib/main/data/sample.xml b/examples/build_system/cmakev2/features/import_lib/main/data/sample.xml new file mode 100644 index 00000000000..d3f0eca227c --- /dev/null +++ b/examples/build_system/cmakev2/features/import_lib/main/data/sample.xml @@ -0,0 +1,7 @@ + + + Tove + Jani + Reminder + Don't forget me this weekend! + diff --git a/examples/build_system/cmakev2/features/import_lib/main/import_lib_example_main.cpp b/examples/build_system/cmakev2/features/import_lib/main/import_lib_example_main.cpp new file mode 100644 index 00000000000..bc58d29100a --- /dev/null +++ b/examples/build_system/cmakev2/features/import_lib/main/import_lib_example_main.cpp @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include "esp_err.h" +#include "esp_log.h" +#include "esp_vfs_fat.h" +#include "tinyxml2.h" + +static const char *TAG = "example"; + + +extern "C" void app_main(void) +{ + ESP_LOGI(TAG, "Initializing the filesystem"); + esp_vfs_fat_mount_config_t mount_config = {}; + mount_config.max_files = 1; + + wl_handle_t wl_handle = WL_INVALID_HANDLE; + esp_err_t err = esp_vfs_fat_spiflash_mount_rw_wl("/spiflash", "storage", &mount_config, &wl_handle); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err)); + return; + } + + // Load the XML file from the filesystem and parse it using tinyxml2 + ESP_LOGI(TAG, "Reading XML file"); + tinyxml2::XMLDocument data; + data.LoadFile("/spiflash/sample.xml"); + + tinyxml2::XMLPrinter printer; + data.Print(&printer); + + ESP_LOGI(TAG, "Read XML data:\n%s", printer.CStr()); + + const char* to_data = data.FirstChildElement("note")->FirstChildElement("to")->GetText(); + const char* from_data = data.FirstChildElement("note")->FirstChildElement("from")->GetText(); + const char* heading_data = data.FirstChildElement("note")->FirstChildElement("heading")->GetText(); + const char* body_data = data.FirstChildElement("note")->FirstChildElement("body")->GetText(); + + ESP_LOGI(TAG, "Parsed XML data:\n\nTo: %s\nFrom: %s\nHeading: %s\nBody: %s", + to_data, from_data, heading_data, body_data); + + // Clean up + esp_vfs_fat_spiflash_unmount_rw_wl("/spiflash", wl_handle); + + ESP_LOGI(TAG, "Example end"); +} diff --git a/examples/build_system/cmakev2/features/import_lib/partitions_example.csv b/examples/build_system/cmakev2/features/import_lib/partitions_example.csv new file mode 100644 index 00000000000..d8fe545ba28 --- /dev/null +++ b/examples/build_system/cmakev2/features/import_lib/partitions_example.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1M, +storage, data, fat, , 528K, diff --git a/examples/build_system/cmakev2/features/import_lib/sdkconfig.defaults b/examples/build_system/cmakev2/features/import_lib/sdkconfig.defaults new file mode 100644 index 00000000000..b9bb0c0a5dc --- /dev/null +++ b/examples/build_system/cmakev2/features/import_lib/sdkconfig.defaults @@ -0,0 +1,3 @@ +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions_example.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions_example.csv"