summaryrefslogtreecommitdiffstats
path: root/cmake/QtBuildRepoExamplesHelpers.cmake
diff options
context:
space:
mode:
Diffstat (limited to 'cmake/QtBuildRepoExamplesHelpers.cmake')
-rw-r--r--cmake/QtBuildRepoExamplesHelpers.cmake652
1 files changed, 652 insertions, 0 deletions
diff --git a/cmake/QtBuildRepoExamplesHelpers.cmake b/cmake/QtBuildRepoExamplesHelpers.cmake
new file mode 100644
index 0000000000..6802d81323
--- /dev/null
+++ b/cmake/QtBuildRepoExamplesHelpers.cmake
@@ -0,0 +1,652 @@
+# 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})
+
+ # Examples are not unity-ready.
+ set(CMAKE_UNITY_BUILD OFF)
+
+ # Skip running deployment steps when the developer asked to deploy a minimal subset of examples.
+ # Each example can then decide whether it wants to be deployed as part of the minimal subset
+ # by unsetting the QT_INTERNAL_SKIP_DEPLOYMENT variable before its qt_internal_add_example call.
+ # This will be used by our CI.
+ if(NOT DEFINED QT_INTERNAL_SKIP_DEPLOYMENT AND QT_DEPLOY_MINIMAL_EXAMPLES)
+ set(QT_INTERNAL_SKIP_DEPLOYMENT TRUE)
+ endif()
+
+ # Use by qt_internal_add_example.
+ set(QT_EXAMPLE_BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}")
+
+ if(QT_BUILD_STANDALONE_EXAMPLES)
+ # Find all qt packages, so that the various if(QT_FEATURE_foo) add_subdirectory()
+ # conditions have correct values, regardless whether we will use ExternalProjects or not.
+ qt_internal_find_standalone_parts_config_files()
+ endif()
+
+ 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)
+ # Don't show warnings for examples that were added via qt_internal_add_example.
+ # Those that are added via add_subdirectory will see the warning, due to the parent scope
+ # having the variable set to TRUE.
+ if(QT_FEATURE_developer_build AND NOT QT_NO_WARN_ABOUT_EXAMPLE_ADD_SUBDIRECTORY_WARNING)
+ set(QT_WARN_ABOUT_EXAMPLE_ADD_SUBDIRECTORY FALSE)
+ endif()
+
+ # 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}")
+ elseif(QT_BUILD_STANDALONE_EXAMPLES)
+ # TODO: We might need to reset and pipe through an empty CMAKE_STAGING_PREFIX if we ever
+ # try to run standalone examples in the CI when cross-compiling, similar how it's done in
+ # qt_internal_set_up_fake_standalone_parts_install_prefix.
+ qt_internal_get_fake_standalone_install_prefix(qt_example_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.
+# Also unset the deployment prefix, so it can be recomputed for each example subdirectory.
+unset(CMAKE_INSTALL_PREFIX)
+unset(QT_DEPLOY_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()
+
+ # We we need to augment the CMAKE_MODULE_PATH with the current repo cmake build dir, to find
+ # files like FindWrapBundledFooConfigExtra.cmake.
+ set(module_paths "${qt_prefixes}")
+ list(TRANSFORM module_paths APPEND "/${INSTALL_LIBDIR}/cmake/${QT_CMAKE_EXPORT_NAMESPACE}")
+ list(APPEND CMAKE_MODULE_PATH ${module_paths})
+
+ # Pass additional paths where qml plugin config files should be included by Qt6QmlPlugins.cmake.
+ # This is needed in prefix builds, where the cmake files are not installed yet.
+ set(glob_prefixes "${qt_prefixes}")
+ list(TRANSFORM glob_prefixes APPEND "/${INSTALL_LIBDIR}/cmake/${QT_CMAKE_EXPORT_NAMESPACE}Qml")
+
+ set(qml_plugin_cmake_config_file_glob_prefixes "")
+ foreach(glob_prefix IN LISTS glob_prefix)
+ if(EXISTS "${glob_prefix}")
+ list(APPEND qml_plugin_cmake_config_file_glob_prefixes "${glob_prefix}")
+ endif()
+ endforeach()
+
+ if(qml_plugin_cmake_config_file_glob_prefixes)
+ set(QT_ADDITIONAL_QML_PLUGIN_GLOB_PREFIXES ${qml_plugin_cmake_config_file_glob_prefixes})
+ 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
+ QT_ADDITIONAL_QML_PLUGIN_GLOB_PREFIXES:STRING
+ QT_INTERNAL_SKIP_DEPLOYMENT:BOOL
+ CMAKE_FIND_ROOT_PATH:STRING
+ CMAKE_MODULE_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
+ )
+
+ # QT_EXAMPLE_CMAKE_VARS_TO_PASS can be set by specific repos to pass any additional required
+ # CMake cache variables.
+ # One use case is passing locations of 3rd party package locations like Protobuf via _ROOT
+ # variables.
+ set(extra_vars_var_name "")
+ if(QT_EXAMPLE_CMAKE_VARS_TO_PASS)
+ set(extra_vars_var_name "QT_EXAMPLE_CMAKE_VARS_TO_PASS")
+ endif()
+ foreach(var_with_type IN LISTS vars_to_pass_if_defined ${extra_vars_var_name})
+ 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()