The function append the unique entries to the CMAKE_CONFIGURE_DEPENDS property. This suppress the ninja recompat issue, which complains about the duplicated entries in that come from the CMAKE_CONFIGURE_DEPENDS property. It's likely the CMake issue, but we may work around it. Pick-to: 6.8 6.9 6.10 Change-Id: I2f10834b0dca3d2aa08fe13fba69849e97fa77d0 Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
971 lines
37 KiB
CMake
971 lines
37 KiB
CMake
# Copyright (C) 2022 The Qt Company Ltd.
|
|
# SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
# copy_if_different works incorrect in Windows if file size if bigger than 2GB.
|
|
# See https://gitlab.kitware.com/cmake/cmake/-/issues/23052 and QTBUG-99491 for details.
|
|
function(_qt_internal_copy_file_if_different_command out_var src_file dst_file)
|
|
# The CMake version higher than 3.23 doesn't contain the issue
|
|
if(CMAKE_HOST_WIN32 AND CMAKE_VERSION VERSION_LESS 3.23)
|
|
set(${out_var} "${CMAKE_COMMAND}"
|
|
"-DSRC_FILE_PATH=${src_file}"
|
|
"-DDST_FILE_PATH=${dst_file}"
|
|
-P "${_qt_6_config_cmake_dir}/QtCopyFileIfDifferent.cmake"
|
|
PARENT_SCOPE
|
|
)
|
|
else()
|
|
set(${out_var} "${CMAKE_COMMAND}"
|
|
-E copy_if_different
|
|
"${src_file}"
|
|
"${dst_file}"
|
|
PARENT_SCOPE
|
|
)
|
|
endif()
|
|
endfunction()
|
|
|
|
# The function checks if add_custom_command has the support of the DEPFILE argument.
|
|
function(_qt_internal_check_depfile_support out_var)
|
|
if(CMAKE_GENERATOR MATCHES "Ninja" OR
|
|
(CMAKE_VERSION VERSION_GREATER_EQUAL 3.20 AND CMAKE_GENERATOR MATCHES "Makefiles")
|
|
OR (CMAKE_VERSION VERSION_GREATER_EQUAL 3.21
|
|
AND (CMAKE_GENERATOR MATCHES "Xcode"
|
|
OR (CMAKE_GENERATOR MATCHES "Visual Studio ([0-9]+)" AND CMAKE_MATCH_1 GREATER_EQUAL 12)
|
|
)
|
|
)
|
|
)
|
|
set(${out_var} TRUE)
|
|
else()
|
|
set(${out_var} FALSE)
|
|
endif()
|
|
set(${out_var} "${${out_var}}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Checks if the path points to the cmake directory, like lib/cmake.
|
|
function(__qt_internal_check_path_points_to_cmake_dir result path)
|
|
string(TOUPPER "${QT_CMAKE_EXPORT_NAMESPACE}" export_namespace_upper)
|
|
if((INSTALL_LIBDIR AND path MATCHES "/${INSTALL_LIBDIR}/cmake$") OR
|
|
(${export_namespace_upper}_INSTALL_LIBS AND
|
|
path MATCHES "/${${export_namespace_upper}_INSTALL_LIBS}/cmake$") OR
|
|
path MATCHES "/lib/cmake$"
|
|
)
|
|
set(${result} TRUE PARENT_SCOPE)
|
|
else()
|
|
set(${result} FALSE PARENT_SCOPE)
|
|
endif()
|
|
endfunction()
|
|
|
|
# Creates a reverse path to prefix from possible cmake directories. Returns the unchanged path
|
|
# if it doesn't point to cmake directory.
|
|
function(__qt_internal_reverse_prefix_path_from_cmake_dir result cmake_path)
|
|
string(TOUPPER "${QT_CMAKE_EXPORT_NAMESPACE}" export_namespace_upper)
|
|
if(INSTALL_LIBDIR AND cmake_path MATCHES "(.+)/${INSTALL_LIBDIR}/cmake$")
|
|
if(CMAKE_MATCH_1)
|
|
set(${result} "${CMAKE_MATCH_1}" PARENT_SCOPE)
|
|
endif()
|
|
elseif(${export_namespace_upper}_INSTALL_LIBS AND
|
|
cmake_path MATCHES "(.+)/${${export_namespace_upper}_INSTALL_LIBS}/cmake$")
|
|
if(CMAKE_MATCH_1)
|
|
set(${result} "${CMAKE_MATCH_1}" PARENT_SCOPE)
|
|
endif()
|
|
elseif(result MATCHES "(.+)/lib/cmake$")
|
|
if(CMAKE_MATCH_1)
|
|
set(${result} "${CMAKE_MATCH_1}" PARENT_SCOPE)
|
|
endif()
|
|
else()
|
|
set(${result} "${cmake_path}" PARENT_SCOPE)
|
|
endif()
|
|
endfunction()
|
|
|
|
# Returns the possible cmake directories based on prefix_path.
|
|
function(__qt_internal_get_possible_cmake_dirs out_paths prefix_path)
|
|
set(${out_paths} "")
|
|
|
|
if(EXISTS "${prefix_path}/lib/cmake")
|
|
list(APPEND ${out_paths} "${prefix_path}/lib/cmake")
|
|
endif()
|
|
|
|
string(TOUPPER "${QT_CMAKE_EXPORT_NAMESPACE}" export_namespace_upper)
|
|
set(next_path "${prefix_path}/${${export_namespace_upper}_INSTALL_LIBS}/cmake")
|
|
if(${export_namespace_upper}_INSTALL_LIBS AND EXISTS "${next_path}")
|
|
list(APPEND ${out_paths} "${next_path}")
|
|
endif()
|
|
|
|
set(next_path "${prefix_path}/${INSTALL_LIBDIR}/cmake")
|
|
if(INSTALL_LIBDIR AND EXISTS "${next_path}")
|
|
list(APPEND ${out_paths} "${next_path}")
|
|
endif()
|
|
|
|
list(REMOVE_DUPLICATES ${out_paths})
|
|
set(${out_paths} "${${out_paths}}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Collect additional package prefix paths to look for Qt packages, both from command line and the
|
|
# env variable ${prefixes_var}. The result is stored in ${out_var} and is a list of paths ending
|
|
# with "/lib/cmake".
|
|
function(__qt_internal_collect_additional_prefix_paths out_var prefixes_var)
|
|
if(DEFINED "${out_var}")
|
|
return()
|
|
endif()
|
|
|
|
set(additional_packages_prefix_paths "")
|
|
|
|
set(additional_packages_prefixes "")
|
|
if(${prefixes_var})
|
|
list(APPEND additional_packages_prefixes ${${prefixes_var}})
|
|
endif()
|
|
if(DEFINED ENV{${prefixes_var}}
|
|
AND NOT "$ENV{${prefixes_var}}" STREQUAL "")
|
|
set(prefixes_from_env "$ENV{${prefixes_var}}")
|
|
if(NOT CMAKE_HOST_WIN32)
|
|
string(REPLACE ":" ";" prefixes_from_env "${prefixes_from_env}")
|
|
endif()
|
|
list(APPEND additional_packages_prefixes ${prefixes_from_env})
|
|
endif()
|
|
|
|
foreach(additional_path IN LISTS additional_packages_prefixes)
|
|
file(TO_CMAKE_PATH "${additional_path}" additional_path)
|
|
|
|
# The prefix paths need to end with lib/cmake to ensure the packages are found when
|
|
# cross compiling. Search for REROOT_PATH_ISSUE_MARKER in the qt.toolchain.cmake file for
|
|
# details.
|
|
# We must pass the values via the PATHS options because the main find_package call uses
|
|
# NO_DEFAULT_PATH, and thus CMAKE_PREFIX_PATH values are discarded.
|
|
# CMAKE_FIND_ROOT_PATH values are not discarded and togegher with the PATHS option, it
|
|
# ensures packages from additional prefixes are found.
|
|
__qt_internal_check_path_points_to_cmake_dir(is_path_to_cmake "${additional_path}")
|
|
if(is_path_to_cmake)
|
|
list(APPEND additional_packages_prefix_paths "${additional_path}")
|
|
else()
|
|
__qt_internal_get_possible_cmake_dirs(additional_cmake_dirs "${additional_path}")
|
|
list(APPEND additional_packages_prefix_paths ${additional_cmake_dirs})
|
|
endif()
|
|
endforeach()
|
|
|
|
set("${out_var}" "${additional_packages_prefix_paths}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Collects CMAKE_MODULE_PATH from QT_ADDITIONAL_PACKAGES_PREFIX_PATH
|
|
function(__qt_internal_collect_additional_module_paths)
|
|
if(__qt_additional_module_paths_set)
|
|
return()
|
|
endif()
|
|
foreach(prefix_path IN LISTS QT_ADDITIONAL_PACKAGES_PREFIX_PATH)
|
|
__qt_internal_check_path_points_to_cmake_dir(is_path_to_cmake "${prefix_path}")
|
|
if(is_path_to_cmake)
|
|
list(APPEND CMAKE_MODULE_PATH "${prefix_path}/${QT_CMAKE_EXPORT_NAMESPACE}")
|
|
else()
|
|
__qt_internal_get_possible_cmake_dirs(additional_cmake_dirs "${prefix_path}")
|
|
list(TRANSFORM additional_cmake_dirs APPEND "/${QT_CMAKE_EXPORT_NAMESPACE}")
|
|
list(APPEND CMAKE_MODULE_PATH ${additional_cmake_dirs})
|
|
endif()
|
|
endforeach()
|
|
list(REMOVE_DUPLICATES CMAKE_MODULE_PATH)
|
|
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" PARENT_SCOPE)
|
|
set(__qt_additional_module_paths_set TRUE PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Take a list of prefix paths ending with "/lib/cmake", and return a list of absolute paths with
|
|
# "/lib/cmake" removed.
|
|
function(__qt_internal_prefix_paths_to_roots out_var prefix_paths)
|
|
set(result "")
|
|
foreach(path IN LISTS prefix_paths)
|
|
__qt_internal_reverse_prefix_path_from_cmake_dir(path "${path}")
|
|
list(APPEND result "${path}")
|
|
endforeach()
|
|
set("${out_var}" "${result}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
function(_qt_internal_get_check_cxx_source_compiles_out_var out_output_var out_func_args)
|
|
# This just resets the output var in the parent scope to an empty string.
|
|
set(${out_output_var} "" PARENT_SCOPE)
|
|
|
|
# OUTPUT_VARIABLE is an internal undocumented variable of check_cxx_source_compiles
|
|
# since 3.23. Allow an opt out in case this breaks in the future.
|
|
set(extra_func_args "")
|
|
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.23"
|
|
AND NOT QT_INTERNAL_NO_TRY_COMPILE_OUTPUT_VARIABLE)
|
|
set(extra_func_args OUTPUT_VARIABLE ${out_output_var})
|
|
endif()
|
|
|
|
set(${out_func_args} "${extra_func_args}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# This function gets all targets below this directory
|
|
#
|
|
# Multi-value Arguments:
|
|
# EXCLUDE list of target types that should be filtered from resulting list.
|
|
#
|
|
# INCLUDE list of target types that should be filtered from resulting list.
|
|
# EXCLUDE has higher priority than INCLUDE.
|
|
function(_qt_internal_collect_buildsystem_targets result dir)
|
|
cmake_parse_arguments(arg "" "" "EXCLUDE;INCLUDE" ${ARGN})
|
|
|
|
if(NOT _qt_internal_collect_buildsystem_targets_inner)
|
|
set(${result} "")
|
|
set(_qt_internal_collect_buildsystem_targets_inner TRUE)
|
|
endif()
|
|
|
|
set(forward_args "")
|
|
if(arg_EXCLUDE)
|
|
set(forward_args APPEND EXCLUDE ${arg_EXCLUDE})
|
|
endif()
|
|
|
|
if(arg_INCLUDE)
|
|
set(forward_args APPEND INCLUDE ${arg_INCLUDE})
|
|
endif()
|
|
|
|
get_property(subdirs DIRECTORY "${dir}" PROPERTY SUBDIRECTORIES)
|
|
|
|
# Make sure that we don't hit endless recursion when running qt-cmake-standalone-test on a
|
|
# in-source test dir, where the currently processed directory lists itself in its SUBDIRECTORIES
|
|
# property.
|
|
# See https://bugreports.qt.io/browse/QTBUG-119998
|
|
# and https://gitlab.kitware.com/cmake/cmake/-/issues/25489
|
|
# Do it only when QT_INTERNAL_IS_STANDALONE_TEST is set, to avoid the possible slowdown when
|
|
# processing many subdirectores when configuring all standalone tests rather than just one.
|
|
if(QT_INTERNAL_IS_STANDALONE_TEST)
|
|
list(REMOVE_ITEM subdirs "${dir}")
|
|
endif()
|
|
|
|
foreach(subdir IN LISTS subdirs)
|
|
_qt_internal_collect_buildsystem_targets(${result} "${subdir}" ${forward_args})
|
|
endforeach()
|
|
get_property(sub_targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS)
|
|
set(real_targets "")
|
|
if(sub_targets)
|
|
foreach(target IN LISTS sub_targets)
|
|
get_target_property(target_type ${target} TYPE)
|
|
if((NOT arg_INCLUDE OR target_type IN_LIST arg_INCLUDE) AND
|
|
(NOT arg_EXCLUDE OR (NOT target_type IN_LIST arg_EXCLUDE)))
|
|
list(APPEND real_targets ${target})
|
|
endif()
|
|
endforeach()
|
|
endif()
|
|
set(${result} ${${result}} ${real_targets} PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Add a custom target ${target} that is *not* added to the default build target in a safe way.
|
|
# Dependencies must then be added with _qt_internal_add_phony_target_dependencies.
|
|
#
|
|
# What's "safe" in this context? For the Visual Studio generators, we cannot use add_dependencies,
|
|
# because this would enable the dependencies in the default build of the solution. See QTBUG-115166
|
|
# and upstream CMake issue #16668 for details. Instead, we record the dependencies (added with
|
|
# _qt_internal_add_phony_target_dependencies) and create the target at the end of the top-level
|
|
# directory scope.
|
|
#
|
|
# This only works if at least CMake 3.19 is used. Older CMake versions will trigger a warning that
|
|
# can be turned off with the variable ${WARNING_VARIABLE}.
|
|
#
|
|
# For other generators, this is just a call to add_custom_target, unless the target already exists,
|
|
# followed by add_dependencies.
|
|
#
|
|
# Use this function for targets that are not part of the default build, i.e. that should be
|
|
# triggered by the user.
|
|
#
|
|
# TARGET_CREATED_HOOK is the name of a function that is called after the target has been created.
|
|
# It takes the target's name as first and only argument.
|
|
#
|
|
# Example:
|
|
# _qt_internal_add_phony_target(update_translations
|
|
# WARNING_VARIABLE QT_NO_GLOBAL_LUPDATE_TARGET_CREATED_WARNING
|
|
# )
|
|
# _qt_internal_add_phony_target_dependencies(update_translations
|
|
# narf_lupdate_zort_lupdate
|
|
# )
|
|
#
|
|
function(_qt_internal_add_phony_target target)
|
|
set(no_value_options "")
|
|
set(single_value_options
|
|
TARGET_CREATED_HOOK
|
|
WARNING_VARIABLE
|
|
)
|
|
set(multi_value_options "")
|
|
cmake_parse_arguments(PARSE_ARGV 0 arg
|
|
"${no_value_options}" "${single_value_options}" "${multi_value_options}"
|
|
)
|
|
if("${arg_WARNING_VARIABLE}" STREQUAL "")
|
|
message(FATAL_ERROR "WARNING_VARIABLE must be provided.")
|
|
endif()
|
|
if(CMAKE_GENERATOR MATCHES "^Visual Studio ")
|
|
if(${CMAKE_VERSION} VERSION_LESS "3.19.0")
|
|
if(NOT ${${arg_WARNING_VARIABLE}})
|
|
message(WARNING
|
|
"Cannot create target ${target} with this CMake version. "
|
|
"Please upgrade to CMake 3.19.0 or newer. "
|
|
"Set ${WARNING_VARIABLE} to ON to disable this warning."
|
|
)
|
|
endif()
|
|
return()
|
|
endif()
|
|
|
|
get_property(already_deferred GLOBAL PROPERTY _qt_target_${target}_creation_deferred)
|
|
if(NOT already_deferred)
|
|
cmake_language(EVAL CODE
|
|
"cmake_language(DEFER DIRECTORY \"${CMAKE_SOURCE_DIR}\" CALL _qt_internal_add_phony_target_deferred \"${target}\")"
|
|
)
|
|
if(DEFINED arg_TARGET_CREATED_HOOK)
|
|
set_property(GLOBAL
|
|
PROPERTY _qt_target_${target}_creation_hook ${arg_TARGET_CREATED_HOOK}
|
|
)
|
|
endif()
|
|
endif()
|
|
set_property(GLOBAL APPEND PROPERTY _qt_target_${target}_creation_deferred ON)
|
|
else()
|
|
if(NOT TARGET ${target})
|
|
add_custom_target(${target})
|
|
if(DEFINED arg_TARGET_CREATED_HOOK)
|
|
if(CMAKE_VERSION VERSION_LESS "3.19")
|
|
set(incfile
|
|
"${CMAKE_CURRENT_BINARY_DIR}/.qt_internal_add_phony_target.cmake"
|
|
)
|
|
file(WRITE "${incfile}" "${arg_TARGET_CREATED_HOOK}(${target})")
|
|
include("${incfile}")
|
|
file(REMOVE "${incfile}")
|
|
else()
|
|
cmake_language(CALL "${arg_TARGET_CREATED_HOOK}" "${target}")
|
|
endif()
|
|
endif()
|
|
endif()
|
|
endif()
|
|
endfunction()
|
|
|
|
# Adds dependencies to a custom target that has been created with
|
|
# _qt_internal_add_phony_target. See the docstring at _qt_internal_add_phony_target for
|
|
# more details.
|
|
function(_qt_internal_add_phony_target_dependencies target)
|
|
set(dependencies ${ARGN})
|
|
if(CMAKE_GENERATOR MATCHES "^Visual Studio ")
|
|
set_property(GLOBAL APPEND PROPERTY _qt_target_${target}_dependencies ${dependencies})
|
|
|
|
# Exclude the dependencies from the solution's default build to avoid them being enabled
|
|
# accidentally should the user add another dependency to them.
|
|
set_target_properties(${dependencies} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD ON)
|
|
else()
|
|
add_dependencies(${target} ${dependencies})
|
|
endif()
|
|
endfunction()
|
|
|
|
# Hack for the Visual Studio generator. Create the custom target named ${target} and work
|
|
# around the lack of a working add_dependencies by calling 'cmake --build' for every dependency.
|
|
function(_qt_internal_add_phony_target_deferred target)
|
|
get_property(target_dependencies GLOBAL PROPERTY _qt_target_${target}_dependencies)
|
|
set(target_commands "")
|
|
foreach(dependency IN LISTS target_dependencies)
|
|
list(APPEND target_commands
|
|
COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" -t ${dependency}
|
|
)
|
|
endforeach()
|
|
add_custom_target(${target} ${target_commands})
|
|
get_property(creation_hook GLOBAL PROPERTY _qt_target_${target}_creation_hook)
|
|
if(creation_hook)
|
|
cmake_language(CALL ${creation_hook} ${target})
|
|
endif()
|
|
endfunction()
|
|
|
|
# The helper function that checks if module was included multiple times, and has the inconsistent
|
|
# set of targets that belong to the module. It's expected that either all 'targets' or none of them
|
|
# will be written to the 'targets_not_defined' variable, if the module was not or was
|
|
# searched before accordingly.
|
|
function(_qt_internal_check_multiple_inclusion targets_not_defined targets)
|
|
set(targets_defined "")
|
|
set(${targets_not_defined} "")
|
|
set(expected_targets "")
|
|
foreach(expected_target ${targets})
|
|
list(APPEND expected_targets ${expected_target})
|
|
if(NOT TARGET Qt::${expected_target})
|
|
list(APPEND ${targets_not_defined} ${expected_target})
|
|
endif()
|
|
if(TARGET Qt::${expected_target})
|
|
list(APPEND targets_defined ${expected_target})
|
|
endif()
|
|
endforeach()
|
|
if("${targets_defined}" STREQUAL "${expected_targets}")
|
|
set(${targets_not_defined} "" PARENT_SCOPE)
|
|
return()
|
|
endif()
|
|
if(NOT "${targets_defined}" STREQUAL "")
|
|
message(FATAL_ERROR "Some (but not all) targets in this export set were already defined."
|
|
"\nTargets Defined: ${targets_defined}\nTargets not yet defined: "
|
|
"${${targets_not_defined}}\n"
|
|
)
|
|
endif()
|
|
set(${targets_not_defined} "${${targets_not_defined}}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# The function is used when creating version less targets using ALIASes.
|
|
function(_qt_internal_create_versionless_alias_targets targets install_namespace)
|
|
foreach(target IN LISTS targets)
|
|
add_library(Qt::${target} ALIAS ${install_namespace}::${target})
|
|
endforeach()
|
|
endfunction()
|
|
|
|
# The function is used when creating version less targets from scratch but not using ALIASes.
|
|
# It assigns the known properties from the versioned targets to the versionless created in this
|
|
# function. This allows versionless targets mimic the versioned.
|
|
function(_qt_internal_create_versionless_targets targets install_namespace)
|
|
set(known_imported_properties
|
|
IMPORTED_LINK_DEPENDENT_LIBRARIES
|
|
)
|
|
|
|
set(known_interface_properties
|
|
QT_MAJOR_VERSION
|
|
AUTOMOC_MACRO_NAMES
|
|
AUTOUIC_OPTIONS
|
|
COMPILE_DEFINITIONS
|
|
COMPILE_FEATURES
|
|
COMPILE_OPTIONS
|
|
CXX_MODULE_SETS
|
|
HEADER_SETS
|
|
HEADER_SETS_TO_VERIFY
|
|
INCLUDE_DIRECTORIES
|
|
LINK_DEPENDS
|
|
LINK_DIRECTORIES
|
|
LINK_LIBRARIES
|
|
LINK_LIBRARIES_DIRECT
|
|
LINK_LIBRARIES_DIRECT_EXCLUDE
|
|
LINK_OPTIONS
|
|
POSITION_INDEPENDENT_CODE
|
|
PRECOMPILE_HEADERS
|
|
SOURCES
|
|
SYSTEM_INCLUDE_DIRECTORIES
|
|
)
|
|
|
|
set(known_qt_exported_properties
|
|
MODULE_PLUGIN_TYPES
|
|
QT_DISABLED_PRIVATE_FEATURES
|
|
QT_DISABLED_PUBLIC_FEATURES
|
|
QT_ENABLED_PRIVATE_FEATURES
|
|
QT_ENABLED_PUBLIC_FEATURES
|
|
QT_QMAKE_PRIVATE_CONFIG
|
|
QT_QMAKE_PUBLIC_CONFIG
|
|
QT_QMAKE_PUBLIC_QT_CONFIG
|
|
)
|
|
|
|
set(known_qt_exported_properties_interface_allowed
|
|
_qt_config_module_name
|
|
_qt_is_public_module
|
|
_qt_module_has_headers
|
|
_qt_module_has_private_headers
|
|
_qt_module_has_public_headers
|
|
_qt_module_has_qpa_headers
|
|
_qt_module_has_rhi_headers
|
|
_qt_module_include_name
|
|
_qt_module_interface_name
|
|
_qt_package_name
|
|
_qt_package_version
|
|
_qt_private_module_target_name
|
|
_qt_sbom_spdx_id
|
|
_qt_sbom_spdx_repo_document_namespace
|
|
_qt_sbom_spdx_relative_installed_repo_document_path
|
|
_qt_sbom_spdx_repo_project_name_lowercase
|
|
)
|
|
|
|
set(supported_target_types STATIC_LIBRARY MODULE_LIBRARY SHARED_LIBRARY OBJECT_LIBRARY
|
|
INTERFACE_LIBRARY)
|
|
|
|
foreach(target IN LISTS targets)
|
|
if(NOT TARGET ${install_namespace}::${target})
|
|
message(FATAL_ERROR "${install_namespace}::${target} is not a target, can not extend"
|
|
" an alias target")
|
|
endif()
|
|
|
|
get_target_property(type ${install_namespace}::${target} TYPE)
|
|
if(NOT type)
|
|
message(FATAL_ERROR "Cannot get the ${install_namespace}::${target} target type.")
|
|
endif()
|
|
|
|
if(NOT "${type}" IN_LIST supported_target_types)
|
|
message(AUTHOR_WARNING "${install_namespace}::${target} requires the versionless"
|
|
" target creation, but it has incompatible type ${type}.")
|
|
continue()
|
|
endif()
|
|
|
|
string(REPLACE "_LIBRARY" "" creation_type "${type}")
|
|
add_library(Qt::${target} ${creation_type} IMPORTED)
|
|
|
|
if(NOT "${type}" STREQUAL "INTERFACE_LIBRARY")
|
|
foreach(config "" _RELEASE _DEBUG _RELWITHDEBINFO _MINSIZEREL)
|
|
get_target_property(target_imported_location
|
|
${install_namespace}::${target} IMPORTED_LOCATION${config})
|
|
if(NOT target_imported_location)
|
|
if("${config}" STREQUAL "")
|
|
message(FATAL_ERROR "Cannot create versionless target for"
|
|
" ${install_namespace}::${target}. IMPORTED_LOCATION property is "
|
|
"missing."
|
|
)
|
|
else()
|
|
continue()
|
|
endif()
|
|
endif()
|
|
set_property(TARGET Qt::${target} PROPERTY
|
|
IMPORTED_LOCATION${config} "${target_imported_location}")
|
|
|
|
foreach(property IN LISTS known_imported_properties)
|
|
get_target_property(exported_property_value
|
|
${install_namespace}::${target} ${property}${config})
|
|
if(exported_property_value)
|
|
set_property(TARGET Qt::${target} APPEND PROPERTY
|
|
${property} "${exported_property_value}")
|
|
endif()
|
|
endforeach()
|
|
endforeach()
|
|
|
|
foreach(property IN LISTS known_qt_exported_properties)
|
|
get_target_property(exported_property_value
|
|
${install_namespace}::${target} ${property})
|
|
if(exported_property_value)
|
|
set_property(TARGET Qt::${target} APPEND PROPERTY
|
|
${property} "${exported_property_value}")
|
|
endif()
|
|
endforeach()
|
|
endif()
|
|
|
|
foreach(property IN LISTS known_interface_properties)
|
|
get_target_property(interface_property_value
|
|
${install_namespace}::${target} INTERFACE_${property})
|
|
if(interface_property_value)
|
|
set_property(TARGET Qt::${target} APPEND PROPERTY
|
|
INTERFACE_${property} "${interface_property_value}")
|
|
endif()
|
|
endforeach()
|
|
|
|
foreach(property IN LISTS known_qt_exported_properties_interface_allowed)
|
|
get_target_property(exported_property_value
|
|
${install_namespace}::${target} ${property})
|
|
if(exported_property_value)
|
|
set_property(TARGET Qt::${target} APPEND PROPERTY
|
|
${property} "${exported_property_value}")
|
|
endif()
|
|
endforeach()
|
|
|
|
set_property(TARGET Qt::${target} PROPERTY _qt_is_versionless_target TRUE)
|
|
endforeach()
|
|
endfunction()
|
|
|
|
# Checks whether any unparsed arguments have been passed to the function at the call site.
|
|
# Use this right after `cmake_parse_arguments`.
|
|
function(_qt_internal_validate_all_args_are_parsed prefix)
|
|
if(DEFINED ${prefix}_UNPARSED_ARGUMENTS)
|
|
message(FATAL_ERROR "Unknown arguments: (${${prefix}_UNPARSED_ARGUMENTS})")
|
|
endif()
|
|
endfunction()
|
|
|
|
# Takes a list of path components and joins them into one path separated by forward slashes "/",
|
|
# and saves the path in out_var.
|
|
# Filters out any path parts that are bare "."s.
|
|
function(_qt_internal_path_join out_var)
|
|
set(args ${ARGN})
|
|
|
|
# Remove any bare ".", to avoid any CMP0177 warnings for paths passed to install().
|
|
list(REMOVE_ITEM args ".")
|
|
|
|
string(JOIN "/" path ${args})
|
|
set(${out_var} ${path} PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# _qt_internal_qt_remove_args can remove arguments from an existing list of function
|
|
# arguments in order to pass a filtered list of arguments to a different function.
|
|
# Parameters:
|
|
# out_var: result of remove all arguments specified by ARGS_TO_REMOVE from ALL_ARGS
|
|
# ARGS_TO_REMOVE: Arguments to remove.
|
|
# ALL_ARGS: All arguments supplied to cmake_parse_arguments
|
|
# from which ARGS_TO_REMOVE should be removed from. We require all the
|
|
# arguments or we can't properly identify the range of the arguments detailed
|
|
# in ARGS_TO_REMOVE.
|
|
# ARGS: Arguments passed into the function, usually ${ARGV}
|
|
#
|
|
# E.g.:
|
|
# We want to forward all arguments from foo to bar, execpt ZZZ since it will
|
|
# trigger an error in bar.
|
|
#
|
|
# foo(target BAR .... ZZZ .... WWW ...)
|
|
# bar(target BAR.... WWW...)
|
|
#
|
|
# function(foo target)
|
|
# cmake_parse_arguments(PARSE_ARGV 1 arg "" "" "BAR;ZZZ;WWW")
|
|
# qt_remove_args(forward_args
|
|
# ARGS_TO_REMOVE ${target} ZZZ
|
|
# ALL_ARGS ${target} BAR ZZZ WWW
|
|
# ARGS ${ARGV}
|
|
# )
|
|
# bar(${target} ${forward_args})
|
|
# endfunction()
|
|
#
|
|
function(_qt_internal_remove_args out_var)
|
|
cmake_parse_arguments(arg "" "" "ARGS_TO_REMOVE;ALL_ARGS;ARGS" ${ARGN})
|
|
set(result ${arg_ARGS})
|
|
foreach(arg IN LISTS arg_ARGS_TO_REMOVE)
|
|
# find arg
|
|
list(FIND result ${arg} find_result)
|
|
if (NOT find_result EQUAL -1)
|
|
# remove arg
|
|
list(REMOVE_AT result ${find_result})
|
|
list(LENGTH result result_len)
|
|
if(find_result EQUAL result_len)
|
|
# We removed the last argument, could have been an option keyword
|
|
continue()
|
|
endif()
|
|
list(GET result ${find_result} arg_current)
|
|
# remove values until we hit another arg or the end of the list
|
|
while(NOT "${arg_current}" IN_LIST arg_ALL_ARGS AND find_result LESS result_len)
|
|
list(REMOVE_AT result ${find_result})
|
|
list(LENGTH result result_len)
|
|
if (NOT find_result EQUAL result_len)
|
|
list(GET result ${find_result} arg_current)
|
|
endif()
|
|
endwhile()
|
|
endif()
|
|
endforeach()
|
|
set(${out_var} "${result}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
function(__qt_internal_handle_find_all_qt_module_packages out_var)
|
|
set(opt_args "")
|
|
set(single_args "")
|
|
set(multi_args
|
|
COMPONENTS
|
|
)
|
|
cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
|
|
_qt_internal_validate_all_args_are_parsed(arg)
|
|
|
|
if(NOT arg_COMPONENTS)
|
|
return()
|
|
endif()
|
|
|
|
set(components ${arg_COMPONENTS})
|
|
|
|
if("ALL_QT_MODULES" IN_LIST components)
|
|
list(FIND components "ALL_QT_MODULES" all_qt_modules_index)
|
|
list(REMOVE_AT components ${all_qt_modules_index})
|
|
|
|
# Find the path to dir with module.json files. # We consider each file name to be a
|
|
# Qt package (component) name that contains a target with the same name.
|
|
set(json_modules_path "${QT6_INSTALL_PREFIX}/${QT6_INSTALL_DESCRIPTIONSDIR}")
|
|
file(GLOB json_modules "${json_modules_path}/*.json")
|
|
|
|
set(all_qt_modules "")
|
|
|
|
foreach(json_module IN LISTS json_modules)
|
|
get_filename_component(module_name "${json_module}" NAME_WE)
|
|
list(APPEND all_qt_modules ${module_name})
|
|
endforeach()
|
|
|
|
if(all_qt_modules)
|
|
list(INSERT components "${all_qt_modules_index}" ${all_qt_modules})
|
|
endif()
|
|
endif()
|
|
|
|
set(${out_var} "${components}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Append ${ARGN} to ${target}'s ${property_name} property, removing duplicates.
|
|
function(_qt_internal_append_to_target_property_without_duplicates target property_name)
|
|
get_target_property(property "${target}" "${property_name}")
|
|
if(NOT property)
|
|
set(property "")
|
|
endif()
|
|
list(APPEND property ${ARGN})
|
|
list(REMOVE_DUPLICATES property)
|
|
set_property(TARGET "${target}" PROPERTY "${property_name}" "${property}")
|
|
endfunction()
|
|
|
|
# Append ${ARGN} to global CMake ${property_name} property, removing duplicates.
|
|
function(_qt_internal_append_to_cmake_property_without_duplicates property_name)
|
|
get_cmake_property(property "${property_name}")
|
|
if(NOT property)
|
|
set(property "")
|
|
endif()
|
|
list(APPEND property ${ARGN})
|
|
list(REMOVE_DUPLICATES property)
|
|
set_property(GLOBAL PROPERTY "${property_name}" "${property}")
|
|
endfunction()
|
|
|
|
# Helper function to forward options from one function to another.
|
|
#
|
|
# This is somewhat the opposite of _qt_internal_remove_args.
|
|
#
|
|
# Parameters:
|
|
# FORWARD_PREFIX is usually arg because we pass cmake_parse_arguments(PARSE_ARGV 0 arg) in most code
|
|
# FORWARD_OPTIONS, FORWARD_SINGLE, FORWARD_MULTI are the options that should be forwarded.
|
|
#
|
|
# The forwarded args will be either set in arg_FORWARD_OUT_VAR or appended if FORWARD_APPEND is set.
|
|
#
|
|
# The function reads the options like ${arg_FORWARD_PREFIX}_${option} in the parent scope.
|
|
function(_qt_internal_forward_function_args)
|
|
set(opt_args
|
|
FORWARD_APPEND
|
|
)
|
|
set(single_args
|
|
FORWARD_PREFIX
|
|
)
|
|
set(multi_args
|
|
FORWARD_OPTIONS
|
|
FORWARD_SINGLE
|
|
FORWARD_MULTI
|
|
FORWARD_OUT_VAR
|
|
)
|
|
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
|
|
_qt_internal_validate_all_args_are_parsed(arg)
|
|
|
|
if(NOT arg_FORWARD_OUT_VAR)
|
|
message(FATAL_ERROR "FORWARD_OUT_VAR must be provided.")
|
|
endif()
|
|
|
|
set(forward_args "")
|
|
foreach(option_name IN LISTS arg_FORWARD_OPTIONS)
|
|
if(${arg_FORWARD_PREFIX}_${option_name})
|
|
list(APPEND forward_args "${option_name}")
|
|
endif()
|
|
endforeach()
|
|
|
|
foreach(option_name IN LISTS arg_FORWARD_SINGLE)
|
|
if(NOT "${${arg_FORWARD_PREFIX}_${option_name}}" STREQUAL "")
|
|
list(APPEND forward_args "${option_name}" "${${arg_FORWARD_PREFIX}_${option_name}}")
|
|
endif()
|
|
endforeach()
|
|
|
|
foreach(option_name IN LISTS arg_FORWARD_MULTI)
|
|
if(NOT "${${arg_FORWARD_PREFIX}_${option_name}}" STREQUAL "")
|
|
list(APPEND forward_args "${option_name}" ${${arg_FORWARD_PREFIX}_${option_name}})
|
|
endif()
|
|
endforeach()
|
|
|
|
if(arg_FORWARD_APPEND)
|
|
set(forward_args ${${arg_FORWARD_OUT_VAR}} "${forward_args}")
|
|
endif()
|
|
|
|
set(${arg_FORWARD_OUT_VAR} "${forward_args}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Function adds the transitive property of the specified type to a target, avoiding duplicates.
|
|
# Supported types: COMPILE, LINK
|
|
#
|
|
# See:
|
|
# https://cmake.org/cmake/help/latest/prop_tgt/TRANSITIVE_COMPILE_PROPERTIES.html
|
|
# https://cmake.org/cmake/help/latest/prop_tgt/TRANSITIVE_LINK_PROPERTIES.html
|
|
function(_qt_internal_add_transitive_property target type property)
|
|
if(CMAKE_VERSION VERSION_LESS 3.30)
|
|
return()
|
|
endif()
|
|
if(NOT type MATCHES "^(COMPILE|LINK)$")
|
|
message(FATAL_ERROR "Attempt to assign unknown TRANSITIVE_${type}_PROPERTIES property")
|
|
endif()
|
|
|
|
_qt_internal_dealias_target(target)
|
|
get_target_property(transitive_properties ${target}
|
|
TRANSITIVE_${type}_PROPERTIES)
|
|
if(NOT "${property}" IN_LIST transitive_properties)
|
|
set_property(TARGET ${target}
|
|
APPEND PROPERTY TRANSITIVE_${type}_PROPERTIES ${property})
|
|
endif()
|
|
endfunction()
|
|
|
|
# Compatibility of `cmake_path(RELATIVE_PATH)`
|
|
#
|
|
# In order to be compatible with `file(RELATIVE_PATH)`, path normalization of the result is
|
|
# always performed, with the trailing slash stripped.
|
|
#
|
|
# Synopsis
|
|
#
|
|
# _qt_internal_relative_path(<path-var>
|
|
# [BASE_DIRECTORY <input>]
|
|
# [OUTPUT_VARIABLE <out-var>]
|
|
# )
|
|
#
|
|
# Arguments
|
|
#
|
|
# `path-var`
|
|
# Equivalent to `cmake_path(RELATIVE_PATH <path-var>)`.
|
|
#
|
|
# `BASE_DIRECTORY`
|
|
# Equivalent to `cmake_path(RELATIVE_PATH BASE_DIRECTORY)`.
|
|
#
|
|
# `OUTPUT_VARIABLE`
|
|
# Equivalent to `cmake_path(RELATIVE_PATH OUTPUT_VARIABLE)`.
|
|
function(_qt_internal_relative_path path_var)
|
|
set(option_args "")
|
|
set(single_args
|
|
BASE_DIRECTORY
|
|
OUTPUT_VARIABLE
|
|
)
|
|
set(multi_args "")
|
|
cmake_parse_arguments(PARSE_ARGV 1 arg "${option_args}" "${single_args}" "${multi_args}")
|
|
|
|
if(NOT arg_BASE_DIRECTORY)
|
|
set(arg_BASE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
|
|
endif()
|
|
if(NOT arg_OUTPUT_VARIABLE)
|
|
set(arg_OUTPUT_VARIABLE ${path_var})
|
|
endif()
|
|
|
|
if(CMAKE_VERSION VERSION_LESS 3.20)
|
|
file(RELATIVE_PATH ${arg_OUTPUT_VARIABLE}
|
|
"${arg_BASE_DIRECTORY}"
|
|
"${${path_var}}")
|
|
else()
|
|
cmake_path(RELATIVE_PATH ${path_var}
|
|
BASE_DIRECTORY "${arg_BASE_DIRECTORY}"
|
|
OUTPUT_VARIABLE ${arg_OUTPUT_VARIABLE}
|
|
)
|
|
cmake_path(NORMAL_PATH ${arg_OUTPUT_VARIABLE})
|
|
string(REGEX REPLACE "/$" "" ${arg_OUTPUT_VARIABLE}
|
|
"${${arg_OUTPUT_VARIABLE}}")
|
|
endif()
|
|
set(${arg_OUTPUT_VARIABLE} "${${arg_OUTPUT_VARIABLE}}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
# Compatibility of `cmake_path(IS_PREFIX)`
|
|
#
|
|
# NORMALIZE keyword is not supported
|
|
#
|
|
# Synopsis
|
|
#
|
|
# _qt_internal_path_is_prefix(<path-var> <input> <out-var>)
|
|
#
|
|
# Arguments
|
|
#
|
|
# `path-var`
|
|
# Equivalent to `cmake_path(IS_PREFIX <path-var>)`.
|
|
#
|
|
# `input`
|
|
# Equivalent to `cmake_path(IS_PREFIX <input>)`.
|
|
#
|
|
# `out-var`
|
|
# Equivalent to `cmake_path(IS_PREFIX <out-var>)`.
|
|
function(_qt_internal_path_is_prefix path_var input out_var)
|
|
|
|
# Make sure the path ends with `/`
|
|
if(NOT ${path_var} MATCHES "/$")
|
|
set(${path_var} "${${path_var}}/")
|
|
endif()
|
|
# For the case input == path_var, we need to also include a trailing `/` to match the
|
|
# previous change. We discard the actual path for input so we can add it unconditionally
|
|
set(input "${input}/")
|
|
if(CMAKE_VERSION VERSION_LESS 3.20)
|
|
string(FIND "${input}" "${${path_var}}" find_pos)
|
|
if(find_pos EQUAL 0)
|
|
# input starts_with path_var
|
|
set(${out_var} ON)
|
|
else()
|
|
set(${out_var} OFF)
|
|
endif()
|
|
else()
|
|
cmake_path(IS_PREFIX ${path_var} ${input} ${out_var})
|
|
endif()
|
|
set(${out_var} "${${out_var}}" PARENT_SCOPE)
|
|
endfunction()
|
|
|
|
function(qt_set01 result)
|
|
if (${ARGN})
|
|
set("${result}" 1 PARENT_SCOPE)
|
|
else()
|
|
set("${result}" 0 PARENT_SCOPE)
|
|
endif()
|
|
endfunction()
|
|
|
|
# Configures the file using either the input template or the CONTENT.
|
|
# Behaves as either file(CONFIGURE or configure_file( command, but do not depend
|
|
# on CMake version.
|
|
#
|
|
# Synopsis
|
|
# _qt_internal_configure_file(<CONFIGURE|GENERATE>
|
|
# OUTPUT <path>
|
|
# <INPUT path|CONTENT data>
|
|
# )
|
|
#
|
|
# Arguments
|
|
# `CONFIGURE` Run in pure CONFIGURE mode.
|
|
#
|
|
# `GENERATE` Configure CONTENT and generate file with the generator expression
|
|
# support.
|
|
#
|
|
# `OUTPUT` The output file name to generate.
|
|
#
|
|
# `INPUT` The input template file name.
|
|
#
|
|
# `CONTENT` The template content. If both INPUT and CONTENT are specified, INPUT
|
|
# argument is ignored.
|
|
function(_qt_internal_configure_file mode)
|
|
cmake_parse_arguments(PARSE_ARGV 1 arg "" "OUTPUT;INPUT;CONTENT" "")
|
|
|
|
if(NOT arg_OUTPUT)
|
|
message(FATAL_ERROR "No OUTPUT file provided to _qt_internal_configure_file.")
|
|
endif()
|
|
|
|
# Substitute the '@' wrapped variables and generate the file with the
|
|
# the generator expressions evaluation inside the resulting CONTENT.
|
|
if(mode STREQUAL "GENERATE")
|
|
if(arg_INPUT)
|
|
configure_file("${arg_INPUT}" "${arg_OUTPUT}.tmp" @ONLY)
|
|
file(GENERATE OUTPUT "${arg_OUTPUT}" INPUT "${arg_OUTPUT}.tmp")
|
|
else()
|
|
string(CONFIGURE "${arg_CONTENT}" arg_CONTENT @ONLY)
|
|
file(GENERATE OUTPUT "${arg_OUTPUT}" CONTENT "${arg_CONTENT}")
|
|
endif()
|
|
return()
|
|
endif()
|
|
|
|
# We use this check for the cases when the specified CONTENT is empty. The value of arg_CONTENT
|
|
# is undefined, but we still want to create a file with empty content.
|
|
if("CONTENT" IN_LIST ARGV)
|
|
if(arg_INPUT)
|
|
message(WARNING "Both CONTENT and INPUT are specified. CONTENT will be used to generate"
|
|
" output")
|
|
endif()
|
|
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.18)
|
|
file(CONFIGURE OUTPUT "${arg_OUTPUT}" CONTENT "${arg_CONTENT}" @ONLY)
|
|
return()
|
|
endif()
|
|
set(template_name "QtFileConfigure.txt.in")
|
|
# When building qtbase, use the source template file.
|
|
# Otherwise use the installed file (basically wherever Qt6 package is found).
|
|
# This should work for non-prefix and superbuilds as well.
|
|
if(QtBase_SOURCE_DIR)
|
|
set(input_file "${QtBase_SOURCE_DIR}/cmake/${template_name}")
|
|
else()
|
|
set(input_file "${_qt_6_config_cmake_dir}/${template_name}")
|
|
endif()
|
|
set(__qt_file_configure_content "${arg_CONTENT}")
|
|
elseif(arg_INPUT)
|
|
set(input_file "${arg_INPUT}")
|
|
else()
|
|
message(FATAL_ERROR "No input value provided to _qt_internal_configure_file.")
|
|
endif()
|
|
|
|
configure_file("${input_file}" "${arg_OUTPUT}" @ONLY)
|
|
endfunction()
|
|
|
|
# The function checks if `value` is a valid C indentifier.
|
|
#
|
|
# Synopsis
|
|
#
|
|
# _qt_internal_is_c_identifier(<out_var> <value>)
|
|
#
|
|
# Arguments
|
|
#
|
|
# `out_var`
|
|
# Variable name for the evaluation result.
|
|
#
|
|
# `value`
|
|
# The string for the evaluation.
|
|
function(_qt_internal_is_c_identifier out_var value)
|
|
string(MAKE_C_IDENTIFIER "${value}" value_valid)
|
|
|
|
if(value AND "${value}" STREQUAL "${value_valid}")
|
|
set(${out_var} "TRUE" PARENT_SCOPE)
|
|
else()
|
|
set(${out_var} "FALSE" PARENT_SCOPE)
|
|
endif()
|
|
endfunction()
|
|
|
|
# Makes appending of the CMake configure time dependencies unique.
|
|
function(_qt_internal_append_cmake_configure_depends)
|
|
get_property(configure_depends DIRECTORY PROPERTY CMAKE_CONFIGURE_DEPENDS)
|
|
foreach(path IN LISTS ARGN)
|
|
get_filename_component(abs_path "${path}" REALPATH)
|
|
if(NOT "${abs_path}" IN_LIST configure_depends)
|
|
list(APPEND configure_depends "${abs_path}")
|
|
endif()
|
|
endforeach()
|
|
set_property(DIRECTORY PROPERTY CMAKE_CONFIGURE_DEPENDS "${configure_depends}")
|
|
endfunction()
|