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 | 161 ++++++------- 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 | 137 +++++------ 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, 562 insertions(+), 201 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 fec9b921d..5a6f19dbb 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 ba2fa9c7e..44663f68e 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 8e26ba162..1047d2117 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 a089a92be..8fcf622d3 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 f8bfb26a2..f7644bd7a 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 1b694f381..d38b8ff8e 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 5ac887a40..719d9fb9d 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 6df7b24db..21978397e 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 9431645e8..c2134be8f 100644 --- a/components/bt/CMakeLists.txt +++ b/components/bt/CMakeLists.txt @@ -1,83 +1,90 @@ -if(NOT CONFIG_BT_ENABLED) - return() +if(CONFIG_BT_ENABLED) + + set(COMPONENT_SRCDIRS .) + set(COMPONENT_ADD_INCLUDEDIRS include) + + if(CONFIG_BLUEDROID_ENABLED) + + 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() -set(COMPONENT_SRCDIRS .) -set(COMPONENT_ADD_INCLUDEDIRS include) +# requirements can't depend on config +set(COMPONENT_PRIV_REQUIRES lwip nvs_flash) -if(CONFIG_BLUEDROID_ENABLED) - - 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() +# 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 cadd463fa..faba93309 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 1c5648654..0b0da5394 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 255f4f3fd..a797640cc 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 8ccaad2c3..0c72aecc4 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 000000000..9bad42868 --- /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 1172dda7f..b231bfd0d 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 ba2fa9c7e..fc21f1eca 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 a9eb90370..165d89fbe 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 9e77a825c..5791a2659 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 c9f5a0fcf..3d24c50bf 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 0a5172dc1..a0be8f086 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 08e78e845..b7dd5d9ac 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 e307838a8..cf64054dd 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 2eabcf5fe..7c19d9ccb 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 c19ec43fa..0b8ba83d6 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 48e5a3f69..674534c91 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 eebd20a04..ad162fe1e 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 5474c4daa..03deae436 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 56dfce17c..44410c149 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 149dcb0bc..4ab664110 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 13bc9cfb6..24e0a9e6f 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 c92518deb..4bc0db3f9 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 c7e4b754b..bf3f72746 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 5970a7915..1fe5ecf26 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 347ade638..84d02ffe8 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 87f12da94..cde193dce 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 eebd20a04..ad162fe1e 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 ba2fa9c7e..58b9aeb67 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 ba2fa9c7e..8b18007de 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 24e859551..b4c088edd 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 f64a6ec11..1eafba54d 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 5e0027f52..a85f98d2b 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 ba2fa9c7e..24deb2f24 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 ba2fa9c7e..fc21f1eca 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 ba2fa9c7e..fc21f1eca 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 a72d0f565..8b5db6231 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 1d2091656..fc0c323fc 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 ba2fa9c7e..810d299d1 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 a4de84060..03eb0c206 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 e638a36d0..8d3830c8b 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 74bb26787..6d19bf48b 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 4334ffee7..506d8a06f 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(components_finish_registration) - # each component should see the include directories of each other - # - # (we can't do this until all components are registered, because if(TARGET ...) won't work - foreach(a ${COMPONENTS} ${CMAKE_PROJECT_NAME}.elf) - 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() +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) + + # 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 and targets exist in cmake, as we have + # a circular requirements graph...) + foreach(a ${BUILD_COMPONENTS}) + if(TARGET ${a}) + 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 0cfd60263..82e697171 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 e88e980af..3415f9da2 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 cc2156305..e313c886a 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 26be56eda..ff344a967 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 000000000..9b56cf090 --- /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()")