diff options
author | Alexandru Croitor <alexandru.croitor@qt.io> | 2023-11-16 19:57:25 +0100 |
---|---|---|
committer | Alexandru Croitor <alexandru.croitor@qt.io> | 2023-11-28 17:19:38 +0100 |
commit | 44dcf0dfd00326e8305171408e72da35f36d1437 (patch) | |
tree | 36ab47c307cef89d2aeed70ee6d7d99c6c8df646 /cmake | |
parent | 4e69f0ced50831ff397a307c0caf5130908b1724 (diff) |
CMake: Split and refactor QtBuildInternalsConfig.cmake
Move most of the code into two new files:
- QtBuildRepoHelpers.cmake
- QtBuildRepoExamplesHelpers.cmake
Task-number: QTBUG-86035
Change-Id: I48c4e7c64f0ffb600118172b8e69b26018f36ffb
Reviewed-by: Alexey Edelev <alexey.edelev@qt.io>
(cherry picked from commit 720b8ee6732f6fc8df5fb7133b569851356641b7)
Diffstat (limited to 'cmake')
-rw-r--r-- | cmake/QtBaseGlobalTargets.cmake | 2 | ||||
-rw-r--r-- | cmake/QtBuildInternals/QtBuildInternalsConfig.cmake | 1489 | ||||
-rw-r--r-- | cmake/QtBuildRepoExamplesHelpers.cmake | 590 | ||||
-rw-r--r-- | cmake/QtBuildRepoHelpers.cmake | 883 |
4 files changed, 1495 insertions, 1469 deletions
diff --git a/cmake/QtBaseGlobalTargets.cmake b/cmake/QtBaseGlobalTargets.cmake index 6c164a6109..1716c3a3fc 100644 --- a/cmake/QtBaseGlobalTargets.cmake +++ b/cmake/QtBaseGlobalTargets.cmake @@ -222,6 +222,8 @@ qt_copy_or_install(FILES cmake/QtBuildOptionsHelpers.cmake cmake/QtBuildPathsHelpers.cmake cmake/QtBuildInformation.cmake + cmake/QtBuildRepoExamplesHelpers.cmake + cmake/QtBuildRepoHelpers.cmake cmake/QtCMakeHelpers.cmake cmake/QtCMakeVersionHelpers.cmake cmake/QtCMakePackageVersionFile.cmake.in diff --git a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake index 633d1b3140..21ff79f9c1 100644 --- a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake +++ b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake @@ -4,11 +4,7 @@ # These values should be kept in sync with those in qtbase/.cmake.conf cmake_minimum_required(VERSION 3.16...3.21) -############################################### -# -# Macros and functions for building Qt modules -# -############################################### +set(QT_BACKUP_CMAKE_INSTALL_PREFIX_BEFORE_EXTRA_INCLUDE "${CMAKE_INSTALL_PREFIX}") # Recursively reads the dependencies section from dependencies.yaml in ${repo_dir} and returns the # list of dependencies, including transitive ones, in out_var. @@ -51,22 +47,23 @@ function(qt_internal_read_repo_dependencies out_var repo_dir) set(${out_var} "${dependencies}" PARENT_SCOPE) endfunction() -set(QT_BACKUP_CMAKE_INSTALL_PREFIX_BEFORE_EXTRA_INCLUDE "${CMAKE_INSTALL_PREFIX}") - +# This depends on qt_internal_read_repo_dependencies existing. if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/QtBuildInternalsExtra.cmake") include(${CMAKE_CURRENT_LIST_DIR}/QtBuildInternalsExtra.cmake) endif() -# The variables might have already been set in QtBuildInternalsExtra.cmake if the file is included -# while building a new module and not QtBase. In that case, stop overriding the value. -if(NOT INSTALL_CMAKE_NAMESPACE) - set(INSTALL_CMAKE_NAMESPACE "Qt${PROJECT_VERSION_MAJOR}" - CACHE STRING "CMake namespace [Qt${PROJECT_VERSION_MAJOR}]") -endif() -if(NOT QT_CMAKE_EXPORT_NAMESPACE) - set(QT_CMAKE_EXPORT_NAMESPACE "Qt${PROJECT_VERSION_MAJOR}" - CACHE STRING "CMake namespace used when exporting targets [Qt${PROJECT_VERSION_MAJOR}]") -endif() +macro(qt_internal_setup_cmake_and_export_namespace) + # The variables might have already been set in QtBuildInternalsExtra.cmake if the file is + # included while building a new module and not QtBase. In that case, stop overriding the value. + if(NOT INSTALL_CMAKE_NAMESPACE) + set(INSTALL_CMAKE_NAMESPACE "Qt${PROJECT_VERSION_MAJOR}" + CACHE STRING "CMake namespace [Qt${PROJECT_VERSION_MAJOR}]") + endif() + if(NOT QT_CMAKE_EXPORT_NAMESPACE) + set(QT_CMAKE_EXPORT_NAMESPACE "Qt${PROJECT_VERSION_MAJOR}" + CACHE STRING "CMake namespace used when exporting targets [Qt${PROJECT_VERSION_MAJOR}]") + endif() +endmacro() macro(qt_set_up_build_internals_paths) # Set up the paths for the cmake modules located in the prefix dir. Prepend, so the paths are @@ -98,1461 +95,15 @@ macro(qt_set_up_build_internals_paths) endif() endmacro() +qt_internal_setup_cmake_and_export_namespace() + # Set up the build internal paths unless explicitly requested not to. if(NOT QT_BUILD_INTERNALS_SKIP_CMAKE_MODULE_PATH_ADDITION) + # Depends on qt_internal_setup_cmake_and_export_namespace qt_set_up_build_internals_paths() endif() -# Define some constants to check for certain platforms, etc. -# Needs to be loaded before qt_repo_build() to handle require() clauses before even starting a repo -# build. -include(QtPlatformSupport) - -# Set FEATURE_${feature} if INPUT_${feature} is set in certain circumstances. -# Set FEATURE_${feature}_computed_from_input to TRUE or FALSE depending on whether the -# INPUT_${feature} value has overridden the FEATURE_${feature} variable. -# -# Needs to be in QtBuildInternalsConfig.cmake instead of QtFeature.cmake because it's used in -# qt_build_internals_disable_pkg_config_if_needed. -function(qt_internal_compute_feature_value_from_possible_input feature) - # If FEATURE_ is not defined try to use the INPUT_ variable to enable/disable feature. - # If FEATURE_ is defined and the configure script is being used (so - # QT_INTERNAL_CALLED_FROM_CONFIGURE is TRUE), ignore the FEATURE_ variable, and take into - # account the INPUT_ variable instead, because a command line argument takes priority over - # a pre-cached FEATURE_ variable. - if((NOT DEFINED FEATURE_${feature} OR QT_INTERNAL_CALLED_FROM_CONFIGURE) - AND DEFINED INPUT_${feature} - AND NOT "${INPUT_${feature}}" STREQUAL "undefined" - AND NOT "${INPUT_${feature}}" STREQUAL "") - if(INPUT_${feature}) - set(FEATURE_${feature} ON) - else() - set(FEATURE_${feature} OFF) - endif() - - set(FEATURE_${feature} "${FEATURE_${feature}}" PARENT_SCOPE) - set(FEATURE_${feature}_computed_from_input TRUE PARENT_SCOPE) - else() - set(FEATURE_${feature}_computed_from_input FALSE PARENT_SCOPE) - endif() -endfunction() - -function(qt_build_internals_disable_pkg_config_if_needed) - # pkg-config should not be used by default on Darwin and Windows platforms (and QNX), as defined - # in the qtbase/configure.json. Unfortunately by the time the feature is evaluated there are - # already a few find_package() calls that try to use the FindPkgConfig module. - # Thus, we have to duplicate the condition logic here and disable pkg-config for those platforms - # by default. - # We also need to check if the pkg-config executable exists, to mirror the condition test in - # configure.json. We do that by trying to find the executable ourselves, and not delegating to - # the FindPkgConfig module because that has more unwanted side-effects. - # - # Note that on macOS, if the pkg-config feature is enabled by the user explicitly, we will also - # tell CMake to consider paths like /usr/local (Homebrew) as system paths when looking for - # packages. - # We have to do that because disabling these paths but keeping pkg-config - # enabled won't enable finding all system libraries via pkg-config alone, many libraries can - # only be found via FooConfig.cmake files which means /usr/local should be in the system prefix - # path. - - set(pkg_config_enabled ON) - qt_build_internals_find_pkg_config_executable() - - if(APPLE OR WIN32 OR QNX OR ANDROID OR WASM OR (NOT PKG_CONFIG_EXECUTABLE)) - set(pkg_config_enabled OFF) - endif() - - # Features won't have been evaluated yet if this is the first run, have to evaluate this here - qt_internal_compute_feature_value_from_possible_input(pkg_config) - - # If user explicitly specified a value for the feature, honor it, even if it might break - # the build. - if(DEFINED FEATURE_pkg_config) - if(FEATURE_pkg_config) - set(pkg_config_enabled ON) - else() - set(pkg_config_enabled OFF) - endif() - endif() - - set(FEATURE_pkg_config "${pkg_config_enabled}" CACHE STRING "Using pkg-config") - if(NOT pkg_config_enabled) - qt_build_internals_disable_pkg_config() - else() - unset(PKG_CONFIG_EXECUTABLE CACHE) - endif() -endfunction() - -# This is a copy of the first few lines in FindPkgConfig.cmake. -function(qt_build_internals_find_pkg_config_executable) - # find pkg-config, use PKG_CONFIG if set - if((NOT PKG_CONFIG_EXECUTABLE) AND (NOT "$ENV{PKG_CONFIG}" STREQUAL "")) - set(PKG_CONFIG_EXECUTABLE "$ENV{PKG_CONFIG}" CACHE FILEPATH "pkg-config executable") - endif() - find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config DOC "pkg-config executable") - mark_as_advanced(PKG_CONFIG_EXECUTABLE) -endfunction() - -function(qt_build_internals_disable_pkg_config) - # Disable pkg-config by setting an empty executable path. There's no documented way to - # mark the package as not found, but we can force all pkg_check_modules calls to do nothing - # by setting the variable to an empty value. - set(PKG_CONFIG_EXECUTABLE "" CACHE STRING "Disabled pkg-config usage." FORCE) -endfunction() - -if(NOT QT_BUILD_INTERNALS_SKIP_PKG_CONFIG_ADJUSTMENT) - qt_build_internals_disable_pkg_config_if_needed() -endif() - -macro(qt_build_internals_find_pkg_config) - # Find package config once before any system prefix modifications. - find_package(PkgConfig QUIET) -endmacro() - -if(NOT QT_BUILD_INTERNALS_SKIP_FIND_PKG_CONFIG) - qt_build_internals_find_pkg_config() -endif() - -function(qt_build_internals_set_up_system_prefixes) - if(APPLE AND NOT FEATURE_pkg_config) - # Remove /usr/local and other paths like that which CMake considers as system prefixes on - # darwin platforms. CMake considers them as system prefixes, but in qmake / Qt land we only - # consider the SDK path as a system prefix. - # 3rd party libraries in these locations should not be picked up when building Qt, - # unless opted-in via the pkg-config feature, which in turn will disable this behavior. - # - # Note that we can't remove /usr as a system prefix path, because many programs won't be - # found then (e.g. perl). - set(QT_CMAKE_SYSTEM_PREFIX_PATH_BACKUP "${CMAKE_SYSTEM_PREFIX_PATH}" PARENT_SCOPE) - set(QT_CMAKE_SYSTEM_FRAMEWORK_PATH_BACKUP "${CMAKE_SYSTEM_FRAMEWORK_PATH}" PARENT_SCOPE) - - list(REMOVE_ITEM CMAKE_SYSTEM_PREFIX_PATH - "/usr/local" # Homebrew - "/opt/homebrew" # Apple Silicon Homebrew - "/usr/X11R6" - "/usr/pkg" - "/opt" - "/sw" # Fink - "/opt/local" # MacPorts - ) - if(_CMAKE_INSTALL_DIR) - list(REMOVE_ITEM CMAKE_SYSTEM_PREFIX_PATH "${_CMAKE_INSTALL_DIR}") - endif() - list(REMOVE_ITEM CMAKE_SYSTEM_FRAMEWORK_PATH "~/Library/Frameworks") - set(CMAKE_SYSTEM_PREFIX_PATH "${CMAKE_SYSTEM_PREFIX_PATH}" PARENT_SCOPE) - set(CMAKE_SYSTEM_FRAMEWORK_PATH "${CMAKE_SYSTEM_FRAMEWORK_PATH}" PARENT_SCOPE) - - # Also tell qt_find_package() not to use PATH when looking for packages. - # We can't simply set CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH to OFF because that will break - # find_program(), and for instance ccache won't be found. - # That's why we set a different variable which is used by qt_find_package. - set(QT_NO_USE_FIND_PACKAGE_SYSTEM_ENVIRONMENT_PATH "ON" PARENT_SCOPE) - endif() -endfunction() - -if(NOT QT_BUILD_INTERNALS_SKIP_SYSTEM_PREFIX_ADJUSTMENT) - qt_build_internals_set_up_system_prefixes() -endif() - -# The macro sets all the necessary pre-conditions and setup consistent environment for building -# the Qt repository. It has to be called right after the find_package(Qt6 COMPONENTS BuildInternals) -# call. Otherwise we cannot make sure that all the required policies will be applied to the Qt -# components that are involved in build procedure. -macro(qt_internal_project_setup) - # Check for the minimum CMake version. - include(QtCMakeVersionHelpers) - qt_internal_require_suitable_cmake_version() - qt_internal_upgrade_cmake_policies() -endmacro() - -macro(qt_build_internals_set_up_private_api) - # TODO: this call needs to be removed once all repositories got the qtbase update - qt_internal_project_setup() - - # Qt specific setup common for all modules: - include(QtSetup) - include(FeatureSummary) - - # Optionally include a repo specific Setup module. - include(${PROJECT_NAME}Setup OPTIONAL) - include(QtRepoSetup OPTIONAL) - - # Find Apple frameworks if needed. - qt_find_apple_system_frameworks() - - # Decide whether tools will be built. - qt_check_if_tools_will_be_built() -endmacro() - -# add toplevel targets for each subdirectory, e.g. qtbase_src -function(qt_build_internals_add_toplevel_targets) - set(qt_repo_target_all "") - get_directory_property(directories DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" SUBDIRECTORIES) - foreach(directory IN LISTS directories) - set(qt_repo_targets "") - get_filename_component(qt_repo_target_basename ${directory} NAME) - _qt_internal_collect_buildsystem_targets(qt_repo_targets "${directory}" EXCLUDE UTILITY) - if (qt_repo_targets) - set(qt_repo_target_name "${qt_repo_targets_name}_${qt_repo_target_basename}") - message(DEBUG "${qt_repo_target_name} depends on ${qt_repo_targets}") - add_custom_target("${qt_repo_target_name}" - COMMENT "Building everything in ${qt_repo_targets_name}/${qt_repo_target_basename}") - add_dependencies("${qt_repo_target_name}" ${qt_repo_targets}) - list(APPEND qt_repo_target_all "${qt_repo_target_name}") - - # Create special dependency target for External Project examples excluding targets - # marked as skipped. - if(qt_repo_target_basename STREQUAL "src") - set(qt_repo_target_name - "${qt_repo_targets_name}_${qt_repo_target_basename}_for_examples") - add_custom_target("${qt_repo_target_name}") - - set(unskipped_targets "") - foreach(target IN LISTS qt_repo_targets) - if(TARGET "${target}") - qt_internal_is_target_skipped_for_examples("${target}" is_skipped) - if(NOT is_skipped) - list(APPEND unskipped_targets "${target}") - endif() - endif() - endforeach() - if(unskipped_targets) - add_dependencies("${qt_repo_target_name}" ${unskipped_targets}) - endif() - endif() - endif() - - endforeach() - if (qt_repo_target_all) - # Note qt_repo_targets_name is different from qt_repo_target_name that is used above. - add_custom_target("${qt_repo_targets_name}" - COMMENT "Building everything in ${qt_repo_targets_name}") - add_dependencies("${qt_repo_targets_name}" ${qt_repo_target_all}) - message(DEBUG "${qt_repo_targets_name} depends on ${qt_repo_target_all}") - endif() -endfunction() - -macro(qt_enable_cmake_languages) - set(__qt_required_language_list C CXX) - set(__qt_platform_required_language_list ) - - if(APPLE) - list(APPEND __qt_platform_required_language_list OBJC OBJCXX) - endif() - - foreach(__qt_lang ${__qt_required_language_list}) - enable_language(${__qt_lang}) - endforeach() - - foreach(__qt_lang ${__qt_platform_required_language_list}) - enable_language(${__qt_lang}) - endforeach() - - # The qtbase call is handled in qtbase/CMakeLists.txt. - # This call is used for projects other than qtbase, including for other project's standalone - # tests. - # Because the function uses QT_FEATURE_foo values, it's important that find_package(Qt6Core) is - # called before this function. but that's usually the case for Qt repos. - if(NOT PROJECT_NAME STREQUAL "QtBase") - qt_internal_set_up_config_optimizations_like_in_qmake() - endif() -endmacro() - -# Minimum setup required to have any CMakeList.txt build as as a standalone -# project after importing BuildInternals -macro(qt_prepare_standalone_project) - qt_set_up_build_internals_paths() - qt_build_internals_set_up_private_api() - qt_enable_cmake_languages() -endmacro() - -# Define a repo target set, and store accompanying information. -# -# A repo target set is a subset of targets in a Qt module repository. To build a repo target set, -# set QT_BUILD_SINGLE_REPO_TARGET_SET to the name of the repo target set. -# -# This function is to be called in the top-level project file of a repository, -# before qt_internal_prepare_single_repo_target_set_build() -# -# This function stores information in variables of the parent scope. -# -# Positional Arguments: -# name - The name of this repo target set. -# -# Named Arguments: -# DEPENDS - List of Qt6 COMPONENTS that are build dependencies of this repo target set. -function(qt_internal_define_repo_target_set name) - set(oneValueArgs DEPENDS) - set(prefix QT_REPO_TARGET_SET_) - cmake_parse_arguments(${prefix}${name} "" ${oneValueArgs} "" ${ARGN}) - foreach(arg IN LISTS oneValueArgs) - set(${prefix}${name}_${arg} ${${prefix}${name}_${arg}} PARENT_SCOPE) - endforeach() - set(QT_REPO_KNOWN_TARGET_SETS "${QT_REPO_KNOWN_TARGET_SETS};${name}" PARENT_SCOPE) -endfunction() - -# Setup a single repo target set build if QT_BUILD_SINGLE_REPO_TARGET_SET is defined. -# -# This macro must be called in the top-level project file of the repository after all repo target -# sets have been defined. -macro(qt_internal_prepare_single_repo_target_set_build) - if(DEFINED QT_BUILD_SINGLE_REPO_TARGET_SET) - if(NOT QT_BUILD_SINGLE_REPO_TARGET_SET IN_LIST QT_REPO_KNOWN_TARGET_SETS) - message(FATAL_ERROR - "Repo target set '${QT_BUILD_SINGLE_REPO_TARGET_SET}' is undefined.") - endif() - message(STATUS - "Preparing single repo target set build of ${QT_BUILD_SINGLE_REPO_TARGET_SET}") - if (NOT "${QT_REPO_TARGET_SET_${QT_BUILD_SINGLE_REPO_TARGET_SET}_DEPENDS}" STREQUAL "") - find_package(${INSTALL_CMAKE_NAMESPACE} ${PROJECT_VERSION} CONFIG REQUIRED - COMPONENTS ${QT_REPO_TARGET_SET_${QT_BUILD_SINGLE_REPO_TARGET_SET}_DEPENDS}) - endif() - endif() -endmacro() - -macro(qt_build_repo_begin) - set(QT_INTERNAL_REPO_POST_PROCESS_CALLED FALSE) - list(APPEND CMAKE_MESSAGE_CONTEXT "${PROJECT_NAME}") - - qt_build_internals_set_up_private_api() - - # Prevent installation in non-prefix builds. - # We need to associate targets with export names, and that is only possible to do with the - # install(TARGETS) command. But in a non-prefix build, we don't want to install anything. - # To make sure that developers don't accidentally run make install, add bail out code to - # cmake_install.cmake. - if(NOT QT_WILL_INSTALL) - # In a top-level build, print a message only in qtbase, which is the first repository. - if(NOT QT_SUPERBUILD OR (PROJECT_NAME STREQUAL "QtBase")) - install(CODE [[message(FATAL_ERROR - "Qt was configured as non-prefix build. " - "Installation is not supported for this arrangement.")]]) - endif() - - install(CODE [[return()]]) - endif() - - qt_enable_cmake_languages() - - qt_internal_generate_binary_strip_wrapper() - - # Add global docs targets that will work both for per-repo builds, and super builds. - if(NOT TARGET docs) - add_custom_target(docs) - add_custom_target(prepare_docs) - add_custom_target(generate_docs) - add_custom_target(html_docs) - add_custom_target(qch_docs) - add_custom_target(install_html_docs) - add_custom_target(install_qch_docs) - add_custom_target(install_docs) - add_dependencies(html_docs generate_docs) - add_dependencies(docs html_docs qch_docs) - add_dependencies(install_docs install_html_docs install_qch_docs) - endif() - - if(NOT TARGET sync_headers) - add_custom_target(sync_headers) - endif() - - # The special target that we use to sync 3rd-party headers before the gn run when building - # qtwebengine in top-level builds. - if(NOT TARGET thirdparty_sync_headers) - add_custom_target(thirdparty_sync_headers) - endif() - - # Add global qt_plugins, qpa_plugins and qpa_default_plugins convenience custom targets. - # Internal executables will add a dependency on the qpa_default_plugins target, - # so that building and running a test ensures it won't fail at runtime due to a missing qpa - # plugin. - if(NOT TARGET qt_plugins) - add_custom_target(qt_plugins) - add_custom_target(qpa_plugins) - add_custom_target(qpa_default_plugins) - endif() - - string(TOLOWER ${PROJECT_NAME} project_name_lower) - - # Target to build all plugins that are part of the current repo. - set(qt_repo_plugins "qt_plugins_${project_name_lower}") - if(NOT TARGET ${qt_repo_plugins}) - add_custom_target(${qt_repo_plugins}) - endif() - - # Target to build all plugins that are part of the current repo and the current repo's - # dependencies plugins. Used for external project example dependencies. - set(qt_repo_plugins_recursive "${qt_repo_plugins}_recursive") - if(NOT TARGET ${qt_repo_plugins_recursive}) - add_custom_target(${qt_repo_plugins_recursive}) - add_dependencies(${qt_repo_plugins_recursive} "${qt_repo_plugins}") - endif() - - qt_internal_read_repo_dependencies(qt_repo_deps "${PROJECT_SOURCE_DIR}") - if(qt_repo_deps) - foreach(qt_repo_dep IN LISTS qt_repo_deps) - if(TARGET qt_plugins_${qt_repo_dep}) - message(DEBUG - "${qt_repo_plugins_recursive} depends on qt_plugins_${qt_repo_dep}") - add_dependencies(${qt_repo_plugins_recursive} "qt_plugins_${qt_repo_dep}") - endif() - endforeach() - endif() - - set(qt_repo_targets_name ${project_name_lower}) - set(qt_docs_target_name docs_${project_name_lower}) - set(qt_docs_prepare_target_name prepare_docs_${project_name_lower}) - set(qt_docs_generate_target_name generate_docs_${project_name_lower}) - set(qt_docs_html_target_name html_docs_${project_name_lower}) - set(qt_docs_qch_target_name qch_docs_${project_name_lower}) - set(qt_docs_install_html_target_name install_html_docs_${project_name_lower}) - set(qt_docs_install_qch_target_name install_qch_docs_${project_name_lower}) - set(qt_docs_install_target_name install_docs_${project_name_lower}) - - add_custom_target(${qt_docs_target_name}) - add_custom_target(${qt_docs_prepare_target_name}) - add_custom_target(${qt_docs_generate_target_name}) - add_custom_target(${qt_docs_qch_target_name}) - add_custom_target(${qt_docs_html_target_name}) - add_custom_target(${qt_docs_install_html_target_name}) - add_custom_target(${qt_docs_install_qch_target_name}) - add_custom_target(${qt_docs_install_target_name}) - - add_dependencies(${qt_docs_generate_target_name} ${qt_docs_prepare_target_name}) - add_dependencies(${qt_docs_html_target_name} ${qt_docs_generate_target_name}) - add_dependencies(${qt_docs_target_name} ${qt_docs_html_target_name} ${qt_docs_qch_target_name}) - add_dependencies(${qt_docs_install_target_name} ${qt_docs_install_html_target_name} ${qt_docs_install_qch_target_name}) - - # Make top-level prepare_docs target depend on the repository-level prepare_docs_<repo> target. - add_dependencies(prepare_docs ${qt_docs_prepare_target_name}) - - # Make top-level install_*_docs targets depend on the repository-level install_*_docs targets. - add_dependencies(install_html_docs ${qt_docs_install_html_target_name}) - add_dependencies(install_qch_docs ${qt_docs_install_qch_target_name}) - - # Add host_tools meta target, so that developrs can easily build only tools and their - # dependencies when working in qtbase. - if(NOT TARGET host_tools) - add_custom_target(host_tools) - add_custom_target(bootstrap_tools) - endif() - - # Add benchmark meta target. It's collection of all benchmarks added/registered by - # 'qt_internal_add_benchmark' helper. - if(NOT TARGET benchmark) - add_custom_target(benchmark) - endif() - - if(QT_INTERNAL_SYNCED_MODULES) - set_property(GLOBAL PROPERTY _qt_synced_modules ${QT_INTERNAL_SYNCED_MODULES}) - endif() -endmacro() - -# Runs delayed actions on some of the Qt targets. -# Can be called either explicitly or as part of qt_build_repo_end(). -macro(qt_build_repo_post_process) - if(NOT QT_INTERNAL_REPO_POST_PROCESS_CALLED) - set(QT_INTERNAL_REPO_POST_PROCESS_CALLED TRUE) - if(NOT QT_BUILD_STANDALONE_TESTS) - include(QtPostProcess) - endif() - endif() -endmacro() - -macro(qt_build_repo_end) - if(NOT QT_BUILD_STANDALONE_TESTS) - qt_build_repo_post_process() - - # Install the repo-specific cmake find modules. - qt_path_join(__qt_repo_install_dir ${QT_CONFIG_INSTALL_DIR} ${INSTALL_CMAKE_NAMESPACE}) - qt_path_join(__qt_repo_build_dir ${QT_CONFIG_BUILD_DIR} ${INSTALL_CMAKE_NAMESPACE}) - - if(NOT PROJECT_NAME STREQUAL "QtBase") - if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cmake") - qt_copy_or_install(DIRECTORY cmake/ - DESTINATION "${__qt_repo_install_dir}" - FILES_MATCHING PATTERN "Find*.cmake" - ) - if(QT_SUPERBUILD AND QT_WILL_INSTALL) - file(COPY cmake/ - DESTINATION "${__qt_repo_build_dir}" - FILES_MATCHING PATTERN "Find*.cmake" - ) - endif() - endif() - endif() - - if(NOT QT_SUPERBUILD) - qt_print_feature_summary() - endif() - endif() - - qt_build_internals_add_toplevel_targets() - - if(NOT QT_SUPERBUILD) - qt_print_build_instructions() - endif() - - get_property(synced_modules GLOBAL PROPERTY _qt_synced_modules) - if(synced_modules) - set(QT_INTERNAL_SYNCED_MODULES ${synced_modules} CACHE INTERNAL - "List of the synced modules. Prevents running syncqt.cpp after the first configuring.") - endif() - - if(NOT QT_SUPERBUILD) - qt_internal_save_previously_visited_packages() - endif() - - if(QT_INTERNAL_FRESH_REQUESTED) - set(QT_INTERNAL_FRESH_REQUESTED "FALSE" CACHE INTERNAL "") - endif() - - if(NOT QT_SUPERBUILD) - qt_internal_qt_configure_end() - endif() - - list(POP_BACK CMAKE_MESSAGE_CONTEXT) -endmacro() - -# Function called either at the end of per-repo configuration, or at the end of configuration of -# a super build. -# At the moment it is called before examples are configured in a per-repo build. We might want -# to change that at some point if needed. -function(qt_internal_qt_configure_end) - # If Qt is configued via the configure script, remove the marker variable, so that any future - # reconfigurations that are done by calling cmake directly don't trigger configure specific - # logic. - unset(QT_INTERNAL_CALLED_FROM_CONFIGURE CACHE) -endfunction() - -macro(qt_build_repo) - qt_build_repo_begin(${ARGN}) - - qt_build_repo_impl_find_package_tests() - qt_build_repo_impl_src() - qt_build_repo_impl_tools() - - qt_build_repo_post_process() - qt_build_repo_impl_tests() - - qt_build_repo_end() - - qt_build_repo_impl_examples() -endmacro() - -macro(qt_build_repo_impl_find_package_tests) - # If testing is enabled, try to find the qtbase Test package. - # Do this before adding src, because there might be test related conditions - # in source. - if (QT_BUILD_TESTS AND NOT QT_BUILD_STANDALONE_TESTS) - # When looking for the Test package, do it using the Qt6 package version, in case if - # PROJECT_VERSION is following a different versioning scheme. - if(Qt6_VERSION) - set(_qt_build_repo_impl_find_package_tests_version "${Qt6_VERSION}") - else() - set(_qt_build_repo_impl_find_package_tests_version "${PROJECT_VERSION}") - endif() - - find_package(Qt6 - "${_qt_build_repo_impl_find_package_tests_version}" - CONFIG REQUIRED COMPONENTS Test) - unset(_qt_build_repo_impl_find_package_tests_version) - endif() -endmacro() - -macro(qt_build_repo_impl_src) - if(NOT QT_BUILD_STANDALONE_TESTS) - if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/CMakeLists.txt") - add_subdirectory(src) - endif() - endif() - if(QT_FEATURE_lttng AND NOT TARGET LTTng::UST) - qt_find_package(LTTngUST PROVIDED_TARGETS LTTng::UST - MODULE_NAME global QMAKE_LIB lttng-ust) - endif() -endmacro() - -macro(qt_build_repo_impl_tools) - if(NOT QT_BUILD_STANDALONE_TESTS) - if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tools/CMakeLists.txt") - add_subdirectory(tools) - endif() - endif() -endmacro() - -macro(qt_build_repo_impl_tests) - if (QT_BUILD_TESTS AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt") - add_subdirectory(tests) - if(NOT QT_BUILD_TESTS_BY_DEFAULT) - set_property(DIRECTORY tests PROPERTY EXCLUDE_FROM_ALL TRUE) - endif() - endif() -endmacro() - -macro(qt_build_repo_impl_examples) - if(QT_BUILD_EXAMPLES - AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/examples/CMakeLists.txt" - AND NOT QT_BUILD_STANDALONE_TESTS) - message(STATUS "Configuring examples.") - add_subdirectory(examples) - endif() -endmacro() - -macro(qt_set_up_standalone_tests_build) - # Remove this macro once all usages of it have been removed. - # Standalone tests are not handled via the main repo project and qt_build_tests. -endmacro() - -function(qt_get_standalone_tests_config_files_path out_var) - set(path "${QT_CONFIG_INSTALL_DIR}/${INSTALL_CMAKE_NAMESPACE}BuildInternals/StandaloneTests") - - # QT_CONFIG_INSTALL_DIR is relative in prefix builds. - if(QT_WILL_INSTALL) - if(DEFINED CMAKE_STAGING_PREFIX) - qt_path_join(path "${CMAKE_STAGING_PREFIX}" "${path}") - else() - qt_path_join(path "${CMAKE_INSTALL_PREFIX}" "${path}") - endif() - endif() - - set("${out_var}" "${path}" PARENT_SCOPE) -endfunction() - -function(qt_internal_get_standalone_tests_config_file_name out_var) - # When doing a "single repo target set" build (like in qtscxqml) ensure we use a unique tests - # config file for each repo target set. Using the PROJECT_NAME only is not enough because - # the same file will be overridden with different content on each repo set install. - set(tests_config_file_name "${PROJECT_NAME}") - - if(QT_BUILD_SINGLE_REPO_TARGET_SET) - string(APPEND tests_config_file_name "RepoSet${QT_BUILD_SINGLE_REPO_TARGET_SET}") - endif() - string(APPEND tests_config_file_name "TestsConfig.cmake") - - set(${out_var} "${tests_config_file_name}" PARENT_SCOPE) -endfunction() - -macro(qt_build_tests) - set(CMAKE_UNITY_BUILD OFF) - - # Prepending to QT_BUILD_CMAKE_PREFIX_PATH helps find components of Qt6, because those - # find_package calls use NO_DEFAULT_PATH, and thus CMAKE_PREFIX_PATH is ignored. - list(PREPEND CMAKE_FIND_ROOT_PATH "${QT_BUILD_DIR}") - list(PREPEND QT_BUILD_CMAKE_PREFIX_PATH "${QT_BUILD_DIR}/${INSTALL_LIBDIR}/cmake") - - if(QT_BUILD_STANDALONE_TESTS) - # Find location of TestsConfig.cmake. These contain the modules that need to be - # find_package'd when testing. - qt_get_standalone_tests_config_files_path(_qt_build_tests_install_prefix) - - qt_internal_get_standalone_tests_config_file_name(_qt_tests_config_file_name) - set(_qt_standalone_tests_config_file_path - "${_qt_build_tests_install_prefix}/${_qt_tests_config_file_name}") - include("${_qt_standalone_tests_config_file_path}" - OPTIONAL - RESULT_VARIABLE _qt_standalone_tests_included) - if(NOT _qt_standalone_tests_included) - message(DEBUG - "Standalone tests config file not included because it does not exist: " - "${_qt_standalone_tests_config_file_path}" - ) - else() - message(DEBUG - "Standalone tests config file included successfully: " - "${_qt_standalone_tests_config_file_path}" - ) - endif() - - unset(_qt_standalone_tests_config_file_path) - unset(_qt_standalone_tests_included) - unset(_qt_tests_config_file_name) - - # Of course we always need the test module as well. - # When looking for the Test package, do it using the Qt6 package version, in case if - # PROJECT_VERSION is following a different versioning scheme. - if(Qt6_VERSION) - set(_qt_build_tests_package_version "${Qt6_VERSION}") - else() - set(_qt_build_tests_package_version "${PROJECT_VERSION}") - endif() - find_package(Qt6 "${_qt_build_tests_package_version}" CONFIG REQUIRED COMPONENTS Test) - unset(_qt_build_tests_package_version) - - # Set language standards after finding Core, because that's when the relevant - # feature variables are available, and the call in QtSetup is too early when building - # standalone tests, because Core was not find_package()'d yet. - qt_set_language_standards() - - if(NOT QT_SUPERBUILD) - # Set up fake standalone tests install prefix, so we don't pollute the Qt install - # prefix. For super builds it needs to be done in qt5/CMakeLists.txt. - qt_set_up_fake_standalone_tests_install_prefix() - endif() - else() - if(ANDROID) - # When building in-tree tests we need to specify the QT_ANDROID_ABIS list. Since we - # build Qt for the single ABI, build tests for this ABI only. - set(QT_ANDROID_ABIS "${CMAKE_ANDROID_ARCH_ABI}") - endif() - endif() - - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/auto/CMakeLists.txt") - add_subdirectory(auto) - endif() - if(NOT QT_BUILD_MINIMAL_STATIC_TESTS AND NOT QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS) - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/baseline/CMakeLists.txt") - add_subdirectory(baseline) - endif() - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/benchmarks/CMakeLists.txt" AND QT_BUILD_BENCHMARKS) - add_subdirectory(benchmarks) - endif() - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/manual/CMakeLists.txt" AND QT_BUILD_MANUAL_TESTS) - add_subdirectory(manual) - # Adding this logic to all tests impacts the configure time ~3sec in addition. We still - # might want this in the future for other test types since currently we have a moderate - # subset of tests that require manual initialization of autotools. - _qt_internal_collect_buildsystem_targets(targets - "${CMAKE_CURRENT_SOURCE_DIR}/manual" EXCLUDE UTILITY ALIAS) - foreach(target ${targets}) - qt_autogen_tools(${target} ENABLE_AUTOGEN_TOOLS "moc" "rcc") - if(TARGET Qt::Widgets) - qt_autogen_tools(${target} ENABLE_AUTOGEN_TOOLS "uic") - endif() - endforeach() - endif() - endif() - - set(CMAKE_UNITY_BUILD ${QT_UNITY_BUILD}) -endmacro() - -function(qt_compute_relative_path_from_cmake_config_dir_to_prefix) - # Compute the reverse relative path from the CMake config dir to the install prefix. - # This is used in QtBuildInternalsExtras to create a relocatable relative install prefix path. - # This path is used for finding syncqt and other things, regardless of initial install prefix - # (e.g installed Qt was archived and unpacked to a different path on a different machine). - # - # This is meant to be called only once when configuring qtbase. - # - # Similar code exists in Qt6CoreConfigExtras.cmake.in and src/corelib/CMakeLists.txt which - # might not be needed anymore. - if(CMAKE_STAGING_PREFIX) - set(__qt_prefix "${CMAKE_STAGING_PREFIX}") - else() - set(__qt_prefix "${CMAKE_INSTALL_PREFIX}") - endif() - - if(QT_WILL_INSTALL) - get_filename_component(clean_config_prefix - "${__qt_prefix}/${QT_CONFIG_INSTALL_DIR}" ABSOLUTE) - else() - get_filename_component(clean_config_prefix "${QT_CONFIG_BUILD_DIR}" ABSOLUTE) - endif() - file(RELATIVE_PATH - qt_path_from_cmake_config_dir_to_prefix - "${clean_config_prefix}" "${__qt_prefix}") - set(qt_path_from_cmake_config_dir_to_prefix "${qt_path_from_cmake_config_dir_to_prefix}" - PARENT_SCOPE) -endfunction() - -function(qt_get_relocatable_install_prefix out_var) - # We need to compute it only once while building qtbase. Afterwards it's loaded from - # QtBuildInternalsExtras.cmake. - if(QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX) - return() - endif() - # The QtBuildInternalsExtras value is dynamically computed, whereas the initial qtbase - # configuration uses an absolute path. - set(${out_var} "${CMAKE_INSTALL_PREFIX}" PARENT_SCOPE) -endfunction() - -function(qt_set_up_fake_standalone_tests_install_prefix) - # Set a fake local (non-cache) CMAKE_INSTALL_PREFIX. - # Needed for standalone tests, we don't want to accidentally install a test into the Qt prefix. - # Allow opt-out, if a user knows what they're doing. - if(QT_NO_FAKE_STANDALONE_TESTS_INSTALL_PREFIX) - return() - endif() - set(new_install_prefix "${CMAKE_BINARY_DIR}/fake_prefix") - - # It's IMPORTANT that this is not a cache variable. Otherwise - # qt_get_standalone_tests_confg_files_path() will not work on re-configuration. - message(STATUS - "Setting local standalone test install prefix (non-cached) to '${new_install_prefix}'.") - set(CMAKE_INSTALL_PREFIX "${new_install_prefix}" PARENT_SCOPE) - - # We also need to clear the staging prefix if it's set, otherwise CMake will modify any computed - # rpaths containing the staging prefix to point to the new fake prefix, which is not what we - # want. This replacement is done in cmComputeLinkInformation::GetRPath(). - # - # By clearing the staging prefix for the standalone tests, any detected link time - # rpaths will be embedded as-is, which will point to the place where Qt was installed (aka - # the staging prefix). - if(DEFINED CMAKE_STAGING_PREFIX) - message(STATUS "Clearing local standalone test staging prefix (non-cached).") - set(CMAKE_STAGING_PREFIX "" PARENT_SCOPE) - endif() -endfunction() - -# Mean to be called when configuring examples as part of the main build tree, as well as for CMake -# tests (tests that call CMake to try and build CMake applications). -macro(qt_internal_set_up_build_dir_package_paths) - list(PREPEND CMAKE_PREFIX_PATH "${QT_BUILD_DIR}/${INSTALL_LIBDIR}/cmake") - # Make sure the CMake config files do not recreate the already-existing targets - set(QT_NO_CREATE_TARGETS TRUE) -endmacro() - -macro(qt_examples_build_begin) - set(options EXTERNAL_BUILD) - set(singleOpts "") - set(multiOpts DEPENDS) - - cmake_parse_arguments(arg "${options}" "${singleOpts}" "${multiOpts}" ${ARGN}) - - set(CMAKE_UNITY_BUILD OFF) - - # Use by qt_internal_add_example. - set(QT_EXAMPLE_BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") - - if(arg_EXTERNAL_BUILD AND QT_BUILD_EXAMPLES_AS_EXTERNAL) - # Examples will be built using ExternalProject. - # We depend on all plugins built as part of the current repo as well as current repo's - # dependencies plugins, to prevent opportunities for - # weird errors associated with loading out-of-date plugins from - # unrelated Qt modules. - # We also depend on all targets from this repo's src and tools subdirectories - # to ensure that we've built anything that a find_package() call within - # an example might use. Projects can add further dependencies if needed, - # but that should rarely be necessary. - set(QT_EXAMPLE_DEPENDENCIES ${qt_repo_plugins_recursive} ${arg_DEPENDS}) - - if(TARGET ${qt_repo_targets_name}_src) - list(APPEND QT_EXAMPLE_DEPENDENCIES ${qt_repo_targets_name}_src_for_examples) - endif() - - if(TARGET ${qt_repo_targets_name}_tools) - list(APPEND QT_EXAMPLE_DEPENDENCIES ${qt_repo_targets_name}_tools) - endif() - - set(QT_IS_EXTERNAL_EXAMPLES_BUILD TRUE) - - string(TOLOWER ${PROJECT_NAME} project_name_lower) - if(NOT TARGET examples) - if(QT_BUILD_EXAMPLES_BY_DEFAULT) - add_custom_target(examples ALL) - else() - add_custom_target(examples) - endif() - endif() - if(NOT TARGET examples_${project_name_lower}) - add_custom_target(examples_${project_name_lower}) - add_dependencies(examples examples_${project_name_lower}) - endif() - - include(ExternalProject) - else() - # This repo has not yet been updated to build examples in a separate - # build from this main build, or we can't use that arrangement yet. - # Build them directly as part of the main build instead for backward - # compatibility. - if(NOT BUILD_SHARED_LIBS) - # Ordinarily, it would be an error to call return() from within a - # macro(), but in this case we specifically want to return from the - # caller's scope if we are doing a static build and the project - # isn't building examples in a separate build from the main build. - # Configuring static builds requires tools that are not available - # until build time. - return() - endif() - - if(NOT QT_BUILD_EXAMPLES_BY_DEFAULT) - set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL TRUE) - endif() - endif() - - # TODO: Change this to TRUE when all examples in all repos are ported to use - # qt_internal_add_example. - # We shouldn't need to call qt_internal_set_up_build_dir_package_paths when - # QT_IS_EXTERNAL_EXAMPLES_BUILD is TRUE. - # Due to not all examples being ported, if we don't - # call qt_internal_set_up_build_dir_package_paths -> set(QT_NO_CREATE_TARGETS TRUE) we'll get - # CMake configuration errors saying we redefine Qt targets because we both build them and find - # them as part of find_package. - set(__qt_all_examples_ported_to_external_projects FALSE) - - # Examples that are built as part of the Qt build need to use the CMake config files from the - # build dir, because they are not installed yet in a prefix build. - # Prepending to CMAKE_PREFIX_PATH helps find the initial Qt6Config.cmake. - # Prepending to QT_BUILD_CMAKE_PREFIX_PATH helps find components of Qt6, because those - # find_package calls use NO_DEFAULT_PATH, and thus CMAKE_PREFIX_PATH is ignored. - # Prepending to CMAKE_FIND_ROOT_PATH ensures the components are found while cross-compiling - # without setting CMAKE_FIND_ROOT_PATH_MODE_PACKAGE to BOTH. - if(NOT QT_IS_EXTERNAL_EXAMPLES_BUILD OR NOT __qt_all_examples_ported_to_external_projects) - qt_internal_set_up_build_dir_package_paths() - list(PREPEND CMAKE_FIND_ROOT_PATH "${QT_BUILD_DIR}") - list(PREPEND QT_BUILD_CMAKE_PREFIX_PATH "${QT_BUILD_DIR}/${INSTALL_LIBDIR}/cmake") - endif() - - # Because CMAKE_INSTALL_RPATH is empty by default in the repo project, examples need to have - # it set here, so they can run when installed. - # This means that installed examples are not relocatable at the moment. We would need to - # annotate where each example is installed to, to be able to derive a relative rpath, and it - # seems there's no way to query such information from CMake itself. - set(CMAKE_INSTALL_RPATH "${_default_install_rpath}") - - install(CODE " -# Backup CMAKE_INSTALL_PREFIX because we're going to change it in each example subdirectory -# and restore it after all examples are processed so that QtFooToolsAdditionalTargetInfo.cmake -# files are installed into the original install prefix. -set(_qt_internal_examples_cmake_install_prefix_backup \"\${CMAKE_INSTALL_PREFIX}\") -") -endmacro() - -macro(qt_examples_build_end) - # We use AUTOMOC/UIC/RCC in the examples. When the examples are part of the - # main build rather than being built in their own separate project, make - # sure we do not fail on a fresh Qt build (e.g. the moc binary won't exist - # yet because it is created at build time). - - _qt_internal_collect_buildsystem_targets(targets - "${CMAKE_CURRENT_SOURCE_DIR}" EXCLUDE UTILITY ALIAS) - - foreach(target ${targets}) - qt_autogen_tools(${target} ENABLE_AUTOGEN_TOOLS "moc" "rcc") - if(TARGET Qt::Widgets) - qt_autogen_tools(${target} ENABLE_AUTOGEN_TOOLS "uic") - endif() - set_target_properties(${target} PROPERTIES UNITY_BUILD OFF) - endforeach() - - install(CODE " -# Restore backed up CMAKE_INSTALL_PREFIX. -set(CMAKE_INSTALL_PREFIX \"\${_qt_internal_examples_cmake_install_prefix_backup}\") -") - - set(CMAKE_UNITY_BUILD ${QT_UNITY_BUILD}) -endmacro() - -# Allows building an example either as an ExternalProject or in-tree with the Qt build. -# Also allows installing the example sources. -function(qt_internal_add_example subdir) - # Pre-compute unique example name based on the subdir, in case of target name clashes. - qt_internal_get_example_unique_name(unique_example_name "${subdir}") - - # QT_INTERNAL_NO_CONFIGURE_EXAMPLES is not meant to be used by Qt builders, it's here for faster - # testing of the source installation code path for build system engineers. - if(NOT QT_INTERNAL_NO_CONFIGURE_EXAMPLES) - if(NOT QT_IS_EXTERNAL_EXAMPLES_BUILD) - qt_internal_add_example_in_tree("${subdir}") - else() - qt_internal_add_example_external_project("${subdir}" - NAME "${unique_example_name}") - endif() - endif() - - if(QT_INSTALL_EXAMPLES_SOURCES) - string(TOLOWER ${PROJECT_NAME} project_name_lower) - - qt_internal_install_example_sources("${subdir}" - NAME "${unique_example_name}" - REPO_NAME "${project_name_lower}") - endif() -endfunction() - -# Gets the install prefix where an example should be installed. -# Used for computing the final installation path. -function(qt_internal_get_example_install_prefix out_var) - # Allow customizing the installation path of the examples. Will be used in CI. - if(QT_INTERNAL_EXAMPLES_INSTALL_PREFIX) - set(qt_example_install_prefix "${QT_INTERNAL_EXAMPLES_INSTALL_PREFIX}") - else() - set(qt_example_install_prefix "${CMAKE_INSTALL_PREFIX}/${INSTALL_EXAMPLESDIR}") - endif() - file(TO_CMAKE_PATH "${qt_example_install_prefix}" qt_example_install_prefix) - set(${out_var} "${qt_example_install_prefix}" PARENT_SCOPE) -endfunction() - -# Gets the install prefix where an example's sources should be installed. -# Used for computing the final installation path. -function(qt_internal_get_examples_sources_install_prefix out_var) - # Allow customizing the installation path of the examples source specifically. - if(QT_INTERNAL_EXAMPLES_SOURCES_INSTALL_PREFIX) - set(qt_example_install_prefix "${QT_INTERNAL_EXAMPLES_SOURCES_INSTALL_PREFIX}") - else() - qt_internal_get_example_install_prefix(qt_example_install_prefix) - endif() - file(TO_CMAKE_PATH "${qt_example_install_prefix}" qt_example_install_prefix) - set(${out_var} "${qt_example_install_prefix}" PARENT_SCOPE) -endfunction() - -# Gets the relative path of an example, relative to the current repo's examples source dir. -# QT_EXAMPLE_BASE_DIR is meant to be already set in a parent scope. -function(qt_internal_get_example_rel_path out_var subdir) - file(RELATIVE_PATH example_rel_path - "${QT_EXAMPLE_BASE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/${subdir}") - set(${out_var} "${example_rel_path}" PARENT_SCOPE) -endfunction() - -# Gets the install path where an example should be installed. -function(qt_internal_get_example_install_path out_var subdir) - qt_internal_get_example_install_prefix(qt_example_install_prefix) - qt_internal_get_example_rel_path(example_rel_path "${subdir}") - set(example_install_path "${qt_example_install_prefix}/${example_rel_path}") - - set(${out_var} "${example_install_path}" PARENT_SCOPE) -endfunction() - -# Gets the install path where an example's sources should be installed. -function(qt_internal_get_examples_sources_install_path out_var subdir) - qt_internal_get_examples_sources_install_prefix(qt_example_install_prefix) - qt_internal_get_example_rel_path(example_rel_path "${subdir}") - set(example_install_path "${qt_example_install_prefix}/${example_rel_path}") - - set(${out_var} "${example_install_path}" PARENT_SCOPE) -endfunction() - -# Get the unique name of an example project based on its subdir or explicitly given name. -# Makes the name unique by appending a short sha1 hash of the relative path of the example -# if a target of the same name already exist. -function(qt_internal_get_example_unique_name out_var subdir) - qt_internal_get_example_rel_path(example_rel_path "${subdir}") - - set(name "${subdir}") - - # qtdeclarative has calls like qt_internal_add_example(imagine/automotive) - # so passing a nested subdirectory. Custom targets (and thus ExternalProjects) can't contain - # slashes, so extract the last part of the path to be used as a name. - if(name MATCHES "/") - string(REPLACE "/" ";" exploded_path "${name}") - list(POP_BACK exploded_path last_dir) - if(NOT last_dir) - message(FATAL_ERROR "Example subdirectory must have a name.") - else() - set(name "${last_dir}") - endif() - endif() - - # Likely a clash with an example subdir ExternalProject custom target of the same name in a - # top-level build. - if(TARGET "${name}") - string(SHA1 rel_path_hash "${example_rel_path}") - string(SUBSTRING "${rel_path_hash}" 0 4 short_hash) - set(name "${name}-${short_hash}") - endif() - - set(${out_var} "${name}" PARENT_SCOPE) -endfunction() - -# Use old non-ExternalProject approach, aka build in-tree with the Qt build. -function(qt_internal_add_example_in_tree subdir) - # Unset the default CMAKE_INSTALL_PREFIX that's generated in - # ${CMAKE_CURRENT_BINARY_DIR}/cmake_install.cmake - # so we can override it with a different value in - # ${CMAKE_CURRENT_BINARY_DIR}/${subdir}/cmake_install.cmake - # - install(CODE " -# Unset the CMAKE_INSTALL_PREFIX in the current cmake_install.cmake file so that it can be -# overridden in the included add_subdirectory-specific cmake_install.cmake files instead. -unset(CMAKE_INSTALL_PREFIX) -") - - # Override the install prefix in the subdir cmake_install.cmake, so that - # relative install(TARGETS DESTINATION) calls in example projects install where we tell them to. - qt_internal_get_example_install_path(example_install_path "${subdir}") - set(CMAKE_INSTALL_PREFIX "${example_install_path}") - - # Make sure unclean example projects have their INSTALL_EXAMPLEDIR set to "." - # Won't have any effect on example projects that don't use INSTALL_EXAMPLEDIR. - # This plus the install prefix above takes care of installing examples where we want them to - # be installed, while allowing us to remove INSTALL_EXAMPLEDIR code in each example - # incrementally. - # TODO: Remove once all repositories use qt_internal_add_example instead of add_subdirectory. - set(QT_INTERNAL_SET_EXAMPLE_INSTALL_DIR_TO_DOT ON) - - add_subdirectory(${subdir}) -endfunction() - -function(qt_internal_add_example_external_project subdir) - set(options "") - set(singleOpts NAME) - set(multiOpts "") - - cmake_parse_arguments(PARSE_ARGV 1 arg "${options}" "${singleOpts}" "${multiOpts}") - - _qt_internal_get_build_vars_for_external_projects( - CMAKE_DIR_VAR qt_cmake_dir - PREFIXES_VAR qt_prefixes - ADDITIONAL_PACKAGES_PREFIXES_VAR qt_additional_packages_prefixes - ) - - list(APPEND QT_ADDITIONAL_PACKAGES_PREFIX_PATH "${qt_additional_packages_prefixes}") - - set(vars_to_pass_if_defined) - set(var_defs) - if(QT_HOST_PATH OR CMAKE_CROSSCOMPILING) - list(APPEND var_defs - -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${qt_cmake_dir}/qt.toolchain.cmake - ) - else() - list(PREPEND CMAKE_PREFIX_PATH ${qt_prefixes}) - - # Setting CMAKE_SYSTEM_NAME affects CMAKE_CROSSCOMPILING, even if it is - # set to the same as the host, so it should only be set if it is different. - # See https://gitlab.kitware.com/cmake/cmake/-/issues/21744 - if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND - NOT CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME) - list(APPEND vars_to_pass_if_defined CMAKE_SYSTEM_NAME:STRING) - endif() - endif() - - # In multi-config mode by default we exclude building tools for configs other than the main one. - # Trying to build an example in a non-default config using the non-installed - # QtFooConfig.cmake files would error out saying moc is not found. - # Make sure to build examples only with the main config. - # When users build an example against an installed Qt they won't have this problem because - # the generated non-main QtFooTargets-$<CONFIG>.cmake file is empty and doesn't advertise - # a tool that is not there. - if(QT_GENERATOR_IS_MULTI_CONFIG) - set(CMAKE_CONFIGURATION_TYPES "${QT_MULTI_CONFIG_FIRST_CONFIG}") - endif() - - # We need to pass the modified CXX flags of the parent project so that using sccache works - # properly and doesn't error out due to concurrent access to the pdb files. - # See qt_internal_set_up_config_optimizations_like_in_qmake, "/Zi" "/Z7". - if(MSVC AND QT_FEATURE_msvc_obj_debug_info) - qt_internal_get_enabled_languages_for_flag_manipulation(enabled_languages) - set(configs RELWITHDEBINFO DEBUG) - foreach(lang ${enabled_languages}) - foreach(config ${configs}) - set(flag_var_name "CMAKE_${lang}_FLAGS_${config}") - list(APPEND vars_to_pass_if_defined "${flag_var_name}:STRING") - endforeach() - endforeach() - endif() - - # When cross-compiling for a qemu target in our CI, we source an environment script - # that sets environment variables like CC and CXX. These are parsed by CMake on initial - # configuration to populate the cache vars CMAKE_${lang}_COMPILER. - # If the environment variable specified not only the compiler path, but also a list of flags - # to pass to the compiler, CMake parses those out into a separate CMAKE_${lang}_COMPILER_ARG1 - # cache variable. In such a case, we want to ensure that the external project also sees those - # flags. - # Unfortunately we can't do that by simply forwarding CMAKE_${lang}_COMPILER_ARG1 to the EP - # because it breaks the compiler identification try_compile call, it simply doesn't consider - # the cache var. From what I could gather, it's a limitation of try_compile and the list - # of variables it considers for forwarding. - # To fix this case, we ensure not to pass either cache variable, and let the external project - # and its compiler identification try_compile project pick up the compiler and the flags - # from the environment variables instead. - foreach(lang_as_env_var CC CXX OBJC OBJCXX) - if(lang_as_env_var STREQUAL "CC") - set(lang_as_cache_var "C") - else() - set(lang_as_cache_var "${lang_as_env_var}") - endif() - set(lang_env_value "$ENV{${lang_as_env_var}}") - if(lang_env_value - AND CMAKE_${lang_as_cache_var}_COMPILER - AND CMAKE_${lang_as_cache_var}_COMPILER_ARG1) - # The compiler environment variable is set and specifies a list of extra flags, don't - # forward the compiler cache vars and rely on the environment variable to be picked up - # instead. - else() - list(APPEND vars_to_pass_if_defined "CMAKE_${lang_as_cache_var}_COMPILER:STRING") - endif() - endforeach() - unset(lang_as_env_var) - unset(lang_as_cache_var) - unset(lang_env_value) - - list(APPEND vars_to_pass_if_defined - CMAKE_BUILD_TYPE:STRING - CMAKE_CONFIGURATION_TYPES:STRING - CMAKE_PREFIX_PATH:STRING - QT_BUILD_CMAKE_PREFIX_PATH:STRING - QT_ADDITIONAL_PACKAGES_PREFIX_PATH:STRING - CMAKE_FIND_ROOT_PATH:STRING - BUILD_SHARED_LIBS:BOOL - CMAKE_OSX_ARCHITECTURES:STRING - CMAKE_OSX_DEPLOYMENT_TARGET:STRING - CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED:BOOL - CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH:BOOL - CMAKE_C_COMPILER_LAUNCHER:STRING - CMAKE_CXX_COMPILER_LAUNCHER:STRING - CMAKE_OBJC_COMPILER_LAUNCHER:STRING - CMAKE_OBJCXX_COMPILER_LAUNCHER:STRING - ) - - foreach(var_with_type IN LISTS vars_to_pass_if_defined) - string(REPLACE ":" ";" key_as_list "${var_with_type}") - list(GET key_as_list 0 var) - if(NOT DEFINED ${var}) - continue() - endif() - - # Preserve lists - string(REPLACE ";" "$<SEMICOLON>" varForGenex "${${var}}") - - list(APPEND var_defs -D${var_with_type}=${varForGenex}) - endforeach() - - if(QT_INTERNAL_VERBOSE_EXAMPLES) - list(APPEND var_defs -DCMAKE_MESSAGE_LOG_LEVEL:STRING=DEBUG) - list(APPEND var_defs -DCMAKE_AUTOGEN_VERBOSE:BOOL=TRUE) - endif() - - set(deps "") - list(REMOVE_DUPLICATES QT_EXAMPLE_DEPENDENCIES) - foreach(dep IN LISTS QT_EXAMPLE_DEPENDENCIES) - if(TARGET ${dep}) - list(APPEND deps ${dep}) - endif() - endforeach() - - set(independent_args) - cmake_policy(PUSH) - if(POLICY CMP0114) - set(independent_args INDEPENDENT TRUE) - cmake_policy(SET CMP0114 NEW) - endif() - - # The USES_TERMINAL_BUILD setting forces the build step to the console pool - # when using Ninja. This has two benefits: - # - # - You see build output as it is generated instead of at the end of the - # build step. - # - Only one task can use the console pool at a time, so it effectively - # serializes all example build steps, thereby preventing CPU - # over-commitment. - # - # If the loss of interactivity is not so important, one can allow CPU - # over-commitment for Ninja builds. This may result in better throughput, - # but is not allowed by default because it can make a machine almost - # unusable while a compilation is running. - set(terminal_args USES_TERMINAL_BUILD TRUE) - if(CMAKE_GENERATOR MATCHES "Ninja") - option(QT_BUILD_EXAMPLES_WITH_CPU_OVERCOMMIT - "Allow CPU over-commitment when building examples (Ninja only)" - ) - if(QT_BUILD_EXAMPLES_WITH_CPU_OVERCOMMIT) - set(terminal_args) - endif() - endif() - - # QT_EXAMPLE_INSTALL_MARKER - # The goal is to install each example project into a directory that keeps the example source dir - # hierarchy, without polluting the example projects with dirty INSTALL_EXAMPLEDIR and - # INSTALL_EXAMPLESDIR usage. - # E.g. ensure qtbase/examples/widgets/widgets/wiggly is installed to - # $qt_example_install_prefix/examples/widgets/widgets/wiggly/wiggly.exe - # $qt_example_install_prefix defaults to ${CMAKE_INSTALL_PREFIX}/${INSTALL_EXAMPLEDIR} - # but can also be set to a custom location. - # This needs to work both: - # - when using ExternalProject to build examples - # - when examples are built in-tree as part of Qt (no ExternalProject). - # The reason we want to support the latter is for nicer IDE integration: a can developer can - # work with a Qt repo and its examples using the same build dir. - # - # In both case we have to ensure examples are not accidentally installed to $qt_prefix/bin or - # similar. - # - # Example projects installation matrix. - # 1) ExternalProject + unclean example install rules (INSTALL_EXAMPLEDIR is set) => - # use _qt_internal_override_example_install_dir_to_dot + ExternalProject_Add's INSTALL_DIR - # using relative_dir from QT_EXAMPLE_BASE_DIR to example_source_dir - # - # 2) ExternalProject + clean example install rules => - # use ExternalProject_Add's INSTALL_DIR using relative_dir from QT_EXAMPLE_BASE_DIR to - # example_source_dir, _qt_internal_override_example_install_dir_to_dot would be a no-op - # - # 3) in-tree + unclean example install rules (INSTALL_EXAMPLEDIR is set) - # + - # 4) in-tree + clean example install rules => - # ensure CMAKE_INSTALL_PREFIX is unset in parent cmake_install.cmake file, set non-cache - # CMAKE_INSTALL_PREFIX using relative_dir from QT_EXAMPLE_BASE_DIR to - # example_source_dir, use _qt_internal_override_example_install_dir_to_dot to ensure - # INSTALL_EXAMPLEDIR does not interfere. - - qt_internal_get_example_install_path(example_install_path "${subdir}") - - set(ep_binary_dir "${CMAKE_CURRENT_BINARY_DIR}/${subdir}") - - set(build_command "") - if(QT_INTERNAL_VERBOSE_EXAMPLES AND CMAKE_GENERATOR MATCHES "Ninja") - set(build_command BUILD_COMMAND "${CMAKE_COMMAND}" --build "." -- -v) - endif() - - ExternalProject_Add(${arg_NAME} - EXCLUDE_FROM_ALL TRUE - SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${subdir}" - PREFIX "${CMAKE_CURRENT_BINARY_DIR}/${subdir}-ep" - STAMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/${subdir}-ep/stamp" - BINARY_DIR "${ep_binary_dir}" - INSTALL_DIR "${example_install_path}" - INSTALL_COMMAND "" - ${build_command} - TEST_COMMAND "" - DEPENDS ${deps} - CMAKE_CACHE_ARGS ${var_defs} - -DCMAKE_INSTALL_PREFIX:STRING=<INSTALL_DIR> - -DQT_INTERNAL_SET_EXAMPLE_INSTALL_DIR_TO_DOT:BOOL=TRUE - ${terminal_args} - ) - - # Install the examples when the the user runs 'make install', and not at build time (which is - # the default for ExternalProjects). - install(CODE "\ -# Install example from inside ExternalProject into the main build's install prefix. -execute_process( - COMMAND - \"${CMAKE_COMMAND}\" --build \"${ep_binary_dir}\" --target install -) -") - - # Force configure step to re-run after we configure the main project - set(reconfigure_check_file ${CMAKE_CURRENT_BINARY_DIR}/reconfigure_${arg_NAME}.txt) - file(TOUCH ${reconfigure_check_file}) - ExternalProject_Add_Step(${arg_NAME} reconfigure-check - DEPENDERS configure - DEPENDS ${reconfigure_check_file} - ${independent_args} - ) - - # Create an apk external project step and custom target that invokes the apk target - # within the external project. - # Make the global apk target depend on that custom target. - if(ANDROID) - ExternalProject_Add_Step(${arg_NAME} apk - COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target apk - DEPENDEES configure - EXCLUDE_FROM_MAIN YES - ${terminal_args} - ) - ExternalProject_Add_StepTargets(${arg_NAME} apk) - - if(TARGET apk) - add_dependencies(apk ${arg_NAME}-apk) - endif() - endif() - - cmake_policy(POP) - - string(TOLOWER ${PROJECT_NAME} project_name_lower) - add_dependencies(examples_${project_name_lower} ${arg_NAME}) - -endfunction() - -function(qt_internal_install_example_sources subdir) - set(options "") - set(single_args NAME REPO_NAME) - set(multi_args "") - - cmake_parse_arguments(PARSE_ARGV 1 arg "${options}" "${single_args}" "${multi_args}") - - qt_internal_get_examples_sources_install_path(example_install_path "${subdir}") - - # The trailing slash is important to avoid duplicate nested directory names. - set(example_source_dir "${subdir}/") - - # Allow controlling whether sources should be part of the default install target. - if(QT_INSTALL_EXAMPLES_SOURCES_BY_DEFAULT) - set(exclude_from_all "") - else() - set(exclude_from_all "EXCLUDE_FROM_ALL") - endif() - - # Create an install component for all example sources. Can also be part of the default - # install target if EXCLUDE_FROM_ALL is not passed. - install( - DIRECTORY "${example_source_dir}" - DESTINATION "${example_install_path}" - COMPONENT "examples_sources" - USE_SOURCE_PERMISSIONS - ${exclude_from_all} - ) - - # Also create a specific install component just for this repo's examples. - install( - DIRECTORY "${example_source_dir}" - DESTINATION "${example_install_path}" - COMPONENT "examples_sources_${arg_REPO_NAME}" - USE_SOURCE_PERMISSIONS - EXCLUDE_FROM_ALL - ) - - # Also create a specific install component just for the current example's sources. - install( - DIRECTORY "${example_source_dir}" - DESTINATION "${example_install_path}" - COMPONENT "examples_sources_${arg_NAME}" - USE_SOURCE_PERMISSIONS - EXCLUDE_FROM_ALL - ) -endfunction() - -if ("STANDALONE_TEST" IN_LIST Qt6BuildInternals_FIND_COMPONENTS) - include(${CMAKE_CURRENT_LIST_DIR}/QtStandaloneTestTemplateProject/Main.cmake) - if (NOT PROJECT_VERSION_MAJOR) - get_property(_qt_major_version TARGET ${QT_CMAKE_EXPORT_NAMESPACE}::Core PROPERTY INTERFACE_QT_MAJOR_VERSION) - set(PROJECT_VERSION ${Qt${_qt_major_version}Core_VERSION}) - - string(REPLACE "." ";" _qt_core_version_list ${PROJECT_VERSION}) - list(GET _qt_core_version_list 0 PROJECT_VERSION_MAJOR) - list(GET _qt_core_version_list 1 PROJECT_VERSION_MINOR) - list(GET _qt_core_version_list 2 PROJECT_VERSION_PATCH) - endif() -endif() - -function(qt_internal_static_link_order_test) - # The CMake versions greater than 3.21 take care about the resource object files order in a - # linker line, it's expected that all object files are located at the beginning of the linker - # line. - # No need to run the test. - if(CMAKE_VERSION VERSION_LESS 3.21) - __qt_internal_check_link_order_matters(link_order_matters) - if(link_order_matters) - set(summary_message "no") - else() - set(summary_message "yes") - endif() - else() - set(summary_message "yes") - endif() - qt_configure_add_summary_entry(TYPE "message" - ARGS "Linker can resolve circular dependencies" - MESSAGE "${summary_message}" - ) -endfunction() - -function(qt_internal_check_cmp0099_available) - # Don't care about CMP0099 in CMake versions greater than or equal to 3.21 - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.21) - return() - endif() - - __qt_internal_check_cmp0099_available(result) - if(result) - set(summary_message "yes") - else() - set(summary_message "no") - endif() - qt_configure_add_summary_entry(TYPE "message" - ARGS "CMake policy CMP0099 is supported" - MESSAGE "${summary_message}" - ) -endfunction() - -function(qt_internal_run_common_config_tests) - qt_configure_add_summary_section(NAME "Common build options") - qt_internal_static_link_order_test() - qt_internal_check_cmp0099_available() - qt_configure_end_summary_section() -endfunction() +include(QtBuildRepoExamplesHelpers) +include(QtBuildRepoHelpers) -# It is used in QtWebEngine to replace the REALPATH with ABSOLUTE path, which is -# useful for building Qt in Homebrew. -function(qt_internal_get_filename_path_mode out_var) - set(mode REALPATH) - if(APPLE AND QT_ALLOW_SYMLINK_IN_PATHS) - set(mode ABSOLUTE) - endif() - set(${out_var} ${mode} PARENT_SCOPE) -endfunction() +qt_internal_setup_build_internals() diff --git a/cmake/QtBuildRepoExamplesHelpers.cmake b/cmake/QtBuildRepoExamplesHelpers.cmake new file mode 100644 index 0000000000..b9186a0044 --- /dev/null +++ b/cmake/QtBuildRepoExamplesHelpers.cmake @@ -0,0 +1,590 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +macro(qt_examples_build_begin) + set(options EXTERNAL_BUILD) + set(singleOpts "") + set(multiOpts DEPENDS) + + cmake_parse_arguments(arg "${options}" "${singleOpts}" "${multiOpts}" ${ARGN}) + + set(CMAKE_UNITY_BUILD OFF) + + # Use by qt_internal_add_example. + set(QT_EXAMPLE_BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + + if(arg_EXTERNAL_BUILD AND QT_BUILD_EXAMPLES_AS_EXTERNAL) + # Examples will be built using ExternalProject. + # We depend on all plugins built as part of the current repo as well as current repo's + # dependencies plugins, to prevent opportunities for + # weird errors associated with loading out-of-date plugins from + # unrelated Qt modules. + # We also depend on all targets from this repo's src and tools subdirectories + # to ensure that we've built anything that a find_package() call within + # an example might use. Projects can add further dependencies if needed, + # but that should rarely be necessary. + set(QT_EXAMPLE_DEPENDENCIES ${qt_repo_plugins_recursive} ${arg_DEPENDS}) + + if(TARGET ${qt_repo_targets_name}_src) + list(APPEND QT_EXAMPLE_DEPENDENCIES ${qt_repo_targets_name}_src_for_examples) + endif() + + if(TARGET ${qt_repo_targets_name}_tools) + list(APPEND QT_EXAMPLE_DEPENDENCIES ${qt_repo_targets_name}_tools) + endif() + + set(QT_IS_EXTERNAL_EXAMPLES_BUILD TRUE) + + string(TOLOWER ${PROJECT_NAME} project_name_lower) + if(NOT TARGET examples) + if(QT_BUILD_EXAMPLES_BY_DEFAULT) + add_custom_target(examples ALL) + else() + add_custom_target(examples) + endif() + endif() + if(NOT TARGET examples_${project_name_lower}) + add_custom_target(examples_${project_name_lower}) + add_dependencies(examples examples_${project_name_lower}) + endif() + + include(ExternalProject) + else() + # This repo has not yet been updated to build examples in a separate + # build from this main build, or we can't use that arrangement yet. + # Build them directly as part of the main build instead for backward + # compatibility. + if(NOT BUILD_SHARED_LIBS) + # Ordinarily, it would be an error to call return() from within a + # macro(), but in this case we specifically want to return from the + # caller's scope if we are doing a static build and the project + # isn't building examples in a separate build from the main build. + # Configuring static builds requires tools that are not available + # until build time. + return() + endif() + + if(NOT QT_BUILD_EXAMPLES_BY_DEFAULT) + set_directory_properties(PROPERTIES EXCLUDE_FROM_ALL TRUE) + endif() + endif() + + # TODO: Change this to TRUE when all examples in all repos are ported to use + # qt_internal_add_example. + # We shouldn't need to call qt_internal_set_up_build_dir_package_paths when + # QT_IS_EXTERNAL_EXAMPLES_BUILD is TRUE. + # Due to not all examples being ported, if we don't + # call qt_internal_set_up_build_dir_package_paths -> set(QT_NO_CREATE_TARGETS TRUE) we'll get + # CMake configuration errors saying we redefine Qt targets because we both build them and find + # them as part of find_package. + set(__qt_all_examples_ported_to_external_projects FALSE) + + # Examples that are built as part of the Qt build need to use the CMake config files from the + # build dir, because they are not installed yet in a prefix build. + # Prepending to CMAKE_PREFIX_PATH helps find the initial Qt6Config.cmake. + # Prepending to QT_BUILD_CMAKE_PREFIX_PATH helps find components of Qt6, because those + # find_package calls use NO_DEFAULT_PATH, and thus CMAKE_PREFIX_PATH is ignored. + # Prepending to CMAKE_FIND_ROOT_PATH ensures the components are found while cross-compiling + # without setting CMAKE_FIND_ROOT_PATH_MODE_PACKAGE to BOTH. + if(NOT QT_IS_EXTERNAL_EXAMPLES_BUILD OR NOT __qt_all_examples_ported_to_external_projects) + qt_internal_set_up_build_dir_package_paths() + list(PREPEND CMAKE_FIND_ROOT_PATH "${QT_BUILD_DIR}") + list(PREPEND QT_BUILD_CMAKE_PREFIX_PATH "${QT_BUILD_DIR}/${INSTALL_LIBDIR}/cmake") + endif() + + # Because CMAKE_INSTALL_RPATH is empty by default in the repo project, examples need to have + # it set here, so they can run when installed. + # This means that installed examples are not relocatable at the moment. We would need to + # annotate where each example is installed to, to be able to derive a relative rpath, and it + # seems there's no way to query such information from CMake itself. + set(CMAKE_INSTALL_RPATH "${_default_install_rpath}") + + install(CODE " +# Backup CMAKE_INSTALL_PREFIX because we're going to change it in each example subdirectory +# and restore it after all examples are processed so that QtFooToolsAdditionalTargetInfo.cmake +# files are installed into the original install prefix. +set(_qt_internal_examples_cmake_install_prefix_backup \"\${CMAKE_INSTALL_PREFIX}\") +") +endmacro() + +macro(qt_examples_build_end) + # We use AUTOMOC/UIC/RCC in the examples. When the examples are part of the + # main build rather than being built in their own separate project, make + # sure we do not fail on a fresh Qt build (e.g. the moc binary won't exist + # yet because it is created at build time). + + _qt_internal_collect_buildsystem_targets(targets + "${CMAKE_CURRENT_SOURCE_DIR}" EXCLUDE UTILITY ALIAS) + + foreach(target ${targets}) + qt_autogen_tools(${target} ENABLE_AUTOGEN_TOOLS "moc" "rcc") + if(TARGET Qt::Widgets) + qt_autogen_tools(${target} ENABLE_AUTOGEN_TOOLS "uic") + endif() + set_target_properties(${target} PROPERTIES UNITY_BUILD OFF) + endforeach() + + install(CODE " +# Restore backed up CMAKE_INSTALL_PREFIX. +set(CMAKE_INSTALL_PREFIX \"\${_qt_internal_examples_cmake_install_prefix_backup}\") +") + + set(CMAKE_UNITY_BUILD ${QT_UNITY_BUILD}) +endmacro() + +# Allows building an example either as an ExternalProject or in-tree with the Qt build. +# Also allows installing the example sources. +function(qt_internal_add_example subdir) + # Pre-compute unique example name based on the subdir, in case of target name clashes. + qt_internal_get_example_unique_name(unique_example_name "${subdir}") + + # QT_INTERNAL_NO_CONFIGURE_EXAMPLES is not meant to be used by Qt builders, it's here for faster + # testing of the source installation code path for build system engineers. + if(NOT QT_INTERNAL_NO_CONFIGURE_EXAMPLES) + if(NOT QT_IS_EXTERNAL_EXAMPLES_BUILD) + qt_internal_add_example_in_tree("${subdir}") + else() + qt_internal_add_example_external_project("${subdir}" + NAME "${unique_example_name}") + endif() + endif() + + if(QT_INSTALL_EXAMPLES_SOURCES) + string(TOLOWER ${PROJECT_NAME} project_name_lower) + + qt_internal_install_example_sources("${subdir}" + NAME "${unique_example_name}" + REPO_NAME "${project_name_lower}") + endif() +endfunction() + +# Gets the install prefix where an example should be installed. +# Used for computing the final installation path. +function(qt_internal_get_example_install_prefix out_var) + # Allow customizing the installation path of the examples. Will be used in CI. + if(QT_INTERNAL_EXAMPLES_INSTALL_PREFIX) + set(qt_example_install_prefix "${QT_INTERNAL_EXAMPLES_INSTALL_PREFIX}") + else() + set(qt_example_install_prefix "${CMAKE_INSTALL_PREFIX}/${INSTALL_EXAMPLESDIR}") + endif() + file(TO_CMAKE_PATH "${qt_example_install_prefix}" qt_example_install_prefix) + set(${out_var} "${qt_example_install_prefix}" PARENT_SCOPE) +endfunction() + +# Gets the install prefix where an example's sources should be installed. +# Used for computing the final installation path. +function(qt_internal_get_examples_sources_install_prefix out_var) + # Allow customizing the installation path of the examples source specifically. + if(QT_INTERNAL_EXAMPLES_SOURCES_INSTALL_PREFIX) + set(qt_example_install_prefix "${QT_INTERNAL_EXAMPLES_SOURCES_INSTALL_PREFIX}") + else() + qt_internal_get_example_install_prefix(qt_example_install_prefix) + endif() + file(TO_CMAKE_PATH "${qt_example_install_prefix}" qt_example_install_prefix) + set(${out_var} "${qt_example_install_prefix}" PARENT_SCOPE) +endfunction() + +# Gets the relative path of an example, relative to the current repo's examples source dir. +# QT_EXAMPLE_BASE_DIR is meant to be already set in a parent scope. +function(qt_internal_get_example_rel_path out_var subdir) + file(RELATIVE_PATH example_rel_path + "${QT_EXAMPLE_BASE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/${subdir}") + set(${out_var} "${example_rel_path}" PARENT_SCOPE) +endfunction() + +# Gets the install path where an example should be installed. +function(qt_internal_get_example_install_path out_var subdir) + qt_internal_get_example_install_prefix(qt_example_install_prefix) + qt_internal_get_example_rel_path(example_rel_path "${subdir}") + set(example_install_path "${qt_example_install_prefix}/${example_rel_path}") + + set(${out_var} "${example_install_path}" PARENT_SCOPE) +endfunction() + +# Gets the install path where an example's sources should be installed. +function(qt_internal_get_examples_sources_install_path out_var subdir) + qt_internal_get_examples_sources_install_prefix(qt_example_install_prefix) + qt_internal_get_example_rel_path(example_rel_path "${subdir}") + set(example_install_path "${qt_example_install_prefix}/${example_rel_path}") + + set(${out_var} "${example_install_path}" PARENT_SCOPE) +endfunction() + +# Get the unique name of an example project based on its subdir or explicitly given name. +# Makes the name unique by appending a short sha1 hash of the relative path of the example +# if a target of the same name already exist. +function(qt_internal_get_example_unique_name out_var subdir) + qt_internal_get_example_rel_path(example_rel_path "${subdir}") + + set(name "${subdir}") + + # qtdeclarative has calls like qt_internal_add_example(imagine/automotive) + # so passing a nested subdirectory. Custom targets (and thus ExternalProjects) can't contain + # slashes, so extract the last part of the path to be used as a name. + if(name MATCHES "/") + string(REPLACE "/" ";" exploded_path "${name}") + list(POP_BACK exploded_path last_dir) + if(NOT last_dir) + message(FATAL_ERROR "Example subdirectory must have a name.") + else() + set(name "${last_dir}") + endif() + endif() + + # Likely a clash with an example subdir ExternalProject custom target of the same name in a + # top-level build. + if(TARGET "${name}") + string(SHA1 rel_path_hash "${example_rel_path}") + string(SUBSTRING "${rel_path_hash}" 0 4 short_hash) + set(name "${name}-${short_hash}") + endif() + + set(${out_var} "${name}" PARENT_SCOPE) +endfunction() + +# Use old non-ExternalProject approach, aka build in-tree with the Qt build. +function(qt_internal_add_example_in_tree subdir) + # Unset the default CMAKE_INSTALL_PREFIX that's generated in + # ${CMAKE_CURRENT_BINARY_DIR}/cmake_install.cmake + # so we can override it with a different value in + # ${CMAKE_CURRENT_BINARY_DIR}/${subdir}/cmake_install.cmake + # + install(CODE " +# Unset the CMAKE_INSTALL_PREFIX in the current cmake_install.cmake file so that it can be +# overridden in the included add_subdirectory-specific cmake_install.cmake files instead. +unset(CMAKE_INSTALL_PREFIX) +") + + # Override the install prefix in the subdir cmake_install.cmake, so that + # relative install(TARGETS DESTINATION) calls in example projects install where we tell them to. + qt_internal_get_example_install_path(example_install_path "${subdir}") + set(CMAKE_INSTALL_PREFIX "${example_install_path}") + + # Make sure unclean example projects have their INSTALL_EXAMPLEDIR set to "." + # Won't have any effect on example projects that don't use INSTALL_EXAMPLEDIR. + # This plus the install prefix above takes care of installing examples where we want them to + # be installed, while allowing us to remove INSTALL_EXAMPLEDIR code in each example + # incrementally. + # TODO: Remove once all repositories use qt_internal_add_example instead of add_subdirectory. + set(QT_INTERNAL_SET_EXAMPLE_INSTALL_DIR_TO_DOT ON) + + add_subdirectory(${subdir}) +endfunction() + +function(qt_internal_add_example_external_project subdir) + set(options "") + set(singleOpts NAME) + set(multiOpts "") + + cmake_parse_arguments(PARSE_ARGV 1 arg "${options}" "${singleOpts}" "${multiOpts}") + + _qt_internal_get_build_vars_for_external_projects( + CMAKE_DIR_VAR qt_cmake_dir + PREFIXES_VAR qt_prefixes + ADDITIONAL_PACKAGES_PREFIXES_VAR qt_additional_packages_prefixes + ) + + list(APPEND QT_ADDITIONAL_PACKAGES_PREFIX_PATH "${qt_additional_packages_prefixes}") + + set(vars_to_pass_if_defined) + set(var_defs) + if(QT_HOST_PATH OR CMAKE_CROSSCOMPILING) + list(APPEND var_defs + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${qt_cmake_dir}/qt.toolchain.cmake + ) + else() + list(PREPEND CMAKE_PREFIX_PATH ${qt_prefixes}) + + # Setting CMAKE_SYSTEM_NAME affects CMAKE_CROSSCOMPILING, even if it is + # set to the same as the host, so it should only be set if it is different. + # See https://gitlab.kitware.com/cmake/cmake/-/issues/21744 + if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND + NOT CMAKE_SYSTEM_NAME STREQUAL CMAKE_HOST_SYSTEM_NAME) + list(APPEND vars_to_pass_if_defined CMAKE_SYSTEM_NAME:STRING) + endif() + endif() + + # In multi-config mode by default we exclude building tools for configs other than the main one. + # Trying to build an example in a non-default config using the non-installed + # QtFooConfig.cmake files would error out saying moc is not found. + # Make sure to build examples only with the main config. + # When users build an example against an installed Qt they won't have this problem because + # the generated non-main QtFooTargets-$<CONFIG>.cmake file is empty and doesn't advertise + # a tool that is not there. + if(QT_GENERATOR_IS_MULTI_CONFIG) + set(CMAKE_CONFIGURATION_TYPES "${QT_MULTI_CONFIG_FIRST_CONFIG}") + endif() + + # We need to pass the modified CXX flags of the parent project so that using sccache works + # properly and doesn't error out due to concurrent access to the pdb files. + # See qt_internal_set_up_config_optimizations_like_in_qmake, "/Zi" "/Z7". + if(MSVC AND QT_FEATURE_msvc_obj_debug_info) + qt_internal_get_enabled_languages_for_flag_manipulation(enabled_languages) + set(configs RELWITHDEBINFO DEBUG) + foreach(lang ${enabled_languages}) + foreach(config ${configs}) + set(flag_var_name "CMAKE_${lang}_FLAGS_${config}") + list(APPEND vars_to_pass_if_defined "${flag_var_name}:STRING") + endforeach() + endforeach() + endif() + + # When cross-compiling for a qemu target in our CI, we source an environment script + # that sets environment variables like CC and CXX. These are parsed by CMake on initial + # configuration to populate the cache vars CMAKE_${lang}_COMPILER. + # If the environment variable specified not only the compiler path, but also a list of flags + # to pass to the compiler, CMake parses those out into a separate CMAKE_${lang}_COMPILER_ARG1 + # cache variable. In such a case, we want to ensure that the external project also sees those + # flags. + # Unfortunately we can't do that by simply forwarding CMAKE_${lang}_COMPILER_ARG1 to the EP + # because it breaks the compiler identification try_compile call, it simply doesn't consider + # the cache var. From what I could gather, it's a limitation of try_compile and the list + # of variables it considers for forwarding. + # To fix this case, we ensure not to pass either cache variable, and let the external project + # and its compiler identification try_compile project pick up the compiler and the flags + # from the environment variables instead. + foreach(lang_as_env_var CC CXX OBJC OBJCXX) + if(lang_as_env_var STREQUAL "CC") + set(lang_as_cache_var "C") + else() + set(lang_as_cache_var "${lang_as_env_var}") + endif() + set(lang_env_value "$ENV{${lang_as_env_var}}") + if(lang_env_value + AND CMAKE_${lang_as_cache_var}_COMPILER + AND CMAKE_${lang_as_cache_var}_COMPILER_ARG1) + # The compiler environment variable is set and specifies a list of extra flags, don't + # forward the compiler cache vars and rely on the environment variable to be picked up + # instead. + else() + list(APPEND vars_to_pass_if_defined "CMAKE_${lang_as_cache_var}_COMPILER:STRING") + endif() + endforeach() + unset(lang_as_env_var) + unset(lang_as_cache_var) + unset(lang_env_value) + + list(APPEND vars_to_pass_if_defined + CMAKE_BUILD_TYPE:STRING + CMAKE_CONFIGURATION_TYPES:STRING + CMAKE_PREFIX_PATH:STRING + QT_BUILD_CMAKE_PREFIX_PATH:STRING + QT_ADDITIONAL_PACKAGES_PREFIX_PATH:STRING + CMAKE_FIND_ROOT_PATH:STRING + BUILD_SHARED_LIBS:BOOL + CMAKE_OSX_ARCHITECTURES:STRING + CMAKE_OSX_DEPLOYMENT_TARGET:STRING + CMAKE_XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED:BOOL + CMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH:BOOL + CMAKE_C_COMPILER_LAUNCHER:STRING + CMAKE_CXX_COMPILER_LAUNCHER:STRING + CMAKE_OBJC_COMPILER_LAUNCHER:STRING + CMAKE_OBJCXX_COMPILER_LAUNCHER:STRING + ) + + foreach(var_with_type IN LISTS vars_to_pass_if_defined) + string(REPLACE ":" ";" key_as_list "${var_with_type}") + list(GET key_as_list 0 var) + if(NOT DEFINED ${var}) + continue() + endif() + + # Preserve lists + string(REPLACE ";" "$<SEMICOLON>" varForGenex "${${var}}") + + list(APPEND var_defs -D${var_with_type}=${varForGenex}) + endforeach() + + if(QT_INTERNAL_VERBOSE_EXAMPLES) + list(APPEND var_defs -DCMAKE_MESSAGE_LOG_LEVEL:STRING=DEBUG) + list(APPEND var_defs -DCMAKE_AUTOGEN_VERBOSE:BOOL=TRUE) + endif() + + set(deps "") + list(REMOVE_DUPLICATES QT_EXAMPLE_DEPENDENCIES) + foreach(dep IN LISTS QT_EXAMPLE_DEPENDENCIES) + if(TARGET ${dep}) + list(APPEND deps ${dep}) + endif() + endforeach() + + set(independent_args) + cmake_policy(PUSH) + if(POLICY CMP0114) + set(independent_args INDEPENDENT TRUE) + cmake_policy(SET CMP0114 NEW) + endif() + + # The USES_TERMINAL_BUILD setting forces the build step to the console pool + # when using Ninja. This has two benefits: + # + # - You see build output as it is generated instead of at the end of the + # build step. + # - Only one task can use the console pool at a time, so it effectively + # serializes all example build steps, thereby preventing CPU + # over-commitment. + # + # If the loss of interactivity is not so important, one can allow CPU + # over-commitment for Ninja builds. This may result in better throughput, + # but is not allowed by default because it can make a machine almost + # unusable while a compilation is running. + set(terminal_args USES_TERMINAL_BUILD TRUE) + if(CMAKE_GENERATOR MATCHES "Ninja") + option(QT_BUILD_EXAMPLES_WITH_CPU_OVERCOMMIT + "Allow CPU over-commitment when building examples (Ninja only)" + ) + if(QT_BUILD_EXAMPLES_WITH_CPU_OVERCOMMIT) + set(terminal_args) + endif() + endif() + + # QT_EXAMPLE_INSTALL_MARKER + # The goal is to install each example project into a directory that keeps the example source dir + # hierarchy, without polluting the example projects with dirty INSTALL_EXAMPLEDIR and + # INSTALL_EXAMPLESDIR usage. + # E.g. ensure qtbase/examples/widgets/widgets/wiggly is installed to + # $qt_example_install_prefix/examples/widgets/widgets/wiggly/wiggly.exe + # $qt_example_install_prefix defaults to ${CMAKE_INSTALL_PREFIX}/${INSTALL_EXAMPLEDIR} + # but can also be set to a custom location. + # This needs to work both: + # - when using ExternalProject to build examples + # - when examples are built in-tree as part of Qt (no ExternalProject). + # The reason we want to support the latter is for nicer IDE integration: a can developer can + # work with a Qt repo and its examples using the same build dir. + # + # In both case we have to ensure examples are not accidentally installed to $qt_prefix/bin or + # similar. + # + # Example projects installation matrix. + # 1) ExternalProject + unclean example install rules (INSTALL_EXAMPLEDIR is set) => + # use _qt_internal_override_example_install_dir_to_dot + ExternalProject_Add's INSTALL_DIR + # using relative_dir from QT_EXAMPLE_BASE_DIR to example_source_dir + # + # 2) ExternalProject + clean example install rules => + # use ExternalProject_Add's INSTALL_DIR using relative_dir from QT_EXAMPLE_BASE_DIR to + # example_source_dir, _qt_internal_override_example_install_dir_to_dot would be a no-op + # + # 3) in-tree + unclean example install rules (INSTALL_EXAMPLEDIR is set) + # + + # 4) in-tree + clean example install rules => + # ensure CMAKE_INSTALL_PREFIX is unset in parent cmake_install.cmake file, set non-cache + # CMAKE_INSTALL_PREFIX using relative_dir from QT_EXAMPLE_BASE_DIR to + # example_source_dir, use _qt_internal_override_example_install_dir_to_dot to ensure + # INSTALL_EXAMPLEDIR does not interfere. + + qt_internal_get_example_install_path(example_install_path "${subdir}") + + set(ep_binary_dir "${CMAKE_CURRENT_BINARY_DIR}/${subdir}") + + set(build_command "") + if(QT_INTERNAL_VERBOSE_EXAMPLES AND CMAKE_GENERATOR MATCHES "Ninja") + set(build_command BUILD_COMMAND "${CMAKE_COMMAND}" --build "." -- -v) + endif() + + ExternalProject_Add(${arg_NAME} + EXCLUDE_FROM_ALL TRUE + SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/${subdir}" + PREFIX "${CMAKE_CURRENT_BINARY_DIR}/${subdir}-ep" + STAMP_DIR "${CMAKE_CURRENT_BINARY_DIR}/${subdir}-ep/stamp" + BINARY_DIR "${ep_binary_dir}" + INSTALL_DIR "${example_install_path}" + INSTALL_COMMAND "" + ${build_command} + TEST_COMMAND "" + DEPENDS ${deps} + CMAKE_CACHE_ARGS ${var_defs} + -DCMAKE_INSTALL_PREFIX:STRING=<INSTALL_DIR> + -DQT_INTERNAL_SET_EXAMPLE_INSTALL_DIR_TO_DOT:BOOL=TRUE + ${terminal_args} + ) + + # Install the examples when the the user runs 'make install', and not at build time (which is + # the default for ExternalProjects). + install(CODE "\ +# Install example from inside ExternalProject into the main build's install prefix. +execute_process( + COMMAND + \"${CMAKE_COMMAND}\" --build \"${ep_binary_dir}\" --target install +) +") + + # Force configure step to re-run after we configure the main project + set(reconfigure_check_file ${CMAKE_CURRENT_BINARY_DIR}/reconfigure_${arg_NAME}.txt) + file(TOUCH ${reconfigure_check_file}) + ExternalProject_Add_Step(${arg_NAME} reconfigure-check + DEPENDERS configure + DEPENDS ${reconfigure_check_file} + ${independent_args} + ) + + # Create an apk external project step and custom target that invokes the apk target + # within the external project. + # Make the global apk target depend on that custom target. + if(ANDROID) + ExternalProject_Add_Step(${arg_NAME} apk + COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target apk + DEPENDEES configure + EXCLUDE_FROM_MAIN YES + ${terminal_args} + ) + ExternalProject_Add_StepTargets(${arg_NAME} apk) + + if(TARGET apk) + add_dependencies(apk ${arg_NAME}-apk) + endif() + endif() + + cmake_policy(POP) + + string(TOLOWER ${PROJECT_NAME} project_name_lower) + add_dependencies(examples_${project_name_lower} ${arg_NAME}) + +endfunction() + +function(qt_internal_install_example_sources subdir) + set(options "") + set(single_args NAME REPO_NAME) + set(multi_args "") + + cmake_parse_arguments(PARSE_ARGV 1 arg "${options}" "${single_args}" "${multi_args}") + + qt_internal_get_examples_sources_install_path(example_install_path "${subdir}") + + # The trailing slash is important to avoid duplicate nested directory names. + set(example_source_dir "${subdir}/") + + # Allow controlling whether sources should be part of the default install target. + if(QT_INSTALL_EXAMPLES_SOURCES_BY_DEFAULT) + set(exclude_from_all "") + else() + set(exclude_from_all "EXCLUDE_FROM_ALL") + endif() + + # Create an install component for all example sources. Can also be part of the default + # install target if EXCLUDE_FROM_ALL is not passed. + install( + DIRECTORY "${example_source_dir}" + DESTINATION "${example_install_path}" + COMPONENT "examples_sources" + USE_SOURCE_PERMISSIONS + ${exclude_from_all} + ) + + # Also create a specific install component just for this repo's examples. + install( + DIRECTORY "${example_source_dir}" + DESTINATION "${example_install_path}" + COMPONENT "examples_sources_${arg_REPO_NAME}" + USE_SOURCE_PERMISSIONS + EXCLUDE_FROM_ALL + ) + + # Also create a specific install component just for the current example's sources. + install( + DIRECTORY "${example_source_dir}" + DESTINATION "${example_install_path}" + COMPONENT "examples_sources_${arg_NAME}" + USE_SOURCE_PERMISSIONS + EXCLUDE_FROM_ALL + ) +endfunction() diff --git a/cmake/QtBuildRepoHelpers.cmake b/cmake/QtBuildRepoHelpers.cmake new file mode 100644 index 0000000000..93066b2df4 --- /dev/null +++ b/cmake/QtBuildRepoHelpers.cmake @@ -0,0 +1,883 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# Macros and functions for building Qt submodules + +# The macro sets all the necessary pre-conditions and setup consistent environment for building +# the Qt repository. It has to be called right after the find_package(Qt6 COMPONENTS BuildInternals) +# call. Otherwise we cannot make sure that all the required policies will be applied to the Qt +# components that are involved in build procedure. +macro(qt_internal_project_setup) + # Check for the minimum CMake version. + include(QtCMakeVersionHelpers) + qt_internal_require_suitable_cmake_version() + qt_internal_upgrade_cmake_policies() +endmacro() + +macro(qt_build_internals_set_up_private_api) + # TODO: this call needs to be removed once all repositories got the qtbase update + qt_internal_project_setup() + + # Qt specific setup common for all modules: + include(QtSetup) + include(FeatureSummary) + + # Optionally include a repo specific Setup module. + include(${PROJECT_NAME}Setup OPTIONAL) + include(QtRepoSetup OPTIONAL) + + # Find Apple frameworks if needed. + qt_find_apple_system_frameworks() + + # Decide whether tools will be built. + qt_check_if_tools_will_be_built() +endmacro() + +# add toplevel targets for each subdirectory, e.g. qtbase_src +function(qt_build_internals_add_toplevel_targets) + set(qt_repo_target_all "") + get_directory_property(directories DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" SUBDIRECTORIES) + foreach(directory IN LISTS directories) + set(qt_repo_targets "") + get_filename_component(qt_repo_target_basename ${directory} NAME) + _qt_internal_collect_buildsystem_targets(qt_repo_targets "${directory}" EXCLUDE UTILITY) + if (qt_repo_targets) + set(qt_repo_target_name "${qt_repo_targets_name}_${qt_repo_target_basename}") + message(DEBUG "${qt_repo_target_name} depends on ${qt_repo_targets}") + add_custom_target("${qt_repo_target_name}" + COMMENT "Building everything in ${qt_repo_targets_name}/${qt_repo_target_basename}") + add_dependencies("${qt_repo_target_name}" ${qt_repo_targets}) + list(APPEND qt_repo_target_all "${qt_repo_target_name}") + + # Create special dependency target for External Project examples excluding targets + # marked as skipped. + if(qt_repo_target_basename STREQUAL "src") + set(qt_repo_target_name + "${qt_repo_targets_name}_${qt_repo_target_basename}_for_examples") + add_custom_target("${qt_repo_target_name}") + + set(unskipped_targets "") + foreach(target IN LISTS qt_repo_targets) + if(TARGET "${target}") + qt_internal_is_target_skipped_for_examples("${target}" is_skipped) + if(NOT is_skipped) + list(APPEND unskipped_targets "${target}") + endif() + endif() + endforeach() + if(unskipped_targets) + add_dependencies("${qt_repo_target_name}" ${unskipped_targets}) + endif() + endif() + endif() + + endforeach() + if (qt_repo_target_all) + # Note qt_repo_targets_name is different from qt_repo_target_name that is used above. + add_custom_target("${qt_repo_targets_name}" + COMMENT "Building everything in ${qt_repo_targets_name}") + add_dependencies("${qt_repo_targets_name}" ${qt_repo_target_all}) + message(DEBUG "${qt_repo_targets_name} depends on ${qt_repo_target_all}") + endif() +endfunction() + +macro(qt_enable_cmake_languages) + set(__qt_required_language_list C CXX) + set(__qt_platform_required_language_list ) + + if(APPLE) + list(APPEND __qt_platform_required_language_list OBJC OBJCXX) + endif() + + foreach(__qt_lang ${__qt_required_language_list}) + enable_language(${__qt_lang}) + endforeach() + + foreach(__qt_lang ${__qt_platform_required_language_list}) + enable_language(${__qt_lang}) + endforeach() + + # The qtbase call is handled in qtbase/CMakeLists.txt. + # This call is used for projects other than qtbase, including for other project's standalone + # tests. + # Because the function uses QT_FEATURE_foo values, it's important that find_package(Qt6Core) is + # called before this function. but that's usually the case for Qt repos. + if(NOT PROJECT_NAME STREQUAL "QtBase") + qt_internal_set_up_config_optimizations_like_in_qmake() + endif() +endmacro() + +# Minimum setup required to have any CMakeList.txt build as as a standalone +# project after importing BuildInternals +macro(qt_prepare_standalone_project) + qt_set_up_build_internals_paths() + qt_build_internals_set_up_private_api() + qt_enable_cmake_languages() +endmacro() + +# Define a repo target set, and store accompanying information. +# +# A repo target set is a subset of targets in a Qt module repository. To build a repo target set, +# set QT_BUILD_SINGLE_REPO_TARGET_SET to the name of the repo target set. +# +# This function is to be called in the top-level project file of a repository, +# before qt_internal_prepare_single_repo_target_set_build() +# +# This function stores information in variables of the parent scope. +# +# Positional Arguments: +# name - The name of this repo target set. +# +# Named Arguments: +# DEPENDS - List of Qt6 COMPONENTS that are build dependencies of this repo target set. +function(qt_internal_define_repo_target_set name) + set(oneValueArgs DEPENDS) + set(prefix QT_REPO_TARGET_SET_) + cmake_parse_arguments(${prefix}${name} "" ${oneValueArgs} "" ${ARGN}) + foreach(arg IN LISTS oneValueArgs) + set(${prefix}${name}_${arg} ${${prefix}${name}_${arg}} PARENT_SCOPE) + endforeach() + set(QT_REPO_KNOWN_TARGET_SETS "${QT_REPO_KNOWN_TARGET_SETS};${name}" PARENT_SCOPE) +endfunction() + +# Setup a single repo target set build if QT_BUILD_SINGLE_REPO_TARGET_SET is defined. +# +# This macro must be called in the top-level project file of the repository after all repo target +# sets have been defined. +macro(qt_internal_prepare_single_repo_target_set_build) + if(DEFINED QT_BUILD_SINGLE_REPO_TARGET_SET) + if(NOT QT_BUILD_SINGLE_REPO_TARGET_SET IN_LIST QT_REPO_KNOWN_TARGET_SETS) + message(FATAL_ERROR + "Repo target set '${QT_BUILD_SINGLE_REPO_TARGET_SET}' is undefined.") + endif() + message(STATUS + "Preparing single repo target set build of ${QT_BUILD_SINGLE_REPO_TARGET_SET}") + if (NOT "${QT_REPO_TARGET_SET_${QT_BUILD_SINGLE_REPO_TARGET_SET}_DEPENDS}" STREQUAL "") + find_package(${INSTALL_CMAKE_NAMESPACE} ${PROJECT_VERSION} CONFIG REQUIRED + COMPONENTS ${QT_REPO_TARGET_SET_${QT_BUILD_SINGLE_REPO_TARGET_SET}_DEPENDS}) + endif() + endif() +endmacro() + +macro(qt_build_repo_begin) + set(QT_INTERNAL_REPO_POST_PROCESS_CALLED FALSE) + list(APPEND CMAKE_MESSAGE_CONTEXT "${PROJECT_NAME}") + + qt_build_internals_set_up_private_api() + + # Prevent installation in non-prefix builds. + # We need to associate targets with export names, and that is only possible to do with the + # install(TARGETS) command. But in a non-prefix build, we don't want to install anything. + # To make sure that developers don't accidentally run make install, add bail out code to + # cmake_install.cmake. + if(NOT QT_WILL_INSTALL) + # In a top-level build, print a message only in qtbase, which is the first repository. + if(NOT QT_SUPERBUILD OR (PROJECT_NAME STREQUAL "QtBase")) + install(CODE [[message(FATAL_ERROR + "Qt was configured as non-prefix build. " + "Installation is not supported for this arrangement.")]]) + endif() + + install(CODE [[return()]]) + endif() + + qt_enable_cmake_languages() + + qt_internal_generate_binary_strip_wrapper() + + # Add global docs targets that will work both for per-repo builds, and super builds. + if(NOT TARGET docs) + add_custom_target(docs) + add_custom_target(prepare_docs) + add_custom_target(generate_docs) + add_custom_target(html_docs) + add_custom_target(qch_docs) + add_custom_target(install_html_docs) + add_custom_target(install_qch_docs) + add_custom_target(install_docs) + add_dependencies(html_docs generate_docs) + add_dependencies(docs html_docs qch_docs) + add_dependencies(install_docs install_html_docs install_qch_docs) + endif() + + if(NOT TARGET sync_headers) + add_custom_target(sync_headers) + endif() + + # The special target that we use to sync 3rd-party headers before the gn run when building + # qtwebengine in top-level builds. + if(NOT TARGET thirdparty_sync_headers) + add_custom_target(thirdparty_sync_headers) + endif() + + # Add global qt_plugins, qpa_plugins and qpa_default_plugins convenience custom targets. + # Internal executables will add a dependency on the qpa_default_plugins target, + # so that building and running a test ensures it won't fail at runtime due to a missing qpa + # plugin. + if(NOT TARGET qt_plugins) + add_custom_target(qt_plugins) + add_custom_target(qpa_plugins) + add_custom_target(qpa_default_plugins) + endif() + + string(TOLOWER ${PROJECT_NAME} project_name_lower) + + # Target to build all plugins that are part of the current repo. + set(qt_repo_plugins "qt_plugins_${project_name_lower}") + if(NOT TARGET ${qt_repo_plugins}) + add_custom_target(${qt_repo_plugins}) + endif() + + # Target to build all plugins that are part of the current repo and the current repo's + # dependencies plugins. Used for external project example dependencies. + set(qt_repo_plugins_recursive "${qt_repo_plugins}_recursive") + if(NOT TARGET ${qt_repo_plugins_recursive}) + add_custom_target(${qt_repo_plugins_recursive}) + add_dependencies(${qt_repo_plugins_recursive} "${qt_repo_plugins}") + endif() + + qt_internal_read_repo_dependencies(qt_repo_deps "${PROJECT_SOURCE_DIR}") + if(qt_repo_deps) + foreach(qt_repo_dep IN LISTS qt_repo_deps) + if(TARGET qt_plugins_${qt_repo_dep}) + message(DEBUG + "${qt_repo_plugins_recursive} depends on qt_plugins_${qt_repo_dep}") + add_dependencies(${qt_repo_plugins_recursive} "qt_plugins_${qt_repo_dep}") + endif() + endforeach() + endif() + + set(qt_repo_targets_name ${project_name_lower}) + set(qt_docs_target_name docs_${project_name_lower}) + set(qt_docs_prepare_target_name prepare_docs_${project_name_lower}) + set(qt_docs_generate_target_name generate_docs_${project_name_lower}) + set(qt_docs_html_target_name html_docs_${project_name_lower}) + set(qt_docs_qch_target_name qch_docs_${project_name_lower}) + set(qt_docs_install_html_target_name install_html_docs_${project_name_lower}) + set(qt_docs_install_qch_target_name install_qch_docs_${project_name_lower}) + set(qt_docs_install_target_name install_docs_${project_name_lower}) + + add_custom_target(${qt_docs_target_name}) + add_custom_target(${qt_docs_prepare_target_name}) + add_custom_target(${qt_docs_generate_target_name}) + add_custom_target(${qt_docs_qch_target_name}) + add_custom_target(${qt_docs_html_target_name}) + add_custom_target(${qt_docs_install_html_target_name}) + add_custom_target(${qt_docs_install_qch_target_name}) + add_custom_target(${qt_docs_install_target_name}) + + add_dependencies(${qt_docs_generate_target_name} ${qt_docs_prepare_target_name}) + add_dependencies(${qt_docs_html_target_name} ${qt_docs_generate_target_name}) + add_dependencies(${qt_docs_target_name} ${qt_docs_html_target_name} ${qt_docs_qch_target_name}) + add_dependencies(${qt_docs_install_target_name} ${qt_docs_install_html_target_name} ${qt_docs_install_qch_target_name}) + + # Make top-level prepare_docs target depend on the repository-level prepare_docs_<repo> target. + add_dependencies(prepare_docs ${qt_docs_prepare_target_name}) + + # Make top-level install_*_docs targets depend on the repository-level install_*_docs targets. + add_dependencies(install_html_docs ${qt_docs_install_html_target_name}) + add_dependencies(install_qch_docs ${qt_docs_install_qch_target_name}) + + # Add host_tools meta target, so that developrs can easily build only tools and their + # dependencies when working in qtbase. + if(NOT TARGET host_tools) + add_custom_target(host_tools) + add_custom_target(bootstrap_tools) + endif() + + # Add benchmark meta target. It's collection of all benchmarks added/registered by + # 'qt_internal_add_benchmark' helper. + if(NOT TARGET benchmark) + add_custom_target(benchmark) + endif() + + if(QT_INTERNAL_SYNCED_MODULES) + set_property(GLOBAL PROPERTY _qt_synced_modules ${QT_INTERNAL_SYNCED_MODULES}) + endif() +endmacro() + +# Runs delayed actions on some of the Qt targets. +# Can be called either explicitly or as part of qt_build_repo_end(). +macro(qt_build_repo_post_process) + if(NOT QT_INTERNAL_REPO_POST_PROCESS_CALLED) + set(QT_INTERNAL_REPO_POST_PROCESS_CALLED TRUE) + if(NOT QT_BUILD_STANDALONE_TESTS) + include(QtPostProcess) + endif() + endif() +endmacro() + +macro(qt_build_repo_end) + if(NOT QT_BUILD_STANDALONE_TESTS) + qt_build_repo_post_process() + + # Install the repo-specific cmake find modules. + qt_path_join(__qt_repo_install_dir ${QT_CONFIG_INSTALL_DIR} ${INSTALL_CMAKE_NAMESPACE}) + qt_path_join(__qt_repo_build_dir ${QT_CONFIG_BUILD_DIR} ${INSTALL_CMAKE_NAMESPACE}) + + if(NOT PROJECT_NAME STREQUAL "QtBase") + if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + qt_copy_or_install(DIRECTORY cmake/ + DESTINATION "${__qt_repo_install_dir}" + FILES_MATCHING PATTERN "Find*.cmake" + ) + if(QT_SUPERBUILD AND QT_WILL_INSTALL) + file(COPY cmake/ + DESTINATION "${__qt_repo_build_dir}" + FILES_MATCHING PATTERN "Find*.cmake" + ) + endif() + endif() + endif() + + if(NOT QT_SUPERBUILD) + qt_print_feature_summary() + endif() + endif() + + qt_build_internals_add_toplevel_targets() + + if(NOT QT_SUPERBUILD) + qt_print_build_instructions() + endif() + + get_property(synced_modules GLOBAL PROPERTY _qt_synced_modules) + if(synced_modules) + set(QT_INTERNAL_SYNCED_MODULES ${synced_modules} CACHE INTERNAL + "List of the synced modules. Prevents running syncqt.cpp after the first configuring.") + endif() + + if(NOT QT_SUPERBUILD) + qt_internal_save_previously_visited_packages() + endif() + + if(QT_INTERNAL_FRESH_REQUESTED) + set(QT_INTERNAL_FRESH_REQUESTED "FALSE" CACHE INTERNAL "") + endif() + + if(NOT QT_SUPERBUILD) + qt_internal_qt_configure_end() + endif() + + list(POP_BACK CMAKE_MESSAGE_CONTEXT) +endmacro() + +# Function called either at the end of per-repo configuration, or at the end of configuration of +# a super build. +# At the moment it is called before examples are configured in a per-repo build. We might want +# to change that at some point if needed. +function(qt_internal_qt_configure_end) + # If Qt is configued via the configure script, remove the marker variable, so that any future + # reconfigurations that are done by calling cmake directly don't trigger configure specific + # logic. + unset(QT_INTERNAL_CALLED_FROM_CONFIGURE CACHE) +endfunction() + +macro(qt_build_repo) + qt_build_repo_begin(${ARGN}) + + qt_build_repo_impl_find_package_tests() + qt_build_repo_impl_src() + qt_build_repo_impl_tools() + + qt_build_repo_post_process() + qt_build_repo_impl_tests() + + qt_build_repo_end() + + qt_build_repo_impl_examples() +endmacro() + +macro(qt_build_repo_impl_find_package_tests) + # If testing is enabled, try to find the qtbase Test package. + # Do this before adding src, because there might be test related conditions + # in source. + if (QT_BUILD_TESTS AND NOT QT_BUILD_STANDALONE_TESTS) + # When looking for the Test package, do it using the Qt6 package version, in case if + # PROJECT_VERSION is following a different versioning scheme. + if(Qt6_VERSION) + set(_qt_build_repo_impl_find_package_tests_version "${Qt6_VERSION}") + else() + set(_qt_build_repo_impl_find_package_tests_version "${PROJECT_VERSION}") + endif() + + find_package(Qt6 + "${_qt_build_repo_impl_find_package_tests_version}" + CONFIG REQUIRED COMPONENTS Test) + unset(_qt_build_repo_impl_find_package_tests_version) + endif() +endmacro() + +macro(qt_build_repo_impl_src) + if(NOT QT_BUILD_STANDALONE_TESTS) + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/CMakeLists.txt") + add_subdirectory(src) + endif() + endif() + if(QT_FEATURE_lttng AND NOT TARGET LTTng::UST) + qt_find_package(LTTngUST PROVIDED_TARGETS LTTng::UST + MODULE_NAME global QMAKE_LIB lttng-ust) + endif() +endmacro() + +macro(qt_build_repo_impl_tools) + if(NOT QT_BUILD_STANDALONE_TESTS) + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tools/CMakeLists.txt") + add_subdirectory(tools) + endif() + endif() +endmacro() + +macro(qt_build_repo_impl_tests) + if (QT_BUILD_TESTS AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt") + add_subdirectory(tests) + if(NOT QT_BUILD_TESTS_BY_DEFAULT) + set_property(DIRECTORY tests PROPERTY EXCLUDE_FROM_ALL TRUE) + endif() + endif() +endmacro() + +macro(qt_build_repo_impl_examples) + if(QT_BUILD_EXAMPLES + AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/examples/CMakeLists.txt" + AND NOT QT_BUILD_STANDALONE_TESTS) + message(STATUS "Configuring examples.") + add_subdirectory(examples) + endif() +endmacro() + +macro(qt_set_up_standalone_tests_build) + # Remove this macro once all usages of it have been removed. + # Standalone tests are not handled via the main repo project and qt_build_tests. +endmacro() + +function(qt_get_standalone_tests_config_files_path out_var) + set(path "${QT_CONFIG_INSTALL_DIR}/${INSTALL_CMAKE_NAMESPACE}BuildInternals/StandaloneTests") + + # QT_CONFIG_INSTALL_DIR is relative in prefix builds. + if(QT_WILL_INSTALL) + if(DEFINED CMAKE_STAGING_PREFIX) + qt_path_join(path "${CMAKE_STAGING_PREFIX}" "${path}") + else() + qt_path_join(path "${CMAKE_INSTALL_PREFIX}" "${path}") + endif() + endif() + + set("${out_var}" "${path}" PARENT_SCOPE) +endfunction() + +function(qt_internal_get_standalone_tests_config_file_name out_var) + # When doing a "single repo target set" build (like in qtscxqml) ensure we use a unique tests + # config file for each repo target set. Using the PROJECT_NAME only is not enough because + # the same file will be overridden with different content on each repo set install. + set(tests_config_file_name "${PROJECT_NAME}") + + if(QT_BUILD_SINGLE_REPO_TARGET_SET) + string(APPEND tests_config_file_name "RepoSet${QT_BUILD_SINGLE_REPO_TARGET_SET}") + endif() + string(APPEND tests_config_file_name "TestsConfig.cmake") + + set(${out_var} "${tests_config_file_name}" PARENT_SCOPE) +endfunction() + +macro(qt_build_tests) + set(CMAKE_UNITY_BUILD OFF) + + # Prepending to QT_BUILD_CMAKE_PREFIX_PATH helps find components of Qt6, because those + # find_package calls use NO_DEFAULT_PATH, and thus CMAKE_PREFIX_PATH is ignored. + list(PREPEND CMAKE_FIND_ROOT_PATH "${QT_BUILD_DIR}") + list(PREPEND QT_BUILD_CMAKE_PREFIX_PATH "${QT_BUILD_DIR}/${INSTALL_LIBDIR}/cmake") + + if(QT_BUILD_STANDALONE_TESTS) + # Find location of TestsConfig.cmake. These contain the modules that need to be + # find_package'd when testing. + qt_get_standalone_tests_config_files_path(_qt_build_tests_install_prefix) + + qt_internal_get_standalone_tests_config_file_name(_qt_tests_config_file_name) + set(_qt_standalone_tests_config_file_path + "${_qt_build_tests_install_prefix}/${_qt_tests_config_file_name}") + include("${_qt_standalone_tests_config_file_path}" + OPTIONAL + RESULT_VARIABLE _qt_standalone_tests_included) + if(NOT _qt_standalone_tests_included) + message(DEBUG + "Standalone tests config file not included because it does not exist: " + "${_qt_standalone_tests_config_file_path}" + ) + else() + message(DEBUG + "Standalone tests config file included successfully: " + "${_qt_standalone_tests_config_file_path}" + ) + endif() + + unset(_qt_standalone_tests_config_file_path) + unset(_qt_standalone_tests_included) + unset(_qt_tests_config_file_name) + + # Of course we always need the test module as well. + # When looking for the Test package, do it using the Qt6 package version, in case if + # PROJECT_VERSION is following a different versioning scheme. + if(Qt6_VERSION) + set(_qt_build_tests_package_version "${Qt6_VERSION}") + else() + set(_qt_build_tests_package_version "${PROJECT_VERSION}") + endif() + find_package(Qt6 "${_qt_build_tests_package_version}" CONFIG REQUIRED COMPONENTS Test) + unset(_qt_build_tests_package_version) + + # Set language standards after finding Core, because that's when the relevant + # feature variables are available, and the call in QtSetup is too early when building + # standalone tests, because Core was not find_package()'d yet. + qt_set_language_standards() + + if(NOT QT_SUPERBUILD) + # Set up fake standalone tests install prefix, so we don't pollute the Qt install + # prefix. For super builds it needs to be done in qt5/CMakeLists.txt. + qt_set_up_fake_standalone_tests_install_prefix() + endif() + else() + if(ANDROID) + # When building in-tree tests we need to specify the QT_ANDROID_ABIS list. Since we + # build Qt for the single ABI, build tests for this ABI only. + set(QT_ANDROID_ABIS "${CMAKE_ANDROID_ARCH_ABI}") + endif() + endif() + + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/auto/CMakeLists.txt") + add_subdirectory(auto) + endif() + if(NOT QT_BUILD_MINIMAL_STATIC_TESTS AND NOT QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/baseline/CMakeLists.txt") + add_subdirectory(baseline) + endif() + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/benchmarks/CMakeLists.txt" AND QT_BUILD_BENCHMARKS) + add_subdirectory(benchmarks) + endif() + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/manual/CMakeLists.txt" AND QT_BUILD_MANUAL_TESTS) + add_subdirectory(manual) + # Adding this logic to all tests impacts the configure time ~3sec in addition. We still + # might want this in the future for other test types since currently we have a moderate + # subset of tests that require manual initialization of autotools. + _qt_internal_collect_buildsystem_targets(targets + "${CMAKE_CURRENT_SOURCE_DIR}/manual" EXCLUDE UTILITY ALIAS) + foreach(target ${targets}) + qt_autogen_tools(${target} ENABLE_AUTOGEN_TOOLS "moc" "rcc") + if(TARGET Qt::Widgets) + qt_autogen_tools(${target} ENABLE_AUTOGEN_TOOLS "uic") + endif() + endforeach() + endif() + endif() + + set(CMAKE_UNITY_BUILD ${QT_UNITY_BUILD}) +endmacro() + +function(qt_compute_relative_path_from_cmake_config_dir_to_prefix) + # Compute the reverse relative path from the CMake config dir to the install prefix. + # This is used in QtBuildInternalsExtras to create a relocatable relative install prefix path. + # This path is used for finding syncqt and other things, regardless of initial install prefix + # (e.g installed Qt was archived and unpacked to a different path on a different machine). + # + # This is meant to be called only once when configuring qtbase. + # + # Similar code exists in Qt6CoreConfigExtras.cmake.in and src/corelib/CMakeLists.txt which + # might not be needed anymore. + if(CMAKE_STAGING_PREFIX) + set(__qt_prefix "${CMAKE_STAGING_PREFIX}") + else() + set(__qt_prefix "${CMAKE_INSTALL_PREFIX}") + endif() + + if(QT_WILL_INSTALL) + get_filename_component(clean_config_prefix + "${__qt_prefix}/${QT_CONFIG_INSTALL_DIR}" ABSOLUTE) + else() + get_filename_component(clean_config_prefix "${QT_CONFIG_BUILD_DIR}" ABSOLUTE) + endif() + file(RELATIVE_PATH + qt_path_from_cmake_config_dir_to_prefix + "${clean_config_prefix}" "${__qt_prefix}") + set(qt_path_from_cmake_config_dir_to_prefix "${qt_path_from_cmake_config_dir_to_prefix}" + PARENT_SCOPE) +endfunction() + +function(qt_get_relocatable_install_prefix out_var) + # We need to compute it only once while building qtbase. Afterwards it's loaded from + # QtBuildInternalsExtras.cmake. + if(QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX) + return() + endif() + # The QtBuildInternalsExtras value is dynamically computed, whereas the initial qtbase + # configuration uses an absolute path. + set(${out_var} "${CMAKE_INSTALL_PREFIX}" PARENT_SCOPE) +endfunction() + +function(qt_set_up_fake_standalone_tests_install_prefix) + # Set a fake local (non-cache) CMAKE_INSTALL_PREFIX. + # Needed for standalone tests, we don't want to accidentally install a test into the Qt prefix. + # Allow opt-out, if a user knows what they're doing. + if(QT_NO_FAKE_STANDALONE_TESTS_INSTALL_PREFIX) + return() + endif() + set(new_install_prefix "${CMAKE_BINARY_DIR}/fake_prefix") + + # It's IMPORTANT that this is not a cache variable. Otherwise + # qt_get_standalone_tests_confg_files_path() will not work on re-configuration. + message(STATUS + "Setting local standalone test install prefix (non-cached) to '${new_install_prefix}'.") + set(CMAKE_INSTALL_PREFIX "${new_install_prefix}" PARENT_SCOPE) + + # We also need to clear the staging prefix if it's set, otherwise CMake will modify any computed + # rpaths containing the staging prefix to point to the new fake prefix, which is not what we + # want. This replacement is done in cmComputeLinkInformation::GetRPath(). + # + # By clearing the staging prefix for the standalone tests, any detected link time + # rpaths will be embedded as-is, which will point to the place where Qt was installed (aka + # the staging prefix). + if(DEFINED CMAKE_STAGING_PREFIX) + message(STATUS "Clearing local standalone test staging prefix (non-cached).") + set(CMAKE_STAGING_PREFIX "" PARENT_SCOPE) + endif() +endfunction() + +# Mean to be called when configuring examples as part of the main build tree, as well as for CMake +# tests (tests that call CMake to try and build CMake applications). +macro(qt_internal_set_up_build_dir_package_paths) + list(PREPEND CMAKE_PREFIX_PATH "${QT_BUILD_DIR}/${INSTALL_LIBDIR}/cmake") + # Make sure the CMake config files do not recreate the already-existing targets + set(QT_NO_CREATE_TARGETS TRUE) +endmacro() + +function(qt_internal_static_link_order_test) + # The CMake versions greater than 3.21 take care about the resource object files order in a + # linker line, it's expected that all object files are located at the beginning of the linker + # line. + # No need to run the test. + if(CMAKE_VERSION VERSION_LESS 3.21) + __qt_internal_check_link_order_matters(link_order_matters) + if(link_order_matters) + set(summary_message "no") + else() + set(summary_message "yes") + endif() + else() + set(summary_message "yes") + endif() + qt_configure_add_summary_entry(TYPE "message" + ARGS "Linker can resolve circular dependencies" + MESSAGE "${summary_message}" + ) +endfunction() + +function(qt_internal_check_cmp0099_available) + # Don't care about CMP0099 in CMake versions greater than or equal to 3.21 + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.21) + return() + endif() + + __qt_internal_check_cmp0099_available(result) + if(result) + set(summary_message "yes") + else() + set(summary_message "no") + endif() + qt_configure_add_summary_entry(TYPE "message" + ARGS "CMake policy CMP0099 is supported" + MESSAGE "${summary_message}" + ) +endfunction() + +function(qt_internal_run_common_config_tests) + qt_configure_add_summary_section(NAME "Common build options") + qt_internal_static_link_order_test() + qt_internal_check_cmp0099_available() + qt_configure_end_summary_section() +endfunction() + +# It is used in QtWebEngine to replace the REALPATH with ABSOLUTE path, which is +# useful for building Qt in Homebrew. +function(qt_internal_get_filename_path_mode out_var) + set(mode REALPATH) + if(APPLE AND QT_ALLOW_SYMLINK_IN_PATHS) + set(mode ABSOLUTE) + endif() + set(${out_var} ${mode} PARENT_SCOPE) +endfunction() + +macro(qt_internal_setup_platform_support_variables) + # Define some constants to check for certain platforms, etc. + # Needs to be loaded before qt_repo_build() to handle require() clauses before even starting a + # repo build. + include(QtPlatformSupport) +endmacro() + +function(qt_build_internals_set_up_system_prefixes) + if(APPLE AND NOT FEATURE_pkg_config) + # Remove /usr/local and other paths like that which CMake considers as system prefixes on + # darwin platforms. CMake considers them as system prefixes, but in qmake / Qt land we only + # consider the SDK path as a system prefix. + # 3rd party libraries in these locations should not be picked up when building Qt, + # unless opted-in via the pkg-config feature, which in turn will disable this behavior. + # + # Note that we can't remove /usr as a system prefix path, because many programs won't be + # found then (e.g. perl). + set(QT_CMAKE_SYSTEM_PREFIX_PATH_BACKUP "${CMAKE_SYSTEM_PREFIX_PATH}" PARENT_SCOPE) + set(QT_CMAKE_SYSTEM_FRAMEWORK_PATH_BACKUP "${CMAKE_SYSTEM_FRAMEWORK_PATH}" PARENT_SCOPE) + + list(REMOVE_ITEM CMAKE_SYSTEM_PREFIX_PATH + "/usr/local" # Homebrew + "/opt/homebrew" # Apple Silicon Homebrew + "/usr/X11R6" + "/usr/pkg" + "/opt" + "/sw" # Fink + "/opt/local" # MacPorts + ) + if(_CMAKE_INSTALL_DIR) + list(REMOVE_ITEM CMAKE_SYSTEM_PREFIX_PATH "${_CMAKE_INSTALL_DIR}") + endif() + list(REMOVE_ITEM CMAKE_SYSTEM_FRAMEWORK_PATH "~/Library/Frameworks") + set(CMAKE_SYSTEM_PREFIX_PATH "${CMAKE_SYSTEM_PREFIX_PATH}" PARENT_SCOPE) + set(CMAKE_SYSTEM_FRAMEWORK_PATH "${CMAKE_SYSTEM_FRAMEWORK_PATH}" PARENT_SCOPE) + + # Also tell qt_find_package() not to use PATH when looking for packages. + # We can't simply set CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH to OFF because that will break + # find_program(), and for instance ccache won't be found. + # That's why we set a different variable which is used by qt_find_package. + set(QT_NO_USE_FIND_PACKAGE_SYSTEM_ENVIRONMENT_PATH "ON" PARENT_SCOPE) + endif() +endfunction() + +# Set FEATURE_${feature} if INPUT_${feature} is set in certain circumstances. +# Set FEATURE_${feature}_computed_from_input to TRUE or FALSE depending on whether the +# INPUT_${feature} value has overridden the FEATURE_${feature} variable. +# +# Needs to be in QtBuildInternalsConfig.cmake instead of QtFeature.cmake because it's used in +# qt_build_internals_disable_pkg_config_if_needed. +function(qt_internal_compute_feature_value_from_possible_input feature) + # If FEATURE_ is not defined try to use the INPUT_ variable to enable/disable feature. + # If FEATURE_ is defined and the configure script is being used (so + # QT_INTERNAL_CALLED_FROM_CONFIGURE is TRUE), ignore the FEATURE_ variable, and take into + # account the INPUT_ variable instead, because a command line argument takes priority over + # a pre-cached FEATURE_ variable. + if((NOT DEFINED FEATURE_${feature} OR QT_INTERNAL_CALLED_FROM_CONFIGURE) + AND DEFINED INPUT_${feature} + AND NOT "${INPUT_${feature}}" STREQUAL "undefined" + AND NOT "${INPUT_${feature}}" STREQUAL "") + if(INPUT_${feature}) + set(FEATURE_${feature} ON) + else() + set(FEATURE_${feature} OFF) + endif() + + set(FEATURE_${feature} "${FEATURE_${feature}}" PARENT_SCOPE) + set(FEATURE_${feature}_computed_from_input TRUE PARENT_SCOPE) + else() + set(FEATURE_${feature}_computed_from_input FALSE PARENT_SCOPE) + endif() +endfunction() + +function(qt_build_internals_disable_pkg_config_if_needed) + # pkg-config should not be used by default on Darwin and Windows platforms (and QNX), as defined + # in the qtbase/configure.json. Unfortunately by the time the feature is evaluated there are + # already a few find_package() calls that try to use the FindPkgConfig module. + # Thus, we have to duplicate the condition logic here and disable pkg-config for those platforms + # by default. + # We also need to check if the pkg-config executable exists, to mirror the condition test in + # configure.json. We do that by trying to find the executable ourselves, and not delegating to + # the FindPkgConfig module because that has more unwanted side-effects. + # + # Note that on macOS, if the pkg-config feature is enabled by the user explicitly, we will also + # tell CMake to consider paths like /usr/local (Homebrew) as system paths when looking for + # packages. + # We have to do that because disabling these paths but keeping pkg-config + # enabled won't enable finding all system libraries via pkg-config alone, many libraries can + # only be found via FooConfig.cmake files which means /usr/local should be in the system prefix + # path. + + set(pkg_config_enabled ON) + qt_build_internals_find_pkg_config_executable() + + if(APPLE OR WIN32 OR QNX OR ANDROID OR WASM OR (NOT PKG_CONFIG_EXECUTABLE)) + set(pkg_config_enabled OFF) + endif() + + # Features won't have been evaluated yet if this is the first run, have to evaluate this here + qt_internal_compute_feature_value_from_possible_input(pkg_config) + + # If user explicitly specified a value for the feature, honor it, even if it might break + # the build. + if(DEFINED FEATURE_pkg_config) + if(FEATURE_pkg_config) + set(pkg_config_enabled ON) + else() + set(pkg_config_enabled OFF) + endif() + endif() + + set(FEATURE_pkg_config "${pkg_config_enabled}" CACHE STRING "Using pkg-config") + if(NOT pkg_config_enabled) + qt_build_internals_disable_pkg_config() + else() + unset(PKG_CONFIG_EXECUTABLE CACHE) + endif() +endfunction() + +# This is a copy of the first few lines in FindPkgConfig.cmake. +function(qt_build_internals_find_pkg_config_executable) + # find pkg-config, use PKG_CONFIG if set + if((NOT PKG_CONFIG_EXECUTABLE) AND (NOT "$ENV{PKG_CONFIG}" STREQUAL "")) + set(PKG_CONFIG_EXECUTABLE "$ENV{PKG_CONFIG}" CACHE FILEPATH "pkg-config executable") + endif() + find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config DOC "pkg-config executable") + mark_as_advanced(PKG_CONFIG_EXECUTABLE) +endfunction() + +function(qt_build_internals_disable_pkg_config) + # Disable pkg-config by setting an empty executable path. There's no documented way to + # mark the package as not found, but we can force all pkg_check_modules calls to do nothing + # by setting the variable to an empty value. + set(PKG_CONFIG_EXECUTABLE "" CACHE STRING "Disabled pkg-config usage." FORCE) +endfunction() + +macro(qt_build_internals_find_pkg_config) + # Find package config once before any system prefix modifications. + find_package(PkgConfig QUIET) +endmacro() + + +macro(qt_internal_setup_pkg_config_and_system_prefixes) + if(NOT QT_BUILD_INTERNALS_SKIP_PKG_CONFIG_ADJUSTMENT) + qt_build_internals_disable_pkg_config_if_needed() + endif() + + if(NOT QT_BUILD_INTERNALS_SKIP_FIND_PKG_CONFIG) + qt_build_internals_find_pkg_config() + endif() + + if(NOT QT_BUILD_INTERNALS_SKIP_SYSTEM_PREFIX_ADJUSTMENT) + qt_build_internals_set_up_system_prefixes() + endif() +endmacro() + +macro(qt_internal_setup_standalone_test_when_called_as_a_find_package_component) + if ("STANDALONE_TEST" IN_LIST Qt6BuildInternals_FIND_COMPONENTS) + include(${CMAKE_CURRENT_LIST_DIR}/QtStandaloneTestTemplateProject/Main.cmake) + if (NOT PROJECT_VERSION_MAJOR) + get_property(_qt_major_version TARGET ${QT_CMAKE_EXPORT_NAMESPACE}::Core PROPERTY INTERFACE_QT_MAJOR_VERSION) + set(PROJECT_VERSION ${Qt${_qt_major_version}Core_VERSION}) + + string(REPLACE "." ";" _qt_core_version_list ${PROJECT_VERSION}) + list(GET _qt_core_version_list 0 PROJECT_VERSION_MAJOR) + list(GET _qt_core_version_list 1 PROJECT_VERSION_MINOR) + list(GET _qt_core_version_list 2 PROJECT_VERSION_PATCH) + endif() + endif() +endmacro() + +macro(qt_internal_setup_build_internals) + qt_internal_setup_platform_support_variables() + qt_internal_setup_pkg_config_and_system_prefixes() + qt_internal_setup_standalone_test_when_called_as_a_find_package_component() +endmacro() |