summaryrefslogtreecommitdiffstats
path: root/cmake/QtToolHelpers.cmake
diff options
context:
space:
mode:
Diffstat (limited to 'cmake/QtToolHelpers.cmake')
-rw-r--r--cmake/QtToolHelpers.cmake626
1 files changed, 475 insertions, 151 deletions
diff --git a/cmake/QtToolHelpers.cmake b/cmake/QtToolHelpers.cmake
index e052479b6c..7dd507c0ee 100644
--- a/cmake/QtToolHelpers.cmake
+++ b/cmake/QtToolHelpers.cmake
@@ -1,6 +1,7 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
# This function is used to define a "Qt tool", such as moc, uic or rcc.
-# The BOOTSTRAP option allows building it as standalone program, otherwise
-# it will be linked against QtCore.
#
# USER_FACING can be passed to mark the tool as a program that is supposed to be
# started directly by users.
@@ -8,150 +9,149 @@
# We must pass this function a target name obtained from
# qt_get_tool_target_name like this:
# qt_get_tool_target_name(target_name my_tool)
-# qt_add_tool(${target_name})
+# qt_internal_add_tool(${target_name})
+#
+# Option Arguments:
+# INSTALL_VERSIONED_LINK
+# Prefix build only. On installation, create a versioned hard-link of the installed file.
+# E.g. create a link of "bin/qmake6" to "bin/qmake".
+# TRY_RUN
+# On Windows, it creates a helper batch script that tests whether the tool can be executed
+# successfully or not. If not, build halts and an error will be show, with tips on what
+# might be cause, and how to fix it. TRY_RUN is disabled when cross-compiling.
+# TRY_RUN_FLAGS
+# Command line flags that are going to be passed to the tool for testing its correctness.
+# If no flags were given, we default to `-v`.
#
-# Arguments:
+# One-value Arguments:
# EXTRA_CMAKE_FILES
# List of additional CMake files that will be installed alongside the tool's exported CMake
# files.
+# EXTRA_CMAKE_INCLUDES
+# List of files that will be included in the Qt6${module}Tools.cmake file.
+# Also see TOOLS_TARGET.
# INSTALL_DIR
# Takes a path, relative to the install prefix, like INSTALL_LIBEXECDIR.
# If this argument is omitted, the default is INSTALL_BINDIR.
# TOOLS_TARGET
# Specifies the module this tool belongs to. The module's Qt6${module}Tools.cmake file
# will then contain targets for this tool.
-#
+# CORE_LIBRARY
+# The argument accepts 'Bootstrap' or 'None' values. If the argument value is set to
+# 'Bootstrap' the Qt::Bootstrap library is linked to the executable instead of Qt::Core.
+# The 'None' value points that core library is not necessary and avoids linking neither
+# Qt::Core or Qt::Bootstrap libraries. Otherwise the Qt::Core library will be publicly
+# linked to the executable target by default.
function(qt_internal_add_tool target_name)
qt_tool_target_to_name(name ${target_name})
- set(one_value_keywords TOOLS_TARGET EXTRA_CMAKE_FILES INSTALL_DIR
- ${__default_target_info_args})
- qt_parse_all_arguments(arg "qt_add_tool" "BOOTSTRAP;NO_INSTALL;USER_FACING"
- "${one_value_keywords}"
- "${__default_private_args}" ${ARGN})
-
- # Handle case when a tool does not belong to a module and it can't be built either (like
- # during a cross-compile).
- if(NOT arg_TOOLS_TARGET AND NOT QT_WILL_BUILD_TOOLS)
- message(FATAL_ERROR "The tool \"${name}\" has not been assigned to a module via"
- " TOOLS_TARGET (so it can't be found) and it can't be built"
- " (QT_WILL_BUILD_TOOLS is ${QT_WILL_BUILD_TOOLS}).")
+ set(option_keywords
+ NO_INSTALL
+ USER_FACING
+ INSTALL_VERSIONED_LINK
+ EXCEPTIONS
+ NO_UNITY_BUILD
+ TRY_RUN)
+ set(one_value_keywords
+ TOOLS_TARGET
+ INSTALL_DIR
+ CORE_LIBRARY
+ TRY_RUN_FLAGS
+ ${__default_target_info_args})
+ set(multi_value_keywords
+ EXTRA_CMAKE_FILES
+ EXTRA_CMAKE_INCLUDES
+ PUBLIC_LIBRARIES
+ ${__default_private_args})
+
+ cmake_parse_arguments(PARSE_ARGV 1 arg
+ "${option_keywords}"
+ "${one_value_keywords}"
+ "${multi_value_keywords}")
+ _qt_internal_validate_all_args_are_parsed(arg)
+
+ qt_internal_find_tool(will_build_tools ${target_name} "${arg_TOOLS_TARGET}")
+
+ if(NOT will_build_tools)
+ return()
endif()
- if(CMAKE_CROSSCOMPILING AND QT_BUILD_TOOLS_WHEN_CROSSCOMPILING AND (name STREQUAL target_name))
- message(FATAL_ERROR
- "qt_add_tool must be passed a target obtained from qt_get_tool_target_name.")
+ set(disable_autogen_tools "${arg_DISABLE_AUTOGEN_TOOLS}")
+ set(corelib "")
+ if(arg_CORE_LIBRARY STREQUAL "Bootstrap" OR arg_CORE_LIBRARY STREQUAL "None")
+ set(corelib CORE_LIBRARY ${arg_CORE_LIBRARY})
+ list(APPEND disable_autogen_tools "uic" "moc" "rcc")
endif()
- set(full_name "${QT_CMAKE_EXPORT_NAMESPACE}::${name}")
- set(imported_tool_target_found FALSE)
- if(TARGET ${full_name})
- get_property(path TARGET ${full_name} PROPERTY LOCATION)
- message(STATUS "Tool '${full_name}' was found at ${path}.")
- set(imported_tool_target_found TRUE)
- if(CMAKE_CROSSCOMPILING AND NOT QT_BUILD_TOOLS_WHEN_CROSSCOMPILING)
- return()
- endif()
+ set(exceptions "")
+ if(arg_EXCEPTIONS)
+ set(exceptions EXCEPTIONS)
endif()
- if(arg_TOOLS_TARGET AND (NOT QT_WILL_BUILD_TOOLS OR QT_BUILD_TOOLS_WHEN_CROSSCOMPILING)
- AND NOT imported_tool_target_found)
- set(tools_package_name "Qt6${arg_TOOLS_TARGET}Tools")
- message(STATUS "Searching for tool '${full_name}' in package ${tools_package_name}.")
-
- # Create the tool targets, even if QT_NO_CREATE_TARGETS is set.
- # Otherwise targets like Qt6::moc are not available in a top-level cross-build.
- set(BACKUP_QT_NO_CREATE_TARGETS ${QT_NO_CREATE_TARGETS})
- set(QT_NO_CREATE_TARGETS OFF)
-
- # Only search in path provided by QT_HOST_PATH. We need to do it with CMAKE_PREFIX_PATH
- # instead of PATHS option, because any find_dependency call inside a Tools package would
- # not get the proper prefix when using PATHS.
- set(BACKUP_CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH})
- set(CMAKE_PREFIX_PATH "${QT_HOST_PATH}")
-
- # Search both with sysroots prepended as well as in the host system. When cross compiling
- # the mode_package might be set to ONLY only, and the Qt6 tools packages are actually
- # in the host system.
- set(BACKUP_CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ${CMAKE_FIND_ROOT_PATH_MODE_PACKAGE})
- set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE "BOTH")
- find_package(
- ${tools_package_name}
- ${PROJECT_VERSION}
- NO_PACKAGE_ROOT_PATH
- NO_CMAKE_ENVIRONMENT_PATH
- NO_SYSTEM_ENVIRONMENT_PATH
- NO_CMAKE_PACKAGE_REGISTRY
- NO_CMAKE_SYSTEM_PATH
- NO_CMAKE_SYSTEM_PACKAGE_REGISTRY)
- set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE "${BACKUP_CMAKE_FIND_ROOT_PATH_MODE_PACKAGE}")
- set(CMAKE_PREFIX_PATH "${BACKUP_CMAKE_PREFIX_PATH}")
- set(QT_NO_CREATE_TARGETS ${BACKUP_QT_NO_CREATE_TARGETS})
-
- if(${${tools_package_name}_FOUND} AND TARGET ${full_name})
- # Even if the tool is already visible, make sure that our modules remain associated
- # with the tools.
- qt_internal_append_known_modules_with_tools("${arg_TOOLS_TARGET}")
- get_property(path TARGET ${full_name} PROPERTY LOCATION)
- message(STATUS "${full_name} was found at ${path} using package ${tools_package_name}.")
- if (NOT QT_BUILD_TOOLS_WHEN_CROSSCOMPILING)
- return()
- endif()
- endif()
+ set(install_dir "${INSTALL_BINDIR}")
+ if(arg_INSTALL_DIR)
+ set(install_dir "${arg_INSTALL_DIR}")
endif()
- if(NOT QT_WILL_BUILD_TOOLS)
- message(FATAL_ERROR "The tool \"${full_name}\" was not found in the "
- "${tools_package_name} package. "
- "Package found: ${${tools_package_name}_FOUND}")
- else()
- if(QT_BUILD_TOOLS_WHEN_CROSSCOMPILING)
- message(STATUS "Tool '${target_name}' will be cross-built from source.")
- else()
- message(STATUS "Tool '${full_name}' will be built from source.")
- endif()
- endif()
+ set(output_dir "${QT_BUILD_DIR}/${install_dir}")
- set(disable_autogen_tools "${arg_DISABLE_AUTOGEN_TOOLS}")
- if (arg_BOOTSTRAP)
- set(corelib ${QT_CMAKE_EXPORT_NAMESPACE}::Bootstrap)
- list(APPEND disable_autogen_tools "uic" "moc" "rcc")
- else()
- set(corelib ${QT_CMAKE_EXPORT_NAMESPACE}::Core)
+ if(arg_PUBLIC_LIBRARIES)
+ message(WARNING
+ "qt_internal_add_tool's PUBLIC_LIBRARIES option is deprecated, and will be "
+ "removed in a future Qt version. Use the LIBRARIES option instead.")
endif()
- set(bootstrap "")
- if(arg_BOOTSTRAP)
- set(bootstrap BOOTSTRAP)
- endif()
+ qt_internal_library_deprecation_level(deprecation_define)
- set(install_dir "${INSTALL_BINDIR}")
- if(arg_INSTALL_DIR)
- set(install_dir "${arg_INSTALL_DIR}")
+ if(arg_NO_UNITY_BUILD)
+ set(arg_NO_UNITY_BUILD "NO_UNITY_BUILD")
+ else()
+ set(arg_NO_UNITY_BUILD "")
endif()
- qt_internal_add_executable("${target_name}" OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${install_dir}"
- ${bootstrap}
+ qt_internal_add_executable("${target_name}"
+ OUTPUT_DIRECTORY "${output_dir}"
+ ${exceptions}
NO_INSTALL
+ ${arg_NO_UNITY_BUILD}
SOURCES ${arg_SOURCES}
+ NO_PCH_SOURCES ${arg_NO_PCH_SOURCES}
+ NO_UNITY_BUILD_SOURCES ${arg_NO_UNITY_BUILD_SOURCES}
INCLUDE_DIRECTORIES
${arg_INCLUDE_DIRECTORIES}
DEFINES
- QT_USE_QSTRINGBUILDER
${arg_DEFINES}
- PUBLIC_LIBRARIES ${corelib}
- LIBRARIES ${arg_LIBRARIES} Qt::PlatformToolInternal
+ ${deprecation_define}
+ ${corelib}
+ LIBRARIES
+ ${arg_LIBRARIES}
+ ${arg_PUBLIC_LIBRARIES}
+ Qt::PlatformToolInternal
COMPILE_OPTIONS ${arg_COMPILE_OPTIONS}
LINK_OPTIONS ${arg_LINK_OPTIONS}
MOC_OPTIONS ${arg_MOC_OPTIONS}
DISABLE_AUTOGEN_TOOLS ${disable_autogen_tools}
- TARGET_VERSION "${arg_TARGET_VERSION}"
- TARGET_PRODUCT "${arg_TARGET_PRODUCT}"
- TARGET_DESCRIPTION "${arg_TARGET_DESCRIPTION}"
- TARGET_COMPANY "${arg_TARGET_COMPANY}"
- TARGET_COPYRIGHT "${arg_TARGET_COPYRIGHT}"
+ TARGET_VERSION ${arg_TARGET_VERSION}
+ TARGET_PRODUCT ${arg_TARGET_PRODUCT}
+ TARGET_DESCRIPTION ${arg_TARGET_DESCRIPTION}
+ TARGET_COMPANY ${arg_TARGET_COMPANY}
+ TARGET_COPYRIGHT ${arg_TARGET_COPYRIGHT}
+ # If you are putting anything after these, make sure that
+ # qt_set_target_info_properties knows how to process them
)
qt_internal_add_target_aliases("${target_name}")
_qt_internal_apply_strict_cpp("${target_name}")
+ qt_internal_adjust_main_config_runtime_output_dir("${target_name}" "${output_dir}")
+
+ if (WIN32)
+ _qt_internal_generate_longpath_win32_rc_file_and_manifest("${target_name}")
+ endif()
+
+ set_target_properties(${target_name} PROPERTIES
+ _qt_package_version "${PROJECT_VERSION}"
+ )
+ set_property(TARGET ${target_name}
+ APPEND PROPERTY
+ EXPORT_PROPERTIES "_qt_package_version")
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.19.0" AND QT_FEATURE_debug_and_release)
set_property(TARGET "${target_name}"
@@ -167,7 +167,7 @@ function(qt_internal_add_tool target_name)
if(TARGET host_tools)
add_dependencies(host_tools "${target_name}")
- if(bootstrap)
+ if(arg_CORE_LIBRARY STREQUAL "Bootstrap")
add_dependencies(bootstrap_tools "${target_name}")
endif()
endif()
@@ -178,16 +178,16 @@ function(qt_internal_add_tool target_name)
)
endif()
+ if(arg_EXTRA_CMAKE_INCLUDES)
+ set_target_properties(${target_name} PROPERTIES
+ EXTRA_CMAKE_INCLUDES "${arg_EXTRA_CMAKE_INCLUDES}"
+ )
+ endif()
+
if(arg_USER_FACING)
set_property(GLOBAL APPEND PROPERTY QT_USER_FACING_TOOL_TARGETS ${target_name})
endif()
- # If building with a multi-config configuration, the main configuration tool will be placed in
- # ./bin, while the rest will be in <CONFIG> specific subdirectories.
- qt_get_tool_cmake_configuration(tool_cmake_configuration)
- set_target_properties("${target_name}" PROPERTIES
- RUNTIME_OUTPUT_DIRECTORY_${tool_cmake_configuration} "${QT_BUILD_DIR}/${install_dir}"
- )
if(NOT arg_NO_INSTALL AND arg_TOOLS_TARGET)
# Assign a tool to an export set, and mark the module to which the tool belongs.
@@ -224,17 +224,74 @@ function(qt_internal_add_tool target_name)
unset(install_initial_call_args)
endforeach()
+ if(arg_INSTALL_VERSIONED_LINK)
+ qt_internal_install_versioned_link(WORKING_DIRECTORY "${install_dir}"
+ TARGETS "${target_name}")
+ endif()
+
qt_apply_rpaths(TARGET "${target_name}" INSTALL_PATH "${install_dir}" RELATIVE_RPATH)
+ qt_internal_apply_staging_prefix_build_rpath_workaround()
+ endif()
+ if(arg_TRY_RUN AND WIN32 AND NOT CMAKE_CROSSCOMPILING)
+ if(NOT arg_TRY_RUN_FLAGS)
+ set(arg_TRY_RUN_FLAGS "-v")
+ endif()
+ _qt_internal_add_try_run_post_build("${target_name}" "${arg_TRY_RUN_FLAGS}")
endif()
- qt_enable_separate_debug_info(${target_name} "${install_dir}")
+ qt_enable_separate_debug_info(${target_name} "${install_dir}" QT_EXECUTABLE)
qt_internal_install_pdb_files(${target_name} "${install_dir}")
endfunction()
+function(_qt_internal_add_try_run_post_build target try_run_flags)
+ qt_internal_get_upper_case_main_cmake_configuration(main_cmake_configuration)
+ get_target_property(target_out_dir ${target}
+ RUNTIME_OUTPUT_DIRECTORY_${main_cmake_configuration})
+ get_target_property(target_bin_dir ${target}
+ BINARY_DIR)
+
+ set(try_run_scripts_path "${target_bin_dir}/${target}_try_run.bat")
+ # The only reason -h is passed is because some of the tools, e.g., moc
+ # wait for an input without any arguments.
+
+ qt_configure_file(OUTPUT "${try_run_scripts_path}"
+ CONTENT "@echo off
+
+${target_out_dir}/${target}.exe ${try_run_flags} > nul 2>&1
+
+if \"%errorlevel%\" == \"-1073741515\" (
+echo
+echo '${target}' is built successfully, but some of the libraries
+echo necessary for running it are missing. If you are building Qt with
+echo 3rdparty libraries, make sure that you add their directory to the
+echo PATH environment variable.
+echo
+exit /b %errorlevel%
+)
+echo. > ${target_bin_dir}/${target}_try_run_passed"
+ )
+
+ add_custom_command(
+ OUTPUT
+ ${target_bin_dir}/${target}_try_run_passed
+ DEPENDS
+ ${target}
+ COMMAND
+ ${CMAKE_COMMAND} -E env QT_COMMAND_LINE_PARSER_NO_GUI_MESSAGE_BOXES=1
+ ${try_run_scripts_path}
+ COMMENT
+ "Testing ${target} by trying to run it."
+ VERBATIM
+ )
+
+ add_custom_target(${target}_try_run ALL
+ DEPENDS ${target_bin_dir}/${target}_try_run_passed)
+endfunction()
+
function(qt_export_tools module_name)
- # Bail out when cross-compiling, unless QT_BUILD_TOOLS_WHEN_CROSSCOMPILING is on.
- if(CMAKE_CROSSCOMPILING AND NOT QT_BUILD_TOOLS_WHEN_CROSSCOMPILING)
+ # Bail out when not building tools.
+ if(NOT QT_WILL_BUILD_TOOLS)
return()
endif()
@@ -262,6 +319,9 @@ function(qt_export_tools module_name)
# Additional cmake files to install
set(extra_cmake_files "")
+ set(extra_cmake_includes "")
+
+ set(first_tool_package_version "")
foreach(tool_name ${QT_KNOWN_MODULE_${module_name}_TOOLS})
# Specific tools can have package dependencies.
@@ -279,30 +339,33 @@ function(qt_export_tools module_name)
endforeach()
endif()
- if (CMAKE_CROSSCOMPILING AND QT_BUILD_TOOLS_WHEN_CROSSCOMPILING)
+ get_target_property(_extra_cmake_includes "${tool_name}" EXTRA_CMAKE_INCLUDES)
+ if(_extra_cmake_includes)
+ list(APPEND extra_cmake_includes "${_extra_cmake_includes}")
+ endif()
+
+ if (QT_WILL_RENAME_TOOL_TARGETS)
string(REGEX REPLACE "_native$" "" tool_name ${tool_name})
endif()
set(extra_cmake_statements "${extra_cmake_statements}
-if (NOT QT_NO_CREATE_TARGETS)
- get_property(is_global TARGET ${INSTALL_CMAKE_NAMESPACE}::${tool_name} PROPERTY IMPORTED_GLOBAL)
- if(NOT is_global)
- set_property(TARGET ${INSTALL_CMAKE_NAMESPACE}::${tool_name} PROPERTY IMPORTED_GLOBAL TRUE)
- endif()
+if(NOT QT_NO_CREATE_TARGETS AND ${INSTALL_CMAKE_NAMESPACE}${target}_FOUND)
+ __qt_internal_promote_target_to_global(${INSTALL_CMAKE_NAMESPACE}::${tool_name})
endif()
")
list(APPEND tool_targets "${QT_CMAKE_EXPORT_NAMESPACE}::${tool_name}")
list(APPEND tool_targets_non_prefixed "${tool_name}")
+
+ if(NOT first_tool_package_version)
+ qt_internal_get_package_version_of_target("${tool_name}" tool_package_version)
+ if(tool_package_version)
+ set(first_tool_package_version "${tool_package_version}")
+ endif()
+ endif()
endforeach()
string(APPEND extra_cmake_statements
"set(${QT_CMAKE_EXPORT_NAMESPACE}${module_name}Tools_TARGETS \"${tool_targets}\")")
- set(extra_cmake_includes "")
- foreach(extra_cmake_file ${extra_cmake_files})
- get_filename_component(extra_cmake_include "${extra_cmake_file}" NAME)
- list(APPEND extra_cmake_includes "${extra_cmake_include}")
- endforeach()
-
# Extract package dependencies that were determined in QtPostProcess, but only if ${module_name}
# is an actual target.
# module_name can be a non-existent target, if the tool doesn't have an existing associated
@@ -343,16 +406,42 @@ endif()
"${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}Config.cmake"
INSTALL_DESTINATION "${config_install_dir}"
)
+
+ # There might be Tools packages which don't have a corresponding real module_name target, like
+ # WaylandScannerTools.
+ # In that case we'll use the package version of the first tool that belongs to that package.
+ if(TARGET "${module_name}")
+ qt_internal_get_package_version_of_target("${module_name}" tools_package_version)
+ elseif(first_tool_package_version)
+ set(tools_package_version "${first_tool_package_version}")
+ else()
+ # This should never happen, because tools_package_version should always have at least some
+ # value. Issue an assertion message just in case the pre-condition ever changes.
+ set(tools_package_version "${PROJECT_VERSION}")
+ if(FEATURE_developer_build)
+ message(WARNING
+ "Could not determine package version of tools package ${module_name}. "
+ "Defaulting to project version ${PROJECT_VERSION}.")
+ endif()
+ endif()
+ message(TRACE
+ "Exporting tools package ${module_name}Tools with package version ${tools_package_version}"
+ "\n included targets: ${tool_targets_non_prefixed}")
write_basic_package_version_file(
- "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersion.cmake"
- VERSION ${PROJECT_VERSION}
+ "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersionImpl.cmake"
+ VERSION "${tools_package_version}"
COMPATIBILITY AnyNewerVersion
ARCH_INDEPENDENT
)
+ qt_internal_write_qt_package_version_file(
+ "${INSTALL_CMAKE_NAMESPACE}${target}"
+ "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersion.cmake"
+ )
qt_install(FILES
"${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}Config.cmake"
"${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersion.cmake"
+ "${config_build_dir}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigVersionImpl.cmake"
DESTINATION "${config_install_dir}"
COMPONENT Devel
)
@@ -382,28 +471,13 @@ endif()
)
endfunction()
-function(qt_get_tool_cmake_configuration out_var)
- qt_get_main_cmake_configuration("${out_var}")
- string(TOUPPER "${${out_var}}" upper_config)
- set("${out_var}" "${upper_config}" PARENT_SCOPE)
-endfunction()
-
-function(qt_get_main_cmake_configuration out_var)
- if(CMAKE_BUILD_TYPE)
- set(config "${CMAKE_BUILD_TYPE}")
- elseif(QT_MULTI_CONFIG_FIRST_CONFIG)
- set(config "${QT_MULTI_CONFIG_FIRST_CONFIG}")
- endif()
- set("${out_var}" "${config}" PARENT_SCOPE)
-endfunction()
-
# Returns the target name for the tool with the given name.
#
# In most cases, the target name is the same as the tool name.
# If the user specifies to build tools when cross-compiling, then the
# suffix "_native" is appended.
function(qt_get_tool_target_name out_var name)
- if (CMAKE_CROSSCOMPILING AND QT_BUILD_TOOLS_WHEN_CROSSCOMPILING)
+ if (QT_WILL_RENAME_TOOL_TARGETS)
set(${out_var} ${name}_native PARENT_SCOPE)
else()
set(${out_var} ${name} PARENT_SCOPE)
@@ -414,22 +488,56 @@ endfunction()
# This is the inverse of qt_get_tool_target_name.
function(qt_tool_target_to_name out_var target)
set(name ${target})
- if (CMAKE_CROSSCOMPILING AND QT_BUILD_TOOLS_WHEN_CROSSCOMPILING)
+ if (QT_WILL_RENAME_TOOL_TARGETS)
string(REGEX REPLACE "_native$" "" name ${target})
endif()
set(${out_var} ${name} PARENT_SCOPE)
endfunction()
-# Sets QT_WILL_BUILD_TOOLS if tools will be built.
+# Sets QT_WILL_BUILD_TOOLS if tools will be built and QT_WILL_RENAME_TOOL_TARGETS
+# if those tools have replaced naming.
function(qt_check_if_tools_will_be_built)
- if(QT_FORCE_FIND_TOOLS OR (CMAKE_CROSSCOMPILING AND NOT QT_BUILD_TOOLS_WHEN_CROSSCOMPILING))
+ # By default, we build our own tools unless we're cross-building or QT_HOST_PATH is set.
+ set(need_target_rename FALSE)
+ set(require_find_tools FALSE)
+ if(CMAKE_CROSSCOMPILING)
set(will_build_tools FALSE)
+ if(QT_FORCE_BUILD_TOOLS)
+ set(will_build_tools TRUE)
+ set(need_target_rename TRUE)
+ endif()
+ set(require_find_tools TRUE)
else()
- set(will_build_tools TRUE)
+ if(QT_HOST_PATH)
+ set(will_build_tools FALSE)
+ else()
+ set(will_build_tools TRUE)
+ endif()
+ if(QT_FORCE_FIND_TOOLS)
+ set(will_build_tools FALSE)
+ set(require_find_tools TRUE)
+ endif()
+ if(QT_FORCE_BUILD_TOOLS)
+ set(will_build_tools TRUE)
+ set(need_target_rename TRUE)
+ endif()
endif()
+
+ set_property(GLOBAL PROPERTY qt_require_find_tools "${require_find_tools}")
+
set(QT_WILL_BUILD_TOOLS ${will_build_tools} CACHE INTERNAL "Are tools going to be built" FORCE)
+ set(QT_WILL_RENAME_TOOL_TARGETS ${need_target_rename} CACHE INTERNAL
+ "Do tool targets need to be renamed" FORCE)
endfunction()
+# Use this macro to exit a file or function scope unless we're building tools. This is supposed to
+# be called after qt_internal_add_tools() to avoid special-casing operations on imported targets.
+macro(qt_internal_return_unless_building_tools)
+ if(NOT QT_WILL_BUILD_TOOLS)
+ return()
+ endif()
+endmacro()
+
# Equivalent of qmake's qtNomakeTools(directory1 directory2).
# If QT_BUILD_TOOLS_BY_DEFAULT is true, then targets within the given directories will be excluded
# from the default 'all' target, as well as from install phase. The private variable is checked by
@@ -443,3 +551,219 @@ function(qt_exclude_tool_directories_from_default_target)
set(__qt_exclude_tool_directories "${absolute_path_directories}" PARENT_SCOPE)
endif()
endfunction()
+
+function(qt_internal_find_tool out_var target_name tools_target)
+ qt_tool_target_to_name(name ${target_name})
+
+ # Handle case when a tool does not belong to a module and it can't be built either (like
+ # during a cross-compile).
+ if(NOT tools_target AND NOT QT_WILL_BUILD_TOOLS)
+ message(FATAL_ERROR "The tool \"${name}\" has not been assigned to a module via"
+ " TOOLS_TARGET (so it can't be found) and it can't be built"
+ " (QT_WILL_BUILD_TOOLS is ${QT_WILL_BUILD_TOOLS}).")
+ endif()
+
+ if(NOT CMAKE_CROSSCOMPILING)
+ if(QT_INTERNAL_FORCE_FIND_HOST_TOOLS_MODULE_LIST AND
+ NOT "${tools_target}" IN_LIST QT_INTERNAL_FORCE_FIND_HOST_TOOLS_MODULE_LIST)
+ message(STATUS "Tool '${full_name}' will be built from source.")
+ set(${out_var} "TRUE" PARENT_SCOPE)
+ return()
+ endif()
+ endif()
+
+ if(QT_WILL_RENAME_TOOL_TARGETS AND (name STREQUAL target_name))
+ message(FATAL_ERROR
+ "qt_internal_add_tool must be passed a target obtained from qt_get_tool_target_name.")
+ endif()
+
+ set(full_name "${QT_CMAKE_EXPORT_NAMESPACE}::${name}")
+ set(imported_tool_target_already_found FALSE)
+
+ # This condition can only be TRUE if a previous find_package(Qt6${tools_target}Tools)
+ # was already done. That can happen if QT_FORCE_FIND_TOOLS was ON or we're cross-compiling.
+ # In such a case, we need to exit early if we're not going to also build the tools.
+ if(TARGET ${full_name})
+ get_property(path TARGET ${full_name} PROPERTY LOCATION)
+ message(STATUS "Tool '${full_name}' was found at ${path}.")
+ set(imported_tool_target_already_found TRUE)
+ if(NOT QT_WILL_BUILD_TOOLS)
+ set(${out_var} "FALSE" PARENT_SCOPE)
+ return()
+ endif()
+ endif()
+
+ # We need to search for the host Tools package when doing a cross-build
+ # or when QT_FORCE_FIND_TOOLS is ON.
+ # As an optimiziation, we don't search for the package one more time if the target
+ # was already brought into scope from a previous find_package.
+ set(search_for_host_package FALSE)
+ if(NOT QT_WILL_BUILD_TOOLS OR QT_WILL_RENAME_TOOL_TARGETS)
+ set(search_for_host_package TRUE)
+ endif()
+ if(search_for_host_package AND NOT imported_tool_target_already_found)
+ set(tools_package_name "${INSTALL_CMAKE_NAMESPACE}${tools_target}Tools")
+ message(STATUS "Searching for tool '${full_name}' in package ${tools_package_name}.")
+
+ # Create the tool targets, even if QT_NO_CREATE_TARGETS is set.
+ # Otherwise targets like Qt6::moc are not available in a top-level cross-build.
+ set(BACKUP_QT_NO_CREATE_TARGETS ${QT_NO_CREATE_TARGETS})
+ set(QT_NO_CREATE_TARGETS OFF)
+
+ # When cross-compiling, we want to search for Tools packages in QT_HOST_PATH.
+ # To do that, we override CMAKE_PREFIX_PATH and CMAKE_FIND_ROOT_PATH.
+ #
+ # We don't use find_package + PATHS option because any recursive find_dependency call
+ # inside a Tools package would not inherit the initial PATHS value given.
+ # TODO: Potentially we could set a global __qt_cmake_host_dir var like we currently
+ # do with _qt_cmake_dir in Qt6Config and change all our host tool find_package calls
+ # everywhere to specify that var in PATHS.
+ #
+ # Note though that due to path rerooting issue in
+ # https://gitlab.kitware.com/cmake/cmake/-/issues/21937
+ # we have to append a lib/cmake suffix to CMAKE_PREFIX_PATH so the value does not get
+ # rerooted on top of CMAKE_FIND_ROOT_PATH.
+ # Use QT_HOST_PATH_CMAKE_DIR for the suffix when available (it would be set by
+ # the qt.toolchain.cmake file when building other repos or given by the user when
+ # configuring qtbase) or derive it from from the Qt6HostInfo package which is
+ # found in QtSetup.
+ set(${tools_package_name}_BACKUP_CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH})
+ set(${tools_package_name}_BACKUP_CMAKE_FIND_ROOT_PATH "${CMAKE_FIND_ROOT_PATH}")
+ if(QT_HOST_PATH_CMAKE_DIR)
+ set(qt_host_path_cmake_dir_absolute "${QT_HOST_PATH_CMAKE_DIR}")
+ elseif(Qt${PROJECT_VERSION_MAJOR}HostInfo_DIR)
+ get_filename_component(qt_host_path_cmake_dir_absolute
+ "${Qt${PROJECT_VERSION_MAJOR}HostInfo_DIR}/.." ABSOLUTE)
+ else()
+ # This should never happen, serves as an assert.
+ message(FATAL_ERROR
+ "Neither QT_HOST_PATH_CMAKE_DIR nor "
+ "Qt${PROJECT_VERSION_MAJOR}HostInfo_DIR available.")
+ endif()
+ set(CMAKE_PREFIX_PATH "${qt_host_path_cmake_dir_absolute}")
+
+ # Look for tools in additional host Qt installations. This is done for conan support where
+ # we have separate installation prefixes per package. For simplicity, we assume here that
+ # all host Qt installations use the same value of INSTALL_LIBDIR.
+ if(DEFINED QT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH)
+ file(RELATIVE_PATH rel_host_cmake_dir "${QT_HOST_PATH}"
+ "${qt_host_path_cmake_dir_absolute}")
+ foreach(host_path IN LISTS QT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH)
+ set(host_cmake_dir "${host_path}/${rel_host_cmake_dir}")
+ list(PREPEND CMAKE_PREFIX_PATH "${host_cmake_dir}")
+ endforeach()
+
+ list(PREPEND CMAKE_FIND_ROOT_PATH "${QT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH}")
+ endif()
+ list(PREPEND CMAKE_FIND_ROOT_PATH "${QT_HOST_PATH}")
+
+ find_package(
+ ${tools_package_name}
+ ${PROJECT_VERSION}
+ NO_PACKAGE_ROOT_PATH
+ NO_CMAKE_ENVIRONMENT_PATH
+ NO_SYSTEM_ENVIRONMENT_PATH
+ NO_CMAKE_PACKAGE_REGISTRY
+ NO_CMAKE_SYSTEM_PATH
+ NO_CMAKE_SYSTEM_PACKAGE_REGISTRY)
+
+ # Restore backups.
+ set(CMAKE_FIND_ROOT_PATH "${${tools_package_name}_BACKUP_CMAKE_FIND_ROOT_PATH}")
+ set(CMAKE_PREFIX_PATH "${${tools_package_name}_BACKUP_CMAKE_PREFIX_PATH}")
+ set(QT_NO_CREATE_TARGETS ${BACKUP_QT_NO_CREATE_TARGETS})
+
+ if(${${tools_package_name}_FOUND} AND TARGET ${full_name})
+ # Even if the tool is already visible, make sure that our modules remain associated
+ # with the tools.
+ qt_internal_append_known_modules_with_tools("${tools_target}")
+ get_property(path TARGET ${full_name} PROPERTY LOCATION)
+ message(STATUS "${full_name} was found at ${path} using package ${tools_package_name}.")
+ if (NOT QT_FORCE_BUILD_TOOLS)
+ set(${out_var} "FALSE" PARENT_SCOPE)
+ return()
+ endif()
+ endif()
+ endif()
+
+ get_property(require_find_tools GLOBAL PROPERTY qt_require_find_tools)
+ if(require_find_tools AND NOT TARGET ${full_name})
+ if(${${tools_package_name}_FOUND})
+ set(pkg_found_msg "")
+ string(APPEND pkg_found_msg
+ "the ${tools_package_name} package, but the package did not contain the tool. "
+ "Make sure that the host module ${tools_target} was built with all features "
+ "enabled (no explicitly disabled tools).")
+ else()
+ set(pkg_found_msg "")
+ string(APPEND pkg_found_msg
+ "the ${tools_package_name} package, but the package could not be found. "
+ "Make sure you have built and installed the host ${tools_target} module, "
+ "which will ensure the creation of the ${tools_package_name} package.")
+ endif()
+ message(FATAL_ERROR
+ "Failed to find the host tool \"${full_name}\". It is part of "
+ ${pkg_found_msg})
+ endif()
+
+ if(QT_WILL_BUILD_TOOLS)
+ message(STATUS "Tool '${full_name}' will be built from source.")
+ endif()
+ set(${out_var} "TRUE" PARENT_SCOPE)
+endfunction()
+
+# This function adds an internal tool that should be compiled at configure time.
+# TOOLS_TARGET
+# Specifies the module this tool belongs to. The Qt6${TOOLS_TARGET}Tools module
+# will then expose targets for this tool. Ignored if NO_INSTALL is set.
+function(qt_internal_add_configure_time_tool target_name)
+ set(one_value_args INSTALL_DIRECTORY TOOLS_TARGET CONFIG)
+ set(multi_value_args)
+ set(option_args NO_INSTALL)
+ cmake_parse_arguments(PARSE_ARGV 1 arg
+ "${option_args}" "${one_value_args}" "${multi_value_args}")
+
+ qt_internal_find_tool(will_build_tools ${target_name} "${arg_TOOLS_TARGET}")
+ if(NOT will_build_tools)
+ return()
+ endif()
+
+ qt_tool_target_to_name(name ${target_name})
+ set(extra_args "")
+ if(arg_NO_INSTALL OR NOT arg_TOOLS_TARGET)
+ list(APPEND extra_args "NO_INSTALL")
+ else()
+ set(install_dir "${INSTALL_BINDIR}")
+ if(arg_INSTALL_DIRECTORY)
+ set(install_dir "${arg_INSTALL_DIRECTORY}")
+ endif()
+ set(extra_args "INSTALL_DIRECTORY" "${install_dir}")
+ endif()
+
+ if(arg_CONFIG)
+ set(tool_config "${arg_CONFIG}")
+ elseif(QT_MULTI_CONFIG_FIRST_CONFIG)
+ set(tool_config "${arg_QT_MULTI_CONFIG_FIRST_CONFIG}")
+ else()
+ set(tool_config "${CMAKE_BUILD_TYPE}")
+ endif()
+
+ string(REPLACE "\\\;" "\\\\\\\;" unparsed_arguments "${arg_UNPARSED_ARGUMENTS}")
+ qt_internal_add_configure_time_executable(${target_name}
+ OUTPUT_NAME ${name}
+ CONFIG ${tool_config}
+ ${extra_args}
+ ${unparsed_arguments}
+ )
+
+ if(TARGET host_tools)
+ add_dependencies(host_tools "${target_name}_build")
+ endif()
+
+ if(NOT arg_NO_INSTALL AND arg_TOOLS_TARGET)
+ qt_internal_add_targets_to_additional_targets_export_file(
+ TARGETS ${target_name}
+ TARGET_EXPORT_NAMES ${QT_CMAKE_EXPORT_NAMESPACE}::${name}
+ EXPORT_NAME_PREFIX ${INSTALL_CMAKE_NAMESPACE}${arg_TOOLS_TARGET}Tools
+ )
+ endif()
+endfunction()