diff --git a/cmake/QtExecutableHelpers.cmake b/cmake/QtExecutableHelpers.cmake index 916cb4d03c5..ef6800fa7b1 100644 --- a/cmake/QtExecutableHelpers.cmake +++ b/cmake/QtExecutableHelpers.cmake @@ -193,93 +193,92 @@ function(qt_internal_add_executable name) add_dependencies("${name}" qpa_default_plugins) endif() - if(NOT BUILD_SHARED_LIBS) - # For static builds, we need to explicitly link to plugins we want to be - # loaded with the executable. User projects get that automatically, but - # for tools built as part of Qt, we can't use that mechanism because it - # would pollute the targets we export as part of an install and lead to - # circular dependencies. The logic here is a simpler equivalent of the - # more dynamic logic in QtPlugins.cmake.in, but restricted to only - # adding plugins that are provided by the same module as the module - # libraries the executable links to. - set(libs - ${arg_LIBRARIES} - ${arg_PUBLIC_LIBRARIES} - ${extra_libraries} - Qt::PlatformCommonInternal - ) + # For static plugins, we need to explicitly link to plugins we want to be + # loaded with the executable. User projects get that automatically, but + # for tools built as part of Qt, we can't use that mechanism because it + # would pollute the targets we export as part of an install and lead to + # circular dependencies. The logic here is a simpler equivalent of the + # more dynamic logic in QtPlugins.cmake.in, but restricted to only + # adding plugins that are provided by the same module as the module + # libraries the executable links to. + set(libs + ${arg_LIBRARIES} + ${arg_PUBLIC_LIBRARIES} + ${extra_libraries} + Qt::PlatformCommonInternal + ) - set(deduped_libs "") - foreach(lib IN LISTS libs) - if(NOT TARGET "${lib}") - continue() + set(deduped_libs "") + foreach(lib IN LISTS libs) + if(NOT TARGET "${lib}") + continue() + endif() + + # Normalize module by stripping any leading "Qt::", because properties are set on the + # versioned target (either Gui when building the module, or Qt6::Gui when it's + # imported). + if(lib MATCHES "Qt::([-_A-Za-z0-9]+)") + set(new_lib "${QT_CMAKE_EXPORT_NAMESPACE}::${CMAKE_MATCH_1}") + if(TARGET "${new_lib}") + set(lib "${new_lib}") endif() + endif() - # Normalize module by stripping any leading "Qt::", because properties are set on the - # versioned target (either Gui when building the module, or Qt6::Gui when it's - # imported). - if(lib MATCHES "Qt::([-_A-Za-z0-9]+)") - set(new_lib "${QT_CMAKE_EXPORT_NAMESPACE}::${CMAKE_MATCH_1}") - if(TARGET "${new_lib}") - set(lib "${new_lib}") - endif() - endif() + # Unalias the target. + get_target_property(aliased_target ${lib} ALIASED_TARGET) + if(aliased_target) + set(lib ${aliased_target}) + endif() - # Unalias the target. - get_target_property(aliased_target ${lib} ALIASED_TARGET) - if(aliased_target) - set(lib ${aliased_target}) - endif() + list(APPEND deduped_libs "${lib}") + endforeach() - list(APPEND deduped_libs "${lib}") - endforeach() + list(REMOVE_DUPLICATES deduped_libs) - list(REMOVE_DUPLICATES deduped_libs) + foreach(lib IN LISTS deduped_libs) + string(MAKE_C_IDENTIFIER "${name}_plugin_imports_${lib}" out_file) + string(APPEND out_file .cpp) - foreach(lib IN LISTS deduped_libs) - string(MAKE_C_IDENTIFIER "${name}_plugin_imports_${lib}" out_file) - string(APPEND out_file .cpp) + # Initialize plugins that are built in the same repository as the Qt module 'lib'. + set(class_names_regular + "$>") - # Initialize plugins that are built in the same repository as the Qt module 'lib'. - set(class_names_regular - "$>") + # Initialize plugins that are built in the current Qt repository, but are associated + # with a Qt module from a different repository (qtsvg's QSvgPlugin associated with + # qtbase's QtGui). + string(MAKE_C_IDENTIFIER "${PROJECT_NAME}" current_project_name) + set(prop_prefix "_qt_repo_${current_project_name}") + set(class_names_current_project + "$>") - # Initialize plugins that are built in the current Qt repository, but are associated - # with a Qt module from a different repository (qtsvg's QSvgPlugin associated with - # qtbase's QtGui). - string(MAKE_C_IDENTIFIER "${PROJECT_NAME}" current_project_name) - set(prop_prefix "_qt_repo_${current_project_name}") - set(class_names_current_project - "$>") + # Only add separator if first list is not empty, so we don't trigger the file generation + # when all lists are empty. + set(class_names_separator "$<$>:;>" ) + set(class_names + "${class_names_regular}${class_names_separator}${class_names_current_project}") - # Only add separator if first list is not empty, so we don't trigger the file generation - # when all lists are empty. - set(class_names_separator "$<$>:;>" ) - set(class_names - "${class_names_regular}${class_names_separator}${class_names_current_project}") + set(out_file_path "${CMAKE_CURRENT_BINARY_DIR}/${out_file}") - set(out_file_path "${CMAKE_CURRENT_BINARY_DIR}/${out_file}") - - file(GENERATE OUTPUT "${out_file_path}" CONTENT + file(GENERATE OUTPUT "${out_file_path}" CONTENT "// This file is auto-generated. Do not edit. #include Q_IMPORT_PLUGIN($) " - CONDITION "$>" - ) + CONDITION "$>" + ) - # CMake versions earlier than 3.18.0 can't find the generated file for some reason, - # failing at generation phase. - # Explicitly marking the file as GENERATED fixes the issue. - set_source_files_properties("${out_file_path}" PROPERTIES GENERATED TRUE) + # CMake versions earlier than 3.18.0 can't find the generated file for some reason, + # failing at generation phase. + # Explicitly marking the file as GENERATED fixes the issue. + set_source_files_properties("${out_file_path}" PROPERTIES GENERATED TRUE) + + target_sources(${name} PRIVATE + "$<$>:${out_file_path}>" + ) + target_link_libraries(${name} PRIVATE + "$" + "$") + endforeach() - target_sources(${name} PRIVATE - "$<$>:${out_file_path}>" - ) - target_link_libraries(${name} PRIVATE - "$" - "$") - endforeach() - endif() endfunction() diff --git a/cmake/QtPluginHelpers.cmake b/cmake/QtPluginHelpers.cmake index f2c628a4040..d594702e3bc 100644 --- a/cmake/QtPluginHelpers.cmake +++ b/cmake/QtPluginHelpers.cmake @@ -203,70 +203,74 @@ function(qt_internal_add_plugin target) endif() get_target_property(is_imported_qt_module ${qt_module_target} IMPORTED) - # Associate plugin with its Qt module when both are both built in the same repository. - # Check that by comparing the PROJECT_NAME of each. - # This covers auto-linking of the majority of plugins to executables and in-tree tests. - # Linking of plugins in standalone tests (when the Qt module will be an imported target) - # is handled instead by the complicated genex logic in QtModulePlugins.cmake.in. - set(is_plugin_and_module_in_same_project FALSE) - if(NOT is_imported_qt_module) - # This QT_PLUGINS assignment is only used by QtPostProcessHelpers to decide if a - # QtModulePlugins.cmake file should be generated (which only happens in static builds). - set_property(TARGET "${qt_module_target}" APPEND PROPERTY QT_PLUGINS "${target}") + set(plugin_target_versioned "${QT_CMAKE_EXPORT_NAMESPACE}::${target}") + get_target_property(type "${plugin_target_versioned}" TYPE) + if(type STREQUAL STATIC_LIBRARY) + # Associate plugin with its Qt module when both are both built in the same repository. + # Check that by comparing the PROJECT_NAME of each. + # This covers auto-linking of the majority of plugins to executables and in-tree tests. + # Linking of plugins in standalone tests (when the Qt module will be an imported target) + # is handled instead by the complicated genex logic in QtModulePlugins.cmake.in. + set(is_plugin_and_module_in_same_project FALSE) + if(NOT is_imported_qt_module) + # This QT_PLUGINS assignment is only used by QtPostProcessHelpers to decide if a + # QtModulePlugins.cmake file should be generated (which only happens in static builds). + set_property(TARGET "${qt_module_target}" APPEND PROPERTY QT_PLUGINS "${target}") - get_target_property(module_source_dir ${qt_module_target} SOURCE_DIR) - get_directory_property(module_project_name - DIRECTORY ${module_source_dir} - DEFINITION PROJECT_NAME - ) - if(module_project_name STREQUAL PROJECT_NAME) - set(is_plugin_and_module_in_same_project TRUE) + get_target_property(module_source_dir ${qt_module_target} SOURCE_DIR) + get_directory_property(module_project_name + DIRECTORY ${module_source_dir} + DEFINITION PROJECT_NAME + ) + if(module_project_name STREQUAL PROJECT_NAME) + set(is_plugin_and_module_in_same_project TRUE) + endif() + + # When linking static plugins with the special logic in qt_internal_add_executable, + # make sure to skip non-default plugins. + if(is_plugin_and_module_in_same_project AND _default_plugin) + set_property(TARGET ${qt_module_target} APPEND PROPERTY + _qt_initial_repo_plugins + "${target}") + set_property(TARGET ${qt_module_target} APPEND PROPERTY + _qt_initial_repo_plugin_class_names + "$" + ) + endif() endif() - # When linking static plugins with the special logic in qt_internal_add_executable, - # make sure to skip non-default plugins. - if(is_plugin_and_module_in_same_project AND _default_plugin) + # Associate plugin with its Qt module when the plugin is built in the current repository + # but the module is built in a different repository (qtsvg's QSvgPlugin associated with + # qtbase's QtGui). + # The association is done in a separate property, to ensure that reconfiguring in-tree tests + # in qtbase doesn't accidentally cause linking to a plugin from a previously built qtsvg. + # Needed for in-tree tests like in qtsvg, qtimageformats. + # This is done for each Qt module regardless if it's an imported target or not, to handle + # both per-repo and top-level builds (in per-repo build of qtsvg QtGui is imported, in a + # top-level build Gui is not imported, but in both cases qtsvg tests need to link to + # QSvgPlugin). + # + # TODO: Top-level in-tree tests and qdeclarative per-repo in-tree tests that depend on + # static Qml plugins won't work due to the requirement of running qmlimportscanner + # at configure time, but qmlimportscanner is not built at that point. Moving the + # execution of qmlimportscanner to build time is non-trivial because qmlimportscanner + # not only generates a cpp file to compile but also outputs a list of static plugins + # that should be linked and there is no straightforward way to tell CMake to link + # against a list of libraries that was discovered at build time (apart from + # response files, which apparently might not work on all platforms). + # qmake doesn't have this problem because each project is configured separately so + # qmlimportscanner is always built by the time it needs to be run for a test. + if(NOT is_plugin_and_module_in_same_project AND _default_plugin) + string(MAKE_C_IDENTIFIER "${PROJECT_NAME}" current_project_name) + set(prop_prefix "_qt_repo_${current_project_name}") set_property(TARGET ${qt_module_target} APPEND PROPERTY - _qt_initial_repo_plugins - "${target}") + ${prop_prefix}_plugins "${target}") set_property(TARGET ${qt_module_target} APPEND PROPERTY - _qt_initial_repo_plugin_class_names - "$" + ${prop_prefix}_plugin_class_names + "$" ) endif() endif() - - # Associate plugin with its Qt module when the plugin is built in the current repository - # but the module is built in a different repository (qtsvg's QSvgPlugin associated with - # qtbase's QtGui). - # The association is done in a separate property, to ensure that reconfiguring in-tree tests - # in qtbase doesn't accidentally cause linking to a plugin from a previously built qtsvg. - # Needed for in-tree tests like in qtsvg, qtimageformats. - # This is done for each Qt module regardless if it's an imported target or not, to handle - # both per-repo and top-level builds (in per-repo build of qtsvg QtGui is imported, in a - # top-level build Gui is not imported, but in both cases qtsvg tests need to link to - # QSvgPlugin). - # - # TODO: Top-level in-tree tests and qdeclarative per-repo in-tree tests that depend on - # static Qml plugins won't work due to the requirement of running qmlimportscanner - # at configure time, but qmlimportscanner is not built at that point. Moving the - # execution of qmlimportscanner to build time is non-trivial because qmlimportscanner - # not only generates a cpp file to compile but also outputs a list of static plugins - # that should be linked and there is no straightforward way to tell CMake to link - # against a list of libraries that was discovered at build time (apart from - # response files, which apparently might not work on all platforms). - # qmake doesn't have this problem because each project is configured separately so - # qmlimportscanner is always built by the time it needs to be run for a test. - if(NOT is_plugin_and_module_in_same_project AND _default_plugin) - string(MAKE_C_IDENTIFIER "${PROJECT_NAME}" current_project_name) - set(prop_prefix "_qt_repo_${current_project_name}") - set_property(TARGET ${qt_module_target} APPEND PROPERTY - ${prop_prefix}_plugins "${target}") - set_property(TARGET ${qt_module_target} APPEND PROPERTY - ${prop_prefix}_plugin_class_names - "$" - ) - endif() endif() # Change the configuration file install location for qml plugins into the Qml package location. diff --git a/cmake/QtPublicPluginHelpers.cmake b/cmake/QtPublicPluginHelpers.cmake index 6dfaead7553..e0f1277762d 100644 --- a/cmake/QtPublicPluginHelpers.cmake +++ b/cmake/QtPublicPluginHelpers.cmake @@ -263,12 +263,16 @@ function(__qt_internal_collect_plugin_libraries plugin_targets out_var) set(plugins_to_link "") foreach(plugin_target ${plugin_targets}) + set(plugin_target_versioned "${QT_CMAKE_EXPORT_NAMESPACE}::${plugin_target}") + get_target_property(type "${plugin_target_versioned}" TYPE) + if(NOT type STREQUAL STATIC_LIBRARY) + continue() + endif() + __qt_internal_get_static_plugin_condition_genex( "${plugin_target}" plugin_condition) - set(plugin_target_versioned "${QT_CMAKE_EXPORT_NAMESPACE}::${plugin_target}") - list(APPEND plugins_to_link "$<${plugin_condition}:${plugin_target_versioned}>") endforeach() @@ -282,6 +286,12 @@ function(__qt_internal_collect_plugin_init_libraries plugin_targets out_var) set(plugin_inits_to_link "") foreach(plugin_target ${plugin_targets}) + set(plugin_target_versioned "${QT_CMAKE_EXPORT_NAMESPACE}::${plugin_target}") + get_target_property(type "${plugin_target_versioned}" TYPE) + if(NOT type STREQUAL STATIC_LIBRARY) + continue() + endif() + __qt_internal_get_static_plugin_condition_genex( "${plugin_target}" plugin_condition) @@ -376,11 +386,6 @@ endfunction() # Main logic of finalizer mode. function(__qt_internal_apply_plugin_imports_finalizer_mode target) - # Nothing to do in a shared build. - if(QT6_IS_SHARED_LIBS_BUILD) - return() - endif() - # Process a target only once. get_target_property(processed ${target} _qt_plugin_finalizer_imports_processed) if(processed) @@ -461,8 +466,10 @@ function(__qt_internal_include_plugin_packages target) list(APPEND "QT_ALL_PLUGINS_FOUND_BY_FIND_PACKAGE_${__plugin_type}" "${plugin_target}") - # Auto-linkage should be set up only for static library builds. - if(NOT QT6_IS_SHARED_LIBS_BUILD) + # Auto-linkage should be set up only for static plugins. + set(plugin_target_versioned "${QT_CMAKE_EXPORT_NAMESPACE}::${plugin_target}") + get_target_property(type "${plugin_target_versioned}" TYPE) + if(type STREQUAL STATIC_LIBRARY) __qt_internal_add_static_plugin_linkage("${plugin_target}" "${_module_target}") __qt_internal_add_static_plugin_import_macro( "${plugin_target}" ${_module_target} "${target}")