From 1cb5712463a8963cd3e8331da90fb5e03f13575f Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 22 Mar 2018 17:27:10 +1100 Subject: [PATCH] cmake: Add component dependency support Components should set the COMPONENT_REQUIRES & COMPONENT_PRIVATE_REQUIRES variables to define their requirements. --- components/app_trace/CMakeLists.txt | 3 + components/app_update/CMakeLists.txt | 3 + components/app_update/esp_ota_ops.c | 1 + components/app_update/include/esp_ota_ops.h | 1 - components/aws_iot/CMakeLists.txt | 11 +- components/bootloader/CMakeLists.txt | 7 +- .../bootloader/subproject/CMakeLists.txt | 2 + components/bootloader_support/CMakeLists.txt | 4 + components/bt/CMakeLists.txt | 157 +++++++------ components/coap/CMakeLists.txt | 2 + components/console/CMakeLists.txt | 2 + components/cxx/CMakeLists.txt | 1 + components/driver/CMakeLists.txt | 2 + components/esp-tls/CMakeLists.txt | 7 + components/esp32/CMakeLists.txt | 11 +- components/esp_adc_cal/CMakeLists.txt | 2 + components/ethernet/CMakeLists.txt | 3 + components/expat/CMakeLists.txt | 2 + components/fatfs/CMakeLists.txt | 2 + components/freertos/CMakeLists.txt | 2 + components/heap/CMakeLists.txt | 2 + components/idf_test/CMakeLists.txt | 1 + components/jsmn/CMakeLists.txt | 2 + components/json/CMakeLists.txt | 2 + components/libsodium/CMakeLists.txt | 2 + components/log/CMakeLists.txt | 1 + components/lwip/CMakeLists.txt | 3 + components/mbedtls/CMakeLists.txt | 2 + components/mdns/CMakeLists.txt | 2 + components/micro-ecc/CMakeLists.txt | 2 + components/newlib/CMakeLists.txt | 2 + components/nghttp/CMakeLists.txt | 2 + components/nvs_flash/CMakeLists.txt | 2 + components/openssl/CMakeLists.txt | 2 + components/partition_table/CMakeLists.txt | 3 +- components/pthread/CMakeLists.txt | 1 + components/sdmmc/CMakeLists.txt | 2 +- components/smartconfig/CMakeLists.txt | 2 + components/soc/CMakeLists.txt | 3 + components/spi_flash/CMakeLists.txt | 3 + components/spiffs/CMakeLists.txt | 4 + components/tcpip_adapter/CMakeLists.txt | 2 + components/ulp/CMakeLists.txt | 2 + components/vfs/CMakeLists.txt | 2 + components/wear_levelling/CMakeLists.txt | 1 + components/wpa_supplicant/CMakeLists.txt | 3 + components/xtensa-debug-module/CMakeLists.txt | 2 + docs/en/api-guides/build-system-cmake.rst | 70 ++++-- docs/en/api-guides/build-system.rst | 4 +- .../components/sh2lib/CMakeLists.txt | 3 + tools/cmake/components.cmake | 131 ++++------- tools/cmake/convert_to_cmake.py | 5 + tools/cmake/idf_functions.cmake | 4 + tools/cmake/kconfig.cmake | 2 +- tools/cmake/project.cmake | 36 ++- tools/cmake/scripts/expand_requirements.cmake | 216 ++++++++++++++++++ 56 files changed, 557 insertions(+), 196 deletions(-) create mode 100644 components/esp-tls/CMakeLists.txt create mode 100644 tools/cmake/scripts/expand_requirements.cmake diff --git a/components/app_trace/CMakeLists.txt b/components/app_trace/CMakeLists.txt index fec9b921d6..5a6f19dbbe 100644 --- a/components/app_trace/CMakeLists.txt +++ b/components/app_trace/CMakeLists.txt @@ -14,6 +14,9 @@ if(CONFIG_SYSVIEW_ENABLE) "sys_view/esp32") endif() +set(COMPONENT_REQUIRES) +set(COMPONENT_PRIV_REQUIRES xtensa-debug-module) + register_component() # disable --coverage for this component, as it is used as transport diff --git a/components/app_update/CMakeLists.txt b/components/app_update/CMakeLists.txt index ba2fa9c7e7..44663f68ea 100644 --- a/components/app_update/CMakeLists.txt +++ b/components/app_update/CMakeLists.txt @@ -1,4 +1,7 @@ set(COMPONENT_SRCDIRS ".") set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_REQUIRES "") +set(COMPONENT_PRIV_REQUIRES bootloader_support spi_flash) + register_component() diff --git a/components/app_update/esp_ota_ops.c b/components/app_update/esp_ota_ops.c index 8e26ba162a..1047d2117f 100644 --- a/components/app_update/esp_ota_ops.c +++ b/components/app_update/esp_ota_ops.c @@ -28,6 +28,7 @@ #include "esp_image_format.h" #include "esp_secure_boot.h" #include "esp_flash_encrypt.h" +#include "esp_spi_flash.h" #include "sdkconfig.h" #include "esp_ota_ops.h" diff --git a/components/app_update/include/esp_ota_ops.h b/components/app_update/include/esp_ota_ops.h index a089a92be0..8fcf622d37 100755 --- a/components/app_update/include/esp_ota_ops.h +++ b/components/app_update/include/esp_ota_ops.h @@ -20,7 +20,6 @@ #include #include "esp_err.h" #include "esp_partition.h" -#include "esp_spi_flash.h" #ifdef __cplusplus extern "C" diff --git a/components/aws_iot/CMakeLists.txt b/components/aws_iot/CMakeLists.txt index f8bfb26a27..f7644bd7a3 100644 --- a/components/aws_iot/CMakeLists.txt +++ b/components/aws_iot/CMakeLists.txt @@ -1,8 +1,11 @@ -if(NOT CONFIG_AWS_IOT_SDK) - return() +if(CONFIG_AWS_IOT_SDK) + set(COMPONENT_ADD_INCLUDEDIRS "include aws-iot-device-sdk-embedded-C/include") + set(COMPONENT_SRCDIRS "aws-iot-device-sdk-embedded-C/src port") +else() + message(STATUS "Building empty aws_iot component due to configuration") endif() -set(COMPONENT_ADD_INCLUDEDIRS "include aws-iot-device-sdk-embedded-C/include") -set(COMPONENT_SRCDIRS "aws-iot-device-sdk-embedded-C/src port") +set(COMPONENT_REQUIRES "mbedtls") +set(COMPONENT_PRIV_REQUIRES "jsmn") register_component() diff --git a/components/bootloader/CMakeLists.txt b/components/bootloader/CMakeLists.txt index 1b694f3819..d38b8ff8e1 100644 --- a/components/bootloader/CMakeLists.txt +++ b/components/bootloader/CMakeLists.txt @@ -1,2 +1,7 @@ -# TODO: add config stuff for bootloader here +# bootloader component logic is all in project_include.cmake, +# and subproject/CMakeLists.txt. +# +# This file is only included so the build system finds the +# component + diff --git a/components/bootloader/subproject/CMakeLists.txt b/components/bootloader/subproject/CMakeLists.txt index 5ac887a404..719d9fb9da 100644 --- a/components/bootloader/subproject/CMakeLists.txt +++ b/components/bootloader/subproject/CMakeLists.txt @@ -9,6 +9,8 @@ set(COMPONENTS bootloader esptool_py esp32 soc bootloader_support log spi_flash set(BOOTLOADER_BUILD 1) add_definitions(-DBOOTLOADER_BUILD=1) +set(COMPONENT_REQUIRES_COMMON log esp32 soc) + set(MAIN_SRCS main/bootloader_start.c) include($ENV{IDF_PATH}/tools/cmake/project.cmake) diff --git a/components/bootloader_support/CMakeLists.txt b/components/bootloader_support/CMakeLists.txt index 6df7b24db9..21978397e8 100644 --- a/components/bootloader_support/CMakeLists.txt +++ b/components/bootloader_support/CMakeLists.txt @@ -2,9 +2,13 @@ set(COMPONENT_SRCDIRS "src") if(${BOOTLOADER_BUILD}) set(COMPONENT_ADD_INCLUDEDIRS "include include_priv") + set(COMPONENT_REQUIRES) + set(COMPONENT_PRIV_REQUIRES spi_flash micro-ecc) else() set(COMPONENT_ADD_INCLUDEDIRS "include") set(COMPONENT_PRIV_INCLUDEDIRS "include_priv") + set(COMPONENT_REQUIRES) + set(COMPONENT_PRIV_REQUIRES spi_flash mbedtls micro-ecc) endif() register_component() diff --git a/components/bt/CMakeLists.txt b/components/bt/CMakeLists.txt index 9431645e84..c2134be8f4 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -1,83 +1,90 @@ -if(NOT CONFIG_BT_ENABLED) - return() -endif() - -set(COMPONENT_SRCDIRS .) -set(COMPONENT_ADD_INCLUDEDIRS include) +if(CONFIG_BT_ENABLED) -if(CONFIG_BLUEDROID_ENABLED) + set(COMPONENT_SRCDIRS .) + set(COMPONENT_ADD_INCLUDEDIRS include) - list(APPEND COMPONENT_ADD_INCLUDEDIRS - bluedroid/bta/include - bluedroid/bta/sys/include - bluedroid/btcore/include - bluedroid/device/include - bluedroid/hci/include - bluedroid/osi/include - bluedroid/external/sbc/decoder/include - bluedroid/external/sbc/encoder/include - bluedroid/btc/profile/esp/blufi/include - bluedroid/btc/profile/esp/include - bluedroid/btc/profile/std/a2dp/include - bluedroid/btc/profile/std/include - bluedroid/btc/include - bluedroid/stack/gap/include - bluedroid/stack/gatt/include - bluedroid/stack/l2cap/include - bluedroid/stack/sdp/include - bluedroid/stack/smp/include - bluedroid/stack/avct/include - bluedroid/stack/avrc/include - bluedroid/stack/avdt/include - bluedroid/stack/a2dp/include - bluedroid/stack/rfcomm/include - bluedroid/stack/include - bluedroid/api/include - bluedroid/include) + if(CONFIG_BLUEDROID_ENABLED) - list(APPEND COMPONENT_SRCDIRS - bluedroid/bta/dm - bluedroid/bta/gatt - bluedroid/bta/hh - bluedroid/bta/sdp - bluedroid/bta/av - bluedroid/bta/ar - bluedroid/bta/sys - bluedroid/bta/jv - bluedroid/btcore - bluedroid/btif - bluedroid/device - bluedroid/hci - bluedroid/main - bluedroid/osi - bluedroid/external/sbc/decoder/srce - bluedroid/external/sbc/encoder/srce - bluedroid/btc/core - bluedroid/btc/profile/esp/blufi - bluedroid/btc/profile/std/gap - bluedroid/btc/profile/std/gatt - bluedroid/btc/profile/std/a2dp - bluedroid/btc/profile/std/avrc - bluedroid/btc/profile/std/spp - bluedroid/stack/btm - bluedroid/stack/btu - bluedroid/stack/gap - bluedroid/stack/gatt - bluedroid/stack/hcic - bluedroid/stack/l2cap - bluedroid/stack/sdp - bluedroid/stack/smp - bluedroid/stack/avct - bluedroid/stack/avrc - bluedroid/stack/avdt - bluedroid/stack/a2dp - bluedroid/stack/rfcomm - bluedroid/api - ) + list(APPEND COMPONENT_ADD_INCLUDEDIRS + bluedroid/bta/include + bluedroid/bta/sys/include + bluedroid/btcore/include + bluedroid/device/include + bluedroid/hci/include + bluedroid/osi/include + bluedroid/external/sbc/decoder/include + bluedroid/external/sbc/encoder/include + bluedroid/btc/profile/esp/blufi/include + bluedroid/btc/profile/esp/include + bluedroid/btc/profile/std/a2dp/include + bluedroid/btc/profile/std/include + bluedroid/btc/include + bluedroid/stack/gap/include + bluedroid/stack/gatt/include + bluedroid/stack/l2cap/include + bluedroid/stack/sdp/include + bluedroid/stack/smp/include + bluedroid/stack/avct/include + bluedroid/stack/avrc/include + bluedroid/stack/avdt/include + bluedroid/stack/a2dp/include + bluedroid/stack/rfcomm/include + bluedroid/stack/include + bluedroid/api/include + bluedroid/include + ) + list(APPEND COMPONENT_SRCDIRS + bluedroid/bta/dm + bluedroid/bta/gatt + bluedroid/bta/hh + bluedroid/bta/sdp + bluedroid/bta/av + bluedroid/bta/ar + bluedroid/bta/sys + bluedroid/bta/jv + bluedroid/btcore + bluedroid/btif + bluedroid/device + bluedroid/hci + bluedroid/main + bluedroid/osi + bluedroid/external/sbc/decoder/srce + bluedroid/external/sbc/encoder/srce + bluedroid/btc/core + bluedroid/btc/profile/esp/blufi + bluedroid/btc/profile/std/gap + bluedroid/btc/profile/std/gatt + bluedroid/btc/profile/std/a2dp + bluedroid/btc/profile/std/avrc + bluedroid/btc/profile/std/spp + bluedroid/stack/btm + bluedroid/stack/btu + bluedroid/stack/gap + bluedroid/stack/gatt + bluedroid/stack/hcic + bluedroid/stack/l2cap + bluedroid/stack/sdp + bluedroid/stack/smp + bluedroid/stack/avct + bluedroid/stack/avrc + bluedroid/stack/avdt + bluedroid/stack/a2dp + bluedroid/stack/rfcomm + bluedroid/api + ) + endif() endif() +# requirements can't depend on config +set(COMPONENT_PRIV_REQUIRES lwip nvs_flash) + +# currently uses libcoap's hash table in hashkey.h +set(COMPONENT_REQUIRES coap) + register_component() -target_link_libraries(bt "-L${CMAKE_CURRENT_LIST_DIR}/lib") -target_link_libraries(bt btdm_app) +if(CONFIG_BT_ENABLED) + target_link_libraries(bt "-L${CMAKE_CURRENT_LIST_DIR}/lib") + target_link_libraries(bt btdm_app) +endif() diff --git a/components/coap/CMakeLists.txt b/components/coap/CMakeLists.txt index cadd463faf..faba933094 100644 --- a/components/coap/CMakeLists.txt +++ b/components/coap/CMakeLists.txt @@ -19,6 +19,8 @@ set(COMPONENT_SRCS port/coap_io_socket.c ) +set(COMPONENT_REQUIRES lwip) + register_component() # Needed for coap headers in public builds, also. diff --git a/components/console/CMakeLists.txt b/components/console/CMakeLists.txt index 1c5648654c..0b0da5394a 100644 --- a/components/console/CMakeLists.txt +++ b/components/console/CMakeLists.txt @@ -1,5 +1,7 @@ set(COMPONENT_ADD_INCLUDEDIRS .) set(COMPONENT_SRCDIRS linenoise argtable3 .) +set(COMPONENT_REQUIRES) + register_component() diff --git a/components/cxx/CMakeLists.txt b/components/cxx/CMakeLists.txt index 255f4f3fd7..a797640cc0 100644 --- a/components/cxx/CMakeLists.txt +++ b/components/cxx/CMakeLists.txt @@ -1,4 +1,5 @@ set(COMPONENT_SRCDIRS ".") +set(COMPONENT_REQUIRES) register_component() target_link_libraries(cxx stdc++) diff --git a/components/driver/CMakeLists.txt b/components/driver/CMakeLists.txt index 8ccaad2c32..0c72aecc43 100644 --- a/components/driver/CMakeLists.txt +++ b/components/driver/CMakeLists.txt @@ -2,4 +2,6 @@ set(COMPONENT_SRCDIRS ".") set(COMPONENT_ADD_INCLUDEDIRS "include") set(COMPONENT_PRIV_INCLUDEDIRS "include/driver") +set(COMPONENT_REQUIRES) + register_component() diff --git a/components/esp-tls/CMakeLists.txt b/components/esp-tls/CMakeLists.txt new file mode 100644 index 0000000000..9bad428680 --- /dev/null +++ b/components/esp-tls/CMakeLists.txt @@ -0,0 +1,7 @@ +set(COMPONENT_SRCDIRS ".") +set(COMPONENT_ADD_INCLUDEDIRS ".") + +set(COMPONENT_REQUIRES mbedtls) +set(COMPONENT_PRIV_REQUIRES lwip nghttp) + +register_component() diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index 1172dda7fb..b231bfd0d7 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -1,7 +1,9 @@ if(BOOTLOADER_BUILD) # For bootloader, all we need from esp32 is headers - add_library(esp32 INTERFACE) - target_include_directories(esp32 INTERFACE include) + set(COMPONENT_ADD_INCLUDEDIRS include) + set(COMPONENT_REQUIRES ${COMPONENTS}) + set(COMPONENT_SRCDIRS "") + register_component(esp32) # as cmake won't attach linker args to a header-only library, attach # linker args directly to the bootloader.elf @@ -18,6 +20,11 @@ else() set(COMPONENT_SRCDIRS ". hwcrypto") set(COMPONENT_ADD_INCLUDEDIRS "include") + set(COMPONENT_REQUIRES driver) # required because esp_sleep.h uses gpio_num_t & touch_pad_t + set(COMPONENT_PRIV_REQUIRES + adapter app bootloader_debug ethernet_flash flash_log mbedtls_module nvs_pthread + spi-supplicant-support tcpip trace_vfs wpa xtensa) + register_component() target_link_libraries(esp32 "-L ${CMAKE_CURRENT_SOURCE_DIR}/lib") diff --git a/components/esp_adc_cal/CMakeLists.txt b/components/esp_adc_cal/CMakeLists.txt index ba2fa9c7e7..fc21f1ecac 100644 --- a/components/esp_adc_cal/CMakeLists.txt +++ b/components/esp_adc_cal/CMakeLists.txt @@ -1,4 +1,6 @@ set(COMPONENT_SRCDIRS ".") set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_REQUIRES) + register_component() diff --git a/components/ethernet/CMakeLists.txt b/components/ethernet/CMakeLists.txt index a9eb903703..165d89fbe1 100644 --- a/components/ethernet/CMakeLists.txt +++ b/components/ethernet/CMakeLists.txt @@ -1,4 +1,7 @@ set(COMPONENT_SRCDIRS . eth_phy) set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_REQUIRES) +set(COMPONENT_PRIV_REQUIRES tcpip_adapter) + register_component() diff --git a/components/expat/CMakeLists.txt b/components/expat/CMakeLists.txt index 9e77a825c4..5791a26599 100644 --- a/components/expat/CMakeLists.txt +++ b/components/expat/CMakeLists.txt @@ -1,6 +1,8 @@ set(COMPONENT_ADD_INCLUDEDIRS port/include include/expat) set(COMPONENT_SRCDIRS library port) +set(COMPONENT_REQUIRES) + register_component() component_compile_definitions(HAVE_EXPAT_CONFIG_H) diff --git a/components/fatfs/CMakeLists.txt b/components/fatfs/CMakeLists.txt index c9f5a0fcfd..3d24c50bf9 100644 --- a/components/fatfs/CMakeLists.txt +++ b/components/fatfs/CMakeLists.txt @@ -1,4 +1,6 @@ set(COMPONENT_SRCDIRS src) set(COMPONENT_ADD_INCLUDEDIRS src) +set(COMPONENT_REQUIRES wear_levelling sdmmc) + register_component() diff --git a/components/freertos/CMakeLists.txt b/components/freertos/CMakeLists.txt index 0a5172dc10..a0be8f0862 100644 --- a/components/freertos/CMakeLists.txt +++ b/components/freertos/CMakeLists.txt @@ -1,6 +1,8 @@ set(COMPONENT_ADD_INCLUDEDIRS include) set(COMPONENT_PRIV_INCLUDEDIRS include/freertos) set(COMPONENT_SRCDIRS ".") +set(COMPONENT_REQUIRES) + register_component() target_link_libraries(freertos "-Wl,--undefined=uxTopUsedPriority") diff --git a/components/heap/CMakeLists.txt b/components/heap/CMakeLists.txt index 08e78e8458..b7dd5d9ac1 100644 --- a/components/heap/CMakeLists.txt +++ b/components/heap/CMakeLists.txt @@ -6,6 +6,8 @@ endif() set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_REQUIRES "") + register_component() if(CONFIG_HEAP_TRACING) diff --git a/components/idf_test/CMakeLists.txt b/components/idf_test/CMakeLists.txt index e307838a8b..cf64054dd8 100644 --- a/components/idf_test/CMakeLists.txt +++ b/components/idf_test/CMakeLists.txt @@ -1,2 +1,3 @@ set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_REQUIRES) register_component() diff --git a/components/jsmn/CMakeLists.txt b/components/jsmn/CMakeLists.txt index 2eabcf5fe6..7c19d9ccba 100644 --- a/components/jsmn/CMakeLists.txt +++ b/components/jsmn/CMakeLists.txt @@ -1,4 +1,6 @@ set(COMPONENT_SRCDIRS "src") set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_REQUIRES "") + register_component() diff --git a/components/json/CMakeLists.txt b/components/json/CMakeLists.txt index c19ec43fa8..0b8ba83d66 100644 --- a/components/json/CMakeLists.txt +++ b/components/json/CMakeLists.txt @@ -1,4 +1,6 @@ set(COMPONENT_SRCDIRS cJSON) set(COMPONENT_ADD_INCLUDEDIRS cJSON) +set(COMPONENT_REQUIRES "") + register_component() diff --git a/components/libsodium/CMakeLists.txt b/components/libsodium/CMakeLists.txt index 48e5a3f690..674534c916 100644 --- a/components/libsodium/CMakeLists.txt +++ b/components/libsodium/CMakeLists.txt @@ -1,5 +1,7 @@ set(SRC libsodium/src/libsodium) +set(COMPONENT_REQUIRES "mbedtls") + set(COMPONENT_SRCDIRS port diff --git a/components/log/CMakeLists.txt b/components/log/CMakeLists.txt index eebd20a046..ad162fe1e1 100644 --- a/components/log/CMakeLists.txt +++ b/components/log/CMakeLists.txt @@ -1,3 +1,4 @@ set(COMPONENT_SRCDIRS ".") set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_REQUIRES) register_component() diff --git a/components/lwip/CMakeLists.txt b/components/lwip/CMakeLists.txt index 5474c4daad..03deae4369 100644 --- a/components/lwip/CMakeLists.txt +++ b/components/lwip/CMakeLists.txt @@ -16,6 +16,9 @@ set(COMPONENT_SRCDIRS ${LWIP_PPP_DIRS} netif port/freertos port/netif port/debug port) +set(COMPONENT_REQUIRES "") +set(COMPONENT_PRIV_REQUIRES ethernet tcpip_adapter) + register_component() component_compile_options(-Wno-address) diff --git a/components/mbedtls/CMakeLists.txt b/components/mbedtls/CMakeLists.txt index 56dfce17cf..44410c1492 100644 --- a/components/mbedtls/CMakeLists.txt +++ b/components/mbedtls/CMakeLists.txt @@ -1,6 +1,8 @@ set(COMPONENT_ADD_INCLUDEDIRS port/include include) set(COMPONENT_SRCDIRS library port) +set(COMPONENT_REQUIRES lwip) + register_component() target_compile_definitions(mbedtls PUBLIC diff --git a/components/mdns/CMakeLists.txt b/components/mdns/CMakeLists.txt index 149dcb0bcc..4ab6641109 100644 --- a/components/mdns/CMakeLists.txt +++ b/components/mdns/CMakeLists.txt @@ -1,5 +1,7 @@ set(COMPONENT_SRCDIRS ".") set(COMPONENT_ADD_INCLUDEDIRS "include") set(COMPONENT_PRIV_INCLUDEDIRS "private_include") +set(COMPONENT_REQUIRES lwip mbedtls console tcpip_adapter) + register_component() diff --git a/components/micro-ecc/CMakeLists.txt b/components/micro-ecc/CMakeLists.txt index 13bc9cfb64..24e0a9e6f6 100644 --- a/components/micro-ecc/CMakeLists.txt +++ b/components/micro-ecc/CMakeLists.txt @@ -3,4 +3,6 @@ set(COMPONENT_SRCS micro-ecc/uECC.c) set(COMPONENT_ADD_INCLUDEDIRS micro-ecc) +set(COMPONENT_REQUIRES) + register_component() diff --git a/components/newlib/CMakeLists.txt b/components/newlib/CMakeLists.txt index c92518deb3..4bc0db3f90 100644 --- a/components/newlib/CMakeLists.txt +++ b/components/newlib/CMakeLists.txt @@ -15,6 +15,8 @@ else() set(LIBM m) endif() +set(COMPONENT_REQUIRES vfs) # for sys/ioctl.h + register_component() target_link_libraries(newlib "-L ${CMAKE_CURRENT_SOURCE_DIR}/lib") diff --git a/components/nghttp/CMakeLists.txt b/components/nghttp/CMakeLists.txt index c7e4b754b8..bf3f727468 100644 --- a/components/nghttp/CMakeLists.txt +++ b/components/nghttp/CMakeLists.txt @@ -1,4 +1,6 @@ set(COMPONENT_ADD_INCLUDEDIRS port/include nghttp2/lib/includes) set(COMPONENT_SRCDIRS nghttp2/lib port) +set(COMPONENT_REQUIRES lwip mbedtls) + register_component() diff --git a/components/nvs_flash/CMakeLists.txt b/components/nvs_flash/CMakeLists.txt index 5970a79154..1fe5ecf26e 100644 --- a/components/nvs_flash/CMakeLists.txt +++ b/components/nvs_flash/CMakeLists.txt @@ -1,4 +1,6 @@ set(COMPONENT_SRCDIRS src) set(COMPONENT_ADD_INCLUDEDIRS include) +set(COMPONENT_REQUIRES spi_flash) + register_component() diff --git a/components/openssl/CMakeLists.txt b/components/openssl/CMakeLists.txt index 347ade6387..84d02ffe88 100644 --- a/components/openssl/CMakeLists.txt +++ b/components/openssl/CMakeLists.txt @@ -2,4 +2,6 @@ set(COMPONENT_ADD_INCLUDEDIRS include) set(COMPONENT_PRIV_INCLUDEDIRS include/internal include/platform include/openssl) set(COMPONENT_SRCDIRS library platform) +set(COMPONENT_REQUIRES mbedtls) + register_component() diff --git a/components/partition_table/CMakeLists.txt b/components/partition_table/CMakeLists.txt index 87f12da943..cde193dce9 100644 --- a/components/partition_table/CMakeLists.txt +++ b/components/partition_table/CMakeLists.txt @@ -1,5 +1,7 @@ set(partition_table_offset 0x8000) +register_config_only_component() + # Set partition_csv to the configured partition source file # if(CONFIG_PARTITION_TABLE_CUSTOM) @@ -56,4 +58,3 @@ set_property(GLOBAL APPEND_STRING PROPERTY ESPTOOL_WRITE_FLASH_ARGS "${partition_table_offset} ${final_partition_bin} ") -register_config_only_component() diff --git a/components/pthread/CMakeLists.txt b/components/pthread/CMakeLists.txt index eebd20a046..ad162fe1e1 100644 --- a/components/pthread/CMakeLists.txt +++ b/components/pthread/CMakeLists.txt @@ -1,3 +1,4 @@ set(COMPONENT_SRCDIRS ".") set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_REQUIRES) register_component() diff --git a/components/sdmmc/CMakeLists.txt b/components/sdmmc/CMakeLists.txt index ba2fa9c7e7..58b9aeb679 100644 --- a/components/sdmmc/CMakeLists.txt +++ b/components/sdmmc/CMakeLists.txt @@ -1,4 +1,4 @@ set(COMPONENT_SRCDIRS ".") set(COMPONENT_ADD_INCLUDEDIRS "include") - +set(COMPONENT_REQUIRES driver) register_component() diff --git a/components/smartconfig/CMakeLists.txt b/components/smartconfig/CMakeLists.txt index ba2fa9c7e7..8b18007dea 100644 --- a/components/smartconfig/CMakeLists.txt +++ b/components/smartconfig/CMakeLists.txt @@ -1,4 +1,6 @@ set(COMPONENT_SRCDIRS ".") set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_PRIV_REQUIRES lwip tcpip_adapter) + register_component() diff --git a/components/soc/CMakeLists.txt b/components/soc/CMakeLists.txt index 24e8595514..b4c088edd7 100644 --- a/components/soc/CMakeLists.txt +++ b/components/soc/CMakeLists.txt @@ -3,4 +3,7 @@ set(SOC_NAME esp32) set(COMPONENT_SRCDIRS ${SOC_NAME}) set(COMPONENT_ADD_INCLUDEDIRS ${SOC_NAME}/include include) +set(COMPONENT_REQUIRES) + + register_component() diff --git a/components/spi_flash/CMakeLists.txt b/components/spi_flash/CMakeLists.txt index f64a6ec117..1eafba54d4 100644 --- a/components/spi_flash/CMakeLists.txt +++ b/components/spi_flash/CMakeLists.txt @@ -2,10 +2,13 @@ if(BOOTLOADER_BUILD) # Bootloader needs SPIUnlock from this file, but doesn't # need other parts of this component set(COMPONENT_SRCS spi_flash_rom_patch.c) + set(COMPONENT_PRIV_REQUIRES bootloader_support) else() set(COMPONENT_SRCDIRS .) + set(COMPONENT_PRIV_REQUIRES bootloader_support app_update) endif() set(COMPONENT_ADD_INCLUDEDIRS include) +set(COMPONENT_REQUIRES) register_component() diff --git a/components/spiffs/CMakeLists.txt b/components/spiffs/CMakeLists.txt index 5e0027f52d..a85f98d2b3 100644 --- a/components/spiffs/CMakeLists.txt +++ b/components/spiffs/CMakeLists.txt @@ -1,5 +1,9 @@ set(COMPONENT_ADD_INCLUDEDIRS "include") set(COMPONENT_PRIV_INCLUDEDIRS "spiffs/src") set(COMPONENT_SRCDIRS ". spiffs/src") + +set(COMPONENT_REQUIRES spi_flash) +set(COMPONENT_PRIV_REQUIRES bootloader_support) + register_component() diff --git a/components/tcpip_adapter/CMakeLists.txt b/components/tcpip_adapter/CMakeLists.txt index ba2fa9c7e7..24deb2f247 100644 --- a/components/tcpip_adapter/CMakeLists.txt +++ b/components/tcpip_adapter/CMakeLists.txt @@ -1,4 +1,6 @@ set(COMPONENT_SRCDIRS ".") set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_REQUIRES lwip) + register_component() diff --git a/components/ulp/CMakeLists.txt b/components/ulp/CMakeLists.txt index ba2fa9c7e7..fc21f1ecac 100644 --- a/components/ulp/CMakeLists.txt +++ b/components/ulp/CMakeLists.txt @@ -1,4 +1,6 @@ set(COMPONENT_SRCDIRS ".") set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_REQUIRES) + register_component() diff --git a/components/vfs/CMakeLists.txt b/components/vfs/CMakeLists.txt index ba2fa9c7e7..fc21f1ecac 100644 --- a/components/vfs/CMakeLists.txt +++ b/components/vfs/CMakeLists.txt @@ -1,4 +1,6 @@ set(COMPONENT_SRCDIRS ".") set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_REQUIRES) + register_component() diff --git a/components/wear_levelling/CMakeLists.txt b/components/wear_levelling/CMakeLists.txt index a72d0f565f..8b5db6231d 100644 --- a/components/wear_levelling/CMakeLists.txt +++ b/components/wear_levelling/CMakeLists.txt @@ -1,4 +1,5 @@ set(COMPONENT_SRCDIRS ".") set(COMPONENT_ADD_INCLUDEDIRS "include") set(COMPONENT_PRIV_INCLUDEDIRS private_include) +set(COMPONENT_REQUIRES spi_flash) register_component() diff --git a/components/wpa_supplicant/CMakeLists.txt b/components/wpa_supplicant/CMakeLists.txt index 1d2091656a..fc0c323fc9 100644 --- a/components/wpa_supplicant/CMakeLists.txt +++ b/components/wpa_supplicant/CMakeLists.txt @@ -1,6 +1,9 @@ set(COMPONENT_SRCDIRS src/crypto port src/fast_crypto) set(COMPONENT_ADD_INCLUDEDIRS include port/include) +set(COMPONENT_REQUIRES "") +set(COMPONENT_PRIV_REQUIRES lwip mbedtls) + register_component() component_compile_definitions(__ets__) diff --git a/components/xtensa-debug-module/CMakeLists.txt b/components/xtensa-debug-module/CMakeLists.txt index ba2fa9c7e7..810d299d13 100644 --- a/components/xtensa-debug-module/CMakeLists.txt +++ b/components/xtensa-debug-module/CMakeLists.txt @@ -1,4 +1,6 @@ set(COMPONENT_SRCDIRS ".") set(COMPONENT_ADD_INCLUDEDIRS "include") +set(COMPONENT_REQUIRES "") + register_component() diff --git a/docs/en/api-guides/build-system-cmake.rst b/docs/en/api-guides/build-system-cmake.rst index a4de840608..03eb0c206f 100644 --- a/docs/en/api-guides/build-system-cmake.rst +++ b/docs/en/api-guides/build-system-cmake.rst @@ -78,7 +78,7 @@ The :ref:`getting started guide ` contains a brief ``idf.py`` should be run in an ESP-IDF "project" directory, ie one containing a ``CMakeLists.txt`` file. Older style projects with a Makefile will not work with ``idf.py``. -Type ``idf.py --help`` for a full list of commands. Here are a summary of a few: +Type ``idf.py --help`` for a full list of commands. Here are a summary of the most useful ones: - ``idf.py menuconfig`` runs the "menuconfig" tool to configure the project. - ``idf.py build`` will build the project found in the current directory. This can involve multiple steps: @@ -207,7 +207,8 @@ These variables all have default values that can be overridden for custom behavi - ``COMPONENT_DIRS``: Directories to search for components. Defaults to `${IDF_PATH}/components`, `${PROJECT_PATH}/components`, and ``EXTRA_COMPONENT_DIRS``. Override this variable if you don't want to search for components in these places. - ``EXTRA_COMPONENT_DIRS``: Optional list of additional directories to search for components. Paths can be relative to the project directory, or absolute. -- ``COMPONENTS``: A list of component names to build into the project. Defaults to all components found in the ``COMPONENT_DIRS`` directories. Use this variable to "trim down" the project for faster build times. +- ``COMPONENTS``: A list of component names to build into the project. Defaults to all components found in the ``COMPONENT_DIRS`` directories. Use this variable to "trim down" the project for faster build times. Note that any component which "requires" another component via ``COMPONENT_REQUIRES`` will automatically have it added to this list, so the ``COMPONENTS`` list doesn't need to include dependencies in this way. +- ``COMPONENT_REQUIRES_COMMON``: A list of components that every component requires. These components are automatically treated to be in every component's ``COMPONENT_PRIV_REQUIRES`` list and also the project's ``COMPONENTS`` list. By default, this is set to the minimal set of core "system" components needed for any ESP-IDF project. Any paths in these variables can be absolute paths, or set relative to the project directory. @@ -273,22 +274,18 @@ The following variables are set at the project level, but available for use in c If you modify any of these variables inside ``CMakeLists.txt`` then this will not prevent other components from building but it may make your component hard to build and/or debug. -Optional Project-Wide Component Variables ------------------------------------------ - -The following variables can be set inside component ``CMakeLists.txt`` to control build settings across the entire project: - ``COMPONENT_ADD_INCLUDEDIRS``: Paths, relative to the component directory, which will be added to the include search path for - all components in the project. If an include directory is only needed to compile + all other components which require this one. If an include directory is only needed to compile this specific component, add it to ``COMPONENT_PRIV_INCLUDEDIRS`` instead. -- ``COMPONENT_DEPENDS``: Optional list of component names that should be - compiled before this component. This is not necessary for - link-time dependencies, because all component include directories - are available at all times. It is necessary if one component - generates an include file which you then want to include in another - component. Most components do not need to set this variable. +- ``COMPONENT_REQUIRES`` is a (space-separated) list of components that are required to include this project's header files into other components. If a public header header file listed in ``COMPONENT_ADD_INCLUDEDIRS`` includes a header from another component, that component should be listed in ``COMPONENT_REQUIRES``. Requirements are recursive. + + The ``COMPONENT_REQUIRES`` list can be empty because some very common components (like newlib for libc, freertos for RTOS functions, etc) are always required by all components. This list is found in the project-level variable ``COMPONENT_REQUIRES_COMMON``. + If a component only requires another component's headers to compile its source files (not for including this component's headers), then these components should be listed in ``COMPONENT_PRIV_REQUIRES`` instead. + + See `Component Requirements` for more details. Optional Component-Specific Variables ------------------------------------- @@ -298,6 +295,7 @@ The following variables can be set inside ``component.mk`` to control the build - ``COMPONENT_PRIV_INCLUDEDIRS``: Directory paths, must be relative to the component directory, which will be added to the include search path for this component's source files only. +- ``COMPONENT_PRIV_REQUIRES`` is a (space-separated) list of components that are required to either compile or link this component's source files. These components' header paths do not propagate to other components which require it, they are only used to compile this component's sources. See `Component Requirements` for more details. - ``COMPONENT_SRCDIRS``: Directory paths, must be relative to the component directory, which will be searched for source files (``*.cpp``, ``*.c``, ``*.S``). Set this to specify a list of directories @@ -345,6 +343,45 @@ The ESP-IDF build system adds the following C preprocessor definitions on the co - ``ESP_PLATFORM`` — Can be used to detect that build happens within ESP-IDF. - ``IDF_VER`` — Defined to a git version string. E.g. ``v2.0`` for a tagged release or ``v1.0-275-g0efaa4f`` for an arbitrary commit. +Component Requirements +====================== + +When compiling each component, the ESP-IDF build system recurisvely evaluates its components. + +Each component's source file is compiled with these include path directories: + +- The current component's ``COMPONENT_ADD_INCLUDEDIRS`` and ``COMPONENT_PRIV_INCLUDEDIRS``. +- The ``COMPONENT_ADD_INCLUDEDIRS`` set by all components in the current component's ``COMPONENT_REQUIRES`` and ``COMPONENT_PRIV_REQUIRES`` variables (ie all the current component's public and private dependencies). +- All of the ``COMPONENT_REQUIRES`` of those components, evaluated recursively (ie all public dependencies of this component's dependencies, recursively expanded). + +When writing a component +------------------------ + +- ``COMPONENT_REQUIRES`` should be set to all components whose header files are #included from the *public* header files of this component. +- ``COMPONENT_PRIV_REQUIRES`` should be set to all components whose header files are #included from *any source files* of this component, unless already listed in ``COMPONENT_REQUIRES``. Or any component which is required to be linked in order for this component to function correctly. +- ``COMPONENT_REQUIRES`` and/or ``COMPONENT_PRIV_REQUIRES`` should be set before calling ``register_component()``. +- The values of ``COMPONENT_REQUIRES`` and ``COMPONENT_PRIV_REQUIRES`` should not depend on any configuration choices (``CONFIG_xxx`` macros). This is because requirements are expanded before configuration is loaded. Other component variables (like include paths or source files) can depend on configuration choices. +- Not setting either or both ``REQUIRES`` variables is fine. If the component has no requirements except for the "common" components needed for RTOS, libc, etc (``COMPONENT_REQUIRES_COMMON``) then both variables can be empty or unset. + +When creating a project +----------------------- + +- By default, every component is included in the build. +- If you set the ``COMPONENTS`` variable to a minimal list of components used directly by your project, then the build will include: + - Components mentioned explicitly in ``COMPONENTS``. + - Those components' requirements (evaluated recursively). + - The "common" components that every component depends on. +- Setting ``COMPONENTS`` to the minimal list of components you need can significantly reduce your project's compile time. +- When compiling the project's source files (``MAIN_SRCS``), the public header directories of all components included in the build are available. + +Requirements in the build system implementation +------------------------------------------------------------------ + +- Very early in the cmake configuration process, the script ``expand_requirements.cmake`` is run. This script does a partial evaluation of all component CMakeLists.txt files and builds a graph of component requirements (this graph may have cycles). The graph is used to generate a file ``component_depends.cmake`` in the build directory. +- The main cmake process then includes this file and uses it to determine the list of components to include in the build (internal ``BUILD_COMPONENTS`` variable). +- Configuration is then evaluated for the components included in the build. +- Each component is included in the build normally and the CMakeLists.txt file is evaluated again to add the component libraries to the build. + Build Process Internals ======================= @@ -362,6 +399,7 @@ project function The custom ``project()`` function performs the following steps: +- Evaluates component dependencies and builds the ``BUILD_COMPONENTS`` list of components to include in the build (see `above`). - Finds all components in the project (searching ``COMPONENT_DIRS`` and filtering by ``COMPONENTS`` if this is set). - Loads the project configuration from the ``sdkconfig`` file and produces a ``cmake`` include file and a C header file, to set config macros. If the project configuration changes, cmake will automatically be re-run to reconfigure the project. - Sets the `CMAKE_TOOLCHAIN_FILE`_ variable to the ESP-IDF toolchain file with the Xtensa ESP32 toolchain. @@ -525,10 +563,8 @@ why it is added to the `ADDITIONAL_MAKE_CLEAN_FILES`_ property. (Note: If generating files as part of the project CMakeLists, not a component CMakeLists, use ``${PROJECT_PATH}`` instead of ``${COMPONENT_PATH}`` and ``${PROJECT_NAME}.elf`` instead of ``${COMPONENT_NAME}``.) -If a a source file from another component included ``logo.h``, then this -component's name would have to be added to the other component's -``COMPONENT_DEPENDS`` list to ensure that the components were built -in-order. +If a a source file from another component included ``logo.h``, then ``add_dependencies`` would need to be called to add a dependency between +the two components, to ensure that the component source files were always compiled in the correct order. Embedding Binary Data --------------------- diff --git a/docs/en/api-guides/build-system.rst b/docs/en/api-guides/build-system.rst index e638a36d07..8d3830c8bb 100644 --- a/docs/en/api-guides/build-system.rst +++ b/docs/en/api-guides/build-system.rst @@ -202,7 +202,7 @@ The following variables can be set inside ``component.mk`` to control build sett the app executable. Defaults to ``-l$(COMPONENT_NAME)``. If adding pre-compiled libraries to this directory, add them as absolute paths - ie $(COMPONENT_PATH)/libwhatever.a -- ``COMPONENT_DEPENDS``: Optional list of component names that should +- ``COMPONENT_REQUIRES``: Optional list of component names that should be compiled before this component. This is not necessary for link-time dependencies, because all component include directories are available at all times. It is necessary if one component @@ -544,7 +544,7 @@ generated before ``graphics_lib.c`` is compiled. If a a source file in another component included ``logo.h``, then this component's name would have to be added to the other component's -``COMPONENT_DEPENDS`` list to ensure that the components were built +``COMPONENT_REQUIRES`` list to ensure that the components were built in-order. Embedding Binary Data diff --git a/examples/protocols/http2_request/components/sh2lib/CMakeLists.txt b/examples/protocols/http2_request/components/sh2lib/CMakeLists.txt index 74bb26787e..6d19bf48b6 100644 --- a/examples/protocols/http2_request/components/sh2lib/CMakeLists.txt +++ b/examples/protocols/http2_request/components/sh2lib/CMakeLists.txt @@ -2,4 +2,7 @@ set(COMPONENT_ADD_INCLUDEDIRS .) set(COMPONENT_SRCDIRS .) +set(COMPONENT_REQUIRES nghttp) +set(COMPONENT_PRIV_REQUIRES lwip esp-tls) + register_component() diff --git a/tools/cmake/components.cmake b/tools/cmake/components.cmake index 4334ffee7d..506d8a06fa 100644 --- a/tools/cmake/components.cmake +++ b/tools/cmake/components.cmake @@ -1,53 +1,16 @@ -# Search 'component_dirs' for components and return them -# as a list of names in 'component_names' and a list of full paths in -# 'component_paths' -# -# component_paths contains only unique component names. Directories -# earlier in the component_dirs list take precedence. -function(components_find_all component_dirs filter_names component_paths component_names) - # component_dirs entries can be files or lists of files - set(paths "") - set(names "") - - # start by expanding the component_dirs list with all subdirectories - foreach(dir ${component_dirs}) - # Iterate any subdirectories for values - file(GLOB subdirs LIST_DIRECTORIES true "${dir}/*") - foreach(subdir ${subdirs}) - set(component_dirs "${component_dirs};${subdir}") - endforeach() - endforeach() - - # Look for a component in each component_dirs entry - foreach(dir ${component_dirs}) - file(GLOB component "${dir}/CMakeLists.txt") - if(component) - get_filename_component(component "${component}" DIRECTORY) - get_filename_component(name "${component}" NAME) - if(NOT filter_names OR (name IN_LIST filter_names)) - if(NOT name IN_LIST names) - set(names "${names};${name}") - set(paths "${paths};${component}") - endif() - endif() - - else() # no CMakeLists.txt file - # test for legacy component.mk and warn - file(GLOB legacy_component "${dir}/component.mk") - if(legacy_component) - get_filename_component(legacy_component "${legacy_component}" DIRECTORY) - message(WARNING "Component ${legacy_component} contains old-style component.mk but no CMakeLists.txt. " - "Component will be skipped.") - endif() +# Given a list of components in 'component_paths', filter only paths to the components +# mentioned in 'components' and return as a list in 'result_paths' +function(components_get_paths component_paths components result_paths) + set(result "") + foreach(path ${component_paths}) + get_filename_component(name "${path}" NAME) + if("${name}" IN_LIST components) + list(APPEND result "${name}") endif() - endforeach() - - set(${component_paths} ${paths} PARENT_SCOPE) - set(${component_names} ${names} PARENT_SCOPE) + set("${result_path}" "${result}" PARENT_SCOPE) endfunction() - # Add a component to the build, using the COMPONENT variables defined # in the parent # @@ -76,7 +39,7 @@ function(register_component) endforeach() endif() - # add public includes from other components when building this component + # add as a PUBLIC library (if there are source files) or INTERFACE (if header only) if(COMPONENT_SRCS OR embed_binaries) add_library(${component} STATIC ${COMPONENT_SRCS}) set(include_type PUBLIC) @@ -97,7 +60,7 @@ function(register_component) target_add_binary_data("${component}" "${embed_data}" "${embed_type}") endforeach() - # add public includes + # add component public includes foreach(include_dir ${COMPONENT_ADD_INCLUDEDIRS}) get_filename_component(abs_dir ${include_dir} ABSOLUTE BASE_DIR ${component_dir}) if(NOT IS_DIRECTORY ${abs_dir}) @@ -107,7 +70,7 @@ function(register_component) target_include_directories(${component} ${include_type} ${abs_dir}) endforeach() - # add private includes + # add component private includes foreach(include_dir ${COMPONENT_PRIV_INCLUDEDIRS}) if(${include_type} STREQUAL INTERFACE) message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE} " @@ -121,7 +84,6 @@ function(register_component) endif() target_include_directories(${component} PRIVATE ${abs_dir}) endforeach() - endfunction() function(register_config_only_component) @@ -131,30 +93,47 @@ function(register_config_only_component) # No-op for now... endfunction() +function(add_component_dependencies target dep dep_type) + get_target_property(target_type ${target} TYPE) + get_target_property(target_imported ${target} IMPORTED) + + if(${target_type} STREQUAL STATIC_LIBRARY OR ${target_type} STREQUAL EXECUTABLE) + if(TARGET ${dep}) + # Add all compile options exported by dep into target + target_include_directories(${target} ${dep_type} + $) + target_compile_definitions(${target} ${dep_type} + $) + target_compile_options(${target} ${dep_type} + $) + endif() + endif() +endfunction() + function(components_finish_registration) - # each component should see the include directories of each other + + # have the executable target depend on all components in the build + set_target_properties(${CMAKE_PROJECT_NAME}.elf PROPERTIES INTERFACE_COMPONENT_REQUIRES "${BUILD_COMPONENTS}") + + spaces2list(COMPONENT_REQUIRES_COMMON) + + # each component should see the include directories of its requirements # - # (we can't do this until all components are registered, because if(TARGET ...) won't work - foreach(a ${COMPONENTS} ${CMAKE_PROJECT_NAME}.elf) + # (we can't do this until all components are registered and targets exist in cmake, as we have + # a circular requirements graph...) + foreach(a ${BUILD_COMPONENTS}) if(TARGET ${a}) - get_target_property(a_imported ${a} IMPORTED) - get_target_property(a_type ${a} TYPE) - if(NOT a_imported) - if(${a_type} STREQUAL STATIC_LIBRARY OR ${a_type} STREQUAL EXECUTABLE) - foreach(b ${COMPONENTS}) - if(TARGET ${b} AND NOT ${a} STREQUAL ${b}) - # Add all public compile options from b in a - target_include_directories(${a} PRIVATE - $) - target_compile_definitions(${a} PRIVATE - $) - target_compile_options(${a} PRIVATE - $) - endif() - endforeach(b) - endif() - endif() + get_component_requirements("${a}" a_deps a_priv_deps) + list(APPEND a_priv_deps ${COMPONENT_REQUIRES_COMMON}) + foreach(b ${a_deps}) + add_component_dependencies(${a} ${b} PUBLIC) + endforeach() + foreach(b ${a_priv_deps}) + add_component_dependencies(${a} ${b} PRIVATE) + endforeach() + + get_target_property(a_type ${a} TYPE) if(${a_type} MATCHES .+_LIBRARY) set(COMPONENT_LIBRARIES "${COMPONENT_LIBRARIES};${a}") endif() @@ -164,7 +143,7 @@ function(components_finish_registration) # Add each component library's link-time dependencies (which are otherwise ignored) to the executable # LINK_DEPENDS in order to trigger a re-link when needed (on Ninja/Makefile generators at least). # (maybe this should probably be something CMake does, but it doesn't do it...) - foreach(component ${COMPONENTS}) + foreach(component ${BUILD_COMPONENTS}) if(TARGET ${component}) get_target_property(imported ${component} IMPORTED) get_target_property(type ${component} TYPE) @@ -179,16 +158,6 @@ function(components_finish_registration) endif() endforeach() - # Embedded binary & text files - spaces2list(COMPONENT_EMBED_FILES) - foreach(embed_src ${COMPONENT_EMBED_FILES}) - target_add_binary_data(${component} "${embed_src}" BINARY) - endforeach() - spaces2list(COMPONENT_EMBED_TXTFILES) - foreach(embed_src ${COMPONENT_EMBED_TXTFILES}) - target_add_binary_data(${component} "${embed_src}" TEXT) - endforeach() - target_link_libraries(${CMAKE_PROJECT_NAME}.elf ${COMPONENT_LIBRARIES}) message(STATUS "Component libraries: ${COMPONENT_LIBRARIES}") diff --git a/tools/cmake/convert_to_cmake.py b/tools/cmake/convert_to_cmake.py index 0cfd60263f..82e6971715 100755 --- a/tools/cmake/convert_to_cmake.py +++ b/tools/cmake/convert_to_cmake.py @@ -180,6 +180,11 @@ def convert_component(project_path, component_path): with open(cmakelists_path, "w") as f: f.write("set(COMPONENT_ADD_INCLUDEDIRS %s)\n\n" % component_add_includedirs) + + f.write("# Edit following two lines to set component requirements (see docs)\n") + f.write("set(COMPONENT_REQUIRES "")\n") + f.write("set(COMPONENT_PRIV_REQUIRES "")\n\n") + if component_srcdirs is not None: f.write("set(COMPONENT_SRCDIRS %s)\n\n" % component_srcdirs) f.write("register_component()\n") diff --git a/tools/cmake/idf_functions.cmake b/tools/cmake/idf_functions.cmake index e88e980af8..3415f9da29 100644 --- a/tools/cmake/idf_functions.cmake +++ b/tools/cmake/idf_functions.cmake @@ -13,6 +13,10 @@ macro(idf_set_global_variables) set_default(EXTRA_COMPONENT_DIRS "") + # Commmon components, required by every component in the build + # + set_default(COMPONENT_REQUIRES_COMMON "cxx esp32 newlib freertos heap log soc") + # PROJECT_PATH has the path to the IDF project (top-level cmake directory) # # (cmake calls this CMAKE_SOURCE_DIR, keeping old name for compatibility.) diff --git a/tools/cmake/kconfig.cmake b/tools/cmake/kconfig.cmake index cc21563054..e313c886a6 100644 --- a/tools/cmake/kconfig.cmake +++ b/tools/cmake/kconfig.cmake @@ -35,7 +35,7 @@ function(kconfig_process_config) # Find Kconfig and Kconfig.projbuild for each component as applicable # if any of these change, cmake should rerun - foreach(dir ${COMPONENT_PATHS} "${CMAKE_SOURCE_DIR}/main") + foreach(dir ${BUILD_COMPONENT_PATHS} "${CMAKE_SOURCE_DIR}/main") file(GLOB kconfig "${dir}/Kconfig") if(kconfig) set(kconfigs "${kconfigs} ${kconfig}") diff --git a/tools/cmake/project.cmake b/tools/cmake/project.cmake index 26be56edab..ff344a967f 100644 --- a/tools/cmake/project.cmake +++ b/tools/cmake/project.cmake @@ -46,16 +46,32 @@ macro(project name) # Set global variables used by rest of the build idf_set_global_variables() - # Search COMPONENT_DIRS for COMPONENTS, make a list of full paths to each - # component in COMPONENT_PATHS - components_find_all("${COMPONENT_DIRS}" "${COMPONENTS}" - COMPONENT_PATHS COMPONENTS) + # Establish dependencies for components in the build + # (this happens before we even generate config...) + if(COMPONENTS) + # Make sure if an explicit list of COMPONENTS is given, it contains the "common" component requirements + # (otherwise, if COMPONENTS is empty then all components will be included in the build.) + set(COMPONENTS "${COMPONENTS} ${COMPONENT_REQUIRES_COMMON}") + endif() + execute_process(COMMAND "${CMAKE_COMMAND}" + -D "COMPONENTS=${COMPONENTS}" + -D "DEPENDENCIES_FILE=${CMAKE_BINARY_DIR}/component_depends.cmake" + -D "COMPONENT_DIRS=${COMPONENT_DIRS}" + -D "BOOTLOADER_BUILD=${BOOTLOADER_BUILD}" + -P "${IDF_PATH}/tools/cmake/scripts/expand_requirements.cmake" + WORKING_DIRECTORY "${IDF_PATH}/tools/cmake") + include("${CMAKE_BINARY_DIR}/component_depends.cmake") + + # We now have the following component-related variables: + # COMPONENTS is the list of initial components set by the user (or empty to include all components in the build). + # BUILD_COMPONENTS is the list of components to include in the build. + # BUILD_COMPONENT_PATHS is the paths to all of these components. # Print list of components - string(REPLACE ";" " " COMPONENTS_SPACES "${COMPONENTS}") - message(STATUS "Component names: ${COMPONENTS_SPACES}") - unset(COMPONENTS_SPACES) - message(STATUS "Component paths: ${COMPONENT_PATHS}") + string(REPLACE ";" " " BUILD_COMPONENTS_SPACES "${BUILD_COMPONENTS}") + message(STATUS "Component names: ${BUILD_COMPONENTS_SPACES}") + unset(BUILD_COMPONENTS_SPACES) + message(STATUS "Component paths: ${BUILD_COMPONENT_PATHS}") kconfig_set_variables() @@ -88,14 +104,14 @@ macro(project name) git_describe(PROJECT_VER "${CMAKE_CURRENT_SOURCE_DIR}") # Include any top-level project_include.cmake files from components - foreach(component ${COMPONENT_PATHS}) + foreach(component ${BUILD_COMPONENT_PATHS}) include_if_exists("${component}/project_include.cmake") endforeach() # # Add each component to the build as a library # - foreach(COMPONENT_PATH ${COMPONENT_PATHS}) + foreach(COMPONENT_PATH ${BUILD_COMPONENT_PATHS}) get_filename_component(COMPONENT_NAME ${COMPONENT_PATH} NAME) add_subdirectory(${COMPONENT_PATH} ${COMPONENT_NAME}) endforeach() diff --git a/tools/cmake/scripts/expand_requirements.cmake b/tools/cmake/scripts/expand_requirements.cmake new file mode 100644 index 0000000000..9b56cf0909 --- /dev/null +++ b/tools/cmake/scripts/expand_requirements.cmake @@ -0,0 +1,216 @@ +# expand_requires.cmake is a utility cmake script to expand component requirements early in the build, +# before the components are ready to be included. +# +# Parameters: +# - COMPONENTS = Space-separated list of initial components to include in the build. +# Can be empty, in which case all components are in the build. +# - DEPENDENCIES_FILE = Path of generated cmake file which will contain the expanded dependencies for these +# components. +# - COMPONENT_DIRS = List of paths to search for all components. +# - DEBUG = Set -DDEBUG=1 to debug component lists in the build. +# +# If successful, DEPENDENCIES_FILE can be expanded to set BUILD_COMPONENTS & BUILD_COMPONENT_PATHS with all +# components required for the build, and the get_component_requirements() function to return each component's +# recursively expanded requirements. +# +# TODO: Error out if a component requirement is missing +cmake_minimum_required(VERSION 3.5) +include("utilities.cmake") + +if(NOT DEPENDENCIES_FILE) + message(FATAL_ERROR "DEPENDENCIES_FILE must be set.") +endif() + +if(NOT COMPONENT_DIRS) + message(FATAL_ERROR "COMPONENT_DIRS variable must be set") +endif() +spaces2list(COMPONENT_DIRS) + +function(debug message) + if(DEBUG) + message(STATUS "${message}") + endif() +endfunction() + +# Dummy register_component used to save requirements variables as global properties, for later expansion +# +# (expand_component_requirements() includes the component CMakeLists.txt, which then sets its component variables, +# calls this dummy macro, and immediately exits again.) +macro(register_component) + spaces2list(COMPONENT_REQUIRES) + set_property(GLOBAL PROPERTY "${COMPONENT}_REQUIRES" "${COMPONENT_REQUIRES}") + spaces2list(COMPONENT_PRIV_REQUIRES) + set_property(GLOBAL PROPERTY "${COMPONENT}_PRIV_REQUIRES" "${COMPONENT_PRIV_REQUIRES}") + + # This is tricky: we override register_component() so it returns out of the component CMakeLists.txt + # (as we're declaring it as a macro not a function, so it doesn't have its own scope.) + # + # This means no targets are defined, and the component expansion ends early. + return() +endmacro() + +macro(register_config_only_component) + register_component() +endmacro() + +# Given a component name (find_name) and a list of component paths (component_paths), +# return the path to the component in 'variable' +# +# Fatal error is printed if the component is not found. +function(find_component_path find_name component_paths variable) + foreach(path ${component_paths}) + get_filename_component(name "${path}" NAME) + if("${name}" STREQUAL "${find_name}") + set("${variable}" "${path}" PARENT_SCOPE) + return() + endif() + endforeach() + # TODO: find a way to print the dependency chain that lead to this not-found component + message(WARNING "Required component ${find_name} is not found in any of the provided COMPONENT_DIRS") +endfunction() + +# components_find_all: Search 'component_dirs' for components and return them +# as a list of names in 'component_names' and a list of full paths in +# 'component_paths' +# +# component_paths contains only unique component names. Directories +# earlier in the component_dirs list take precedence. +function(components_find_all component_dirs component_paths component_names) + # component_dirs entries can be files or lists of files + set(paths "") + set(names "") + + # start by expanding the component_dirs list with all subdirectories + foreach(dir ${component_dirs}) + # Iterate any subdirectories for values + file(GLOB subdirs LIST_DIRECTORIES true "${dir}/*") + foreach(subdir ${subdirs}) + set(component_dirs "${component_dirs};${subdir}") + endforeach() + endforeach() + + # Look for a component in each component_dirs entry + foreach(dir ${component_dirs}) + file(GLOB component "${dir}/CMakeLists.txt") + if(component) + get_filename_component(component "${component}" DIRECTORY) + get_filename_component(name "${component}" NAME) + if(NOT name IN_LIST names) + set(names "${names};${name}") + set(paths "${paths};${component}") + endif() + + else() # no CMakeLists.txt file + # test for legacy component.mk and warn + file(GLOB legacy_component "${dir}/component.mk") + if(legacy_component) + get_filename_component(legacy_component "${legacy_component}" DIRECTORY) + message(WARNING "Component ${legacy_component} contains old-style component.mk but no CMakeLists.txt. " + "Component will be skipped.") + endif() + endif() + + endforeach() + + set(${component_paths} ${paths} PARENT_SCOPE) + set(${component_names} ${names} PARENT_SCOPE) +endfunction() + +# expand_component_requirements: Recursively expand a component's requirements, +# setting global properties BUILD_COMPONENTS & BUILD_COMPONENT_PATHS and +# also invoking the components to call register_component() above, +# which will add per-component global properties with dependencies, etc. +function(expand_component_requirements component) + get_property(build_components GLOBAL PROPERTY BUILD_COMPONENTS) + if(${component} IN_LIST build_components) + return() # already added this component + endif() + + find_component_path("${component}" "${ALL_COMPONENT_PATHS}" component_path) + debug("Expanding dependencies of ${component} @ ${component_path}") + if(NOT component_path) + set_property(GLOBAL APPEND PROPERTY COMPONENTS_NOT_FOUND ${component}) + return() + endif() + + # include the component CMakeLists.txt to expand its properties + # into the global cache (via register_component(), above) + unset(COMPONENT_REQUIRES) + unset(COMPONENT_PRIV_REQUIRES) + set(COMPONENT ${component}) + include(${component_path}/CMakeLists.txt) + + set_property(GLOBAL APPEND PROPERTY BUILD_COMPONENT_PATHS ${component_path}) + set_property(GLOBAL APPEND PROPERTY BUILD_COMPONENTS ${component}) + + get_property(requires GLOBAL PROPERTY "${component}_REQUIRES") + get_property(requires_priv GLOBAL PROPERTY "${component}_PRIV_REQUIRES") + foreach(req ${requires} ${requires_priv}) + expand_component_requirements(${req}) + endforeach() +endfunction() + + +# Main functionality goes here + +# Find every available component in COMPONENT_DIRS, save as ALL_COMPONENT_PATHS and ALL_COMPONENTS +components_find_all("${COMPONENT_DIRS}" ALL_COMPONENT_PATHS ALL_COMPONENTS) + +if(NOT COMPONENTS) + set(COMPONENTS "${ALL_COMPONENTS}") +endif() +spaces2list(COMPONENTS) + +debug("ALL_COMPONENT_PATHS ${ALL_COMPONENT_PATHS}") +debug("ALL_COMPONENTS ${ALL_COMPONENTS}") + +set_property(GLOBAL PROPERTY BUILD_COMPONENTS "") +set_property(GLOBAL PROPERTY BUILD_COMPONENT_PATHS "") +set_property(GLOBAL PROPERTY COMPONENTS_NOT_FOUND "") + +foreach(component ${COMPONENTS}) + debug("Expanding initial component ${component}") + expand_component_requirements(${component}) +endforeach() + +get_property(build_components GLOBAL PROPERTY BUILD_COMPONENTS) +get_property(build_component_paths GLOBAL PROPERTY BUILD_COMPONENT_PATHS) +get_property(not_found GLOBAL PROPERTY COMPONENTS_NOT_FOUND) + +debug("components in build: ${build_components}") +debug("components in build: ${build_component_paths}") +debug("components not found: ${not_found}") + +function(line contents) + file(APPEND "${DEPENDENCIES_FILE}" "${contents}\n") +endfunction() + +file(WRITE "${DEPENDENCIES_FILE}" "# Component requirements generated by expand_requirements.cmake\n\n") +line("set(BUILD_COMPONENTS ${build_components})") +line("set(BUILD_COMPONENT_PATHS ${build_component_paths})") +line("") + +line("# get_component_requirements: Generated function to read the dependencies of a given component.") +line("#") +line("# Parameters:") +line("# - component: Name of component") +line("# - var_requires: output variable name. Set to recursively expanded COMPONENT_REQUIRES ") +line("# for this component.") +line("# - var_private_requires: output variable name. Set to recursively expanded COMPONENT_PRIV_REQUIRES ") +line("# for this component.") +line("#") +line("# Throws a fatal error if 'componeont' is not found (indicates a build system problem).") +line("#") +line("function(get_component_requirements component var_requires var_private_requires)") +foreach(build_component ${build_components}) + get_property(reqs GLOBAL PROPERTY "${build_component}_REQUIRES") + get_property(private_reqs GLOBAL PROPERTY "${build_component}_PRIV_REQUIRES") + line(" if(\"\$\{component}\" STREQUAL \"${build_component}\")") + line(" set(\${var_requires} \"${reqs}\" PARENT_SCOPE)") + line(" set(\${var_private_requires} \"${private_reqs}\" PARENT_SCOPE)") + line(" return()") + line(" endif()") +endforeach() + +line(" message(FATAL_ERROR \"Component not found: \${component}\")") +line("endfunction()") -- 2.40.0