cmake: Link static plugins even in shared Qt builds
It's perfectly possible to create static plugins in an otherwise shared Qt build, but the logic to import these plugins into applications was assuming a fully static Qt build. We now handle this more granularly. This works for in-source tools and tests as well, which don't go through the same CMake machinery for plugins as user projects do. The only case that does not currently work is in-source examples, as they don't share any of the plugin machinery with neither Qt internal tools/tests or user project, but that's a bigger architectural issue that goes beyond this change. Change-Id: Ie00a97b02ac38ec4affadc447a3bfd0ec7d9c69a Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
This commit is contained in:
parent
5a4e5c62af
commit
f68e2c92cc
@ -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
|
||||
"$<GENEX_EVAL:$<TARGET_PROPERTY:${lib},_qt_initial_repo_plugin_class_names>>")
|
||||
|
||||
# Initialize plugins that are built in the same repository as the Qt module 'lib'.
|
||||
set(class_names_regular
|
||||
"$<GENEX_EVAL:$<TARGET_PROPERTY:${lib},_qt_initial_repo_plugin_class_names>>")
|
||||
# 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
|
||||
"$<GENEX_EVAL:$<TARGET_PROPERTY:${lib},${prop_prefix}_plugin_class_names>>")
|
||||
|
||||
# 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
|
||||
"$<GENEX_EVAL:$<TARGET_PROPERTY:${lib},${prop_prefix}_plugin_class_names>>")
|
||||
# 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 "$<$<NOT:$<STREQUAL:${class_names_regular},>>:;>" )
|
||||
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 "$<$<NOT:$<STREQUAL:${class_names_regular},>>:;>" )
|
||||
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 <QtPlugin>
|
||||
|
||||
Q_IMPORT_PLUGIN($<JOIN:${class_names},)\nQ_IMPORT_PLUGIN(>)
|
||||
"
|
||||
CONDITION "$<NOT:$<STREQUAL:${class_names},>>"
|
||||
)
|
||||
CONDITION "$<NOT:$<STREQUAL:${class_names},>>"
|
||||
)
|
||||
|
||||
# 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
|
||||
"$<$<NOT:$<STREQUAL:${class_names},>>:${out_file_path}>"
|
||||
)
|
||||
target_link_libraries(${name} PRIVATE
|
||||
"$<TARGET_PROPERTY:${lib},_qt_initial_repo_plugins>"
|
||||
"$<TARGET_PROPERTY:${lib},${prop_prefix}_plugins>")
|
||||
endforeach()
|
||||
|
||||
target_sources(${name} PRIVATE
|
||||
"$<$<NOT:$<STREQUAL:${class_names},>>:${out_file_path}>"
|
||||
)
|
||||
target_link_libraries(${name} PRIVATE
|
||||
"$<TARGET_PROPERTY:${lib},_qt_initial_repo_plugins>"
|
||||
"$<TARGET_PROPERTY:${lib},${prop_prefix}_plugins>")
|
||||
endforeach()
|
||||
endif()
|
||||
endfunction()
|
||||
|
@ -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
|
||||
"$<TARGET_PROPERTY:${target},QT_PLUGIN_CLASS_NAME>"
|
||||
)
|
||||
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
|
||||
"$<TARGET_PROPERTY:${target},QT_PLUGIN_CLASS_NAME>"
|
||||
${prop_prefix}_plugin_class_names
|
||||
"$<TARGET_PROPERTY:${target},QT_PLUGIN_CLASS_NAME>"
|
||||
)
|
||||
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
|
||||
"$<TARGET_PROPERTY:${target},QT_PLUGIN_CLASS_NAME>"
|
||||
)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Change the configuration file install location for qml plugins into the Qml package location.
|
||||
|
@ -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}")
|
||||
|
Loading…
Reference in New Issue
Block a user