From 1bd408d6f7eac7908c77a0129e0173c280fce55b Mon Sep 17 00:00:00 2001 From: Alexandru Croitor Date: Wed, 19 May 2021 12:35:18 +0200 Subject: [PATCH] CMake: Introduce a public qt_add_library function Internally it uses a new _qt_internal_add_library function (similar how we have qt_add_executable and _qt_internal_add_executable) as well as finalizer code line the executable case. _qt_internal_add_library forwards arguments to add_library with some Qt specific adjustments to the selected default target type (based on whether Qt is configured as static or shared). The new _qt_internal_add_library is now used in qt_add_plugin as well as some internal library creating functions like qt_internal_add_module. This reduces some duplication of file name adjustments across functions and creates a central point for creation of Qt-like libraries (for some definition of Qt-like). Change-Id: Id9a31fe6bf278c8c3bb1e61e00a9febf7f1a2664 Reviewed-by: Joerg Bornemann Reviewed-by: Qt CI Bot --- cmake/Qt3rdPartyLibraryHelpers.cmake | 191 ++++++++++++++++----------- cmake/QtModuleHelpers.cmake | 31 +++-- src/corelib/Qt6CoreMacros.cmake | 136 ++++++++++++++++--- 3 files changed, 251 insertions(+), 107 deletions(-) diff --git a/cmake/Qt3rdPartyLibraryHelpers.cmake b/cmake/Qt3rdPartyLibraryHelpers.cmake index a2f35cf362f..b7fbea62c91 100644 --- a/cmake/Qt3rdPartyLibraryHelpers.cmake +++ b/cmake/Qt3rdPartyLibraryHelpers.cmake @@ -1,52 +1,96 @@ -# Wrapper function to create a regular cmake target and forward all the -# arguments collected by the conversion script. -function(qt_internal_add_cmake_library target) - # Process arguments: - qt_parse_all_arguments(arg "qt_add_cmake_library" - "SHARED;MODULE;STATIC;INTERFACE" - "OUTPUT_DIRECTORY;ARCHIVE_INSTALL_DIRECTORY;INSTALL_DIRECTORY" - "${__default_private_args};${__default_public_args}" +macro(qt_internal_get_add_library_option_args option_args) + set(${option_args} + SHARED + STATIC + MODULE + INTERFACE + ) +endmacro() + +# Helper to create a library using the public _qt_internal_add_library function. +# +# The difference to _qt_internal_add_library is that MODULE is replaced with STATIC in a static +# Qt build. +# Everything else is just prepation for option validating. +function(qt_internal_add_common_qt_library_helper target) + qt_internal_get_add_library_option_args(option_args) + qt_parse_all_arguments(arg "qt_internal_add_common_qt_library_helper" + "${option_args}" + "" + "" ${ARGN} ) - set(is_static_lib 0) - - ### Define Targets: - if(${arg_INTERFACE}) - add_library("${target}" INTERFACE) - elseif(${arg_STATIC} OR (${arg_MODULE} AND NOT BUILD_SHARED_LIBS)) - add_library("${target}" STATIC) - set(is_static_lib 1) - elseif(${arg_SHARED}) - add_library("${target}" SHARED) - _qt_internal_apply_win_prefix_and_suffix("${target}") - elseif(${arg_MODULE}) - add_library("${target}" MODULE) - set_property(TARGET ${name} PROPERTY C_VISIBILITY_PRESET default) - set_property(TARGET ${name} PROPERTY CXX_VISIBILITY_PRESET default) - set_property(TARGET ${name} PROPERTY OBJC_VISIBILITY_PRESET default) - set_property(TARGET ${name} PROPERTY OBJCXX_VISIBILITY_PRESET default) - - 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}") + if(arg_SHARED) + set(arg_SHARED SHARED) else() - add_library("${target}") - if(NOT BUILD_SHARED_LIBS) - set(is_static_lib 1) - endif() + set(arg_SHARED "") endif() + if(arg_MODULE) + set(arg_MODULE MODULE) + else() + set(arg_MODULE "") + endif() + + if(arg_STATIC) + set(arg_STATIC STATIC) + else() + set(arg_STATIC "") + endif() + + if(arg_INTERFACE) + set(arg_INTERFACE INTERFACE) + else() + set(arg_INTERFACE "") + endif() + + if(arg_MODULE AND NOT BUILD_SHARED_LIBS) + set(arg_MODULE STATIC) + endif() + + _qt_internal_add_library(${target} ${arg_STATIC} ${arg_SHARED} ${arg_MODULE} ${arg_INTERFACE}) +endfunction() + +# Wrapper function to create a regular cmake target and forward all the +# arguments collected by the conversion script. +function(qt_internal_add_cmake_library target) + qt_internal_get_add_library_option_args(option_args) + set(single_args + OUTPUT_DIRECTORY + ARCHIVE_INSTALL_DIRECTORY + INSTALL_DIRECTORY + ) + set(multi_args + ${__default_private_args} + ${__default_public_args} + ) + + qt_parse_all_arguments(arg "qt_add_cmake_library" + "${option_args}" + "${single_args}" + "${multi_args}" + ${ARGN} + ) + + qt_remove_args(library_helper_args + ARGS_TO_REMOVE + ${single_args} + ${multi_args} + ALL_ARGS + ${option_args} + ${single_args} + ${multi_args} + ARGS + ${ARGN} + ) + + qt_internal_add_common_qt_library_helper(${target} ${library_helper_args}) + if (NOT arg_ARCHIVE_INSTALL_DIRECTORY AND arg_INSTALL_DIRECTORY) set(arg_ARCHIVE_INSTALL_DIRECTORY "${arg_INSTALL_DIRECTORY}") endif() - if (ANDROID) - qt_android_apply_arch_suffix("${target}") - endif() qt_skip_warnings_are_errors_when_repo_unclean("${target}") if (arg_INSTALL_DIRECTORY) @@ -91,42 +135,43 @@ endfunction() # compile 3rdparty libraries as part of the build. # function(qt_internal_add_3rdparty_library target) - # Process arguments: - qt_parse_all_arguments(arg "qt_add_3rdparty_library" - "SHARED;MODULE;STATIC;INTERFACE;EXCEPTIONS;INSTALL;SKIP_AUTOMOC" - "OUTPUT_DIRECTORY;QMAKE_LIB_NAME" - "${__default_private_args};${__default_public_args}" + qt_internal_get_add_library_option_args(library_option_args) + set(option_args + EXCEPTIONS + INSTALL + SKIP_AUTOMOC + ) + set(single_args + OUTPUT_DIRECTORY + QMAKE_LIB_NAME + ) + set(multi_args + ${__default_private_args} + ${__default_public_args} + ) + + qt_parse_all_arguments(arg "qt_internal_add_3rdparty_library" + "${library_option_args};${option_args}" + "${single_args}" + "${multi_args}" ${ARGN} ) - set(is_static_lib 0) + qt_remove_args(library_helper_args + ARGS_TO_REMOVE + ${option_args} + ${single_args} + ${multi_args} + ALL_ARGS + ${library_option_args} + ${option_args} + ${single_args} + ${multi_args} + ARGS + ${ARGN} + ) - ### Define Targets: - if(${arg_INTERFACE}) - add_library("${target}" INTERFACE) - elseif(${arg_STATIC} OR (${arg_MODULE} AND NOT BUILD_SHARED_LIBS)) - add_library("${target}" STATIC) - set(is_static_lib 1) - elseif(${arg_SHARED}) - add_library("${target}" SHARED) - elseif(${arg_MODULE}) - add_library("${target}" MODULE) - set_property(TARGET ${name} PROPERTY C_VISIBILITY_PRESET default) - set_property(TARGET ${name} PROPERTY CXX_VISIBILITY_PRESET default) - set_property(TARGET ${name} PROPERTY OBJC_VISIBILITY_PRESET default) - set_property(TARGET ${name} PROPERTY OBJCXX_VISIBILITY_PRESET default) - - 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() - else() - add_library("${target}") - if(NOT BUILD_SHARED_LIBS) - set(is_static_lib 1) - endif() - endif() + qt_internal_add_common_qt_library_helper(${target} ${library_helper_args}) if(NOT arg_INTERFACE) qt_set_common_target_properties(${target}) @@ -143,10 +188,6 @@ function(qt_internal_add_3rdparty_library target) qt_internal_add_target_aliases(${target}) _qt_internal_apply_strict_cpp(${target}) - if (ANDROID) - qt_android_apply_arch_suffix("${target}") - endif() - qt_skip_warnings_are_errors_when_repo_unclean("${target}") set_target_properties(${target} PROPERTIES diff --git a/cmake/QtModuleHelpers.cmake b/cmake/QtModuleHelpers.cmake index 9704523f89c..f49bb4311c6 100644 --- a/cmake/QtModuleHelpers.cmake +++ b/cmake/QtModuleHelpers.cmake @@ -93,21 +93,30 @@ function(qt_internal_add_module target) endif() ### Define Targets: + if(arg_HEADER_MODULE) + set(type_to_create INTERFACE) + elseif(arg_STATIC) + set(type_to_create STATIC) + else() + # Use default depending on Qt configuration. + set(type_to_create "") + endif() + + _qt_internal_add_library("${target}" ${type_to_create}) + + get_target_property(target_type ${target} TYPE) + set(is_interface_lib 0) set(is_shared_lib 0) set(is_static_lib 0) - if(${arg_HEADER_MODULE}) - add_library("${target}" INTERFACE) + if(target_type STREQUAL "INTERFACE_LIBRARY") set(is_interface_lib 1) - elseif(${arg_STATIC}) - add_library("${target}" STATIC) + elseif(target_type STREQUAL "STATIC_LIBRARY") set(is_static_lib 1) - elseif(${QT_BUILD_SHARED_LIBS}) - add_library("${target}" SHARED) + elseif(target_type STREQUAL "SHARED_LIBRARY") set(is_shared_lib 1) else() - add_library("${target}" STATIC) - set(is_static_lib 1) + message(FATAL_ERROR "Invalid target type '${target_type}' for Qt module '${target}'") endif() set_target_properties(${target} PROPERTIES @@ -171,9 +180,6 @@ function(qt_internal_add_module target) target_compile_options(${target} PRIVATE -ffat-lto-objects) endif() - if (ANDROID) - qt_android_apply_arch_suffix("${target}") - endif() qt_internal_add_target_aliases("${target}") qt_skip_warnings_are_errors_when_repo_unclean("${target}") _qt_internal_apply_strict_cpp("${target}") @@ -248,8 +254,6 @@ function(qt_internal_add_module target) ) endif() - _qt_internal_apply_win_prefix_and_suffix("${target}") - if (WIN32 AND BUILD_SHARED_LIBS) _qt_internal_generate_win32_rc_file(${target}) endif() @@ -582,7 +586,6 @@ set(QT_CMAKE_EXPORT_NAMESPACE ${QT_CMAKE_EXPORT_NAMESPACE})") message(WARNING "GENERATE_METATYPES is on by default for Qt modules. Please remove the manual specification.") endif() if (NOT ${arg_NO_GENERATE_METATYPES}) - get_target_property(target_type ${target} TYPE) if (NOT target_type STREQUAL "INTERFACE_LIBRARY") set(metatypes_install_dir ${INSTALL_LIBDIR}/metatypes) set(args) diff --git a/src/corelib/Qt6CoreMacros.cmake b/src/corelib/Qt6CoreMacros.cmake index a46f1689dea..d4e8d948b15 100644 --- a/src/corelib/Qt6CoreMacros.cmake +++ b/src/corelib/Qt6CoreMacros.cmake @@ -1731,31 +1731,33 @@ function(qt6_add_plugin target) ) endif() - # If no explicit STATIC/SHARED option is set, default to the flavor of the Qt build. - if(QT6_IS_SHARED_LIBS_BUILD) - set(create_static_plugin FALSE) - else() - set(create_static_plugin TRUE) - endif() - # Explicit option takes priority over the computed default. if(arg_STATIC) set(create_static_plugin TRUE) elseif(arg_SHARED) set(create_static_plugin FALSE) + else() + # If no explicit STATIC/SHARED option is set, default to the flavor of the Qt build. + if(QT6_IS_SHARED_LIBS_BUILD) + set(create_static_plugin FALSE) + else() + set(create_static_plugin TRUE) + endif() endif() - if (create_static_plugin) - add_library(${target} STATIC) - target_compile_definitions(${target} PRIVATE QT_STATICPLUGIN) + # The default of _qt_internal_add_library creates SHARED in a shared Qt build, so we need to + # be explicit about the MODULE. + if(create_static_plugin) + set(type_to_create 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}) + set(type_to_create MODULE) + endif() + + _qt_internal_add_library(${target} ${type_to_create}) + + get_target_property(target_type "${target}" TYPE) + if (target_type STREQUAL "STATIC_LIBRARY") + target_compile_definitions(${target} PRIVATE QT_STATICPLUGIN) endif() set(output_name ${target}) @@ -1765,7 +1767,6 @@ function(qt6_add_plugin target) set_property(TARGET "${target}" PROPERTY OUTPUT_NAME "${output_name}") if (ANDROID) - qt6_android_apply_arch_suffix("${target}") set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_NAME "plugins_${arg_TYPE}_${output_name}" @@ -1796,6 +1797,105 @@ if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) endfunction() endif() +# Creates a library by forwarding arguments to add_library, applies some Qt naming file name naming +# conventions and ensures the execution of Qt specific finalizers. +function(qt6_add_library target) + cmake_parse_arguments(PARSE_ARGV 1 arg "MANUAL_FINALIZATION" "" "") + + _qt_internal_add_library("${target}" ${arg_UNPARSED_ARGUMENTS}) + + if(arg_MANUAL_FINALIZATION) + # Caller says they will call qt6_finalize_target() themselves later + return() + endif() + + # Defer the finalization if we can. When the caller's project requires + # CMake 3.19 or later, this makes the calls to this function concise while + # still allowing target property modification before finalization. + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19) + # Need to wrap in an EVAL CODE or else ${target} won't be evaluated + # due to special behavior of cmake_language() argument handling + cmake_language(EVAL CODE "cmake_language(DEFER CALL qt6_finalize_target ${target})") + else() + set_target_properties("${target}" PROPERTIES _qt_is_immediately_finalized TRUE) + qt6_finalize_target("${target}") + endif() +endfunction() + +# Creates a library target by forwarding the arguments to add_library. +# +# Applies some Qt specific behaviors: +# - If no type option is specified, rather than defaulting to STATIC it defaults to STATIC or SHARED +# depending on the Qt configuration. +# - Applies Qt specific prefixes and suffixes to file names depending on platform. +function(_qt_internal_add_library target) + set(opt_args + STATIC + SHARED + MODULE + INTERFACE + OBJECT + ) + set(single_args "") + set(multi_args "") + cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}") + + set(option_type_count 0) + if(arg_STATIC) + set(type_to_create STATIC) + math(EXPR option_type_count "${option_type_count}+1") + elseif(arg_SHARED) + set(type_to_create SHARED) + math(EXPR option_type_count "${option_type_count}+1") + elseif(arg_MODULE) + set(type_to_create MODULE) + math(EXPR option_type_count "${option_type_count}+1") + elseif(arg_INTERFACE) + set(type_to_create INTERFACE) + math(EXPR option_type_count "${option_type_count}+1") + elseif(arg_OBJECT) + set(type_to_create OBJECT) + math(EXPR option_type_count "${option_type_count}+1") + endif() + + if(option_type_count GREATER 1) + message(FATAL_ERROR + "Multiple type options were given. Only one should be used." + ) + endif() + + # If no explicit type option is set, default to the flavor of the Qt build. + # This in contrast to CMake which defaults to STATIC. + if(NOT arg_STATIC AND NOT arg_SHARED AND NOT arg_MODULE AND NOT arg_INTERFACE + AND NOT arg_OBJECT) + if(QT6_IS_SHARED_LIBS_BUILD) + set(type_to_create SHARED) + else() + set(type_to_create STATIC) + endif() + endif() + + add_library(${target} ${type_to_create} ${arg_UNPARSED_ARGUMENTS}) + + _qt_internal_apply_win_prefix_and_suffix("${target}") + + if(arg_MODULE AND 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() + + if(ANDROID) + qt6_android_apply_arch_suffix("${target}") + endif() +endfunction() + +if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS) + function(qt_add_library) + qt6_add_library(${ARGV}) + endfunction() +endif() + # By default Qt6 forces usage of utf8 sources for consumers of Qt. # Users can opt out of utf8 sources by calling this function with the target name of their # application or library.