Add support for evaluating genexes when exporting custom target props

Properties specified in EXPORT_PROPERTIES have their values exported
verbatim, without evaluating generator expression they might contain.
This limitation is removed by the introduced functions. They collect
properties that needs to be exported and evaluate generator
expressions inside the properties using file(GENERATE) before
exporting them. The functions generate the ExtraProperties.cmake
file that contains set_property calls with exported properties
and corresponding values.

Change-Id: If32c30a82a62e8bd48bb91f3df21ff2ad8d07243
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
This commit is contained in:
Alexey Edelev 2022-05-13 18:24:22 +02:00
parent 0a27f4030e
commit d51d7d4d5c
3 changed files with 161 additions and 0 deletions

View File

@ -27,6 +27,8 @@ endif()
if (NOT QT_NO_CREATE_TARGETS AND @INSTALL_CMAKE_NAMESPACE@@target@_FOUND)
include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@Targets.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@AdditionalTargetInfo.cmake")
include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@ExtraProperties.cmake"
OPTIONAL)
if(NOT QT_NO_CREATE_VERSIONLESS_TARGETS)
include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@VersionlessTargets.cmake")
endif()

View File

@ -800,6 +800,11 @@ set(QT_ALLOW_MISSING_TOOLS_PACKAGES TRUE)")
EXPORT_NAME_PREFIX ${INSTALL_CMAKE_NAMESPACE}${target}
CONFIG_INSTALL_DIR "${config_install_dir}")
qt_internal_export_genex_properties(TARGETS ${target}
EXPORT_NAME_PREFIX ${INSTALL_CMAKE_NAMESPACE}${target}
CONFIG_INSTALL_DIR "${config_install_dir}"
)
### fixme: cmake is missing a built-in variable for this. We want to apply it only to modules and plugins
# that belong to Qt.
if(NOT arg_HEADER_MODULE)

View File

@ -1142,3 +1142,157 @@ function(qt_internal_get_target_sources_property out_var)
endif()
set(${out_var} "${${out_var}}" PARENT_SCOPE)
endfunction()
# This function collects target properties that contain generator expressions and needs to be
# exported. This function is needed since the CMake EXPORT_PROPERTIES property doesn't support
# properties that contain generator expressions.
# Usage: qt_internal_add_genex_properties_export(target properties...)
function(qt_internal_add_genex_properties_export target)
get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
set(config_check_begin "")
set(config_check_end "")
if(is_multi_config)
list(GET CMAKE_CONFIGURATION_TYPES 0 first_config_type)
# The genex snippet is evaluated to '$<NOT:$<BOOL:$<CONFIG>>>' in the generated cmake file.
# The check is only applicable to the 'main' configuration. If user project doesn't use
# multi-config generator, then the check supposed to return true and the value from the
# 'main' configuration supposed to be used.
string(JOIN "" check_if_config_empty
"$<1:$><NOT:"
"$<1:$><BOOL:"
"$<1:$><CONFIG$<ANGLE-R>"
"$<ANGLE-R>"
"$<ANGLE-R>"
)
# The genex snippet is evaluated to '$<CONFIG:'Qt config type'>' in the generated cmake
# file and checks if the config that user uses matches the generated cmake file config.
string(JOIN "" check_user_config
"$<1:$><CONFIG:$<CONFIG>$<ANGLE-R>"
)
# The genex snippet is evaluated to '$<$<OR:$<CONFIG:'Qt config type'>>:'Property content'>
# for non-main Qt configs and to
# $<$<OR:$<CONFIG:'Qt config type'>,$<NOT:$<BOOL:$<CONFIG>>>>:'Property content'> for the
# main Qt config. This guard is required to choose the correct value of the property for the
# user project according to the user config type.
# All genexes need to be escaped properly to protect them from evaluation by the
# file(GENERATE call in the qt_internal_export_genex_properties function.
string(JOIN "" config_check_begin
"$<1:$><"
"$<1:$><OR:"
"${check_user_config}"
"$<$<CONFIG:${first_config_type}>:$<COMMA>${check_if_config_empty}>"
"$<ANGLE-R>:"
)
set(config_check_end "$<ANGLE-R>")
endif()
set(target_name "${QT_CMAKE_EXPORT_NAMESPACE}::${target}")
foreach(property IN LISTS ARGN)
set(target_property_genex "$<TARGET_PROPERTY:${target_name},${property}>")
# All properties that contain lists need to be protected of processing by JOIN genex calls.
# So this escapes the semicolons for these list.
set(target_property_list_escape
"$<JOIN:$<GENEX_EVAL:${target_property_genex}>,\;>")
set(property_value
"\"${config_check_begin}${target_property_list_escape}${config_check_end}\"")
set_property(TARGET ${target} APPEND PROPERTY _qt_export_genex_properties_content
"${property} ${property_value}")
endforeach()
endfunction()
# This function executes generator expressions for the properties that are added by the
# qt_internal_add_genex_properties_export function and sets the calculated values to the
# corresponding properties in the generated ExtraProperties.cmake file. The file then needs to be
# included after the target creation routines in Config.cmake files. It also supports Multi-Config
# builds.
# Arguments:
# EXPORT_NAME_PREFIX:
# The portion of the file name before ExtraProperties.cmake
# CONFIG_INSTALL_DIR:
# Installation location for the file.
# TARGETS:
# The internal target names.
function(qt_internal_export_genex_properties)
set(option_args "")
set(single_args
EXPORT_NAME_PREFIX
CONFIG_INSTALL_DIR
)
set(multi_args TARGETS)
cmake_parse_arguments(arg "${option_args}" "${single_args}" "${multi_args}" ${ARGN})
if(NOT arg_EXPORT_NAME_PREFIX)
message(FATAL_ERROR "qt_internal_export_genex_properties: "
"Missing EXPORT_NAME_PREFIX argument.")
endif()
if(NOT arg_TARGETS)
message(FATAL_ERROR "qt_internal_export_genex_properties: "
"TARGETS argument must contain at least one target")
endif()
foreach(target IN LISTS arg_TARGETS)
get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG)
set(output_file_base_name "${arg_EXPORT_NAME_PREFIX}ExtraProperties")
set(should_append "")
set(config_suffix "")
if(is_multi_config)
list(GET CMAKE_CONFIGURATION_TYPES 0 first_config_type)
set(config_suffix "$<$<NOT:$<CONFIG:${first_config_type}>>:-$<CONFIG>>")
# If the generated file belongs to the 'main' config type, we should set property
# but not append it.
string(JOIN "" should_append
"$<$<NOT:$<CONFIG:${first_config_type}>>: APPEND>")
endif()
set(file_name "${output_file_base_name}${config_suffix}.cmake")
qt_path_join(output_file "${arg_CONFIG_INSTALL_DIR}"
"${file_name}")
if(NOT IS_ABSOLUTE "${output_file}")
qt_path_join(output_file "${QT_BUILD_DIR}" "${output_file}")
endif()
set(target_name "${QT_CMAKE_EXPORT_NAMESPACE}::${target}")
string(JOIN "" set_property_begin "set_property(TARGET "
"${target_name}${should_append} PROPERTY "
)
set(set_property_end ")")
set(set_property_glue "${set_property_end}\n${set_property_begin}")
set(property_list
"$<GENEX_EVAL:$<TARGET_PROPERTY:${target},_qt_export_genex_properties_content>>")
string(JOIN "" set_property_content "${set_property_begin}"
"$<JOIN:${property_list},${set_property_glue}>"
"${set_property_end}")
if(is_multi_config)
set(config_includes "")
foreach(config IN LISTS CMAKE_CONFIGURATION_TYPES)
if(NOT first_config_type STREQUAL config)
set(include_file_name
"${output_file_base_name}-${config}.cmake")
list(APPEND config_includes
"include(\"\${CMAKE_CURRENT_LIST_DIR}/${include_file_name}\")")
endif()
endforeach()
list(JOIN config_includes "\n" config_includes_string)
set(config_includes_string
"\n$<$<CONFIG:${first_config_type}>:${config_includes_string}>")
endif()
file(GENERATE OUTPUT "${output_file}"
CONTENT "$<$<BOOL:${property_list}>:${set_property_content}${config_includes_string}>"
CONDITION "$<BOOL:${property_list}>"
)
endforeach()
qt_install(FILES "$<$<BOOL:${property_list}>:${output_file}>"
DESTINATION "${arg_CONFIG_INSTALL_DIR}"
COMPONENT Devel
)
endfunction()