From 297b2c5a3974b376bb17dc3785e8ffffb2701913 Mon Sep 17 00:00:00 2001 From: Renz Christian Bagaporo Date: Tue, 28 May 2019 21:43:03 +0800 Subject: [PATCH] cmake: evaluate component requirements in one go !4452 simplified early expansion by using an early expansion script that only does one thing: get the public and private requirements for each component, albeit one by one. This was also dependent on parsing the command output of the expansion script. This commit makes it so that a list of all components to be processed to passed to the expansion script, generating a cmake file that sets each component requirements in one go. This also makes sure that only components that registered themselves get included in the final build list. --- tools/cmake/build.cmake | 46 ++-------- tools/cmake/component.cmake | 58 ++++++------ .../scripts/component_get_requirements.cmake | 88 +++++++++++++++---- 3 files changed, 107 insertions(+), 85 deletions(-) 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