diff --git a/docs/en/api-guides/build-system.rst b/docs/en/api-guides/build-system.rst index fc190a218e..ec2a806069 100644 --- a/docs/en/api-guides/build-system.rst +++ b/docs/en/api-guides/build-system.rst @@ -1010,6 +1010,28 @@ Espressif's fork of `mbedtls `_. See its :co The CMake variable ``ESP_PLATFORM`` is set to 1 whenever the ESP-IDF build system is being used. Tests such as ``if (ESP_PLATFORM)`` can be used in generic CMake code if special IDF-specific logic is required. +Using Prebuilt Libraries with Components +======================================== + +.. highlight:: cmake + +The ESP-IDF build system provides a utility function ``add_prebuilt_library`` for users to be able to easily import and use +prebuilt libraries:: + + add_prebuilt_library(target_name lib_path [REQUIRES req1 req2 ...] [PRIV_REQUIRES req1 req2 ...]) + +where: + +- ``target_name``- name that can be used to reference the imported library, such as when linking to other targets +- ``lib_path``- path to prebuilt library; may be an absolute or relative path to the component directory + +Optional arguments ``REQUIRES`` and ``PRIV_REQUIRES`` specify dependency on other components. These have the same meaning as the arguments for ``idf_component_register``. + +Take note that the prebuilt library must have been compiled for the same target as the consuming project. Configuration relevant to the prebuilt +library must also match. If not paid attention to, these two factors may contribute to subtle bugs in the app. + +For an example, take a look at :example:`build_system/cmake/import_prebuilt`. + Using ESP-IDF in Custom CMake Projects ====================================== diff --git a/examples/build_system/cmake/import_prebuilt/CMakeLists.txt b/examples/build_system/cmake/import_prebuilt/CMakeLists.txt new file mode 100644 index 0000000000..b4a43722d3 --- /dev/null +++ b/examples/build_system/cmake/import_prebuilt/CMakeLists.txt @@ -0,0 +1,6 @@ +# 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.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(import_prebuilt) diff --git a/examples/build_system/cmake/import_prebuilt/README.md b/examples/build_system/cmake/import_prebuilt/README.md new file mode 100644 index 0000000000..0ad1f90395 --- /dev/null +++ b/examples/build_system/cmake/import_prebuilt/README.md @@ -0,0 +1,37 @@ +# Import Prebuilt Library Example + +This example illustrates how to import a prebuilt static library in the ESP-IDF build system. + +## Example Flow + +Users need to first build the project in the [prebuilt](prebuilt) subdirectory: + +``` +cd prebuilt +idf.py build +``` + +This builds a component named [prebuilt](prebuilt/components/prebuilt), which has private dependency on ESP-IDF components `spi_flash`, `log` and `app_update` (see [its CMakeLists.txt](prebuilt/components/prebuilt/CMakeLists.txt)). Once built, the archive file `libprebuilt.a`, along with the header file `prebuilt.h`, is automatically copied to the [`main` component](main) of this example project. + +The [`main` component's CMakeLists.txt](main/CMakeLists.txt) demonstrates how to import `libprebuilt.a` and link it to `main` so that the definitions inside can be used. +It also demonstrates how to specify the same dependencies the original component had so as to properly resolve symbols used inside the prebuilt library. + +Users can then return to this directory and build the main example: + +``` +cd .. +idf.py build +``` + + +### Output + +The example simply outputs the current running partition. + +``` +I (319) prebuilt: The running partition is 'factory'! +``` + +--- + +There is a discussion on importing prebuilt libraries in the programming guide under `API Guides` -> `Build System` -> `Using Prebuilt Libraries with Components` diff --git a/examples/build_system/cmake/import_prebuilt/main/CMakeLists.txt b/examples/build_system/cmake/import_prebuilt/main/CMakeLists.txt new file mode 100644 index 0000000000..8184f63d84 --- /dev/null +++ b/examples/build_system/cmake/import_prebuilt/main/CMakeLists.txt @@ -0,0 +1,12 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS ".") + +# Import the library, specifying a target name and the library path. +# The private dependencies of the library is also specified. +add_prebuilt_library(prebuilt "libprebuilt.a" + PRIV_REQUIRES spi_flash app_update log) + +# `main` calls a function from the library, so link it to `main` +target_link_libraries(${COMPONENT_LIB} PRIVATE prebuilt) + + diff --git a/examples/build_system/cmake/import_prebuilt/main/main.c b/examples/build_system/cmake/import_prebuilt/main/main.c new file mode 100644 index 0000000000..8751293849 --- /dev/null +++ b/examples/build_system/cmake/import_prebuilt/main/main.c @@ -0,0 +1,17 @@ +/* Import Prebuilt Library Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +// Include the prebuilt library's header file so as to be able +// to reference `prebuilt_func` here. +#include "prebuilt.h" + +void app_main(void) +{ + prebuilt_func(); +} diff --git a/examples/build_system/cmake/import_prebuilt/main/project_include.cmake b/examples/build_system/cmake/import_prebuilt/main/project_include.cmake new file mode 100644 index 0000000000..9bf6e7cb6e --- /dev/null +++ b/examples/build_system/cmake/import_prebuilt/main/project_include.cmake @@ -0,0 +1,6 @@ +# For users checking this example, ignore the following code. This is so that +# the prebuilt project is built automatically in ESP-IDF CI. +if("$ENV{CI}") + execute_process(COMMAND ${IDFTOOL} build + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/prebuilt) +endif() diff --git a/examples/build_system/cmake/import_prebuilt/prebuilt/CMakeLists.txt b/examples/build_system/cmake/import_prebuilt/prebuilt/CMakeLists.txt new file mode 100644 index 0000000000..fab78937a3 --- /dev/null +++ b/examples/build_system/cmake/import_prebuilt/prebuilt/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.5) + +# Since we're only interested in the prebuilt library, trim +# the build +set(COMPONENTS prebuilt main esptool_py) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) + +project(prebuilt C) diff --git a/examples/build_system/cmake/import_prebuilt/prebuilt/components/prebuilt/CMakeLists.txt b/examples/build_system/cmake/import_prebuilt/prebuilt/components/prebuilt/CMakeLists.txt new file mode 100644 index 0000000000..b58f0b3f06 --- /dev/null +++ b/examples/build_system/cmake/import_prebuilt/prebuilt/components/prebuilt/CMakeLists.txt @@ -0,0 +1,10 @@ +idf_component_register(SRCS prebuilt.c + INCLUDE_DIRS "." + PRIV_REQUIRES app_update spi_flash log) + +# After build, copy the archive file and header file to parent example directory's main component +add_custom_command(TARGET ${COMPONENT_LIB} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy $ ${CMAKE_SOURCE_DIR}/../main + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_LIST_DIR}/prebuilt.h ${CMAKE_SOURCE_DIR}/../main + COMMENT "Copying built archive file and header to parent example directory...") diff --git a/examples/build_system/cmake/import_prebuilt/prebuilt/components/prebuilt/prebuilt.c b/examples/build_system/cmake/import_prebuilt/prebuilt/components/prebuilt/prebuilt.c new file mode 100644 index 0000000000..8830747775 --- /dev/null +++ b/examples/build_system/cmake/import_prebuilt/prebuilt/components/prebuilt/prebuilt.c @@ -0,0 +1,21 @@ +/* Import Prebuilt Library Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include "esp_ota_ops.h" +#include "esp_partition.h" +#include "esp_log.h" + +const char *TAG = "prebuilt"; + +void prebuilt_func(void) +{ + const esp_partition_t* running_partition = esp_ota_get_running_partition(); + ESP_LOGI(TAG, "The running partition is '%s'!", running_partition->label); +} + + diff --git a/examples/build_system/cmake/import_prebuilt/prebuilt/components/prebuilt/prebuilt.h b/examples/build_system/cmake/import_prebuilt/prebuilt/components/prebuilt/prebuilt.h new file mode 100644 index 0000000000..cca94efef5 --- /dev/null +++ b/examples/build_system/cmake/import_prebuilt/prebuilt/components/prebuilt/prebuilt.h @@ -0,0 +1,11 @@ +/* Import Prebuilt Library Example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#pragma once + +void prebuilt_func(void); diff --git a/examples/build_system/cmake/import_prebuilt/prebuilt/main/CMakeLists.txt b/examples/build_system/cmake/import_prebuilt/prebuilt/main/CMakeLists.txt new file mode 100644 index 0000000000..8a3ab69279 --- /dev/null +++ b/examples/build_system/cmake/import_prebuilt/prebuilt/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "main.c" + INCLUDE_DIRS "") diff --git a/examples/build_system/cmake/import_prebuilt/prebuilt/main/main.c b/examples/build_system/cmake/import_prebuilt/prebuilt/main/main.c new file mode 100644 index 0000000000..5505eea54f --- /dev/null +++ b/examples/build_system/cmake/import_prebuilt/prebuilt/main/main.c @@ -0,0 +1,6 @@ +#include + +void app_main(void) +{ + printf("Hello World!\n"); +} diff --git a/tools/cmake/utilities.cmake b/tools/cmake/utilities.cmake index dcd4e9fbf5..5d3a8ebc69 100644 --- a/tools/cmake/utilities.cmake +++ b/tools/cmake/utilities.cmake @@ -258,3 +258,26 @@ function(add_c_compile_options) add_compile_options($<$:${option}>) endforeach() endfunction() + + +function(add_prebuilt_library target_name lib_path) + cmake_parse_arguments(_ "" "" "REQUIRES;PRIV_REQUIRES" ${ARGN}) + + get_filename_component(lib_path "${lib_path}" + ABSOLUTE BASE_DIR "${CMAKE_CURRENT_LIST_DIR}") + + add_library(${target_name} STATIC IMPORTED) + set_property(TARGET ${target_name} PROPERTY IMPORTED_LOCATION ${lib_path}) + + foreach(req ${__REQUIRES}) + idf_component_get_property(req_lib "${req}" COMPONENT_LIB) + set_property(TARGET ${target_name} APPEND PROPERTY LINK_LIBRARIES "${req_lib}") + set_property(TARGET ${target_name} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${req_lib}") + endforeach() + + foreach(req ${__PRIV_REQUIRES}) + idf_component_get_property(req_lib "${req}" COMPONENT_LIB) + set_property(TARGET ${target_name} APPEND PROPERTY LINK_LIBRARIES "${req_lib}") + set_property(TARGET ${target_name} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "$") + endforeach() +endfunction() \ No newline at end of file