summaryrefslogtreecommitdiffstats
path: root/cmake/QtBuildInternals
diff options
context:
space:
mode:
Diffstat (limited to 'cmake/QtBuildInternals')
-rw-r--r--cmake/QtBuildInternals/QtBuildInternalsAndroid.cmake217
-rw-r--r--cmake/QtBuildInternals/QtBuildInternalsConfig.cmake291
-rw-r--r--cmake/QtBuildInternals/QtStandaloneTestTemplateProject/CMakeLists.txt30
3 files changed, 538 insertions, 0 deletions
diff --git a/cmake/QtBuildInternals/QtBuildInternalsAndroid.cmake b/cmake/QtBuildInternals/QtBuildInternalsAndroid.cmake
new file mode 100644
index 0000000000..9830cf9871
--- /dev/null
+++ b/cmake/QtBuildInternals/QtBuildInternalsAndroid.cmake
@@ -0,0 +1,217 @@
+#
+# Android specific functions/macros/properties required for building Qt Modules
+#
+
+define_property(TARGET
+ PROPERTY
+ QT_ANDROID_MODULE_INSTALL_DIR
+ BRIEF_DOCS
+ "Recorded install location for a Qt Module."
+ FULL_DOCS
+ "Recorded install location for a Qt Module. Used by qt_android_dependencies()."
+)
+
+
+define_property(TARGET
+ PROPERTY
+ QT_ANDROID_JAR_DEPENDENCIES
+ BRIEF_DOCS
+ "Qt Module Jar dependencies list."
+ FULL_DOCS
+ "Qt Module Jar dependencies list."
+)
+
+define_property(TARGET
+ PROPERTY
+ QT_ANDROID_BUNDLED_JAR_DEPENDENCIES
+ BRIEF_DOCS
+ "Qt Module Jars that should be bundled with it during packing."
+ FULL_DOCS
+ "Qt Module Jars that should be bundled with it during packing."
+)
+
+define_property(TARGET
+ PROPERTY
+ QT_ANDROID_LIB_DEPENDENCIES
+ BRIEF_DOCS
+ "Qt Module C++ libraries that should be bundled with it during packing."
+ FULL_DOCS
+ "Qt Module C++ libraries that should be bundled with it during packing."
+)
+
+define_property(TARGET
+ PROPERTY
+ QT_ANDROID_LIB_DEPENDENCY_REPLACEMENTS
+ BRIEF_DOCS
+ "Qt Module C++ libraries that can replace libraries declared with the QT_ANDROID_LIB_DEPENDENCIES property."
+ FULL_DOCS
+ "Qt Module C++ libraries that can replace libraries declared with the QT_ANDROID_LIB_DEPENDENCIES property."
+)
+
+define_property(TARGET
+ PROPERTY
+ QT_ANDROID_BUNDLED_FILES
+ BRIEF_DOCS
+ "Qt Module files that need to be bundled during packing."
+ FULL_DOCS
+ "Qt Module files that need to be bundled during packing."
+)
+
+define_property(TARGET
+ PROPERTY
+ QT_ANDROID_PERMISSIONS
+ BRIEF_DOCS
+ "Qt Module android permission list."
+ FULL_DOCS
+ "Qt Module android permission list."
+)
+# Generate Qt Module -android-dependencies.xml required by the
+# androiddeploytoolqt to successfully copy all the plugins and other dependent
+# items into tha APK
+function(qt_android_dependencies target)
+ get_target_property(target_type "${target}" TYPE)
+ if(target_type STREQUAL "INTERFACE_LIBRARY")
+ return()
+ endif()
+
+ get_target_property(arg_JAR_DEPENDENCIES ${target} QT_ANDROID_JAR_DEPENDENCIES)
+ get_target_property(arg_BUNDLED_JAR_DEPENDENCIES ${target} QT_ANDROID_BUNDLED_JAR_DEPENDENCIES)
+ get_target_property(arg_LIB_DEPENDENCIES ${target} QT_ANDROID_LIB_DEPENDENCIES)
+ get_target_property(arg_LIB_DEPENDENCY_REPLACEMENTS ${target} QT_ANDROID_LIB_DEPENDENCY_REPLACEMENTS)
+ get_target_property(arg_BUNDLED_FILES ${target} QT_ANDROID_BUNDLED_FILES)
+ get_target_property(arg_PERMISSIONS ${target} QT_ANDROID_PERMISSIONS)
+ get_target_property(module_plugins ${target} MODULE_PLUGIN_TYPES)
+
+ if ((NOT module_plugins)
+ AND (NOT arg_JAR_DEPENDENCIES)
+ AND (NOT arg_LIB_DEPENDENCY_REPLACEMENTS)
+ AND (NOT arg_LIB_DEPENDENCIES)
+ AND (NOT arg_BUNDLED_JAR_DEPENDENCIES)
+ AND (NOT arg_PERMISSIONS)
+ AND (NOT arg_BUNDLED_FILES))
+ # None of the values were set, so there's nothing to do
+ return()
+ endif()
+
+
+ get_target_property(target_output_name ${target} OUTPUT_NAME)
+ if (NOT target_output_name)
+ set(target_name ${target})
+ else()
+ set(target_name ${target_output_name})
+ endif()
+
+ # mimic qmake's section and string splitting from
+ # mkspecs/feature/qt_android_deps.prf
+ macro(section string delimiter first second)
+ string(FIND ${string} ${delimiter} delimiter_location)
+ if (NOT ${delimiter_location} EQUAL -1)
+ string(SUBSTRING ${string} 0 ${delimiter_location} ${first})
+ math(EXPR delimiter_location "${delimiter_location} + 1")
+ string(SUBSTRING ${string} ${delimiter_location} -1 ${second})
+ else()
+ set(${first} ${string})
+ set(${second} "")
+ endif()
+ endmacro()
+
+ get_target_property(target_bin_dir ${target} BINARY_DIR)
+ set(dependency_file "${target_bin_dir}/${target_name}_${CMAKE_ANDROID_ARCH_ABI}-android-dependencies.xml")
+
+ set(file_contents "<rules><dependencies>\n")
+ string(APPEND file_contents "<lib name=\"${target_name}_${CMAKE_ANDROID_ARCH_ABI}\"><depends>\n")
+
+ # Jar Dependencies
+ if(arg_JAR_DEPENDENCIES)
+ foreach(jar_dependency IN LISTS arg_JAR_DEPENDENCIES)
+ section(${jar_dependency} ":" jar_file init_class)
+ if (init_class)
+ set(init_class "initClass=\"${init_class}\"")
+ endif()
+ file(TO_NATIVE_PATH ${jar_file} jar_file_native)
+ string(APPEND file_contents "<jar file=\"${jar_file_native}\" ${init_class} />\n")
+ endforeach()
+ endif()
+
+ # Bundled Jar Dependencies
+ if(arg_BUNDLED_JAR_DEPENDENCIES)
+ foreach(jar_bundle IN LISTS arg_BUNDLED_JAR_DEPENDENCIES)
+ section(${jar_bundle} ":" bundle_file init_calss)
+ if (init_class)
+ set(init_class "initClass=\"${init_class}\"")
+ endif()
+ file(TO_NATIVE_PATH ${jar_bundle} jar_bundle_native)
+ string(APPEND file_contents "<jar bundling=\"1\" file=\"${jar_bundle_native}\" ${init_class} />\n")
+ endforeach()
+ endif()
+
+ # Lib Dependencies
+ if(arg_LIB_DEPENDENCIES)
+ foreach(lib IN LISTS arg_LIB_DEPENDENCIES)
+ string(REPLACE ".so" "_${CMAKE_ANDROID_ARCH_ABI}.so" lib ${lib})
+ section(${lib} ":" lib_file lib_extends)
+ if (lib_extends)
+ set(lib_extends "extends=\"${lib_extends}\"")
+ endif()
+ file(TO_NATIVE_PATH ${lib_file} lib_file_native)
+ string(APPEND file_contents "<lib file=\"${lib_file_native}\" ${lib_extends} />\n")
+ endforeach()
+ endif()
+
+ # Lib Dependencies Replacements
+ if(arg_LIB_DEPENDENCY_REPLACEMENTS)
+ foreach(lib IN LISTS arg_LIB_DEPENDENCY_REPLACEMENTS)
+ string(REPLACE ".so" "_${CMAKE_ANDROID_ARCH_ABI}.so" lib ${lib})
+ section(${lib} ":" lib_file lib_replacement)
+ if (lib_replacement)
+ file(TO_NATIVE_PATH ${lib_replacement} lib_replacement_native)
+ set(lib_replacement "replaces=\"${lib_replacement_native}\"")
+ endif()
+ file(TO_NATIVE_PATH ${lib_file} lib_file_native)
+ string(APPEND file_contents "<lib file=\"${lib_file_native}\" ${lib_replacement} />\n")
+ endforeach()
+ endif()
+
+
+ # Bundled files
+ if(arg_BUNDLED_FILES)
+ foreach(bundled_file IN LISTS arg_BUNDLED_FILES)
+ file(TO_NATIVE_PATH ${bundled_file} file_native)
+ string(APPEND file_contents "<bundled file=\"${file_native}\" />\n")
+ endforeach()
+ endif()
+
+ # Module plugins
+ if(module_plugins)
+ foreach(plugin IN LISTS module_plugins)
+ string(APPEND file_contents "<bundled file=\"plugins/${plugin}\" />\n")
+ endforeach()
+ endif()
+
+ # Android Permissions
+ if(arg_PERMISSIONS)
+ foreach(permission IN LISTS arg_PERMISSIONS)
+ string(APPEND file_contents "<permission name=\"${permission}\" />\n")
+ endforeach()
+ endif()
+
+ string(APPEND file_contents "</depends></lib>")
+ string(APPEND file_contents "</dependencies></rules>\n")
+ file(WRITE ${dependency_file} ${file_contents})
+
+ get_target_property(target_install_dir ${target} QT_ANDROID_MODULE_INSTALL_DIR)
+ if (NOT target_install_dir)
+ message(SEND_ERROR "qt_android_dependencies: Target ${target} is either not a Qt Module or has no recorded install location")
+ return()
+ endif()
+
+ # Copy file into install directory, required by the androiddeployqt tool.
+ qt_install(FILES
+ ${dependency_file}
+ DESTINATION
+ ${target_install_dir}
+ COMPONENT
+ Devel)
+endfunction()
+
+
diff --git a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake
new file mode 100644
index 0000000000..5648326ca2
--- /dev/null
+++ b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake
@@ -0,0 +1,291 @@
+if (CMAKE_VERSION VERSION_LESS 3.1.0)
+ message(FATAL_ERROR "Qt requires at least CMake version 3.1.0")
+endif()
+
+######################################
+#
+# Macros for building Qt modules
+#
+######################################
+
+if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/QtBuildInternalsExtra.cmake")
+ include(${CMAKE_CURRENT_LIST_DIR}/QtBuildInternalsExtra.cmake)
+endif()
+
+macro(qt_set_up_build_internals_paths)
+ # Set up the paths for the cmake modules located in the prefix dir. Prepend, so the paths are
+ # least important compared to the source dir ones, but more important than command line
+ # provided ones.
+ set(QT_CMAKE_MODULE_PATH "${QT_BUILD_INTERNALS_PATH}/../${QT_CMAKE_EXPORT_NAMESPACE}")
+ list(PREPEND CMAKE_MODULE_PATH "${QT_CMAKE_MODULE_PATH}")
+
+ # Prepend the qtbase source cmake directory to CMAKE_MODULE_PATH,
+ # so that if a change is done in cmake/QtBuild.cmake, it gets automatically picked up when
+ # building qtdeclarative, rather than having to build qtbase first (which will copy
+ # QtBuild.cmake to the build dir). This is similar to qmake non-prefix builds, where the
+ # source qtbase/mkspecs directory is used.
+ if(EXISTS "${QT_SOURCE_TREE}/cmake")
+ list(PREPEND CMAKE_MODULE_PATH "${QT_SOURCE_TREE}/cmake")
+ endif()
+
+ # If the repo has its own cmake modules, include those in the module path.
+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+ list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake")
+ endif()
+
+ # Find the cmake files when doing a standalone tests build.
+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/../cmake")
+ list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../cmake")
+ endif()
+endmacro()
+
+# Set up the build internal paths unless explicitly requested not to.
+if(NOT QT_BUILD_INTERNALS_SKIP_CMAKE_MODULE_PATH_ADDITION)
+ qt_set_up_build_internals_paths()
+endif()
+
+# Define some constants to check for certain platforms, etc.
+# Needs to be loaded before qt_repo_build() to handle require() clauses before even starting a repo
+# build.
+include(QtPlatformSupport)
+
+macro(qt_build_internals_set_up_private_api)
+ # Qt specific setup common for all modules:
+ include(QtSetup)
+ include(FeatureSummary)
+
+ # Optionally include a repo specific Setup module.
+ include(${PROJECT_NAME}Setup OPTIONAL)
+ include(QtRepoSetup OPTIONAL)
+
+ # Find Apple frameworks if needed.
+ qt_find_apple_system_frameworks()
+
+ # Decide whether tools will be built.
+ qt_check_if_tools_will_be_built()
+endmacro()
+
+macro(qt_enable_cmake_languages)
+ include(CheckLanguage)
+ set(__qt_required_language_list C CXX)
+ set(__qt_optional_language_list )
+
+ # https://gitlab.kitware.com/cmake/cmake/-/issues/20545
+ if(APPLE)
+ list(APPEND __qt_optional_language_list OBJC OBJCXX)
+ endif()
+
+ foreach(__qt_lang ${__qt_required_language_list})
+ enable_language(${__qt_lang})
+ endforeach()
+
+ foreach(__qt_lang ${__qt_optional_language_list})
+ check_language(${__qt_lang})
+ if(CMAKE_${__qt_lang}_COMPILER)
+ enable_language(${__qt_lang})
+ endif()
+ endforeach()
+endmacro()
+
+macro(qt_build_repo_begin)
+ qt_build_internals_set_up_private_api()
+ qt_enable_cmake_languages()
+
+ # Add global docs targets that will work both for per-repo builds, and super builds.
+ if(NOT TARGET docs)
+ add_custom_target(docs)
+ add_custom_target(prepare_docs)
+ add_custom_target(generate_docs)
+ add_custom_target(html_docs)
+ add_custom_target(qch_docs)
+ add_custom_target(install_html_docs_docs)
+ add_custom_target(install_qch_docs_docs)
+ add_custom_target(install_docs_docs)
+ endif()
+
+ string(TOLOWER ${PROJECT_NAME} project_name_lower)
+
+ set(qt_docs_target_name docs_${project_name_lower})
+ set(qt_docs_prepare_target_name prepare_docs_${project_name_lower})
+ set(qt_docs_generate_target_name generate_docs_${project_name_lower})
+ set(qt_docs_html_target_name html_docs_${project_name_lower})
+ set(qt_docs_qch_target_name qch_docs_${project_name_lower})
+ set(qt_docs_install_html_target_name install_html_docs_${project_name_lower})
+ set(qt_docs_install_qch_target_name install_qch_docs_${project_name_lower})
+ set(qt_docs_install_target_name install_docs_${project_name_lower})
+
+ add_custom_target(${qt_docs_target_name})
+ add_custom_target(${qt_docs_prepare_target_name})
+ add_custom_target(${qt_docs_generate_target_name})
+ add_custom_target(${qt_docs_qch_target_name})
+ add_custom_target(${qt_docs_html_target_name})
+ add_custom_target(${qt_docs_install_html_target_name})
+ add_custom_target(${qt_docs_install_qch_target_name})
+ add_custom_target(${qt_docs_install_target_name})
+
+ add_dependencies(${qt_docs_generate_target_name} ${qt_docs_prepare_target_name})
+ add_dependencies(${qt_docs_html_target_name} ${qt_docs_generate_target_name})
+ add_dependencies(${qt_docs_install_html_target_name} ${qt_docs_html_target_name})
+ add_dependencies(${qt_docs_install_qch_target_name} ${qt_docs_qch_target_name})
+ add_dependencies(${qt_docs_install_target_name} ${qt_docs_install_html_target_name} ${qt_docs_install_qch_target_name})
+
+ # Make global doc targets depend on the module ones.
+ add_dependencies(docs ${qt_docs_target_name})
+ add_dependencies(prepare_docs ${qt_docs_prepare_target_name})
+ add_dependencies(generate_docs ${qt_docs_generate_target_name})
+ add_dependencies(html_docs ${qt_docs_qch_target_name})
+ add_dependencies(qch_docs ${qt_docs_html_target_name})
+ add_dependencies(install_html_docs_docs ${qt_docs_install_html_target_name})
+ add_dependencies(install_qch_docs_docs ${qt_docs_install_qch_target_name})
+ add_dependencies(install_docs_docs ${qt_docs_install_target_name})
+endmacro()
+
+macro(qt_build_repo_end)
+ if(NOT QT_BUILD_STANDALONE_TESTS)
+ # Delayed actions on some of the Qt targets:
+ include(QtPostProcess)
+
+ # Install the repo-specific cmake find modules.
+ qt_path_join(__qt_repo_install_dir ${QT_CONFIG_INSTALL_DIR} ${INSTALL_CMAKE_NAMESPACE})
+
+ if(NOT PROJECT_NAME STREQUAL "QtBase")
+ if (EXISTS cmake)
+ qt_copy_or_install(DIRECTORY cmake/
+ DESTINATION "${__qt_repo_install_dir}"
+ FILES_MATCHING PATTERN "Find*.cmake"
+ )
+ endif()
+ endif()
+
+ if(NOT QT_SUPERBUILD)
+ qt_print_feature_summary()
+ endif()
+ endif()
+
+ if(NOT QT_SUPERBUILD)
+ qt_print_build_instructions()
+ endif()
+endmacro()
+
+macro(qt_build_repo)
+ qt_build_repo_begin(${ARGN})
+
+ # If testing is enabled, try to find the qtbase Test package.
+ # Do this before adding src, because there might be test related conditions
+ # in source.
+ if (BUILD_TESTING AND NOT QT_BUILD_STANDALONE_TESTS)
+ find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS Test)
+ endif()
+
+ if(NOT QT_BUILD_STANDALONE_TESTS)
+ if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/CMakeLists.txt")
+ add_subdirectory(src)
+ endif()
+
+ if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tools/CMakeLists.txt")
+ add_subdirectory(tools)
+ endif()
+ endif()
+
+ if (BUILD_TESTING AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt")
+ add_subdirectory(tests)
+ if(QT_NO_MAKE_TESTS)
+ set_property(DIRECTORY tests PROPERTY EXCLUDE_FROM_ALL TRUE)
+ endif()
+ endif()
+
+ qt_build_repo_end()
+
+ if (BUILD_EXAMPLES AND BUILD_SHARED_LIBS
+ AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/examples/CMakeLists.txt"
+ AND NOT QT_BUILD_STANDALONE_TESTS)
+ add_subdirectory(examples)
+ if(QT_NO_MAKE_EXAMPLES)
+ set_property(DIRECTORY examples PROPERTY EXCLUDE_FROM_ALL TRUE)
+ endif()
+ endif()
+endmacro()
+
+macro(qt_set_up_standalone_tests_build)
+ # Remove this macro once all usages of it have been removed.
+ # Standalone tests are not handled via the main repo project and qt_build_tests.
+endmacro()
+
+function(qt_get_standalone_tests_confg_files_path out_var)
+ set(path "${QT_CONFIG_INSTALL_DIR}/${INSTALL_CMAKE_NAMESPACE}BuildInternals/StandaloneTests")
+ set("${out_var}" "${path}" PARENT_SCOPE)
+endfunction()
+
+macro(qt_build_tests)
+ if(QT_BUILD_STANDALONE_TESTS)
+ # Find location of TestsConfig.cmake. These contain the modules that need to be
+ # find_package'd when testing.
+ qt_get_standalone_tests_confg_files_path(_qt_build_tests_install_prefix)
+ if(QT_WILL_INSTALL)
+ qt_path_join(_qt_build_tests_install_prefix
+ ${CMAKE_INSTALL_PREFIX} ${_qt_build_tests_install_prefix})
+ endif()
+ include("${_qt_build_tests_install_prefix}/${PROJECT_NAME}TestsConfig.cmake" OPTIONAL)
+
+ # Of course we always need the test module as well.
+ find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS Test)
+
+ # Set language standards after finding Core, because that's when the relevant
+ # feature variables are available, and the call in QtSetup is too early when building
+ # standalone tests, because Core was not find_package()'d yet.
+ qt_set_language_standards()
+ endif()
+
+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/auto/CMakeLists.txt")
+ add_subdirectory(auto)
+ endif()
+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/benchmarks/CMakeLists.txt" AND QT_BUILD_BENCHMARKS)
+ add_subdirectory(benchmarks)
+ endif()
+ if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/manual/CMakeLists.txt")
+ # add_subdirectory(manual) don't build manual tests for now, because qmake doesn't.
+ endif()
+endmacro()
+
+macro(qt_examples_build_begin)
+ # 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.
+ list(APPEND CMAKE_PREFIX_PATH "${QT_BUILD_DIR}")
+ list(APPEND QT_EXAMPLES_CMAKE_PREFIX_PATH "${QT_BUILD_DIR}")
+ # Also make sure the CMake config files do not recreate the already-existing targets
+ set(QT_NO_CREATE_TARGETS TRUE)
+ set(BACKUP_CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ${CMAKE_FIND_ROOT_PATH_MODE_PACKAGE})
+ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE "BOTH")
+endmacro()
+
+macro(qt_examples_build_end)
+ # We use AUTOMOC/UIC/RCC in the examples. Make sure to not fail on a fresh Qt build, that e.g. the moc binary does not exist yet.
+
+ # This function gets all targets below this directory
+ function(get_all_targets _result _dir)
+ get_property(_subdirs DIRECTORY "${_dir}" PROPERTY SUBDIRECTORIES)
+ foreach(_subdir IN LISTS _subdirs)
+ get_all_targets(${_result} "${_subdir}")
+ endforeach()
+ get_property(_sub_targets DIRECTORY "${_dir}" PROPERTY BUILDSYSTEM_TARGETS)
+ set(${_result} ${${_result}} ${_sub_targets} PARENT_SCOPE)
+ endfunction()
+
+ get_all_targets(targets "${CMAKE_CURRENT_SOURCE_DIR}")
+
+ 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()
+ endforeach()
+
+ set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ${BACKUP_CMAKE_FIND_ROOT_PATH_MODE_PACKAGE})
+endmacro()
+
+if (ANDROID)
+ include(${CMAKE_CURRENT_LIST_DIR}/QtBuildInternalsAndroid.cmake)
+endif()
diff --git a/cmake/QtBuildInternals/QtStandaloneTestTemplateProject/CMakeLists.txt b/cmake/QtBuildInternals/QtStandaloneTestTemplateProject/CMakeLists.txt
new file mode 100644
index 0000000000..a486d1a722
--- /dev/null
+++ b/cmake/QtBuildInternals/QtStandaloneTestTemplateProject/CMakeLists.txt
@@ -0,0 +1,30 @@
+cmake_minimum_required(VERSION 3.16)
+project(qt_single_test VERSION 6.0.0 LANGUAGES C CXX ASM)
+
+find_package(Qt6 REQUIRED COMPONENTS BuildInternals)
+
+# Includes QtSetup and friends for private CMake API.
+qt_build_internals_set_up_private_api()
+
+# Find all StandaloneTestsConfig.cmake files, and include them
+# This will find all Qt packages that are required for standalone tests.
+# It will find more packages that needed for a certain test, but will ensure any test can
+# be built.
+qt_get_standalone_tests_confg_files_path(standalone_tests_config_path)
+file(GLOB config_files "${standalone_tests_config_path}/*")
+foreach(file ${config_files})
+ include("${file}")
+endforeach()
+
+# Get the absolute path of the passed-in project dir, relative to the current working directory
+# of the calling script, rather than relative to this source directory.
+# The calling script sets PWD. If not set, just use the passed-in value as-is.
+if(DEFINED PWD)
+ get_filename_component(absolute_project_path "${QT_STANDALONE_TEST_PATH}" ABSOLUTE
+ BASE_DIR "${PWD}")
+else()
+ set(absolute_project_path "${QT_STANDALONE_TEST_PATH}")
+endif()
+
+# Add the test project path as a subdirectory project.
+add_subdirectory("${absolute_project_path}" "build_dir")