summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexandru Croitor <alexandru.croitor@qt.io>2023-08-07 12:15:35 +0200
committerAlexandru Croitor <alexandru.croitor@qt.io>2023-08-16 19:04:40 +0200
commit121f7f382badc0f8af6fdbb547f8649b5e1b75c3 (patch)
tree8ba9a6c285f09070662581489a48b129fb2b521c
parent120fc8244a484a5fde5b19f2e603b05692b79f61 (diff)
CMake: Allow installation of example sources into the Qt prefix
In Qt 5 times, if Qt was configured with -make examples, running make install would not only build and install the example binaries, but would also install the example sources into the prefix. Installation of example sources was not implemented when the Qt 6 build system has switched to using CMake. There is still a use case for it though, mainly for Qt Creator, which only shows the examples of a Qt kit if the sources are available. In contrast to Qt 5, in Qt 6 we will not install example sources by default. It will be opt in. To enable installation of examples sources, configure with configure -make examples -install-examples-sources or cmake -DQT_BUILD_EXAMPLES=ON -DQT_INSTALL_EXAMPLES_SOURCES=ON The -make examples part is required, otherwise -install-examples-sources has no effect. All example sources can be installed by calling cmake --install . --component examples_sources in the qt repo build directory. In a top-level build, per-repo installation can be done using cmake --install . --component examples_sources_<repo_name> where repo_name could be 'qtbase'. A single example's source can be installed by calling cmake --install . --component examples_sources_<subdir_name> where subdir_name is the subdirectory name of the example, e.g. 'gallery'. Implement installation of example sources by hooking into the qt_internal_add_example command. This means that all examples in all repos need to be added via qt_internal_add_example instead of add_subdirectory, to ensure the sources are installed. The majority of repos already use it. For testing purposes one can configure with -DQT_BUILD_EXAMPLES=ON -DQT_INSTALL_EXAMPLES_SOURCES=ON -DQT_INTERNAL_NO_CONFIGURE_EXAMPLES=ON to allow testing installation of examples sources without building them. Take into account an additional variable called QT_INTERNAL_EXAMPLES_SOURCES_INSTALL_PREFIX to allow installation of example sources into a location different from the example binaries. As a cleanup, the NAME option that could previously be passed to qt_internal_add_example_external_project has been removed. That's because it's never used anywhere and could not have worked anyway because qt_internal_add_example_in_tree never handled it. Pick-to: 6.6 Fixes: QTBUG-112135 Change-Id: I52aa5ec643ff7e212276c88d8dd2dfecdbdbeb0d Reviewed-by: Alexey Edelev <alexey.edelev@qt.io>
-rw-r--r--cmake/QtBuildInternals/QtBuildInternalsConfig.cmake209
-rw-r--r--cmake/QtProcessConfigureArgs.cmake1
-rw-r--r--cmake/QtSetup.cmake3
-rw-r--r--cmake/configure-cmake-mapping.md1
-rw-r--r--config_help.txt3
-rw-r--r--configure.cmake7
-rw-r--r--qt_cmdline.cmake1
7 files changed, 173 insertions, 52 deletions
diff --git a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake
index 4ee68e92c8..80d1d5413c 100644
--- a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake
+++ b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake
@@ -1000,19 +1000,118 @@ 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)
- if(NOT QT_IS_EXTERNAL_EXAMPLES_BUILD)
- qt_internal_add_example_in_tree(${ARGV})
+ # Pre-compute unique example name based on the subdir, in case of target name clashes.
+ qt_internal_get_example_unique_name(unique_example_name "${subdir}")
+
+ # QT_INTERNAL_NO_CONFIGURE_EXAMPLES is not meant to be used by Qt builders, it's here for faster
+ # testing of the source installation code path for build system engineers.
+ if(NOT QT_INTERNAL_NO_CONFIGURE_EXAMPLES)
+ if(NOT QT_IS_EXTERNAL_EXAMPLES_BUILD)
+ qt_internal_add_example_in_tree("${subdir}")
+ else()
+ qt_internal_add_example_external_project("${subdir}"
+ NAME "${unique_example_name}")
+ endif()
+ endif()
+
+ if(QT_INSTALL_EXAMPLES_SOURCES)
+ string(TOLOWER ${PROJECT_NAME} project_name_lower)
+
+ qt_internal_install_example_sources("${subdir}"
+ NAME "${unique_example_name}"
+ REPO_NAME "${project_name_lower}")
+ endif()
+endfunction()
+
+# Gets the install prefix where an example should be installed.
+# Used for computing the final installation path.
+function(qt_internal_get_example_install_prefix out_var)
+ # Allow customizing the installation path of the examples. Will be used in CI.
+ if(QT_INTERNAL_EXAMPLES_INSTALL_PREFIX)
+ set(qt_example_install_prefix "${QT_INTERNAL_EXAMPLES_INSTALL_PREFIX}")
else()
- qt_internal_add_example_external_project(${ARGV})
+ 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()
-# Use old non-ExternalProject approach, aka build in-tree with the Qt build.
-function(qt_internal_add_example_in_tree subdir)
+# 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
@@ -1026,15 +1125,8 @@ unset(CMAKE_INSTALL_PREFIX)
# Override the install prefix in the subdir cmake_install.cmake, so that
# relative install(TARGETS DESTINATION) calls in example projects install where we tell them to.
- # Allow customizing the installation path of the examples. Will be used in CI.
- if(QT_INTERNAL_EXAMPLES_INSTALL_PREFIX)
- set(qt_example_install_prefix "${QT_INTERNAL_EXAMPLES_INSTALL_PREFIX}")
- else()
- set(qt_example_install_prefix "${CMAKE_INSTALL_PREFIX}/${INSTALL_EXAMPLESDIR}")
- endif()
- file(TO_CMAKE_PATH "${qt_example_install_prefix}" qt_example_install_prefix)
-
- set(CMAKE_INSTALL_PREFIX "${qt_example_install_prefix}/${example_rel_path}")
+ 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.
@@ -1044,7 +1136,7 @@ unset(CMAKE_INSTALL_PREFIX)
# 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} ${ARGN})
+ add_subdirectory(${subdir})
endfunction()
function(qt_internal_add_example_external_project subdir)
@@ -1054,33 +1146,6 @@ function(qt_internal_add_example_external_project subdir)
cmake_parse_arguments(PARSE_ARGV 1 arg "${options}" "${singleOpts}" "${multiOpts}")
- file(RELATIVE_PATH example_rel_path
- "${QT_EXAMPLE_BASE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/${subdir}")
-
- if(NOT arg_NAME)
- set(arg_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(arg_NAME MATCHES "/")
- string(REPLACE "/" ";" exploded_path "${arg_NAME}")
- list(POP_BACK exploded_path last_dir)
- if(NOT last_dir)
- message(FATAL_ERROR "Example subdirectory must have a name.")
- else()
- set(arg_NAME "${last_dir}")
- endif()
- endif()
- endif()
-
- # Likely a clash with an example subdir ExternalProject custom target of the same name.
- if(TARGET "${arg_NAME}")
- string(SHA1 rel_path_hash "${example_rel_path}")
- string(SUBSTRING "${rel_path_hash}" 0 4 short_hash)
- set(arg_NAME "${arg_NAME}-${short_hash}")
- endif()
-
# 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,
@@ -1301,15 +1366,7 @@ function(qt_internal_add_example_external_project subdir)
# example_source_dir, use _qt_internal_override_example_install_dir_to_dot to ensure
# INSTALL_EXAMPLEDIR does not interfere.
- # Allow customizing the installation path of the examples. Will be used in CI.
- if(QT_INTERNAL_EXAMPLES_INSTALL_PREFIX)
- set(qt_example_install_prefix "${QT_INTERNAL_EXAMPLES_INSTALL_PREFIX}")
- else()
- set(qt_example_install_prefix "${CMAKE_INSTALL_PREFIX}/${INSTALL_EXAMPLESDIR}")
- endif()
- file(TO_CMAKE_PATH "${qt_example_install_prefix}" qt_example_install_prefix)
-
- set(example_install_prefix "${qt_example_install_prefix}/${example_rel_path}")
+ qt_internal_get_example_install_path(example_install_path "${subdir}")
set(ep_binary_dir "${CMAKE_CURRENT_BINARY_DIR}/${subdir}")
@@ -1324,7 +1381,7 @@ function(qt_internal_add_example_external_project 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_prefix}"
+ INSTALL_DIR "${example_install_path}"
INSTALL_COMMAND ""
${build_command}
TEST_COMMAND ""
@@ -1378,6 +1435,54 @@ execute_process(
endfunction()
+function(qt_internal_install_example_sources subdir)
+ set(options "")
+ set(single_args NAME REPO_NAME)
+ set(multi_args "")
+
+ cmake_parse_arguments(PARSE_ARGV 1 arg "${options}" "${single_args}" "${multi_args}")
+
+ qt_internal_get_examples_sources_install_path(example_install_path "${subdir}")
+
+ # The trailing slash is important to avoid duplicate nested directory names.
+ set(example_source_dir "${subdir}/")
+
+ # Allow controlling whether sources should be part of the default install target.
+ if(QT_INSTALL_EXAMPLES_SOURCES_BY_DEFAULT)
+ set(exclude_from_all "")
+ else()
+ set(exclude_from_all "EXCLUDE_FROM_ALL")
+ endif()
+
+ # Create an install component for all example sources. Can also be part of the default
+ # install target if EXCLUDE_FROM_ALL is not passed.
+ install(
+ DIRECTORY "${example_source_dir}"
+ DESTINATION "${example_install_path}"
+ COMPONENT "examples_sources"
+ USE_SOURCE_PERMISSIONS
+ ${exclude_from_all}
+ )
+
+ # Also create a specific install component just for this repo's examples.
+ install(
+ DIRECTORY "${example_source_dir}"
+ DESTINATION "${example_install_path}"
+ COMPONENT "examples_sources_${arg_REPO_NAME}"
+ USE_SOURCE_PERMISSIONS
+ EXCLUDE_FROM_ALL
+ )
+
+ # Also create a specific install component just for the current example's sources.
+ install(
+ DIRECTORY "${example_source_dir}"
+ DESTINATION "${example_install_path}"
+ COMPONENT "examples_sources_${arg_NAME}"
+ USE_SOURCE_PERMISSIONS
+ EXCLUDE_FROM_ALL
+ )
+endfunction()
+
if ("STANDALONE_TEST" IN_LIST Qt6BuildInternals_FIND_COMPONENTS)
include(${CMAKE_CURRENT_LIST_DIR}/QtStandaloneTestTemplateProject/Main.cmake)
if (NOT PROJECT_VERSION_MAJOR)
diff --git a/cmake/QtProcessConfigureArgs.cmake b/cmake/QtProcessConfigureArgs.cmake
index 7200d7d228..995f64a95f 100644
--- a/cmake/QtProcessConfigureArgs.cmake
+++ b/cmake/QtProcessConfigureArgs.cmake
@@ -907,6 +907,7 @@ endif()
drop_input(make)
drop_input(nomake)
+translate_boolean_input(install-examples-sources QT_INSTALL_EXAMPLES_SOURCES)
check_qt_build_parts(nomake)
check_qt_build_parts(make)
diff --git a/cmake/QtSetup.cmake b/cmake/QtSetup.cmake
index 6d65a3c0d5..7288fc4147 100644
--- a/cmake/QtSetup.cmake
+++ b/cmake/QtSetup.cmake
@@ -364,6 +364,9 @@ enable_testing()
option(QT_BUILD_EXAMPLES "Build Qt examples" OFF)
option(QT_BUILD_EXAMPLES_BY_DEFAULT "Should examples be built as part of the default 'all' target." ON)
+option(QT_INSTALL_EXAMPLES_SOURCES "Install example sources" OFF)
+option(QT_INSTALL_EXAMPLES_SOURCES_BY_DEFAULT
+ "Install example sources as part of the default 'install' target" ON)
# FIXME: Support prefix builds as well QTBUG-96232
if(QT_WILL_INSTALL)
diff --git a/cmake/configure-cmake-mapping.md b/cmake/configure-cmake-mapping.md
index 0ce347b5f5..006fd40f71 100644
--- a/cmake/configure-cmake-mapping.md
+++ b/cmake/configure-cmake-mapping.md
@@ -102,6 +102,7 @@ The following table describes the mapping of configure options to CMake argument
| | | build them separately, after configuration. |
| -nomake <part> | -DQT_BUILD_TESTS=OFF | A way to turn off tools explicitly is missing. |
| | -DQT_BUILD_EXAMPLES=OFF | |
+| -install-examples-sources | -DQT_INSTALL_EXAMPLES_SOURCES=ON | |
| -no-gui | -DFEATURE_gui=OFF | |
| -no-widgets | -DFEATURE_widgets=OFF | |
| -no-dbus | -DFEATURE_dbus=OFF | |
diff --git a/config_help.txt b/config_help.txt
index e056620f71..37b67e088f 100644
--- a/config_help.txt
+++ b/config_help.txt
@@ -201,6 +201,9 @@ Component selection:
[default: libs and examples, also tools if not
cross-building, also tests if -developer-build]
-nomake <part> ....... Exclude <part> from the list of parts to be built.
+ -install-examples-sources Installs examples source code into the Qt prefix
+ Only possible when -make examples is also passed
+ [no]
-gui ................. Build the Qt GUI module and dependencies [yes]
-widgets ............. Build the Qt Widgets module and dependencies [yes]
-no-dbus ............. Do not build the Qt D-Bus module
diff --git a/configure.cmake b/configure.cmake
index 385b460e4d..80c8b38384 100644
--- a/configure.cmake
+++ b/configure.cmake
@@ -1133,6 +1133,13 @@ qt_configure_add_summary_entry(ARGS "sanitize_fuzzer_no_link")
qt_configure_add_summary_entry(ARGS "sanitize_undefined")
qt_configure_end_summary_section() # end of "Sanitizers" section
qt_configure_add_summary_build_parts("Build parts")
+if(QT_INSTALL_EXAMPLES_SOURCES)
+ set(_examples_sources_entry_message "yes")
+else()
+ set(_examples_sources_entry_message "no")
+endif()
+qt_configure_add_summary_entry(ARGS "Install examples sources" TYPE "message"
+ MESSAGE "${_examples_sources_entry_message}")
qt_configure_add_summary_entry(
ARGS "appstore-compliant"
CONDITION APPLE OR ANDROID OR WIN32
diff --git a/qt_cmdline.cmake b/qt_cmdline.cmake
index 0ed1980521..b655bdcc5e 100644
--- a/qt_cmdline.cmake
+++ b/qt_cmdline.cmake
@@ -78,6 +78,7 @@ qt_commandline_option(ltcg TYPE boolean)
qt_commandline_option(intelcet TYPE boolean)
qt_commandline_option(make TYPE addString VALUES examples libs tests tools
benchmarks manual-tests minimal-static-tests)
+qt_commandline_option(install-examples-sources TYPE boolean)
qt_commandline_option(mips_dsp TYPE boolean)
qt_commandline_option(mips_dspr2 TYPE boolean)
qt_commandline_option(nomake TYPE addString VALUES examples tests tools benchmarks