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}) - else() - qt_internal_add_example_external_project(${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() + 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) - file(RELATIVE_PATH example_rel_path - "${QT_EXAMPLE_BASE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/${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 | -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 ....... Exclude 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