qt5base-lts/cmake/QtPluginHelpers.cmake

364 lines
15 KiB
CMake
Raw Normal View History

# This is the main entry point for defining Qt plugins.
# A CMake target is created with the given target. The TYPE parameter is needed to place the
# plugin into the correct plugins/ sub-directory.
function(qt_internal_add_plugin target)
qt_internal_module_info(module "${target}")
qt_internal_set_qt_known_plugins("${QT_KNOWN_PLUGINS}" "${target}")
qt_parse_all_arguments(arg "qt_internal_add_plugin"
"${__qt_add_plugin_optional_args};SKIP_INSTALL"
"${__qt_add_plugin_single_args}"
"${__qt_add_plugin_multi_args}"
"${ARGN}"
)
# Put this behind a cache option for now. It's too noisy for general use
# until most repos are updated.
option(QT_WARN_PLUGIN_PUBLIC_KEYWORDS "Warn if a plugin specifies a PUBLIC keyword")
if(QT_WARN_PLUGIN_PUBLIC_KEYWORDS)
foreach(publicKeyword IN LISTS __default_public_args)
if(NOT "${arg_${publicKeyword}}" STREQUAL "")
string(REPLACE "PUBLIC_" "" privateKeyword "${publicKeyword}")
message(AUTHOR_WARNING
"Plugins are not intended to be linked to. "
"They should not have any public properties, but ${target} "
"sets ${publicKeyword} to the following value:\n"
" ${arg_${publicKeyword}}\n"
"Update your project to use ${privateKeyword} instead.\n")
endif()
endforeach()
endif()
qt_get_sanitized_plugin_type("${arg_TYPE}" plugin_type_escaped)
set(output_directory_default "${QT_BUILD_DIR}/${INSTALL_PLUGINSDIR}/${arg_TYPE}")
set(install_directory_default "${INSTALL_PLUGINSDIR}/${arg_TYPE}")
# Derive the class name from the target name if it's not explicitly specified.
# Don't set it for qml plugins though.
set(plugin_class_name "")
if (NOT "${plugin_type_escaped}" STREQUAL "qml_plugin")
if (NOT arg_CLASS_NAME)
set(plugin_class_name "${target}")
else()
set(plugin_class_name "${arg_CLASS_NAME}")
endif()
endif()
qt_internal_check_directory_or_type(OUTPUT_DIRECTORY "${arg_OUTPUT_DIRECTORY}" "${arg_TYPE}"
"${output_directory_default}" output_directory)
if (NOT arg_SKIP_INSTALL)
qt_internal_check_directory_or_type(INSTALL_DIRECTORY "${arg_INSTALL_DIRECTORY}" "${arg_TYPE}"
"${install_directory_default}" install_directory)
set(archive_install_directory ${arg_ARCHIVE_INSTALL_DIRECTORY})
if (NOT archive_install_directory AND install_directory)
set(archive_install_directory "${install_directory}")
endif()
endif()
if(arg_STATIC OR NOT BUILD_SHARED_LIBS)
add_library("${target}" STATIC)
else()
add_library("${target}" MODULE)
if(APPLE)
# CMake defaults to using .so extensions for loadable modules, aka plugins,
# but Qt plugins are actually suffixed with .dylib.
set_property(TARGET "${target}" PROPERTY SUFFIX ".dylib")
endif()
qt_internal_apply_win_prefix_and_suffix("${target}")
endif()
qt_set_common_target_properties(${target})
qt_set_target_info_properties(${target} ${ARGN} TARGET_VERSION "${arg_VERSION}")
# Make sure the Qt6 plugin library names are like they were in Qt5 qmake land.
# Whereas the Qt6 CMake target names are like the Qt5 CMake target names.
set(output_name "${target}")
if(arg_OUTPUT_NAME)
set(output_name "${arg_OUTPUT_NAME}")
endif()
set_property(TARGET "${target}" PROPERTY OUTPUT_NAME "${output_name}${QT_LIBINFIX}")
# Add a custom target with the Qt5 qmake name for a more user friendly ninja experience.
if(arg_OUTPUT_NAME AND NOT TARGET "${output_name}")
# But don't create such a target if it would just differ in case from "${target}"
# and we're not using Ninja. See https://gitlab.kitware.com/cmake/cmake/-/issues/21915
string(TOUPPER "${output_name}" uc_output_name)
string(TOUPPER "${target}" uc_target)
if(NOT uc_output_name STREQUAL uc_target OR CMAKE_GENERATOR MATCHES "^Ninja")
add_custom_target("${output_name}")
add_dependencies("${output_name}" "${target}")
endif()
endif()
if (ANDROID)
qt_android_apply_arch_suffix("${target}")
set_target_properties(${target}
PROPERTIES
LIBRARY_OUTPUT_NAME "plugins_${arg_TYPE}_${output_name}"
)
endif()
qt_internal_add_target_aliases("${target}")
qt_skip_warnings_are_errors_when_repo_unclean("${target}")
_qt_internal_apply_strict_cpp("${target}")
# Disable linking of plugins against other plugins during static regular and
# super builds. The latter causes cyclic dependencies otherwise.
_qt_internal_disable_static_default_plugins("${target}")
set_target_properties("${target}" PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${output_directory}"
RUNTIME_OUTPUT_DIRECTORY "${output_directory}"
ARCHIVE_OUTPUT_DIRECTORY "${output_directory}"
QT_PLUGIN_TYPE "${plugin_type_escaped}"
# Save the non-sanitized plugin type values for qmake consumption via .pri files.
QT_QMAKE_PLUGIN_TYPE "${arg_TYPE}"
QT_PLUGIN_CLASS_NAME "${plugin_class_name}")
qt_handle_multi_config_output_dirs("${target}")
qt_internal_library_deprecation_level(deprecation_define)
qt_autogen_tools_initial_setup(${target})
set(static_plugin_define "")
if (arg_STATIC OR NOT QT_BUILD_SHARED_LIBS)
set(static_plugin_define "QT_STATICPLUGIN")
endif()
# Save the Qt module in the plug-in's properties
if(NOT plugin_type_escaped STREQUAL "qml_plugin")
qt_internal_get_module_for_plugin("${target}" "${plugin_type_escaped}" qt_module)
set_target_properties("${target}" PROPERTIES QT_MODULE "${qt_module}")
set(plugin_install_package_suffix "${qt_module}")
endif()
# Add the plug-in to the list of plug-ins of this module
if(TARGET "${qt_module}")
set_property(TARGET "${qt_module}" APPEND PROPERTY QT_PLUGINS "${target}")
Add plugins to Qt tools and executables for static builds In static builds, we cannot allow any part of the main build to make a call to find_package(Qt6...) where such a call may load a Qt6*Plugins.cmake file. That would add additional dependencies to the main module targets, setting up a circular dependency in the set of *Config.cmake files which cannot be resolved. This scenario would be triggered by per-repo builds or user projects. But Qt's tools and other executables still need to load some plugins in static builds. Sometimes a platform plugin may be enough, other times we may want all supportable plugins (e.g. Qt Designer). Therefore, add all plugins we can identify as relevant for an executable that is part of the Qt build, but add them directly to the executable without affecting the linking relationships between the main module libraries. Also remove the now unnecessary check for QT_BUILD_PROJECT_NAME in top level builds because there should be no difference between per-repo and top level builds any more (as far as linking static plugins is concerned). Examples that build as part of the main build will still build successfully after this change, but they will not run if they require a platform plugin. Examples need to be moved out to a separate build where they can call find_package(Qt6) without QT_NO_CREATE_TARGETS set to TRUE to be runnable (see QTBUG-90820). Fixes: QTBUG-91915 Pick-to: 6.1 Change-Id: I8088baddb54e394ca111b103313596d6743570ba Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
2021-03-19 09:04:23 +00:00
get_target_property(module_source_dir ${qt_module} SOURCE_DIR)
get_directory_property(module_project_name
DIRECTORY ${module_source_dir}
DEFINITION PROJECT_NAME
)
if(module_project_name STREQUAL PROJECT_NAME)
set_property(TARGET ${qt_module} APPEND PROPERTY QT_REPO_PLUGINS "${target}")
set_property(TARGET ${qt_module} APPEND PROPERTY QT_REPO_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.
if(plugin_type_escaped STREQUAL "qml_plugin" AND TARGET "${INSTALL_CMAKE_NAMESPACE}::Qml")
set(plugin_install_package_suffix "Qml/QmlPlugins")
endif()
# Save the install package suffix as a property, so that the Dependencies file is placed
# in the current location.
if(plugin_install_package_suffix)
set_target_properties("${target}" PROPERTIES
_qt_plugin_install_package_suffix "${plugin_install_package_suffix}")
endif()
# The generic plugins should be enabled by default.
# But platform plugins should always be disabled by default, and only one is enabled
# based on the platform (condition specified in arg_DEFAULT_IF).
if(plugin_type_escaped STREQUAL "platforms")
set(_default_plugin 0)
else()
set(_default_plugin 1)
endif()
if (DEFINED arg_DEFAULT_IF)
if (NOT ${arg_DEFAULT_IF})
set(_default_plugin 0)
else()
set(_default_plugin 1)
endif()
endif()
add_dependencies(qt_plugins "${target}")
if(arg_TYPE STREQUAL "platforms")
add_dependencies(qpa_plugins "${target}")
if(_default_plugin)
add_dependencies(qpa_default_plugins "${target}")
endif()
endif()
set_property(TARGET "${target}" PROPERTY QT_DEFAULT_PLUGIN "${_default_plugin}")
set_property(TARGET "${target}" APPEND PROPERTY EXPORT_PROPERTIES "QT_PLUGIN_CLASS_NAME;QT_PLUGIN_TYPE;QT_MODULE;QT_DEFAULT_PLUGIN")
set(private_includes
"${CMAKE_CURRENT_SOURCE_DIR}"
"${CMAKE_CURRENT_BINARY_DIR}"
# For the syncqt headers
"$<BUILD_INTERFACE:${module_repo_include_dir}>"
${arg_INCLUDE_DIRECTORIES}
)
set(public_includes
${arg_PUBLIC_INCLUDE_DIRECTORIES}
)
qt_internal_extend_target("${target}"
SOURCES ${arg_SOURCES}
INCLUDE_DIRECTORIES
${private_includes}
PUBLIC_INCLUDE_DIRECTORIES
${public_includes}
LIBRARIES ${arg_LIBRARIES} Qt::PlatformPluginInternal
PUBLIC_LIBRARIES ${arg_PUBLIC_LIBRARIES}
DEFINES
${arg_DEFINES}
QT_DEPRECATED_WARNINGS
${deprecation_define}
"${static_plugin_define}"
QT_PLUGIN
PUBLIC_DEFINES
${arg_PUBLIC_DEFINES}
FEATURE_DEPENDENCIES ${arg_FEATURE_DEPENDENCIES}
DBUS_ADAPTOR_SOURCES "${arg_DBUS_ADAPTOR_SOURCES}"
DBUS_ADAPTOR_FLAGS "${arg_DBUS_ADAPTOR_FLAGS}"
DBUS_INTERFACE_SOURCES "${arg_DBUS_INTERFACE_SOURCES}"
DBUS_INTERFACE_FLAGS "${arg_DBUS_INTERFACE_FLAGS}"
COMPILE_OPTIONS ${arg_COMPILE_OPTIONS}
PUBLIC_COMPILE_OPTIONS ${arg_PUBLIC_COMPILE_OPTIONS}
LINK_OPTIONS ${arg_LINK_OPTIONS}
PUBLIC_LINK_OPTIONS ${arg_PUBLIC_LINK_OPTIONS}
MOC_OPTIONS ${arg_MOC_OPTIONS}
ENABLE_AUTOGEN_TOOLS ${arg_ENABLE_AUTOGEN_TOOLS}
DISABLE_AUTOGEN_TOOLS ${arg_DISABLE_AUTOGEN_TOOLS}
)
qt_internal_set_exceptions_flags("${target}" ${arg_EXCEPTIONS})
set(qt_libs_private "")
qt_internal_get_qt_all_known_modules(known_modules)
foreach(it ${known_modules})
list(FIND arg_LIBRARIES "Qt::${it}Private" pos)
if(pos GREATER -1)
list(APPEND qt_libs_private "Qt::${it}Private")
endif()
endforeach()
qt_register_target_dependencies("${target}" "${arg_PUBLIC_LIBRARIES}" "${qt_libs_private}")
if (NOT BUILD_SHARED_LIBS)
qt_generate_plugin_pri_file("${target}" pri_file)
endif()
if (NOT arg_SKIP_INSTALL)
# Handle creation of cmake files for consumers of find_package().
# If we are part of a Qt module, the plugin cmake files are installed as part of that
# module.
# For qml plugins, they are all installed into the QtQml package location for automatic
# discovery.
if(plugin_install_package_suffix)
set(path_suffix "${INSTALL_CMAKE_NAMESPACE}${plugin_install_package_suffix}")
else()
set(path_suffix "${INSTALL_CMAKE_NAMESPACE}${target}")
endif()
qt_path_join(config_build_dir ${QT_CONFIG_BUILD_DIR} ${path_suffix})
qt_path_join(config_install_dir ${QT_CONFIG_INSTALL_DIR} ${path_suffix})
qt_internal_export_additional_targets_file(
TARGETS ${target}
EXPORT_NAME_PREFIX ${INSTALL_CMAKE_NAMESPACE}${target}
CONFIG_INSTALL_DIR "${config_install_dir}")
CMake: Enable NEW policies by CMake version with a global default When a CMake release introduces a new policy that affects most Qt modules, it may be appropriate to make each module aware of that newer CMake version and use the NEW policy without raising the minimum CMake version requirement. To reduce the churn associated with making that change across all Qt modules individually, this change allows it to be updated in a central place (qtbase), but in a way that allows a Qt module to override it in its own .cmake.conf file if required (e.g. to address the issues identified by policy warnings at a later time). The policies are modified at the start of the call to qt_build_repo_begin(). For commands defined by the qtbase module, qtbase needs to be in control of the policy settings at the point where those commands are defined. The above mechanism should not affect the policy settings for these commands, so the various *Config.cmake.in files must not specify policy ranges in a way that a Qt module's .cmake.conf file could influence. Starting with CMake 3.12, policies can be specified as a version range with the cmake_minimum_required() and cmake_policy() commands. All policies introduced in CMake versions up to the upper limit of that range will be set to NEW. The actual version of CMake being used only has to be at least the lower limit of the specified version range. This change uses cmake_minimum_required() rather than cmake_policy() due to the latter not halting further processing upon failure. See the following: https://gitlab.kitware.com/cmake/cmake/-/issues/21557 Task-number: QTBUG-88700 Pick-to: 6.0 Change-Id: I0a1f2611dd629f847a18186394f500d7f52753bc Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
2020-11-30 07:46:49 +00:00
qt_internal_get_min_new_policy_cmake_version(min_new_policy_version)
qt_internal_get_max_new_policy_cmake_version(max_new_policy_version)
configure_package_config_file(
"${QT_CMAKE_DIR}/QtPluginConfig.cmake.in"
"${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}Config.cmake"
INSTALL_DESTINATION "${config_install_dir}"
)
write_basic_package_version_file(
"${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersion.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY AnyNewerVersion
)
qt_install(FILES
"${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}Config.cmake"
"${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersion.cmake"
DESTINATION "${config_install_dir}"
COMPONENT Devel
)
if(pri_file)
qt_install(FILES "${pri_file}" DESTINATION "${INSTALL_MKSPECSDIR}/modules")
endif()
# Make the export name of plugins be consistent with modules, so that
# qt_add_resource adds its additional targets to the same export set in a static Qt build.
set(export_name "${INSTALL_CMAKE_NAMESPACE}${target}Targets")
qt_install(TARGETS "${target}"
EXPORT ${export_name}
RUNTIME DESTINATION "${install_directory}"
LIBRARY DESTINATION "${install_directory}"
ARCHIVE DESTINATION "${archive_install_directory}"
)
qt_install(EXPORT ${export_name}
NAMESPACE ${QT_CMAKE_EXPORT_NAMESPACE}::
DESTINATION "${config_install_dir}"
)
qt_apply_rpaths(TARGET "${target}" INSTALL_PATH "${install_directory}" RELATIVE_RPATH)
endif()
if (NOT arg_ALLOW_UNDEFINED_SYMBOLS)
### fixme: cmake is missing a built-in variable for this. We want to apply it only to
# modules and plugins that belong to Qt.
qt_internal_add_link_flags_no_undefined("${target}")
endif()
qt_internal_add_linker_version_script(${target})
qt_add_list_file_finalizer(qt_finalize_plugin ${target} "${install_directory}")
qt_enable_separate_debug_info(${target} "${install_directory}")
qt_internal_install_pdb_files(${target} "${install_directory}")
endfunction()
function(qt_finalize_plugin target install_directory)
if(WIN32 AND BUILD_SHARED_LIBS)
_qt_internal_generate_win32_rc_file("${target}")
endif()
# Generate .prl files for plugins of static Qt builds.
if(NOT BUILD_SHARED_LIBS)
qt_generate_prl_file(${target} "${install_directory}")
endif()
endfunction()
function(qt_get_sanitized_plugin_type plugin_type out_var)
# Used to handle some edge cases such as platforms/darwin
string(REGEX REPLACE "[-/]" "_" plugin_type "${plugin_type}")
set("${out_var}" "${plugin_type}" PARENT_SCOPE)
endfunction()
# Utility function to find the module to which a plug-in belongs.
function(qt_internal_get_module_for_plugin target target_type out_var)
qt_internal_get_qt_all_known_modules(known_modules)
qt_get_sanitized_plugin_type("${target_type}" target_type)
foreach(qt_module ${known_modules})
get_target_property(module_type "${QT_CMAKE_EXPORT_NAMESPACE}::${qt_module}" TYPE)
# Assuming interface libraries can't have plugins. Otherwise we'll need to fix the property
# name, because the current one would be invalid for interface libraries.
if(module_type STREQUAL "INTERFACE_LIBRARY")
continue()
endif()
get_target_property(plugin_types
"${QT_CMAKE_EXPORT_NAMESPACE}::${qt_module}"
MODULE_PLUGIN_TYPES)
if(plugin_types AND target_type IN_LIST plugin_types)
set("${out_var}" "${qt_module}" PARENT_SCOPE)
return()
endif()
endforeach()
message(FATAL_ERROR "The plug-in '${target}' does not belong to any Qt module.")
endfunction()