From 471ff20f33677fe5dd598b1fbd27d10fdb4056a6 Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Thu, 29 Apr 2021 12:47:52 +0200 Subject: CMake: Make qt_internal_walk_libs available in public projects Needed for the upcoming static plugin mechanism, where we have to extract the list of Qt module dependencies of a target and then extract the plugins associated with those modules. To do that we need to recursively collect the dependencies of a given target. Rename the moved functions to contain the __qt_internal prefix. Also rename the existing QtPublicTargetsHelpers.cmake into QtPlatformTargetHelpers.cmake to avoid confusion with the newly introduced QtPublicTargetHelpers.cmake. Task-number: QTBUG-92933 Change-Id: I48b5b6a8718a3424f59ca60f11fc9e97a809765d Reviewed-by: Joerg Bornemann --- cmake/QtBaseGlobalTargets.cmake | 6 +- cmake/QtBuild.cmake | 2 + cmake/QtConfig.cmake.in | 2 + cmake/QtFindPackageHelpers.cmake | 6 +- cmake/QtPlatformTargetHelpers.cmake | 33 +++++ cmake/QtPostProcessHelpers.cmake | 2 +- cmake/QtPrlHelpers.cmake | 239 +----------------------------------- cmake/QtPublicTargetHelpers.cmake | 16 +++ cmake/QtPublicTargetsHelpers.cmake | 33 ----- cmake/QtPublicWalkLibsHelpers.cmake | 237 +++++++++++++++++++++++++++++++++++ cmake/QtTargetHelpers.cmake | 17 --- 11 files changed, 300 insertions(+), 293 deletions(-) create mode 100644 cmake/QtPlatformTargetHelpers.cmake create mode 100644 cmake/QtPublicTargetHelpers.cmake delete mode 100644 cmake/QtPublicTargetsHelpers.cmake create mode 100644 cmake/QtPublicWalkLibsHelpers.cmake (limited to 'cmake') diff --git a/cmake/QtBaseGlobalTargets.cmake b/cmake/QtBaseGlobalTargets.cmake index 0f5d68cd0c..54891f9206 100644 --- a/cmake/QtBaseGlobalTargets.cmake +++ b/cmake/QtBaseGlobalTargets.cmake @@ -134,7 +134,7 @@ target_include_directories(GlobalConfigPrivate INTERFACE ) add_library(Qt::GlobalConfigPrivate ALIAS GlobalConfigPrivate) -include(QtPublicTargetsHelpers) +include(QtPlatformTargetHelpers) qt_internal_setup_public_platform_target() # defines PlatformCommonInternal PlatformModuleInternal PlatformPluginInternal PlatformToolInternal @@ -214,7 +214,7 @@ qt_copy_or_install(FILES cmake/QtPrecompiledHeadersHelpers.cmake cmake/QtPriHelpers.cmake cmake/QtPrlHelpers.cmake - cmake/QtPublicTargetsHelpers.cmake + cmake/QtPlatformTargetHelpers.cmake cmake/QtProcessConfigureArgs.cmake cmake/QtQmakeHelpers.cmake cmake/QtResourceHelpers.cmake @@ -245,6 +245,8 @@ set(__public_cmake_helpers cmake/QtFeature.cmake cmake/QtFeatureCommon.cmake cmake/QtPublicPluginHelpers.cmake + cmake/QtPublicTargetHelpers.cmake + cmake/QtPublicWalkLibsHelpers.cmake ) qt_copy_or_install(FILES ${__public_cmake_helpers} DESTINATION "${__GlobalConfig_install_dir}") diff --git a/cmake/QtBuild.cmake b/cmake/QtBuild.cmake index 20efee0d6e..d246685952 100644 --- a/cmake/QtBuild.cmake +++ b/cmake/QtBuild.cmake @@ -547,6 +547,8 @@ endif() # Helpers that are available in public projects and while building Qt itself. include(QtPublicPluginHelpers) +include(QtPublicTargetHelpers) +include(QtPublicWalkLibsHelpers) # TODO: This block provides support for old variables. It should be removed once diff --git a/cmake/QtConfig.cmake.in b/cmake/QtConfig.cmake.in index d762cd50ca..7708954b90 100644 --- a/cmake/QtConfig.cmake.in +++ b/cmake/QtConfig.cmake.in @@ -56,6 +56,8 @@ file(TO_CMAKE_PATH "$ENV{QT_ADDITIONAL_PACKAGES_PREFIX_PATH}" _qt_additional_pac # Public helpers available to all Qt packages. include("${CMAKE_CURRENT_LIST_DIR}/QtFeature.cmake") include("${CMAKE_CURRENT_LIST_DIR}/QtPublicPluginHelpers.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/QtPublicTargetHelpers.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/QtPublicWalkLibsHelpers.cmake") # Find required dependencies, if any. include(CMakeFindDependencyMacro) diff --git a/cmake/QtFindPackageHelpers.cmake b/cmake/QtFindPackageHelpers.cmake index 1162f2c39e..85dc1ff7db 100644 --- a/cmake/QtFindPackageHelpers.cmake +++ b/cmake/QtFindPackageHelpers.cmake @@ -7,10 +7,10 @@ # Only works if called from qt_find_package(), because the promotion needs to happen in the same # directory scope where the imported target is first created. # -# Uses qt_internal_walk_libs. +# Uses __qt_internal_walk_libs. function(qt_find_package_promote_targets_to_global_scope target) - qt_internal_walk_libs("${target}" _discarded_out_var _discarded_out_var_2 - "qt_find_package_targets_dict" "promote_global") + __qt_internal_walk_libs("${target}" _discarded_out_var _discarded_out_var_2 + "qt_find_package_targets_dict" "promote_global") endfunction() macro(qt_find_package) diff --git a/cmake/QtPlatformTargetHelpers.cmake b/cmake/QtPlatformTargetHelpers.cmake new file mode 100644 index 0000000000..68ff8a3ea0 --- /dev/null +++ b/cmake/QtPlatformTargetHelpers.cmake @@ -0,0 +1,33 @@ +# Defines the public Qt::Platform target, which serves as a dependency for all internal Qt target +# as well as user projects consuming Qt. +function(qt_internal_setup_public_platform_target) + ## QtPlatform Target: + add_library(Platform INTERFACE) + add_library(Qt::Platform ALIAS Platform) + add_library(${INSTALL_CMAKE_NAMESPACE}::Platform ALIAS Platform) + target_include_directories(Platform + INTERFACE + $ + $ + $ + $ + ) + target_compile_definitions(Platform INTERFACE ${QT_PLATFORM_DEFINITIONS}) + + # When building on android we need to link against the logging library + # in order to satisfy linker dependencies. Both of these libraries are part of + # the NDK. + if (ANDROID) + target_link_libraries(Platform INTERFACE log) + endif() + + qt_set_msvc_cplusplus_options(Platform INTERFACE) + + # Propagate minimum C++ 17 via Platform to Qt consumers (apps), after the global features + # are computed. + qt_set_language_standards_interface_compile_features(Platform) + + # By default enable utf8 sources for both Qt and Qt consumers. Can be opted out. + qt_enable_utf8_sources(Platform) + +endfunction() diff --git a/cmake/QtPostProcessHelpers.cmake b/cmake/QtPostProcessHelpers.cmake index 6e09fcdf94..c8d310d93d 100644 --- a/cmake/QtPostProcessHelpers.cmake +++ b/cmake/QtPostProcessHelpers.cmake @@ -36,7 +36,7 @@ macro(qt_collect_third_party_deps target) endif() # Strip any directory scope tokens. - qt_internal_strip_target_directory_scope_token("${dep}" dep) + __qt_internal_strip_target_directory_scope_token("${dep}" dep) if(TARGET ${dep}) list(FIND third_party_deps_seen ${dep} dep_seen) diff --git a/cmake/QtPrlHelpers.cmake b/cmake/QtPrlHelpers.cmake index f843fb4bbe..86555dbb65 100644 --- a/cmake/QtPrlHelpers.cmake +++ b/cmake/QtPrlHelpers.cmake @@ -1,250 +1,15 @@ -# Add libraries to variable ${out_libs_var} in a way that duplicates -# are added at the end. This ensures the library order needed for the -# linker. -function(qt_merge_libs out_libs_var) - foreach(dep ${ARGN}) - list(REMOVE_ITEM ${out_libs_var} ${dep}) - list(APPEND ${out_libs_var} ${dep}) - endforeach() - set(${out_libs_var} ${${out_libs_var}} PARENT_SCOPE) -endfunction() - # Collects the library dependencies of a target. # As well as rcc object file dependencies. # This takes into account transitive usage requirements. function(qt_collect_libs target libs_out_var rcc_objects_out_var) - qt_internal_walk_libs("${target}" "${libs_out_var}" - "${rcc_objects_out_var}" "qt_collect_libs_dict" "collect_libs") + __qt_internal_walk_libs("${target}" "${libs_out_var}" + "${rcc_objects_out_var}" "qt_collect_libs_dict" "collect_libs") set("${libs_out_var}" "${${libs_out_var}}" PARENT_SCOPE) set(${rcc_objects_out_var} "${${rcc_objects_out_var}}" PARENT_SCOPE) endfunction() -# Extracts value from per-target dict key and assigns it to out_var. -# Assumes dict_name to be an existing INTERFACE target. -function(qt_internal_get_dict_key_values out_var target_infix dict_name dict_key) - get_target_property(values "${dict_name}" "INTERFACE_${target_infix}_${dict_key}") - set(${out_var} "${values}" PARENT_SCOPE) -endfunction() - -# Assigns 'values' to per-target dict key, including for aliases of the target. -# Assumes dict_name to be an existing INTERFACE target. -function(qt_internal_memoize_values_in_dict target dict_name dict_key values) - # Memoize the computed values for the target as well as its aliases. - # - # Aka assigns the contents of ${values} to INTERFACE_Core, INTERFACE_Qt::Core, - # INTERFACE_Qt6::Core. - # - # Yes, i know it's crazy that target names are legal property names. - # - # Assigning for library aliases is needed to avoid multiple recomputation of values. - # Scenario in the context of qt_internal_walk_libs: - # 'values' are computed for Core target and memoized to INTERFACE_Core. - # When processing Gui, it depends on Qt::Core, but there are no values for INTERFACE_Qt::Core. - set_target_properties(${dict_name} PROPERTIES INTERFACE_${target}_${dict_key} "${values}") - - get_target_property(versionless_alias "${target}" "_qt_versionless_alias") - if(versionless_alias) - qt_internal_get_dict_key_values( - versionless_values "${versionless_alias}" "${dict_name}" "${dict_key}") - if(versionless_values MATCHES "-NOTFOUND$") - set_target_properties(${dict_name} - PROPERTIES INTERFACE_${versionless_alias}_${dict_key} "${values}") - endif() - endif() - - get_target_property(versionfull_alias "${target}" "_qt_versionfull_alias") - if(versionfull_alias) - qt_internal_get_dict_key_values( - versionfull_values "${versionfull_alias}" "${dict_name}" "${dict_key}") - if(versionfull_values MATCHES "-NOTFOUND$") - set_target_properties(${dict_name} - PROPERTIES INTERFACE_${versionfull_alias}_${dict_key} "${values}") - endif() - endif() -endfunction() - -# Walks a target's link libraries recursively, and performs some actions (poor man's polypmorphism) -# -# out_var is the name of the variable where the result will be assigned. The result is a list of -# libraries, mostly in generator expression form. -# rcc_objects_out_var is the name of the variable where the collected rcc object files will be -# assigned (for the initial target and its dependencies) -# dict_name is used for caching the results, and preventing the same target from being processed -# twice -# operation is a string to tell the function what additional behaviors to execute. -function(qt_internal_walk_libs - target out_var rcc_objects_out_var dict_name operation) - set(collected ${ARGN}) - if(target IN_LIST collected) - return() - endif() - list(APPEND collected ${target}) - - if(target STREQUAL "${QT_CMAKE_EXPORT_NAMESPACE}::EntryPoint") - # We can't (and don't need to) process EntryPoint because it brings in $ - # genexes which get replaced with $ genexes in the code below - # and that causes 'INTERFACE_LIBRARY targets may only have whitelisted properties.' errors - # with CMake versions equal to or lower than 3.18. These errors are super unintuitive to - # debug because there's no mention that it's happening during a file(GENERATE) call. - return() - endif() - - if(NOT TARGET ${dict_name}) - add_library(${dict_name} INTERFACE IMPORTED GLOBAL) - endif() - qt_internal_get_dict_key_values(libs "${target}" "${dict_name}" "libs") - qt_internal_get_dict_key_values(rcc_objects "${target}" "${dict_name}" "rcc_objects") - - if(libs MATCHES "-NOTFOUND$") - unset(libs) - unset(rcc_objects) - get_target_property(target_libs ${target} INTERFACE_LINK_LIBRARIES) - if(NOT target_libs) - unset(target_libs) - endif() - get_target_property(target_type ${target} TYPE) - if(target_type STREQUAL "STATIC_LIBRARY") - get_target_property(link_libs ${target} LINK_LIBRARIES) - if(link_libs) - list(APPEND target_libs ${link_libs}) - endif() - endif() - - # Need to record the rcc object file info not only for dependencies, but also for - # the current target too. Otherwise the saved information is incomplete for prl static - # build purposes. - get_target_property(main_target_rcc_objects ${target} _qt_rcc_objects) - if(main_target_rcc_objects) - qt_merge_libs(rcc_objects ${main_target_rcc_objects}) - endif() - - foreach(lib ${target_libs}) - # Cannot use $ in add_custom_command. - # Check the policy now, and replace the generator expression with the value. - while(lib MATCHES "\\$]+)>") - cmake_policy(GET ${CMAKE_MATCH_1} value) - if(value STREQUAL "NEW") - set(value "TRUE") - else() - set(value "FALSE") - endif() - string(REPLACE "${CMAKE_MATCH_0}" "${value}" lib "${lib}") - endwhile() - - # Fix up $ expressions that refer to the "current" target. - # Those cannot be used with add_custom_command. - while(lib MATCHES "\\$]+)>") - string(REPLACE "${CMAKE_MATCH_0}" "$" - lib "${lib}") - endwhile() - - # Skip static plugins. - set(_is_plugin_marker_genex "\\$") - if(lib MATCHES "${_is_plugin_marker_genex}") - continue() - endif() - - # Strip any directory scope tokens. - qt_internal_strip_target_directory_scope_token("${lib}" lib) - - if(lib MATCHES "^\\$$") - set(lib_target ${CMAKE_MATCH_1}) - else() - set(lib_target ${lib}) - endif() - - # Skip CMAKE_DIRECTORY_ID_SEP. If a target_link_libraries is applied to a target - # that was defined in a different scope, CMake appends and prepends a special directory - # id separator. Filter those out. - if(lib_target MATCHES "^::@") - continue() - elseif(TARGET ${lib_target}) - if ("${lib_target}" MATCHES "^Qt::(.*)") - # If both, Qt::Foo and Foo targets exist, prefer the target name without - # namespace. Which one is preferred doesn't really matter. This code exists to - # avoid ending up with both, Qt::Foo and Foo in our dependencies. - set(namespaceless_lib_target "${CMAKE_MATCH_1}") - if(TARGET namespaceless_lib_target) - set(lib_target ${namespaceless_lib_target}) - endif() - endif() - get_target_property(lib_target_type ${lib_target} TYPE) - if(lib_target_type STREQUAL "INTERFACE_LIBRARY") - qt_internal_walk_libs( - ${lib_target} - lib_libs_${target} - lib_rcc_objects_${target} - "${dict_name}" "${operation}" ${collected}) - if(lib_libs_${target}) - qt_merge_libs(libs ${lib_libs_${target}}) - set(is_module 0) - endif() - if(lib_rcc_objects_${target}) - qt_merge_libs(rcc_objects ${lib_rcc_objects_${target}}) - endif() - elseif(NOT lib_target_type STREQUAL "OBJECT_LIBRARY") - qt_merge_libs(libs "$") - - get_target_property(target_rcc_objects "${lib_target}" _qt_rcc_objects) - if(target_rcc_objects) - qt_merge_libs(rcc_objects ${target_rcc_objects}) - endif() - - qt_internal_walk_libs( - ${lib_target} - lib_libs_${target} - lib_rcc_objects_${target} - "${dict_name}" "${operation}" ${collected}) - if(lib_libs_${target}) - qt_merge_libs(libs ${lib_libs_${target}}) - endif() - if(lib_rcc_objects_${target}) - qt_merge_libs(rcc_objects ${lib_rcc_objects_${target}}) - endif() - endif() - if(operation STREQUAL "promote_global") - set(lib_target_unaliased "${lib_target}") - get_target_property(aliased_target ${lib_target} ALIASED_TARGET) - if(aliased_target) - set(lib_target_unaliased ${aliased_target}) - endif() - - get_property(is_imported TARGET ${lib_target_unaliased} PROPERTY IMPORTED) - get_property(is_global TARGET ${lib_target_unaliased} PROPERTY IMPORTED_GLOBAL) - - # Allow opting out of promotion. This is useful in certain corner cases - # like with WrapLibClang and Threads in qttools. - qt_internal_should_not_promote_package_target_to_global( - "${lib_target_unaliased}" should_not_promote) - if(NOT is_global AND is_imported AND NOT should_not_promote) - set_property(TARGET ${lib_target_unaliased} PROPERTY IMPORTED_GLOBAL TRUE) - endif() - endif() - elseif("${lib_target}" MATCHES "^Qt::(.*)") - message(FATAL_ERROR "The ${CMAKE_MATCH_1} target is mentioned as a dependency for \ -${target}, but not declared.") - else() - set(final_lib_name_to_merge "${lib_target}") - if(lib_target MATCHES "/([^/]+).framework$") - set(final_lib_name_to_merge "-framework ${CMAKE_MATCH_1}") - endif() - qt_merge_libs(libs "${final_lib_name_to_merge}") - endif() - endforeach() - qt_internal_memoize_values_in_dict("${target}" "${dict_name}" "libs" "${libs}") - qt_internal_memoize_values_in_dict("${target}" "${dict_name}" - "rcc_objects" "${rcc_objects}") - - endif() - set(${out_var} ${libs} PARENT_SCOPE) - set(${rcc_objects_out_var} ${rcc_objects} PARENT_SCOPE) -endfunction() - # Generate a qmake .prl file for the given target. # The install_dir argument is a relative path, for example "lib". function(qt_generate_prl_file target install_dir) diff --git a/cmake/QtPublicTargetHelpers.cmake b/cmake/QtPublicTargetHelpers.cmake new file mode 100644 index 0000000000..9ce8cbf700 --- /dev/null +++ b/cmake/QtPublicTargetHelpers.cmake @@ -0,0 +1,16 @@ +function(__qt_internal_strip_target_directory_scope_token target out_var) + # In CMake versions earlier than CMake 3.18, a subdirectory scope id is appended to the + # target name if the target is referenced in a target_link_libraries command from a + # different directory scope than where the target was created. + # Strip it. + # + # For informational purposes, in CMake 3.18, the target name looks as follows: + # ::@(0x5604cb3f6b50);Threads::Threads;::@ + # This case doesn't have to be stripped (at least for now), because when we iterate over + # link libraries, the tokens appear as separate target names. + # + # Example: Threads::Threads::@<0x5604cb3f6b50> + # Output: Threads::Threads + string(REGEX REPLACE "::@<.+>$" "" target "${target}") + set("${out_var}" "${target}" PARENT_SCOPE) +endfunction() diff --git a/cmake/QtPublicTargetsHelpers.cmake b/cmake/QtPublicTargetsHelpers.cmake deleted file mode 100644 index 68ff8a3ea0..0000000000 --- a/cmake/QtPublicTargetsHelpers.cmake +++ /dev/null @@ -1,33 +0,0 @@ -# Defines the public Qt::Platform target, which serves as a dependency for all internal Qt target -# as well as user projects consuming Qt. -function(qt_internal_setup_public_platform_target) - ## QtPlatform Target: - add_library(Platform INTERFACE) - add_library(Qt::Platform ALIAS Platform) - add_library(${INSTALL_CMAKE_NAMESPACE}::Platform ALIAS Platform) - target_include_directories(Platform - INTERFACE - $ - $ - $ - $ - ) - target_compile_definitions(Platform INTERFACE ${QT_PLATFORM_DEFINITIONS}) - - # When building on android we need to link against the logging library - # in order to satisfy linker dependencies. Both of these libraries are part of - # the NDK. - if (ANDROID) - target_link_libraries(Platform INTERFACE log) - endif() - - qt_set_msvc_cplusplus_options(Platform INTERFACE) - - # Propagate minimum C++ 17 via Platform to Qt consumers (apps), after the global features - # are computed. - qt_set_language_standards_interface_compile_features(Platform) - - # By default enable utf8 sources for both Qt and Qt consumers. Can be opted out. - qt_enable_utf8_sources(Platform) - -endfunction() diff --git a/cmake/QtPublicWalkLibsHelpers.cmake b/cmake/QtPublicWalkLibsHelpers.cmake new file mode 100644 index 0000000000..674a9da7a7 --- /dev/null +++ b/cmake/QtPublicWalkLibsHelpers.cmake @@ -0,0 +1,237 @@ +# Add libraries to variable ${out_libs_var} in a way that duplicates +# are added at the end. This ensures the library order needed for the +# linker. +function(__qt_internal_merge_libs out_libs_var) + foreach(dep ${ARGN}) + list(REMOVE_ITEM ${out_libs_var} ${dep}) + list(APPEND ${out_libs_var} ${dep}) + endforeach() + set(${out_libs_var} ${${out_libs_var}} PARENT_SCOPE) +endfunction() + + +# Extracts value from per-target dict key and assigns it to out_var. +# Assumes dict_name to be an existing INTERFACE target. +function(__qt_internal_get_dict_key_values out_var target_infix dict_name dict_key) + get_target_property(values "${dict_name}" "INTERFACE_${target_infix}_${dict_key}") + set(${out_var} "${values}" PARENT_SCOPE) +endfunction() + + +# Assigns 'values' to per-target dict key, including for aliases of the target. +# Assumes dict_name to be an existing INTERFACE target. +function(__qt_internal_memoize_values_in_dict target dict_name dict_key values) + # Memoize the computed values for the target as well as its aliases. + # + # Aka assigns the contents of ${values} to INTERFACE_Core, INTERFACE_Qt::Core, + # INTERFACE_Qt6::Core. + # + # Yes, i know it's crazy that target names are legal property names. + # + # Assigning for library aliases is needed to avoid multiple recomputation of values. + # Scenario in the context of __qt_internal_walk_libs: + # 'values' are computed for Core target and memoized to INTERFACE_Core. + # When processing Gui, it depends on Qt::Core, but there are no values for INTERFACE_Qt::Core. + set_target_properties(${dict_name} PROPERTIES INTERFACE_${target}_${dict_key} "${values}") + + get_target_property(versionless_alias "${target}" "_qt_versionless_alias") + if(versionless_alias) + __qt_internal_get_dict_key_values( + versionless_values "${versionless_alias}" "${dict_name}" "${dict_key}") + if(versionless_values MATCHES "-NOTFOUND$") + set_target_properties(${dict_name} + PROPERTIES INTERFACE_${versionless_alias}_${dict_key} "${values}") + endif() + endif() + + get_target_property(versionfull_alias "${target}" "_qt_versionfull_alias") + if(versionfull_alias) + __qt_internal_get_dict_key_values( + versionfull_values "${versionfull_alias}" "${dict_name}" "${dict_key}") + if(versionfull_values MATCHES "-NOTFOUND$") + set_target_properties(${dict_name} + PROPERTIES INTERFACE_${versionfull_alias}_${dict_key} "${values}") + endif() + endif() +endfunction() + + +# Walks a target's link libraries recursively, and performs some actions (poor man's polypmorphism) +# +# out_var is the name of the variable where the result will be assigned. The result is a list of +# libraries, mostly in generator expression form. +# rcc_objects_out_var is the name of the variable where the collected rcc object files will be +# assigned (for the initial target and its dependencies) +# dict_name is used for caching the results, and preventing the same target from being processed +# twice +# operation is a string to tell the function what additional behaviors to execute. +function(__qt_internal_walk_libs + target out_var rcc_objects_out_var dict_name operation) + set(collected ${ARGN}) + if(target IN_LIST collected) + return() + endif() + list(APPEND collected ${target}) + + if(target STREQUAL "${QT_CMAKE_EXPORT_NAMESPACE}::EntryPoint") + # We can't (and don't need to) process EntryPoint because it brings in $ + # genexes which get replaced with $ genexes in the code below + # and that causes 'INTERFACE_LIBRARY targets may only have whitelisted properties.' errors + # with CMake versions equal to or lower than 3.18. These errors are super unintuitive to + # debug because there's no mention that it's happening during a file(GENERATE) call. + return() + endif() + + if(NOT TARGET ${dict_name}) + add_library(${dict_name} INTERFACE IMPORTED GLOBAL) + endif() + __qt_internal_get_dict_key_values(libs "${target}" "${dict_name}" "libs") + __qt_internal_get_dict_key_values(rcc_objects "${target}" "${dict_name}" "rcc_objects") + + if(libs MATCHES "-NOTFOUND$") + unset(libs) + unset(rcc_objects) + get_target_property(target_libs ${target} INTERFACE_LINK_LIBRARIES) + if(NOT target_libs) + unset(target_libs) + endif() + get_target_property(target_type ${target} TYPE) + if(target_type STREQUAL "STATIC_LIBRARY") + get_target_property(link_libs ${target} LINK_LIBRARIES) + if(link_libs) + list(APPEND target_libs ${link_libs}) + endif() + endif() + + # Need to record the rcc object file info not only for dependencies, but also for + # the current target too. Otherwise the saved information is incomplete for prl static + # build purposes. + get_target_property(main_target_rcc_objects ${target} _qt_rcc_objects) + if(main_target_rcc_objects) + __qt_internal_merge_libs(rcc_objects ${main_target_rcc_objects}) + endif() + + foreach(lib ${target_libs}) + # Cannot use $ in add_custom_command. + # Check the policy now, and replace the generator expression with the value. + while(lib MATCHES "\\$]+)>") + cmake_policy(GET ${CMAKE_MATCH_1} value) + if(value STREQUAL "NEW") + set(value "TRUE") + else() + set(value "FALSE") + endif() + string(REPLACE "${CMAKE_MATCH_0}" "${value}" lib "${lib}") + endwhile() + + # Fix up $ expressions that refer to the "current" target. + # Those cannot be used with add_custom_command. + while(lib MATCHES "\\$]+)>") + string(REPLACE "${CMAKE_MATCH_0}" "$" + lib "${lib}") + endwhile() + + # Skip static plugins. + set(_is_plugin_marker_genex "\\$") + if(lib MATCHES "${_is_plugin_marker_genex}") + continue() + endif() + + # Strip any directory scope tokens. + __qt_internal_strip_target_directory_scope_token("${lib}" lib) + + if(lib MATCHES "^\\$$") + set(lib_target ${CMAKE_MATCH_1}) + else() + set(lib_target ${lib}) + endif() + + # Skip CMAKE_DIRECTORY_ID_SEP. If a target_link_libraries is applied to a target + # that was defined in a different scope, CMake appends and prepends a special directory + # id separator. Filter those out. + if(lib_target MATCHES "^::@") + continue() + elseif(TARGET ${lib_target}) + if ("${lib_target}" MATCHES "^Qt::(.*)") + # If both, Qt::Foo and Foo targets exist, prefer the target name without + # namespace. Which one is preferred doesn't really matter. This code exists to + # avoid ending up with both, Qt::Foo and Foo in our dependencies. + set(namespaceless_lib_target "${CMAKE_MATCH_1}") + if(TARGET namespaceless_lib_target) + set(lib_target ${namespaceless_lib_target}) + endif() + endif() + get_target_property(lib_target_type ${lib_target} TYPE) + if(lib_target_type STREQUAL "INTERFACE_LIBRARY") + __qt_internal_walk_libs( + ${lib_target} + lib_libs_${target} + lib_rcc_objects_${target} + "${dict_name}" "${operation}" ${collected}) + if(lib_libs_${target}) + __qt_internal_merge_libs(libs ${lib_libs_${target}}) + set(is_module 0) + endif() + if(lib_rcc_objects_${target}) + __qt_internal_merge_libs(rcc_objects ${lib_rcc_objects_${target}}) + endif() + elseif(NOT lib_target_type STREQUAL "OBJECT_LIBRARY") + __qt_internal_merge_libs(libs "$") + + get_target_property(target_rcc_objects "${lib_target}" _qt_rcc_objects) + if(target_rcc_objects) + __qt_internal_merge_libs(rcc_objects ${target_rcc_objects}) + endif() + + __qt_internal_walk_libs( + ${lib_target} + lib_libs_${target} + lib_rcc_objects_${target} + "${dict_name}" "${operation}" ${collected}) + if(lib_libs_${target}) + __qt_internal_merge_libs(libs ${lib_libs_${target}}) + endif() + if(lib_rcc_objects_${target}) + __qt_internal_merge_libs(rcc_objects ${lib_rcc_objects_${target}}) + endif() + endif() + if(operation STREQUAL "promote_global") + set(lib_target_unaliased "${lib_target}") + get_target_property(aliased_target ${lib_target} ALIASED_TARGET) + if(aliased_target) + set(lib_target_unaliased ${aliased_target}) + endif() + + get_property(is_imported TARGET ${lib_target_unaliased} PROPERTY IMPORTED) + get_property(is_global TARGET ${lib_target_unaliased} PROPERTY IMPORTED_GLOBAL) + + # Allow opting out of promotion. This is useful in certain corner cases + # like with WrapLibClang and Threads in qttools. + qt_internal_should_not_promote_package_target_to_global( + "${lib_target_unaliased}" should_not_promote) + if(NOT is_global AND is_imported AND NOT should_not_promote) + set_property(TARGET ${lib_target_unaliased} PROPERTY IMPORTED_GLOBAL TRUE) + endif() + endif() + elseif("${lib_target}" MATCHES "^Qt::(.*)") + message(FATAL_ERROR "The ${CMAKE_MATCH_1} target is mentioned as a dependency for \ +${target}, but not declared.") + else() + set(final_lib_name_to_merge "${lib_target}") + if(lib_target MATCHES "/([^/]+).framework$") + set(final_lib_name_to_merge "-framework ${CMAKE_MATCH_1}") + endif() + __qt_internal_merge_libs(libs "${final_lib_name_to_merge}") + endif() + endforeach() + __qt_internal_memoize_values_in_dict("${target}" "${dict_name}" "libs" "${libs}") + __qt_internal_memoize_values_in_dict("${target}" "${dict_name}" + "rcc_objects" "${rcc_objects}") + + endif() + set(${out_var} ${libs} PARENT_SCOPE) + set(${rcc_objects_out_var} ${rcc_objects} PARENT_SCOPE) +endfunction() diff --git a/cmake/QtTargetHelpers.cmake b/cmake/QtTargetHelpers.cmake index 6b9af27c7e..cc8bfde85f 100644 --- a/cmake/QtTargetHelpers.cmake +++ b/cmake/QtTargetHelpers.cmake @@ -264,23 +264,6 @@ function(qt_internal_check_directory_or_type name dir type default result_var) endif() endfunction() -function(qt_internal_strip_target_directory_scope_token target out_var) - # In CMake versions earlier than CMake 3.18, a subdirectory scope id is appended to the - # target name if the target is referenced in a target_link_libraries command from a - # different directory scope than where the target was created. - # Strip it. - # - # For informational purposes, in CMake 3.18, the target name looks as follows: - # ::@(0x5604cb3f6b50);Threads::Threads;::@ - # This case doesn't have to be stripped (at least for now), because when we iterate over - # link libraries, the tokens appear as separate target names. - # - # Example: Threads::Threads::@<0x5604cb3f6b50> - # Output: Threads::Threads - string(REGEX REPLACE "::@<.+>$" "" target "${target}") - set("${out_var}" "${target}" PARENT_SCOPE) -endfunction() - # Create a Qt*AdditionalTargetInfo.cmake file that is included by Qt*Config.cmake # and sets IMPORTED_*_ properties on the exported targets. # -- cgit v1.2.3