summaryrefslogtreecommitdiffstats
path: root/cmake/QtBuildInternals
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2021-12-14 16:56:39 +0100
committerAlexandru Croitor <alexandru.croitor@qt.io>2022-02-01 16:51:01 +0100
commit98c89c8cc1c5ceb4bfbb5f5ed6c96ecdbab99afa (patch)
treec4ab5256149efa6eebe9dce4dda421d973023773 /cmake/QtBuildInternals
parent1273ffb0002b14d952807c250dbcc6228f60ff26 (diff)
CMake: Build examples as ExternalProjects in prefix builds
Change prefix builds to use ExternalProjects to build examples by default. This will affect our CI which only does prefix builds. To make it work, we have to do a few adjustments: - look for Config files in the build-tree (before Qt is installed) - build only one examples with only a single config, even if Qt is a multi-config build - install examples as part of main make install step, rather than as part of the make step (which is the default for EPs) - adjust CXX flags when building with MSVC to ensure we can still use sccache and separate debug info - derive the correct install prefix for each example and pass it to the ExternalProject As a drive-by, add TODOs to address tidiness of the code and corner cases that likely don't work (Conan). Amends d97fd7af2bc5c89a0ad9e5fac080041b78d01179 Pick-to: 6.2 6.3 Task-number: QTBUG-90820 Task-number: QTBUG-96232 Change-Id: I3060da5dc64e7b06052f9dcb720d4d250f876450 Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org> Reviewed-by: Jörg Bornemann <joerg.bornemann@qt.io>
Diffstat (limited to 'cmake/QtBuildInternals')
-rw-r--r--cmake/QtBuildInternals/QtBuildInternalsConfig.cmake140
1 files changed, 123 insertions, 17 deletions
diff --git a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake
index b41e75dd7b..618f120597 100644
--- a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake
+++ b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake
@@ -719,6 +719,10 @@ macro(qt_internal_set_up_build_dir_package_paths)
list(APPEND CMAKE_PREFIX_PATH "${QT_BUILD_DIR}")
# Make sure the CMake config files do not recreate the already-existing targets
set(QT_NO_CREATE_TARGETS TRUE)
+
+ # TODO: Remove reliance on CMAKE_FIND_ROOT_PATH_MODE_PACKAGE and instead pass any required
+ # prefixes as a combination of CMAKE_PREFIX_PATH and CMAKE_FIND_ROOT_PATH like we do in
+ # qt_internal_add_tool.
set(BACKUP_CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ${CMAKE_FIND_ROOT_PATH_MODE_PACKAGE})
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE "BOTH")
endmacro()
@@ -733,7 +737,6 @@ macro(qt_examples_build_begin)
# Use by qt_internal_add_example.
set(QT_EXAMPLE_BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
- # FIXME: Support prefix builds as well QTBUG-96232
if(arg_EXTERNAL_BUILD AND QT_BUILD_EXAMPLES_AS_EXTERNAL)
# Examples will be built using ExternalProject.
# We always depend on all plugins so as to prevent opportunities for
@@ -789,13 +792,25 @@ macro(qt_examples_build_begin)
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.
# Appending to CMAKE_PREFIX_PATH helps find the initial Qt6Config.cmake.
# Appending to QT_EXAMPLES_CMAKE_PREFIX_PATH helps find components of Qt6, because those
# find_package calls use NO_DEFAULT_PATH, and thus CMAKE_PREFIX_PATH is ignored.
- qt_internal_set_up_build_dir_package_paths()
- list(APPEND QT_EXAMPLES_CMAKE_PREFIX_PATH "${QT_BUILD_DIR}")
+ 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(APPEND QT_EXAMPLES_CMAKE_PREFIX_PATH "${QT_BUILD_DIR}")
+ 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.
@@ -896,8 +911,6 @@ unset(CMAKE_INSTALL_PREFIX)
endfunction()
function(qt_internal_add_example_external_project subdir)
- # FIXME: Support building examples externally for prefix builds as well.
-
set(options "")
set(singleOpts NAME)
set(multiOpts "")
@@ -918,15 +931,33 @@ function(qt_internal_add_example_external_project subdir)
set(arg_NAME "${arg_NAME}-${short_hash}")
endif()
- if(QtBase_BINARY_DIR)
- # Always use the copy in the build directory, even for prefix builds.
- # We may build examples without installing, so we can't use the
- # install or staging area.
- set(qt_cmake_dir ${QtBase_BINARY_DIR}/lib/cmake/${QT_CMAKE_EXPORT_NAMESPACE})
+ # TODO: Fix 'prefix Qt' example builds not to rely on CMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH
+ # for finding Qt packages when cross-compiling.
+ # TODO: Fix example builds when using Conan / install prefixes are different for each repo.
+ if(QT_SUPERBUILD OR QtBase_BINARY_DIR)
+ # When doing a top-level build or when building qtbase,
+ # always use the Config file from the current build directory, even for prefix builds.
+ # We strive to allow building examples without installing Qt first, which means we can't
+ # use the install or staging Config files.
+ set(qt_prefixes "${QT_BUILD_DIR}")
+ set(qt_cmake_dir "${QT_CONFIG_BUILD_DIR}/${QT_CMAKE_EXPORT_NAMESPACE}")
else()
# This is a per-repo build that isn't the qtbase repo, so we know that
# qtbase was found via find_package() and Qt6_DIR must be set
- set(qt_cmake_dir ${${QT_CMAKE_EXPORT_NAMESPACE}_DIR})
+ set(qt_cmake_dir "${${QT_CMAKE_EXPORT_NAMESPACE}_DIR}")
+
+ # In a prefix build of a non-qtbase repo, we want to pick up the installed Config files
+ # for all repos except the one that is currently built. For the repo that is currently
+ # built, we pick up the Config files from the current repo build dir instead.
+ # For non-prefix builds, there's only one prefix, the main build dir.
+ set(qt_prefixes "${QT_BUILD_DIR}")
+ if(QT_WILL_INSTALL)
+ list(APPEND qt_prefixes "${QT6_INSTALL_PREFIX}")
+
+ # Appending to QT_EXAMPLES_CMAKE_PREFIX_PATH helps find Qt6 components in
+ # non-qtbase prefix builds because we use NO_DEFAULT_PATH in find_package calls.
+ set(QT_EXAMPLES_CMAKE_PREFIX_PATH "${qt_prefixes}")
+ endif()
endif()
set(vars_to_pass_if_defined)
@@ -935,16 +966,14 @@ function(qt_internal_add_example_external_project subdir)
# Android NDK forces CMAKE_FIND_ROOT_PATH_MODE_PACKAGE to ONLY, so we
# can't rely on this setting here making it through to the example
# project.
- # TODO: We should probably leave CMAKE_FIND_ROOT_PATH_MODE_PACKAGE
- # alone. It may be a leftover from earlier methods that are no
- # longer used or that no longer need this.
+ # TODO: Remove CMAKE_FIND_ROOT_PATH_MODE_PACKAGE once cross-compiling examples uses
+ # CMAKE_FIND_ROOT_PATH + CMAKE_PREFIX_PATH instead.
list(APPEND var_defs
-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${qt_cmake_dir}/qt.toolchain.cmake
-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE:STRING=BOTH
)
else()
- get_filename_component(prefix_dir ${qt_cmake_dir}/../../.. ABSOLUTE)
- list(PREPEND CMAKE_PREFIX_PATH ${prefix_dir})
+ 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.
@@ -955,9 +984,36 @@ function(qt_internal_add_example_external_project subdir)
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()
+
list(APPEND vars_to_pass_if_defined
CMAKE_BUILD_TYPE:STRING
+ CMAKE_CONFIGURATION_TYPES:STRING
CMAKE_PREFIX_PATH:STRING
+ QT_EXAMPLES_CMAKE_PREFIX_PATH:STRING
CMAKE_FIND_ROOT_PATH:STRING
CMAKE_FIND_ROOT_PATH_MODE_PACKAGE:STRING
BUILD_SHARED_LIBS:BOOL
@@ -1023,19 +1079,69 @@ function(qt_internal_add_example_external_project subdir)
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}
+ # 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.
+
+ set(qt_example_install_prefix "${CMAKE_INSTALL_PREFIX}/${INSTALL_EXAMPLESDIR}")
+ set(example_install_prefix "${qt_example_install_prefix}/${example_rel_path}")
+
+ set(ep_binary_dir "${CMAKE_CURRENT_BINARY_DIR}/${subdir}")
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 "${CMAKE_CURRENT_BINARY_DIR}/${subdir}"
+ BINARY_DIR "${ep_binary_dir}"
+ INSTALL_DIR "${example_install_prefix}"
INSTALL_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})