From b2e129fe4fcc156197ef6eae35fab14b9fb68c78 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 29 Jun 2022 20:14:37 +0200 Subject: [PATCH 1/4] examples: import_lib: demonstrate ExternalProject_Add Many of the more complicated CMake projects can't be added to the IDF build system simply by calling add_subdirectory. "add_subdirectory" also cannot be used with projects which use build systems other than CMake (for example GNU Make or cargo). This commit changes the example to use ExternalProject_Add, instead, which is a more general way of adding subprojects. As part of this change, tinyxml2 is now downloaded from the Internet, which allows removing one submodule. --- .../build_system/cmake/import_lib/README.md | 8 ++- .../components/tinyxml2/CMakeLists.txt | 67 +++++++++++++++++++ .../cmake/import_lib/main/CMakeLists.txt | 25 +------ .../cmake/import_lib/main/main.cpp | 2 +- 4 files changed, 76 insertions(+), 26 deletions(-) create mode 100644 examples/build_system/cmake/import_lib/components/tinyxml2/CMakeLists.txt diff --git a/examples/build_system/cmake/import_lib/README.md b/examples/build_system/cmake/import_lib/README.md index c6071c8db4..88b3775678 100644 --- a/examples/build_system/cmake/import_lib/README.md +++ b/examples/build_system/cmake/import_lib/README.md @@ -4,9 +4,11 @@ This example demonstrates how to import third-party CMake libraries. ## Example Flow -[tinyxml2](https://github.com/leethomason/tinyxml2) is a -a small C++ XML parser. It is imported, without modification, for use in the project's `main` component (see the `main` component's [CMakeLists.txt](main/CMakeLists.txt)). To demonstrate the library being used, a sample XML is embedded into the project. -This sample XML is then read and parsed later on using `tinyxml2`. +[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 diff --git a/examples/build_system/cmake/import_lib/components/tinyxml2/CMakeLists.txt b/examples/build_system/cmake/import_lib/components/tinyxml2/CMakeLists.txt new file mode 100644 index 0000000000..a3267a016f --- /dev/null +++ b/examples/build_system/cmake/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://gitcode.net/mirrors/leethomason/tinyxml2/-/archive/9.0.0/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/cmake/import_lib/main/CMakeLists.txt b/examples/build_system/cmake/import_lib/main/CMakeLists.txt index 3459763fc2..3477a4a46d 100644 --- a/examples/build_system/cmake/import_lib/main/CMakeLists.txt +++ b/examples/build_system/cmake/import_lib/main/CMakeLists.txt @@ -1,23 +1,4 @@ idf_component_register(SRCS "main.cpp" - INCLUDE_DIRS "." - EMBED_TXTFILES "sample.xml") - -# Build static library, do not build test executables -option(BUILD_SHARED_LIBS OFF) -option(BUILD_TESTING OFF) - -# Unfortunately the library performs install and export. Would -# have been nice if devs made that an option like BUILD_SHARED_LIBS -# and BUILD_TESTING. Override install() and export() to do nothing -# instead. -function(install) -endfunction() - -function(export) -endfunction() - -# Import tinyxml2 targets -add_subdirectory(lib/tinyxml2) - -# Link tinyxml2 to main component -target_link_libraries(${COMPONENT_LIB} PUBLIC tinyxml2) + INCLUDE_DIRS "." + EMBED_TXTFILES sample.xml + PRIV_REQUIRES tinyxml2 fatfs) diff --git a/examples/build_system/cmake/import_lib/main/main.cpp b/examples/build_system/cmake/import_lib/main/main.cpp index 10278cde6f..970b2b5e15 100644 --- a/examples/build_system/cmake/import_lib/main/main.cpp +++ b/examples/build_system/cmake/import_lib/main/main.cpp @@ -7,7 +7,7 @@ #include "esp_err.h" #include "esp_log.h" #include "esp_vfs_fat.h" -#include "lib/tinyxml2/tinyxml2.h" +#include "tinyxml2.h" using namespace tinyxml2; From faa349b93f079daafc7a8e83d283a511e0e62fb0 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 29 Jun 2022 20:23:07 +0200 Subject: [PATCH 2/4] global: remove tinyxml2 submodule --- .flake8 | 1 - .gitlab/ci/rules.yml | 1 - .gitmodules | 4 ---- examples/build_system/cmake/import_lib/main/lib/tinyxml2 | 1 - tools/ci/check_build_warnings.py | 1 - tools/ci/static-analysis-rules.yml | 1 - 6 files changed, 9 deletions(-) delete mode 160000 examples/build_system/cmake/import_lib/main/lib/tinyxml2 diff --git a/.flake8 b/.flake8 index 1e235f901c..0a4be0a0f7 100644 --- a/.flake8 +++ b/.flake8 @@ -149,7 +149,6 @@ exclude = components/tinyusb, components/unity/unity, components/spiffs/spiffs, - examples/build_system/cmake/import_lib/main/lib/tinyxml2, examples/peripherals/secure_element/atecc608_ecdsa/components/esp-cryptoauthlib, # autogenerated scripts components/protocomm/python/constants_pb2.py, diff --git a/.gitlab/ci/rules.yml b/.gitlab/ci/rules.yml index 063f53d0ff..377158ba9e 100644 --- a/.gitlab/ci/rules.yml +++ b/.gitlab/ci/rules.yml @@ -193,7 +193,6 @@ - "components/spiffs/spiffs" - "components/tinyusb/tinyusb" - "components/unity/unity" - - "examples/build_system/cmake/import_lib/main/lib/tinyxml2" - "examples/peripherals/secure_element/atecc608_ecdsa/components/esp-cryptoauthlib" .patterns-example_test-related_changes-ota: &patterns-example_test-related_changes-ota diff --git a/.gitmodules b/.gitmodules index 4954f2144a..8be23b0f12 100644 --- a/.gitmodules +++ b/.gitmodules @@ -39,10 +39,6 @@ path = components/unity/unity url = ../../ThrowTheSwitch/Unity.git -[submodule "examples/build_system/cmake/import_lib/main/lib/tinyxml2"] - path = examples/build_system/cmake/import_lib/main/lib/tinyxml2 - url = ../../leethomason/tinyxml2.git - [submodule "components/bt/host/nimble/nimble"] path = components/bt/host/nimble/nimble url = ../../espressif/esp-nimble.git diff --git a/examples/build_system/cmake/import_lib/main/lib/tinyxml2 b/examples/build_system/cmake/import_lib/main/lib/tinyxml2 deleted file mode 160000 index 7e8e249990..0000000000 --- a/examples/build_system/cmake/import_lib/main/lib/tinyxml2 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 7e8e249990ec491ec15990cf95b6d871a66cf64a diff --git a/tools/ci/check_build_warnings.py b/tools/ci/check_build_warnings.py index f40a196760..7e6eafd3fa 100755 --- a/tools/ci/check_build_warnings.py +++ b/tools/ci/check_build_warnings.py @@ -39,7 +39,6 @@ IGNORE_WARNS = [ r'crosstool_version_check\.cmake', r'CryptographyDeprecationWarning', r'Warning: \d+/\d+ app partitions are too small for binary', - r'CMake Deprecation Warning at main/lib/tinyxml2/CMakeLists\.txt:11 \(cmake_policy\)', ] ] diff --git a/tools/ci/static-analysis-rules.yml b/tools/ci/static-analysis-rules.yml index afa6fffb95..a9cc044957 100644 --- a/tools/ci/static-analysis-rules.yml +++ b/tools/ci/static-analysis-rules.yml @@ -29,7 +29,6 @@ skip: - "components/spiffs/spiffs" - "components/tinyusb/tinyusb" - "components/unity/unity" - - "examples/build_system/cmake/import_lib/main/lib/tinyxml2" - "examples/peripherals/secure_element/atecc608_ecdsa/components/esp-cryptoauthlib" # disabled temporarily to pass the CI From 94230777d9d4161ccf1d9e33704abd76227b7f18 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 29 Jun 2022 20:20:46 +0200 Subject: [PATCH 3/4] examples: import_lib: simplify with fatfs partition generator Instead of embedding the file and copying it to the FAT partition at run time, generate the FAT partition with the file in it. --- .../cmake/import_lib/main/CMakeLists.txt | 7 +- .../import_lib/main/{ => data}/sample.xml | 0 .../main/import_lib_example_main.cpp | 49 +++++++++++++ .../cmake/import_lib/main/main.cpp | 72 ------------------- tools/ci/check_copyright_ignore.txt | 1 - 5 files changed, 54 insertions(+), 75 deletions(-) rename examples/build_system/cmake/import_lib/main/{ => data}/sample.xml (100%) create mode 100644 examples/build_system/cmake/import_lib/main/import_lib_example_main.cpp delete mode 100644 examples/build_system/cmake/import_lib/main/main.cpp diff --git a/examples/build_system/cmake/import_lib/main/CMakeLists.txt b/examples/build_system/cmake/import_lib/main/CMakeLists.txt index 3477a4a46d..6e9d7d94ca 100644 --- a/examples/build_system/cmake/import_lib/main/CMakeLists.txt +++ b/examples/build_system/cmake/import_lib/main/CMakeLists.txt @@ -1,4 +1,7 @@ -idf_component_register(SRCS "main.cpp" +idf_component_register(SRCS "import_lib_example_main.cpp" INCLUDE_DIRS "." - EMBED_TXTFILES sample.xml 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/cmake/import_lib/main/sample.xml b/examples/build_system/cmake/import_lib/main/data/sample.xml similarity index 100% rename from examples/build_system/cmake/import_lib/main/sample.xml rename to examples/build_system/cmake/import_lib/main/data/sample.xml diff --git a/examples/build_system/cmake/import_lib/main/import_lib_example_main.cpp b/examples/build_system/cmake/import_lib/main/import_lib_example_main.cpp new file mode 100644 index 0000000000..069296cb03 --- /dev/null +++ b/examples/build_system/cmake/import_lib/main/import_lib_example_main.cpp @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2022 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/cmake/import_lib/main/main.cpp b/examples/build_system/cmake/import_lib/main/main.cpp deleted file mode 100644 index 970b2b5e15..0000000000 --- a/examples/build_system/cmake/import_lib/main/main.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "esp_err.h" -#include "esp_log.h" -#include "esp_vfs_fat.h" -#include "tinyxml2.h" - -using namespace tinyxml2; - -static const char *TAG = "example"; - -// Handle of the wear levelling library instance -static wl_handle_t s_wl_handle = WL_INVALID_HANDLE; - -// Mount path for the partition -const char *base_path = "/spiflash"; - -extern "C" void app_main(void) -{ - // Do example setup - ESP_LOGI(TAG, "Setting up..."); - esp_vfs_fat_mount_config_t mount_config; - mount_config.max_files = 4; - mount_config.format_if_mount_failed = true; - mount_config.allocation_unit_size = CONFIG_WL_SECTOR_SIZE; - - esp_err_t err = esp_vfs_fat_spiflash_mount_rw_wl(base_path, "storage", &mount_config, &s_wl_handle); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to mount FATFS (%s)", esp_err_to_name(err)); - return; - } - - // The sample XML is embedded binary data. Create a file first containing the embedded - // so it can be accessed. - ESP_LOGI(TAG, "Copying sample XML to filesystem..."); - - extern const char data_start[] asm("_binary_sample_xml_start"); - extern const char data_end[] asm("_binary_sample_xml_end"); - FILE *f = fopen("/spiflash/sample.xml", "wb"); - - if (f == NULL) { - ESP_LOGE(TAG, "Failed to open file for writing"); - return; - } - fwrite(data_start, sizeof(char), data_end - data_start + 1, f); - fclose(f); - - // Now that the file is created, load it using tinyxml2 and parse - ESP_LOGI(TAG, "Reading XML file"); - - XMLDocument data; - data.LoadFile("/spiflash/sample.xml"); - - 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); - - ESP_LOGI(TAG, "Example end"); -} diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index b2adaca858..d3faf1a6d7 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -1733,7 +1733,6 @@ examples/bluetooth/nimble/bleprph_wifi_coex/main/bleprph.h examples/bluetooth/nimble/bleprph_wifi_coex/main/gatt_svr.c examples/bluetooth/nimble/bleprph_wifi_coex/main/main.c examples/build_system/cmake/component_manager/main/component_manager.c -examples/build_system/cmake/import_lib/main/main.cpp examples/build_system/cmake/import_prebuilt/main/main.c examples/build_system/cmake/import_prebuilt/prebuilt/components/prebuilt/prebuilt.c examples/build_system/cmake/import_prebuilt/prebuilt/components/prebuilt/prebuilt.h From b0191ef292dcf18dbdc4131031f524082bc54042 Mon Sep 17 00:00:00 2001 From: Ivan Grokhotkov Date: Wed, 29 Jun 2022 21:51:03 +0200 Subject: [PATCH 4/4] examples: import_lib: add example test --- .../cmake/import_lib/pytest_import_lib.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 examples/build_system/cmake/import_lib/pytest_import_lib.py diff --git a/examples/build_system/cmake/import_lib/pytest_import_lib.py b/examples/build_system/cmake/import_lib/pytest_import_lib.py new file mode 100644 index 0000000000..49a450a1db --- /dev/null +++ b/examples/build_system/cmake/import_lib/pytest_import_lib.py @@ -0,0 +1,19 @@ +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded_qemu.dut import QemuDut + + +@pytest.mark.esp32 # we only support qemu on esp32 for now +@pytest.mark.host_test +@pytest.mark.qemu +def test_pytest_host(dut: QemuDut) -> None: + dut.expect_exact('Initializing the filesystem') + dut.expect_exact('Read XML data:') + dut.expect_exact('Parsed XML data:') + dut.expect_exact('To: Tove') + dut.expect_exact('From: Jani') + dut.expect_exact('Heading: Reminder') + dut.expect_exact('Body: Don\'t forget me this weekend!') + dut.expect_exact('Example end', timeout=20)