diff options
Diffstat (limited to 'src/corelib/Qt6CTestMacros.cmake')
-rw-r--r-- | src/corelib/Qt6CTestMacros.cmake | 763 |
1 files changed, 645 insertions, 118 deletions
diff --git a/src/corelib/Qt6CTestMacros.cmake b/src/corelib/Qt6CTestMacros.cmake index 962d49d6b2..8722553cd0 100644 --- a/src/corelib/Qt6CTestMacros.cmake +++ b/src/corelib/Qt6CTestMacros.cmake @@ -1,3 +1,6 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # # W A R N I N G # ------------- @@ -8,97 +11,616 @@ # # We mean it. -message("CMAKE_VERSION: ${CMAKE_VERSION}") -message("CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}") -message("CMAKE_MODULES_UNDER_TEST: ${CMAKE_MODULES_UNDER_TEST}") -foreach(_mod ${CMAKE_MODULES_UNDER_TEST}) - message("CMAKE_${_mod}_MODULE_MAJOR_VERSION: ${CMAKE_${_mod}_MODULE_MAJOR_VERSION}") - message("CMAKE_${_mod}_MODULE_MINOR_VERSION: ${CMAKE_${_mod}_MODULE_MINOR_VERSION}") - message("CMAKE_${_mod}_MODULE_PATCH_VERSION: ${CMAKE_${_mod}_MODULE_PATCH_VERSION}") -endforeach() - -set(BUILD_OPTIONS_LIST) - -if (CMAKE_C_COMPILER) - list(APPEND BUILD_OPTIONS_LIST "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}") -endif() - -if (CMAKE_CXX_COMPILER) - list(APPEND BUILD_OPTIONS_LIST "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") -endif() - -if (CMAKE_BUILD_TYPE) - list(APPEND BUILD_OPTIONS_LIST "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") -endif() - -if (CMAKE_TOOLCHAIN_FILE) - list(APPEND BUILD_OPTIONS_LIST "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}") -endif() - -if (CMAKE_VERBOSE_MAKEFILE) - list(APPEND BUILD_OPTIONS_LIST "-DCMAKE_VERBOSE_MAKEFILE=1") -endif() - -if (NO_GUI) - list(APPEND BUILD_OPTIONS_LIST "-DNO_GUI=True") -endif() -if (NO_WIDGETS) - list(APPEND BUILD_OPTIONS_LIST "-DNO_WIDGETS=True") -endif() -if (NO_DBUS) - list(APPEND BUILD_OPTIONS_LIST "-DNO_DBUS=True") -endif() - -# Qt requires C++11 features in header files, which means -# the buildsystem needs to add a -std flag for certain compilers -# CMake adds the flag automatically in most cases, but notably not -# on Windows prior to CMake 3.3 -if (CMAKE_VERSION VERSION_LESS 3.3) - if (CMAKE_CXX_COMPILER_ID STREQUAL AppleClang - OR (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)) - list(APPEND BUILD_OPTIONS_LIST "-DCMAKE_CXX_FLAGS=-std=gnu++0x -stdlib=libc++") - elseif (CMAKE_CXX_COMPILER_ID STREQUAL GNU - OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) - list(APPEND BUILD_OPTIONS_LIST "-DCMAKE_CXX_FLAGS=-std=gnu++0x") - endif() -endif() - -foreach(module ${CMAKE_MODULES_UNDER_TEST}) - list(APPEND BUILD_OPTIONS_LIST - "-DCMAKE_${module}_MODULE_MAJOR_VERSION=${CMAKE_${module}_MODULE_MAJOR_VERSION}" - "-DCMAKE_${module}_MODULE_MINOR_VERSION=${CMAKE_${module}_MODULE_MINOR_VERSION}" - "-DCMAKE_${module}_MODULE_PATCH_VERSION=${CMAKE_${module}_MODULE_PATCH_VERSION}" - ) -endforeach() - -macro(expect_pass _dir) - cmake_parse_arguments(_ARGS "" "BINARY" "" ${ARGN}) - string(REPLACE "(" "_" testname "${_dir}") - string(REPLACE ")" "_" testname "${testname}") - add_test(${testname} ${CMAKE_CTEST_COMMAND} - --build-and-test - "${CMAKE_CURRENT_SOURCE_DIR}/${_dir}" - "${CMAKE_CURRENT_BINARY_DIR}/${_dir}" - --build-config "${CMAKE_BUILD_TYPE}" - --build-generator ${CMAKE_GENERATOR} - --build-makeprogram ${CMAKE_MAKE_PROGRAM} - --build-project ${_dir} - --build-options "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" ${BUILD_OPTIONS_LIST} - --test-command ${_ARGS_BINARY} - ) +message(STATUS "CMAKE_VERSION: ${CMAKE_VERSION}") +message(STATUS "CMAKE_PREFIX_PATH: ${CMAKE_PREFIX_PATH}") +message(STATUS "CMAKE_MODULES_UNDER_TEST: ${CMAKE_MODULES_UNDER_TEST}") + +# Generate a shell script wrapper that calls ninja with -v parameter. +# Upstream issue to allow specifying custom build tool options when using ctest's --build-and-test +# https://gitlab.kitware.com/cmake/cmake/-/issues/22443. +# Only one file is created that is used by all tests. +function(_qt_internal_get_ninja_wrapper ninja_path out_wrapper_path) + if(QT_INTERNAL_CTEST_NINJA_WRAPPER AND EXISTS "${QT_INTERNAL_CTEST_NINJA_WRAPPER}") + set(${out_wrapper_path} "${QT_INTERNAL_CTEST_NINJA_WRAPPER}" PARENT_SCOPE) + return() + endif() + + if(NOT ninja_path) + message(FATAL_ERROR "Invalid ninja path specified: '${ninja_path}'.") + endif() + + set(wrapper_extension "") + + if(NOT CMAKE_HOST_UNIX) + set(wrapper_extension ".bat") + endif() + + set(script_name "qt-internal-ninja") + + # the libexec literal is used on purpose for the source, so the file is found + # on Windows hosts. + set(wrapper_rel_path "libexec/${script_name}${wrapper_extension}.in") + + # Need to find the libexec input file depending whether the qtbase sources are available. + # This mirrors the logic in qt_set_up_build_internals_paths. + # TODO: Clean this up, together with qt_set_up_build_internals_paths to only use the + # the qtbase sources when building qtbase. And perhaps also when doing a non-prefix + # developer-build. + set(qtbase_wrapper_in_path "${QT_SOURCE_TREE}/${wrapper_rel_path}") + set(installed_wrapper_in_path + "${_qt_cmake_dir}/${QT_CMAKE_EXPORT_NAMESPACE}/${wrapper_rel_path}") + + # qtbase sources available, always use them, regardless of prefix or non-prefix builds. + if(EXISTS "${qtbase_wrapper_in_path}") + set(wrapper_in "${qtbase_wrapper_in_path}") + + # qtbase sources unavailable, use installed files. + elseif(EXISTS "${installed_wrapper_in_path}") + set(wrapper_in "${installed_wrapper_in_path}") + else() + message(FATAL_ERROR "Can't find ${script_name}${wrapper_extension}.in file.") + endif() + + set(wrapper_out "${CMAKE_BINARY_DIR}/.qt/${script_name}${wrapper_extension}") + + set(original_ninja "${ninja_path}") + set(ninja_arguments "-v") + + configure_file("${wrapper_in}" "${wrapper_out}" @ONLY) + + set(QT_INTERNAL_CTEST_NINJA_WRAPPER "${wrapper_out}" CACHE STRING + "Internal Qt ninja wrapper for ctest tests") + + set(${out_wrapper_path} "${QT_INTERNAL_CTEST_NINJA_WRAPPER}" PARENT_SCOPE) +endfunction() + +# The function collects configuring options for the test projects generated by Qt cmake tests. +# Arguments: +# OUT_PREFIX_PATH <variable name>: stores the CMAKE_PREFIX_PATH value in the output variable. +function(_qt_internal_get_cmake_test_configure_options out_var) + cmake_parse_arguments(arg "" "OUT_PREFIX_PATH" "" ${ARGN}) + set(option_list) + + if (CMAKE_C_COMPILER AND NOT CMAKE_CROSSCOMPILING) + list(APPEND option_list "-DCMAKE_C_COMPILER=${CMAKE_C_COMPILER}") + endif() + + if (CMAKE_CXX_COMPILER AND NOT CMAKE_CROSSCOMPILING) + list(APPEND option_list "-DCMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}") + endif() + + get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) + if(is_multi_config) + if(CMAKE_CONFIGURATION_TYPES) + string(REPLACE ";" "\;" configuration_types "${CMAKE_CONFIGURATION_TYPES}") + list(APPEND option_list "-DCMAKE_CONFIGURATION_TYPES=${configuration_types}") + endif() + else() + if(CMAKE_BUILD_TYPE) + list(APPEND option_list "-DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}") + endif() + endif() + + if (CMAKE_TOOLCHAIN_FILE) + file(TO_CMAKE_PATH "${CMAKE_TOOLCHAIN_FILE}" _CMAKE_TOOLCHAIN_FILE) + list(APPEND option_list "-DCMAKE_TOOLCHAIN_FILE=${_CMAKE_TOOLCHAIN_FILE}") + endif() + + if (CMAKE_VERBOSE_MAKEFILE) + list(APPEND option_list "-DCMAKE_VERBOSE_MAKEFILE=1") + endif() + + if (NO_GUI) + list(APPEND option_list "-DNO_GUI=True") + endif() + if (NO_WIDGETS) + list(APPEND option_list "-DNO_WIDGETS=True") + endif() + if (NO_DBUS) + list(APPEND option_list "-DNO_DBUS=True") + endif() + + list(APPEND option_list "-DCMAKE_MESSAGE_LOG_LEVEL=DEBUG") + list(APPEND option_list "-DCMAKE_AUTOGEN_VERBOSE=TRUE") + + if(APPLE AND CMAKE_OSX_ARCHITECTURES) + list(LENGTH CMAKE_OSX_ARCHITECTURES osx_arch_count) + + # When Qt is built as universal config (macOS or iOS), force CMake build tests to build one + # architecture instead of all of them, because the build machine that builds the cmake tests + # might not have a universal SDK installed. + if(osx_arch_count GREATER 1) + list(APPEND option_list "-DQT_FORCE_SINGLE_QT_OSX_ARCHITECTURE=ON") + endif() + endif() + + foreach(module ${CMAKE_MODULES_UNDER_TEST}) + list(APPEND option_list + "-DCMAKE_${module}_MODULE_MAJOR_VERSION=${CMAKE_${module}_MODULE_MAJOR_VERSION}" + "-DCMAKE_${module}_MODULE_MINOR_VERSION=${CMAKE_${module}_MODULE_MINOR_VERSION}" + "-DCMAKE_${module}_MODULE_PATCH_VERSION=${CMAKE_${module}_MODULE_PATCH_VERSION}" + ) + endforeach() + + _qt_internal_get_build_vars_for_external_projects( + PREFIXES_VAR prefixes + ADDITIONAL_PACKAGES_PREFIXES_VAR additional_prefixes + ) + + if(arg_OUT_PREFIX_PATH) + set(${arg_OUT_PREFIX_PATH} "${prefixes}" PARENT_SCOPE) + endif() + + string(REPLACE ";" "\;" prefixes "${prefixes}") + list(APPEND option_list "-DCMAKE_PREFIX_PATH=${prefixes}") + list(APPEND option_list "-DQT_ADDITIONAL_PACKAGES_PREFIX_PATH=${additional_prefixes}") + + set(${out_var} "${option_list}" PARENT_SCOPE) +endfunction() + +function(_qt_internal_set_up_test_run_environment testname) + set(no_value_options NO_PLUGIN_PATH) + set(single_value_options "") + set(multi_value_options "") + cmake_parse_arguments(PARSE_ARGV 1 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + + # This is copy-pasted from qt_add_test and adapted to the standalone project case. + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + set(QT_PATH_SEPARATOR "\\;") + else() + set(QT_PATH_SEPARATOR ":") + endif() + + if(NOT INSTALL_BINDIR) + set(INSTALL_BINDIR bin) + endif() + + if(NOT INSTALL_PLUGINSDIR) + set(INSTALL_PLUGINSDIR "plugins") + endif() + + set(install_prefixes "") + if(NOT CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(install_prefixes "${CMAKE_INSTALL_PREFIX}") + endif() + + # If part of Qt build or standalone tests, use the build internals install prefix. + # If the tests are configured as a separate project, use the Qt6 package provided install + # prefix. + if(QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX) + list(APPEND install_prefixes "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}") + else() + list(APPEND install_prefixes "${QT6_INSTALL_PREFIX}") + endif() + + set(test_env_path "PATH=${CMAKE_CURRENT_BINARY_DIR}") + foreach(install_prefix ${install_prefixes}) + set(test_env_path "${test_env_path}${QT_PATH_SEPARATOR}${install_prefix}/${INSTALL_BINDIR}") + endforeach() + set(test_env_path "${test_env_path}${QT_PATH_SEPARATOR}$ENV{PATH}") + string(REPLACE ";" "\;" test_env_path "${test_env_path}") + set_property(TEST "${testname}" APPEND PROPERTY ENVIRONMENT "${test_env_path}") + set_property(TEST "${testname}" APPEND PROPERTY ENVIRONMENT "QT_TEST_RUNNING_IN_CTEST=1") + + if(NOT arg_NO_PLUGIN_PATH) + # Add the install prefix to list of plugin paths when doing a prefix build + if(NOT QT_INSTALL_DIR) + foreach(install_prefix ${install_prefixes}) + list(APPEND plugin_paths "${install_prefix}/${INSTALL_PLUGINSDIR}") + endforeach() + endif() + + # TODO: Collect all paths from known repositories when performing a super build. + list(APPEND plugin_paths "${PROJECT_BINARY_DIR}/${INSTALL_PLUGINSDIR}") + list(JOIN plugin_paths "${QT_PATH_SEPARATOR}" plugin_paths_joined) + set_property(TEST "${testname}" + APPEND PROPERTY ENVIRONMENT "QT_PLUGIN_PATH=${plugin_paths_joined}") + endif() +endfunction() + +# Checks if the test project can be built successfully. Arguments: +# +# NO_CLEAN_STEP: Skips calling 'clean' target before building. +# +# NO_BUILD_PROJECT_ARG: Skips adding --build-project argument. Useful when using Xcode generator. +# +# GENERATOR: Use a custom generator. When not specified, uses existing CMAKE_GENERATOR value. +# +# NO_IOS_DEFAULT_ARGS: Skips setting default iOS-specific options like the generator to be used. +# +# MAKE_PROGRAM: Specify a different make program. Can be useful with a custom make or ninja wrapper. +# +# BUILD_TYPE: Specify a different CMake build type. Defaults to CMAKE_BUILD_TYPE if it is not empty. +# Which means no build type is passed if the top-level project is configured with a +# multi-config generator. +# +# SIMULATE_IN_SOURCE: If the option is specified, the function copies sources of the tests to the +# CMAKE_CURRENT_BINARY_DIR directory, creates internal build directory in the +# copied sources and uses this directory to build and test the project. +# This makes possible to have relative paths to the source files in the +# generated ninja rules. +# +# BUILD_DIR: A custom build dir relative to the calling project CMAKE_CURRENT_BINARY_DIR. +# Useful when configuring the same test project with different options in separate +# build dirs. +# +# BINARY: Path to the test artifact that will be executed after the build is complete. If a +# relative path is specified, it will be counted from the build directory. +# Can also be passed a random executable to be found in PATH, like 'ctest'. +# +# BINARY_ARGS: Additional arguments to pass to the BINARY. +# +# TESTNAME: a custom test name to use instead of the one derived from the source directory name +# +# BUILD_OPTIONS: a list of -D style CMake definitions to pass to ctest's --build-options (which +# are ultimately passed to the CMake invocation of the test project). You may +# escape semicolons inside the definitions using: +# https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#bracket-argument +# so the argument containing list will look as following: +# -DLIST_ARGUMENT=item1[[;]]item2[[;]]...itemN. +macro(_qt_internal_test_expect_pass _dir) + if(WASM) + return() + endif() + set(_test_option_args + SIMULATE_IN_SOURCE + NO_CLEAN_STEP + NO_BUILD_PROJECT_ARG + NO_IOS_DEFAULT_ARGS + NO_RUN_ENVIRONMENT_PLUGIN_PATH + ) + set(_test_single_args + BINARY + TESTNAME + BUILD_DIR + GENERATOR + MAKE_PROGRAM + BUILD_TYPE + ) + set(_test_multi_args + BUILD_OPTIONS + BINARY_ARGS + ) + cmake_parse_arguments(_ARGS + "${_test_option_args}" + "${_test_single_args}" + "${_test_multi_args}" + ${ARGN} + ) + + if(NOT _ARGS_NO_IOS_DEFAULT_ARGS AND IOS) + set(_ARGS_NO_BUILD_PROJECT_ARG TRUE) + set(_ARGS_GENERATOR Xcode) + set(_ARGS_MAKE_PROGRAM xcodebuild) + endif() + + if(_ARGS_TESTNAME) + set(testname "${_ARGS_TESTNAME}") + else() + string(REPLACE "(" "_" testname "${_dir}") + string(REPLACE ")" "_" testname "${testname}") + string(REPLACE "/" "_" testname "${testname}") + endif() + + # Allow setting a different generator. Needed for iOS. + set(generator "${CMAKE_GENERATOR}") + if(_ARGS_GENERATOR) + set(generator "${_ARGS_GENERATOR}") + endif() + + # Allow setting a different make program. + if(_ARGS_MAKE_PROGRAM) + set(make_program "${_ARGS_MAKE_PROGRAM}") + elseif(CMAKE_GENERATOR MATCHES "Ninja") + # Use a ninja wrapper when generator is ninja + _qt_internal_get_ninja_wrapper("${CMAKE_MAKE_PROGRAM}" ninja_wrapper) + set(make_program "${ninja_wrapper}") + else() + set(make_program "${CMAKE_MAKE_PROGRAM}") + endif() + + # Only pass build config if it was specified during the initial tests/auto project + # configuration. Important when using Qt multi-config builds which won't have CMAKE_BUILD_TYPE + # set. + set(build_type "") + + if(_ARGS_BUILD_TYPE) + set(build_type "${_ARGS_BUILD_TYPE}") + elseif(CMAKE_BUILD_TYPE) + set(build_type "${CMAKE_BUILD_TYPE}") + endif() + if(build_type) + set(build_type "--build-config" "${build_type}") + endif() + + # Allow skipping clean step. + set(build_no_clean "") + if(_ARGS_NO_CLEAN_STEP) + set(build_no_clean "--build-noclean") + endif() + + # Allow omitting the --build-project arg. It's relevant for xcode projects where the project + # name on disk is different from the project source dir name. + if(NOT _ARGS_NO_BUILD_PROJECT_ARG) + set(build_project "--build-project" "${_dir}") + else() + set(build_project) + endif() + + # Allow omitting test command if no binary or binary args are provided. + set(test_command "") + if(_ARGS_BINARY) + list(APPEND test_command ${_ARGS_BINARY} ${_ARGS_BINARY_ARGS}) + endif() + if(test_command) + set(test_command "--test-command" ${test_command}) + endif() + + set(additional_configure_args "") + + # Allow passing additional configure options to all projects via either a cache var or env var. + # Can be useful for certain catch-all scenarios. + if(QT_CMAKE_TESTS_ADDITIONAL_CONFIGURE_OPTIONS) + list(APPEND additional_configure_args ${QT_CMAKE_TESTS_ADDITIONAL_CONFIGURE_OPTIONS}) + endif() + if(DEFINED ENV{QT_CMAKE_TESTS_ADDITIONAL_CONFIGURE_OPTIONS}) + list(APPEND additional_configure_args $ENV{QT_CMAKE_TESTS_ADDITIONAL_CONFIGURE_OPTIONS}) + endif() + + # When building an iOS CMake test in the CI using a universal Qt build, target the simulator + # sdk, because the CI currently doesn't have a proper setup for signing device binaries + # (missing a working signing certificate and provisioning profile). Allow opt-out. + if(IOS) + set(osx_arch_count 0) + if(QT_OSX_ARCHITECTURES) + list(LENGTH QT_OSX_ARCHITECTURES osx_arch_count) + endif() + + set(build_environment "") + if(DEFINED ENV{QT_BUILD_ENVIRONMENT}) + set(build_environment "$ENV{QT_BUILD_ENVIRONMENT}") + endif() + if(build_environment STREQUAL "ci" + AND osx_arch_count GREATER_EQUAL 2 + AND NOT QT_APPLE_SDK + AND NOT QT_NO_IOS_BUILD_ADJUSTMENT_IN_CI) + list(APPEND additional_configure_args + -DCMAKE_OSX_ARCHITECTURES=x86_64 -DCMAKE_OSX_SYSROOT=iphonesimulator) + endif() + endif() + + set(__expect_pass_build_dir "${CMAKE_CURRENT_BINARY_DIR}/${_dir}") + if(_ARGS_BUILD_DIR) + set(__expect_pass_build_dir "${CMAKE_CURRENT_BINARY_DIR}/${_ARGS_BUILD_DIR}") + endif() + + set(__expect_pass_source_dir "${CMAKE_CURRENT_SOURCE_DIR}/${_dir}") + if(_ARGS_SIMULATE_IN_SOURCE) + set(__expect_pass_in_source_build_dir "${CMAKE_CURRENT_BINARY_DIR}/in_source") + set(__expect_pass_build_dir "${__expect_pass_in_source_build_dir}/${_dir}/build") + set(__expect_pass_source_dir "${__expect_pass_in_source_build_dir}/${_dir}") + + unset(__expect_pass_in_source_build_dir) + endif() + + if(_ARGS_BINARY AND NOT IS_ABSOLUTE "${_ARGS_BINARY}") + set(_ARGS_BINARY "${__expect_pass_build_dir}/${_ARGS_BINARY}") + endif() + + if(_ARGS_SIMULATE_IN_SOURCE) + add_test(NAME ${testname}_cleanup + COMMAND ${CMAKE_COMMAND} -E remove_directory "${__expect_pass_source_dir}" + ) + set_tests_properties(${testname}_cleanup PROPERTIES + FIXTURES_SETUP "${testname}SIMULATE_IN_SOURCE_FIXTURE" + ) + add_test(${testname}_copy_sources ${CMAKE_COMMAND} -E copy_directory + "${CMAKE_CURRENT_SOURCE_DIR}/${_dir}" "${__expect_pass_source_dir}" + ) + set_tests_properties(${testname}_copy_sources PROPERTIES + FIXTURES_SETUP "${testname}SIMULATE_IN_SOURCE_FIXTURE" + DEPENDS ${testname}_cleanup + ) + endif() + + string(REPLACE "[[;]]" "\;" _ARGS_BUILD_OPTIONS "${_ARGS_BUILD_OPTIONS}") + + _qt_internal_get_cmake_test_configure_options(option_list) + set(ctest_command_args + --build-and-test + "${__expect_pass_source_dir}" + "${__expect_pass_build_dir}" + ${build_type} + ${build_no_clean} + --build-generator "${generator}" + --build-makeprogram "${make_program}" + ${build_project} + --build-options "${option_list}" + "${_ARGS_BUILD_OPTIONS}" ${additional_configure_args} + ${test_command} + ) + add_test(${testname} ${CMAKE_CTEST_COMMAND} ${ctest_command_args}) + if(_ARGS_SIMULATE_IN_SOURCE) + set_tests_properties(${testname} PROPERTIES + FIXTURES_REQUIRED "${testname}SIMULATE_IN_SOURCE_FIXTURE" + ) + endif() + set_tests_properties(${testname} PROPERTIES ENVIRONMENT "ASAN_OPTIONS=detect_leaks=0") + + if(_ARGS_BINARY) + set(run_env_args "") + if(_ARGS_NO_RUN_ENVIRONMENT_PLUGIN_PATH) + list(APPEND run_env_args NO_PLUGIN_PATH) + endif() + _qt_internal_set_up_test_run_environment("${testname}" ${run_env_args}) + endif() + + unset(__expect_pass_source_dir) + unset(__expect_pass_build_dir) endmacro() -macro(expect_fail _dir) +# Checks if a qmake project can be built successfully. Arguments: +# +# TESTNAME: a custom test name to use instead of the one derived from the source directory name. +# the name also applies to the generated build directory. +# +# QMAKE_OPTIONS: a list of variable assignments to pass to the qmake invocation. +# e.g. CONFIG+=debug +# +# BUILD_ENVIRONMENT: a list of environment assignments to use when invoking the build tool +function(_qt_internal_add_qmake_test dir_name) + set(test_option_args + ) + set(test_single_args + TESTNAME + ) + set(test_multi_args + QMAKE_OPTIONS + BUILD_ENVIRONMENT + ) + + # PARSE_ARGV parsing keeps ';' in ENVIRONMENT variables + cmake_parse_arguments(PARSE_ARGV 1 arg + "${test_option_args}" + "${test_single_args}" + "${test_multi_args}" + ) + + if(arg_TESTNAME) + set(testname "${arg_TESTNAME}") + else() + string(REGEX REPLACE "[/)(]" "_" testname "${dir_name}") + endif() + + set(source_dir "${CMAKE_CURRENT_SOURCE_DIR}/${dir_name}") + if(arg_TESTNAME) + set(build_dir "${CMAKE_CURRENT_BINARY_DIR}/${arg_TESTNAME}") + else() + set(build_dir "${CMAKE_CURRENT_BINARY_DIR}/${dir_name}") + endif() + + # Find the qmake binary or the wrapper qmake script when cross-compiling.. + if(QtBase_BINARY_DIR AND NOT QT_BUILD_STANDALONE_TESTS) + set(qmake_dir "${QtBase_BINARY_DIR}/${INSTALL_BINDIR}") + else() + set(qmake_dir "${QT6_INSTALL_PREFIX}/${QT6_INSTALL_BINS}") + endif() + + set(qmake_path "${qmake_dir}/qmake${CMAKE_EXECUTABLE_SUFFIX}") + + set(qmake_args + "${source_dir}" + ${arg_QMAKE_OPTIONS} + ) + + # Try to choose an appropriate build tool. + if(ENV{QT_QMAKE_TEST_BUILD_TOOL}) + set(build_tool "$ENV{QT_QMAKE_TEST_BUILD_TOOL}") + elseif(MSVC) + set(build_tool "nmake") + elseif(MINGW) + set(build_tool "mingw32-make") + else() + set(build_tool "make") + endif() + + set(build_tool_args "") + if(ENV{QT_QMAKE_TEST_BUILD_TOOL_OPTIONS}) + set(build_tool_args "$ENV{QT_QMAKE_TEST_BUILD_TOOL_OPTIONS}") + endif() + + # Remove any stale build dir, and create a new one on each test rerun. + add_test(${testname}_remove_build_dir + ${CMAKE_COMMAND} -E remove_directory "${build_dir}" + ) + set_tests_properties(${testname}_remove_build_dir PROPERTIES + FIXTURES_SETUP "${testname}_ensure_clean_build_dir" + ) + + add_test(${testname}_create_build_dir + ${CMAKE_COMMAND} -E make_directory "${build_dir}" + ) + set_tests_properties(${testname}_create_build_dir PROPERTIES + FIXTURES_SETUP "${testname}_ensure_clean_build_dir" + ) + + set_tests_properties(${testname}_create_build_dir + PROPERTIES DEPENDS ${testname}_remove_build_dir) + + # Add test to call qmake. + # + # We can't use the add_test(NAME) signature to set a working directory, because that breaks + # when calling ctest without a -C <config> using multi-config generators, and the CI calls + # ctest without -C, and we use Xcode when configuring tests for iOS, which is multi-config. + # The plain add_test signature does not have this issue. + # Work around this by using a wrapper script that sets a working directory and use the plain + # signature. + # Somewhat related issue https://gitlab.kitware.com/cmake/cmake/-/issues/20283 + set(qmake_wrapper_file "${CMAKE_CURRENT_BINARY_DIR}/run_qmake_${testname}.cmake") + _qt_internal_create_command_script( + COMMAND "${qmake_path}" ${qmake_args} + COMMAND_ECHO STDOUT + OUTPUT_FILE "${qmake_wrapper_file}" + WORKING_DIRECTORY "${build_dir}" + ) + + add_test(${testname}_qmake "${CMAKE_COMMAND}" "-P" "${qmake_wrapper_file}") + + set_tests_properties(${testname}_qmake PROPERTIES + DEPENDS ${testname}_create_build_dir + FIXTURES_REQUIRED "${testname}_ensure_clean_build_dir" + FIXTURES_SETUP "${testname}_configure_project" + ) + + # Add test to build the generated qmake project. + set(build_tool_wrapper_file "${CMAKE_CURRENT_BINARY_DIR}/run_build_${testname}.cmake") + _qt_internal_create_command_script( + COMMAND "${build_tool}" ${build_tool_args} + COMMAND_ECHO STDOUT + OUTPUT_FILE "${build_tool_wrapper_file}" + WORKING_DIRECTORY "${build_dir}" + ENVIRONMENT ${arg_BUILD_ENVIRONMENT} + ) + + add_test(${testname} "${CMAKE_COMMAND}" "-P" "${build_tool_wrapper_file}") + + set_tests_properties(${testname} PROPERTIES + DEPENDS ${testname}_qmake + FIXTURES_REQUIRED "${testname}_ensure_clean_build_dir;${testname}_configure_project" + ) +endfunction() + +# Checks if the build of the test project fails. +# This test passes if the test project fails either at the +# configuring or build steps. +# Arguments: See _qt_internal_test_expect_pass +macro(_qt_internal_test_expect_fail) + _qt_internal_test_expect_pass(${ARGV}) + set_tests_properties(${testname} PROPERTIES WILL_FAIL TRUE) +endmacro() + +# Checks if the build of the test project fails. +# This test passes only if the test project fails at the build step, +# but not at the configuring step. +macro(_qt_internal_test_expect_build_fail _dir) string(REPLACE "(" "_" testname "${_dir}") string(REPLACE ")" "_" testname "${testname}") file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/failbuild/${_dir}") file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/${_dir}" DESTINATION "${CMAKE_CURRENT_BINARY_DIR}/failbuild/${_dir}") - file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/failbuild/${_dir}/${_dir}/FindPackageHints.cmake" "set(Qt5Tests_PREFIX_PATH \"${CMAKE_PREFIX_PATH}\")") + set(__expect_fail_prefixes "") + _qt_internal_get_cmake_test_configure_options(option_list OUT_PREFIX_PATH __expect_fail_prefixes) + + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/failbuild/${_dir}/${_dir}/FindPackageHints.cmake" + "set(Qt6Tests_PREFIX_PATH \"${__expect_fail_prefixes}\") +list(APPEND CMAKE_PREFIX_PATH \"${__expect_fail_prefixes}\") +") file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/failbuild/${_dir}/CMakeLists.txt" " - cmake_minimum_required(VERSION 2.8) + cmake_minimum_required(VERSION 3.16) project(${_dir}) try_compile(Result \${CMAKE_CURRENT_BINARY_DIR}/${_dir} @@ -112,29 +634,40 @@ macro(expect_fail _dir) endif() " ) + + + if(CMAKE_GENERATOR MATCHES "Ninja") + # Use a ninja wrapper when generator is ninja + _qt_internal_get_ninja_wrapper("${CMAKE_MAKE_PROGRAM}" ninja_wrapper) + set(make_program "${ninja_wrapper}") + else() + set(make_program "${CMAKE_MAKE_PROGRAM}") + endif() + add_test(${testname} ${CMAKE_CTEST_COMMAND} --build-and-test "${CMAKE_CURRENT_BINARY_DIR}/failbuild/${_dir}" "${CMAKE_CURRENT_BINARY_DIR}/failbuild/${_dir}/build" --build-config "${CMAKE_BUILD_TYPE}" - --build-generator ${CMAKE_GENERATOR} - --build-makeprogram ${CMAKE_MAKE_PROGRAM} - --build-project ${_dir} - --build-options "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" ${BUILD_OPTIONS_LIST} + --build-generator "${CMAKE_GENERATOR}" + --build-makeprogram "${make_program}" + --build-project "${_dir}" + --build-options ${option_list} ) + unset(__expect_fail_prefixes) endmacro() -function(test_module_includes) +function(_qt_internal_test_module_includes) set(all_args ${ARGN}) set(packages_string "") set(libraries_string "") - foreach(_package ${Qt5_MODULE_TEST_DEPENDS}) + foreach(_package ${Qt6_MODULE_TEST_DEPENDS}) set(packages_string " ${packages_string} - find_package(Qt5${_package} 5.0.0 REQUIRED) + find_package(Qt6${_package} 6.0.0 REQUIRED) " ) endforeach() @@ -147,52 +680,36 @@ function(test_module_includes) set(packages_string "${packages_string} - find_package(Qt5${qtmodule} 5.0.0 REQUIRED) - include_directories(\${Qt5${qtmodule}_INCLUDE_DIRS}) - add_definitions(\${Qt5${qtmodule}_DEFINITIONS})\n") + find_package(Qt6${qtmodule} 6.0.0 REQUIRED)\n") list(FIND CMAKE_MODULES_UNDER_TEST ${qtmodule} _findIndex) if (NOT _findIndex STREQUAL -1) set(packages_string "${packages_string} - if(NOT \"\${Qt5${qtmodule}_VERSION}\" VERSION_EQUAL ${CMAKE_MODULE_VERSION}) - message(SEND_ERROR \"Qt5${qtmodule}_VERSION variable was not ${CMAKE_MODULE_VERSION}. Got \${Qt5${qtmodule}_VERSION} instead.\") + if(NOT \"\${Qt6${qtmodule}_VERSION}\" VERSION_EQUAL ${CMAKE_MODULE_VERSION}) + message(SEND_ERROR \"Qt6${qtmodule}_VERSION variable was not ${CMAKE_MODULE_VERSION}. Got \${Qt6${qtmodule}_VERSION} instead.\") endif() - if(NOT \"\${Qt5${qtmodule}_VERSION_MAJOR}\" VERSION_EQUAL ${CMAKE_${qtmodule}_MODULE_MAJOR_VERSION}) - message(SEND_ERROR \"Qt5${qtmodule}_VERSION_MAJOR variable was not ${CMAKE_${qtmodule}_MODULE_MAJOR_VERSION}. Got \${Qt5${qtmodule}_VERSION_MAJOR} instead.\") + if(NOT \"\${Qt6${qtmodule}_VERSION_MAJOR}\" VERSION_EQUAL ${CMAKE_${qtmodule}_MODULE_MAJOR_VERSION}) + message(SEND_ERROR \"Qt6${qtmodule}_VERSION_MAJOR variable was not ${CMAKE_${qtmodule}_MODULE_MAJOR_VERSION}. Got \${Qt6${qtmodule}_VERSION_MAJOR} instead.\") endif() - if(NOT \"\${Qt5${qtmodule}_VERSION_MINOR}\" VERSION_EQUAL ${CMAKE_${qtmodule}_MODULE_MINOR_VERSION}) - message(SEND_ERROR \"Qt5${qtmodule}_VERSION_MINOR variable was not ${CMAKE_${qtmodule}_MODULE_MINOR_VERSION}. Got \${Qt5${qtmodule}_VERSION_MINOR} instead.\") + if(NOT \"\${Qt6${qtmodule}_VERSION_MINOR}\" VERSION_EQUAL ${CMAKE_${qtmodule}_MODULE_MINOR_VERSION}) + message(SEND_ERROR \"Qt6${qtmodule}_VERSION_MINOR variable was not ${CMAKE_${qtmodule}_MODULE_MINOR_VERSION}. Got \${Qt6${qtmodule}_VERSION_MINOR} instead.\") endif() - if(NOT \"\${Qt5${qtmodule}_VERSION_PATCH}\" VERSION_EQUAL ${CMAKE_${qtmodule}_MODULE_PATCH_VERSION}) - message(SEND_ERROR \"Qt5${qtmodule}_VERSION_PATCH variable was not ${CMAKE_${qtmodule}_MODULE_PATCH_VERSION}. Got \${Qt5${qtmodule}_VERSION_PATCH} instead.\") - endif() - if(NOT \"\${Qt5${qtmodule}_VERSION_STRING}\" VERSION_EQUAL ${CMAKE_MODULE_VERSION}) - message(SEND_ERROR \"Qt5${qtmodule}_VERSION_STRING variable was not ${CMAKE_MODULE_VERSION}. Got \${Qt5${qtmodule}_VERSION_STRING} instead.\") + if(NOT \"\${Qt6${qtmodule}_VERSION_PATCH}\" VERSION_EQUAL ${CMAKE_${qtmodule}_MODULE_PATCH_VERSION}) + message(SEND_ERROR \"Qt6${qtmodule}_VERSION_PATCH variable was not ${CMAKE_${qtmodule}_MODULE_PATCH_VERSION}. Got \${Qt6${qtmodule}_VERSION_PATCH} instead.\") endif()\n" ) endif() - set(libraries_string "${libraries_string} Qt5::${qtmodule}") + set(libraries_string "${libraries_string} Qt6::${qtmodule}") endwhile() file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/module_includes/CMakeLists.txt" " - cmake_minimum_required(VERSION 2.8) + cmake_minimum_required(VERSION 3.16) project(module_includes) ${packages_string} - set(CMAKE_CXX_FLAGS \"\${CMAKE_CXX_FLAGS} \${Qt5Core_EXECUTABLE_COMPILE_FLAGS}\") - if (CMAKE_VERSION VERSION_LESS 3.3) - if (CMAKE_CXX_COMPILER_ID STREQUAL AppleClang - OR (APPLE AND CMAKE_CXX_COMPILER_ID STREQUAL Clang)) - set(CMAKE_CXX_FLAGS \"\${CMAKE_CXX_FLAGS} -std=gnu++0x -stdlib=libc++\") - elseif (CMAKE_CXX_COMPILER_ID STREQUAL GNU - OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) - set(CMAKE_CXX_FLAGS \"\${CMAKE_CXX_FLAGS} -std=gnu++0x\") - endif() - endif() - add_executable(module_includes_exe \"\${CMAKE_CURRENT_SOURCE_DIR}/main.cpp\") target_link_libraries(module_includes_exe ${libraries_string})\n" ) @@ -230,14 +747,24 @@ function(test_module_includes) int main(int, char **) { ${instances_string} return 0; }\n" ) + _qt_internal_get_cmake_test_configure_options(option_list) + + if(CMAKE_GENERATOR MATCHES "Ninja") + # Use a ninja wrapper when generator is ninja + _qt_internal_get_ninja_wrapper("${CMAKE_MAKE_PROGRAM}" ninja_wrapper) + set(make_program "${ninja_wrapper}") + else() + set(make_program "${CMAKE_MAKE_PROGRAM}") + endif() + add_test(module_includes ${CMAKE_CTEST_COMMAND} --build-and-test "${CMAKE_CURRENT_BINARY_DIR}/module_includes/" "${CMAKE_CURRENT_BINARY_DIR}/module_includes/build" --build-config "${CMAKE_BUILD_TYPE}" - --build-generator ${CMAKE_GENERATOR} - --build-makeprogram ${CMAKE_MAKE_PROGRAM} + --build-generator "${CMAKE_GENERATOR}" + --build-makeprogram "${make_program}" --build-project module_includes - --build-options "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH}" ${BUILD_OPTIONS_LIST} + --build-options ${option_list} ) endfunction() |