Cristian Le 46b323d94b ConfigureBuildQt: Reconfigure standalone parts also
Change-Id: Iaa985d43206d365b7e9138925801fc0dbccbde2e
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
2025-06-07 13:20:12 +02:00

1497 lines
49 KiB
CMake

# Copyright (C) 2025 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
# Wrapper for RunCMake's run_cmake, with extra logging and early failure handling.
function(run_command prefix name)
set(RunCMake_TEST_COMMAND "${ARGN}")
set(RunCMake-check-file "check.cmake")
set(args ${ARGN})
list(JOIN args " " args_str)
set(working_dir "${RunCMake_TEST_COMMAND_WORKING_DIRECTORY}")
message(STATUS "Running command: '${args_str}' in dir: '${working_dir}'")
run_cmake("${prefix}${name}")
# set by the check file above.
if(should_error_out)
message(FATAL_ERROR "Command ${prefix}${name} failed. Exiting early.")
endif()
endfunction()
# Query the var name from the CMake cache or the environment or use a default value.
function(get_cmake_or_env_or_default out_var var_name_to_check default_value)
# Load the cache variables from the build dir containing the ConfigueBuildQt test
# RunCMake_BINARY_DIR is not actually created at this point so we have to normalize the path
cmake_path(SET actual_BINARY_DIR NORMALIZE "${RunCMake_BINARY_DIR}/..")
load_cache("${actual_BINARY_DIR}"
READ_WITH_PREFIX CACHE_VAL_ "${var_name_to_check}"
)
if(${var_name_to_check})
# This is set within the script, highest priority
set(value "${var_name_to_check}")
elseif(DEFINED ENV{${var_name_to_check}})
# This may be used to change parameters at ctest runtime
set(value "$ENV{${var_name_to_check}}")
elseif(DEFINED CACHE_VAL_${var_name_to_check})
# These parameters are set at configure time
set(value "${CACHE_VAL_${var_name_to_check}}")
else()
set(value "${default_value}")
endif()
set(${out_var} "${value}" PARENT_SCOPE)
endfunction()
# Sets verbose command output based on a cmake var or env var for run_command calls.
macro(setup_verbose_command_output)
get_cmake_or_env_or_default(verbose_command_output QT_CI_BUILD_QT_VERBOSE_COMMAND_OUTPUT "ON")
if(verbose_command_output)
# Show output as it arrives.
# This uses a custom qtbase patch of RunCMake.
set(RunCMake_TEST_ECHO_OUTPUT_VARIABLE TRUE)
set(RunCMake_TEST_ECHO_ERROR_VARIABLE TRUE)
endif()
endmacro()
# Gets the remote git base URL for qt repositories.
function(get_repo_base_url out_var)
# This Coin CI git daemon IP is set in all CI jobs.
# Prefer using it when available, to avoid general network issues.
# Sample value: QT_COIN_GIT_DAEMON=10.215.0.212:9418
get_cmake_or_env_or_default(coin_git_ip_port QT_COIN_GIT_DAEMON "")
# Allow opting out of using the coin git daemon.
get_cmake_or_env_or_default(skip_coin_git QT_CI_BUILD_QT_SKIP_COIN_GIT_DAEMON FALSE)
# Allow override of the default remote.
get_cmake_or_env_or_default(git_remote QT_CI_BUILD_QT_GIT_REMOTE "")
if(coin_git_ip_port AND NOT skip_coin_git)
set(value "git://${coin_git_ip_port}/qt-project")
elseif(git_remote)
set(value "${git_remote}")
else()
set(value "https://code.qt.io")
endif()
set(${out_var} "${value}" PARENT_SCOPE)
endfunction()
function(get_default_repo_to_sync out_var)
set(${out_var} "qtdeclarative" PARENT_SCOPE)
endfunction()
function(get_default_ref_to_sync out_var)
set(${out_var} "dev" PARENT_SCOPE)
endfunction()
# Decides module to clone to sync to and the ref to sync to.
function(setup_qt_repos_to_clone)
# Allow pinning the sha1 or any other git ref, based on a cmake var or env var.
get_default_repo_to_sync(default_module)
get_default_ref_to_sync(default_ref)
get_cmake_or_env_or_default(SYNC_MODULE QT_CI_BUILD_QT_SYNC_MODULE "${default_module}")
get_cmake_or_env_or_default(SYNC_TO_REF QT_CI_BUILD_QT_PIN_GIT_REF "${default_ref}")
message(STATUS "Will sync to module '${SYNC_MODULE}' at ref '${SYNC_TO_REF}'")
set(SYNC_TO_REF "${SYNC_TO_REF}" PARENT_SCOPE)
set(SYNC_MODULE "${SYNC_MODULE}" PARENT_SCOPE)
endfunction()
# A parser extra cherry-pick or checkout refs to apply to the initialized submodules.
# value - the string to parse.
# out_prefix - the prefix to use for the output variables.
# Sets the following variables:
# <out_prefix>_repos - a list of the repos to apply the changes to.
# <out_prefix>_<repo_name> - the list of refs to apply to the given repo.
function(parse_cherry_pick_entries out_prefix value)
if(value STREQUAL "")
set(${out_prefix}_repos "" PARENT_SCOPE)
return()
endif()
set(result "")
set(repos "")
# Format is as follows.
# "repo sha1,sha2,sha3|repo2 sha1,sha2,sha3"
# E.g.
# set(cherrypicks "qtbase a,b,c|qtdeclarative d,e,f")
string(REPLACE "|" ";" repo_tuples "${value}")
foreach(repo_tuple IN LISTS repo_tuples)
set(result "")
string(REPLACE " " ";" repo_tuple "${repo_tuple}")
list(LENGTH repo_tuple tuple_len)
if(NOT tuple_len EQUAL 2)
message(FATAL_ERROR "Invalid cherry-pick entry: ${repo_tuple}")
endif()
list(GET repo_tuple 0 repo_name)
list(GET repo_tuple 1 refs)
string(REPLACE "," ";" refs "${refs}")
list(APPEND repos "${repo_name}")
foreach(ref IN LISTS refs)
list(APPEND result "${ref}")
endforeach()
set(${out_prefix}_${repo_name} "${result}" PARENT_SCOPE)
endforeach()
set(${out_prefix}_repos "${repos}" PARENT_SCOPE)
endfunction()
# Applies extra gerrit cherry-picks or checks out repos to given ref for the initialized repos.
# This allows applying patches before they are merged in gerrit.
# These can be set for example in a Coin integration or when configuring the test locally.
# The changes are applied in the order they are given, with cherry-picks coming after the check
# outs.
# The format for the values is as follows:
# QT_CI_BUILD_QT_EXTRA_CHERRYPICK_CHANGES='qtbase ref1,ref2|qtdeclarative ref3,ref4'
# Example usage on the command line:
# export QT_CI_BUILD_QT_EXTRA_CHERRYPICK_CHANGES='qtbase 6d2b6b810163610a5b56cef19b196286708acabb'
# export QT_CI_BUILD_QT_EXTRA_CHECKOUT_CHANGES='qtsvg refs/changes/10/624710/5'
# ctest -V -R RunCMake.ConfigureBuildQt
function(apply_extra_gerrit_changes)
get_cmake_or_env_or_default(cherrypicks QT_CI_BUILD_QT_EXTRA_CHERRYPICK_CHANGES "")
get_cmake_or_env_or_default(checkouts QT_CI_BUILD_QT_EXTRA_CHECKOUT_CHANGES "")
set(src_dir_backup "${RunCMake_TEST_COMMAND_WORKING_DIRECTORY}")
parse_cherry_pick_entries(check_outs "${checkouts}")
if(check_outs_repos)
message(STATUS "Applying extra checkout changes for: ${check_outs_repos}")
endif()
foreach(repo_name IN LISTS check_outs_repos)
set(refs "${check_outs_${repo_name}}")
list(LENGTH refs refs_len)
if(refs_len GREATER 1)
message(FATAL_ERROR "More than one checkout ref specified for: ${repo_name}")
endif()
foreach(ref IN LISTS refs)
set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${src_dir_backup}/${repo_name}")
run_command("extra_" git_fetch_checkout_sha1
git fetch "https://codereview.qt-project.org/qt/${repo_name}" "${ref}"
)
run_command("extra_" git_checkout_fetch_head
git checkout FETCH_HEAD
)
endforeach()
endforeach()
parse_cherry_pick_entries(cherry_picks "${cherrypicks}")
if(cherry_picks_repos)
message(STATUS "Applying extra cherry-pick changes for: ${cherry_picks_repos}")
endif()
foreach(repo_name IN LISTS cherry_picks_repos)
set(refs "${cherry_picks_${repo_name}}")
foreach(ref IN LISTS refs)
set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${src_dir_backup}/${repo_name}")
run_command("extra_" git_fetch_cherry_pick_sha1
git fetch "https://codereview.qt-project.org/qt/${repo_name}" "${ref}"
)
run_command("extra_" git_cherry_pick_fetch_head
git cherry-pick FETCH_HEAD
)
endforeach()
endforeach()
endfunction()
# Maeks a copy of the current qt5.git repo of into a temporary directory, and syncs it to the
# given module and ref, initializing any missing submodules along the way,
# as well as applies any extra gerrit changes.
# Also sets the root build dir path for all future Qt builds.
#
# In the CI this instead does a clean clone from the official mirror, instead of a copy, due to
# missing .git info.
#
# Does not rerun the cloning and syncing if the sources are already synced, based on the presence
# of the sources_synced.txt file.
function(setup_qt_sources)
set(opt_args "")
set(single_args "")
set(multi_args "")
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
# Set in Common.cmake
set(local_clone_url "${top_repo_dir_path}")
# Path to working dir based on test build path.
set(working_dir "${RunCMake_BINARY_DIR}")
set(top_level_src_dir "${working_dir}/src")
set(builds_path "${working_dir}/builds")
# Avoid creating many empty build dirs for each run_cmake command, by always reusing the
# same one.
set(RunCMake_TEST_NO_CLEAN TRUE)
set(RunCMake_TEST_NO_CLEAN TRUE PARENT_SCOPE)
set(RunCMake_TEST_BINARY_DIR "${working_dir}")
set(RunCMake_TEST_BINARY_DIR "${working_dir}" PARENT_SCOPE)
# This used by some of the other functions.
set(TOP_LEVEL_SRC_DIR "${top_level_src_dir}" PARENT_SCOPE)
set(BUILDS_PATH "${builds_path}" PARENT_SCOPE)
if(EXISTS "${working_dir}/sources_synced.txt")
# Return early for now, not to retouch the sources, and thus
# cause rebuilds when re-running the test.
message(STATUS "Skipping setting up sources as they are already synced. "
"Remove ${working_dir}/sources_synced.txt to re-sync.")
return()
endif()
# Prepare paths.
file(MAKE_DIRECTORY "${working_dir}")
file(MAKE_DIRECTORY "${builds_path}")
# prefix is empty for now.
set(prefix "")
set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${working_dir}")
set(code_qt_io_mirror "https://code.qt.io/qt/qt5.git")
set(gerrit_mirror "https://codereview.qt-project.org/qt/qt5")
# Make a copy of the qt6 repo on which the test will operate on.
# On the CI we clone it from the official mirror, because we don't have a git repo, but only
# a source archive.
# On a local build we use the current qt6 checkout.
set(ci_ref "$ENV{TESTED_MODULE_REVISION_COIN}")
if(ci_ref)
set(final_clone_url "${code_qt_io_mirror}")
else()
set(final_clone_url "${local_clone_url}")
endif()
setup_verbose_command_output()
# Make a copy of the qt6 repo to operate on it.
if(NOT EXISTS "${top_level_src_dir}")
message(STATUS "Cloning qt6 repo to: ${top_level_src_dir} from ${final_clone_url}")
run_command("${prefix}" prepare_qt6_clone git clone "${final_clone_url}"
"${top_level_src_dir}" --quiet)
else()
message(STATUS "Skipping cloning qt6 repo as it already exists at: ${top_level_src_dir}")
endif()
set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${top_level_src_dir}")
# On the CI we also checkout the exact tested ref, so it behaves as it would with locally.
if(ci_ref)
run_command("${prefix}" fetch_ci_qt6_ref git fetch "${gerrit_mirror}" "${ci_ref}" --quiet)
run_command("${prefix}" checkout_ci_qt6_ref git checkout FETCH_HEAD --quiet)
endif()
# Merge stdout with stderr, to avoid having stderr output from git trigger errors.
set(RunCMake_TEST_OUTPUT_MERGE TRUE)
get_cmake_or_env_or_default(use_local USE_LOCAL_SUBMODULE_SYMLINKS OFF)
if(use_local)
file(GLOB qt_submodules RELATIVE "${top_level_src_dir}" "${top_level_src_dir}/qt*")
foreach(submodule IN LISTS qt_submodules)
message(STATUS
"Making a symlink of submodule ${submodule} "
"pointing to ${top_level_src_dir}/${submodule}"
)
file(REMOVE_RECURSE "${top_level_src_dir}/${submodule}")
file(CREATE_LINK "${top_repo_dir_path}/${submodule}" "${top_level_src_dir}/${submodule}"
SYMBOLIC)
endforeach()
else()
# Adjust its remote url not to be the local url.
get_repo_base_url(repo_base_url)
set(remote_clone_url "${repo_base_url}/qt/qt5.git")
run_command("${prefix}" set_remote_url git remote set-url origin "${remote_clone_url}")
# Sync to given module.
run_command("${prefix}" git_sync_to_${SYNC_MODULE}
${CMAKE_COMMAND}
"-DSYNC_TO_MODULE=${SYNC_MODULE}"
"-DSYNC_TO_BRANCH=${SYNC_TO_REF}"
-DSHOW_PROGRESS=1
-DVERBOSE=1
-DGIT_DEPTH=1
-P cmake/QtSynchronizeRepo.cmake
)
apply_extra_gerrit_changes()
endif()
file(TOUCH "${working_dir}/sources_synced.txt")
endfunction()
# Compute the list of submodules that will be built and skipped based on the module to sync to.
function(setup_list_of_repos_to_build)
# Fake set the CMAKE_CURRENT_SOURCE_DIR so that we can parse the dependencies.
set(CMAKE_CURRENT_SOURCE_DIR "${TOP_LEVEL_SRC_DIR}")
set(sort_module_args "")
set(optional_submodules_skipped "")
get_cmake_or_env_or_default(exclude_optional_deps EXCLUDE_OPTIONAL_DEPS "ON")
if(exclude_optional_deps)
list(APPEND sort_module_args
EXCLUDE_OPTIONAL_DEPS
EXCLUDE_OPTIONAL_DEPS_VAR optional_submodules_skipped
)
endif()
# TODO: hard-coding how to deal with qtdeclarative here for now
# We need qtshadertools to build standalone tests in qtdeclarative because of qtquick
if("qtdeclarative" IN_LIST SYNC_MODULE)
list(PREPEND SYNC_MODULE qtshadertools)
endif()
qt_internal_sort_module_dependencies("${SYNC_MODULE}" initial_submodules ${sort_module_args})
set(final_submodules ${initial_submodules})
get_default_repo_to_sync(default_module)
# Skip any submodules that were explicitly set.
get_cmake_or_env_or_default(skip_submodules QT_CI_BUILD_QT_SKIP_SUBMODULES "")
if(skip_submodules)
# Support semicolons and commas.
string(REPLACE "," ";" skip_submodules "${skip_submodules}")
endif()
list(APPEND skip_submodules ${optional_submodules_skipped})
list(REMOVE_DUPLICATES skip_submodules)
if(skip_submodules)
list(REMOVE_ITEM final_submodules ${skip_submodules})
endif()
message(STATUS "Initial submodules: ${initial_submodules}")
if(skip_submodules)
message(STATUS "Skipping submodules: ${skip_submodules}")
endif()
message(STATUS "Final submodules: ${final_submodules}")
# Will be used by other functions to decide which repos to build.
set(QT_BUILD_SUBMODULES "${final_submodules}" PARENT_SCOPE)
set(QT_SKIP_SUBMODULES "${skip_submodules}" PARENT_SCOPE)
endfunction()
# Copies the top-level qt sources to the given destination, to facilitate clean in-source builds.
function(copy_qt_sources)
set(opt_args "")
set(single_args
DESTINATION_PATH
)
set(multi_args "")
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
if(NOT arg_DESTINATION_PATH)
message(FATAL_ERROR "DESTINATION_PATH is required.")
endif()
if(EXISTS "${arg_DESTINATION_PATH}/CMakeLists.txt")
message(STATUS "Skipping copying sources to ${arg_DESTINATION_PATH} as it already has "
"contents. Remove the root CMakeLists.txt file to re-copy.")
return()
endif()
message(STATUS "Copying sources to ${arg_DESTINATION_PATH}")
file(GLOB files_top_level "${TOP_LEVEL_SRC_DIR}/*")
foreach(top_path IN LISTS files_top_level)
# Skip copying qt5 .git dir.
if(top_path STREQUAL "${TOP_LEVEL_SRC_DIR}/.git")
continue()
endif()
if(IS_DIRECTORY "${top_path}")
get_filename_component(top_dir_name "${top_path}" NAME)
set(child_destination_path "${arg_DESTINATION_PATH}/${top_dir_name}")
file(MAKE_DIRECTORY "${child_destination_path}")
file(GLOB child_paths "${top_path}/*")
foreach(child_path IN LISTS child_paths)
# Skip copying repo .git dir.
if(child_path STREQUAL "${top_path}/.git")
continue()
endif()
file(COPY "${child_path}" DESTINATION "${child_destination_path}")
endforeach()
else()
file(COPY "${top_path}" DESTINATION "${arg_DESTINATION_PATH}")
endif()
endforeach()
endfunction()
# Common helper used in a few functions.
macro(check_and_set_common_qt_configure_options)
if(NOT arg_TEST_NAME)
message(FATAL_ERROR "TEST_NAME is required.")
endif()
set(test_name "${arg_TEST_NAME}")
if(NOT arg_REPO_NAME)
message(FATAL_ERROR "REPO_NAME is required.")
endif()
set(repo_name "${arg_REPO_NAME}")
if(NOT arg_BUILD_DIR_ROOT_PATH)
message(FATAL_ERROR "BUILD_DIR_ROOT_PATH is required.")
endif()
set(build_dir_root "${arg_BUILD_DIR_ROOT_PATH}")
if(NOT arg_REPO_PATH)
message(FATAL_ERROR "REPO_PATH is required.")
endif()
set(repo_path "${arg_REPO_PATH}")
endmacro()
function(get_libexec_dir out_var)
if(CMAKE_HOST_WIN32)
set(value "bin")
else()
set(value "libexec")
endif()
set(${out_var} "${value}" PARENT_SCOPE)
endfunction()
function(get_executable_suffix)
if(CMAKE_HOST_WIN32)
set(value ".bat")
else()
set(value "")
endif()
set(${out_var} "${value}" PARENT_SCOPE)
endfunction()
function(get_win_host_batch_file_workaround_prefix out_var)
set(args "")
# execute_process can't handle commands with arguments that contain spaces in both the
# executable path and the arguments, if the executable is a batch script. Work around it
# by prepending 'cmd /c call', which avoids the issue.
# See https://gitlab.kitware.com/cmake/cmake/-/issues/26655
if(CMAKE_HOST_WIN32)
list(APPEND args "cmd" "/c" "call")
endif()
endfunction()
# Some common cmake args for configuring qt and standalone tests / examples.
function(get_common_cmake_args out_var)
set(use_ccache_option "")
get_cmake_or_env_or_default(use_ccache QT_CI_BUILD_QT_USE_CCACHE "ON")
find_program(CCACHE_EXECUTABLE ccache)
if(CCACHE_EXECUTABLE AND use_ccache)
set(use_ccache_option -DQT_USE_CCACHE=ON)
endif()
set(common_cmake_args
${use_ccache_option}
-DWARNINGS_ARE_ERRORS=OFF
-DQT_GENERATE_SBOM=OFF
# When 6.x.y version bumps are not merged in DAG-dependency order, this avoids
# blocking integrations due to mismatched package versions.
-DQT_NO_PACKAGE_VERSION_INCOMPATIBLE_WARNING=ON
# Avoid using too much disk space.
-DBUILD_WITH_PCH=OFF
--log-level STATUS
)
# Get the common CI args, to set the sccache args, etc.
get_cmake_or_env_or_default(ci_common_cmake_args COMMON_CMAKE_ARGS "")
if(ci_common_cmake_args)
separate_arguments(ci_common_cmake_args NATIVE_COMMAND ${ci_common_cmake_args})
endif()
if(ci_common_cmake_args)
list(APPEND common_cmake_args ${ci_common_cmake_args})
endif()
set(${out_var} "${common_cmake_args}" PARENT_SCOPE)
endfunction()
# Configures a qt repo, either as per-repo or top-level.
# Handles qtbase vs top-level vs other repo case.
# Handles no-prefix builds.
# Configure is not re-ran un repeated test runs, to speed up test runs. Re-configuring can be
# forced by removing the CMakeCache.txt file.
# Passes some additional options like no-pch and no-sbom.
# Uses ccache when available.
function(configure_qt)
set(opt_args
USE_QT_CONFIGURE_MODULE
TOP_LEVEL
NO_PREFIX
)
set(single_args
TEST_NAME
CMAKE_GENERATOR
BUILD_DIR_ROOT_PATH
REPO_NAME
REPO_PATH
INSTALL_PREFIX
TOP_LEVEL_SOURCE_DIR_PATH
)
set(multi_args
CONFIGURE_ARGS
CMAKE_ARGS
)
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
check_and_set_common_qt_configure_options()
if(NOT arg_TOP_LEVEL_SOURCE_DIR_PATH)
message(FATAL_ERROR "TOP_LEVEL_SOURCE_DIR_PATH is required.")
endif()
set(source_dir "${arg_TOP_LEVEL_SOURCE_DIR_PATH}")
if(arg_TOP_LEVEL)
set(build_dir_path "${build_dir_root}")
else()
set(build_dir_path "${build_dir_root}/${repo_name}")
endif()
file(MAKE_DIRECTORY "${build_dir_path}")
message(STATUS "Configuring: ${test_name}")
if(EXISTS "${build_dir_path}/CMakeCache.txt")
message(STATUS "Skipping configuring '${repo_name}' for ${test_name} in "
"${build_dir_path} as it is already configured. Remove the build dir or the "
"CMakeCache.txt file to re-configure.")
return()
endif()
if(arg_NO_PREFIX)
# In a no-prefix build, the prefix is the qtbase build dir.
set(install_prefix "${build_dir_root}/qtbase")
set(install_prefix_option -no-prefix)
elseif(arg_INSTALL_PREFIX)
set(install_prefix "${arg_INSTALL_PREFIX}")
set(install_prefix_option -prefix "${arg_INSTALL_PREFIX}")
endif()
if(repo_name STREQUAL "qtbase" OR arg_TOP_LEVEL)
set(repo_path_option "")
set(use_qt_configure_module FALSE)
else()
set(repo_path_option "${repo_path}")
set(use_qt_configure_module TRUE)
endif()
set(bin_dir "bin")
get_executable_suffix(executable_suffix)
get_win_host_batch_file_workaround_prefix(exec_prefix)
if(arg_TOP_LEVEL)
set(configure_path "${source_dir}/configure${executable_suffix}")
elseif(use_qt_configure_module)
set(configure_path "${install_prefix}/${bin_dir}/qt-configure-module${executable_suffix}")
else()
# qtbase case.
set(configure_path "${source_dir}/${repo_name}/configure${executable_suffix}")
endif()
if(arg_CMAKE_GENERATOR)
set(cmake_generator -G "${arg_CMAKE_GENERATOR}")
else()
set(cmake_generator -G "${RunCMake_GENERATOR}")
endif()
set(initial_configure_args
${install_prefix_option}
-release
)
set(initial_cmake_args "")
set(common_configure_args
${exec_prefix}
"${configure_path}"
${repo_path_option}
${arg_CONFIGURE_ARGS}
)
get_common_cmake_args(common_cmake_args)
list(APPEND common_cmake_args
${cmake_generator}
${arg_CMAKE_ARGS}
)
set(final_configure_args ${common_configure_args})
if(repo_name STREQUAL "qtbase" OR arg_TOP_LEVEL)
list(APPEND final_configure_args ${initial_configure_args})
endif()
set(final_cmake_args ${common_cmake_args})
if(repo_name STREQUAL "qtbase" OR arg_TOP_LEVEL)
list(APPEND final_cmake_args ${initial_cmake_args})
endif()
if(final_cmake_args)
list(APPEND final_configure_args "--" ${final_cmake_args})
endif()
# Merge stdout with stderr, to avoid having stderr output trigger errors because the
# configure mixes writes to both streams.
set(RunCMake_TEST_OUTPUT_MERGE TRUE)
set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${build_dir_path}")
setup_verbose_command_output()
set(prefix "${test_name}_")
run_command("${prefix}" configure ${final_configure_args})
endfunction()
function(get_standalone_part_name out_var)
set(opt_args "")
set(single_args
PART_NAME
PART_VARIANT
)
set(multi_args "")
cmake_parse_arguments(PARSE_ARGV 1 arg "${opt_args}" "${single_args}" "${multi_args}")
if(NOT arg_PART_NAME)
message(FATAL_ERROR "PART_NAME is required.")
endif()
set(supported_parts TESTS EXAMPLES)
if(NOT arg_PART_NAME IN_LIST supported_parts)
message(FATAL_ERROR "PART_NAME must be one of: ${supported_parts}")
endif()
string(TOLOWER "${arg_PART_NAME}" part_name)
string(TOLOWER "${arg_PART_NAME}" part_name)
if(arg_PART_NAME STREQUAL "EXAMPLES")
if(NOT arg_PART_VARIANT)
message(FATAL_ERROR "PART_VARIANT is required for EXAMPLES")
endif()
set(supported_examples_part_variant EXAMPLES_IN_TREE EXAMPLES_AS_EXTERNAL_PROJECTS)
if(NOT arg_PART_VARIANT IN_LIST supported_examples_part_variant)
message(FATAL_ERROR "PART_VARIANT must be one of: ${supported_examples_part_variant}")
endif()
if(arg_PART_VARIANT STREQUAL "EXAMPLES_IN_TREE")
set(part_variant_suffix "_it")
elseif(arg_PART_VARIANT STREQUAL "EXAMPLES_AS_EXTERNAL_PROJECTS")
set(part_variant_suffix "_ep")
endif()
endif()
set(${out_var} "standalone_${part_name}${part_variant_suffix}" PARENT_SCOPE)
endfunction()
# Configures standalone tests or examples for a repo.
function(configure_standalone_part)
set(opt_args
TOP_LEVEL
NO_PREFIX
)
set(single_args
TEST_NAME
CMAKE_GENERATOR
BUILD_DIR_ROOT_PATH
REPO_NAME
REPO_PATH
INSTALL_PREFIX
PART_NAME
PART_VARIANT
OUT_VAR_BUILD_DIR
)
set(multi_args
CMAKE_ARGS
)
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
if(NOT arg_PART_NAME)
message(FATAL_ERROR "PART_NAME is required.")
endif()
set(standalone_part_name_args PART_NAME ${arg_PART_NAME})
if(arg_PART_VARIANT)
list(APPEND standalone_part_name_args PART_VARIANT ${arg_PART_VARIANT})
endif()
get_standalone_part_name(standalone_part_name ${standalone_part_name_args})
string(TOLOWER "${arg_PART_NAME}" part_name)
check_and_set_common_qt_configure_options()
message(STATUS "Configuring standalone parts: ${arg_TEST_NAME}:${repo_name}:${part_name}")
if(arg_TOP_LEVEL)
set(build_dir_path "${build_dir_root}/${standalone_part_name}")
else()
set(build_dir_path
"${build_dir_root}/${standalone_part_name}_${repo_name}")
endif()
file(MAKE_DIRECTORY "${build_dir_path}")
set(${arg_OUT_VAR_BUILD_DIR} "${build_dir_path}" PARENT_SCOPE)
if(EXISTS "${build_dir_path}/CMakeCache.txt")
message(STATUS "Skipping configuring '${repo_name}' standalone ${part_name} for "
"${arg_TEST_NAME} in ${build_dir_path} as it is already configured. Remove the build "
"dir or the CMakeCache.txt file to re-configure.")
return()
endif()
if(arg_NO_PREFIX)
# In a no-prefix build, the prefix is the qtbase build dir.
set(install_prefix "${build_dir_root}/qtbase")
elseif(arg_INSTALL_PREFIX)
set(install_prefix "${arg_INSTALL_PREFIX}")
endif()
get_libexec_dir(libexec_dir)
get_executable_suffix(executable_suffix)
get_win_host_batch_file_workaround_prefix(exec_prefix)
set(libexec_path "${install_prefix}")
set(configure_script
"${libexec_path}/${libexec_dir}/qt-internal-configure-${part_name}${executable_suffix}")
set(common_configure_args
${exec_prefix}
"${configure_script}"
-S "${repo_path}"
-B .
)
get_common_cmake_args(common_cmake_args)
if(arg_CMAKE_GENERATOR)
list(APPEND common_cmake_args -G "${arg_CMAKE_GENERATOR}")
else()
list(APPEND common_cmake_args -G "${RunCMake_GENERATOR}")
endif()
list(APPEND common_cmake_args
${arg_CMAKE_ARGS}
)
if(arg_PART_NAME STREQUAL "EXAMPLES")
if(arg_PART_VARIANT STREQUAL "EXAMPLES_IN_TREE")
set(variant_value "OFF")
else()
set(variant_value "ON")
endif()
list(APPEND common_configure_args -DQT_BUILD_EXAMPLES_AS_EXTERNAL=${variant_value})
endif()
set(final_configure_args "")
list(APPEND final_configure_args ${common_configure_args})
set(final_cmake_args "")
list(APPEND final_cmake_args ${common_cmake_args})
if(final_cmake_args)
list(APPEND final_configure_args ${final_cmake_args})
endif()
# Merge stdout with stderr, to avoid having stderr output trigger errors because the
# configure mixes writes to both streams.
set(RunCMake_TEST_OUTPUT_MERGE TRUE)
set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${build_dir_path}")
setup_verbose_command_output()
set(prefix "${test_name}_")
run_command("${prefix}" "configure_${part_name}" ${final_configure_args})
set(${arg_OUT_VAR_BUILD_DIR} "${build_dir_path}" PARENT_SCOPE)
endfunction()
# Calls cmake in the given directory.
function(call_cmake_in_dir)
set(opt_args "")
set(single_args
TEST_NAME
OP_NAME
BUILD_DIR_PATH
LOG_LEVEL
)
set(multi_args
CMAKE_ARGS
)
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
set(prefix "${arg_TEST_NAME}_")
if(NOT arg_BUILD_DIR_PATH)
message(FATAL_ERROR "BUILD_DIR_PATH is required.")
endif()
set(build_dir_path "${arg_BUILD_DIR_PATH}")
set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${build_dir_path}")
message(STATUS "Calling cmake: ${arg_TEST_NAME}:${build_dir_path}")
setup_verbose_command_output()
if(arg_OP_NAME)
set(op_name "${arg_OP_NAME}")
else()
set(op_name "cmake")
endif()
set(extra_cmake_args ${arg_CMAKE_ARGS})
if(arg_LOG_LEVEL)
list(APPEND extra_cmake_args "--log-level=${arg_LOG_LEVEL}")
endif()
# Merge stdout with stderr, to avoid having stderr output when configure is re-ran as part of
# the build.
set(RunCMake_TEST_OUTPUT_MERGE TRUE)
run_command("${prefix}" ${op_name}
${CMAKE_COMMAND}
.
${extra_cmake_args}
)
endfunction()
function(call_cmake_in_qt_build_dir)
set(opt_args
TOP_LEVEL
)
set(single_args
TEST_NAME
BUILD_DIR_ROOT_PATH
REPO_NAME
REPO_PATH
LOG_LEVEL
STANDALONE_PART_PREFIX
)
set(multi_args
CMAKE_ARGS
)
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
check_and_set_common_qt_configure_options()
set(repo_prefix "")
if(arg_STANDALONE_PART)
set(repo_prefix "${arg_STANDALONE_PART_PREFIX}_")
endif()
if(arg_TOP_LEVEL)
set(build_dir_path "${build_dir_root}")
else()
set(build_dir_path "${build_dir_root}/${repo_prefix}${repo_name}")
endif()
set(op_name "cmake_in_qt")
set(extra_cmake_args "")
if(arg_CMAKE_ARGS)
list(APPEND extra_cmake_args ${arg_CMAKE_ARGS})
endif()
if(arg_LOG_LEVEL)
list(APPEND extra_cmake_args LOG_LEVEL "${arg_LOG_LEVEL}")
endif()
if(extra_cmake_args)
set(extra_cmake_args CMAKE_ARGS ${extra_cmake_args})
endif()
call_cmake_in_dir(
TEST_NAME "${test_name}"
BUILD_DIR_PATH "${build_dir_path}"
OP_NAME "${op_name}"
${extra_cmake_args}
)
endfunction()
# Calls cmake --build in the given directory.
function(build_in_dir)
set(opt_args
BUILD_VERBOSE
BUILD_NINJA_EXPLAIN
)
set(single_args
TEST_NAME
OP_NAME
BUILD_DIR_PATH
)
set(multi_args "")
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
set(prefix "${arg_TEST_NAME}_")
if(NOT arg_BUILD_DIR_PATH)
message(FATAL_ERROR "BUILD_DIR_PATH is required.")
endif()
set(build_dir_path "${arg_BUILD_DIR_PATH}")
set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${build_dir_path}")
message(STATUS "Building: ${arg_TEST_NAME}:${build_dir_path}")
setup_verbose_command_output()
if(arg_OP_NAME)
set(op_name "${arg_OP_NAME}")
else()
set(op_name "build")
endif()
# Merge stdout with stderr, to avoid having stderr output when configure is re-ran as part of
# the build.
set(RunCMake_TEST_OUTPUT_MERGE TRUE)
set(build_args
${CMAKE_COMMAND}
--build .
--parallel
)
if(arg_BUILD_VERBOSE)
list(APPEND build_args --verbose)
endif()
set(build_tool_args "")
if(arg_BUILD_NINJA_EXPLAIN)
list(APPEND build_tool_args -d explain)
endif()
if(build_tool_args)
list(APPEND build_args -- ${build_tool_args})
endif()
run_command("${prefix}" ${op_name} ${build_args})
endfunction()
# Builds a qt repo.
function(build_qt)
set(opt_args
TOP_LEVEL
IS_REBUILD
BUILD_VERBOSE
BUILD_NINJA_EXPLAIN
)
set(single_args
TEST_NAME
REPO_NAME
REPO_PATH
BUILD_DIR_ROOT_PATH
)
set(multi_args "")
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
check_and_set_common_qt_configure_options()
if(arg_TOP_LEVEL)
set(build_dir_path "${build_dir_root}")
else()
set(build_dir_path "${build_dir_root}/${repo_name}")
endif()
if(arg_IS_REBUILD)
set(op_name "rebuild_qt")
else()
set(op_name "build_qt")
endif()
set(extra_build_args "")
if(arg_BUILD_VERBOSE)
list(APPEND extra_build_args BUILD_VERBOSE)
endif()
if(arg_BUILD_NINJA_EXPLAIN)
list(APPEND extra_build_args BUILD_NINJA_EXPLAIN)
endif()
build_in_dir(
TEST_NAME "${test_name}"
BUILD_DIR_PATH "${build_dir_path}"
OP_NAME "${op_name}"
${extra_build_args}
)
endfunction()
# Installs a qt repo.
# Is a no-op for no-prefix builds.
function(install_qt)
set(opt_args
TOP_LEVEL
NO_PREFIX
)
set(single_args
TEST_NAME
REPO_NAME
REPO_PATH
BUILD_DIR_ROOT_PATH
INSTALL_PREFIX
)
set(multi_args "")
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
message(STATUS "Installing: ${arg_TEST_NAME}")
if(arg_NO_PREFIX)
# Installation is a no-op for no-prefix builds.
message(STATUS "Skipping installation for ${arg_TEST_NAME} because it's a no-prefix build.")
return()
endif()
check_and_set_common_qt_configure_options()
set(prefix "${test_name}_")
if(arg_TOP_LEVEL)
set(build_dir_path "${build_dir_root}")
else()
set(build_dir_path "${build_dir_root}/${repo_name}")
endif()
get_libexec_dir(libexec_dir)
if(arg_TOP_LEVEL)
set(libexec_path "${build_dir_path}/qtbase")
elseif(repo_name STREQUAL "qtbase")
set(libexec_path "${build_dir_path}")
else()
set(libexec_path "${arg_INSTALL_PREFIX}")
endif()
set(install_script "${libexec_path}/${libexec_dir}/qt-cmake-private-install.cmake")
# Merge stdout with stderr, to avoid having stderr output when runinng install_name_tool on
# test reruns on macOS.
set(RunCMake_TEST_OUTPUT_MERGE TRUE)
set(RunCMake_TEST_COMMAND_WORKING_DIRECTORY "${build_dir_path}")
setup_verbose_command_output()
run_command("${prefix}" install
${CMAKE_COMMAND}
"-DQT_BUILD_DIR=${build_dir_path}"
-P "${install_script}"
)
endfunction()
# A helper to configure build and install a list of qt repos (or top-level).
# Also configures and builds standalone tests and examples, or in-tree tests and examples.
# Handles no-prefix builds, top-level builds, in-source builds.
function(qt_build_helper)
set(opt_args
NO_PREFIX
IN_SOURCE
TOP_LEVEL
BUILD_STANDALONE_TESTS
BUILD_STANDALONE_EXAMPLES_IN_TREE
BUILD_STANDALONE_EXAMPLES_AS_EXTERNAL_PROJECTS
BUILD_IN_TREE_TESTS
BUILD_IN_TREE_EXAMPLES
SKIP_STANDALONE_PARTS
RECONFIGURE_WITHOUT_ARGS_AFTER_BUILD
RECONFIGURE_WITHOUT_ARGS_IMMEDIATELY
BUILD_AFTER_RECONFIGURE
RECONFIGURE_STANDALONE_PARTS
)
set(single_args
TEST_NAME
BUILD_DIR_NAME
)
set(multi_args "")
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
if(NOT arg_TEST_NAME)
message(FATAL_ERROR "TEST_NAME is required.")
endif()
if(NOT arg_BUILD_DIR_NAME)
set(arg_BUILD_DIR_NAME "${arg_TEST_NAME}")
endif()
message(STATUS "Running test: ${arg_TEST_NAME}")
set(repos ${QT_BUILD_SUBMODULES})
set(repos_to_skip ${QT_SKIP_SUBMODULES})
set(build_dir_root "${BUILDS_PATH}/${arg_BUILD_DIR_NAME}")
set(install_prefix "${BUILDS_PATH}/${arg_BUILD_DIR_NAME}/installed")
if(arg_IN_SOURCE)
set(source_dir "${build_dir_root}")
copy_qt_sources(
DESTINATION_PATH "${source_dir}"
)
else()
set(source_dir "${TOP_LEVEL_SRC_DIR}")
endif()
set(extra_args "")
if(arg_NO_PREFIX)
list(APPEND extra_args
NO_PREFIX
)
else()
list(APPEND extra_args
INSTALL_PREFIX "${install_prefix}"
)
endif()
set(extra_cmake_args "")
if(arg_TOP_LEVEL)
set(repos_to_process "qt6")
# Explicit skip configuring skipped repos for top-level builds.
foreach(repo_to_skip IN LISTS repos_to_skip)
list(APPEND extra_cmake_args "-DBUILD_${repo_to_skip}=OFF")
endforeach()
list(APPEND extra_args TOP_LEVEL)
else()
set(repos_to_process ${repos})
endif()
foreach(repo_name IN LISTS repos_to_process)
if(NOT arg_TOP_LEVEL)
# For per-repo builds, we just skip calling configure.
if(repo_name IN_LIST repos_to_skip)
message(STATUS "Skipping building ${repo_name}")
continue()
endif()
endif()
if(arg_TOP_LEVEL)
set(repo_path "${source_dir}")
set(test_name "${arg_TEST_NAME}")
else()
set(repo_path "${source_dir}/${repo_name}")
set(test_name "${arg_TEST_NAME}_${repo_name}")
endif()
set(common_args
TEST_NAME "${test_name}"
REPO_NAME "${repo_name}"
REPO_PATH "${repo_path}"
TOP_LEVEL_SOURCE_DIR_PATH "${source_dir}"
BUILD_DIR_ROOT_PATH "${build_dir_root}"
${extra_args}
)
set(common_cmake_args ${extra_cmake_args})
set(build_in_tree_tests OFF)
if(arg_BUILD_IN_TREE_TESTS)
set(build_in_tree_tests ON)
endif()
set(build_in_tree_examples OFF)
if(arg_BUILD_IN_TREE_EXAMPLES)
set(build_in_tree_examples ON)
list(APPEND common_cmake_args -DQT_BUILD_EXAMPLES_AS_EXTERNAL=OFF)
endif()
list(APPEND common_cmake_args
-DQT_BUILD_TESTS=${build_in_tree_tests}
-DQT_BUILD_EXAMPLES=${build_in_tree_examples}
)
set(common_args_with_cmake_args ${common_args})
if(common_cmake_args)
list(APPEND common_args_with_cmake_args
CMAKE_ARGS ${common_cmake_args}
)
endif()
configure_qt(${common_args_with_cmake_args})
if(arg_RECONFIGURE_WITHOUT_ARGS_IMMEDIATELY)
call_cmake_in_qt_build_dir(
${common_args}
LOG_LEVEL STATUS
)
endif()
build_qt(${common_args})
if(arg_RECONFIGURE_WITHOUT_ARGS_AFTER_BUILD)
call_cmake_in_qt_build_dir(
${common_args}
LOG_LEVEL STATUS
)
endif()
if(arg_BUILD_AFTER_RECONFIGURE)
build_qt(
IS_REBUILD
BUILD_VERBOSE
BUILD_NINJA_EXPLAIN
${common_args}
)
endif()
install_qt(${common_args_with_cmake_args})
if(arg_BUILD_STANDALONE_TESTS AND NOT arg_SKIP_STANDALONE_PARTS)
configure_standalone_part(
${common_args_with_cmake_args}
PART_NAME "TESTS"
OUT_VAR_BUILD_DIR tests_build_dir
)
build_in_dir(
TEST_NAME "${test_name}"
BUILD_DIR_PATH "${tests_build_dir}"
OP_NAME "build_tests"
)
if(arg_RECONFIGURE_STANDALONE_PARTS)
get_standalone_part_name(standalone_part_name
PART_NAME "TESTS"
)
call_cmake_in_qt_build_dir(
${common_args}
LOG_LEVEL STATUS
STANDALONE_PART_PREFIX "${standalone_part_name}"
)
endif()
endif()
if(arg_BUILD_STANDALONE_EXAMPLES_IN_TREE AND NOT arg_SKIP_STANDALONE_PARTS)
configure_standalone_part(
${common_args_with_cmake_args}
PART_NAME "EXAMPLES"
PART_VARIANT "EXAMPLES_IN_TREE"
OUT_VAR_BUILD_DIR examples_build_dir
)
build_in_dir(
TEST_NAME "${test_name}"
BUILD_DIR_PATH "${examples_build_dir}"
OP_NAME "build_examples"
)
if(arg_RECONFIGURE_STANDALONE_PARTS)
get_standalone_part_name(standalone_part_name
PART_NAME "EXAMPLES"
PART_VARIANT "EXAMPLES_IN_TREE"
)
call_cmake_in_qt_build_dir(
${common_args}
LOG_LEVEL STATUS
STANDALONE_PART_PREFIX "${standalone_part_name}"
)
endif()
endif()
if(arg_BUILD_STANDALONE_EXAMPLES_AS_EXTERNAL_PROJECTS AND NOT arg_SKIP_STANDALONE_PARTS)
configure_standalone_part(
${common_args_with_cmake_args}
PART_NAME "EXAMPLES"
PART_VARIANT "EXAMPLES_AS_EXTERNAL_PROJECTS"
OUT_VAR_BUILD_DIR examples_build_dir
)
build_in_dir(
TEST_NAME "${test_name}"
BUILD_DIR_PATH "${examples_build_dir}"
OP_NAME "build_examples"
)
if(arg_RECONFIGURE_STANDALONE_PARTS)
get_standalone_part_name(standalone_part_name
PART_NAME "EXAMPLES"
PART_VARIANT "EXAMPLES_AS_EXTERNAL_PROJECTS"
)
call_cmake_in_qt_build_dir(
${common_args}
LOG_LEVEL STATUS
STANDALONE_PART_PREFIX "${standalone_part_name}"
)
endif()
endif()
endforeach()
# Do another round of reconfigure (and in certain cases rebuild) after all submodules were built
# This will include any plugin navigations in the find_package calls
if(arg_RECONFIGURE_WITHOUT_ARGS_IMMEDIATELY
OR arg_RECONFIGURE_WITHOUT_ARGS_AFTER_BUILD)
foreach(repo_name IN LISTS repos_to_process)
set(repo_path "${source_dir}/${repo_name}")
set(test_name "${arg_TEST_NAME}_${repo_name}")
set(common_args
TEST_NAME "${test_name}"
REPO_NAME "${repo_name}"
REPO_PATH "${repo_path}"
TOP_LEVEL_SOURCE_DIR_PATH "${source_dir}"
BUILD_DIR_ROOT_PATH "${build_dir_root}"
${extra_args}
)
call_cmake_in_qt_build_dir(
${common_args}
LOG_LEVEL STATUS
)
if(arg_BUILD_AFTER_RECONFIGURE)
build_qt(
IS_REBUILD
BUILD_VERBOSE
BUILD_NINJA_EXPLAIN
${common_args}
)
endif()
if(arg_RECONFIGURE_STANDALONE_PARTS AND NOT arg_SKIP_STANDALONE_PARTS)
if(arg_BUILD_STANDALONE_TESTS)
get_standalone_part_name(standalone_part_name
PART_NAME "TESTS"
)
call_cmake_in_qt_build_dir(
${common_args}
LOG_LEVEL STATUS
STANDALONE_PART_PREFIX "${standalone_part_name}"
)
endif()
if(arg_BUILD_STANDALONE_EXAMPLES_IN_TREE)
get_standalone_part_name(standalone_part_name
PART_NAME "EXAMPLES"
PART_VARIANT "EXAMPLES_IN_TREE"
)
call_cmake_in_qt_build_dir(
${common_args}
LOG_LEVEL STATUS
STANDALONE_PART_PREFIX "${standalone_part_name}"
)
endif()
if(arg_BUILD_STANDALONE_EXAMPLES_AS_EXTERNAL_PROJECTS)
get_standalone_part_name(standalone_part_name
PART_NAME "EXAMPLES"
PART_VARIANT "EXAMPLES_AS_EXTERNAL_PROJECTS"
)
call_cmake_in_qt_build_dir(
${common_args}
LOG_LEVEL STATUS
STANDALONE_PART_PREFIX "${standalone_part_name}"
)
endif()
endif()
endforeach()
endif()
endfunction()
function(add_test_case)
set(opt_args "")
set(single_args
TEST_NAME
)
set(multi_args
TEST_GROUPS
TEST_ARGS
)
cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}")
if(NOT arg_TEST_NAME)
message(FATAL_ERROR "TEST_NAME is required.")
endif()
set(test_cases "${TEST_CASES}")
set(test_groups "${TEST_GROUPS}")
list(APPEND test_cases "${arg_TEST_NAME}")
if(arg_TEST_GROUPS)
list(APPEND test_groups "${arg_TEST_GROUPS}")
list(REMOVE_DUPLICATES test_groups)
foreach(group IN LISTS arg_TEST_GROUPS)
set(group_cases ${TEST_GROUPS_${group}})
list(APPEND group_cases "${arg_TEST_NAME}")
set(TEST_GROUPS_${group} "${group_cases}")
set(TEST_GROUPS_${group} "${group_cases}" PARENT_SCOPE)
endforeach()
endif()
set(TESTS_${arg_TEST_NAME}_ARGS ${arg_TEST_ARGS} PARENT_SCOPE)
set(TEST_CASES "${test_cases}" PARENT_SCOPE)
set(TEST_GROUPS "${test_groups}" PARENT_SCOPE)
endfunction()
macro(setup_qt_sources_and_repos_to_build)
setup_qt_repos_to_clone()
setup_qt_sources()
setup_list_of_repos_to_build()
endmacro()
# Applies a filter to the collected test cases from the QT_CI_BUILD_QT_TESTS cmake or env var.
# The filter is a comma separated list of one of the following:
# 'all' to include all tests
# 'test case name' to include a specific test
# 'group name' to include all tests in a specific group
# '-all' to exclude all tests
# '-test case name' to exclude a specific test
# '-group name' to exclude all tests in a specific group
# '-standalone_parts' to exclude building standalone tests and examples for all tests
# The values are processed in the order they appear.
# Default is all.
macro(apply_filter_to_collected_tests)
get_cmake_or_env_or_default(cases_filter QT_CI_BUILD_QT_FILTER "all")
# Allow both semicolons and commas.
string(REPLACE "," ";" cases_filter "${cases_filter}")
message(STATUS "Applying filter: ${cases_filter}")
# Validate filters.
set(special_filters
all
standalone_parts
)
foreach(filter IN LISTS cases_filter)
set(clean_filter "${filter}")
if(filter MATCHES "^-")
string(SUBSTRING "${filter}" 1 -1 clean_filter)
endif()
if(NOT clean_filter IN_LIST TEST_CASES
AND NOT clean_filter IN_LIST TEST_GROUPS
AND NOT clean_filter IN_LIST special_filters)
message(FATAL_ERROR "Invalid filter: ${filter}")
endif()
endforeach()
set(TEST_CASES_FINAL "")
# Allow configuring which test cases to run.
foreach(test_name IN LISTS TEST_CASES)
set(include_test_case FALSE)
foreach(filter IN LISTS cases_filter)
set(clean_group_name "${filter}")
if(filter MATCHES "^-")
string(SUBSTRING "${filter}" 1 -1 clean_group_name)
endif()
if(
# Check if exact test case is included
test_name STREQUAL filter
# Check if all test cases should be run
OR filter STREQUAL "all"
# Check if test case is in a specific filter group
OR test_name IN_LIST TEST_GROUPS_${filter}
)
set(include_test_case TRUE)
endif()
if(
# Check if test case is explicitly excluded
"-${test_name}" STREQUAL "${filter}"
# Check if all test cases should be excluded
OR filter STREQUAL "-all"
# Check if specific group is excluded
OR (filter MATCHES "^-"
AND test_name IN_LIST TEST_GROUPS_${clean_group_name})
)
set(include_test_case FALSE)
endif()
endforeach()
if(include_test_case)
list(APPEND TEST_CASES_FINAL "${test_name}")
endif()
endforeach()
if("-standalone_parts" IN_LIST cases_filter)
set(SKIP_STANDALONE_PARTS SKIP_STANDALONE_PARTS)
endif()
endmacro()
# Runs the full suite of tests for building Qt.
function(run_tests)
setup_qt_sources_and_repos_to_build()
collect_tests()
apply_filter_to_collected_tests()
set(no_cases "")
if(NOT TEST_CASES_FINAL)
set(no_cases "No test cases to run.")
endif()
message(STATUS "Running test cases: ${TEST_CASES_FINAL}${no_cases}")
foreach(test_name IN LISTS TEST_CASES_FINAL)
qt_build_helper(
TEST_NAME "${test_name}"
${TESTS_${test_name}_ARGS}
${SKIP_STANDALONE_PARTS}
)
endforeach()
endfunction()