diff --git a/CMakeLists.txt b/CMakeLists.txt index 2c6ef7621f..c98d3d8c5e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,161 +1,150 @@ cmake_minimum_required(VERSION 3.5) project(esp-idf C CXX ASM) -if(NOT IDF_PATH) - set(IDF_PATH ${CMAKE_CURRENT_LIST_DIR}) -endif() +# Include the sdkconfig cmake file, since the following operations require +# knowledge of config values. +idf_build_get_property(sdkconfig_cmake SDKCONFIG_CMAKE) +include(${sdkconfig_cmake}) -include(tools/cmake/idf_functions.cmake) - -# -# Set variables that control the build configuration and the build itself -# -idf_set_variables() - -kconfig_set_variables() - -# -# Generate a component dependencies file, enumerating components to be included in the build -# as well as their dependencies. -# -execute_process(COMMAND "${CMAKE_COMMAND}" - -D "COMPONENTS=${IDF_COMPONENTS}" - -D "COMPONENT_REQUIRES_COMMON=${IDF_COMPONENT_REQUIRES_COMMON}" - -D "EXCLUDE_COMPONENTS=${IDF_EXCLUDE_COMPONENTS}" - -D "TEST_COMPONENTS=${IDF_TEST_COMPONENTS}" - -D "TEST_EXCLUDE_COMPONENTS=${IDF_TEST_EXCLUDE_COMPONENTS}" - -D "BUILD_TESTS=${IDF_BUILD_TESTS}" - -D "DEPENDENCIES_FILE=${CMAKE_BINARY_DIR}/component_depends.cmake" - -D "COMPONENT_DIRS=${IDF_COMPONENT_DIRS}" - -D "BOOTLOADER_BUILD=${BOOTLOADER_BUILD}" - -D "IDF_TARGET=${IDF_TARGET}" - -D "IDF_PATH=${IDF_PATH}" - -D "DEBUG=${DEBUG}" - -P "${IDF_PATH}/tools/cmake/scripts/expand_requirements.cmake" - WORKING_DIRECTORY "${PROJECT_PATH}" - RESULT_VARIABLE expand_requirements_result) - -if(expand_requirements_result) - message(FATAL_ERROR "Failed to expand component requirements") -endif() - -include("${CMAKE_BINARY_DIR}/component_depends.cmake") - -# -# We now have the following component-related variables: -# -# IDF_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, obtained from the component dependencies file. -# -# Print the list of found components and test components -# -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}") - -# Print list of test components -if(TESTS_ALL EQUAL 1 OR TEST_COMPONENTS) - string(REPLACE ";" " " BUILD_TEST_COMPONENTS_SPACES "${BUILD_TEST_COMPONENTS}") - message(STATUS "Test component names: ${BUILD_TEST_COMPONENTS_SPACES}") - unset(BUILD_TEST_COMPONENTS_SPACES) - message(STATUS "Test component paths: ${BUILD_TEST_COMPONENT_PATHS}") -endif() - -# Generate project configuration -kconfig_process_config() - -# Include sdkconfig.cmake so rest of the build knows the configuration -include(${SDKCONFIG_CMAKE}) - -# Verify the environment is configured correctly -idf_verify_environment() - -# Check git revision (may trigger reruns of cmake) -## sets IDF_VER to IDF git revision -idf_get_git_revision() - -# Check that the targets set in cache, sdkconfig, and in environment all match -idf_check_config_target() - -## get PROJECT_VER -if(NOT BOOTLOADER_BUILD) - app_get_revision("${CMAKE_SOURCE_DIR}") -endif() - -# Add some idf-wide definitions -idf_set_global_compile_options() - -# generate compile_commands.json (needs to come after project) -set(CMAKE_EXPORT_COMPILE_COMMANDS 1) - -# -# Setup variables for linker script generation -# -ldgen_set_variables() - -# Include any top-level project_include.cmake files from components -foreach(component ${BUILD_COMPONENT_PATHS}) - set(COMPONENT_PATH "${component}") - include_if_exists("${component}/project_include.cmake") - unset(COMPONENT_PATH) +# Make each build property available as a read-only variable +idf_build_get_property(build_properties __BUILD_PROPERTIES) +foreach(build_property ${build_properties}) + idf_build_get_property(val ${build_property}) + set(${build_property} "${val}") endforeach() -# -# Add each component to the build as a library -# -foreach(COMPONENT_PATH ${BUILD_COMPONENT_PATHS}) - get_filename_component(COMPONENT_NAME ${COMPONENT_PATH} NAME) +# Check that the CMake target value matches the Kconfig target value. +__target_check() - list(FIND BUILD_TEST_COMPONENT_PATHS ${COMPONENT_PATH} idx) +unset(compile_options) +unset(c_compile_options) +unset(cxx_compile_options) +unset(compile_definitions) - if(NOT idx EQUAL -1) - list(GET BUILD_TEST_COMPONENTS ${idx} test_component) - set(COMPONENT_NAME ${test_component}) +# Add the following build specifications here, since these seem to be dependent +# on config values on the root Kconfig. + +# Temporary trick to support both gcc5 and gcc8 builds +if(CMAKE_C_COMPILER_VERSION VERSION_EQUAL 5.2.0) + set(GCC_NOT_5_2_0 0 CACHE STRING "GCC is 5.2.0 version") +else() + set(GCC_NOT_5_2_0 1 CACHE STRING "GCC is not 5.2.0 version") +endif() + +list(APPEND compile_definitions "-DGCC_NOT_5_2_0=${GCC_NOT_5_2_0}") + +if(CONFIG_OPTIMIZATION_LEVEL_RELEASE) + list(APPEND compile_options "-Os") +else() + list(APPEND compile_options "-Og") +endif() + +if(CONFIG_CXX_EXCEPTIONS) + list(APPEND cxx_compile_options "-fexceptions") +else() + list(APPEND cxx_compile_options "-fno-exceptions") +endif() + +if(CONFIG_DISABLE_GCC8_WARNINGS) + list(APPEND compile_options "-Wno-parentheses" + "-Wno-sizeof-pointer-memaccess" + "-Wno-clobbered") + + # doesn't use GCC_NOT_5_2_0 because idf_set_global_variables was not called before + if(GCC_NOT_5_2_0) + list(APPEND compile_options "-Wno-format-overflow" + "-Wno-stringop-truncation" + "-Wno-misleading-indentation" + "-Wno-cast-function-type" + "-Wno-implicit-fallthrough" + "-Wno-unused-const-variable" + "-Wno-switch-unreachable" + "-Wno-format-truncation" + "-Wno-memset-elt-size" + "-Wno-int-in-bool-context") endif() +endif() - component_get_target(COMPONENT_TARGET ${COMPONENT_NAME}) +if(CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED) + list(APPEND compile_definitions "NDEBUG") +endif() - add_subdirectory(${COMPONENT_PATH} ${COMPONENT_NAME}) +if(CONFIG_STACK_CHECK_NORM) + list(APPEND compile_options "-fstack-protector") +elseif(CONFIG_STACK_CHECK_STRONG) + list(APPEND compile_options "-fstack-protector-strong") +elseif(CONFIG_STACK_CHECK_ALL) + list(APPEND compile_options "-fstack-protector-all") +endif() + +# All targets built under this scope is with the ESP-IDF build system +set(ESP_PLATFORM 1) +list(APPEND compile_definitions "-DESP_PLATFORM") + +idf_build_set_property(COMPILE_OPTIONS "${compile_options}" APPEND) +idf_build_set_property(C_COMPILE_OPTIONS "${c_compile_options}" APPEND) +idf_build_set_property(CXX_COMPILE_OPTIONS "${cxx_compile_options}" APPEND) +idf_build_set_property(COMPILE_DEFINITIONS "${compile_definitions}" APPEND) + +idf_build_get_property(build_component_targets __BUILD_COMPONENT_TARGETS) + +# Include each component's project_include.cmake +foreach(component_target ${build_component_targets}) + __component_get_property(dir ${component_target} COMPONENT_DIR) + __component_get_property(_name ${component_target} COMPONENT_NAME) + set(COMPONENT_NAME ${_name}) + set(COMPONENT_DIR ${dir}) + set(COMPONENT_PATH ${dir}) # this is deprecated, users are encouraged to use COMPONENT_DIR; + # retained for compatibility + if(EXISTS ${COMPONENT_DIR}/project_include.cmake) + include(${COMPONENT_DIR}/project_include.cmake) + endif() endforeach() -unset(COMPONENT_NAME) -unset(COMPONENT_PATH) -# 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(component ${BUILD_COMPONENTS}) - component_get_target(component_target ${component}) - if(TARGET ${component_target}) - get_component_requirements(${component} deps priv_deps) +# Add each component as a subdirectory, processing each component's CMakeLists.txt +foreach(component_target ${build_component_targets}) + __component_get_property(dir ${component_target} COMPONENT_DIR) + __component_get_property(_name ${component_target} COMPONENT_NAME) + __component_get_property(prefix ${component_target} __PREFIX) + __component_get_property(alias ${component_target} COMPONENT_ALIAS) + set(COMPONENT_NAME ${_name}) + set(COMPONENT_DIR ${dir}) + set(COMPONENT_ALIAS ${alias}) + set(COMPONENT_PATH ${dir}) # same deprecation situation here + idf_build_get_property(build_prefix __PREFIX) + set(__idf_component_context 1) + if(NOT prefix STREQUAL build_prefix) + add_subdirectory(${dir} ${prefix}_${_name} EXCLUDE_FROM_ALL) + else() + add_subdirectory(${dir} ${_name} EXCLUDE_FROM_ALL) + endif() + set(__idf_component_context 0) +endforeach() - list(APPEND priv_deps ${IDF_COMPONENT_REQUIRES_COMMON}) +# Establish dependencies between components +idf_build_get_property(build_components BUILD_COMPONENTS) +foreach(build_component ${build_components}) + __component_get_target(component_target ${build_component}) + __component_get_property(component_lib ${component_target} COMPONENT_LIB) + __component_get_property(reqs ${component_target} __REQUIRES) + foreach(req ${reqs}) + __component_get_property(req_lib ${req} COMPONENT_LIB) + if(TARGET ${req_lib}) + set_property(TARGET ${component_lib} APPEND PROPERTY INTERFACE_LINK_LIBRARIES ${req_lib}) + endif() + endforeach() - foreach(dep ${deps}) - component_get_target(dep_target ${dep}) - add_component_dependencies(${component_target} ${dep_target} PUBLIC) - endforeach() - - foreach(dep ${priv_deps}) - component_get_target(dep_target ${dep}) - add_component_dependencies(${component_target} ${dep_target} PRIVATE) + get_property(type TARGET ${component_lib} PROPERTY TYPE) + if(type STREQUAL STATIC_LIBRARY) + __component_get_property(reqs ${component_target} __REQUIRES) + __component_get_property(priv_reqs ${component_target} __PRIV_REQUIRES) + foreach(req ${reqs} ${priv_reqs}) + __component_get_property(req_lib ${req} COMPONENT_LIB) + if(TARGET ${req_lib}) + set_property(TARGET ${component_lib} APPEND PROPERTY LINK_LIBRARIES ${req_lib}) + endif() endforeach() endif() endforeach() -if(IDF_BUILD_ARTIFACTS) - # Write project description JSON file - make_json_list("${BUILD_COMPONENTS}" build_components_json) - make_json_list("${BUILD_COMPONENT_PATHS}" build_component_paths_json) - configure_file("${IDF_PATH}/tools/cmake/project_description.json.in" - "${IDF_BUILD_ARTIFACTS_DIR}/project_description.json") - unset(build_components_json) - unset(build_component_paths_json) -endif() -set(BUILD_COMPONENTS ${BUILD_COMPONENTS} PARENT_SCOPE) - -ldgen_add_dependencies() diff --git a/docs/zh_CN/api-guides/build-system-cmake.rst b/docs/zh_CN/api-guides/build-system-cmake.rst index ff4bd44f34..2cf27bda98 100644 --- a/docs/zh_CN/api-guides/build-system-cmake.rst +++ b/docs/zh_CN/api-guides/build-system-cmake.rst @@ -357,9 +357,9 @@ ESP-IDF 在搜索所有待构建的组件时,会按照 ``COMPONENT_DIRS`` 指 .. highlight:: cmake -在编译特定组件的源文件时,可以使用 ``component_compile_options`` 命令来传递编译器选项:: +在编译特定组件的源文件时,可以使用 ``target_compile_options(${COMPONENT_TARGET} PRIVATE `` 命令来传递编译器选项:: - component_compile_options(-Wno-unused-variable) + target_compile_options(${COMPONENT_TARGET} PRIVATE -Wno-unused-variable) 这条命令封装了 CMake 的 `target_compile_options`_ 命令。 diff --git a/examples/system/gcov/README.md b/examples/system/gcov/README.md index 941ae68c48..8faf9f31e3 100644 --- a/examples/system/gcov/README.md +++ b/examples/system/gcov/README.md @@ -25,7 +25,7 @@ It will enable coverage info for all source files of your component. If you need `gcov_example.o: CFLAGS += --coverage` Replace `gcov_example.o` with path to your file. - For CMake-based build system, use `component_compile_options(--coverage)` or: ` set_source_files_properties(gcov_example.c PROPERTIES COMPILE_FLAGS --coverage` + For CMake-based build system, use `target_compile_options(${COMPONENT_TARGET} PRIVATE --coverage)` or: ` set_source_files_properties(gcov_example.c PROPERTIES COMPILE_FLAGS --coverage` ### Hard-coded Dump Call diff --git a/tools/cmake/build.cmake b/tools/cmake/build.cmake new file mode 100644 index 0000000000..f85ef1d31f --- /dev/null +++ b/tools/cmake/build.cmake @@ -0,0 +1,434 @@ + +# idf_build_get_property +# +# @brief Retrieve the value of the specified property related to ESP-IDF build. +# +# @param[out] var the variable to store the value in +# @param[in] property the property to get the value of +# +# @param[in, optional] GENERATOR_EXPRESSION (option) retrieve the generator expression for the property +# instead of actual value +function(idf_build_get_property var property) + cmake_parse_arguments(_ "GENERATOR_EXPRESSION" "" "" ${ARGN}) + if(__GENERATOR_EXPRESSION) + set(val "$") + else() + get_property(val TARGET __idf_build_target PROPERTY ${property}) + endif() + set(${var} ${val} PARENT_SCOPE) +endfunction() + +# idf_build_set_property +# +# @brief Set the value of the specified property related to ESP-IDF build. The property is +# also added to the internal list of build properties if it isn't there already. +# +# @param[in] property the property to set the value of +# @param[out] value value of the property +# +# @param[in, optional] APPEND (option) append the value to the current value of the +# property instead of replacing it +function(idf_build_set_property property value) + cmake_parse_arguments(_ "APPEND" "" "" ${ARGN}) + + if(__APPEND) + set_property(TARGET __idf_build_target APPEND PROPERTY ${property} ${value}) + else() + set_property(TARGET __idf_build_target PROPERTY ${property} ${value}) + endif() + + # Keep track of set build properties so that they can be exported to a file that + # will be included in early expansion script. + idf_build_get_property(build_properties __BUILD_PROPERTIES) + if(NOT property IN_LIST build_properties) + idf_build_set_property(__BUILD_PROPERTIES "${property}" APPEND) + endif() +endfunction() + +# idf_build_unset_property +# +# @brief Unset the value of the specified property related to ESP-IDF build. Equivalent +# to setting the property to an empty string; though it also removes the property +# from the internal list of build properties. +# +# @param[in] property the property to unset the value of +function(idf_build_unset_property property) + idf_build_set_property(${property} "") # set to an empty value + idf_build_get_property(build_properties __BUILD_PROPERTIES) # remove from tracked properties + list(REMOVE_ITEM build_properties ${property}) + idf_build_set_property(__BUILD_PROPERTIES "${build_properties}") +endfunction() + +# +# Retrieve the IDF_PATH repository's version, either using a version +# file or Git revision. Sets the IDF_VER build property. +# +function(__build_get_idf_git_revision) + idf_build_get_property(idf_path IDF_PATH) + git_describe(idf_ver_git "${idf_path}") + if(EXISTS "${idf_path}/version.txt") + file(STRINGS "${idf_path}/version.txt" idf_ver_t) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${idf_path}/version.txt") + else() + set(idf_ver_t ${idf_ver_git}) + endif() + # cut IDF_VER to required 32 characters. + string(SUBSTRING "${idf_ver_t}" 0 31 idf_ver) + idf_build_set_property(COMPILE_DEFINITIONS "-DIDF_VER=\"${idf_ver}\"" APPEND) + git_submodule_check("${idf_path}") + idf_build_set_property(IDF_VER ${idf_ver}) +endfunction() + +# +# Sets initial list of build specifications (compile options, definitions, etc.) common across +# all library targets built under the ESP-IDF build system. These build specifications are added +# privately using the directory-level CMake commands (add_compile_options, include_directories, etc.) +# during component registration. +# +function(__build_set_default_build_specifications) + unset(compile_definitions) + unset(compile_options) + unset(c_compile_options) + unset(cxx_compile_options) + + list(APPEND compile_definitions "-DHAVE_CONFIG_H") + + list(APPEND compile_options "-ffunction-sections" + "-fdata-sections" + "-fstrict-volatile-bitfields" + "-nostdlib" + # warning-related flags + "-Wall" + "-Werror=all" + "-Wno-error=unused-function" + "-Wno-error=unused-but-set-variable" + "-Wno-error=unused-variable" + "-Wno-error=deprecated-declarations" + "-Wextra" + "-Wno-unused-parameter" + "-Wno-sign-compare" + # always generate debug symbols (even in release mode, these don't + # go into the final binary so have no impact on size + "-ggdb") + + list(APPEND c_compile_options "-std=gnu99" + "-Wno-old-style-declaration") + + list(APPEND cxx_compile_options "-std=gnu++11" + "-fno-rtti") + + idf_build_set_property(COMPILE_DEFINITIONS "${compile_definitions}" APPEND) + idf_build_set_property(COMPILE_OPTIONS "${compile_options}" APPEND) + idf_build_set_property(C_COMPILE_OPTIONS "${c_compile_options}" APPEND) + idf_build_set_property(CXX_COMPILE_OPTIONS "${cxx_compile_options}" APPEND) +endfunction() + +# +# Initialize the build. This gets called upon inclusion of idf.cmake to set internal +# properties used for the processing phase of the build. +# +function(__build_init idf_path) + # Create the build target, to which the ESP-IDF build properties, dependencies are attached to + add_custom_target(__idf_build_target) + + set_default(python "python") + + idf_build_set_property(PYTHON ${python}) + idf_build_set_property(IDF_PATH ${idf_path}) + + idf_build_set_property(__PREFIX idf) + idf_build_set_property(__CHECK_PYTHON 1) + + __build_set_default_build_specifications() + + # Add internal components to the build + idf_build_get_property(idf_path IDF_PATH) + idf_build_get_property(prefix __PREFIX) + file(GLOB component_dirs ${idf_path}/components/*) + foreach(component_dir ${component_dirs}) + get_filename_component(component_dir ${component_dir} ABSOLUTE) + __component_add(${component_dir} ${prefix}) + endforeach() + + # Set components required by all other components in the build + set(requires_common cxx newlib freertos heap log soc esp_rom esp_common xtensa) + idf_build_set_property(__COMPONENT_REQUIRES_COMMON "${requires_common}") + + __build_get_idf_git_revision() + __kconfig_init() +endfunction() + +# idf_build_component +# +# @brief Specify component directory for the build system to process. +# Relative paths are converted to absolute paths with respect to current directory. +# Any component that needs to be processed has to be specified using this +# command before calling idf_build_process. +# +# @param[in] component_dir directory of the component to process +function(idf_build_component component_dir) + idf_build_get_property(prefix __PREFIX) + __component_add(${component_dir} ${prefix} 0) +endfunction() + +# +# Resolve the requirement component to the component target created for that component. +# +function(__build_resolve_and_add_req var component_target req type) + __component_get_target(_component_target ${req}) + if(NOT _component_target) + message(FATAL_ERROR "Failed to resolve component '${req}'.") + endif() + __component_set_property(${component_target} ${type} ${_component_target} APPEND) + set(${var} ${_component_target} PARENT_SCOPE) +endfunction() + +# +# Build a list of components (in the form of component targets) to be added to the build +# based on public and private requirements. This list is saved in an internal property, +# __BUILD_COMPONENT_TARGETS. +# +function(__build_expand_requirements component_target) + # Since there are circular dependencies, make sure that we do not infinitely + # expand requirements for each component. + idf_build_get_property(component_targets_seen __COMPONENT_TARGETS_SEEN) + if(component_target IN_LIST component_targets_seen) + return() + endif() + + idf_build_set_property(__COMPONENT_TARGETS_SEEN ${component_target} APPEND) + + get_property(reqs TARGET ${component_target} PROPERTY REQUIRES) + get_property(priv_reqs TARGET ${component_target} PROPERTY PRIV_REQUIRES) + + foreach(req ${reqs}) + __build_resolve_and_add_req(_component_target ${component_target} ${req} __REQUIRES) + __build_expand_requirements(${_component_target}) + endforeach() + + foreach(req ${priv_reqs}) + __build_resolve_and_add_req(_component_target ${component_target} ${req} __PRIV_REQUIRES) + __build_expand_requirements(${_component_target}) + endforeach() + + idf_build_get_property(build_component_targets __BUILD_COMPONENT_TARGETS) + if(NOT component_target IN_LIST build_component_targets) + idf_build_set_property(__BUILD_COMPONENT_TARGETS ${component_target} APPEND) + endif() +endfunction() + +# +# Write a CMake file containing set build properties, owing to the fact that an internal +# list of properties is maintained in idf_build_set_property() call. This is used to convert +# those set properties to variables in the scope the output file is included in. +# +function(__build_write_properties output_file) + idf_build_get_property(build_properties __BUILD_PROPERTIES) + foreach(property ${build_properties}) + idf_build_get_property(val ${property}) + set(build_properties_text "${build_properties_text}\nset(${property} ${val})") + endforeach() + file(WRITE ${output_file} "${build_properties_text}") +endfunction() + +# +# Check if the Python interpreter used for the build has all the required modules. +# +function(__build_check_python) + idf_build_get_property(check __CHECK_PYTHON) + if(check) + idf_build_get_property(python PYTHON) + idf_build_get_property(idf_path IDF_PATH) + message(STATUS "Checking Python dependencies...") + execute_process(COMMAND "${python}" "${idf_path}/tools/check_python_dependencies.py" + RESULT_VARIABLE result) + if(NOT result EQUAL 0) + message(FATAL_ERROR "Some Python dependencies must be installed. Check above message for details.") + endif() + endif() +endfunction() + +# +# Utility macro for setting default property value if argument is not specified +# for idf_build_process(). +# +macro(__build_set_default var default) + set(_var __${var}) + if(${_var}) + idf_build_set_property(${var} "${${_var}}") + else() + idf_build_set_property(${var} "${default}") + endif() + unset(_var) +endmacro() + +# idf_build_process +# +# @brief Main processing step for ESP-IDF build: config generation, adding components to the build, +# dependency resolution, etc. +# +# @param[in] target ESP-IDF target +# +# @param[in, optional] PROJECT_DIR (single value) directory of the main project the buildsystem +# is processed for; defaults to CMAKE_SOURCE_DIR +# @param[in, optional] PROJECT_VER (single value) version string of the main project; defaults +# to 0.0.0 +# @param[in, optional] PROJECT_NAME (single value) main project name, defaults to CMAKE_PROJECT_NAME +# @param[in, optional] SDKCONFIG (single value) sdkconfig output path, defaults to PROJECT_DIR/sdkconfig +# if PROJECT_DIR is set and CMAKE_SOURCE_DIR/sdkconfig if not +# @param[in, optional] SDKCONFIG_DEFAULTS (single value) config defaults file to use for the build; defaults +# to none (Kconfig defaults or previously generated config are used) +# @param[in, optional] BUILD_DIR (single value) directory for build artifacts; defautls to CMAKE_BINARY_DIR +# @param[in, optional] COMPONENTS (multivalue) starting components for trimming build +function(idf_build_process target) + set(options) + set(single_value PROJECT_DIR PROJECT_VER PROJECT_NAME BUILD_DIR SDKCONFIG SDKCONFIG_DEFAULTS) + set(multi_value COMPONENTS) + cmake_parse_arguments(_ "${options}" "${single_value}" "${multi_value}" ${ARGN}) + + idf_build_set_property(BOOTLOADER_BUILD "${BOOTLOADER_BUILD}") + + # Check build target is specified. Since this target corresponds to a component + # name, the target component is automatically added to the list of common component + # requirements. + if(NOT target OR target STREQUAL "") + message(FATAL_ERROR "Build target not specified.") + endif() + + idf_build_set_property(IDF_TARGET ${target}) + + __build_set_default(PROJECT_DIR ${CMAKE_SOURCE_DIR}) + __build_set_default(PROJECT_NAME ${CMAKE_PROJECT_NAME}) + __build_set_default(PROJECT_VER "0.0.0") + __build_set_default(BUILD_DIR ${CMAKE_BINARY_DIR}) + + idf_build_get_property(project_dir PROJECT_DIR) + __build_set_default(SDKCONFIG "${project_dir}/sdkconfig") + + __build_set_default(SDKCONFIG_DEFAULTS "") + + # Check for required Python modules + __build_check_python() + + # Generate config values in different formats + idf_build_get_property(sdkconfig SDKCONFIG) + idf_build_get_property(sdkconfig_defaults SDKCONFIG_DEFAULTS) + __kconfig_generate_config("${sdkconfig}" "${sdkconfig_defaults}") + + # Write the partial build properties to a temporary file. + # The path to this generated file is set to a short-lived build + # property BUILD_PROPERTIES_FILE. + idf_build_get_property(build_dir BUILD_DIR) + set(build_properties_file ${build_dir}/build_properties.temp.cmake) + idf_build_set_property(BUILD_PROPERTIES_FILE ${build_properties_file}) + __build_write_properties(${build_properties_file}) + + # Perform early expansion of component CMakeLists.txt in CMake scripting mode. + # It is here we retrieve the public and private requirements of each component. + # It is also here we add the common component requirements to each component's + # own requirements. + idf_build_get_property(component_targets __COMPONENT_TARGETS) + idf_build_set_property(__COMPONENT_REQUIRES_COMMON ${target} APPEND) + idf_build_get_property(common_reqs __COMPONENT_REQUIRES_COMMON) + foreach(component_target ${component_targets}) + get_property(component_dir TARGET ${component_target} PROPERTY COMPONENT_DIR) + __component_get_requirements(error reqs priv_reqs ${component_dir}) + if(error) + message(FATAL_ERROR "${error}") + endif() + + list(APPEND reqs "${common_reqs}") + + # Remove duplicates and the component itself from its requirements + __component_get_property(alias ${component_target} COMPONENT_ALIAS) + __component_get_property(_name ${component_target} COMPONENT_NAME) + + # Prevent component from linking to itself. + if(reqs) + list(REMOVE_DUPLICATES reqs) + list(REMOVE_ITEM reqs ${alias} ${_name}) + endif() + + if(priv_reqs) + list(REMOVE_DUPLICATES priv_reqs) + list(REMOVE_ITEM priv_reqs ${alias} ${_name}) + endif() + + __component_set_property(${component_target} REQUIRES "${reqs}") + __component_set_property(${component_target} PRIV_REQUIRES "${priv_reqs}") + endforeach() + idf_build_unset_property(BUILD_PROPERTIES_FILE) + file(REMOVE ${build_properties_file}) + + # Finally, do component expansion. In this case it simply means getting a final list + # of build component targets given the requirements set by each component. + if(__COMPONENTS) + unset(component_targets) + foreach(component ${__COMPONENTS}) + __component_get_target(component_target ${component}) + if(NOT component_target) + message(FATAL_ERROR "Failed to resolve component '${component}'.") + endif() + list(APPEND component_targets ${component_target}) + endforeach() + endif() + + foreach(component_target ${component_targets}) + __build_expand_requirements(${component_target}) + endforeach() + unset(__COMPONENT_TARGETS_SEEN) + + # Get a list of common component requirements in component targets form (previously + # we just have a list of component names) + foreach(common_req ${common_reqs}) + __component_get_target(component_target ${common_req}) + __component_get_property(lib ${component_target} COMPONENT_LIB) + idf_build_set_property(___COMPONENT_REQUIRES_COMMON ${lib} APPEND) + endforeach() + + # Perform component processing (inclusion of project_include.cmake, adding component + # subdirectories, creating library targets, linking libraries, etc.) + idf_build_get_property(idf_path IDF_PATH) + add_subdirectory(${idf_path} ${build_dir}/esp-idf) +endfunction() + +# idf_build_executable +# +# @brief Specify the executable the build system can attach dependencies to (for generating +# files used for linking, targets which should execute before creating the specified executable, +# generating additional binary files, generating files related to flashing, etc.) +function(idf_build_executable elf) + # Propagate link dependencies from component library targets to the executable + idf_build_get_property(build_components BUILD_COMPONENTS) + foreach(build_component ${build_components}) + get_target_property(type ${build_component} TYPE) + if(type STREQUAL "INTERFACE_LIBRARY") + get_target_property(iface_link_depends ${build_component} INTERFACE_LINK_DEPENDS) + else() + get_target_property(link_depends ${build_component} LINK_DEPENDS) + get_target_property(iface_link_depends ${build_component} INTERFACE_LINK_DEPENDS) + endif() + if(iface_link_depends) + list(APPEND _link_depends ${iface_link_depends}) + endif() + if(link_depends) + list(APPEND _link_depends ${link_depends}) + endif() + endforeach() + + idf_build_get_property(link_depends LINK_DEPENDS) + if(link_depends) + list(APPEND _link_depends ${link_depends}) + endif() + + set_property(TARGET ${elf} APPEND PROPERTY LINK_DEPENDS "${_link_depends}") + + # Set the EXECUTABLE_NAME and EXECUTABLE properties since there are generator expression + # from components that depend on it + get_filename_component(elf_name ${elf} NAME_WE) + idf_build_set_property(EXECUTABLE_NAME ${elf_name}) + idf_build_set_property(EXECUTABLE ${elf}) + + # Add dependency of the build target to the executable + add_dependencies(${elf} __idf_build_target) +endfunction() \ No newline at end of file diff --git a/tools/cmake/component.cmake b/tools/cmake/component.cmake new file mode 100644 index 0000000000..ccb7bc02a1 --- /dev/null +++ b/tools/cmake/component.cmake @@ -0,0 +1,460 @@ +# +# Internal function for retrieving component properties from a component target. +# +function(__component_get_property var component_target property) + get_property(val TARGET ${component_target} PROPERTY ${property}) + set(${var} "${val}" PARENT_SCOPE) +endfunction() + +# +# Internal function for setting component properties on a component target. As with build properties, +# set properties are also keeped track of. +# +function(__component_set_property component_target property val) + cmake_parse_arguments(_ "APPEND" "" "" ${ARGN}) + + if(__APPEND) + set_property(TARGET ${component_target} APPEND PROPERTY ${property} "${val}") + else() + set_property(TARGET ${component_target} PROPERTY ${property} "${val}") + endif() + + # Keep track of set component properties + __component_get_property(properties ${component_target} __COMPONENT_PROPERTIES) + if(NOT property IN_LIST properties) + __component_set_property(${component_target} __COMPONENT_PROPERTIES ${property} APPEND) + endif() +endfunction() + +# +# Given a component name or alias, get the corresponding component target. +# +function(__component_get_target var name_or_alias) + # Look at previously resolved names or aliases + idf_build_get_property(component_names_resolved __COMPONENT_NAMES_RESOLVED) + list(FIND component_names_resolved ${name_or_alias} result) + if(NOT result EQUAL -1) + # If it has been resolved before, return that value. The index is the same + # as in __COMPONENT_NAMES_RESOLVED as these are parallel lists. + idf_build_get_property(component_targets_resolved __COMPONENT_TARGETS_RESOLVED) + list(GET component_targets_resolved ${result} target) + set(${var} ${target} PARENT_SCOPE) + return() + endif() + + idf_build_get_property(component_targets __COMPONENT_TARGETS) + + # Assume first that the paramters is an alias. + string(REPLACE "::" "_" name_or_alias "${name_or_alias}") + set(component_target ___${name_or_alias}) + + if(component_target IN_LIST component_targets) + set(${var} ${component_target} PARENT_SCOPE) + set(target ${component_target}) + else() # assumption is wrong, try to look for it manually + unset(target) + foreach(component_target ${component_targets}) + __component_get_property(_component_name ${component_target} COMPONENT_NAME) + if(name_or_alias STREQUAL _component_name) + # There should only be one component of the same name + if(NOT target) + set(target ${component_target}) + else() + message(FATAL_ERROR "Multiple components with name '${name_or_alias}' found.") + return() + endif() + endif() + endforeach() + set(${var} ${target} PARENT_SCOPE) + endif() + + # Save the resolved name or alias + if(target) + idf_build_set_property(__COMPONENT_NAMES_RESOLVED ${name_or_alias} APPEND) + idf_build_set_property(__COMPONENT_TARGETS_RESOLVED ${target} APPEND) + endif() +endfunction() + +# +# Called during component registration, sets basic properties of the current component. +# +macro(__component_set_properties) + __component_get_property(type ${component_target} COMPONENT_TYPE) + + # Fill in the rest of component property + __component_set_property(${component_target} SRCS "${sources}") + __component_set_property(${component_target} INCLUDE_DIRS "${__INCLUDE_DIRS}") + + if(type STREQUAL LIBRARY) + __component_set_property(${component_target} PRIV_INCLUDE_DIRS "${__PRIV_INCLUDE_DIRS}") + endif() + + __component_set_property(${component_target} LDFRAGMENTS "${__LDFRAGMENTS}") + __component_set_property(${component_target} EMBED_FILES "${__EMBED_FILES}") + __component_set_property(${component_target} EMBED_TXTFILES "${__EMBED_TXTFILES}") + __component_set_property(${component_target} REQUIRED_IDF_TARGETS "${__REQUIRED_IDF_TARGETS}") +endmacro() + +# +# Add a component to process in the build. The components are keeped tracked of in property +# __COMPONENT_TARGETS in component target form. +# +function(__component_add component_dir prefix) + # For each component, two entities are created: a component target and a component library. The + # component library is created during component registration (the actual static/interface library). + # On the other hand, component targets are created early in the build + # (during adding component as this function suggests). + # This is so that we still have a target to attach properties to up until the component registration. + # Plus, interface libraries have limitations on the types of properties that can be set on them, + # so later in the build, these component targets actually contain the properties meant for the + # corresponding component library. + idf_build_get_property(component_targets __COMPONENT_TARGETS) + get_filename_component(abs_dir ${component_dir} ABSOLUTE) + get_filename_component(base_dir ${abs_dir} NAME) + + set(component_name ${base_dir}) + # The component target has three underscores as a prefix. The corresponding component library + # only has two. + set(component_target ___${prefix}_${component_name}) + + # If a component of the same name has not been added before If it has been added + # before just override the properties. As a side effect, components added later + # 'override' components added earlier. + if(NOT component_target IN_LIST component_targets) + if(NOT TARGET ${component_target}) + add_custom_target(${component_target} EXCLUDE_FROM_ALL) + endif() + idf_build_set_property(__COMPONENT_TARGETS ${component_target} APPEND) + endif() + + set(component_lib __${prefix}_${component_name}) + set(component_dir ${abs_dir}) + set(component_alias ${prefix}::${component_name}) # The 'alias' of the component library, + # used to refer to the component outside + # the build system. Users can use this name + # to resolve ambiguity with component names + # and to link IDF components to external targets. + + # Set the basic properties of the component + __component_set_property(${component_target} COMPONENT_LIB ${component_lib}) + __component_set_property(${component_target} COMPONENT_NAME ${component_name}) + __component_set_property(${component_target} COMPONENT_DIR ${component_dir}) + __component_set_property(${component_target} COMPONENT_ALIAS ${component_alias}) + __component_set_property(${component_target} __PREFIX ${prefix}) + + # Set Kconfig related properties on the component + __kconfig_component_init(${component_target}) +endfunction() + +# +# Given a component directory, get the requirements by expanding it early. The expansion is performed +# using a separate CMake script (the expansion is performed in a separate instance of CMake in scripting mode). +# +function(__component_get_requirements error requires_var priv_requires_var component_dir) + idf_build_get_property(idf_path IDF_PATH) + idf_build_get_property(build_properties_file BUILD_PROPERTIES_FILE) + idf_build_get_property(idf_target IDF_TARGET) + + # This function assumes that the directory has been checked to contain a component, thus + # no check is performed here. + execute_process(COMMAND "${CMAKE_COMMAND}" + -D "IDF_PATH=${idf_path}" + -D "IDF_TARGET=${idf_target}" + -D "COMPONENT_DIR=${component_dir}" + -D "BUILD_PROPERTIES_FILE=${build_properties_file}" + -D "CMAKE_BUILD_EARLY_EXPANSION=1" + -P "${idf_path}/tools/cmake/scripts/component_get_requirements.cmake" + RESULT_VARIABLE result + ERROR_VARIABLE error + ) + + if(NOT result EQUAL 0) + set(error "${error}" PARENT_SCOPE) + return() + endif() + + string(REGEX REPLACE ";" "\\\\;" _output "${error}") + string(REGEX REPLACE "\n" ";" _output "${_output}") + list(REVERSE _output) + + if(_output) + list(GET _output 1 _output) + + string(REGEX MATCH "\(.*\):::\(.*\)" _output "${_output}") + + string(REPLACE ":" ";" requires "${CMAKE_MATCH_1}") + string(REPLACE ":" ";" priv_requires "${CMAKE_MATCH_2}") + endif() + + set(${requires_var} ${requires} PARENT_SCOPE) + set(${priv_requires_var} ${priv_requires} PARENT_SCOPE) +endfunction() + +# __component_add_sources, __component_check_target +# +# Utility macros for component registration. Adds source files and checks target requirements +# respectively. +macro(__component_add_sources sources) + set(sources "") + if(__SRCS) + if(__SRC_DIRS) + message(WARNING "SRCS and SRC_DIRS are both specified; ignoring SRC_DIRS.") + endif() + foreach(src ${__SRCS}) + get_filename_component(src "${src}" ABSOLUTE BASE_DIR ${COMPONENT_DIR}) + list(APPEND sources ${src}) + endforeach() + else() + if(__SRC_DIRS) + foreach(dir ${__SRC_DIRS}) + get_filename_component(abs_dir ${dir} ABSOLUTE BASE_DIR ${COMPONENT_DIR}) + + if(NOT IS_DIRECTORY ${abs_dir}) + message(FATAL_ERROR "SRC_DIRS entry '${dir}' does not exist.") + endif() + + file(GLOB dir_sources "${abs_dir}/*.c" "${abs_dir}/*.cpp" "${abs_dir}/*.S") + + if(dir_sources) + foreach(src ${dir_sources}) + get_filename_component(src "${src}" ABSOLUTE BASE_DIR ${COMPONENT_DIR}) + list(APPEND sources "${src}") + endforeach() + else() + message(WARNING "No source files found for SRC_DIRS entry '${dir}'.") + endif() + endforeach() + endif() + + if(__EXCLUDE_SRCS) + foreach(src ${__EXCLUDE_SRCS}) + get_filename_component(src "${src}" ABSOLUTE) + list(REMOVE_ITEM source "${src}") + endforeach() + endif() + endif() + + list(REMOVE_DUPLICATES sources) +endmacro() + +macro(__component_check_target) + if(__REQUIRED_IDF_TARGETS) + idf_build_get_property(idf_target IDF_TARGET) + if(NOT idf_target IN_LIST __REQUIRED_IDF_TARGETS) + message(FATAL_ERROR "Component ${COMPONENT_NAME} only supports targets: ${__REQUIRED_IDF_TARGETS}") + endif() + endif() +endmacro() + +# idf_component_get_property +# +# @brief Retrieve the value of the specified component property +# +# @param[out] var the variable to store the value of the property in +# @param[in] component the component name or alias to get the value of the property of +# @param[in] property the property to get the value of +# +# @param[in, optional] GENERATOR_EXPRESSION (option) retrieve the generator expression for the property +# instead of actual value +function(idf_component_get_property var component property) + cmake_parse_arguments(_ "GENERATOR_EXPRESSION" "" "" ${ARGN}) + __component_get_target(component_target ${component}) + if(__GENERATOR_EXPRESSION) + set(val "$") + else() + __component_get_property(val ${component_target} ${property}) + endif() + set(${var} "${val}" PARENT_SCOPE) +endfunction() + +# idf_component_set_property +# +# @brief Set the value of the specified component property related. The property is +# also added to the internal list of component properties if it isn't there already. +# +# @param[in] component component name or alias of the component to set the property of +# @param[in] property the property to set the value of +# @param[out] value value of the property to set to +# +# @param[in, optional] APPEND (option) append the value to the current value of the +# property instead of replacing it +function(idf_component_set_property component property val) + cmake_parse_arguments(_ "APPEND" "" "" ${ARGN}) + __component_get_target(component_target ${component}) + + if(__APPEND) + __component_set_property(${component_target} ${property} "${val}" APPEND) + else() + __component_set_property(${component_target} ${property} "${val}") + endif() +endfunction() + +# idf_component_register +# +# @brief Register a component to the build, creating component library targets etc. +# +# @param[in, optional] SRCS (multivalue) list of source files for the component +# @param[in, optional] SRC_DIRS (multivalue) list of source directories to look for source files +# in (.c, .cpp. .S); ignored when SRCS is specified. +# @param[in, optional] EXCLUDE_SRCS (multivalue) used to exclude source files for the specified +# SRC_DIRS +# @param[in, optional] INCLUDE_DIRS (multivalue) public include directories for the created component library +# @param[in, optional] PRIV_INCLUDE_DIRS (multivalue) private include directories for the created component library +# @param[in, optional] LDFRAGMENTS (multivalue) linker script fragments for the component +# @param[in, optional] REQUIRES (multivalue) publicly required components in terms of usage requirements +# @param[in, optional] PRIV_REQUIRES (multivalue) privately required components in terms of usage requirements +# or components only needed for functions/values defined in its project_include.cmake +# @param[in, optional] REQUIRED_IDF_TARGETS (multivalue) the list of IDF build targets that the component only supports +# @param[in, optional] EMBED_FILES (multivalue) list of binary files to embed with the component +# @param[in, optional] EMBED_TXTFILES (multivalue) list of text files to embed with the component +function(idf_component_register) + set(options) + set(single_value) + set(multi_value SRCS SRC_DIRS EXCLUDE_SRCS + INCLUDE_DIRS PRIV_INCLUDE_DIRS LDFRAGMENTS REQUIRES + PRIV_REQUIRES REQUIRED_IDF_TARGETS EMBED_FILES EMBED_TXTFILES) + cmake_parse_arguments(_ "${options}" "${single_value}" "${multi_value}" ${ARGN}) + + if(NOT __idf_component_context) + message(FATAL_ERROR "Called idf_component_register from a non-component directory.") + endif() + + __component_check_target() + __component_add_sources(sources) + + # Create the final target for the component. This target is the target that is + # visible outside the build system. + __component_get_target(component_target ${COMPONENT_ALIAS}) + __component_get_property(component_lib ${component_target} COMPONENT_LIB) + + # Use generator expression so that users can append/override flags even after call to + # idf_build_process + idf_build_get_property(include_directories INCLUDE_DIRECTORIES GENERATOR_EXPRESSION) + idf_build_get_property(compile_options COMPILE_OPTIONS GENERATOR_EXPRESSION) + idf_build_get_property(c_compile_options C_COMPILE_OPTIONS GENERATOR_EXPRESSION) + idf_build_get_property(cxx_compile_options CXX_COMPILE_OPTIONS GENERATOR_EXPRESSION) + idf_build_get_property(common_reqs ___COMPONENT_REQUIRES_COMMON) + + include_directories("${include_directories}") + add_compile_options("${compile_options}") + add_c_compile_options("${c_compile_options}") + add_cxx_compile_options("${cxx_compile_options}") + + # Unfortunately add_definitions() does not support generator expressions. A new command + # add_compile_definition() does but is only available on CMake 3.12 or newer. This uses + # add_compile_options(), which can add any option as the workaround. + # + # TODO: Use add_compile_definitions() once minimum supported version is 3.12 or newer. + idf_build_get_property(compile_definitions COMPILE_DEFINITIONS GENERATOR_EXPRESSION) + add_compile_options("${compile_definitions}") + + list(REMOVE_ITEM common_reqs ${component_lib}) + link_libraries(${common_reqs}) + + idf_build_get_property(sdkconfig_h SDKCONFIG_HEADER) + get_filename_component(sdkconfig_h ${sdkconfig_h} DIRECTORY) + + # The contents of 'sources' is from the __component_add_sources call + if(sources OR __EMBED_FILES OR __EMBED_TXTFILES) + add_library(${component_lib} STATIC ${sources}) + __component_set_property(${component_target} COMPONENT_TYPE LIBRARY) + target_include_directories(${component_lib} PUBLIC ${__INCLUDE_DIRS}) + target_include_directories(${component_lib} PRIVATE ${__PRIV_INCLUDE_DIRS}) + target_include_directories(${component_lib} PUBLIC ${sdkconfig_h}) + set_target_properties(${component_lib} PROPERTIES OUTPUT_NAME ${COMPONENT_NAME}) + __ldgen_add_component(${component_lib}) + else() + add_library(${component_lib} INTERFACE) + __component_set_property(${component_target} COMPONENT_TYPE CONFIG_ONLY) + target_include_directories(${component_lib} INTERFACE ${__INCLUDE_DIRS}) + target_include_directories(${component_lib} INTERFACE ${sdkconfig_h}) + endif() + + # Alias the static/interface library created for linking to external targets. + # The alias is the :: name. + __component_get_property(component_alias ${component_target} COMPONENT_ALIAS) + add_library(${component_alias} ALIAS ${component_lib}) + + # Perform other component processing, such as embedding binaries and processing linker + # script fragments + foreach(file ${__EMBED_FILES}) + target_add_binary_data(${component_lib} "${file}" "BINARY") + endforeach() + + foreach(file ${__EMBED_TXTFILES}) + target_add_binary_data(${component_lib} "${file}" "TEXT") + endforeach() + + if(__LDFRAGMENTS) + __ldgen_add_fragment_files("${__LDFRAGMENTS}") + endif() + + # Add the component to built components + idf_build_set_property(__BUILD_COMPONENTS ${component_lib} APPEND) + idf_build_set_property(BUILD_COMPONENTS ${component_alias} APPEND) + + # Make the COMPONENT_LIB variable available in the component CMakeLists.txt + set(COMPONENT_LIB ${component_lib} PARENT_SCOPE) + # COMPONENT_TARGET is deprecated but is made available with same function + # as COMPONENT_LIB for compatibility. + set(COMPONENT_TARGET ${component_lib} PARENT_SCOPE) +endfunction() + +# +# Deprecated functions +# + +# register_component +# +# Compatibility function for registering 3.xx style components. +macro(register_component) + spaces2list(COMPONENT_SRCS) + spaces2list(COMPONENT_SRCDIRS) + spaces2list(COMPONENT_ADD_INCLUDEDIRS) + spaces2list(COMPONENT_PRIV_INCLUDEDIRS) + spaces2list(COMPONENT_REQUIRES) + spaces2list(COMPONENT_PRIV_REQUIRES) + spaces2list(COMPONENT_ADD_LDFRAGMENTS) + spaces2list(COMPONENT_EMBED_FILES) + spaces2list(COMPONENT_EMBED_TXTFILES) + spaces2list(COMPONENT_SRCEXCLUDE) + idf_component_register(SRCS "${COMPONENT_SRCS}" + SRC_DIRS "${COMPONENT_SRCDIRS}" + INCLUDE_DIRS "${COMPONENT_ADD_INCLUDEDIRS}" + PRIV_INCLUDE_DIRS "${COMPONENT_PRIV_INCLUDEDIRS}" + REQUIRES "${COMPONENT_REQUIRES}" + PRIV_REQUIRES "${COMPONENT_PRIV_REQUIRES}" + LDFRAGMENTS "${COMPONENT_ADD_LDFRAGMENTS}" + EMBED_FILES "${COMPONENT_EMBED_FILES}" + EMBED_TXTFILES "${COMPONENT_EMBED_TXTFILES}" + EXCLUDE_SRCS "${COMPONENT_SRCEXCLUDE}") +endmacro() + +# require_idf_targets +# +# Compatibility function for requiring IDF build targets for 3.xx style components. +function(require_idf_targets) + set(__REQUIRED_IDF_TARGETS "${ARGN}") + __component_check_target() +endfunction() + +# register_config_only_component +# +# Compatibility function for registering 3.xx style config components. +macro(register_config_only_component) + register_component() +endmacro() + +# component_compile_options +# +# Wrapper around target_compile_options that passes the component name +function(component_compile_options) + target_compile_options(${COMPONENT_LIB} PRIVATE ${ARGV}) +endfunction() + +# component_compile_definitions +# +# Wrapper around target_compile_definitions that passes the component name +function(component_compile_definitions) + target_compile_definitions(${COMPONENT_LIB} PRIVATE ${ARGV}) +endfunction() \ No newline at end of file diff --git a/tools/cmake/component_utils.cmake b/tools/cmake/component_utils.cmake deleted file mode 100644 index 4100664770..0000000000 --- a/tools/cmake/component_utils.cmake +++ /dev/null @@ -1,76 +0,0 @@ -function(debug message) - if(DEBUG) - message(STATUS "${message}") - endif() -endfunction() - -# 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 components component_paths variable) - list(FIND components ${find_name} idx) - if(NOT idx EQUAL -1) - list(GET component_paths ${idx} path) - set("${variable}" "${path}" PARENT_SCOPE) - return() - else() - endif() - # 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 test_component_names) - # component_dirs entries can be files or lists of files - set(paths "") - set(names "") - set(test_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}) - debug("Looking for CMakeLists.txt in ${dir}") - file(GLOB component "${dir}/CMakeLists.txt") - if(component) - debug("CMakeLists.txt file ${component}") - get_filename_component(component "${component}" DIRECTORY) - get_filename_component(name "${component}" NAME) - if(NOT name IN_LIST names) - list(APPEND names "${name}") - list(APPEND paths "${component}") - - # Look for test component directory - file(GLOB test "${component}/test/CMakeLists.txt") - if(test) - list(APPEND test_names "${name}") - 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() - endif() - endforeach() - - set(${component_paths} ${paths} PARENT_SCOPE) - set(${component_names} ${names} PARENT_SCOPE) - set(${test_component_names} ${test_names} PARENT_SCOPE) -endfunction() diff --git a/tools/cmake/components.cmake b/tools/cmake/components.cmake deleted file mode 100644 index d2be1ffa4c..0000000000 --- a/tools/cmake/components.cmake +++ /dev/null @@ -1,179 +0,0 @@ -# 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("${result_path}" "${result}" PARENT_SCOPE) -endfunction() - -# Add a component to the build, using the COMPONENT variables defined -# in the parent -# -function(register_component) - get_filename_component(component_dir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY) - - spaces2list(COMPONENT_SRCS) - spaces2list(COMPONENT_SRCDIRS) - spaces2list(COMPONENT_ADD_INCLUDEDIRS) - spaces2list(COMPONENT_SRCEXCLUDE) - - if(COMPONENT_SRCDIRS) - # Warn user if both COMPONENT_SRCDIRS and COMPONENT_SRCS are set - if(COMPONENT_SRCS) - message(WARNING "COMPONENT_SRCDIRS and COMPONENT_SRCS are both set, COMPONENT_SRCS will be ignored") - endif() - - set(COMPONENT_SRCS "") - - foreach(dir ${COMPONENT_SRCDIRS}) - get_filename_component(abs_dir ${dir} ABSOLUTE BASE_DIR ${component_dir}) - if(NOT IS_DIRECTORY ${abs_dir}) - message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: COMPONENT_SRCDIRS entry '${dir}' does not exist") - endif() - - file(GLOB matches "${abs_dir}/*.c" "${abs_dir}/*.cpp" "${abs_dir}/*.S") - if(matches) - list(SORT matches) - set(COMPONENT_SRCS "${COMPONENT_SRCS};${matches}") - else() - message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: COMPONENT_SRCDIRS entry '${dir}' has no source files") - endif() - endforeach() - endif() - - # Remove COMPONENT_SRCEXCLUDE matches - foreach(exclude ${COMPONENT_SRCEXCLUDE}) - get_filename_component(exclude "${exclude}" ABSOLUTE ${component_dir}) - foreach(src ${COMPONENT_SRCS}) - get_filename_component(abs_src "${src}" ABSOLUTE ${component_dir}) - if("${exclude}" STREQUAL "${abs_src}") # compare as canonical paths - list(REMOVE_ITEM COMPONENT_SRCS "${src}") - endif() - endforeach() - endforeach() - - # add as a PUBLIC library (if there are source files) or INTERFACE (if header only) - if(COMPONENT_SRCS OR embed_binaries) - add_library(${COMPONENT_TARGET} STATIC ${COMPONENT_SRCS}) - set(include_type PUBLIC) - - set_property(TARGET ${COMPONENT_TARGET} PROPERTY OUTPUT_NAME ${COMPONENT_NAME}) - - ldgen_component_add(${COMPONENT_TARGET}) - else() - add_library(${COMPONENT_TARGET} INTERFACE) # header-only component - set(include_type INTERFACE) - endif() - - # binaries to embed directly in library - spaces2list(COMPONENT_EMBED_FILES) - spaces2list(COMPONENT_EMBED_TXTFILES) - foreach(embed_data ${COMPONENT_EMBED_FILES} ${COMPONENT_EMBED_TXTFILES}) - if(embed_data IN_LIST COMPONENT_EMBED_TXTFILES) - set(embed_type "TEXT") - else() - set(embed_type "BINARY") - endif() - target_add_binary_data("${COMPONENT_TARGET}" "${embed_data}" "${embed_type}") - endforeach() - - # 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}) - message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: " - "COMPONENT_ADD_INCLUDEDIRS entry '${include_dir}' not found") - endif() - target_include_directories(${COMPONENT_TARGET} ${include_type} ${abs_dir}) - endforeach() - - # add component private includes - foreach(include_dir ${COMPONENT_PRIV_INCLUDEDIRS}) - if(${include_type} STREQUAL INTERFACE) - message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE} " - "sets no component source files but sets COMPONENT_PRIV_INCLUDEDIRS") - endif() - - get_filename_component(abs_dir ${include_dir} ABSOLUTE BASE_DIR ${component_dir}) - if(NOT IS_DIRECTORY ${abs_dir}) - message(FATAL_ERROR "${CMAKE_CURRENT_LIST_FILE}: " - "COMPONENT_PRIV_INCLUDEDIRS entry '${include_dir}' does not exist") - endif() - target_include_directories(${COMPONENT_TARGET} PRIVATE ${abs_dir}) - endforeach() - - if(${COMPONENT_NAME} IN_LIST BUILD_TEST_COMPONENTS) - target_link_libraries(${COMPONENT_TARGET} "-L${CMAKE_CURRENT_BINARY_DIR}") - target_link_libraries(${COMPONENT_TARGET} "-Wl,--whole-archive -l${COMPONENT_NAME} -Wl,--no-whole-archive") - endif() - - if(COMPONENT_SRCS OR embed_binaries) - target_include_directories(${COMPONENT_TARGET} PUBLIC ${IDF_INCLUDE_DIRECTORIES}) - target_compile_options(${COMPONENT_TARGET} PUBLIC ${IDF_COMPILE_OPTIONS}) - target_compile_options(${COMPONENT_TARGET} PUBLIC $<$:${IDF_C_COMPILE_OPTIONS}>) - target_compile_options(${COMPONENT_TARGET} PUBLIC $<$:${IDF_CXX_COMPILE_OPTIONS}>) - target_compile_definitions(${COMPONENT_TARGET} PUBLIC ${IDF_COMPILE_DEFINITIONS}) - endif() - - if(COMPONENT_ADD_LDFRAGMENTS) - spaces2list(COMPONENT_ADD_LDFRAGMENTS) - ldgen_add_fragment_files(${COMPONENT_TARGET} "${COMPONENT_ADD_LDFRAGMENTS}") - endif() -endfunction() - -function(register_config_only_component) - get_filename_component(component_dir ${CMAKE_CURRENT_LIST_FILE} DIRECTORY) - get_filename_component(component ${component_dir} NAME) - - # 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(require_idf_targets) - if(NOT ${IDF_TARGET} IN_LIST ARGN) - message(FATAL_ERROR "Component ${COMPONENT_NAME} only supports targets: ${ARGN}") - endif() -endfunction() - -# component_compile_options -# -# Wrapper around target_compile_options that passes the component name -function(component_compile_options) - target_compile_options(${COMPONENT_TARGET} PRIVATE ${ARGV}) -endfunction() - -# component_compile_definitions -# -# Wrapper around target_compile_definitions that passes the component name -function(component_compile_definitions) - target_compile_definitions(${COMPONENT_TARGET} PRIVATE ${ARGV}) -endfunction() - -# component_get_target -# -# Get the library target created for the given component -function(component_get_target var component) - get_property(prefix GLOBAL PROPERTY __IDF_COMPONENTS_PREFIX) - set(${var} ${prefix}_${component} PARENT_SCOPE) -endfunction() diff --git a/tools/cmake/convert_to_cmake.py b/tools/cmake/convert_to_cmake.py index d2d46d462c..80d9296d62 100755 --- a/tools/cmake/convert_to_cmake.py +++ b/tools/cmake/convert_to_cmake.py @@ -173,7 +173,7 @@ def convert_component(project_path, component_path): else: f.write("register_config_only_component()\n") if cflags is not None: - f.write("component_compile_options(%s)\n" % cflags) + f.write("target_compile_options(${COMPONENT_LIB} PRIVATE %s)\n" % cflags) print("Converted %s" % cmakelists_path) diff --git a/tools/cmake/crosstool_version_check.cmake b/tools/cmake/crosstool_version_check.cmake index 1180fc029a..2ea681da1b 100644 --- a/tools/cmake/crosstool_version_check.cmake +++ b/tools/cmake/crosstool_version_check.cmake @@ -31,7 +31,8 @@ function(crosstool_version_check expected_ctng_version) endfunction() function(get_expected_ctng_version _toolchain_ver _gcc_ver) - file(STRINGS ${IDF_PATH}/tools/toolchain_versions.mk config_contents) + idf_build_get_property(idf_path IDF_PATH) + file(STRINGS ${idf_path}/tools/toolchain_versions.mk config_contents) foreach(name_and_value ${config_contents}) # Strip spaces string(REPLACE " " "" name_and_value ${name_and_value}) diff --git a/tools/cmake/idf.cmake b/tools/cmake/idf.cmake new file mode 100644 index 0000000000..9d45fd9ddf --- /dev/null +++ b/tools/cmake/idf.cmake @@ -0,0 +1,45 @@ +get_property(__idf_env_set GLOBAL PROPERTY __IDF_ENV_SET) +if(NOT __idf_env_set) + # Infer an IDF_PATH relative to the tools/cmake directory + get_filename_component(_idf_path "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) + file(TO_CMAKE_PATH "${_idf_path}" _idf_path) + + # Get the path set in environment + set(idf_path $ENV{IDF_PATH}) + file(TO_CMAKE_PATH "${idf_path}" idf_path) + + # Environment IDF_PATH should match the inferred IDF_PATH. If not, warn the user. + if(idf_path) + if(NOT idf_path STREQUAL _idf_path) + message(WARNING "IDF_PATH environment variable is different from inferred IDF_PATH. + Check if your project's top-level CMakeLists.txt includes the right + CMake files. Environment IDF_PATH will be used for the build.") + endif() + else() + message(WARNING "IDF_PATH environment variable not found. Setting IDF_PATH to '${_idf_path}'.") + set(idf_path ${_idf_path}) + set(ENV{IDF_PATH} ${_idf_path}) + endif() + + # Include other CMake modules required + set(CMAKE_MODULE_PATH + "${idf_path}/tools/cmake" + "${idf_path}/tools/cmake/third_party" + ${CMAKE_MODULE_PATH}) + include(build) + + set(IDF_PATH ${idf_path}) + + include(GetGitRevisionDescription) + include(git_submodules) + include(crosstool_version_check) + include(kconfig) + include(component) + include(utilities) + include(targets) + include(ldgen) + + __build_init("${idf_path}") + + set_property(GLOBAL PROPERTY __IDF_ENV_SET 1) +endif() \ No newline at end of file diff --git a/tools/cmake/idf_functions.cmake b/tools/cmake/idf_functions.cmake deleted file mode 100644 index 697ef45ff0..0000000000 --- a/tools/cmake/idf_functions.cmake +++ /dev/null @@ -1,326 +0,0 @@ -# -# Load cmake modules -# - -get_property(__idf_environment_set GLOBAL PROPERTY __IDF_ENVIRONMENT_SET) - -if(NOT __idf_environment_set) - - # Set IDF_PATH, as nothing else will work without this. - set(IDF_PATH "$ENV{IDF_PATH}") - if(NOT IDF_PATH) - # Documentation says you should set IDF_PATH in your environment, but we - # can infer it relative to tools/cmake directory if it's not set. - get_filename_component(IDF_PATH "${CMAKE_CURRENT_LIST_DIR}/../.." ABSOLUTE) - endif() - file(TO_CMAKE_PATH "${IDF_PATH}" IDF_PATH) - set(ENV{IDF_PATH} ${IDF_PATH}) - - set(CMAKE_MODULE_PATH - "${IDF_PATH}/tools/cmake" - "${IDF_PATH}/tools/cmake/third_party" - ${CMAKE_MODULE_PATH}) - include(utilities) - include(components) - include(kconfig) - include(targets) - include(git_submodules) - include(GetGitRevisionDescription) - include(crosstool_version_check) - include(ldgen) - - set_default(PYTHON "python") - - if(NOT PYTHON_DEPS_CHECKED AND NOT BOOTLOADER_BUILD) - message(STATUS "Checking Python dependencies...") - execute_process(COMMAND "${PYTHON}" "${IDF_PATH}/tools/check_python_dependencies.py" - RESULT_VARIABLE result) - if(NOT result EQUAL 0) - message(FATAL_ERROR "Some Python dependencies must be installed. Check above message for details.") - endif() - endif() - - idf_set_target() - - set_property(GLOBAL APPEND PROPERTY __IDF_COMPONENTS_PREFIX "idf_component") - set_property(GLOBAL PROPERTY __IDF_ENVIRONMENT_SET 1) -endif() - -macro(idf_set_variables) - set_default(IDF_BUILD_ARTIFACTS OFF) - - if(IDF_BUILD_ARTIFACTS) - if(NOT IDF_BUILD_ARTIFACTS_DIR OR NOT IDF_PROJECT_EXECUTABLE) - message(FATAL_ERROR "IDF_BUILD_ARTIFACTS and IDF_PROJECT_EXECUTABLE needs to be specified \ - if IDF_BUILD_ARTIFACTS is ON.") - endif() - endif() - - set_default(IDF_COMPONENT_DIRS "${IDF_EXTRA_COMPONENT_DIRS} ${IDF_PATH}/components") - set_default(IDF_COMPONENTS "") - set_default(IDF_COMPONENT_REQUIRES_COMMON "cxx ${IDF_TARGET} newlib freertos heap log \ - esp_rom esp_common xtensa") - - list(FIND IDF_COMPONENT_REQUIRES_COMMON "${IDF_TARGET}" result) - if(result EQUAL -1) - list(APPEND IDF_COMPONENT_REQUIRES_COMMON "${IDF_TARGET}") - endif() - - if(CONFIG_LEGACY_INCLUDE_COMMON_HEADERS) - list(APPEND IDF_COMPONENT_REQUIRES_COMMON "soc") - endif() - - set(IDF_PROJECT_PATH "${CMAKE_SOURCE_DIR}") - - set(ESP_PLATFORM 1 CACHE BOOL INTERNAL) - - spaces2list(IDF_COMPONENT_DIRS) - spaces2list(IDF_COMPONENTS) - spaces2list(IDF_COMPONENT_REQUIRES_COMMON) -endmacro() - -# Add all the IDF global compiler & preprocessor options -# (applied to all components). Some are config-dependent -# -# If you only want to set options for a particular component, -# don't call or edit this function. TODO DESCRIBE WHAT TO DO INSTEAD -# -function(idf_set_global_compile_options) - # Temporary trick to support both gcc5 and gcc8 builds - if(CMAKE_C_COMPILER_VERSION VERSION_EQUAL 5.2.0) - set(GCC_NOT_5_2_0 0 CACHE STRING "GCC is 5.2.0 version") - else() - set(GCC_NOT_5_2_0 1 CACHE STRING "GCC is not 5.2.0 version") - endif() - list(APPEND compile_definitions "GCC_NOT_5_2_0=${GCC_NOT_5_2_0}") - - list(APPEND compile_definitions "ESP_PLATFORM" "HAVE_CONFIG_H") - - spaces2list(CMAKE_C_FLAGS) - spaces2list(CMAKE_CXX_FLAGS) - list(APPEND compile_options "${CMAKE_C_FLAGS}") - list(APPEND c_compile_options "${CMAKE_C_FLAGS}") - list(APPEND cxx_compile_options "${CMAKE_CXX_FLAGS}") - - if(CONFIG_OPTIMIZATION_LEVEL_RELEASE) - list(APPEND compile_options "-Os") - else() - list(APPEND compile_options "-Og") - endif() - - list(APPEND c_compile_options "-std=gnu99") - list(APPEND cxx_compile_options "-std=gnu++11" "-fno-rtti") - - # IDF uses some GNU extension from libc - list(APPEND compile_definitions "_GNU_SOURCE") - - if(CONFIG_CXX_EXCEPTIONS) - list(APPEND cxx_compile_options "-fexceptions") - else() - list(APPEND cxx_compile_options "-fno-exceptions") - endif() - - # Default compiler configuration - list(APPEND compile_options "-ffunction-sections" - "-fdata-sections" - "-fstrict-volatile-bitfields" - "-nostdlib") - - list(APPEND compile_options "-Wall" - "-Werror=all" - "-Wno-error=unused-function" - "-Wno-error=unused-but-set-variable" - "-Wno-error=unused-variable" - "-Wno-error=deprecated-declarations" - "-Wextra" - "-Wno-unused-parameter" - "-Wno-sign-compare") - - list(APPEND c_compile_options "-Wno-old-style-declaration") - - if(CONFIG_DISABLE_GCC8_WARNINGS) - list(APPEND compile_options - "-Wno-parentheses" - "-Wno-sizeof-pointer-memaccess" - "-Wno-clobbered" - ) - - # doesn't use GCC_NOT_5_2_0 because idf_set_global_variables was not called before - if(NOT CMAKE_C_COMPILER_VERSION VERSION_EQUAL 5.2.0) - list(APPEND compile_options - "-Wno-format-overflow" - "-Wno-stringop-truncation" - "-Wno-misleading-indentation" - "-Wno-cast-function-type" - "-Wno-implicit-fallthrough" - "-Wno-unused-const-variable" - "-Wno-switch-unreachable" - "-Wno-format-truncation" - "-Wno-memset-elt-size" - "-Wno-int-in-bool-context" - ) - endif() - endif() - - # Stack protection - if(NOT BOOTLOADER_BUILD) - if(CONFIG_STACK_CHECK_NORM) - list(APPEND compile_options "-fstack-protector") - elseif(CONFIG_STACK_CHECK_STRONG) - list(APPEND compile_options "-fstack-protector-strong") - elseif(CONFIG_STACK_CHECK_ALL) - list(APPEND compile_options "-fstack-protector-all") - endif() - endif() - - if(CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED) - list(APPEND compile_definitions "NDEBUG") - endif() - - # Always generate debug symbols (even in Release mode, these don't - # go into the final binary so have no impact on size) - list(APPEND compile_options "-ggdb") - - # Use EXTRA_CFLAGS, EXTRA_CXXFLAGS and EXTRA_CPPFLAGS to add more priority options to the compiler - # EXTRA_CPPFLAGS is used for both C and C++ - # Unlike environments' CFLAGS/CXXFLAGS/CPPFLAGS which work for both host and target build, - # these works only for target build - set(EXTRA_CFLAGS "$ENV{EXTRA_CFLAGS}") - set(EXTRA_CXXFLAGS "$ENV{EXTRA_CXXFLAGS}") - set(EXTRA_CPPFLAGS "$ENV{EXTRA_CPPFLAGS}") - spaces2list(EXTRA_CFLAGS) - spaces2list(EXTRA_CXXFLAGS) - spaces2list(EXTRA_CPPFLAGS) - list(APPEND c_compile_options ${EXTRA_CFLAGS}) - list(APPEND cxx_compile_options ${EXTRA_CXXFLAGS}) - list(APPEND compile_options ${EXTRA_CPPFLAGS}) - - set_default(IDF_COMPILE_DEFINITIONS "${compile_definitions}") - set_default(IDF_COMPILE_OPTIONS "${compile_options}") - set_default(IDF_C_COMPILE_OPTIONS "${c_compile_options}") - set_default(IDF_CXX_COMPILE_OPTIONS "${cxx_compile_options}") - set_default(IDF_INCLUDE_DIRECTORIES "${CONFIG_DIR}") - - set(IDF_COMPILE_DEFINITIONS ${IDF_COMPILE_DEFINITIONS} PARENT_SCOPE) - set(IDF_COMPILE_OPTIONS ${IDF_COMPILE_OPTIONS} PARENT_SCOPE) - set(IDF_C_COMPILE_OPTIONS ${IDF_C_COMPILE_OPTIONS} PARENT_SCOPE) - set(IDF_CXX_COMPILE_OPTIONS ${IDF_CXX_COMPILE_OPTIONS} PARENT_SCOPE) - set(IDF_INCLUDE_DIRECTORIES ${CONFIG_DIR} PARENT_SCOPE) -endfunction() - -# Verify the IDF environment is configured correctly (environment, toolchain, etc) -function(idf_verify_environment) - if(NOT CMAKE_PROJECT_NAME) - message(FATAL_ERROR "Internal error, IDF project.cmake should have set this variable already") - endif() - - # Check toolchain is configured properly in cmake - if(NOT ( ${CMAKE_SYSTEM_NAME} STREQUAL "Generic" AND ${CMAKE_C_COMPILER} MATCHES xtensa)) - message(FATAL_ERROR "Internal error, toolchain has not been set correctly by project " - "(or an invalid CMakeCache.txt file has been generated somehow)") - endif() - - # - # Warn if the toolchain version doesn't match - # - # TODO: make these platform-specific for diff toolchains - get_expected_ctng_version(expected_toolchain expected_gcc) - gcc_version_check("${expected_gcc}") - crosstool_version_check("${expected_toolchain}") -endfunction() - -# idf_get_git_revision -# -# Set global IDF_VER to the git revision of ESP-IDF. -# -# Running git_describe() here automatically triggers rebuilds -# if the ESP-IDF git version changes -function(idf_get_git_revision) - git_describe(IDF_VER_GIT "${IDF_PATH}") - if(EXISTS "${IDF_PATH}/version.txt") - file(STRINGS "${IDF_PATH}/version.txt" IDF_VER_T) - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${IDF_PATH}/version.txt") - else() - set(IDF_VER_T ${IDF_VER_GIT}) - endif() - # cut IDF_VER to required 32 characters. - string(SUBSTRING "${IDF_VER_T}" 0 31 IDF_VER) - message(STATUS "IDF_VER: ${IDF_VER}") - add_definitions(-DIDF_VER=\"${IDF_VER}\") - git_submodule_check("${IDF_PATH}") - set(IDF_VER ${IDF_VER} PARENT_SCOPE) -endfunction() - -# app_get_revision -# -# Set global PROJECT_VER -# -# If PROJECT_VER variable set in project CMakeLists.txt file, its value will be used. -# Else, if the _project_path/version.txt exists, its contents will be used as PROJECT_VER. -# Else, if the project is located inside a Git repository, the output of git describe will be used. -# Otherwise, PROJECT_VER will be "1". -function(app_get_revision _project_path) - if(NOT DEFINED PROJECT_VER) - if(EXISTS "${_project_path}/version.txt") - file(STRINGS "${_project_path}/version.txt" PROJECT_VER) - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_project_path}/version.txt") - else() - git_describe(PROJECT_VER_GIT "${_project_path}") - if(PROJECT_VER_GIT) - set(PROJECT_VER ${PROJECT_VER_GIT}) - else() - message(STATUS "Project is not inside a git repository, \ - will not use 'git describe' to determine PROJECT_VER.") - set(PROJECT_VER "1") - endif() - endif() - endif() - message(STATUS "Project version: ${PROJECT_VER}") - set(PROJECT_VER ${PROJECT_VER} PARENT_SCOPE) -endfunction() - -# idf_link_components -# -# Link library components to the target -function(idf_link_components target components) - foreach(component ${components}) - component_get_target(component_target ${component}) - - # 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...) - if(TARGET ${component_target}) - get_target_property(type ${component_target} TYPE) - get_target_property(imported ${component_target} IMPORTED) - if(NOT imported) - if(${type} STREQUAL STATIC_LIBRARY OR ${type} STREQUAL EXECUTABLE) - get_target_property(link_depends "${component_target}" LINK_DEPENDS) - if(link_depends) - set_property(TARGET ${target} APPEND PROPERTY LINK_DEPENDS "${link_depends}") - endif() - endif() - endif() - - if(${type} MATCHES .+_LIBRARY) - list(APPEND libraries ${component_target}) - endif() - endif() - endforeach() - - if(libraries) - # gc-sections is necessary for linking some IDF binary libraries - # (and without it, IDF apps are much larger than they should be) - target_link_libraries(${target} "-Wl,--gc-sections") - target_link_libraries(${target} "-Wl,--start-group") - target_link_libraries(${target} ${libraries}) - message(STATUS "Component libraries: ${IDF_COMPONENT_LIBRARIES}") - endif() -endfunction() - -# idf_import_components -# -# Adds ESP-IDF as a subdirectory to the current project and imports the components -function(idf_import_components var idf_path build_path) - add_subdirectory(${idf_path} ${build_path}) - set(${var} ${BUILD_COMPONENTS} PARENT_SCOPE) -endfunction() diff --git a/tools/cmake/kconfig.cmake b/tools/cmake/kconfig.cmake index 3c8dfb2ccf..813363a931 100644 --- a/tools/cmake/kconfig.cmake +++ b/tools/cmake/kconfig.cmake @@ -1,147 +1,209 @@ include(ExternalProject) -macro(kconfig_set_variables) - set_default(IDF_SDKCONFIG_DEFAULTS "") +function(__kconfig_init) + idf_build_get_property(idf_path IDF_PATH) + if(CMAKE_HOST_WIN32) + # Prefer a prebuilt mconf-idf on Windows + if(DEFINED ENV{MSYSTEM}) + find_program(WINPTY winpty) + else() + unset(WINPTY CACHE) # in case previous CMake run was in a tty and this one is not + endif() + find_program(MCONF mconf-idf) - set_default(CONFIG_DIR ${IDF_BUILD_ARTIFACTS_DIR}/config) - set_default(SDKCONFIG ${IDF_PROJECT_PATH}/sdkconfig) - set(SDKCONFIG_HEADER ${CONFIG_DIR}/sdkconfig.h) - set(SDKCONFIG_CMAKE ${CONFIG_DIR}/sdkconfig.cmake) - set(SDKCONFIG_JSON ${CONFIG_DIR}/sdkconfig.json) - set(KCONFIG_JSON_MENUS ${CONFIG_DIR}/kconfig_menus.json) + # Fall back to the old binary which was called 'mconf' not 'mconf-idf' + if(NOT MCONF) + find_program(MCONF mconf) + if(MCONF) + message(WARNING "Falling back to mconf binary '${MCONF}' not mconf-idf. " + "This is probably because an old version of IDF mconf is installed and this is fine. " + "However if there are config problems please check the Getting Started guide for your platform.") + endif() + endif() - set(ROOT_KCONFIG ${IDF_PATH}/Kconfig) -endmacro() - -if(CMAKE_HOST_WIN32) - # Prefer a prebuilt mconf-idf on Windows - if(DEFINED ENV{MSYSTEM}) - find_program(WINPTY winpty) - else() - unset(WINPTY CACHE) # in case previous CMake run was in a tty and this one is not - endif() - find_program(MCONF mconf-idf) - - # Fall back to the old binary which was called 'mconf' not 'mconf-idf' - if(NOT MCONF) - find_program(MCONF mconf) - if(MCONF) - message(WARNING "Falling back to mconf binary '${MCONF}' not mconf-idf. " - "This is probably because an old version of IDF mconf is installed and this is fine. " - "However if there are config problems please check the Getting Started guide for your platform.") + if(NOT MCONF) + find_program(NATIVE_GCC gcc) + if(NOT NATIVE_GCC) + message(FATAL_ERROR + "Windows requires a prebuilt mconf-idf for your platform " + "on the PATH, or an MSYS2 version of gcc on the PATH to build mconf-idf. " + "Consult the setup docs for ESP-IDF on Windows.") + endif() + elseif(WINPTY) + set(MCONF "${WINPTY}" "${MCONF}") endif() endif() if(NOT MCONF) - find_program(NATIVE_GCC gcc) - if(NOT NATIVE_GCC) - message(FATAL_ERROR - "Windows requires a prebuilt mconf-idf for your platform " - "on the PATH, or an MSYS2 version of gcc on the PATH to build mconf-idf. " - "Consult the setup docs for ESP-IDF on Windows.") - endif() - elseif(WINPTY) - set(MCONF "${WINPTY}" "${MCONF}") + # Use the existing Makefile to build mconf (out of tree) when needed + # + set(MCONF ${CMAKE_BINARY_DIR}/kconfig_bin/mconf-idf) + + externalproject_add(mconf-idf + SOURCE_DIR ${idf_path}/tools/kconfig + CONFIGURE_COMMAND "" + BINARY_DIR "kconfig_bin" + BUILD_COMMAND make -f ${idf_path}/tools/kconfig/Makefile mconf-idf + BUILD_BYPRODUCTS ${MCONF} + INSTALL_COMMAND "" + EXCLUDE_FROM_ALL 1 + ) + + file(GLOB mconf_srcfiles ${idf_path}/tools/kconfig/*.c) + externalproject_add_stepdependencies(mconf-idf build + ${mconf_srcfiles} + ${idf_path}/tools/kconfig/Makefile + ${CMAKE_CURRENT_LIST_FILE}) + unset(mconf_srcfiles) + + set(menuconfig_depends DEPENDS mconf-idf) endif() -endif() -if(NOT MCONF) - # Use the existing Makefile to build mconf (out of tree) when needed - # - set(MCONF ${CMAKE_BINARY_DIR}/kconfig_bin/mconf-idf) + idf_build_set_property(__MCONF ${MCONF}) + idf_build_set_property(__MENUCONFIG_DEPENDS "${menuconfig_depends}") - externalproject_add(mconf-idf - SOURCE_DIR ${IDF_PATH}/tools/kconfig - CONFIGURE_COMMAND "" - BINARY_DIR "kconfig_bin" - BUILD_COMMAND make -f ${IDF_PATH}/tools/kconfig/Makefile mconf-idf - BUILD_BYPRODUCTS ${MCONF} - INSTALL_COMMAND "" - EXCLUDE_FROM_ALL 1 - ) + idf_build_get_property(idf_path IDF_PATH) + idf_build_set_property(__ROOT_KCONFIG ${idf_path}/Kconfig) + idf_build_set_property(__OUTPUT_SDKCONFIG 1) +endfunction() - file(GLOB mconf_srcfiles ${IDF_PATH}/tools/kconfig/*.c) - externalproject_add_stepdependencies(mconf-idf build - ${mconf_srcfiles} - ${IDF_PATH}/tools/kconfig/Makefile - ${CMAKE_CURRENT_LIST_FILE}) - unset(mconf_srcfiles) +# +# Initialize Kconfig-related properties for components. +# This function assumes that all basic properties of the components have been +# set prior to calling it. +# +function(__kconfig_component_init component_target) + __component_get_property(component_dir ${component_target} COMPONENT_DIR) + file(GLOB kconfig "${component_dir}/Kconfig") + __component_set_property(${component_target} KCONFIG "${kconfig}") + file(GLOB kconfig "${component_dir}/Kconfig.projbuild") + __component_set_property(${component_target} KCONFIG_PROJBUILD "${kconfig}") +endfunction() - set(menuconfig_depends DEPENDS mconf-idf) - -endif() - -# Find all Kconfig files for all components -function(kconfig_process_config) - file(MAKE_DIRECTORY "${CONFIG_DIR}") - set(kconfigs) - set(kconfigs_projbuild) - - # Components are usually sorted (somewhat) topologically via their dependencies. This extends to the component - # paths list. Obtain an alphabetical list in order to present menus also in the same order. - set(components ${BUILD_COMPONENTS}) - list(SORT components) - - foreach(component ${components}) - list(FIND BUILD_COMPONENTS ${component} idx) - list(GET BUILD_COMPONENT_PATHS ${idx} component_path) - list(APPEND component_paths ${component_path}) - endforeach() - - # Find Kconfig and Kconfig.projbuild for each component as applicable - # if any of these change, cmake should rerun - foreach(dir ${component_paths}) - file(GLOB kconfig "${dir}/Kconfig") +# +# Generate the config files and create config related targets and configure +# dependencies. +# +function(__kconfig_generate_config sdkconfig sdkconfig_defaults) + # List all Kconfig and Kconfig.projbuild in known components + idf_build_get_property(component_targets __COMPONENT_TARGETS) + foreach(component_target ${component_targets}) + __component_get_property(kconfig ${component_target} KCONFIG) + __component_get_property(kconfig_projbuild ${component_target} KCONFIG_PROJBUILD) if(kconfig) - set(kconfigs "${kconfigs} ${kconfig}") - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${kconfig}) + list(APPEND kconfigs ${kconfig}) endif() - file(GLOB kconfig ${dir}/Kconfig.projbuild) - if(kconfig) - set(kconfigs_projbuild "${kconfigs_projbuild} ${kconfig}") - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${kconfig}) + if(kconfig_projbuild) + list(APPEND kconfig_projbuilds ${kconfig_projbuild}) endif() endforeach() - if(IDF_SDKCONFIG_DEFAULTS) - set(defaults_arg --defaults "${IDF_SDKCONFIG_DEFAULTS}") + # Store the list version of kconfigs and kconfig_projbuilds + idf_build_set_property(KCONFIGS "${kconfigs}") + idf_build_set_property(KCONFIG_PROJBUILDS "${kconfig_projbuilds}") + + idf_build_get_property(idf_target IDF_TARGET) + + if(sdkconfig_defaults) + set(defaults_arg --defaults "${sdkconfig_defaults}") endif() - if(EXISTS "${IDF_SDKCONFIG_DEFAULTS}.${IDF_TARGET}") - list(APPEND defaults_arg --defaults "${IDF_SDKCONFIG_DEFAULTS}.${IDF_TARGET}") + if(EXISTS "${sdkconfig_defaults}.${idf_target}") + list(APPEND defaults_arg --defaults "${sdkconfig_defaults}.${idf_target}") endif() - # Set these in the parent scope, so that they can be written to project_description.json - set(kconfigs "${kconfigs}") - set(COMPONENT_KCONFIGS "${kconfigs}" PARENT_SCOPE) - set(COMPONENT_KCONFIGS_PROJBUILD "${kconfigs_projbuild}" PARENT_SCOPE) + idf_build_get_property(idf_path IDF_PATH) + idf_build_get_property(root_kconfig __ROOT_KCONFIG) + idf_build_get_property(python PYTHON) + + string(REPLACE ";" " " kconfigs "${kconfigs}") + string(REPLACE ";" " " kconfig_projbuilds "${kconfig_projbuilds}") set(confgen_basecommand - ${PYTHON} ${IDF_PATH}/tools/kconfig_new/confgen.py - --kconfig ${ROOT_KCONFIG} - --config ${SDKCONFIG} + ${python} ${idf_path}/tools/kconfig_new/confgen.py + --kconfig ${root_kconfig} + --config ${sdkconfig} ${defaults_arg} --env "COMPONENT_KCONFIGS=${kconfigs}" - --env "COMPONENT_KCONFIGS_PROJBUILD=${kconfigs_projbuild}" - --env "IDF_CMAKE=y") + --env "COMPONENT_KCONFIGS_PROJBUILD=${kconfig_projbuilds}" + --env "IDF_CMAKE=y" + --env "IDF_TARGET=${idf_target}") + + idf_build_get_property(build_dir BUILD_DIR) + set(config_dir ${build_dir}/config) + file(MAKE_DIRECTORY "${config_dir}") + + # Generate the config outputs + set(sdkconfig_cmake ${config_dir}/sdkconfig.cmake) + set(sdkconfig_header ${config_dir}/sdkconfig.h) + set(sdkconfig_json ${config_dir}/sdkconfig.json) + set(sdkconfig_json_menus ${config_dir}/${kconfig_menus}.json) + + idf_build_get_property(output_sdkconfig __OUTPUT_SDKCONFIG) + if(output_sdkconfig) + execute_process( + COMMAND ${confgen_basecommand} + --output header ${sdkconfig_header} + --output cmake ${sdkconfig_cmake} + --output json ${sdkconfig_json} + --output json_menus ${sdkconfig_json_menus} + --output config ${sdkconfig} + RESULT_VARIABLE config_result) + else() + execute_process( + COMMAND ${confgen_basecommand} + --output header ${sdkconfig_header} + --output cmake ${sdkconfig_cmake} + --output json ${sdkconfig_json} + --output json_menus ${sdkconfig_json_menus} + RESULT_VARIABLE config_result) + endif() + + if(config_result) + message(FATAL_ERROR "Failed to run confgen.py (${confgen_basecommand}). Error ${config_result}") + endif() + + # Add the generated config header to build specifications. + idf_build_set_property(INCLUDE_DIRECTORIES ${config_dir} APPEND) + + # When sdkconfig file changes in the future, trigger a cmake run + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${sdkconfig}") + + # Ditto if either of the generated files are missing/modified (this is a bit irritating as it means + # you can't edit these manually without them being regenerated, but I don't know of a better way...) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${sdkconfig_header}") + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${sdkconfig_cmake}") + + # Or if the config generation tool changes + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${idf_path}/tools/kconfig_new/confgen.py") + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${idf_path}/tools/kconfig_new/kconfiglib.py") + + set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY + ADDITIONAL_MAKE_CLEAN_FILES "${sdkconfig_header}" "${sdkconfig_cmake}") + + idf_build_set_property(SDKCONFIG_HEADER ${sdkconfig_header}) + idf_build_set_property(SDKCONFIG_JSON ${sdkconfig_json}) + idf_build_set_property(SDKCONFIG_CMAKE ${sdkconfig_cmake}) + idf_build_set_property(SDKCONFIG_JSON_MENUS ${sdkconfig_json_menus}) + + idf_build_get_property(menuconfig_depends __MENUCONFIG_DEPENDS) # Generate the menuconfig target (uses C-based mconf-idf tool, either prebuilt or via mconf-idf target above) add_custom_target(menuconfig ${menuconfig_depends} # create any missing config file, with defaults if necessary - COMMAND ${confgen_basecommand} --env "IDF_TARGET=${IDF_TARGET}" --output config ${SDKCONFIG} + COMMAND ${confgen_basecommand} --env "IDF_TARGET=${idf_target}" --output config ${sdkconfig} COMMAND ${CMAKE_COMMAND} -E env "COMPONENT_KCONFIGS=${kconfigs}" - "COMPONENT_KCONFIGS_PROJBUILD=${kconfigs_projbuild}" + "COMPONENT_KCONFIGS_PROJBUILD=${kconfig_projbuilds}" "IDF_CMAKE=y" - "KCONFIG_CONFIG=${SDKCONFIG}" - "IDF_TARGET=${IDF_TARGET}" - ${MCONF} ${ROOT_KCONFIG} + "KCONFIG_CONFIG=${sdkconfig}" + ${MCONF} ${root_kconfig} VERBATIM USES_TERMINAL # additional run of confgen esures that the deprecated options will be inserted into sdkconfig (for backward # compatibility) - COMMAND ${confgen_basecommand} --env "IDF_TARGET=${IDF_TARGET}" --output config ${SDKCONFIG} + COMMAND ${confgen_basecommand} --env "IDF_TARGET=${idf_target}" --output config ${sdkconfig} ) # Custom target to run confserver.py from the build tool @@ -152,47 +214,4 @@ function(kconfig_process_config) ${PYTHON} ${IDF_PATH}/tools/kconfig_new/confserver.py --kconfig ${IDF_PATH}/Kconfig --config ${SDKCONFIG} VERBATIM USES_TERMINAL) - - # Generate configuration output via confgen.py - # makes sdkconfig.h and skdconfig.cmake - # - # This happens during the cmake run not during the build - if(NOT BOOTLOADER_BUILD) - execute_process( - COMMAND ${confgen_basecommand} - --output header ${SDKCONFIG_HEADER} - --output cmake ${SDKCONFIG_CMAKE} - --output json ${SDKCONFIG_JSON} - --output json_menus ${KCONFIG_JSON_MENUS} - --output config ${SDKCONFIG} # only generate config at the top-level project - RESULT_VARIABLE config_result) - else() - execute_process( - COMMAND ${confgen_basecommand} - --output header ${SDKCONFIG_HEADER} - --output cmake ${SDKCONFIG_CMAKE} - --output json ${SDKCONFIG_JSON} - --output json_menus ${KCONFIG_JSON_MENUS} - RESULT_VARIABLE config_result) - endif() - if(config_result) - message(FATAL_ERROR "Failed to run confgen.py (${confgen_basecommand}). Error ${config_result}") - endif() - - # When sdkconfig file changes in the future, trigger a cmake run - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${SDKCONFIG}") - - # Ditto if either of the generated files are missing/modified (this is a bit irritating as it means - # you can't edit these manually without them being regenerated, but I don't know of a better way...) - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${SDKCONFIG_HEADER}") - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${SDKCONFIG_CMAKE}") - - # Or if the config generation tool changes - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${IDF_PATH}/tools/kconfig_new/confgen.py") - set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${IDF_PATH}/tools/kconfig_new/kconfiglib.py") - - set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY - ADDITIONAL_MAKE_CLEAN_FILES - "${SDKCONFIG_HEADER}" "${SDKCONFIG_CMAKE}") - endfunction() diff --git a/tools/cmake/ldgen.cmake b/tools/cmake/ldgen.cmake index 9688b601ad..9161ecd8f1 100644 --- a/tools/cmake/ldgen.cmake +++ b/tools/cmake/ldgen.cmake @@ -1,81 +1,84 @@ # Utilities for supporting linker script generation in the build system -# ldgen_set_variables -# -# Create the custom target to attach the fragment files and template files -# for the build to. -function(ldgen_set_variables) - add_custom_target(ldgen) -endfunction() - -# ldgen_add_fragment_files +# __ldgen_add_fragment_files # # Add one or more linker fragment files, and append it to the list of fragment # files found so far. -function(ldgen_add_fragment_files component fragment_files) +function(__ldgen_add_fragment_files fragment_files) spaces2list(fragment_files) foreach(fragment_file ${fragment_files}) - get_filename_component(_fragment_file ${fragment_file} ABSOLUTE) - list(APPEND _fragment_files ${_fragment_file}) + get_filename_component(abs_path ${fragment_file} ABSOLUTE) + list(APPEND _fragment_files ${abs_path}) endforeach() - set_property(TARGET ldgen APPEND PROPERTY FRAGMENT_FILES ${_fragment_files}) + idf_build_set_property(__LDGEN_FRAGMENT_FILES "${_fragment_files}" APPEND) endfunction() -# ldgen_component_add +# __ldgen_add_component # -# Add component to known libraries for linker script generation -function(ldgen_component_add component_lib) - set_property(TARGET ldgen APPEND PROPERTY OUTPUT_LIBRARIES "$") - set_property(TARGET ldgen APPEND PROPERTY LIBRARIES ${component_lib}) +# Generate sections info for specified target to be used in linker script generation +function(__ldgen_add_component component_lib) + idf_build_set_property(__LDGEN_LIBRARIES "$" APPEND) + idf_build_set_property(__LDGEN_DEPENDS ${component_lib} APPEND) endfunction() -# ldgen_process_template +# __ldgen_process_template # # Passes a linker script template to the linker script generation tool for # processing -function(ldgen_process_template template output) - get_property(output_libraries TARGET ldgen PROPERTY OUTPUT_LIBRARIES) - file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/ldgen_libraries.in CONTENT "$") - file(GENERATE OUTPUT ${CMAKE_BINARY_DIR}/ldgen_libraries INPUT ${CMAKE_BINARY_DIR}/ldgen_libraries.in) +function(__ldgen_process_template output_var template) + idf_build_get_property(idf_target IDF_TARGET) + idf_build_get_property(idf_path IDF_PATH) - get_filename_component(filename "${template}" NAME_WE) + idf_build_get_property(build_dir BUILD_DIR) + idf_build_get_property(ldgen_libraries __LDGEN_LIBRARIES GENERATOR_EXPRESSION) + file(GENERATE OUTPUT ${build_dir}/ldgen_libraries.in CONTENT $) + file(GENERATE OUTPUT ${build_dir}/ldgen_libraries INPUT ${build_dir}/ldgen_libraries.in) + + get_filename_component(filename "${template}" NAME) + + set(output ${CMAKE_CURRENT_BINARY_DIR}/${filename}.ld) + set(${output_var} ${output} PARENT_SCOPE) set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES - "${CMAKE_BINARY_DIR}/ldgen_libraries.in" - "${CMAKE_BINARY_DIR}/ldgen_libraries") + "${build_dir}/ldgen_libraries.in" + "${build_dir}/ldgen_libraries") + + idf_build_get_property(ldgen_fragment_files __LDGEN_FRAGMENT_FILES GENERATOR_EXPRESSION) + idf_build_get_property(ldgen_depends __LDGEN_DEPENDS GENERATOR_EXPRESSION) + # Create command to invoke the linker script generator tool. + idf_build_get_property(sdkconfig SDKCONFIG) + idf_build_get_property(root_kconfig __ROOT_KCONFIG) + idf_build_get_property(kconfigs KCONFIGS) + idf_build_get_property(kconfig_projbuilds KCONFIG_PROJBUILDS) + + idf_build_get_property(python PYTHON) + + string(REPLACE ";" " " kconfigs "${kconfigs}") + string(REPLACE ";" " " kconfig_projbuilds "${kconfig_projbuilds}") add_custom_command( OUTPUT ${output} - COMMAND ${PYTHON} ${IDF_PATH}/tools/ldgen/ldgen.py - --config ${SDKCONFIG} - --fragments "$,\t>" + COMMAND ${python} ${idf_path}/tools/ldgen/ldgen.py + --config ${sdkconfig} + --fragments "$" --input ${template} --output ${output} - --kconfig ${ROOT_KCONFIG} - --env "COMPONENT_KCONFIGS=${COMPONENT_KCONFIGS}" - --env "COMPONENT_KCONFIGS_PROJBUILD=${COMPONENT_KCONFIGS_PROJBUILD}" + --kconfig ${root_kconfig} + --env "COMPONENT_KCONFIGS=${kconfigs}" + --env "COMPONENT_KCONFIGS_PROJBUILD=${kconfig_projbuilds}" --env "IDF_CMAKE=y" - --env "IDF_PATH=${IDF_PATH}" - --env "IDF_TARGET=${IDF_TARGET}" - --libraries-file ${CMAKE_BINARY_DIR}/ldgen_libraries + --env "IDF_PATH=${idf_path}" + --env "IDF_TARGET=${idf_target}" + --libraries-file ${build_dir}/ldgen_libraries --objdump ${CMAKE_OBJDUMP} - DEPENDS ${template} $ - $ ${SDKCONFIG} + DEPENDS ${template} ${ldgen_fragment_files} ${ldgen_depends} ${SDKCONFIG} ) - get_filename_component(output_name ${output} NAME) - add_custom_target(ldgen_${output_name}_script DEPENDS ${output}) - add_dependencies(ldgen ldgen_${output_name}_script) -endfunction() - -# ldgen_add_dependencies -# -# Add dependency of project executable to ldgen custom target. -function(ldgen_add_dependencies) - if(IDF_PROJECT_EXECUTABLE) - add_dependencies(${IDF_PROJECT_EXECUTABLE} ldgen) - endif() + get_filename_component(_name ${output} NAME) + add_custom_target(__ldgen_output_${_name} DEPENDS ${output}) + add_dependencies(__idf_build_target __ldgen_output_${_name}) + idf_build_set_property(LINK_DEPENDS ${output} APPEND) endfunction() \ No newline at end of file diff --git a/tools/cmake/project.cmake b/tools/cmake/project.cmake index 1bf088c3f8..437e715a48 100644 --- a/tools/cmake/project.cmake +++ b/tools/cmake/project.cmake @@ -1,11 +1,226 @@ # Designed to be included from an IDF app's CMakeLists.txt file -# cmake_minimum_required(VERSION 3.5) -include(${CMAKE_CURRENT_LIST_DIR}/idf_functions.cmake) +# The mere inclusion of this CMake file sets up some interal build properties. +# These properties can be modified in between this inclusion the the idf_build_process +# call. +include(${CMAKE_CURRENT_LIST_DIR}/idf.cmake) -# Set the path of idf.py. set(IDFTOOL ${PYTHON} "${IDF_PATH}/tools/idf.py") +# Internally, the Python interpreter is already set to 'python'. Re-set here +# to be absolutely sure. +set_default(PYTHON "python") +idf_build_set_property(PYTHON ${PYTHON}) + +# On processing, checking Python required modules can be turned off if it was +# already checked externally. +if(PYTHON_DEPS_CHECKED) + idf_build_set_property(__CHECK_PYTHON 0) +endif() + +# Initialize build target for this build using the environment variable or +# value passed externally. +__target_init() + +# +# Get the project version from either a version file or the Git revision. This is passed +# to the idf_build_process call. Dependencies are also set here for when the version file +# changes (if it is used). +# +function(__project_get_revision var) + set(_project_path "${CMAKE_CURRENT_LIST_DIR}") + if(NOT DEFINED PROJECT_VER) + if(EXISTS "${_project_path}/version.txt") + file(STRINGS "${_project_path}/version.txt" PROJECT_VER) + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${_project_path}/version.txt") + else() + git_describe(PROJECT_VER_GIT "${_project_path}") + if(PROJECT_VER_GIT) + set(PROJECT_VER ${PROJECT_VER_GIT}) + else() + message(STATUS "Project is not inside a git repository, \ + will not use 'git describe' to determine PROJECT_VER.") + set(PROJECT_VER "1") + endif() + endif() + endif() + message(STATUS "Project version: ${PROJECT_VER}") + set(${var} "${PROJECT_VER}" PARENT_SCOPE) +endfunction() + +# +# Output the built components to the user. Generates files for invoking idf_monitor.py +# that doubles as an overview of some of the more important build properties. +# +function(__project_info test_components) + idf_build_get_property(prefix __PREFIX) + idf_build_get_property(_build_components BUILD_COMPONENTS) + idf_build_get_property(build_dir BUILD_DIR) + idf_build_get_property(idf_path IDF_PATH) + + list(SORT _build_components) + + unset(build_components) + unset(build_component_paths) + + foreach(build_component ${_build_components}) + __component_get_target(component_target "${build_component}") + __component_get_property(_name ${component_target} COMPONENT_NAME) + __component_get_property(_prefix ${component_target} __PREFIX) + __component_get_property(_alias ${component_target} COMPONENT_ALIAS) + __component_get_property(_dir ${component_target} COMPONENT_DIR) + + if(_alias IN_LIST test_components) + list(APPEND test_component_paths ${_dir}) + else() + if(_prefix STREQUAL prefix) + set(component ${_name}) + else() + set(component ${_alias}) + endif() + list(APPEND build_components ${component}) + list(APPEND build_component_paths ${_dir}) + endif() + endforeach() + + set(PROJECT_NAME ${CMAKE_PROJECT_NAME}) + idf_build_get_property(PROJECT_PATH PROJECT_DIR) + idf_build_get_property(BUILD_DIR BUILD_DIR) + idf_build_get_property(SDKCONFIG SDKCONFIG) + idf_build_get_property(SDKCONFIG_DEFAULTS SDKCONFIG_DEFAULTS) + idf_build_get_property(PROJECT_EXECUTABLE EXECUTABLE) + set(PROJECT_BIN ${CMAKE_PROJECT_NAME}.bin) + idf_build_get_property(IDF_VER IDF_VER) + + idf_build_get_property(sdkconfig_cmake SDKCONFIG_CMAKE) + include(${sdkconfig_cmake}) + idf_build_get_property(COMPONENT_KCONFIGS KCONFIGS) + idf_build_get_property(COMPONENT_KCONFIGS_PROJBUILD KCONFIG_PROJBUILDS) + + # Write project description JSON file + idf_build_get_property(build_dir BUILD_DIR) + make_json_list("${build_components};${test_components}" build_components_json) + make_json_list("${build_component_paths};${test_component_paths}" build_component_paths_json) + configure_file("${idf_path}/tools/cmake/project_description.json.in" + "${build_dir}/project_description.json") + + # We now have the following component-related variables: + # + # build_components is the list of components to include in the build. + # build_component_paths is the paths to all of these components, obtained from the component dependencies file. + # + # Print the list of found components and test components + string(REPLACE ";" " " build_components "${build_components}") + string(REPLACE ";" " " build_component_paths "${build_component_paths}") + message(STATUS "Components: ${build_components}") + message(STATUS "Component paths: ${build_component_paths}") + + if(test_components) + string(REPLACE ";" " " test_components "${test_components}") + string(REPLACE ";" " " test_component_paths "${test_component_paths}") + message(STATUS "Test components: ${test_components}") + message(STATUS "Test component paths: ${test_component_paths}") + endif() +endfunction() + +function(__project_init components_var test_components_var) + # Use EXTRA_CFLAGS, EXTRA_CXXFLAGS and EXTRA_CPPFLAGS to add more priority options to the compiler + # EXTRA_CPPFLAGS is used for both C and C++ + # Unlike environments' CFLAGS/CXXFLAGS/CPPFLAGS which work for both host and target build, + # these works only for target build + set(extra_cflags "$ENV{EXTRA_CFLAGS}") + set(extra_cxxflags "$ENV{EXTRA_CXXFLAGS}") + set(extra_cppflags "$ENV{EXTRA_CPPFLAGS}") + + spaces2list(extra_cflags) + spaces2list(extra_cxxflags) + spaces2list(extra_cppflags) + + idf_build_set_property(C_COMPILE_OPTIONS "${extra_cflags}" APPEND) + idf_build_set_property(CXX_COMPILE_OPTIONS "${extra_cxxflags}" APPEND) + idf_build_set_property(COMPILE_OPTIONS "${extra_cppflags}" APPEND) + + function(__project_component_dir component_dir) + get_filename_component(component_dir "${component_dir}" ABSOLUTE) + if(EXISTS ${component_dir}/CMakeLists.txt) + idf_build_component(${component_dir}) + else() + file(GLOB component_dirs ${component_dir}/*) + foreach(component_dir ${component_dirs}) + if(EXISTS ${component_dir}/CMakeLists.txt) + idf_build_component(${component_dir}) + endif() + endforeach() + endif() + endfunction() + + # Add component directories to the build, given the component filters, exclusions + # extra directories, etc. passed from the root CMakeLists.txt. + if(COMPONENT_DIRS) + # User wants to fully override where components are pulled from. + spaces2list(COMPONENT_DIRS) + idf_build_set_property(__COMPONENT_TARGETS "") + foreach(component_dir ${COMPONENT_DIRS}) + __project_component_dir(${component_dir}) + endforeach() + else() + # Look for components in the usual places: CMAKE_CURRENT_LIST_DIR/main, + # CMAKE_CURRENT_LIST_DIR/components, and the extra component dirs + if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/main") + __project_component_dir("${CMAKE_CURRENT_LIST_DIR}/main") + endif() + + __project_component_dir("${CMAKE_CURRENT_LIST_DIR}/components") + + spaces2list(EXTRA_COMPONENT_DIRS) + foreach(component_dir ${EXTRA_COMPONENT_DIRS}) + __project_component_dir("${component_dir}") + endforeach() + endif() + + spaces2list(COMPONENTS) + spaces2list(EXCLUDE_COMPONENTS) + idf_build_get_property(component_targets __COMPONENT_TARGETS) + foreach(component_target ${component_targets}) + __component_get_property(component_name ${component_target} COMPONENT_NAME) + set(include 1) + if(COMPONENTS AND NOT component_name IN_LIST COMPONENTS) + set(include 0) + endif() + if(EXCLUDE_COMPONENTS AND component_name IN_LIST EXCLUDE_COMPONENTS) + set(include 0) + endif() + if(include) + list(APPEND components ${component_name}) + endif() + endforeach() + + if(TESTS_ALL OR BUILD_TESTS OR TEST_COMPONENTS OR TEST_EXCLUDE_COMPONENTS) + spaces2list(TEST_COMPONENTS) + spaces2list(TEST_EXCLUDE_COMPONENTS) + idf_build_get_property(component_targets __COMPONENT_TARGETS) + foreach(component_target ${component_targets}) + __component_get_property(component_dir ${component_target} COMPONENT_DIR) + __component_get_property(component_name ${component_target} COMPONENT_NAME) + if(component_name IN_LIST components) + set(include 1) + if(TEST_COMPONENTS AND NOT component_name IN_LIST TEST_COMPONENTS) + set(include 0) + endif() + if(TEST_EXCLUDE_COMPONENTS AND component_name IN_LIST TEST_EXCLUDE_COMPONENTS) + set(include 0) + endif() + if(include AND EXISTS ${component_dir}/test) + __component_add(${component_dir}/test ${component_name}) + list(APPEND test_components ${component_name}::test) + endif() + endif() + endforeach() + endif() + + set(${components_var} "${components}" PARENT_SCOPE) + set(${test_components_var} "${test_components}" PARENT_SCOPE) +endfunction() # Trick to temporarily redefine project(). When functions are overriden in CMake, the originals can still be accessed # using an underscore prefixed function of the same name. The following lines make sure that __project calls @@ -16,72 +231,14 @@ endfunction() function(_project) endfunction() -macro(project name) +macro(project project_name) + # Initialize project, preparing COMPONENTS argument for idf_build_process() + # call later using external COMPONENT_DIRS, COMPONENTS_DIRS, EXTRA_COMPONENTS_DIR, + # EXTRA_COMPONENTS_DIRS, COMPONENTS, EXLUDE_COMPONENTS, TEST_COMPONENTS, + # TEST_EXLUDE_COMPONENTS, TESTS_ALL, BUILD_TESTS + __project_init(components test_components) - # Bridge existing documented variable names with library namespaced - # variables in order for old projects to work. - - if(COMPONENT_DIRS) - spaces2list(COMPONENT_DIRS) - - foreach(component_dir ${COMPONENT_DIRS}) - get_filename_component(component_dir ${component_dir} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR}) - list(APPEND IDF_COMPONENT_DIRS "${component_dir}") - endforeach() - endif() - - if(EXTRA_COMPONENT_DIRS) - spaces2list(EXTRA_COMPONENT_DIRS) - - foreach(component_dir ${EXTRA_COMPONENT_DIRS}) - get_filename_component(component_dir ${component_dir} ABSOLUTE BASE_DIR ${CMAKE_SOURCE_DIR}) - list(APPEND IDF_EXTRA_COMPONENT_DIRS "${component_dir}") - endforeach() - endif() - - list(APPEND IDF_EXTRA_COMPONENT_DIRS "${CMAKE_SOURCE_DIR}/components") - - if(NOT MAIN_SRCS) - list(APPEND IDF_EXTRA_COMPONENT_DIRS "${CMAKE_SOURCE_DIR}/main") - endif() - - if(COMPONENTS) - set(IDF_COMPONENTS "${COMPONENTS}") - endif() - - if(COMPONENT_REQUIRES_COMMON) - set(IDF_COMPONENT_REQUIRES_COMMON "${COMPONENT_REQUIRES_COMMON}") - endif() - - if(EXCLUDE_COMPONENTS) - set(IDF_EXCLUDE_COMPONENTS "${COMPONENT_EXCLUDES}") - endif() - - if(TESTS_ALL EQUAL 1 OR TEST_COMPONENTS) - set(IDF_BUILD_TESTS 1) - endif() - - if(TEST_COMPONENTS) - set(IDF_TEST_COMPONENTS "${TEST_COMPONENTS}") - endif() - - if(TEST_EXCLUDE_COMPONENTS) - set(IDF_TEST_EXCLUDE_COMPONENTS "${TEST_EXCLUDE_COMPONENTS}") - endif() - - if(NOT SDKCONFIG_DEFAULTS) - if(EXISTS ${CMAKE_SOURCE_DIR}/sdkconfig.defaults) - set(IDF_SDKCONFIG_DEFAULTS ${CMAKE_SOURCE_DIR}/sdkconfig.defaults) - endif() - else() - set(IDF_SDKCONFIG_DEFAULTS ${SDKCONFIG_DEFAULTS}) - endif() - - # Set build variables - idf_set_variables() - - # Now the configuration is loaded, set the toolchain appropriately - idf_set_toolchain() + __target_set_toolchain() # Enable ccache if it's on the path if(NOT CCACHE_DISABLE) @@ -92,50 +249,11 @@ macro(project name) endif() endif() - __project(${name} C CXX ASM) + # The actual call to project() + __project(${project_name} C CXX ASM) - set(IDF_BUILD_ARTIFACTS ON) - set(IDF_PROJECT_EXECUTABLE ${CMAKE_PROJECT_NAME}.elf) - set(IDF_BUILD_ARTIFACTS_DIR ${CMAKE_BINARY_DIR}) - - if(MAIN_SRCS) - spaces2list(MAIN_SRCS) - add_executable(${IDF_PROJECT_EXECUTABLE} ${MAIN_SRCS}) - else() - # Create a dummy file to work around CMake requirement of having a source - # file while adding an executable - add_executable(${IDF_PROJECT_EXECUTABLE} "${CMAKE_CURRENT_BINARY_DIR}/dummy_main_src.c") - add_custom_command(OUTPUT dummy_main_src.c - COMMAND ${CMAKE_COMMAND} -E touch dummy_main_src.c - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - VERBATIM) - - add_custom_target(dummy_main_src DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/dummy_main_src.c) - - add_dependencies(${IDF_PROJECT_EXECUTABLE} dummy_main_src) - endif() - - set(mapfile "${CMAKE_PROJECT_NAME}.map") - - target_link_libraries(${IDF_PROJECT_EXECUTABLE} "-Wl,--cref -Wl,--Map=${mapfile}") - - set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY - ADDITIONAL_MAKE_CLEAN_FILES - "${CMAKE_CURRENT_BINARY_DIR}/${mapfile}") - - # Add size targets, depend on map file, run idf_size.py - add_custom_target(size - DEPENDS ${exe_target} - COMMAND ${PYTHON} ${IDF_PATH}/tools/idf_size.py ${mapfile} - ) - add_custom_target(size-files - DEPENDS ${exe_target} - COMMAND ${PYTHON} ${IDF_PATH}/tools/idf_size.py --files ${mapfile} - ) - add_custom_target(size-components - DEPENDS ${exe_target} - COMMAND ${PYTHON} ${IDF_PATH}/tools/idf_size.py --archives ${mapfile} - ) + # Generate compile_commands.json (needs to come after project call). + set(CMAKE_EXPORT_COMPILE_COMMANDS 1) # Since components can import third-party libraries, the original definition of project() should be restored # before the call to add components to the build. @@ -144,7 +262,140 @@ macro(project name) __project(${${project_ARGV}}) endfunction() - # Finally, add the rest of the components to the build. - idf_import_components(components $ENV{IDF_PATH} esp-idf) - idf_link_components(${IDF_PROJECT_EXECUTABLE} "${components}") + # Prepare the following arguments for the idf_build_process() call using external + # user values: + # + # SDKCONFIG_DEFAULTS is from external SDKCONFIG_DEFAULTS + # SDKCONFIG is from external SDKCONFIG + # BUILD_DIR is set to project binary dir + # + # PROJECT_NAME is taken from the passed name from project() call + # PROJECT_DIR is set to the current directory + # PROJECT_VER is from the version text or git revision of the current repo + if(SDKCONFIG_DEFAULTS) + get_filename_component(sdkconfig_defaults "${SDKCONFIG_DEFAULTS}" ABSOLUTE) + if(NOT EXISTS "${sdkconfig_defaults}") + message(FATAL_ERROR "SDKCONFIG_DEFAULTS '${sdkconfig_defaults}' does not exist.") + endif() + else() + if(EXISTS "${CMAKE_SOURCE_DIR}/sdkconfig.defaults") + set(sdkconfig_defaults "${CMAKE_SOURCE_DIR}/sdkconfig.defaults") + else() + set(sdkconfig_defaults "") + endif() + endif() + + if(SDKCONFIG) + get_filename_component(sdkconfig "${SDKCONFIG}" ABSOLUTE) + if(NOT EXISTS "${sdkconfig}") + message(FATAL_ERROR "SDKCONFIG '${sdkconfig}' does not exist.") + endif() + set(sdkconfig ${SDKCONFIG}) + else() + set(sdkconfig "${CMAKE_CURRENT_LIST_DIR}/sdkconfig") + endif() + + if(BUILD_DIR) + get_filename_component(build_dir "${BUILD_DIR}" ABSOLUTE) + if(NOT EXISTS "${build_dir}") + message(FATAL_ERROR "BUILD_DIR '${build_dir}' does not exist.") + endif() + else() + set(build_dir ${CMAKE_BINARY_DIR}) + endif() + + __project_get_revision(project_ver) + + message(STATUS "Building ESP-IDF components for target ${IDF_TARGET}") + + idf_build_process(${IDF_TARGET} + SDKCONFIG_DEFAULTS "${sdkconfig_defaults}" + SDKCONFIG ${sdkconfig} + BUILD_DIR ${build_dir} + PROJECT_NAME ${CMAKE_PROJECT_NAME} + PROJECT_DIR ${CMAKE_CURRENT_LIST_DIR} + PROJECT_VER "${project_ver}" + COMPONENTS "${components};${test_components}") + + # Special treatment for 'main' component for standard projects (not part of core build system). + # Have it depend on every other component in the build. This is + # a convenience behavior for the standard project; thus is done outside of the core build system + # so that it treats components equally. + # + # This behavior should only be when user did not set REQUIRES/PRIV_REQUIRES manually. + idf_build_get_property(build_components BUILD_COMPONENTS) + if(idf::main IN_LIST build_components) + __component_get_target(main_target idf::main) + __component_get_property(reqs ${main_target} REQUIRES) + __component_get_property(priv_reqs ${main_target} PRIV_REQUIRES) + idf_build_get_property(common_reqs __COMPONENT_REQUIRES_COMMON) + if(reqs STREQUAL common_reqs AND NOT priv_reqs) #if user has not set any requirements + list(REMOVE_ITEM build_components idf::main) + __component_get_property(lib ${main_target} COMPONENT_LIB) + set_property(TARGET ${lib} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "${build_components}") + get_property(type TARGET ${lib} PROPERTY TYPE) + if(type STREQUAL STATIC_LIBRARY) + set_property(TARGET ${lib} APPEND PROPERTY LINK_LIBRARIES "${build_components}") + endif() + endif() + endif() + + set(project_elf ${CMAKE_PROJECT_NAME}.elf) + + # Create a dummy file to work around CMake requirement of having a source + # file while adding an executable + set(project_elf_src ${CMAKE_BINARY_DIR}/project_elf_src.c) + add_custom_command(OUTPUT ${project_elf_src} + COMMAND ${CMAKE_COMMAND} -E touch ${project_elf_src} + VERBATIM) + add_custom_target(_project_elf_src DEPENDS "${project_elf_src}") + add_executable(${project_elf} "${project_elf_src}") + add_dependencies(${project_elf} _project_elf_src) + + target_link_libraries(${project_elf} "-Wl,--gc-sections") + target_link_libraries(${project_elf} "-Wl,--start-group") + + if(test_components) + target_link_libraries(${project_elf} "-Wl,--whole-archive") + foreach(test_component ${test_components}) + if(TARGET ${test_component}) + target_link_libraries(${project_elf} ${test_component}) + endif() + endforeach() + target_link_libraries(${project_elf} "-Wl,--no-whole-archive") + endif() + + idf_build_get_property(build_components BUILD_COMPONENTS) + if(test_components) + list(REMOVE_ITEM build_components ${test_components}) + endif() + target_link_libraries(${project_elf} ${build_components}) + + set(mapfile "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.map") + target_link_libraries(${project_elf} "-Wl,--cref -Wl,--Map=${mapfile}") + + set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY + ADDITIONAL_MAKE_CLEAN_FILES + "${mapfile}" "${project_elf_src}") + + idf_build_get_property(idf_path IDF_PATH) + idf_build_get_property(python PYTHON) + + # Add size targets, depend on map file, run idf_size.py + add_custom_target(size + DEPENDS ${project_elf} + COMMAND ${python} ${idf_path}/tools/idf_size.py ${mapfile} + ) + add_custom_target(size-files + DEPENDS ${project_elf} + COMMAND ${python} ${idf_path}/tools/idf_size.py --files ${mapfile} + ) + add_custom_target(size-components + DEPENDS ${project_elf} + COMMAND ${python} ${idf_path}/tools/idf_size.py --archives ${mapfile} + ) + + idf_build_executable(${project_elf}) + + __project_info("${test_components}") endmacro() diff --git a/tools/cmake/project_description.json.in b/tools/cmake/project_description.json.in index 55c3fb97d0..3d02e1632b 100644 --- a/tools/cmake/project_description.json.in +++ b/tools/cmake/project_description.json.in @@ -1,11 +1,11 @@ { - "project_name": "${IDF_PROJECT_NAME}", - "project_path": "${IDF_PROJECT_PATH}", - "build_dir": "${IDF_BUILD_ARTIFACTS_DIR}", + "project_name": "${PROJECT_NAME}", + "project_path": "${PROJECT_PATH}", + "build_dir": "${BUILD_DIR}", "config_file": "${SDKCONFIG}", - "config_defaults": "${IDF_SDKCONFIG_DEFAULTS}", - "app_elf": "${IDF_PROJECT_EXECUTABLE}", - "app_bin": "${IDF_PROJECT_BIN}", + "config_defaults": "${SDKCONFIG_DEFAULTS}", + "app_elf": "${PROJECT_EXECUTABLE}", + "app_bin": "${PROJECT_BIN}", "git_revision": "${IDF_VER}", "phy_data_partition": "${CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION}", "monitor_baud" : "${CONFIG_MONITOR_BAUD}", diff --git a/tools/cmake/scripts/component_get_requirements.cmake b/tools/cmake/scripts/component_get_requirements.cmake new file mode 100644 index 0000000000..8223f709be --- /dev/null +++ b/tools/cmake/scripts/component_get_requirements.cmake @@ -0,0 +1,49 @@ +include(${IDF_PATH}/tools/cmake/utilities.cmake) + +include("${BUILD_PROPERTIES_FILE}") +include("${SDKCONFIG_CMAKE}") + +macro(require_idf_targets) +endmacro() + +function(idf_build_get_property var property) + cmake_parse_arguments(_ "GENERATOR_EXPRESSION" "" "" ${ARGN}) + if(__GENERATOR_EXPRESSION) + message(FATAL_ERROR "Getting build property generator expression not + supported before idf_component_register().") + endif() + set(${var} ${property} PARENT_SCOPE) +endfunction() + +function(print_requires requires priv_requires) + spaces2list(requires) + spaces2list(priv_requires) + string(REPLACE ";" ":" requires "${requires}") + string(REPLACE ";" ":" priv_requires "${priv_requires}") + message("${requires}:::${priv_requires}") +endfunction() + +macro(idf_component_register) + set(options) + set(single_value) + set(multi_value SRCS SRC_DIRS EXCLUDE_SRCS + INCLUDE_DIRS PRIV_INCLUDE_DIRS LDFRAGMENTS REQUIRES + PRIV_REQUIRES REQUIRED_IDF_TARGETS EMBED_FILES EMBED_TXTFILES) + cmake_parse_arguments(_ "${options}" "${single_value}" "${multi_value}" "${ARGN}") + print_requires("${__REQUIRES}" "${__PRIV_REQUIRES}") + set(__is_component 1) + return() +endmacro() + +macro(register_component) + print_requires("${COMPONENT_REQUIRES}" "${COMPONENT_PRIV_REQUIRES}") + set(__is_component 1) + return() +endmacro() + +macro(register_config_only_component) + register_component() +endmacro() + +set(CMAKE_BUILD_EARLY_EXPANSION) +include(${COMPONENT_DIR}/CMakeLists.txt OPTIONAL) diff --git a/tools/cmake/scripts/expand_requirements.cmake b/tools/cmake/scripts/expand_requirements.cmake deleted file mode 100644 index 26234853dd..0000000000 --- a/tools/cmake/scripts/expand_requirements.cmake +++ /dev/null @@ -1,269 +0,0 @@ -# expand_requirements.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. -# - COMPONENT_REQUIRES_COMMON = Components to always include in the build, and treated as dependencies -# of all other components. -# - 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. -# -# BUILD_COMPONENTS & BUILD_COMPONENT_PATHS will be ordered in a best-effort way so that dependencies are listed first. -# (Note that IDF supports cyclic dependencies, and dependencies in a cycle have ordering guarantees.) -# -# Determinism: -# -# Given the the same list of names in COMPONENTS (regardless of order), and an identical value of -# COMPONENT_REQUIRES_COMMON, and all the same COMPONENT_REQUIRES & COMPONENT_PRIV_REQUIRES values in -# each component, then the output of BUILD_COMPONENTS should always be in the same -# order. -# -# BUILD_COMPONENT_PATHS will be in the same component order as BUILD_COMPONENTS, even if the -# actual component paths are different due to different paths. -# -# TODO: Error out if a component requirement is missing -cmake_minimum_required(VERSION 3.5) -include("${IDF_PATH}/tools/cmake/utilities.cmake") -include("${IDF_PATH}/tools/cmake/component_utils.cmake") - -set(ESP_PLATFORM 1) - -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) - -spaces2list(COMPONENT_REQUIRES_COMMON) - -# 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) - if(COMPONENT STREQUAL main AND NOT COMPONENT_REQUIRES) - set(main_component_requires ${COMPONENTS}) - list(REMOVE_ITEM main_component_requires "main") - - set_property(GLOBAL PROPERTY "${COMPONENT}_REQUIRES" "${main_component_requires}") - else() - spaces2list(COMPONENT_REQUIRES) - set_property(GLOBAL PROPERTY "${COMPONENT}_REQUIRES" "${COMPONENT_REQUIRES}") - endif() - - 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() - -function(require_idf_targets) - if(NOT ${IDF_TARGET} IN_LIST ARGN) - message(FATAL_ERROR "Component ${COMPONENT_NAME} only supports targets: ${ARGN}") - endif() -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(seen_components GLOBAL PROPERTY SEEN_COMPONENTS) - if(component IN_LIST seen_components) - return() # already added, or in process of adding, this component - endif() - set_property(GLOBAL APPEND PROPERTY SEEN_COMPONENTS ${component}) - - find_component_path("${component}" "${ALL_COMPONENTS}" "${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) - - get_property(requires GLOBAL PROPERTY "${component}_REQUIRES") - get_property(requires_priv GLOBAL PROPERTY "${component}_PRIV_REQUIRES") - - # Recurse dependencies first, so that they appear first in the list (when possible) - foreach(req ${COMPONENT_REQUIRES_COMMON} ${requires} ${requires_priv}) - expand_component_requirements(${req}) - endforeach() - - list(FIND TEST_COMPONENTS ${component} idx) - - if(NOT idx EQUAL -1) - list(GET TEST_COMPONENTS ${idx} test_component) - list(GET TEST_COMPONENT_PATHS ${idx} test_component_path) - set_property(GLOBAL APPEND PROPERTY BUILD_TEST_COMPONENTS ${test_component}) - set_property(GLOBAL APPEND PROPERTY BUILD_TEST_COMPONENT_PATHS ${test_component_path}) - endif() - - # Now append this component to the full list (after its dependencies) - set_property(GLOBAL APPEND PROPERTY BUILD_COMPONENT_PATHS ${COMPONENT_PATH}) - set_property(GLOBAL APPEND PROPERTY BUILD_COMPONENTS ${component}) -endfunction() - -# filter_components_list: Filter the components included in the build -# as specified by the user. Or, in the case of unit testing, filter out -# the test components to be built. -macro(filter_components_list) - spaces2list(COMPONENTS) - spaces2list(EXCLUDE_COMPONENTS) - spaces2list(TEST_COMPONENTS) - spaces2list(TEST_EXCLUDE_COMPONENTS) - - list(LENGTH ALL_COMPONENTS all_components_length) - math(EXPR all_components_length "${all_components_length} - 1") - - foreach(component_idx RANGE 0 ${all_components_length}) - list(GET ALL_COMPONENTS ${component_idx} component) - list(GET ALL_COMPONENT_PATHS ${component_idx} component_path) - - if(COMPONENTS) - if(${component} IN_LIST COMPONENTS) - set(add_component 1) - else() - set(add_component 0) - endif() - else() - set(add_component 1) - endif() - - if(NOT ${component} IN_LIST EXCLUDE_COMPONENTS AND add_component EQUAL 1) - list(APPEND components ${component}) - list(APPEND component_paths ${component_path}) - - if(BUILD_TESTS EQUAL 1) - - if(TEST_COMPONENTS) - if(${component} IN_LIST TEST_COMPONENTS) - set(add_test_component 1) - else() - set(add_test_component 0) - endif() - else() - set(add_test_component 1) - endif() - - if(${component} IN_LIST ALL_TEST_COMPONENTS) - if(NOT ${component} IN_LIST TEST_EXCLUDE_COMPONENTS AND add_test_component EQUAL 1) - list(APPEND test_components ${component}_test) - list(APPEND test_component_paths ${component_path}/test) - - list(APPEND components ${component}_test) - list(APPEND component_paths ${component_path}/test) - endif() - endif() - endif() - endif() - endforeach() - - set(COMPONENTS ${components}) - - set(TEST_COMPONENTS ${test_components}) - set(TEST_COMPONENT_PATHS ${test_component_paths}) - - list(APPEND ALL_COMPONENTS "${TEST_COMPONENTS}") - list(APPEND ALL_COMPONENT_PATHS "${TEST_COMPONENT_PATHS}") -endmacro() - -# 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 ALL_TEST_COMPONENTS) - -filter_components_list() - -debug("ALL_COMPONENT_PATHS ${ALL_COMPONENT_PATHS}") -debug("ALL_COMPONENTS ${ALL_COMPONENTS}") -debug("ALL_TEST_COMPONENTS ${ALL_TEST_COMPONENTS}") - -set_property(GLOBAL PROPERTY SEEN_COMPONENTS "") # anti-infinite-recursion -set_property(GLOBAL PROPERTY BUILD_COMPONENTS "") -set_property(GLOBAL PROPERTY BUILD_COMPONENT_PATHS "") -set_property(GLOBAL PROPERTY BUILD_TEST_COMPONENTS "") -set_property(GLOBAL PROPERTY BUILD_TEST_COMPONENT_PATHS "") -set_property(GLOBAL PROPERTY COMPONENTS_NOT_FOUND "") - -# Indicate that the component CMakeLists.txt is being included in the early expansion phase of the build, -# and might not want to execute particular operations. -set(CMAKE_BUILD_EARLY_EXPANSION 1) -foreach(component ${COMPONENTS}) - debug("Expanding initial component ${component}") - expand_component_requirements(${component}) -endforeach() -unset(CMAKE_BUILD_EARLY_EXPANSION) - -get_property(build_components GLOBAL PROPERTY BUILD_COMPONENTS) -get_property(build_component_paths GLOBAL PROPERTY BUILD_COMPONENT_PATHS) -get_property(build_test_components GLOBAL PROPERTY BUILD_TEST_COMPONENTS) -get_property(build_test_component_paths GLOBAL PROPERTY BUILD_TEST_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}.tmp" "${contents}\n") -endfunction() - -file(WRITE "${DEPENDENCIES_FILE}.tmp" "# Component requirements generated by expand_requirements.cmake\n\n") -line("set(BUILD_COMPONENTS ${build_components})") -line("set(BUILD_COMPONENT_PATHS ${build_component_paths})") -line("set(BUILD_TEST_COMPONENTS ${build_test_components})") -line("set(BUILD_TEST_COMPONENT_PATHS ${build_test_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()") - -# only replace DEPENDENCIES_FILE if it has changed (prevents ninja/make build loops.) -execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${DEPENDENCIES_FILE}.tmp" "${DEPENDENCIES_FILE}") -execute_process(COMMAND ${CMAKE_COMMAND} -E remove "${DEPENDENCIES_FILE}.tmp") diff --git a/tools/cmake/targets.cmake b/tools/cmake/targets.cmake index 2092985a82..57d3b7640f 100644 --- a/tools/cmake/targets.cmake +++ b/tools/cmake/targets.cmake @@ -1,6 +1,7 @@ -include(component_utils) - -macro(idf_set_target) +# +# Set the target used for the standard project build. +# +macro(__target_init) # Input is IDF_TARGET environement variable set(env_idf_target $ENV{IDF_TARGET}) @@ -26,28 +27,38 @@ macro(idf_set_target) # Finally, set IDF_TARGET in cache set(IDF_TARGET ${env_idf_target} CACHE STRING "IDF Build Target") - - message(STATUS "Building for target ${IDF_TARGET}") endmacro() -macro(idf_check_config_target) - if(NOT ${IDF_TARGET} STREQUAL ${CONFIG_IDF_TARGET}) +# +# Check that the set build target and the config target matches. +# +function(__target_check) + # Should be called after sdkconfig CMake file has been included. + idf_build_get_property(idf_target IDF_TARGET) + if(NOT ${idf_target} STREQUAL ${CONFIG_IDF_TARGET}) message(FATAL_ERROR "CONFIG_IDF_TARGET in sdkconfig does not match " "IDF_TARGET environement variable. To change the target, delete " "sdkconfig file and build the project again.") endif() -endmacro() +endfunction() -macro(idf_set_toolchain) - # First try to load the toolchain file from the tools/cmake/ directory of IDF - set(toolchain_file_global $ENV{IDF_PATH}/tools/cmake/toolchain-${IDF_TARGET}.cmake) +# +# Used by the project CMake file to set the toolchain before project() call. +# +macro(__target_set_toolchain) + idf_build_get_property(idf_path IDF_PATH) + # First try to load the toolchain file from the tools/cmake/directory of IDF + set(toolchain_file_global ${idf_path}/tools/cmake/toolchain-${IDF_TARGET}.cmake) if(EXISTS ${toolchain_file_global}) set(CMAKE_TOOLCHAIN_FILE ${toolchain_file_global}) else() - # Try to load the toolchain file from the directory of ${IDF_TARGET} component - components_find_all("${IDF_COMPONENT_DIRS}" ALL_COMPONENT_PATHS ALL_COMPONENTS ALL_TEST_COMPONENTS) - find_component_path(${IDF_TARGET} "${ALL_COMPONENTS}" "${ALL_COMPONENT_PATHS}" target_component_path) - set(toolchain_file_component ${target_component_path}/toolchain-${IDF_TARGET}.cmake) + __component_get_target(component_target ${IDF_TARGET}) + if(NOT component_target) + message(FATAL_ERROR "Unable to resolve '${IDF_TARGET}' for setting toolchain file.") + endif() + get_property(component_dir TARGET ${component_target} PROPERTY COMPONENT_DIR) + # Try to load the toolchain file from the directory of IDF_TARGET component + set(toolchain_file_component ${component_dir}/toolchain-${IDF_TARGET}.cmake) if(EXISTS ${toolchain_file_component}) set(CMAKE_TOOLCHAIN_FILE ${toolchain_file_component}) else() @@ -55,4 +66,4 @@ macro(idf_set_toolchain) "checked ${toolchain_file_global} and ${toolchain_file_component}") endif() endif() -endmacro() +endmacro() \ No newline at end of file diff --git a/tools/cmake/utilities.cmake b/tools/cmake/utilities.cmake index 36cfbc7654..4e64c16201 100644 --- a/tools/cmake/utilities.cmake +++ b/tools/cmake/utilities.cmake @@ -77,21 +77,23 @@ endfunction() # by converting it to a generated source file which is then compiled # to a binary object as part of the build function(target_add_binary_data target embed_file embed_type) + idf_build_get_property(build_dir BUILD_DIR) + idf_build_get_property(idf_path IDF_PATH) get_filename_component(embed_file "${embed_file}" ABSOLUTE) get_filename_component(name "${embed_file}" NAME) - set(embed_srcfile "${IDF_BUILD_ARTIFACTS_DIR}/${name}.S") + set(embed_srcfile "${build_dir}/${name}.S") add_custom_command(OUTPUT "${embed_srcfile}" COMMAND "${CMAKE_COMMAND}" -D "DATA_FILE=${embed_file}" -D "SOURCE_FILE=${embed_srcfile}" -D "FILE_TYPE=${embed_type}" - -P "${IDF_PATH}/tools/cmake/scripts/data_file_embed_asm.cmake" + -P "${idf_path}/tools/cmake/scripts/data_file_embed_asm.cmake" MAIN_DEPENDENCY "${embed_file}" - DEPENDS "${IDF_PATH}/tools/cmake/scripts/data_file_embed_asm.cmake" - WORKING_DIRECTORY "${IDF_BUILD_ARTIFACTS_DIR}" + DEPENDS "${idf_path}/tools/cmake/scripts/data_file_embed_asm.cmake" + WORKING_DIRECTORY "${build_dir}" VERBATIM) set_property(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES "${embed_srcfile}") @@ -128,25 +130,50 @@ endfunction() # Automatically adds a -L search path for the containing directory (if found), # and then adds -T with the filename only. This allows INCLUDE directives to be # used to include other linker scripts in the same directory. -function(target_linker_script target) - foreach(scriptfile ${ARGN}) +function(target_linker_script target scriptfiles) + cmake_parse_arguments(_ "PROCESS" "" "" ${ARGN}) + foreach(scriptfile ${scriptfiles}) get_filename_component(abs_script "${scriptfile}" ABSOLUTE) message(STATUS "Adding linker script ${abs_script}") + if(__PROCESS) + __ldgen_process_template(output ${abs_script}) + set(abs_script ${output}) + endif() + get_filename_component(search_dir "${abs_script}" DIRECTORY) get_filename_component(scriptname "${abs_script}" NAME) - get_target_property(link_libraries "${target}" LINK_LIBRARIES) - list(FIND "${link_libraries}" "-L ${search_dir}" found_search_dir) - if(found_search_dir EQUAL "-1") # not already added as a search path - target_link_libraries("${target}" "-L ${search_dir}") + get_target_property(type ${target} TYPE) + if(type STREQUAL "INTERFACE_LIBRARY") + set(is_interface "INTERFACE") endif() - target_link_libraries("${target}" "-T ${scriptname}") + if(is_interface) + get_target_property(link_libraries "${target}" INTERFACE_LINK_LIBRARIES) + else() + get_target_property(link_libraries "${target}" LINK_LIBRARIES) + endif() + + list(FIND "${link_libraries}" "-L ${search_dir}" found_search_dir) + if(found_search_dir EQUAL "-1") # not already added as a search path + target_link_libraries("${target}" "${is_interface}" "-L ${search_dir}") + endif() + + target_link_libraries("${target}" "${is_interface}" "-T ${scriptname}") # Note: In ESP-IDF, most targets are libraries and libary LINK_DEPENDS don't propagate to - # executable(s) the library is linked to. This is done manually in components.cmake. - set_property(TARGET "${target}" APPEND PROPERTY LINK_DEPENDS "${abs_script}") + # executable(s) the library is linked to. Attach manually to executable once it is known. + # + # Property INTERFACE_LINK_DEPENDS is available in CMake 3.13 which should propagate link + # dependencies. + if(NOT __PROCESS) + if(is_interface) + set_property(TARGET ${target} APPEND PROPERTY INTERFACE_LINK_DEPENDS ${abs_script}) + else() + set_property(TARGET ${target} APPEND PROPERTY LINK_DEPENDS ${abs_script}) + endif() + endif() endforeach() endfunction() @@ -177,6 +204,7 @@ endfunction() # We cannot use CMAKE_CONFIGURE_DEPENDS instead because it only works for files which exist at CMake runtime. # function(fail_at_build_time target_name message_line0) + idf_build_get_property(idf_path IDF_PATH) set(message_lines COMMAND ${CMAKE_COMMAND} -E echo "${message_line0}") foreach(message_line ${ARGN}) set(message_lines ${message_lines} COMMAND ${CMAKE_COMMAND} -E echo "${message_line}") @@ -184,6 +212,44 @@ function(fail_at_build_time target_name message_line0) add_custom_target(${target_name} ALL ${message_lines} COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_BINARY_DIR}/CMakeCache.txt" - COMMAND ${CMAKE_COMMAND} -P ${IDF_PATH}/tools/cmake/scripts/fail.cmake + COMMAND ${CMAKE_COMMAND} -P ${idf_path}/tools/cmake/scripts/fail.cmake VERBATIM) endfunction() + +function(check_exclusive_args args prefix) + set(_args ${args}) + spaces2list(_args) + set(only_arg 0) + foreach(arg ${_args}) + if(${prefix}_${arg} AND only_arg) + message(FATAL_ERROR "${args} are exclusive arguments") + endif() + + if(${prefix}_${arg}) + set(only_arg 1) + endif() + endforeach() +endfunction() + + +# add_compile_options variant for C++ code only +# +# This adds global options, set target properties for +# component-specific flags +function(add_cxx_compile_options) + foreach(option ${ARGV}) + # note: the Visual Studio Generator doesn't support this... + add_compile_options($<$:${option}>) + endforeach() +endfunction() + +# add_compile_options variant for C code only +# +# This adds global options, set target properties for +# component-specific flags +function(add_c_compile_options) + foreach(option ${ARGV}) + # note: the Visual Studio Generator doesn't support this... + add_compile_options($<$:${option}>) + endforeach() +endfunction() diff --git a/tools/kconfig_new/confgen.py b/tools/kconfig_new/confgen.py index 346b8352b4..5d49c85392 100755 --- a/tools/kconfig_new/confgen.py +++ b/tools/kconfig_new/confgen.py @@ -301,6 +301,8 @@ def write_cmake(deprecated_options, config, filename): # """) + configs_list = list() + def write_node(node): sym = node.item if not isinstance(sym, kconfiglib.Symbol): @@ -314,10 +316,15 @@ def write_cmake(deprecated_options, config, filename): val = "" # write unset values as empty variables write("set({}{} \"{}\")\n".format( prefix, sym.name, val)) + + configs_list.append(prefix + sym.name) dep_opt = deprecated_options.get_deprecated_option(sym.name) if dep_opt: tmp_dep_list.append("set({}{} \"{}\")\n".format(prefix, dep_opt, val)) + configs_list.append(prefix + dep_opt) + config.walk_menu(write_node) + write("set(CONFIGS_LIST {})".format(";".join(configs_list))) if len(tmp_dep_list) > 0: write('\n# List of deprecated options for backward compatibility\n')