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.
pull/3627/head
Renz Christian Bagaporo 2019-05-28 21:43:03 +08:00
rodzic 33dd7011be
commit 297b2c5a39
3 zmienionych plików z 107 dodań i 85 usunięć

Wyświetl plik

@ -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)

Wyświetl plik

@ -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

Wyświetl plik

@ -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}")