diff --git a/tools/cmake/build.cmake b/tools/cmake/build.cmake index 97c43e8a23..6cf9a73b55 100644 --- a/tools/cmake/build.cmake +++ b/tools/cmake/build.cmake @@ -195,7 +195,8 @@ 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) + __component_get_property(component_registered ${component_target} __COMPONENT_REGISTERED) + if(component_target IN_LIST component_targets_seen OR NOT component_registered) return() endif() @@ -365,51 +366,13 @@ macro(idf_build_process target) # Check for required Python modules __build_check_python() - # 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}) + idf_build_set_property(__COMPONENT_REQUIRES_COMMON ${target} APPEND) + __component_get_requirements() # 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. @@ -431,6 +394,7 @@ macro(idf_build_process target) # Get a list of common component requirements in component targets form (previously # we just have a list of component names) + idf_build_get_property(common_reqs __COMPONENT_REQUIRES_COMMON) foreach(common_req ${common_reqs}) __component_get_target(component_target ${common_req}) __component_get_property(lib ${component_target} COMPONENT_LIB) diff --git a/tools/cmake/component.cmake b/tools/cmake/component.cmake index b3dd460c83..6caca93a74 100644 --- a/tools/cmake/component.cmake +++ b/tools/cmake/component.cmake @@ -125,6 +125,23 @@ function(__component_dir_quick_check var component_dir) set(${var} ${res} PARENT_SCOPE) endfunction() +# +# Write a CMake file containing all component and their properties. This is possible because each component +# keeps a list of all its properties. +# +function(__component_write_properties output_file) + idf_build_get_property(component_targets __COMPONENT_TARGETS) + foreach(component_target ${component_targets}) + __component_get_property(component_properties ${component_target} __COMPONENT_PROPERTIES) + foreach(property ${component_properties}) + __component_get_property(val ${component_target} ${property}) + set(component_properties_text + "${component_properties_text}\nset(__component_${component_target}_${property} ${val})") + endforeach() + file(WRITE ${output_file} "${component_properties_text}") + endforeach() +endfunction() + # # Add a component to process in the build. The components are keeped tracked of in property # __COMPONENT_TARGETS in component target form. @@ -184,44 +201,35 @@ 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) +function(__component_get_requirements) 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. + idf_build_get_property(build_dir BUILD_DIR) + set(build_properties_file ${build_dir}/build_properties.temp.cmake) + set(component_properties_file ${build_dir}/component_properties.temp.cmake) + set(component_requires_file ${build_dir}/component_requires.temp.cmake) + + __build_write_properties(${build_properties_file}) + __component_write_properties(${component_properties_file}) + 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" + -D "COMPONENT_PROPERTIES_FILE=${component_properties_file}" + -D "COMPONENT_REQUIRES_FILE=${component_requires_file}" -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() + message(FATAL_ERROR "${error}") endif() - string(REGEX REPLACE ";" "\\\\;" _output "${error}") - string(REGEX REPLACE "\n" ";" _output "${_output}") - list(REVERSE _output) + include(${component_requires_file}) - 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) + file(REMOVE ${build_properties_file}) + file(REMOVE ${component_properties_file}) + file(REMOVE ${component_requires_file}) endfunction() # __component_add_sources, __component_check_target diff --git a/tools/cmake/scripts/component_get_requirements.cmake b/tools/cmake/scripts/component_get_requirements.cmake index 8223f709be..0b0d18727e 100644 --- a/tools/cmake/scripts/component_get_requirements.cmake +++ b/tools/cmake/scripts/component_get_requirements.cmake @@ -1,10 +1,5 @@ -include(${IDF_PATH}/tools/cmake/utilities.cmake) - include("${BUILD_PROPERTIES_FILE}") -include("${SDKCONFIG_CMAKE}") - -macro(require_idf_targets) -endmacro() +include("${COMPONENT_PROPERTIES_FILE}") function(idf_build_get_property var property) cmake_parse_arguments(_ "GENERATOR_EXPRESSION" "" "" ${ARGN}) @@ -12,17 +7,20 @@ function(idf_build_get_property var property) message(FATAL_ERROR "Getting build property generator expression not supported before idf_component_register().") endif() - set(${var} ${property} PARENT_SCOPE) + 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}") +idf_build_get_property(idf_path IDF_PATH) +include(${idf_path}/tools/cmake/utilities.cmake) + +function(__component_get_property var component_target property) + set(_property __component_${component_target}_${property}) + set(${var} ${${_property}} PARENT_SCOPE) endfunction() +macro(require_idf_targets) +endmacro() + macro(idf_component_register) set(options) set(single_value) @@ -30,14 +28,16 @@ macro(idf_component_register) 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) + set(__component_requires "${__REQUIRES}") + set(__component_priv_requires "${__PRIV_REQUIRES}") + set(__component_registered 1) return() endmacro() macro(register_component) - print_requires("${COMPONENT_REQUIRES}" "${COMPONENT_PRIV_REQUIRES}") - set(__is_component 1) + set(__component_requires "${COMPONENT_REQUIRES}") + set(__component_priv_requires "${COMPONENT_PRIV_REQUIRES}") + set(__component_registered 1) return() endmacro() @@ -45,5 +45,55 @@ macro(register_config_only_component) register_component() endmacro() -set(CMAKE_BUILD_EARLY_EXPANSION) -include(${COMPONENT_DIR}/CMakeLists.txt OPTIONAL) +idf_build_get_property(__common_reqs __COMPONENT_REQUIRES_COMMON) +idf_build_get_property(__component_targets __COMPONENT_TARGETS) + +function(__component_get_requirements) + # This is in a function (separate variable context) so that variables declared + # and set by the included CMakeLists.txt does not bleed into the next inclusion. + # We are only interested in the public and private requirements of components + __component_get_property(__component_dir ${__component_target} COMPONENT_DIR) + include(${__component_dir}/CMakeLists.txt OPTIONAL) + + spaces2list(__component_requires) + spaces2list(__component_priv_requires) + + set(__component_requires "${__component_requires}" PARENT_SCOPE) + set(__component_priv_requires "${__component_priv_requires}" PARENT_SCOPE) + set(__component_registered ${__component_registered} PARENT_SCOPE) +endfunction() + +set(CMAKE_BUILD_EARLY_EXPANSION 1) +foreach(__component_target ${__component_targets}) + set(__component_requires "") + set(__component_priv_requires "") + set(__component_registered 0) + + __component_get_requirements() + + list(APPEND __component_requires "${__common_reqs}") + + # Remove duplicates and the component itself from its requirements + __component_get_property(__component_alias ${__component_target} COMPONENT_ALIAS) + __component_get_property(__component_name ${__component_target} COMPONENT_NAME) + + # Prevent component from linking to itself. + if(__component_requires) + list(REMOVE_DUPLICATES __component_requires) + list(REMOVE_ITEM __component_requires ${__component_alias} ${__component_name}) + endif() + + if(__component_requires) + list(REMOVE_DUPLICATES __component_priv_requires) + list(REMOVE_ITEM __component_priv_requires ${__component_alias} ${__component_name}) + endif() + + set(__contents +"__component_set_property(${__component_target} REQUIRES \"${__component_requires}\") +__component_set_property(${__component_target} PRIV_REQUIRES \"${__component_priv_requires}\") +__component_set_property(${__component_target} __COMPONENT_REGISTERED ${__component_registered})" + ) + set(__component_requires_contents "${__component_requires_contents}\n${__contents}") +endforeach() + +file(WRITE ${COMPONENT_REQUIRES_FILE} "${__component_requires_contents}") \ No newline at end of file