diff options
Diffstat (limited to 'cmake')
118 files changed, 7872 insertions, 4018 deletions
diff --git a/cmake/3rdparty/extra-cmake-modules/find-modules/FindEGL.cmake b/cmake/3rdparty/extra-cmake-modules/find-modules/FindEGL.cmake index 0733f1f702..9ac8e2fa0c 100644 --- a/cmake/3rdparty/extra-cmake-modules/find-modules/FindEGL.cmake +++ b/cmake/3rdparty/extra-cmake-modules/find-modules/FindEGL.cmake @@ -128,7 +128,8 @@ check_cxx_source_compiles(" #include <EGL/egl.h> int main(int, char **) { - EGLint x = 0; EGLDisplay dpy = 0; EGLContext ctx = 0; + [[maybe_unused]] EGLint x = 0; + EGLDisplay dpy = 0; EGLContext ctx = 0; eglDestroyContext(dpy, ctx); }" HAVE_EGL) diff --git a/cmake/3rdparty/extra-cmake-modules/find-modules/FindXCB.cmake b/cmake/3rdparty/extra-cmake-modules/find-modules/FindXCB.cmake index dc60289401..26b9bf8963 100644 --- a/cmake/3rdparty/extra-cmake-modules/find-modules/FindXCB.cmake +++ b/cmake/3rdparty/extra-cmake-modules/find-modules/FindXCB.cmake @@ -137,6 +137,9 @@ foreach(_comp ${XCB_known_components}) set(XCB_${_comp}_pkg_config "xcb-${_lc_comp}") set(XCB_${_comp}_lib "xcb-${_lc_comp}") set(XCB_${_comp}_header "xcb/${_lc_comp}.h") + if(USE_XCB_${_comp}_STATIC) + set(XCB_${_comp}_lib "lib${XCB_${_comp}_lib}.a") + endif() endforeach() # exceptions set(XCB_XCB_component_deps) diff --git a/cmake/3rdparty/extra-cmake-modules/modules/ECMEnableSanitizers.cmake b/cmake/3rdparty/extra-cmake-modules/modules/ECMEnableSanitizers.cmake index dcdb8621d3..15610587bd 100644 --- a/cmake/3rdparty/extra-cmake-modules/modules/ECMEnableSanitizers.cmake +++ b/cmake/3rdparty/extra-cmake-modules/modules/ECMEnableSanitizers.cmake @@ -158,7 +158,15 @@ if (ECM_ENABLE_SANITIZERS) endif() set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${XSAN_COMPILE_FLAGS}" ) if(CMAKE_CXX_COMPILER_ID MATCHES "GNU") - link_libraries(${XSAN_LINKER_FLAGS}) + string(JOIN "" linker_flags + "$<" + "$<NOT:" + "$<BOOL:$<TARGET_PROPERTY:SKIP_SANITIZER>>" + ">:" + "${XSAN_LINKER_FLAGS}" + ">" + ) + link_libraries("${linker_flags}") endif() if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") string(REPLACE "-Wl,--no-undefined" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") diff --git a/cmake/CODESTYLE.md b/cmake/CODESTYLE.md new file mode 100644 index 0000000000..6dfc676d16 --- /dev/null +++ b/cmake/CODESTYLE.md @@ -0,0 +1,197 @@ +# Unofficial Qt CMake Coding Style + +CMake scripts are a bit of a wild west. Depending on when the code was written, there were +different conventions as well as syntax facilities available. It's also unfortunate that there is +no agreed upon CMake code formatter similar to clang-format. +https://github.com/cheshirekow/cmake_format exists, but appears abandoned. We might look into +using it at some point. + +It's hard to convince people to use a certain code style for a language like CMake. + +Nevertheless this short document aims to be a guideline for formatting CMake code within the Qt +project. + +## Common conventions + +* When in doubt, prefer the local code conventions of the function or file you are editing. +* Prefer functions over macros (macros have certain problems with parameter escaping) +* Prefix macro local variables to avoid naming collisions + +## Case Styles + +For CMake identifiers we refer to following case styles in the text below. + +| Case style name | Example identifier | +|-------------------|----------------------------| +| Snake case | `moc_options` | +| Shouty case | `INTERFACE_LINK_LIBRARIES` | +| Pascal case | `QmlModels` | + +## Indentation + +* When in doubt, follow local indentation +* Prefer indenting with 4 spaces, e.g. + +``` +if(1) + if(2) + message("3") + endif() +endif() + +``` + +## Variables + +* local variables inside a function should be snake cased => `list_of_arguments` +* local variables inside a macro should be snake cased and have a unique prefix => + `__qt_list_of_packages` + * If your macro is recursively called, you might need to prepend a dynamically computed prefix + to avoid overriding the same variable in nested calls => + `__qt_${dependency_name}_list_of_packages` +* cache variables should be shouty cased => `BUILD_SHARED_LIBS` + * Qt cache variables should be prefixed with `QT_` + +## Properties + +Currently the Qt property naming is a bit of a mess. The upstream issue +https://gitlab.kitware.com/cmake/cmake/-/issues/19261 mentions that properties prefixed with +`INTERFACE_` are reserved for CMake use. + +Prefer to use snake case for new property names. + +* Most upstream CMake properties are shouty cased => `INTERFACE_LINK_LIBRARIES` +* Properties created in Qt 5 times follow the same CMake convention => `QT_ENABLED_PUBLIC_FEATURES` + No such new properties should be added. +* New Qt properties should be snake cased and prefixed with `qt_` => `qt_qmake_module_name` +* Other internal Qt properties should be snake cased and prefixed with `_qt_` => `_qt_target_deps` + +## Functions + +* Function names should be snake cased => `qt_add_module` + * public Qt functions should be prefixed with `qt_` + * internal Qt functions should be prefixed with `qt_internal_` + * internal Qt functions that live in public CMake API files should be prefixed with + `_qt_internal_` + * some internal functions that live in public CMake API files are prefixed with + `__qt_internal_`, prefer not to introduce such functions for now +* If a public function takes more than 1 parameter, consider using `cmake_parse_arguments` +* If an internal Qt function takes more than 1 parameter, consider using `qt_parse_all_arguments` +* Public functions should usually have both a version-full and version-less name => `qt_add_plugin` + and `qt6_add_plugin` + +### Macros + +* Try to avoid macros where a function can be used instead + * A common case when a macro can not be avoided is when it wraps a CMake macro e.g + `find_package` => `qt_find_package` +* Macro names should be snake cased => `qt_find_package` +* Prefix macro local variables to avoid naming collisions => `__qt_find_package_arguments` + +## Commands + +Command names in CMake are case-insensitive, therefore: +* Only use lower case command names e.g `add_executable(app main.cpp)` not `ADD_EXECUTABLE(app + main.cpp)` +* Command flags / options are usually shouty cased => `file(GENERATE OUTPUT "foo" CONTENT "bar")` + +## 'If' command + +* Keep the parenthesis next to the `if()`, so don't write `if ()`. `if` is a command name and like + other command names e.g. `find_package(foo bar)` the parenethesis are next to the name. + +* To check that a variable is an empty string (and not just a falsy value, think Javascript's + `foo === ""`) use the following snippet +``` +if(var_name STREQUAL "") + message("${var_name}") +endif() +``` + +If you are not sure if the variable is defined, make sure to evaluate the variable name +``` +if("${var_name}" STREQUAL "") + message("${var_name}") +endif() +``` + + +* Falsy values are `0`, `OFF`, `NO`, `FALSE`, `N`, `IGNORE`, `NOTFOUND`, the empty string `""`, or + a string ending in `-NOTFOUND`. To check that a variable is NOT falsy use the first suggested + code snippet +``` +# Nice and clean +if(var_name) + message("${var_name}") +endif() + +# Wrong, can lead to problems due to double variable evaluation +if(${var_name}) + message("${var_name}") +endif() + +# Incorrect if you want to check for all falsy values. This only checks for string emptiness. +if("${var_name}" STREQUAL "") + message("${var_name}") +endif() +``` + +* To check if a variable is defined, use +``` +if(DEFINED var_name) +endif() +``` + +* To compare a defined variable's contents to a string, use +``` +if(var_name STREQUAL "my_string") +endif() +``` + +* To compare a defined variable's contents to another defined variable's contents, use +``` +if(var_name STREQUAL var_name_2) +endif() +``` + +* To compare a possibly undefined variable make sure to explicitly evaluate it first +``` +if("${var_name}" STREQUAL "my_string") +endif() + +if("${var_name}" STREQUAL "${var_name_2}") +endif() +``` + +## find_package + +* Inside Qt module projects, use the private Qt-specific `qt_find_package` macro to look for + dependencies. + * Make sure to specify the `PROVIDED_TARGETS` option to properly register 3rd party target + dependencies with Qt's internal build system. +* Inside examples / projects (which only use public CMake API) use the regular `find_package` + command. + +## CMake Target names + +* Qt target names should be pascal cased => `QuickParticles`. +* When Qt is installed, the Qt CMake targets are put inside the `Qt6::` namespace. Associated + versionless targets in the `Qt::` namespace are usually automatically created by appropriate + functions (`qt_internal_add_module`, `qt_internal_add_tool`) +* Wrapper 3rd party system libraries usually repeat the target name as the namespace e.g. + ```WrapSystemHarfbuzz::WrapSystemHarfbuzz``` + +## Finding 3rd party libraries via Find modules (FindWrapFoo.cmake) + +qmake-Qt uses `configure.json` and `configure.pri` and `QT_LIBS_FOO` variables to implement a +mechnism that searches for system 3rd party libraries. + +The equivalent CMake mechanism are Find modules (and CMake package Config files; not to be confused +with pkg-config). Usually Qt provides wrapper Find modules that use any available upstream Find +modules or Config package files. + +The naming convention for the files are: + +* `FindWrapFoo.cmake` => `FindWrapPCRE2.cmake` +* `FindWrapSystemFoo.cmake` => `FindWrapSystemPCRE2.cmake` + diff --git a/cmake/FindGSSAPI.cmake b/cmake/FindGSSAPI.cmake index 91ad99c069..44594941e3 100644 --- a/cmake/FindGSSAPI.cmake +++ b/cmake/FindGSSAPI.cmake @@ -23,7 +23,7 @@ set(gssapi_library_names gssapi # FreeBSD gssapi_krb5 ) -if(VCPKG_TARGET_TRIPLET AND APPLE) +if(APPLE) list(REMOVE_ITEM gssapi_library_names "gssapi_krb5") endif() diff --git a/cmake/FindMimer.cmake b/cmake/FindMimer.cmake index 5bc8d95a5a..5cbc6e6907 100644 --- a/cmake/FindMimer.cmake +++ b/cmake/FindMimer.cmake @@ -5,18 +5,62 @@ # FindMimer # --------- # Try to locate the Mimer SQL client library +if(NOT DEFINED MimerSQL_ROOT) + if(DEFINED ENV{MIMERSQL_DEV_ROOT}) + set(MimerSQL_ROOT "$ENV{MIMERSQL_DEV_ROOT}") + endif() +endif() -find_package(PkgConfig QUIET) -pkg_check_modules(PC_Mimer QUIET mimctrl) +if(NOT DEFINED MimerSQL_ROOT) + find_package(PkgConfig QUIET) +endif() +if(PkgConfig_FOUND AND NOT DEFINED MimerSQL_ROOT) + pkg_check_modules(PC_Mimer QUIET mimcontrol) + set(MimerSQL_include_dir_hints "${PC_MimerSQL_INCLUDEDIR}") + set(MimerSQL_library_hints "${PC_MimerSQL_LIBDIR}") +else() + if(DEFINED MimerSQL_ROOT) + if(WIN32) + set(MimerSQL_include_dir_hints "${MimerSQL_ROOT}\\include") + if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86|X86)$") + set(MimerSQL_library_hints "${MimerSQL_ROOT}\\lib\\x86") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(amd64|AMD64)$") + set(MimerSQL_library_hints "${MimerSQL_ROOT}\\lib\\amd64") + else() + set(MimerSQL_library_hints "") + endif() + else() + set(MimerSQL_include_dir_hints "${MimerSQL_ROOT}/include") + set(MimerSQL_library_hints "${MimerSQL_ROOT}/lib") + endif() + else() + if(WIN32) + set(MimerSQL_include_dir_hints "C:\\MimerSQLDev\\include") + if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86|X86)$") + set(MimerSQL_library_hints "C:\\MimerSQLDev\\lib\\x86") + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(amd64|AMD64)$") + set(MimerSQL_library_hints "C:\\MimerSQLDev\\lib\\amd64") + else() + set(MimerSQL_library_hints "") + endif() + elseif(APPLE AND ${CMAKE_SYSTEM_NAME} MATCHES "Darwin") + set(MimerSQL_library_hints "/usr/local/lib") + set(MimerSQL_include_dir_hints "/usr/local/include") + else() + set(MimerSQL_include_dir_hints "") + set(MimerSQL_library_hints "") + endif() + endif() +endif() find_path(Mimer_INCLUDE_DIR NAMES mimerapi.h - HINTS ${PC_Mimer_INCLUDEDIR}) + HINTS ${MimerSQL_include_dir_hints}) if(WIN32) - if("$ENV{PROCESSOR_ARCHITECTURE}" STREQUAL "x86") + if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86|X86)$") set(MIMER_LIBS_NAMES mimapi32) - else() + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(amd64|AMD64)$") set(MIMER_LIBS_NAMES mimapi64) endif() else() @@ -25,7 +69,7 @@ endif() find_library(Mimer_LIBRARIES NAMES ${MIMER_LIBS_NAMES} - HINTS ${PC_Mimer_LIBDIR}) + HINTS ${MimerSQL_library_hints}) include(FindPackageHandleStandardArgs) diff --git a/cmake/FindPostgreSQL.cmake b/cmake/FindPostgreSQL.cmake index ca3a454527..a61bec9337 100644 --- a/cmake/FindPostgreSQL.cmake +++ b/cmake/FindPostgreSQL.cmake @@ -99,6 +99,9 @@ endif() cmake_policy(PUSH) cmake_policy(SET CMP0057 NEW) # if IN_LIST +if(POLICY CMP0159) + cmake_policy(SET CMP0159 NEW) +endif() set(PostgreSQL_INCLUDE_PATH_DESCRIPTION "top-level directory containing the PostgreSQL include directories. E.g /usr/local/include/PostgreSQL/8.4 or C:/Program Files/PostgreSQL/8.4/include") set(PostgreSQL_INCLUDE_DIR_MESSAGE "Set the PostgreSQL_INCLUDE_DIR cmake cache entry to the ${PostgreSQL_INCLUDE_PATH_DESCRIPTION}") diff --git a/cmake/FindRenderDoc.cmake b/cmake/FindRenderDoc.cmake new file mode 100644 index 0000000000..02970fc7d6 --- /dev/null +++ b/cmake/FindRenderDoc.cmake @@ -0,0 +1,20 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +if(WIN32 OR UNIX) + find_path(RenderDoc_INCLUDE_DIR + NAMES renderdoc_app.h) +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(RenderDoc + DEFAULT_MSG + RenderDoc_INCLUDE_DIR) + +mark_as_advanced(RenderDoc_INCLUDE_DIR) + +if(RenderDoc_FOUND AND NOT TARGET RenderDoc::RenderDoc) + add_library(RenderDoc::RenderDoc INTERFACE IMPORTED) + set_target_properties(RenderDoc::RenderDoc PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${RenderDoc_INCLUDE_DIR}") +endif() diff --git a/cmake/FindWrapBrotli.cmake b/cmake/FindWrapBrotli.cmake index e2d7b564f6..d254f4292e 100644 --- a/cmake/FindWrapBrotli.cmake +++ b/cmake/FindWrapBrotli.cmake @@ -20,6 +20,13 @@ if (unofficial-brotli_FOUND) set(WrapBrotli_FOUND ON) else() + get_cmake_property(__packages_not_found PACKAGES_NOT_FOUND) + if(__packages_not_found) + list(REMOVE_ITEM __packages_not_found unofficial-brotli) + set_property(GLOBAL PROPERTY PACKAGES_NOT_FOUND "${__packages_not_found}") + endif() + unset(__packages_not_found) + find_package(PkgConfig QUIET) if (PKG_CONFIG_FOUND) pkg_check_modules(libbrotlidec QUIET IMPORTED_TARGET "libbrotlidec") diff --git a/cmake/FindWrapOpenGL.cmake b/cmake/FindWrapOpenGL.cmake index c4e0fe5d3e..7295a159ca 100644 --- a/cmake/FindWrapOpenGL.cmake +++ b/cmake/FindWrapOpenGL.cmake @@ -17,14 +17,18 @@ if (OpenGL_FOUND) add_library(WrapOpenGL::WrapOpenGL INTERFACE IMPORTED) if(APPLE) + # CMake 3.27 and older: # On Darwin platforms FindOpenGL sets IMPORTED_LOCATION to the absolute path of the library # within the framework. This ends up as an absolute path link flag, which we don't want, # because that makes our .prl files un-relocatable. # Extract the framework path instead, and use that in INTERFACE_LINK_LIBRARIES, - # which CMake ends up transforming into a reloctable -framework flag. + # which CMake ends up transforming into a relocatable -framework flag. # See https://gitlab.kitware.com/cmake/cmake/-/issues/20871 for details. + # + # CMake 3.28 and above: + # IMPORTED_LOCATION is the absolute path the the OpenGL.framework folder. get_target_property(__opengl_fw_lib_path OpenGL::GL IMPORTED_LOCATION) - if(__opengl_fw_lib_path) + if(__opengl_fw_lib_path AND NOT __opengl_fw_lib_path MATCHES "/([^/]+)\\.framework$") get_filename_component(__opengl_fw_path "${__opengl_fw_lib_path}" DIRECTORY) endif() @@ -46,6 +50,16 @@ if (OpenGL_FOUND) else() target_link_libraries(WrapOpenGL::WrapOpenGL INTERFACE OpenGL::GL) endif() +elseif(UNIX AND NOT APPLE AND NOT CMAKE_SYSTEM_NAME STREQUAL "Integrity") + # Requesting only the OpenGL component ensures CMake does not mark the package as + # not found if neither GLX nor libGL are available. This allows finding OpenGL + # on an X11-less Linux system. + find_package(OpenGL ${WrapOpenGL_FIND_VERSION} COMPONENTS OpenGL) + if (OpenGL_FOUND) + set(WrapOpenGL_FOUND ON) + add_library(WrapOpenGL::WrapOpenGL INTERFACE IMPORTED) + target_link_libraries(WrapOpenGL::WrapOpenGL INTERFACE OpenGL::OpenGL) + endif() endif() include(FindPackageHandleStandardArgs) diff --git a/cmake/FindWrapResolv.cmake b/cmake/FindWrapResolv.cmake index a0d1e7864c..1afd253ee8 100644 --- a/cmake/FindWrapResolv.cmake +++ b/cmake/FindWrapResolv.cmake @@ -31,7 +31,7 @@ check_cxx_source_compiles(" int main(int, char **argv) { - res_state statep; + res_state statep = 0; int n = res_nmkquery(statep, 0, argv[1], 0, 0, NULL, 0, NULL, NULL, 0); n = res_nsend(statep, NULL, 0, NULL, 0); n = dn_expand(NULL, NULL, NULL, NULL, 0); diff --git a/cmake/FindWrapSystemHarfbuzz.cmake b/cmake/FindWrapSystemHarfbuzz.cmake index 07b3405bc0..d989c93e44 100644 --- a/cmake/FindWrapSystemHarfbuzz.cmake +++ b/cmake/FindWrapSystemHarfbuzz.cmake @@ -28,6 +28,13 @@ if(harfbuzz_FOUND AND TARGET "${__harfbuzz_target_name}") if(harfbuzz_VERSION) set(WrapSystemHarfbuzz_VERSION "${harfbuzz_VERSION}") endif() +else() + get_cmake_property(__packages_not_found PACKAGES_NOT_FOUND) + if(__packages_not_found) + list(REMOVE_ITEM __packages_not_found harfbuzz) + set_property(GLOBAL PROPERTY PACKAGES_NOT_FOUND "${__packages_not_found}") + endif() + unset(__packages_not_found) endif() if(__harfbuzz_broken_config_file OR NOT __harfbuzz_found) diff --git a/cmake/FindWrapSystemPCRE2.cmake b/cmake/FindWrapSystemPCRE2.cmake index 61e0d2fb5b..ce00252c8c 100644 --- a/cmake/FindWrapSystemPCRE2.cmake +++ b/cmake/FindWrapSystemPCRE2.cmake @@ -16,6 +16,13 @@ if(PCRE2_FOUND AND TARGET "${__pcre2_target_name}") if(PCRE2_VERSION) set(WrapSystemPCRE2_VERSION "${PCRE2_VERSION}") endif() +else() + get_cmake_property(__packages_not_found PACKAGES_NOT_FOUND) + if(__packages_not_found) + list(REMOVE_ITEM __packages_not_found PCRE2) + set_property(GLOBAL PROPERTY PACKAGES_NOT_FOUND "${__packages_not_found}") + endif() + unset(__packages_not_found) endif() if(NOT __pcre2_found) diff --git a/cmake/FindWrapSystemZLIB.cmake b/cmake/FindWrapSystemZLIB.cmake index 5db43db626..f9a8f004ec 100644 --- a/cmake/FindWrapSystemZLIB.cmake +++ b/cmake/FindWrapSystemZLIB.cmake @@ -28,5 +28,13 @@ if(ZLIB_FOUND) endif() endif() +if(ZLIB_VERSION) + set(WrapSystemZLIB_VERSION "${ZLIB_VERSION}") +elseif(ZLIB_VERSION_STRING) + set(WrapSystemZLIB_VERSION "${ZLIB_VERSION_STRING}") +endif() + include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(WrapSystemZLIB DEFAULT_MSG WrapSystemZLIB_FOUND) +find_package_handle_standard_args(WrapSystemZLIB + REQUIRED_VARS WrapSystemZLIB_FOUND + VERSION_VAR WrapSystemZLIB_VERSION) diff --git a/cmake/FindWrapVulkanHeaders.cmake b/cmake/FindWrapVulkanHeaders.cmake index 79f5dfc975..92510ae000 100644 --- a/cmake/FindWrapVulkanHeaders.cmake +++ b/cmake/FindWrapVulkanHeaders.cmake @@ -48,6 +48,26 @@ if(Vulkan_INCLUDE_DIR) target_include_directories(WrapVulkanHeaders::WrapVulkanHeaders INTERFACE ${__qt_molten_vk_homebrew_include_path}) endif() + + # Check for homebrew vulkan-headers folder structure + # If instead of molten-vk folder, CMAKE_PREFIX_PATH points to Homebrew's + # vulkan-headers installation, then we will not be able to find molten-vk + # headers. If we assume that user has installed the molten-vk formula as + # well, then we might have a chance to pick it up like this. + if(Vulkan_INCLUDE_DIR MATCHES "/homebrew/Cellar/") + set(__qt_standalone_molten_vk_homebrew_include_path + "${Vulkan_INCLUDE_DIR}/../../../../opt/molten-vk/include") + else() + set(__qt_standalone_molten_vk_homebrew_include_path + "${Vulkan_INCLUDE_DIR}/../../molten-vk/include") + endif() + get_filename_component( + __qt_standalone_molten_vk_homebrew_include_path + "${__qt_standalone_molten_vk_homebrew_include_path}" ABSOLUTE) + if(EXISTS "${__qt_standalone_molten_vk_homebrew_include_path}") + target_include_directories(WrapVulkanHeaders::WrapVulkanHeaders INTERFACE + ${__qt_standalone_molten_vk_homebrew_include_path}) + endif() endif() endif() diff --git a/cmake/FindWrapZSTD.cmake b/cmake/FindWrapZSTD.cmake index fb424236b8..310f6cf960 100644 --- a/cmake/FindWrapZSTD.cmake +++ b/cmake/FindWrapZSTD.cmake @@ -25,20 +25,30 @@ find_package(zstd CONFIG QUIET) include(FindPackageHandleStandardArgs) -if(TARGET zstd::libzstd_static OR TARGET zstd::libzstd_shared) +if(TARGET zstd::libzstd_static OR TARGET zstd::libzstd_shared OR TARGET zstd::libzstd) find_package_handle_standard_args(WrapZSTD REQUIRED_VARS zstd_VERSION VERSION_VAR zstd_VERSION) if(TARGET zstd::libzstd_shared) set(zstdtargetsuffix "_shared") + elseif(TARGET zstd::libzstd) + set(zstdtargetsuffix "") else() set(zstdtargetsuffix "_static") endif() + if(NOT TARGET WrapZSTD::WrapZSTD) add_library(WrapZSTD::WrapZSTD INTERFACE IMPORTED) set_target_properties(WrapZSTD::WrapZSTD PROPERTIES INTERFACE_LINK_LIBRARIES "zstd::libzstd${zstdtargetsuffix}") endif() else() + get_cmake_property(__packages_not_found PACKAGES_NOT_FOUND) + if(__packages_not_found) + list(REMOVE_ITEM __packages_not_found zstd) + set_property(GLOBAL PROPERTY PACKAGES_NOT_FOUND "${__packages_not_found}") + endif() + unset(__packages_not_found) + find_package(PkgConfig QUIET) pkg_check_modules(PC_ZSTD QUIET "libzstd") diff --git a/cmake/Qt3rdPartyLibraryConfig.cmake.in b/cmake/Qt3rdPartyLibraryConfig.cmake.in index 3f59d212d8..869c67443f 100644 --- a/cmake/Qt3rdPartyLibraryConfig.cmake.in +++ b/cmake/Qt3rdPartyLibraryConfig.cmake.in @@ -1,3 +1,6 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + @PACKAGE_INIT@ cmake_minimum_required(VERSION @min_new_policy_version@...@max_new_policy_version@) @@ -20,7 +23,11 @@ if (NOT QT_NO_CREATE_TARGETS) include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@Targets.cmake") include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@AdditionalTargetInfo.cmake") if(NOT QT_NO_CREATE_VERSIONLESS_TARGETS) - include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@VersionlessTargets.cmake") + if(CMAKE_VERSION VERSION_LESS 3.18 OR QT_USE_OLD_VERSION_LESS_TARGETS) + include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@VersionlessTargets.cmake") + else() + include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@VersionlessAliasTargets.cmake") + endif() endif() endif() diff --git a/cmake/Qt3rdPartyLibraryHelpers.cmake b/cmake/Qt3rdPartyLibraryHelpers.cmake index dca360d074..924db182be 100644 --- a/cmake/Qt3rdPartyLibraryHelpers.cmake +++ b/cmake/Qt3rdPartyLibraryHelpers.cmake @@ -310,6 +310,7 @@ function(qt_internal_add_3rdparty_library target) qt_internal_export_modern_cmake_config_targets_file( TARGETS ${target} EXPORT_NAME_PREFIX ${INSTALL_CMAKE_NAMESPACE}${target} + CONFIG_BUILD_DIR "${config_build_dir}" CONFIG_INSTALL_DIR "${config_install_dir}" ) diff --git a/cmake/QtAndroidHelpers.cmake b/cmake/QtAndroidHelpers.cmake index 857a029991..0743fe41a9 100644 --- a/cmake/QtAndroidHelpers.cmake +++ b/cmake/QtAndroidHelpers.cmake @@ -5,87 +5,88 @@ # 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_internal_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." -) - -define_property(TARGET - PROPERTY - QT_ANDROID_FEATURES - BRIEF_DOCS - "Qt Module android feature list." - FULL_DOCS - "Qt Module android feature list." -) - -define_property(TARGET - PROPERTY - QT_ANDROID_ABIS - BRIEF_DOCS - "List of ABIs that the target packages are built with." - FULL_DOCS - "List of ABIs that the target packages are built with." -) +macro(qt_internal_setup_android_target_properties) + 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_internal_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." + ) + + define_property(TARGET + PROPERTY + QT_ANDROID_FEATURES + BRIEF_DOCS + "Qt Module android feature list." + FULL_DOCS + "Qt Module android feature list." + ) + + define_property(TARGET + PROPERTY + QT_ANDROID_ABIS + BRIEF_DOCS + "List of ABIs that the target packages are built with." + FULL_DOCS + "List of ABIs that the target packages are built with." + ) +endmacro() function(qt_internal_android_dependencies_content target file_content_out) get_target_property(arg_JAR_DEPENDENCIES ${target} QT_ANDROID_JAR_DEPENDENCIES) @@ -252,7 +253,8 @@ function(qt_internal_android_dependencies target) # Module plugins if(module_plugin_types) foreach(plugin IN LISTS module_plugin_types) - string(APPEND file_contents "<bundled file=\"${INSTALL_PLUGINSDIR}/${plugin}\" />\n") + string(APPEND file_contents + "<bundled file=\"${INSTALL_PLUGINSDIR}/${plugin}\" type=\"plugin_dir\"/>\n") endforeach() endif() diff --git a/cmake/QtAppHelpers.cmake b/cmake/QtAppHelpers.cmake index 4be5c7d5fe..f0dbd110ab 100644 --- a/cmake/QtAppHelpers.cmake +++ b/cmake/QtAppHelpers.cmake @@ -35,8 +35,6 @@ function(qt_internal_add_app target) "a future Qt version. Use the LIBRARIES option instead.") endif() - qt_internal_library_deprecation_level(deprecation_define) - if(arg_NO_UNITY_BUILD) set(arg_NO_UNITY_BUILD "NO_UNITY_BUILD") else() @@ -53,6 +51,7 @@ function(qt_internal_add_app target) ${arg_NO_UNITY_BUILD} ${forward_install_dir} SOURCES ${arg_SOURCES} + NO_PCH_SOURCES ${arg_NO_PCH_SOURCES} NO_UNITY_BUILD_SOURCES ${arg_NO_UNITY_BUILD_SOURCES} INCLUDE_DIRECTORIES ${arg_INCLUDE_DIRECTORIES} diff --git a/cmake/QtAutoDetect.cmake b/cmake/QtAutoDetect.cmake index a994fc5e55..464e8e900b 100644 --- a/cmake/QtAutoDetect.cmake +++ b/cmake/QtAutoDetect.cmake @@ -1,499 +1,5 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -# -# Collection of auto detection routines to improve the user experience when -# building Qt from source. -# -# Make sure to not run detection when building standalone tests, because the detection was already -# done when initially configuring qtbase. - -function(qt_internal_ensure_static_qt_config) - if(NOT DEFINED BUILD_SHARED_LIBS) - set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build Qt statically or dynamically" FORCE) - endif() - - if(BUILD_SHARED_LIBS) - message(FATAL_ERROR - "Building Qt for ${CMAKE_SYSTEM_NAME} as shared libraries is not supported.") - endif() -endfunction() - -include("${CMAKE_CURRENT_LIST_DIR}/QtPublicWasmToolchainHelpers.cmake") -function(qt_auto_detect_wasm) - if("${QT_QMAKE_TARGET_MKSPEC}" STREQUAL "wasm-emscripten" OR "${QT_QMAKE_TARGET_MKSPEC}" STREQUAL "wasm-emscripten-64") - if (NOT DEFINED ENV{EMSDK}) - message(FATAL_ERROR - "Can't find an Emscripten SDK! Make sure the EMSDK environment variable is " - "available by activating and sourcing the emscripten sdk. Also ensure emcc is in " - "your path.") - endif() - if(NOT DEFINED QT_AUTODETECT_WASM_IS_DONE) - message(STATUS "Extracting Emscripten SDK info from EMSDK env var: $ENV{EMSDK}") - __qt_internal_get_emroot_path_suffix_from_emsdk_env(EMROOT_PATH) - - __qt_internal_query_emsdk_version("${EMROOT_PATH}" TRUE CMAKE_EMSDK_REGEX_VERSION) - set(EMCC_VERSION "${CMAKE_EMSDK_REGEX_VERSION}" CACHE STRING INTERNAL FORCE) - - if(NOT DEFINED BUILD_SHARED_LIBS) - qt_internal_ensure_static_qt_config() - endif() - - # Find toolchain file - if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) - __qt_internal_get_emscripten_cmake_toolchain_file_path_from_emsdk_env( - "${EMROOT_PATH}" wasm_toolchain_file) - set(CMAKE_TOOLCHAIN_FILE "${wasm_toolchain_file}" CACHE STRING "" FORCE) - endif() - - if(EXISTS "${CMAKE_TOOLCHAIN_FILE}") - message(STATUS - "Emscripten ${EMCC_VERSION} toolchain file detected at ${CMAKE_TOOLCHAIN_FILE}") - else() - __qt_internal_show_error_no_emscripten_toolchain_file_found_when_building_qt() - endif() - - __qt_internal_get_emcc_recommended_version(recommended_version) - set(QT_EMCC_RECOMMENDED_VERSION "${recommended_version}" CACHE STRING INTERNAL FORCE) - - set(QT_AUTODETECT_WASM_IS_DONE TRUE CACHE BOOL "") - else() - message(STATUS - "Reusing cached Emscripten ${EMCC_VERSION} toolchain file detected at " - "${CMAKE_TOOLCHAIN_FILE}") - endif() - endif() -endfunction() - -function(qt_auto_detect_cmake_generator) - if(NOT CMAKE_GENERATOR MATCHES "Ninja" AND NOT QT_SILENCE_CMAKE_GENERATOR_WARNING) - message(WARNING - "The officially supported CMake generator for building Qt is Ninja. " - "You are using: '${CMAKE_GENERATOR}' instead. " - "Thus, you might encounter issues. Use at your own risk.") - endif() -endfunction() - -function(qt_auto_detect_android) - # We assume an Android build if any of the ANDROID_* cache variables are set. - if(DEFINED ANDROID_SDK_ROOT - OR DEFINED ANDROID_NDK_ROOT - OR DEFINED ANDROID_ABI - OR DEFINED ANDROID_NATIVE_ABI_LEVEL - OR DEFINED ANDROID_STL) - set(android_detected TRUE) - else() - set(android_detected FALSE) - endif() - - # Auto-detect NDK root - if(NOT DEFINED ANDROID_NDK_ROOT AND DEFINED ANDROID_SDK_ROOT) - file(GLOB ndk_versions LIST_DIRECTORIES true RELATIVE "${ANDROID_SDK_ROOT}/ndk" - "${ANDROID_SDK_ROOT}/ndk/*") - unset(ndk_root) - if(NOT ndk_versions STREQUAL "") - # Use the NDK with the highest version number. - if(CMAKE_VERSION VERSION_LESS 3.18) - list(SORT ndk_versions) - list(REVERSE ndk_versions) - else() - list(SORT ndk_versions COMPARE NATURAL ORDER DESCENDING) - endif() - list(GET ndk_versions 0 ndk_root) - string(PREPEND ndk_root "${ANDROID_SDK_ROOT}/ndk/") - else() - # Fallback: use the deprecated "ndk-bundle" directory within the SDK root. - set(ndk_root "${ANDROID_SDK_ROOT}/ndk-bundle") - if(NOT IS_DIRECTORY "${ndk_root}") - unset(ndk_root) - endif() - endif() - if(DEFINED ndk_root) - message(STATUS "Android NDK detected: ${ndk_root}") - set(ANDROID_NDK_ROOT "${ndk_root}" CACHE STRING "") - endif() - endif() - - # Auto-detect toolchain file - if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ANDROID_NDK_ROOT) - set(toolchain_file "${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake") - if(EXISTS "${toolchain_file}") - message(STATUS "Android toolchain file within NDK detected: ${toolchain_file}") - set(CMAKE_TOOLCHAIN_FILE "${toolchain_file}" CACHE STRING "") - else() - message(FATAL_ERROR "Cannot find the toolchain file '${toolchain_file}'. " - "Please specify the toolchain file with -DCMAKE_TOOLCHAIN_FILE=<file>.") - endif() - endif() - - if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND android_detected) - message(FATAL_ERROR "An Android build was requested, but no Android toolchain file was " - "specified nor detected.") - endif() - - if(DEFINED CMAKE_TOOLCHAIN_FILE AND NOT DEFINED QT_AUTODETECT_ANDROID) - # Peek into the toolchain file and check if it looks like an Android one. - if(NOT android_detected) - file(READ ${CMAKE_TOOLCHAIN_FILE} toolchain_file_content OFFSET 0 LIMIT 80) - string(FIND "${toolchain_file_content}" "The Android Open Source Project" - find_result REVERSE) - if(NOT ${find_result} EQUAL -1) - set(android_detected TRUE) - endif() - endif() - - if(android_detected) - message(STATUS "Android build detected, checking configuration defaults...") - # ANDROID_NATIVE_API_LEVEL is an just an alias to ANDROID_PLATFORM, check for both - if(NOT DEFINED ANDROID_PLATFORM AND NOT DEFINED ANDROID_NATIVE_API_LEVEL) - message(STATUS "Neither ANDROID_PLATFORM nor ANDROID_NATIVE_API_LEVEL were specified, using API level 23 as default") - set(ANDROID_PLATFORM "android-23" CACHE STRING "") - set(ANDROID_NATIVE_API_LEVEL 23 CACHE STRING "") - endif() - if(NOT DEFINED ANDROID_STL) - set(ANDROID_STL "c++_shared" CACHE STRING "") - endif() - endif() - set(QT_AUTODETECT_ANDROID ${android_detected} CACHE STRING "") - elseif (QT_AUTODETECT_ANDROID) - message(STATUS "Android build detected") - endif() -endfunction() - -function(qt_auto_detect_vcpkg) - if(DEFINED ENV{VCPKG_ROOT}) - set(vcpkg_toolchain_file "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake") - get_filename_component(vcpkg_toolchain_file "${vcpkg_toolchain_file}" ABSOLUTE) - - if(DEFINED CMAKE_TOOLCHAIN_FILE) - get_filename_component(supplied_toolchain_file "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE) - if(NOT supplied_toolchain_file STREQUAL vcpkg_toolchain_file) - set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "${CMAKE_TOOLCHAIN_FILE}" CACHE STRING "") - endif() - unset(supplied_toolchain_file) - endif() - set(CMAKE_TOOLCHAIN_FILE "${vcpkg_toolchain_file}" CACHE STRING "" FORCE) - message(STATUS "Using vcpkg from $ENV{VCPKG_ROOT}") - if(DEFINED ENV{VCPKG_DEFAULT_TRIPLET} AND NOT DEFINED VCPKG_TARGET_TRIPLET) - set(VCPKG_TARGET_TRIPLET "$ENV{VCPKG_DEFAULT_TRIPLET}" CACHE STRING "") - message(STATUS "Using vcpkg triplet ${VCPKG_TARGET_TRIPLET}") - endif() - unset(vcpkg_toolchain_file) - message(STATUS "CMAKE_TOOLCHAIN_FILE is: ${CMAKE_TOOLCHAIN_FILE}") - if(DEFINED VCPKG_CHAINLOAD_TOOLCHAIN_FILE) - message(STATUS "VCPKG_CHAINLOAD_TOOLCHAIN_FILE is: ${VCPKG_CHAINLOAD_TOOLCHAIN_FILE}") - endif() - endif() -endfunction() - -function(qt_auto_detect_ios) - if(CMAKE_SYSTEM_NAME STREQUAL iOS) - message(STATUS "Using internal CMake ${CMAKE_SYSTEM_NAME} toolchain file.") - - # The QT_UIKIT_SDK check simulates the input.sdk condition for simulator_and_device in - # configure.json. - # If the variable is explicitly provided, assume simulator_and_device to be off. - if(QT_UIKIT_SDK) - set(simulator_and_device OFF) - else() - # Default to simulator_and_device when an explicit sdk is not requested. - # Requires CMake 3.17.0+. - set(simulator_and_device ON) - endif() - - message(STATUS "simulator_and_device set to: \"${simulator_and_device}\".") - - # Choose relevant architectures. - # Using a non Xcode generator requires explicit setting of the - # architectures, otherwise compilation fails with unknown defines. - if(simulator_and_device) - set(osx_architectures "arm64;x86_64") - elseif(QT_UIKIT_SDK STREQUAL "iphoneos") - set(osx_architectures "arm64") - elseif(QT_UIKIT_SDK STREQUAL "iphonesimulator") - set(osx_architectures "x86_64") - else() - if(NOT DEFINED QT_UIKIT_SDK) - message(FATAL_ERROR "Please provide a value for -DQT_UIKIT_SDK." - " Possible values: iphoneos, iphonesimulator.") - else() - message(FATAL_ERROR - "Unknown SDK argument given to QT_UIKIT_SDK: ${QT_UIKIT_SDK}.") - endif() - endif() - - # For non simulator_and_device builds, we need to explicitly set the SYSROOT aka the sdk - # value. - if(QT_UIKIT_SDK) - set(CMAKE_OSX_SYSROOT "${QT_UIKIT_SDK}" CACHE STRING "") - endif() - set(CMAKE_OSX_ARCHITECTURES "${osx_architectures}" CACHE STRING "") - - if(NOT DEFINED BUILD_SHARED_LIBS) - qt_internal_ensure_static_qt_config() - endif() - - # Disable qt rpaths for iOS, just like mkspecs/common/uikit.conf does, due to those - # bundles not being able to use paths outside the app bundle. Not sure this is strictly - # needed though. - set(QT_DISABLE_RPATH "OFF" CACHE BOOL "Disable automatic Qt rpath handling." FORCE) - endif() -endfunction() - -function(qt_auto_detect_cmake_config) - if(CMAKE_CONFIGURATION_TYPES) - # Allow users to specify this option. - if(NOT QT_MULTI_CONFIG_FIRST_CONFIG) - list(GET CMAKE_CONFIGURATION_TYPES 0 first_config_type) - set(QT_MULTI_CONFIG_FIRST_CONFIG "${first_config_type}") - set(QT_MULTI_CONFIG_FIRST_CONFIG "${first_config_type}" PARENT_SCOPE) - endif() - - set(CMAKE_TRY_COMPILE_CONFIGURATION "${QT_MULTI_CONFIG_FIRST_CONFIG}" PARENT_SCOPE) - if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config") - # Create build-<config>.ninja files for all specified configurations. - set(CMAKE_CROSS_CONFIGS "all" CACHE STRING "") - - # The configuration that will be considered the main one (for example when - # configuring standalone tests with a single-config generator like Ninja). - set(CMAKE_DEFAULT_BUILD_TYPE "${QT_MULTI_CONFIG_FIRST_CONFIG}" CACHE STRING "") - - # By default when ninja is called without parameters, it will build all configurations. - set(CMAKE_DEFAULT_CONFIGS "all" CACHE STRING "") - endif() - endif() -endfunction() - -function(qt_auto_detect_cyclic_toolchain) - if(CMAKE_TOOLCHAIN_FILE AND CMAKE_TOOLCHAIN_FILE MATCHES "/qt.toolchain.cmake$") - message(FATAL_ERROR - "Woah there! You can't use the Qt generated qt.toolchain.cmake file to configure " - "qtbase, because that will create a toolchain file that includes itself!\n" - "Did you accidentally use qt-cmake to configure qtbase? Make sure to remove the " - "CMakeCache.txt file, and configure qtbase with 'cmake' instead of 'qt-cmake'.") - endif() -endfunction() - -function(qt_internal_get_darwin_sdk_version out_var) - if(APPLE) - if(CMAKE_SYSTEM_NAME STREQUAL iOS) - set(sdk_name "iphoneos") - else() - # Default to macOS - set(sdk_name "macosx") - endif() - set(xcrun_version_arg "--show-sdk-version") - execute_process(COMMAND /usr/bin/xcrun --sdk ${sdk_name} ${xcrun_version_arg} - OUTPUT_VARIABLE sdk_version - ERROR_VARIABLE xcrun_error) - if(NOT sdk_version) - message(FATAL_ERROR - "Can't determine darwin ${sdk_name} SDK version. Error: ${xcrun_error}") - endif() - string(STRIP "${sdk_version}" sdk_version) - set(${out_var} "${sdk_version}" PARENT_SCOPE) - endif() -endfunction() - -function(qt_internal_get_xcode_version out_var) - if(APPLE) - execute_process(COMMAND /usr/bin/xcrun xcodebuild -version - OUTPUT_VARIABLE xcode_version - ERROR_VARIABLE xcrun_error) - string(REPLACE "\n" " " xcode_version "${xcode_version}") - string(STRIP "${xcode_version}" xcode_version) - set(${out_var} "${xcode_version}" PARENT_SCOPE) - endif() -endfunction() - -function(qt_auto_detect_darwin) - if(APPLE) - # If no CMAKE_OSX_DEPLOYMENT_TARGET is provided, default to a value that Qt defines. - # This replicates the behavior in mkspecs/common/macx.conf where - # QMAKE_MACOSX_DEPLOYMENT_TARGET is set. - set(description - "Minimum OS X version to target for deployment (at runtime); newer APIs weak linked. Set to empty string for default value.") - if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) - if(NOT CMAKE_SYSTEM_NAME) - # macOS - set(version "11.0") - elseif(CMAKE_SYSTEM_NAME STREQUAL iOS) - set(version "14.0") - endif() - if(version) - set(CMAKE_OSX_DEPLOYMENT_TARGET "${version}" CACHE STRING "${description}") - endif() - endif() - - qt_internal_get_darwin_sdk_version(darwin_sdk_version) - set(QT_MAC_SDK_VERSION "${darwin_sdk_version}" CACHE STRING "Darwin SDK version.") - - qt_internal_get_xcode_version(xcode_version) - set(QT_MAC_XCODE_VERSION "${xcode_version}" CACHE STRING "Xcode version.") - - list(LENGTH CMAKE_OSX_ARCHITECTURES arch_count) - if(NOT CMAKE_SYSTEM_NAME STREQUAL iOS AND arch_count GREATER 0) - foreach(arch ${CMAKE_OSX_ARCHITECTURES}) - if(arch STREQUAL "arm64e") - message(WARNING "Applications built against an arm64e Qt architecture will " - "likely fail to run on Apple Silicon. Consider targeting " - "'arm64' instead.") - endif() - endforeach() - endif() - endif() -endfunction() - -function(qt_auto_detect_macos_universal) - if(APPLE AND NOT CMAKE_SYSTEM_NAME STREQUAL iOS) - list(LENGTH CMAKE_OSX_ARCHITECTURES arch_count) - - set(is_universal "OFF") - if(arch_count GREATER 1) - set(is_universal "ON") - endif() - - set(QT_IS_MACOS_UNIVERSAL "${is_universal}" CACHE INTERNAL "Build universal Qt for macOS") - endif() -endfunction() - -function(qt_auto_detect_pch) - set(default_value "ON") - - if(CMAKE_OSX_ARCHITECTURES AND CMAKE_VERSION VERSION_LESS 3.18.0 AND NOT QT_FORCE_PCH) - list(LENGTH CMAKE_OSX_ARCHITECTURES arch_count) - # CMake versions lower than 3.18 don't support PCH when multiple architectures are set. - # This is the case for simulator_and_device builds. - if(arch_count GREATER 1) - set(default_value "OFF") - message(WARNING "PCH support disabled due to usage of multiple architectures.") - endif() - endif() - - option(BUILD_WITH_PCH "Build Qt using precompiled headers?" "${default_value}") -endfunction() - -function(qt_auto_detect_win32_arm) - if("${QT_QMAKE_TARGET_MKSPEC}" STREQUAL "win32-arm64-msvc") - set(CMAKE_SYSTEM_NAME "Windows" CACHE STRING "") - set(CMAKE_SYSTEM_VERSION "10" CACHE STRING "") - set(CMAKE_SYSTEM_PROCESSOR "arm64" CACHE STRING "") - endif() -endfunction() - -function(qt_auto_detect_linux_x86) - if("${QT_QMAKE_TARGET_MKSPEC}" STREQUAL "linux-g++-32" AND NOT QT_NO_AUTO_DETECT_LINUX_X86) - - # Add flag to ensure code is compiled for 32bit x86 ABI aka i386 or its flavors. - set(__qt_toolchain_common_flags_init "-m32") - - if(NOT QT_NO_OVERRIDE_LANG_FLAGS_INIT) - set(CMAKE_C_FLAGS_INIT "${__qt_toolchain_common_flags_init}" PARENT_SCOPE) - set(CMAKE_CXX_FLAGS_INIT "${__qt_toolchain_common_flags_init}" PARENT_SCOPE) - set(CMAKE_ASM_FLAGS_INIT "${__qt_toolchain_common_flags_init}" PARENT_SCOPE) - endif() - - # Each distro places arch-specific libraries according to its own file system layout. - # - # https://wiki.debian.org/Multiarch/TheCaseForMultiarch - # https://wiki.ubuntu.com/MultiarchSpec - # https://wiki.gentoo.org/wiki/Project:AMD64/Multilib_layout - # https://wiki.archlinux.org/title/official_repositories#multilib - # https://documentation.suse.com/sles/15-SP3/html/SLES-all/cha-64bit.html - # https://pilotlogic.com/sitejoom/index.php/wiki?id=398 - # https://unix.stackexchange.com/questions/458069/multilib-and-multiarch - # - # CMake can usually find 32 bit libraries just fine on its own. - # find_library will use prefixes from CMAKE_PREFIX_PATH / CMAKE_SYSTEM_PREFIX_PATH - # and add arch-specific lib folders like 'lib/i386-linux-gnu' on debian based systems - # or lib32/lib64 on other distros. - # The problem is that if no 32 bit library is found, a 64 bit one might get picked up. - # That's why we need to specify additional ignore paths. - # - # The paths used in the code below are Ubuntu specific. - # You can opt out of using them if you are using a different distro, but then you need to - # specify appropriate paths yourself in your own CMake toolchain file. - # - # Note that to absolutely ensure no x86_64 library is picked up on a multiarch / - # multilib-enabled system, you might need to specify extra directories in - # CMAKE_INGORE_PATH for each sub-directory containing a library. - # - # For example to exclude /usr/lib/x86_64-linux-gnu/mit-krb5/libgssapi_krb5.so - # you need to add /usr/lib/x86_64-linux-gnu/mit-krb5 explicitly to CMAKE_IGNORE_PATH. - # Adding just /usr/lib/x86_64-linux-gnu to either CMAKE_IGNORE_PATH or - # CMAKE_IGNORE_PREFIX_PATH is not enough. - # - # Another consideration are results returned by CMake's pkg_check_modules which uses - # pkg-config. - # CMAKE_IGNORE_PATH is not read by pkg_check_modules, but CMAKE_PREFIX_PATH - # values are passed as additional prefixes to look for .pc files, IN ADDITION to the default - # prefixes searched by pkg-config of each specific distro. - # For example on Ubuntu, the default searched paths on an x86_64 host are: - # /usr/local/lib/x86_64-linux-gnu/pkgconfig - # /usr/local/lib/pkgconfig - # /usr/local/share/pkgconfig - # /usr/lib/x86_64-linux-gnu/pkgconfig - # /usr/lib/pkgconfig - # /usr/share/pkgconfig - # To ensure the x86_64 packages are not picked up, the PKG_CONFIG_LIBDIR environment - # variable can be overridden with an explicit list of prefixes. - # Again, the paths below are Ubuntu specific. - if(NOT QT_NO_OVERRIDE_CMAKE_IGNORE_PATH) - set(linux_x86_ignore_path "/usr/lib/x86_64-linux-gnu;/lib/x86_64-linux-gnu") - set(CMAKE_IGNORE_PATH "${linux_x86_ignore_path}" PARENT_SCOPE) - set_property(GLOBAL PROPERTY - _qt_internal_linux_x86_ignore_path "${linux_x86_ignore_path}") - endif() - if(NOT QT_NO_OVERRIDE_PKG_CONFIG_LIBDIR) - set(pc_config_libdir "") - list(APPEND pc_config_libdir "/usr/local/lib/i386-linux-gnu/pkgconfig") - list(APPEND pc_config_libdir "/usr/local/lib/pkgconfig") - list(APPEND pc_config_libdir "/usr/local/share/pkgconfig") - list(APPEND pc_config_libdir "/usr/lib/i386-linux-gnu/pkgconfig") - list(APPEND pc_config_libdir "/usr/lib/pkgconfig") - list(APPEND pc_config_libdir "/usr/share/pkgconfig") - list(JOIN pc_config_libdir ":" pc_config_libdir) - - set_property(GLOBAL PROPERTY - _qt_internal_linux_x86_pc_config_libdir "${pc_config_libdir}") - - # Overrides the default prefix list. - set(ENV{PKG_CONFIG_LIBDIR} "${pc_config_libdir}") - - # Overrides the additional prefixes list. - set(ENV{PKG_CONFIG_DIR} "") - endif() - endif() -endfunction() - -function(qt_auto_detect_integrity) - if( - # Qt's custom CMake toolchain file sets this value. - CMAKE_SYSTEM_NAME STREQUAL "Integrity" OR - - # Upstream CMake expects this name, but we don't currently use it in Qt. - CMAKE_SYSTEM_NAME STREQUAL "GHS-MULTI" - ) - qt_internal_ensure_static_qt_config() - endif() -endfunction() - -# Let CMake load our custom platform modules. -# CMake-provided platform modules take precedence. -if(NOT QT_AVOID_CUSTOM_PLATFORM_MODULES) - list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/platforms") -endif() - -qt_auto_detect_cmake_generator() -qt_auto_detect_cyclic_toolchain() -qt_auto_detect_cmake_config() -qt_auto_detect_darwin() -qt_auto_detect_macos_universal() -qt_auto_detect_ios() -qt_auto_detect_android() -qt_auto_detect_vcpkg() -qt_auto_detect_pch() -qt_auto_detect_wasm() -qt_auto_detect_win32_arm() -qt_auto_detect_linux_x86() -qt_auto_detect_integrity() +include("${CMAKE_CURRENT_LIST_DIR}/QtAutoDetectHelpers.cmake") +qt_internal_setup_autodetect() diff --git a/cmake/QtAutoDetectHelpers.cmake b/cmake/QtAutoDetectHelpers.cmake new file mode 100644 index 0000000000..5621888308 --- /dev/null +++ b/cmake/QtAutoDetectHelpers.cmake @@ -0,0 +1,487 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# Collection of auto detection routines to improve the user experience when +# building Qt from source. +# +# Make sure to not run detection when building standalone tests, because the detection was already +# done when initially configuring qtbase. + +function(qt_internal_ensure_static_qt_config) + if(NOT DEFINED BUILD_SHARED_LIBS) + set(BUILD_SHARED_LIBS OFF CACHE BOOL "Build Qt statically or dynamically" FORCE) + endif() + + if(BUILD_SHARED_LIBS) + message(FATAL_ERROR + "Building Qt for ${CMAKE_SYSTEM_NAME} as shared libraries is not supported.") + endif() +endfunction() + +function(qt_auto_detect_wasm) + if("${QT_QMAKE_TARGET_MKSPEC}" STREQUAL "wasm-emscripten" + OR "${QT_QMAKE_TARGET_MKSPEC}" STREQUAL "wasm-emscripten-64") + if (NOT DEFINED ENV{EMSDK}) + message(FATAL_ERROR + "Can't find an Emscripten SDK! Make sure the EMSDK environment variable is " + "available by activating and sourcing the emscripten sdk. Also ensure emcc is in " + "your path.") + endif() + if(NOT DEFINED QT_AUTODETECT_WASM_IS_DONE) + message(STATUS "Extracting Emscripten SDK info from EMSDK env var: $ENV{EMSDK}") + __qt_internal_get_emroot_path_suffix_from_emsdk_env(EMROOT_PATH) + + __qt_internal_query_emsdk_version("${EMROOT_PATH}" TRUE CMAKE_EMSDK_REGEX_VERSION) + set(EMCC_VERSION "${CMAKE_EMSDK_REGEX_VERSION}" CACHE STRING INTERNAL FORCE) + + if(NOT DEFINED BUILD_SHARED_LIBS) + qt_internal_ensure_static_qt_config() + endif() + + # Find toolchain file + if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) + __qt_internal_get_emscripten_cmake_toolchain_file_path_from_emsdk_env( + "${EMROOT_PATH}" wasm_toolchain_file) + set(CMAKE_TOOLCHAIN_FILE "${wasm_toolchain_file}" CACHE STRING "" FORCE) + endif() + + if(EXISTS "${CMAKE_TOOLCHAIN_FILE}") + message(STATUS + "Emscripten ${EMCC_VERSION} toolchain file detected at ${CMAKE_TOOLCHAIN_FILE}") + else() + __qt_internal_show_error_no_emscripten_toolchain_file_found_when_building_qt() + endif() + + __qt_internal_get_emcc_recommended_version(recommended_version) + set(QT_EMCC_RECOMMENDED_VERSION "${recommended_version}" CACHE STRING INTERNAL FORCE) + + set(QT_AUTODETECT_WASM_IS_DONE TRUE CACHE BOOL "") + else() + message(STATUS + "Reusing cached Emscripten ${EMCC_VERSION} toolchain file detected at " + "${CMAKE_TOOLCHAIN_FILE}") + endif() + endif() +endfunction() + +function(qt_auto_detect_android) + # We assume an Android build if any of the ANDROID_* cache variables are set. + if(DEFINED ANDROID_SDK_ROOT + OR DEFINED ANDROID_NDK_ROOT + OR DEFINED ANDROID_ABI + OR DEFINED ANDROID_NATIVE_ABI_LEVEL + OR DEFINED ANDROID_STL) + set(android_detected TRUE) + else() + set(android_detected FALSE) + endif() + + # Auto-detect NDK root + if(NOT DEFINED ANDROID_NDK_ROOT AND DEFINED ANDROID_SDK_ROOT) + file(GLOB ndk_versions LIST_DIRECTORIES true RELATIVE "${ANDROID_SDK_ROOT}/ndk" + "${ANDROID_SDK_ROOT}/ndk/*") + unset(ndk_root) + if(NOT ndk_versions STREQUAL "") + # Use the NDK with the highest version number. + if(CMAKE_VERSION VERSION_LESS 3.18) + list(SORT ndk_versions) + list(REVERSE ndk_versions) + else() + list(SORT ndk_versions COMPARE NATURAL ORDER DESCENDING) + endif() + list(GET ndk_versions 0 ndk_root) + string(PREPEND ndk_root "${ANDROID_SDK_ROOT}/ndk/") + else() + # Fallback: use the deprecated "ndk-bundle" directory within the SDK root. + set(ndk_root "${ANDROID_SDK_ROOT}/ndk-bundle") + if(NOT IS_DIRECTORY "${ndk_root}") + unset(ndk_root) + endif() + endif() + if(DEFINED ndk_root) + message(STATUS "Android NDK detected: ${ndk_root}") + set(ANDROID_NDK_ROOT "${ndk_root}" CACHE STRING "") + endif() + endif() + + # Auto-detect toolchain file + if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ANDROID_NDK_ROOT) + set(toolchain_file "${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake") + if(EXISTS "${toolchain_file}") + message(STATUS "Android toolchain file within NDK detected: ${toolchain_file}") + set(CMAKE_TOOLCHAIN_FILE "${toolchain_file}" CACHE STRING "") + else() + message(FATAL_ERROR "Cannot find the toolchain file '${toolchain_file}'. " + "Please specify the toolchain file with -DCMAKE_TOOLCHAIN_FILE=<file>.") + endif() + endif() + + if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND android_detected) + message(FATAL_ERROR "An Android build was requested, but no Android toolchain file was " + "specified nor detected.") + endif() + + if(DEFINED CMAKE_TOOLCHAIN_FILE AND NOT DEFINED QT_AUTODETECT_ANDROID) + # Peek into the toolchain file and check if it looks like an Android one. + if(NOT android_detected) + file(READ ${CMAKE_TOOLCHAIN_FILE} toolchain_file_content OFFSET 0 LIMIT 80) + string(FIND "${toolchain_file_content}" "The Android Open Source Project" + find_result REVERSE) + if(NOT ${find_result} EQUAL -1) + set(android_detected TRUE) + endif() + endif() + + if(android_detected) + message(STATUS "Android build detected, checking configuration defaults...") + # ANDROID_NATIVE_API_LEVEL is an just an alias to ANDROID_PLATFORM, check for both + if(NOT DEFINED ANDROID_PLATFORM AND NOT DEFINED ANDROID_NATIVE_API_LEVEL) + message(STATUS "Neither ANDROID_PLATFORM nor ANDROID_NATIVE_API_LEVEL" + " were specified, using API level 28 as default") + set(ANDROID_PLATFORM "android-28" CACHE STRING "") + set(ANDROID_NATIVE_API_LEVEL 28 CACHE STRING "") + endif() + if(NOT DEFINED ANDROID_STL) + set(ANDROID_STL "c++_shared" CACHE STRING "") + endif() + endif() + set(QT_AUTODETECT_ANDROID ${android_detected} CACHE STRING "") + elseif (QT_AUTODETECT_ANDROID) + message(STATUS "Android build detected") + endif() +endfunction() + +function(qt_auto_detect_vcpkg) + if(QT_USE_VCPKG AND DEFINED ENV{VCPKG_ROOT}) + set(vcpkg_toolchain_file "$ENV{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake") + get_filename_component(vcpkg_toolchain_file "${vcpkg_toolchain_file}" ABSOLUTE) + + if(DEFINED CMAKE_TOOLCHAIN_FILE) + get_filename_component(supplied_toolchain_file "${CMAKE_TOOLCHAIN_FILE}" ABSOLUTE) + if(NOT supplied_toolchain_file STREQUAL vcpkg_toolchain_file) + set(VCPKG_CHAINLOAD_TOOLCHAIN_FILE "${supplied_toolchain_file}" CACHE STRING "") + endif() + unset(supplied_toolchain_file) + endif() + set(CMAKE_TOOLCHAIN_FILE "${vcpkg_toolchain_file}" CACHE STRING "" FORCE) + message(STATUS "Using vcpkg from $ENV{VCPKG_ROOT}") + if(DEFINED ENV{QT_VCPKG_TARGET_TRIPLET} AND NOT DEFINED VCPKG_TARGET_TRIPLET) + set(VCPKG_TARGET_TRIPLET "$ENV{QT_VCPKG_TARGET_TRIPLET}" CACHE STRING "") + message(STATUS "Using vcpkg triplet ${VCPKG_TARGET_TRIPLET}") + endif() + unset(vcpkg_toolchain_file) + message(STATUS "CMAKE_TOOLCHAIN_FILE is: ${CMAKE_TOOLCHAIN_FILE}") + if(DEFINED VCPKG_CHAINLOAD_TOOLCHAIN_FILE) + message(STATUS "VCPKG_CHAINLOAD_TOOLCHAIN_FILE is: ${VCPKG_CHAINLOAD_TOOLCHAIN_FILE}") + endif() + endif() +endfunction() + +function(qt_auto_detect_apple) + if(NOT APPLE) + return() + endif() + + if("${QT_QMAKE_TARGET_MKSPEC}" STREQUAL "macx-ios-clang") + set(CMAKE_SYSTEM_NAME "iOS" CACHE STRING "") + elseif("${QT_QMAKE_TARGET_MKSPEC}" STREQUAL "macx-visionos-clang") + set(CMAKE_SYSTEM_NAME "visionOS" CACHE STRING "") + endif() + + if(CMAKE_SYSTEM_NAME STREQUAL iOS) + message(STATUS "Using internal CMake ${CMAKE_SYSTEM_NAME} toolchain file.") + + # Pass on QT_UIKIT_SDK for compatibility + if(QT_UIKIT_SDK AND NOT QT_APPLE_SDK) + set(QT_APPLE_SDK "${QT_UIKIT_SDK}" CACHE STRING "") + endif() + + # The QT_APPLE_SDK check simulates the input.sdk condition for simulator_and_device in + # configure.json. + # If the variable is explicitly provided, assume simulator_and_device to be off. + if(QT_APPLE_SDK) + set(simulator_and_device OFF) + else() + # Default to simulator_and_device when an explicit sdk is not requested. + # Requires CMake 3.17.0+. + set(simulator_and_device ON) + endif() + + message(STATUS "simulator_and_device set to: \"${simulator_and_device}\".") + + # Choose relevant architectures. + # Using a non Xcode generator requires explicit setting of the + # architectures, otherwise compilation fails with unknown defines. + if(simulator_and_device) + set(osx_architectures "arm64;x86_64") + elseif(QT_APPLE_SDK STREQUAL "iphoneos") + set(osx_architectures "arm64") + elseif(QT_APPLE_SDK STREQUAL "iphonesimulator") + set(osx_architectures "x86_64") + else() + if(NOT DEFINED QT_APPLE_SDK) + message(FATAL_ERROR "Please provide a value for -DQT_APPLE_SDK." + " Possible values: iphoneos, iphonesimulator.") + else() + message(FATAL_ERROR + "Unknown SDK argument given to QT_APPLE_SDK: ${QT_APPLE_SDK}.") + endif() + endif() + + set(CMAKE_OSX_ARCHITECTURES "${osx_architectures}" CACHE STRING "") + endif() + + if(QT_APPLE_SDK) + set(CMAKE_OSX_SYSROOT "${QT_APPLE_SDK}" CACHE STRING "") + endif() + + if(CMAKE_SYSTEM_NAME STREQUAL iOS OR CMAKE_SYSTEM_NAME STREQUAL visionOS) + if(NOT DEFINED BUILD_SHARED_LIBS) + qt_internal_ensure_static_qt_config() + endif() + + # Disable qt rpaths for iOS, just like mkspecs/common/uikit.conf does, due to those + # bundles not being able to use paths outside the app bundle. Not sure this is strictly + # needed though. + set(QT_DISABLE_RPATH "OFF" CACHE BOOL "Disable automatic Qt rpath handling." FORCE) + endif() + + # If no CMAKE_OSX_DEPLOYMENT_TARGET is provided, default to a value that Qt defines. + # This replicates the behavior in mkspecs/common/macx.conf where + # QMAKE_MACOSX_DEPLOYMENT_TARGET is set. + set(description + "Minimum OS X version to target for deployment (at runtime); newer APIs weak linked." + " Set to empty string for default value.") + if(NOT CMAKE_OSX_DEPLOYMENT_TARGET) + if(NOT CMAKE_SYSTEM_NAME) + # macOS + set(version "12.0") + elseif(CMAKE_SYSTEM_NAME STREQUAL iOS) + set(version "16.0") + endif() + if(version) + set(CMAKE_OSX_DEPLOYMENT_TARGET "${version}" CACHE STRING "${description}") + endif() + endif() + + _qt_internal_get_apple_sdk_version(apple_sdk_version) + set(QT_MAC_SDK_VERSION "${apple_sdk_version}" CACHE STRING "Darwin SDK version.") + + _qt_internal_get_xcode_version_raw(xcode_version_raw) + set(QT_MAC_XCODE_VERSION "${xcode_version_raw}" CACHE STRING "Xcode version.") + + if(NOT CMAKE_SYSTEM_NAME) + # macOS + list(LENGTH CMAKE_OSX_ARCHITECTURES arch_count) + if(arch_count GREATER 0) + foreach(arch ${CMAKE_OSX_ARCHITECTURES}) + if(arch STREQUAL "arm64e") + message(WARNING "Applications built against an arm64e Qt architecture will " + "likely fail to run on Apple Silicon. Consider targeting " + "'arm64' instead.") + endif() + endforeach() + endif() + + set(is_universal "OFF") + if(arch_count GREATER 1) + set(is_universal "ON") + endif() + set(QT_IS_MACOS_UNIVERSAL "${is_universal}" CACHE INTERNAL "Build universal Qt for macOS") + endif() +endfunction() + +function(qt_auto_detect_cmake_config) + # If CMAKE_CONFIGURATION_TYPES are not set for the multi-config generator use Release and + # Debug configurations by default, instead of those are proposed by the CMake internal logic. + get_property(is_multi GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + if(is_multi) + if(NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_CONFIGURATION_TYPES Release Debug) + set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" PARENT_SCOPE) + endif() + + # Allow users to specify this option. + if(NOT QT_MULTI_CONFIG_FIRST_CONFIG) + list(GET CMAKE_CONFIGURATION_TYPES 0 first_config_type) + set(QT_MULTI_CONFIG_FIRST_CONFIG "${first_config_type}") + set(QT_MULTI_CONFIG_FIRST_CONFIG "${first_config_type}" PARENT_SCOPE) + endif() + + set(CMAKE_TRY_COMPILE_CONFIGURATION "${QT_MULTI_CONFIG_FIRST_CONFIG}" PARENT_SCOPE) + if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config") + # Create build-<config>.ninja files for all specified configurations. + set(CMAKE_CROSS_CONFIGS "all" CACHE STRING "") + + # The configuration that will be considered the main one (for example when + # configuring standalone tests with a single-config generator like Ninja). + set(CMAKE_DEFAULT_BUILD_TYPE "${QT_MULTI_CONFIG_FIRST_CONFIG}" CACHE STRING "") + + # By default when ninja is called without parameters, it will build all configurations. + set(CMAKE_DEFAULT_CONFIGS "all" CACHE STRING "") + endif() + endif() +endfunction() + +function(qt_auto_detect_cyclic_toolchain) + if(CMAKE_TOOLCHAIN_FILE AND CMAKE_TOOLCHAIN_FILE MATCHES "/qt\\.toolchain\\.cmake$") + message(FATAL_ERROR + "Woah there! You can't use the Qt generated qt.toolchain.cmake file to configure " + "qtbase, because that will create a toolchain file that includes itself!\n" + "Did you accidentally use qt-cmake to configure qtbase? Make sure to remove the " + "CMakeCache.txt file, and configure qtbase with 'cmake' instead of 'qt-cmake'.") + endif() +endfunction() + +function(qt_auto_detect_pch) + set(default_value "ON") + + if(CMAKE_OSX_ARCHITECTURES AND CMAKE_VERSION VERSION_LESS 3.18.0 AND NOT QT_FORCE_PCH) + list(LENGTH CMAKE_OSX_ARCHITECTURES arch_count) + # CMake versions lower than 3.18 don't support PCH when multiple architectures are set. + # This is the case for simulator_and_device builds. + if(arch_count GREATER 1) + set(default_value "OFF") + message(WARNING "PCH support disabled due to usage of multiple architectures.") + endif() + endif() + + option(BUILD_WITH_PCH "Build Qt using precompiled headers?" "${default_value}") +endfunction() + +function(qt_auto_detect_win32_arm) + if("${QT_QMAKE_TARGET_MKSPEC}" STREQUAL "win32-arm64-msvc") + set(CMAKE_SYSTEM_NAME "Windows" CACHE STRING "") + set(CMAKE_SYSTEM_VERSION "10" CACHE STRING "") + set(CMAKE_SYSTEM_PROCESSOR "arm64" CACHE STRING "") + endif() +endfunction() + +function(qt_auto_detect_linux_x86) + if("${QT_QMAKE_TARGET_MKSPEC}" STREQUAL "linux-g++-32" AND NOT QT_NO_AUTO_DETECT_LINUX_X86) + + # Add flag to ensure code is compiled for 32bit x86 ABI aka i386 or its flavors. + set(__qt_toolchain_common_flags_init "-m32") + + if(NOT QT_NO_OVERRIDE_LANG_FLAGS_INIT) + set(CMAKE_C_FLAGS_INIT "${__qt_toolchain_common_flags_init}" PARENT_SCOPE) + set(CMAKE_CXX_FLAGS_INIT "${__qt_toolchain_common_flags_init}" PARENT_SCOPE) + set(CMAKE_ASM_FLAGS_INIT "${__qt_toolchain_common_flags_init}" PARENT_SCOPE) + endif() + + # Each distro places arch-specific libraries according to its own file system layout. + # + # https://wiki.debian.org/Multiarch/TheCaseForMultiarch + # https://wiki.ubuntu.com/MultiarchSpec + # https://wiki.gentoo.org/wiki/Project:AMD64/Multilib_layout + # https://wiki.archlinux.org/title/official_repositories#multilib + # https://documentation.suse.com/sles/15-SP3/html/SLES-all/cha-64bit.html + # https://pilotlogic.com/sitejoom/index.php/wiki?id=398 + # https://unix.stackexchange.com/questions/458069/multilib-and-multiarch + # + # CMake can usually find 32 bit libraries just fine on its own. + # find_library will use prefixes from CMAKE_PREFIX_PATH / CMAKE_SYSTEM_PREFIX_PATH + # and add arch-specific lib folders like 'lib/i386-linux-gnu' on debian based systems + # or lib32/lib64 on other distros. + # The problem is that if no 32 bit library is found, a 64 bit one might get picked up. + # That's why we need to specify additional ignore paths. + # + # The paths used in the code below are Ubuntu specific. + # You can opt out of using them if you are using a different distro, but then you need to + # specify appropriate paths yourself in your own CMake toolchain file. + # + # Note that to absolutely ensure no x86_64 library is picked up on a multiarch / + # multilib-enabled system, you might need to specify extra directories in + # CMAKE_INGORE_PATH for each sub-directory containing a library. + # + # For example to exclude /usr/lib/x86_64-linux-gnu/mit-krb5/libgssapi_krb5.so + # you need to add /usr/lib/x86_64-linux-gnu/mit-krb5 explicitly to CMAKE_IGNORE_PATH. + # Adding just /usr/lib/x86_64-linux-gnu to either CMAKE_IGNORE_PATH or + # CMAKE_IGNORE_PREFIX_PATH is not enough. + # + # Another consideration are results returned by CMake's pkg_check_modules which uses + # pkg-config. + # CMAKE_IGNORE_PATH is not read by pkg_check_modules, but CMAKE_PREFIX_PATH + # values are passed as additional prefixes to look for .pc files, IN ADDITION to the default + # prefixes searched by pkg-config of each specific distro. + # For example on Ubuntu, the default searched paths on an x86_64 host are: + # /usr/local/lib/x86_64-linux-gnu/pkgconfig + # /usr/local/lib/pkgconfig + # /usr/local/share/pkgconfig + # /usr/lib/x86_64-linux-gnu/pkgconfig + # /usr/lib/pkgconfig + # /usr/share/pkgconfig + # To ensure the x86_64 packages are not picked up, the PKG_CONFIG_LIBDIR environment + # variable can be overridden with an explicit list of prefixes. + # Again, the paths below are Ubuntu specific. + if(NOT QT_NO_OVERRIDE_CMAKE_IGNORE_PATH) + set(linux_x86_ignore_path "/usr/lib/x86_64-linux-gnu;/lib/x86_64-linux-gnu") + set(CMAKE_IGNORE_PATH "${linux_x86_ignore_path}" PARENT_SCOPE) + set_property(GLOBAL PROPERTY + _qt_internal_linux_x86_ignore_path "${linux_x86_ignore_path}") + endif() + if(NOT QT_NO_OVERRIDE_PKG_CONFIG_LIBDIR) + set(pc_config_libdir "") + list(APPEND pc_config_libdir "/usr/local/lib/i386-linux-gnu/pkgconfig") + list(APPEND pc_config_libdir "/usr/local/lib/pkgconfig") + list(APPEND pc_config_libdir "/usr/local/share/pkgconfig") + list(APPEND pc_config_libdir "/usr/lib/i386-linux-gnu/pkgconfig") + list(APPEND pc_config_libdir "/usr/lib/pkgconfig") + list(APPEND pc_config_libdir "/usr/share/pkgconfig") + list(JOIN pc_config_libdir ":" pc_config_libdir) + + set_property(GLOBAL PROPERTY + _qt_internal_linux_x86_pc_config_libdir "${pc_config_libdir}") + + # Overrides the default prefix list. + set(ENV{PKG_CONFIG_LIBDIR} "${pc_config_libdir}") + + # Overrides the additional prefixes list. + set(ENV{PKG_CONFIG_DIR} "") + endif() + endif() +endfunction() + +function(qt_auto_detect_integrity) + if( + # Qt's custom CMake toolchain file sets this value. + CMAKE_SYSTEM_NAME STREQUAL "Integrity" OR + + # Upstream CMake expects this name, but we don't currently use it in Qt. + CMAKE_SYSTEM_NAME STREQUAL "GHS-MULTI" + ) + qt_internal_ensure_static_qt_config() + endif() +endfunction() + +# Save the build type before project() might set one. +# This allows us to determine if the user has set an explicit build type that we should use. +function(qt_auto_detect_cmake_build_type) + set(__qt_auto_detect_cmake_build_type_before_project_call "${CMAKE_BUILD_TYPE}" PARENT_SCOPE) +endfunction() + +macro(qt_internal_setup_autodetect) + # This needs to be here because QtAutoDetect loads before any other modules + option(QT_USE_VCPKG "Enable the use of vcpkg" OFF) + + include("${CMAKE_CURRENT_LIST_DIR}/QtPublicAppleHelpers.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/QtPublicWasmToolchainHelpers.cmake") + + # Let CMake load our custom platform modules. + # CMake-provided platform modules take precedence. + if(NOT QT_AVOID_CUSTOM_PLATFORM_MODULES) + list(PREPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/platforms") + endif() + + qt_auto_detect_cyclic_toolchain() + qt_auto_detect_cmake_config() + qt_auto_detect_apple() + qt_auto_detect_android() + qt_auto_detect_pch() + qt_auto_detect_wasm() + qt_auto_detect_win32_arm() + qt_auto_detect_linux_x86() + qt_auto_detect_integrity() + qt_auto_detect_cmake_build_type() + qt_auto_detect_vcpkg() +endmacro() diff --git a/cmake/QtAutogenHelpers.cmake b/cmake/QtAutogenHelpers.cmake index eb0ca29ff3..029a709e90 100644 --- a/cmake/QtAutogenHelpers.cmake +++ b/cmake/QtAutogenHelpers.cmake @@ -39,8 +39,7 @@ function(qt_enable_autogen_tool target tool enable) # that the moc scanner has to look for. Inform the CMake moc scanner about it. if(tool STREQUAL "moc" AND enable) set_target_properties("${target}" PROPERTIES - AUTOMOC_MACRO_NAMES "Q_OBJECT;Q_GADGET;Q_GADGET_EXPORT;Q_NAMESPACE;Q_NAMESPACE_EXPORT;Q_ENUM_NS") - + AUTOMOC_MACRO_NAMES "${CMAKE_AUTOMOC_MACRO_NAMES};Q_ENUM_NS;Q_GADGET_EXPORT") if (TARGET Qt::Platform) get_target_property(_abi_tag Qt::Platform qt_libcpp_abi_tag) if (_abi_tag) @@ -79,14 +78,23 @@ endfunction() # Complete manual moc invocation with full control. # Use AUTOMOC whenever possible. -# INCLUDE_DIRECTORIES specifies a list of include directories used by 'moc'. -# INCLUDE_DIRECTORY_TARGETS specifies a list of targets to extract the INTERFACE_INCLUDE_DIRECTORIES -# property and use it as the 'moc' include directories. +# Multi-value Arguments: +# INCLUDE_DIRECTORIES +# Specifies a list of include directories used by 'moc'. +# INCLUDE_DIRECTORY_TARGETS +# Specifies a list of targets to extract the INTERFACE_INCLUDE_DIRECTORIES +# property and use it as the 'moc' include directories.(Deprecated use TARGETS instead) +# DEFINITIONS +# List of the definitions that should be added to the moc command line arguments. +# Supports the syntax both with and without the prepending '-D'. +# TARGETS +# The list of targets that will be used to collect the INTERFACE_INCLUDE_DIRECTORIES, +# INCLUDE_DIRECTORIES, and COMPILE_DEFINITIONS properties. function(qt_manual_moc result) cmake_parse_arguments(arg "" "OUTPUT_MOC_JSON_FILES" - "FLAGS;INCLUDE_DIRECTORIES;INCLUDE_DIRECTORY_TARGETS" + "FLAGS;INCLUDE_DIRECTORIES;INCLUDE_DIRECTORY_TARGETS;DEFINITIONS;TARGETS" ${ARGN}) set(moc_files) set(metatypes_json_list) @@ -103,7 +111,7 @@ function(qt_manual_moc result) "-I\n${dir}") endforeach() - foreach(dep IN ITEMS ${arg_INCLUDE_DIRECTORY_TARGETS}) + foreach(dep IN LISTS arg_INCLUDE_DIRECTORY_TARGETS arg_TARGETS) set(include_expr "$<TARGET_PROPERTY:${dep},INTERFACE_INCLUDE_DIRECTORIES>") list(APPEND moc_parameters "$<$<BOOL:${include_expr}>:-I\n$<JOIN:${include_expr},\n-I\n>>") @@ -129,6 +137,30 @@ function(qt_manual_moc result) endif() endforeach() + foreach(dep IN LISTS arg_TARGETS) + set(include_property_expr + "$<TARGET_GENEX_EVAL:${dep},$<TARGET_PROPERTY:${dep},INCLUDE_DIRECTORIES>>") + list(APPEND moc_parameters + "$<$<BOOL:${include_property_expr}>:-I\n$<JOIN:${include_property_expr},\n-I\n>>") + + set(defines_property_expr + "$<TARGET_GENEX_EVAL:${dep},$<TARGET_PROPERTY:${dep},COMPILE_DEFINITIONS>>") + set(defines_with_d "$<FILTER:${defines_property_expr},INCLUDE,^-D>") + set(defines_without_d "$<FILTER:${defines_property_expr},EXCLUDE,^-D>") + list(APPEND moc_parameters + "$<$<BOOL:${defines_with_d}>:$<JOIN:${defines_with_d},\n>>") + list(APPEND moc_parameters + "$<$<BOOL:${defines_without_d}>:-D\n$<JOIN:${defines_without_d},\n-D\n>>") + endforeach() + + foreach(def IN LISTS arg_DEFINITIONS) + if(NOT def MATCHES "^-D") + list(APPEND moc_parameters "-D\n${def}") + else() + list(APPEND moc_parameters "${def}") + endif() + endforeach() + set(metatypes_byproducts) if (arg_OUTPUT_MOC_JSON_FILES) set(moc_json_file "${outfile}.json") diff --git a/cmake/QtBaseConfigureTests.cmake b/cmake/QtBaseConfigureTests.cmake index b62eec8410..66a0b3b6dd 100644 --- a/cmake/QtBaseConfigureTests.cmake +++ b/cmake/QtBaseConfigureTests.cmake @@ -62,8 +62,13 @@ function(qt_run_config_test_architecture) endif() message(STATUS "Extracting architecture info from ${_arch_file}.") + cmake_policy(PUSH) + if(POLICY CMP0159) + cmake_policy(SET CMP0159 NEW) + endif() file(STRINGS "${_arch_file}" _arch_lines LENGTH_MINIMUM 16 LENGTH_MAXIMUM 1024 ENCODING UTF-8 REGEX "==Qt=magic=Qt==") + cmake_policy(POP) foreach (_line ${_arch_lines}) string(LENGTH "${_line}" lineLength) @@ -73,7 +78,7 @@ function(qt_run_config_test_architecture) string(SUBSTRING "${_line}" ${_pos} -1 _architecture) endif() string(FIND "${_line}" "==Qt=magic=Qt== Sub-architecture:" _pos) - if (_pos GREATER -1 AND ${lineLength} GREATER 33) + if (_pos GREATER -1 AND NOT _line MATCHES "Sub-architecture:$") math(EXPR _pos "${_pos}+34") string(SUBSTRING "${_line}" ${_pos} -1 _sub_architecture) string(REPLACE " " ";" _sub_architecture "${_sub_architecture}") @@ -195,7 +200,7 @@ function(qt_internal_print_cmake_darwin_info) set(default_osx_arch " (defaults to ${CMAKE_SYSTEM_PROCESSOR})") endif() message(STATUS "CMAKE_OSX_ARCHITECTURES: \"${CMAKE_OSX_ARCHITECTURES}\"${default_osx_arch}") - message(STATUS "CMAKE_OSX_SYSROOT: \"${CMAKE_OSX_SYSROOT}\"") + message(STATUS "CMAKE_OSX_SYSROOT: \"$CACHE{CMAKE_OSX_SYSROOT}\" / \"${CMAKE_OSX_SYSROOT}\"") message(STATUS "CMAKE_OSX_DEPLOYMENT_TARGET: \"${CMAKE_OSX_DEPLOYMENT_TARGET}\"") message(STATUS "QT_MAC_SDK_VERSION: \"${QT_MAC_SDK_VERSION}\"") message(STATUS "QT_MAC_XCODE_VERSION: \"${QT_MAC_XCODE_VERSION}\"") @@ -203,8 +208,8 @@ function(qt_internal_print_cmake_darwin_info) if(DEFINED CACHE{QT_IS_MACOS_UNIVERSAL}) message(STATUS "QT_IS_MACOS_UNIVERSAL: \"${QT_IS_MACOS_UNIVERSAL}\"") endif() - if(QT_UIKIT_SDK) - message(STATUS "QT_UIKIT_SDK: \"${QT_UIKIT_SDK}\"") + if(QT_APPLE_SDK) + message(STATUS "QT_APPLE_SDK: \"${QT_APPLE_SDK}\"") endif() qt_internal_get_first_osx_arch(osx_first_arch) if(osx_first_arch) diff --git a/cmake/QtBaseGlobalTargets.cmake b/cmake/QtBaseGlobalTargets.cmake index 5ac19a9ba3..1e604559ed 100644 --- a/cmake/QtBaseGlobalTargets.cmake +++ b/cmake/QtBaseGlobalTargets.cmake @@ -63,12 +63,8 @@ qt_copy_or_install( set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/cmake/QtBuildInternals/${__build_internals_standalone_test_template_dir}/CMakeLists.txt") -include(QtToolchainHelpers) qt_internal_create_toolchain_file() -include(QtWrapperScriptHelpers) -qt_internal_create_wrapper_scripts() - ## Library to hold global features: ## These features are stored and accessed via Qt::GlobalConfig, but the ## files always lived in Qt::Core, so we keep it that way @@ -122,6 +118,9 @@ qt_generate_global_module_pri_file() qt_generate_global_device_pri_file() qt_generate_qmake_and_qtpaths_wrapper_for_target() +# Depends on the global features being evaluated. +qt_internal_create_wrapper_scripts() + add_library(Qt::GlobalConfig ALIAS GlobalConfig) add_library(GlobalConfigPrivate INTERFACE) @@ -133,8 +132,8 @@ target_include_directories(GlobalConfigPrivate INTERFACE $<INSTALL_INTERFACE:${INSTALL_INCLUDEDIR}/QtCore/${PROJECT_VERSION}/QtCore> ) add_library(Qt::GlobalConfigPrivate ALIAS GlobalConfigPrivate) +add_library(${QT_CMAKE_EXPORT_NAMESPACE}::GlobalConfigPrivate ALIAS GlobalConfigPrivate) -include(QtPlatformTargetHelpers) qt_internal_setup_public_platform_target() # defines PlatformCommonInternal PlatformModuleInternal PlatformPluginInternal PlatformToolInternal @@ -160,9 +159,10 @@ qt_install(EXPORT ${__export_name} DESTINATION "${__GlobalConfig_install_dir}") qt_internal_export_modern_cmake_config_targets_file(TARGETS ${__export_targets} - EXPORT_NAME_PREFIX ${INSTALL_CMAKE_NAMESPACE} - CONFIG_INSTALL_DIR - ${__GlobalConfig_install_dir}) + EXPORT_NAME_PREFIX ${INSTALL_CMAKE_NAMESPACE} + CONFIG_BUILD_DIR "${__GlobalConfig_build_dir}" + CONFIG_INSTALL_DIR "${__GlobalConfig_install_dir}" +) # Save minimum required CMake version to use Qt. qt_internal_get_supported_min_cmake_version_for_using_qt(supported_min_version_for_using_qt) @@ -172,6 +172,11 @@ qt_internal_get_computed_min_cmake_version_for_using_qt(computed_min_version_for qt_internal_get_min_new_policy_cmake_version(min_new_policy_version) qt_internal_get_max_new_policy_cmake_version(max_new_policy_version) +# Get the list of public helper files that should be automatically included in Qt6Config.cmake. +# Used in QtConfig.cmake.in template and further down for installation purposes. +qt_internal_get_qt_build_public_helpers(__qt_cmake_public_helpers) +list(JOIN __qt_cmake_public_helpers "\n " QT_PUBLIC_FILES_TO_INCLUDE) + # Generate and install Qt6 config file. Make sure it happens after the global feature evaluation so # they can be accessed in the Config file if needed. configure_package_config_file( @@ -180,6 +185,8 @@ configure_package_config_file( INSTALL_DESTINATION "${__GlobalConfig_install_dir}" ) +_qt_internal_export_apple_sdk_and_xcode_version_requirements(QT_CONFIG_EXTRAS_CODE) + configure_file( "${PROJECT_SOURCE_DIR}/cmake/QtConfigExtras.cmake.in" "${__GlobalConfig_build_dir}/${INSTALL_CMAKE_NAMESPACE}ConfigExtras.cmake" @@ -196,101 +203,48 @@ qt_internal_write_qt_package_version_file( "${__GlobalConfig_build_dir}/${INSTALL_CMAKE_NAMESPACE}ConfigVersion.cmake" ) +# Compute the reverse relative path from QtConfig.cmake to the install prefix +# this is used in QtInstallPaths to make the install paths relocatable +if(QT_WILL_INSTALL) + get_filename_component(_clean_prefix + "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}/${QT_CONFIG_INSTALL_DIR}" ABSOLUTE) +else() + get_filename_component(_clean_prefix "${QT_CONFIG_BUILD_DIR}" ABSOLUTE) +endif() +file(RELATIVE_PATH QT_INVERSE_CONFIG_INSTALL_DIR + "${_clean_prefix}" "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}") +configure_file( + "${PROJECT_SOURCE_DIR}/cmake/QtInstallPaths.cmake.in" + "${__GlobalConfig_build_dir}/QtInstallPaths.cmake" + @ONLY +) + qt_install(FILES "${__GlobalConfig_build_dir}/${INSTALL_CMAKE_NAMESPACE}Config.cmake" "${__GlobalConfig_build_dir}/${INSTALL_CMAKE_NAMESPACE}ConfigExtras.cmake" "${__GlobalConfig_build_dir}/${INSTALL_CMAKE_NAMESPACE}ConfigVersion.cmake" "${__GlobalConfig_build_dir}/${INSTALL_CMAKE_NAMESPACE}ConfigVersionImpl.cmake" + "${__GlobalConfig_build_dir}/QtInstallPaths.cmake" DESTINATION "${__GlobalConfig_install_dir}" COMPONENT Devel ) +qt_internal_get_qt_build_private_helpers(__qt_cmake_private_helpers) +list(TRANSFORM __qt_cmake_private_helpers PREPEND "cmake/") +list(TRANSFORM __qt_cmake_private_helpers APPEND ".cmake") + +qt_internal_get_qt_build_private_files_to_install(__qt_private_files_to_install) +list(TRANSFORM __qt_private_files_to_install PREPEND "cmake/") + # Install internal CMake files. # The functions defined inside can not be used in public projects. # They can only be used while building Qt itself. +set(__private_files + ${__qt_cmake_private_helpers} + ${__qt_private_files_to_install} +) qt_copy_or_install(FILES - cmake/ModuleDescription.json.in - cmake/PkgConfigLibrary.pc.in - cmake/Qt3rdPartyLibraryConfig.cmake.in - cmake/Qt3rdPartyLibraryHelpers.cmake - cmake/QtAndroidHelpers.cmake - cmake/QtAppHelpers.cmake - cmake/QtAutogenHelpers.cmake - cmake/QtBuild.cmake - cmake/QtBuildInformation.cmake - cmake/QtCMakeHelpers.cmake - cmake/QtCMakeVersionHelpers.cmake - cmake/QtCMakePackageVersionFile.cmake.in - cmake/QtCompilerFlags.cmake - cmake/QtCompilerOptimization.cmake - cmake/QtConfigDependencies.cmake.in - cmake/QtConfigureTimeExecutableCMakeLists.txt.in - cmake/QtDeferredDependenciesHelpers.cmake - cmake/QtDbusHelpers.cmake - cmake/QtDocsHelpers.cmake - cmake/QtExecutableHelpers.cmake - cmake/QtFileConfigure.txt.in - cmake/QtFindPackageHelpers.cmake - cmake/QtFindWrapConfigExtra.cmake.in - cmake/QtFindWrapHelper.cmake - cmake/QtFinishPkgConfigFile.cmake - cmake/QtFinishPrlFile.cmake - cmake/QtFlagHandlingHelpers.cmake - cmake/QtFrameworkHelpers.cmake - cmake/QtGenerateExtPri.cmake - cmake/QtGenerateLibHelpers.cmake - cmake/QtGenerateLibPri.cmake - cmake/QtGenerateVersionScript.cmake - cmake/QtGlobalStateHelpers.cmake - cmake/QtHeadersClean.cmake - cmake/QtInstallHelpers.cmake - cmake/QtJavaHelpers.cmake - cmake/QtLalrHelpers.cmake - cmake/QtModuleConfig.cmake.in - cmake/QtModuleDependencies.cmake.in - cmake/QtModuleHeadersCheck.cmake - cmake/QtModuleHelpers.cmake - cmake/QtModuleToolsConfig.cmake.in - cmake/QtModuleToolsDependencies.cmake.in - cmake/QtModuleToolsVersionlessTargets.cmake.in - cmake/QtNoLinkTargetHelpers.cmake - cmake/QtPkgConfigHelpers.cmake - cmake/QtPlatformAndroid.cmake - cmake/QtPlatformSupport.cmake - cmake/QtPluginConfig.cmake.in - cmake/QtPluginDependencies.cmake.in - cmake/QtPluginHelpers.cmake - cmake/QtPlugins.cmake.in - cmake/QtPostProcess.cmake - cmake/QtPostProcessHelpers.cmake - cmake/QtPrecompiledHeadersHelpers.cmake - cmake/QtUnityBuildHelpers.cmake - cmake/QtPriHelpers.cmake - cmake/QtPrlHelpers.cmake - cmake/QtPlatformTargetHelpers.cmake - cmake/QtProcessConfigureArgs.cmake - cmake/QtQmakeHelpers.cmake - cmake/QtResourceHelpers.cmake - cmake/QtRpathHelpers.cmake - cmake/QtSanitizerHelpers.cmake - cmake/QtScopeFinalizerHelpers.cmake - cmake/QtSeparateDebugInfo.Info.plist.in - cmake/QtSeparateDebugInfo.cmake - cmake/QtSetup.cmake - cmake/QtSimdHelpers.cmake - cmake/QtSingleRepoTargetSetBuildHelpers.cmake - cmake/QtStandaloneTestsConfig.cmake.in - cmake/QtSyncQtHelpers.cmake - cmake/QtTargetHelpers.cmake - cmake/QtTestHelpers.cmake - cmake/QtToolchainHelpers.cmake - cmake/QtToolHelpers.cmake - cmake/QtWasmHelpers.cmake - cmake/QtWrapperScriptHelpers.cmake - cmake/QtWriteArgsFile.cmake - cmake/modulecppexports.h.in - cmake/modulecppexports_p.h.in - cmake/qbatchedtestrunner.in.cpp + ${__private_files} DESTINATION "${__GlobalConfig_install_dir}" ) @@ -324,39 +278,29 @@ if(QT_WILL_INSTALL) endforeach() endif() +# Wrap previously queried helpers file. +list(TRANSFORM __qt_cmake_public_helpers PREPEND "cmake/") +list(TRANSFORM __qt_cmake_public_helpers APPEND ".cmake") + +qt_internal_get_qt_build_public_files_to_install(__qt_public_files_to_install) +list(TRANSFORM __qt_public_files_to_install PREPEND "cmake/") + # Install public CMake files. # The functions defined inside can be used in both public projects and while building Qt. # Usually we put such functions into Qt6CoreMacros.cmake, but that's getting bloated. # These files will be included by Qt6Config.cmake. -set(__public_cmake_helpers - cmake/QtCopyFileIfDifferent.cmake - cmake/QtFeature.cmake - cmake/QtFeatureCommon.cmake - cmake/QtInitProject.cmake - cmake/QtPublicAppleHelpers.cmake - cmake/QtPublicCMakeHelpers.cmake - cmake/QtPublicCMakeVersionHelpers.cmake - cmake/QtPublicFinalizerHelpers.cmake - cmake/QtPublicPluginHelpers.cmake - cmake/QtPublicTargetHelpers.cmake - cmake/QtPublicTestHelpers.cmake - cmake/QtPublicToolHelpers.cmake - cmake/QtPublicWalkLibsHelpers.cmake - cmake/QtPublicFindPackageHelpers.cmake - cmake/QtPublicDependencyHelpers.cmake - - # Public CMake files that are installed next Qt6Config.cmake, but are NOT included by it. - # Instead they are included by the generated CMake toolchain file. - cmake/QtPublicWasmToolchainHelpers.cmake +set(__public_files + ${__qt_cmake_public_helpers} + ${__qt_public_files_to_install} ) -qt_copy_or_install(FILES ${__public_cmake_helpers} DESTINATION "${__GlobalConfig_install_dir}") +qt_copy_or_install(FILES ${__public_files} DESTINATION "${__GlobalConfig_install_dir}") # In prefix builds we also need to copy the files into the build config directory, so that the # build-dir Qt6Config.cmake finds the files when building examples in-tree. if(QT_WILL_INSTALL) - foreach(_public_cmake_helper ${__public_cmake_helpers}) - file(COPY "${_public_cmake_helper}" DESTINATION "${__GlobalConfig_build_dir}") + foreach(_public_file ${__public_files}) + file(COPY "${_public_file}" DESTINATION "${__GlobalConfig_build_dir}") endforeach() endif() @@ -375,6 +319,11 @@ qt_copy_or_install(DIRECTORY cmake/ FILES_MATCHING PATTERN "Find*.cmake" PATTERN "tests" EXCLUDE PATTERN "3rdparty" EXCLUDE + PATTERN "macos" EXCLUDE + PATTERN "ios" EXCLUDE + PATTERN "visionos" EXCLUDE + PATTERN "platforms" EXCLUDE + PATTERN "QtBuildInternals" EXCLUDE ) # In prefix builds we also need to copy the files into the build config directory, so that the @@ -385,6 +334,11 @@ if(QT_WILL_INSTALL) FILES_MATCHING PATTERN "Find*.cmake" PATTERN "tests" EXCLUDE PATTERN "3rdparty" EXCLUDE + PATTERN "macos" EXCLUDE + PATTERN "ios" EXCLUDE + PATTERN "visionos" EXCLUDE + PATTERN "platforms" EXCLUDE + PATTERN "QtBuildInternals" EXCLUDE ) endif() @@ -393,8 +347,11 @@ if(APPLE) set(platform_shortname "macos") elseif(IOS) set(platform_shortname "ios") + elseif(VISIONOS) + set(platform_shortname "visionos") endif() + # Info.plist qt_copy_or_install(FILES "cmake/${platform_shortname}/Info.plist.app.in" DESTINATION "${__GlobalConfig_install_dir}/${platform_shortname}" ) @@ -403,6 +360,15 @@ if(APPLE) DESTINATION "${__GlobalConfig_build_dir}/${platform_shortname}" ) + # Privacy manifest + qt_copy_or_install(FILES "cmake/${platform_shortname}/PrivacyInfo.xcprivacy" + DESTINATION "${__GlobalConfig_install_dir}/${platform_shortname}" + ) + # For examples built as part of prefix build before install + file(COPY "cmake/${platform_shortname}/PrivacyInfo.xcprivacy" + DESTINATION "${__GlobalConfig_build_dir}/${platform_shortname}" + ) + if(IOS) qt_copy_or_install(FILES "cmake/ios/LaunchScreen.storyboard" DESTINATION "${__GlobalConfig_install_dir}/ios" diff --git a/cmake/QtBaseHelpers.cmake b/cmake/QtBaseHelpers.cmake new file mode 100644 index 0000000000..a16f6987b4 --- /dev/null +++ b/cmake/QtBaseHelpers.cmake @@ -0,0 +1,222 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause +# +# Note that this file is not installed. + +# Bail out if any part of the build directory's path is symlinked. +function(qt_internal_check_if_path_has_symlinks path) + get_filename_component(dir "${path}" ABSOLUTE) + set(is_symlink FALSE) + if(CMAKE_HOST_WIN32) + # CMake marks Windows mount points as symbolic links, so use simplified REALPATH check + # on Windows platforms instead of IS_SYMLINK. + get_filename_component(dir_realpath "${dir}" REALPATH) + if(NOT dir STREQUAL dir_realpath) + set(is_symlink TRUE) + endif() + else() + while(TRUE) + if(IS_SYMLINK "${dir}") + set(is_symlink TRUE) + break() + endif() + + set(prev_dir "${dir}") + get_filename_component(dir "${dir}" DIRECTORY) + if("${dir}" STREQUAL "${prev_dir}") + return() + endif() + endwhile() + endif() + if(is_symlink) + set(possible_solutions_for_resolving_symlink [[ + - Map directories using a transparent mechanism such as mount --bind + - Pass the real path of the build directory to CMake, e.g. using + cd $(realpath <path>) before invoking cmake <source_dir>. + ]]) + if(QT_ALLOW_SYMLINK_IN_PATHS) + # In some cases, e.g., Homebrew, it is beneficial to skip this check. + # Before this, Homebrew had to patch this out to be able to get their build. + message(WARNING + "The path \"${path}\" contains symlinks. " + "This is not recommended, and it may lead to unexpected issues. If you do " + "not have a good reason for enabling 'QT_ALLOW_SYMLINK_IN_PATHS', disable " + "it, and follow one of the following solutions: \n" + "${possible_solutions_for_resolving_symlink} ") + else() + message(FATAL_ERROR + "The path \"${path}\" contains symlinks. " + "This is not supported. Possible solutions: \n" + "${possible_solutions_for_resolving_symlink} ") + endif() + endif() +endfunction() + +# There are three necessary copies of this macro in +# qtbase/cmake/QtBaseHelpers.cmake +# qtbase/cmake/QtBaseTopLevelHelpers.cmake +# qtbase/cmake/QtBuildRepoHelpers.cmake +macro(qt_internal_qtbase_setup_standalone_parts) + # A generic marker for any kind of standalone builds, either tests or examples. + if(NOT DEFINED QT_INTERNAL_BUILD_STANDALONE_PARTS + AND (QT_BUILD_STANDALONE_TESTS OR QT_BUILD_STANDALONE_EXAMPLES)) + set(QT_INTERNAL_BUILD_STANDALONE_PARTS TRUE CACHE INTERNAL + "Whether standalone tests or examples are being built") + endif() +endmacro() + +macro(qt_internal_qtbase_run_autodetect) + qt_internal_qtbase_setup_standalone_parts() + + # Run auto detection routines, but not when doing standalone tests or standalone examples. + # In that case, the detection + # results are taken from either QtBuildInternals or the qt.toolchain.cmake file. Also, inhibit + # auto-detection in a top-level build, because the top-level project file already includes it. + if(NOT QT_INTERNAL_BUILD_STANDALONE_PARTS AND NOT QT_SUPERBUILD) + include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/QtAutoDetect.cmake) + endif() +endmacro() + +macro(qt_internal_qtbase_pre_project_setup) + if(NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) + # Should this Qt be static or dynamically linked? + option(BUILD_SHARED_LIBS "Build Qt statically or dynamically" ON) + set(QT_BUILD_SHARED_LIBS ${BUILD_SHARED_LIBS}) + + # This variable is also set in Qt6CoreConfigExtras.cmake, but it's not loaded when building + # qtbase. Set it here so qt_add_plugin can compute the proper plugin flavor. + set(QT6_IS_SHARED_LIBS_BUILD ${BUILD_SHARED_LIBS}) + + # BUILD_SHARED_LIBS influences the minimum required CMake version. The value is set either + # by: + # a cache variable provided on the configure command line + # or set by QtAutoDetect.cmake depending on the platform + # or specified via a toolchain file that is loaded by the project() call + # or set by the option() call above + include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/QtCMakeVersionHelpers.cmake") + include("${CMAKE_CURRENT_SOURCE_DIR}/cmake/QtPublicCMakeVersionHelpers.cmake") + qt_internal_check_and_warn_about_unsuitable_cmake_version() + + ## Add some paths to check for cmake modules: + list(PREPEND CMAKE_MODULE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/3rdparty/extra-cmake-modules/find-modules" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/3rdparty/kwin" + ) + + if(MACOS) + # Add module directory to pick up custom Info.plist template + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/macos") + elseif(IOS) + # Add module directory to pick up custom Info.plist template + list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ios") + endif() + + ## Find the build internals package. + set(QT_BUILD_INTERNALS_SKIP_CMAKE_MODULE_PATH_ADDITION TRUE) + list(PREPEND CMAKE_PREFIX_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/cmake" + ) + find_package(QtBuildInternals CMAKE_FIND_ROOT_PATH_BOTH) + unset(QT_BUILD_INTERNALS_SKIP_CMAKE_MODULE_PATH_ADDITION) + else() + # When building standalone parts, an istalled BuildInternals package already exists. + find_package(Qt6 REQUIRED COMPONENTS BuildInternals CMAKE_FIND_ROOT_PATH_BOTH) + endif() +endmacro() + +macro(qt_internal_qtbase_install_mkspecs) + # As long as we use the mkspecs (for qplatformdefs.h), we need to always + # install it, especially when cross-compiling. + set(mkspecs_install_dir "${INSTALL_MKSPECSDIR}") + qt_path_join(mkspecs_install_dir ${QT_INSTALL_DIR} ${mkspecs_install_dir}) + + file(GLOB mkspecs_subdirs + LIST_DIRECTORIES TRUE + "${PROJECT_SOURCE_DIR}/mkspecs/*") + foreach(entry IN LISTS mkspecs_subdirs) + if (IS_DIRECTORY ${entry}) + qt_copy_or_install(DIRECTORY "${entry}" + DESTINATION ${mkspecs_install_dir} + USE_SOURCE_PERMISSIONS) + else() + qt_copy_or_install(FILES "${entry}" + DESTINATION ${mkspecs_install_dir}) + endif() + endforeach() +endmacro() + +macro(qt_internal_qtbase_build_repo) + qt_internal_qtbase_pre_project_setup() + + qt_internal_project_setup() + + qt_build_repo_begin() + + if(NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) + ## Should this Qt be built with Werror? + option(WARNINGS_ARE_ERRORS "Build Qt with warnings as errors" ${FEATURE_developer_build}) + + ## Should this Qt create versioned hard link for some tools? + option(QT_CREATE_VERSIONED_HARD_LINK "Enable the use of versioned hard link" ON) + + ## QtBase specific configure tests: + include(QtBaseConfigureTests) + + ## Build System tests: + include(QtBaseCMakeTesting) + + ## Targets for global features, etc.: + include(QtBaseGlobalTargets) + + ## Set language standards after QtBaseGlobalTargets, because that's when the relevant + ## feature variables are available. + qt_set_language_standards() + + #include CoreMacros() for qt6_generate_meta_types() + set(QT_DEFAULT_MAJOR_VERSION 6) + include(src/corelib/Qt6CoreMacros.cmake) + + # Needed when building qtbase for android. + if(ANDROID) + include(src/corelib/Qt6AndroidMacros.cmake) + _qt_internal_create_global_android_targets() + endif() + + if(WASM) + # Needed when building for WebAssembly. + include(cmake/QtWasmHelpers.cmake) + include(src/corelib/Qt6WasmMacros.cmake) + qt_internal_setup_wasm_target_properties(Platform) + endif() + + # Set up optimization flags like in qmake. + # This function must be called after the global QT_FEATURE_xxx variables have been set up, + # aka after QtBaseGlobalTargets is processed. + # It also has to be called /before/ adding add_subdirectory(src), so that per-directory + # modifications can still be applied if necessary (like in done in Core and Gui). + qt_internal_set_up_config_optimizations_like_in_qmake() + + ## Setup documentation + add_subdirectory(doc) + + ## Visit all the directories: + add_subdirectory(src) + endif() + + if(NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) + if(QT_WILL_BUILD_TOOLS AND QT_FEATURE_settings) + add_subdirectory(qmake) + endif() + + qt_internal_qtbase_install_mkspecs() + endif() + + qt_build_repo_post_process() + + qt_build_repo_impl_tests() + + qt_build_repo_end() + + qt_build_repo_impl_examples() +endmacro() diff --git a/cmake/QtBaseTopLevelHelpers.cmake b/cmake/QtBaseTopLevelHelpers.cmake new file mode 100644 index 0000000000..07893b6cec --- /dev/null +++ b/cmake/QtBaseTopLevelHelpers.cmake @@ -0,0 +1,96 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# There are three necessary copies of this macro in +# qtbase/cmake/QtBaseHelpers.cmake +# qtbase/cmake/QtBaseTopLevelHelpers.cmake +# qtbase/cmake/QtBuildRepoHelpers.cmake +macro(qt_internal_top_level_setup_standalone_parts) + # A generic marker for any kind of standalone builds, either tests or examples. + if(NOT DEFINED QT_INTERNAL_BUILD_STANDALONE_PARTS + AND (QT_BUILD_STANDALONE_TESTS OR QT_BUILD_STANDALONE_EXAMPLES)) + set(QT_INTERNAL_BUILD_STANDALONE_PARTS TRUE CACHE INTERNAL + "Whether standalone tests or examples are being built") + endif() +endmacro() + +# Depends on __qt6_qtbase_src_path being set in the top-level dir. +macro(qt_internal_top_level_setup_autodetect) + qt_internal_top_level_setup_standalone_parts() + + # Run platform auto-detection /before/ the first project() call and thus + # before the toolchain file is loaded. + # Don't run auto-detection when doing standalone tests. In that case, the detection + # results are taken from either QtBuildInternals or the qt.toolchain.cmake file. + + if(NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) + set(__qt6_auto_detect_path "${__qt6_qtbase_src_path}/cmake/QtAutoDetect.cmake") + if(NOT EXISTS "${__qt6_auto_detect_path}") + message(FATAL_ERROR "Required file does not exist: '${__qt6_auto_detect_path}'") + endif() + include("${__qt6_auto_detect_path}") + endif() +endmacro() + +macro(qt_internal_top_level_setup_after_project) + qt_internal_top_level_setup_testing() +endmacro() + +macro(qt_internal_top_level_setup_testing) + # Required so we can call ctest from the root build directory + enable_testing() +endmacro() + +# Depends on __qt6_qtbase_src_path being set in the top-level dir. +macro(qt_internal_top_level_setup_cmake_module_path) + if (NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) + set(__qt6_cmake_module_path "${__qt6_qtbase_src_path}/cmake") + if(NOT EXISTS "${__qt6_cmake_module_path}") + message(FATAL_ERROR "Required directory does not exist: '${__qt6_cmake_module_path}'") + endif() + + list(APPEND CMAKE_MODULE_PATH "${__qt6_cmake_module_path}") + + list(APPEND CMAKE_MODULE_PATH + "${__qt6_cmake_module_path}/3rdparty/extra-cmake-modules/find-modules") + list(APPEND CMAKE_MODULE_PATH "${__qt6_cmake_module_path}/3rdparty/kwin") + endif() +endmacro() + +macro(qt_internal_top_level_before_build_submodules) + qt_internal_top_level_setup_no_create_targets() +endmacro() + +macro(qt_internal_top_level_setup_no_create_targets) + # Also make sure the CMake config files do not recreate the already-existing targets + if (NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) + set(QT_NO_CREATE_TARGETS TRUE) + endif() +endmacro() + +macro(qt_internal_top_level_end) + qt_internal_print_top_level_info() + + # Depends on QtBuildInternalsConfig being included, which is the case whenver any repo is + # configured. + qt_internal_qt_configure_end() +endmacro() + +function(qt_internal_print_top_level_info) + if(NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) + # Display a summary of everything + include(QtBuildInformation) + include(QtPlatformSupport) + qt_print_feature_summary() + qt_print_build_instructions() + endif() +endfunction() + +macro(qt_internal_top_level_after_add_subdirectory) + if(module STREQUAL "qtbase") + if (NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) + list(APPEND CMAKE_PREFIX_PATH "${QtBase_BINARY_DIR}/${INSTALL_LIBDIR}/cmake") + list(APPEND CMAKE_FIND_ROOT_PATH "${QtBase_BINARY_DIR}") + endif() + endif() +endmacro() diff --git a/cmake/QtBuild.cmake b/cmake/QtBuild.cmake index c4bf173e8b..ee24ba188e 100644 --- a/cmake/QtBuild.cmake +++ b/cmake/QtBuild.cmake @@ -1,583 +1,4 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -include(CMakePackageConfigHelpers) -include(QtSeparateDebugInfo) - -function(qt_configure_process_path name default docstring) - # Values are computed once for qtbase, and then exported and reused for other projects. - if(NOT PROJECT_NAME STREQUAL "QtBase") - return() - endif() - - # No value provided, set the default. - if(NOT DEFINED "${name}") - set("${name}" "${default}" CACHE STRING "${docstring}") - else() - get_filename_component(given_path_as_abs "${${name}}" ABSOLUTE BASE_DIR - "${CMAKE_INSTALL_PREFIX}") - file(RELATIVE_PATH rel_path "${CMAKE_INSTALL_PREFIX}" - "${given_path_as_abs}") - - # If absolute path given, check that it's inside the prefix (error out if not). - # TODO: Figure out if we need to support paths that are outside the prefix. - # - # If relative path given, it's relative to the install prefix (rather than the binary dir, - # which is what qmake does for some reason). - # In both cases, store the value as a relative path. - if("${rel_path}" STREQUAL "") - # file(RELATIVE_PATH) returns an empty string if the given absolute paths are equal - set(rel_path ".") - elseif(rel_path MATCHES "^\.\./") - # INSTALL_SYSCONFDIR is allowed to be outside the prefix. - if(NOT name STREQUAL "INSTALL_SYSCONFDIR") - message(FATAL_ERROR - "Path component '${name}' is outside computed install prefix: ${rel_path} ") - return() - endif() - set("${name}" "${${name}}" CACHE STRING "${docstring}" FORCE) - else() - set("${name}" "${rel_path}" CACHE STRING "${docstring}" FORCE) - endif() - endif() -endfunction() - -# Install locations: -qt_configure_process_path(INSTALL_BINDIR "bin" "Executables [PREFIX/bin]") -qt_configure_process_path(INSTALL_INCLUDEDIR "include" "Header files [PREFIX/include]") -qt_configure_process_path(INSTALL_LIBDIR "lib" "Libraries [PREFIX/lib]") -qt_configure_process_path(INSTALL_MKSPECSDIR "mkspecs" "Mkspecs files [PREFIX/mkspecs]") -qt_configure_process_path(INSTALL_ARCHDATADIR "." "Arch-dependent data [PREFIX]") -qt_configure_process_path(INSTALL_PLUGINSDIR - "${INSTALL_ARCHDATADIR}/plugins" - "Plugins [ARCHDATADIR/plugins]") - -if(NOT INSTALL_MKSPECSDIR MATCHES "(^|/)mkspecs") - message(FATAL_ERROR "INSTALL_MKSPECSDIR must end with '/mkspecs'") -endif() - -# Given CMAKE_CONFIG and ALL_CMAKE_CONFIGS, determines if a directory suffix needs to be appended -# to each destination, and sets the computed install target destination arguments in OUT_VAR. -# Defaults used for each of the destination types, and can be configured per destination type. -function(qt_get_install_target_default_args) - cmake_parse_arguments(PARSE_ARGV 0 arg - "" - "OUT_VAR;CMAKE_CONFIG;RUNTIME;LIBRARY;ARCHIVE;INCLUDES;BUNDLE" - "ALL_CMAKE_CONFIGS") - _qt_internal_validate_all_args_are_parsed(arg) - - if(NOT arg_CMAKE_CONFIG) - message(FATAL_ERROR "No value given for CMAKE_CONFIG.") - endif() - if(NOT arg_ALL_CMAKE_CONFIGS) - message(FATAL_ERROR "No value given for ALL_CMAKE_CONFIGS.") - endif() - list(LENGTH arg_ALL_CMAKE_CONFIGS all_configs_count) - list(GET arg_ALL_CMAKE_CONFIGS 0 first_config) - - set(suffix "") - if(all_configs_count GREATER 1 AND NOT arg_CMAKE_CONFIG STREQUAL first_config) - set(suffix "/${arg_CMAKE_CONFIG}") - endif() - - set(runtime "${INSTALL_BINDIR}") - if(arg_RUNTIME) - set(runtime "${arg_RUNTIME}") - endif() - - set(library "${INSTALL_LIBDIR}") - if(arg_LIBRARY) - set(library "${arg_LIBRARY}") - endif() - - set(archive "${INSTALL_LIBDIR}") - if(arg_ARCHIVE) - set(archive "${arg_ARCHIVE}") - endif() - - set(includes "${INSTALL_INCLUDEDIR}") - if(arg_INCLUDES) - set(includes "${arg_INCLUDES}") - endif() - - set(bundle "${INSTALL_BINDIR}") - if(arg_BUNDLE) - set(bundle "${arg_BUNDLE}") - endif() - - set(args - RUNTIME DESTINATION "${runtime}${suffix}" - LIBRARY DESTINATION "${library}${suffix}" - ARCHIVE DESTINATION "${archive}${suffix}" COMPONENT Devel - BUNDLE DESTINATION "${bundle}${suffix}" - INCLUDES DESTINATION "${includes}${suffix}") - set(${arg_OUT_VAR} "${args}" PARENT_SCOPE) -endfunction() - -if (WIN32) - set(_default_libexec "${INSTALL_ARCHDATADIR}/bin") -else() - set(_default_libexec "${INSTALL_ARCHDATADIR}/libexec") -endif() - -qt_configure_process_path( - INSTALL_LIBEXECDIR - "${_default_libexec}" - "Helper programs [ARCHDATADIR/bin on Windows, ARCHDATADIR/libexec otherwise]") -qt_configure_process_path(INSTALL_QMLDIR - "${INSTALL_ARCHDATADIR}/qml" - "QML imports [ARCHDATADIR/qml]") -qt_configure_process_path(INSTALL_DATADIR "." "Arch-independent data [PREFIX]") -qt_configure_process_path(INSTALL_DOCDIR "${INSTALL_DATADIR}/doc" "Documentation [DATADIR/doc]") -qt_configure_process_path(INSTALL_TRANSLATIONSDIR "${INSTALL_DATADIR}/translations" - "Translations [DATADIR/translations]") -if(APPLE) - set(QT_DEFAULT_SYS_CONF_DIR "/Library/Preferences/Qt") -else() - set(QT_DEFAULT_SYS_CONF_DIR "etc/xdg") -endif() -qt_configure_process_path(INSTALL_SYSCONFDIR - "${QT_DEFAULT_SYS_CONF_DIR}" - "Settings used by Qt programs [PREFIX/etc/xdg]/[/Library/Preferences/Qt]") -qt_configure_process_path(INSTALL_EXAMPLESDIR "examples" "Examples [PREFIX/examples]") -qt_configure_process_path(INSTALL_TESTSDIR "tests" "Tests [PREFIX/tests]") -qt_configure_process_path(INSTALL_DESCRIPTIONSDIR - "${INSTALL_ARCHDATADIR}/modules" - "Module description files directory") - -if(NOT "${CMAKE_STAGING_PREFIX}" STREQUAL "") - set(QT_STAGING_PREFIX "${CMAKE_STAGING_PREFIX}") -else() - set(QT_STAGING_PREFIX "${CMAKE_INSTALL_PREFIX}") -endif() - -if(PROJECT_NAME STREQUAL "QtBase") - set(QT_COORD_TYPE double CACHE STRING "Type of qreal") -endif() - -function(qt_internal_set_up_global_paths) - # Compute the values of QT_BUILD_DIR, QT_INSTALL_DIR, QT_CONFIG_BUILD_DIR, QT_CONFIG_INSTALL_DIR - # taking into account whether the current build is a prefix build or a non-prefix build, - # and whether it is a superbuild or non-superbuild. - # A third case is when another module or standalone tests are built against a super-built Qt. - # The layout for the third case is the same as for non-superbuilds. - # - # These values should be prepended to file paths in commands or properties, - # in order to correctly place generated Config files, generated Targets files, - # executables / libraries, when copying / installing files, etc. - # - # The build dir variables will always be absolute paths. - # The QT_INSTALL_DIR variable will have a relative path in a prefix build, - # which means that it can be empty, so use qt_join_path to prevent accidental absolute paths. - if(QT_SUPERBUILD) - # In this case, we always copy all the build products in qtbase/{bin,lib,...} - if(QT_WILL_INSTALL) - set(QT_BUILD_DIR "${QtBase_BINARY_DIR}") - set(QT_INSTALL_DIR "") - else() - if("${CMAKE_STAGING_PREFIX}" STREQUAL "") - set(QT_BUILD_DIR "${QtBase_BINARY_DIR}") - set(QT_INSTALL_DIR "${QtBase_BINARY_DIR}") - else() - set(QT_BUILD_DIR "${CMAKE_STAGING_PREFIX}") - set(QT_INSTALL_DIR "${CMAKE_STAGING_PREFIX}") - endif() - endif() - else() - if(QT_WILL_INSTALL) - # In the usual prefix build case, the build dir is the current module build dir, - # and the install dir is the prefix, so we don't set it. - set(QT_BUILD_DIR "${CMAKE_BINARY_DIR}") - set(QT_INSTALL_DIR "") - else() - # When doing a non-prefix build, both the build dir and install dir are the same, - # pointing to the qtbase build dir. - set(QT_BUILD_DIR "${QT_STAGING_PREFIX}") - set(QT_INSTALL_DIR "${QT_BUILD_DIR}") - endif() - endif() - - set(__config_path_part "${INSTALL_LIBDIR}/cmake") - set(QT_CONFIG_BUILD_DIR "${QT_BUILD_DIR}/${__config_path_part}") - set(QT_CONFIG_INSTALL_DIR "${QT_INSTALL_DIR}") - if(QT_CONFIG_INSTALL_DIR) - string(APPEND QT_CONFIG_INSTALL_DIR "/") - endif() - string(APPEND QT_CONFIG_INSTALL_DIR ${__config_path_part}) - - set(QT_BUILD_DIR "${QT_BUILD_DIR}" PARENT_SCOPE) - set(QT_INSTALL_DIR "${QT_INSTALL_DIR}" PARENT_SCOPE) - set(QT_CONFIG_BUILD_DIR "${QT_CONFIG_BUILD_DIR}" PARENT_SCOPE) - set(QT_CONFIG_INSTALL_DIR "${QT_CONFIG_INSTALL_DIR}" PARENT_SCOPE) -endfunction() -qt_internal_set_up_global_paths() -qt_get_relocatable_install_prefix(QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX) - -set(QT_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}") - -# Find the path to mkspecs/, depending on whether we are building as part of a standard qtbuild, -# or a module against an already installed version of qt. -if(NOT QT_MKSPECS_DIR) - if("${QT_BUILD_INTERNALS_PATH}" STREQUAL "") - get_filename_component(QT_MKSPECS_DIR "${CMAKE_CURRENT_LIST_DIR}/../mkspecs" ABSOLUTE) - else() - # We can rely on QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX being set by - # QtBuildInternalsExtra.cmake. - get_filename_component( - QT_MKSPECS_DIR - "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}/${INSTALL_MKSPECSDIR}" ABSOLUTE) - endif() - set(QT_MKSPECS_DIR "${QT_MKSPECS_DIR}" CACHE INTERNAL "") -endif() - -# macOS versions 10.14 and less don't have the implementation of std::filesystem API. -if(CMAKE_HOST_APPLE AND CMAKE_HOST_SYSTEM_VERSION VERSION_LESS "19.0.0") - message(FATAL_ERROR "macOS versions less than 10.15 are not supported for building Qt.") -endif() - -# the default RPATH to be used when installing, but only if it's not a system directory -list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIBDIR}" isSystemDir) -if("${isSystemDir}" STREQUAL "-1") - set(_default_install_rpath "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIBDIR}") -endif("${isSystemDir}" STREQUAL "-1") - -# The default rpath settings for installed targets is empty. -# The rpaths will instead be computed for each target separately using qt_apply_rpaths(). -# Additional rpaths can be passed via QT_EXTRA_RPATHS. -# By default this will include $ORIGIN / @loader_path, so the installation is relocatable. -# Bottom line: No need to pass anything to CMAKE_INSTALL_RPATH. -set(CMAKE_INSTALL_RPATH "" CACHE STRING "RPATH for installed binaries") - -# By default, don't embed auto-determined RPATHs pointing to directories -# outside of the build tree, into the installed binaries. -# This ended up adding rpaths like ${CMAKE_INSTALL_PREFIX}/lib (or /Users/qt/work/install/lib into -# the official libraries created by the CI) into the non-qtbase libraries, plugins, etc. -# -# It should not be necessary, given that qt_apply_rpaths() already adds the necessary rpaths, either -# relocatable ones or absolute ones, depending on what the platform supports. -if(NOT QT_NO_DISABLE_CMAKE_INSTALL_RPATH_USE_LINK_PATH) - set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) -endif() - -# Ensure that GNUInstallDirs's CMAKE_INSTALL_LIBDIR points to the same lib dir that Qt was -# configured with. Currently this is important for QML plugins, which embed an rpath based -# on that value. -set(CMAKE_INSTALL_LIBDIR "${INSTALL_LIBDIR}") - -function(qt_setup_tool_path_command) - if(NOT CMAKE_HOST_WIN32) - return() - endif() - set(bindir "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}/${INSTALL_BINDIR}") - file(TO_NATIVE_PATH "${bindir}" bindir) - list(APPEND command COMMAND) - list(APPEND command set PATH=${bindir}$<SEMICOLON>%PATH%) - set(QT_TOOL_PATH_SETUP_COMMAND "${command}" CACHE INTERNAL "internal command prefix for tool invocations" FORCE) - # QT_TOOL_PATH_SETUP_COMMAND is deprecated. Please use _qt_internal_get_wrap_tool_script_path - # instead. -endfunction() -qt_setup_tool_path_command() - -# Platform define path, etc. -if(WIN32) - set(QT_DEFAULT_PLATFORM_DEFINITIONS WIN32 _ENABLE_EXTENDED_ALIGNED_STORAGE) - if(CMAKE_SIZEOF_VOID_P EQUAL 8) - list(APPEND QT_DEFAULT_PLATFORM_DEFINITIONS WIN64 _WIN64) - endif() - - if(CLANG) - if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC" OR MSVC) - set(QT_DEFAULT_MKSPEC win32-clang-msvc) - elseif(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU" OR MINGW) - set(QT_DEFAULT_MKSPEC win32-clang-g++) - endif() - elseif(MSVC) - if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") - set(QT_DEFAULT_MKSPEC win32-arm64-msvc) - else() - set(QT_DEFAULT_MKSPEC win32-msvc) - endif() - elseif(MINGW) - set(QT_DEFAULT_MKSPEC win32-g++) - list(APPEND QT_DEFAULT_PLATFORM_DEFINITIONS MINGW_HAS_SECURE_API=1) - endif() -elseif(LINUX) - if(GCC) - set(QT_DEFAULT_MKSPEC linux-g++) - elseif(CLANG) - set(QT_DEFAULT_MKSPEC linux-clang) - endif() -elseif(ANDROID) - if(GCC) - set(QT_DEFAULT_MKSPEC android-g++) - elseif(CLANG) - set(QT_DEFAULT_MKSPEC android-clang) - endif() -elseif(IOS) - set(QT_DEFAULT_MKSPEC macx-ios-clang) -elseif(APPLE) - set(QT_DEFAULT_MKSPEC macx-clang) -elseif(WASM) - if(WASM64) - set(QT_DEFAULT_MKSPEC wasm-emscripten-64) - else() - set(QT_DEFAULT_MKSPEC wasm-emscripten) - endif() -elseif(QNX) - # Certain POSIX defines are not set if we don't compile with -std=gnuXX - set(QT_ENABLE_CXX_EXTENSIONS ON) - - list(APPEND QT_DEFAULT_PLATFORM_DEFINITIONS _FORTIFY_SOURCE=2 _REENTRANT) - - set(compiler_aarch64le aarch64le) - set(compiler_armle-v7 armv7le) - set(compiler_x86-64 x86_64) - set(compiler_x86 x86) - foreach(arch aarch64le armle-v7 x86-64 x86) - if (CMAKE_CXX_COMPILER_TARGET MATCHES "${compiler_${arch}}$") - set(QT_DEFAULT_MKSPEC qnx-${arch}-qcc) - endif() - endforeach() -elseif(FREEBSD) - if(CLANG) - set(QT_DEFAULT_MKSPEC freebsd-clang) - elseif(GCC) - set(QT_DEFAULT_MKSPEC freebsd-g++) - endif() -elseif(NETBSD) - set(QT_DEFAULT_MKSPEC netbsd-g++) -elseif(OPENBSD) - set(QT_DEFAULT_MKSPEC openbsd-g++) -elseif(SOLARIS) - if(GCC) - if(QT_64BIT) - set(QT_DEFAULT_MKSPEC solaris-g++-64) - else() - set(QT_DEFAULT_MKSPEC solaris-g++) - endif() - else() - if(QT_64BIT) - set(QT_DEFAULT_MKSPEC solaris-cc-64) - else() - set(QT_DEFAULT_MKSPEC solaris-cc) - endif() - endif() -elseif(HURD) - set(QT_DEFAULT_MKSPEC hurd-g++) -endif() - -if(NOT QT_QMAKE_TARGET_MKSPEC) - set(QT_QMAKE_TARGET_MKSPEC "${QT_DEFAULT_MKSPEC}" CACHE STRING "QMake target mkspec") -endif() - -if(CMAKE_CROSSCOMPILING) - set(QT_QMAKE_HOST_MKSPEC "${QT${PROJECT_VERSION_MAJOR}_HOST_INFO_QMAKE_MKSPEC}") -else() - set(QT_QMAKE_HOST_MKSPEC "${QT_QMAKE_TARGET_MKSPEC}") -endif() - -if(NOT QT_QMAKE_TARGET_MKSPEC OR NOT EXISTS "${QT_MKSPECS_DIR}/${QT_QMAKE_TARGET_MKSPEC}") - if(NOT QT_QMAKE_TARGET_MKSPEC) - set(reason "Platform is not detected. Please make sure your build environment is configured" - " properly or specify it manually using QT_QMAKE_TARGET_MKSPEC variable and one of the" - " known platforms.") - else() - set(reason "Unknown platform ${QT_QMAKE_TARGET_MKSPEC}") - endif() - - file(GLOB known_platforms - LIST_DIRECTORIES true - RELATIVE "${QT_MKSPECS_DIR}" - "${QT_MKSPECS_DIR}/*" - ) - list(JOIN known_platforms "\n " known_platforms) - message(FATAL_ERROR "${reason}\n" - "Known platforms:\n ${known_platforms}") -endif() - -if(NOT DEFINED QT_DEFAULT_PLATFORM_DEFINITIONS) - set(QT_DEFAULT_PLATFORM_DEFINITIONS "") -endif() - -set(QT_PLATFORM_DEFINITIONS ${QT_DEFAULT_PLATFORM_DEFINITIONS} - CACHE STRING "Qt platform specific pre-processor defines") - -set(QT_NAMESPACE "" CACHE STRING "Qt Namespace") - -include(QtGlobalStateHelpers) - -# Reset global state: -qt_internal_clear_qt_repo_known_modules() -qt_internal_clear_qt_repo_known_plugin_types() -qt_internal_set_qt_known_plugins("") - -set(QT_KNOWN_MODULES_WITH_TOOLS "" CACHE INTERNAL "Known Qt modules with tools" FORCE) - -# For adjusting variables when running tests, we need to know what -# the correct variable is for separating entries in PATH-alike -# variables. -if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") - set(QT_PATH_SEPARATOR "\\;") -else() - set(QT_PATH_SEPARATOR ":") -endif() - -# This is used to hold extra cmake code that should be put into QtBuildInternalsExtra.cmake file -# at the QtPostProcess stage. -set(QT_BUILD_INTERNALS_EXTRA_CMAKE_CODE "") - -# Save the value of the current first project source dir. -# This will be /path/to/qtbase for qtbase both in a super-build and a non super-build. -# This will be /path/to/qtbase/tests when building standalone tests. -set(QT_TOP_LEVEL_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") - -# Prevent warnings about object files without any symbols. This is a common -# thing in Qt as we tend to build files unconditionally, and then use ifdefs -# to compile out parts that are not relevant. -if(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang") - foreach(lang ASM C CXX) - # We have to tell 'ar' to not run ranlib by itself, by passing the 'S' option - set(CMAKE_${lang}_ARCHIVE_CREATE "<CMAKE_AR> qcS <TARGET> <LINK_FLAGS> <OBJECTS>") - set(CMAKE_${lang}_ARCHIVE_APPEND "<CMAKE_AR> qS <TARGET> <LINK_FLAGS> <OBJECTS>") - set(CMAKE_${lang}_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols <TARGET>") - endforeach() -endif() - -# Functions and macros: - -# Needed for qt_internal_add_link_flags_no_undefined. -include(CheckCXXSourceCompiles) - -set(__default_private_args - SOURCES - LIBRARIES - INCLUDE_DIRECTORIES - SYSTEM_INCLUDE_DIRECTORIES - DEFINES - DBUS_ADAPTOR_BASENAME - DBUS_ADAPTOR_FLAGS - DBUS_ADAPTOR_SOURCES - DBUS_INTERFACE_BASENAME - DBUS_INTERFACE_FLAGS - DBUS_INTERFACE_SOURCES - FEATURE_DEPENDENCIES - COMPILE_OPTIONS - LINK_OPTIONS - MOC_OPTIONS - DISABLE_AUTOGEN_TOOLS - ENABLE_AUTOGEN_TOOLS - PLUGIN_TYPES - NO_UNITY_BUILD_SOURCES -) -set(__default_public_args - PUBLIC_LIBRARIES - PUBLIC_INCLUDE_DIRECTORIES - PUBLIC_DEFINES - PUBLIC_COMPILE_OPTIONS - PUBLIC_LINK_OPTIONS -) -set(__default_private_module_args - PRIVATE_MODULE_INTERFACE -) -set(__default_target_info_args - TARGET_VERSION - TARGET_PRODUCT - TARGET_DESCRIPTION - TARGET_COMPANY - TARGET_COPYRIGHT -) - -# Collection of arguments so they can be shared across qt_internal_add_executable -# and qt_internal_add_test_helper. -set(__qt_internal_add_executable_optional_args - GUI - NO_INSTALL - EXCEPTIONS - DELAY_RC - DELAY_TARGET_INFO - QT_APP - NO_UNITY_BUILD -) -set(__qt_internal_add_executable_single_args - CORE_LIBRARY - OUTPUT_DIRECTORY - INSTALL_DIRECTORY - VERSION - ${__default_target_info_args} -) -set(__qt_internal_add_executable_multi_args - ${__default_private_args} - ${__default_public_args} -) - -option(QT_CMAKE_DEBUG_EXTEND_TARGET "Debug extend_target calls in Qt's build system" OFF) - -# Internal helpers available only while building Qt itself. -include(Qt3rdPartyLibraryHelpers) -include(QtAppHelpers) -include(QtAutogenHelpers) -include(QtCMakeHelpers) -include(QtDeferredDependenciesHelpers) -include(QtDbusHelpers) -include(QtDocsHelpers) -include(QtExecutableHelpers) -include(QtFindPackageHelpers) -include(QtFlagHandlingHelpers) -include(QtFrameworkHelpers) -include(QtInstallHelpers) -include(QtLalrHelpers) -include(QtModuleHelpers) -include(QtNoLinkTargetHelpers) -include(QtPluginHelpers) -include(QtPrecompiledHeadersHelpers) -include(QtUnityBuildHelpers) -include(QtPkgConfigHelpers) -include(QtPriHelpers) -include(QtPrlHelpers) -include(QtQmakeHelpers) -include(QtResourceHelpers) -include(QtRpathHelpers) -include(QtSanitizerHelpers) -include(QtScopeFinalizerHelpers) -include(QtSimdHelpers) -include(QtSingleRepoTargetSetBuildHelpers) -include(QtSyncQtHelpers) -include(QtTargetHelpers) -include(QtTestHelpers) -include(QtToolHelpers) -include(QtHeadersClean) -include(QtJavaHelpers) - -if(ANDROID) - include(QtAndroidHelpers) -endif() - -if(WASM) - include(QtWasmHelpers) -endif() - -# Helpers that are available in public projects and while building Qt itself. -include(QtPublicAppleHelpers) -include(QtPublicCMakeHelpers) -include(QtPublicPluginHelpers) -include(QtPublicTargetHelpers) -include(QtPublicWalkLibsHelpers) -include(QtPublicFindPackageHelpers) -include(QtPublicDependencyHelpers) -include(QtPublicTestHelpers) -include(QtPublicToolHelpers) - -if(CMAKE_CROSSCOMPILING) - if(NOT IS_DIRECTORY "${QT_HOST_PATH}") - message(FATAL_ERROR "You need to set QT_HOST_PATH to cross compile Qt.") - endif() -endif() - -_qt_internal_determine_if_host_info_package_needed(__qt_build_requires_host_info_package) -_qt_internal_find_host_info_package("${__qt_build_requires_host_info_package}") - -# This sets up the poor man's scope finalizer mechanism. -# For newer CMake versions, we use cmake_language(DEFER CALL) instead. -if(CMAKE_VERSION VERSION_LESS "3.19.0") - variable_watch(CMAKE_CURRENT_LIST_DIR qt_watch_current_list_dir) -endif() +qt_internal_setup_build_and_global_variables() diff --git a/cmake/QtBuildHelpers.cmake b/cmake/QtBuildHelpers.cmake new file mode 100644 index 0000000000..cce57cd5f6 --- /dev/null +++ b/cmake/QtBuildHelpers.cmake @@ -0,0 +1,458 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +function(qt_internal_validate_cmake_generator) + get_property(warning_shown GLOBAL PROPERTY _qt_validate_cmake_generator_warning_shown) + + if(NOT warning_shown + AND NOT CMAKE_GENERATOR MATCHES "Ninja" + AND NOT QT_SILENCE_CMAKE_GENERATOR_WARNING + AND NOT DEFINED ENV{QT_SILENCE_CMAKE_GENERATOR_WARNING}) + set_property(GLOBAL PROPERTY _qt_validate_cmake_generator_warning_shown TRUE) + message(WARNING + "The officially supported CMake generator for building Qt is " + "Ninja / Ninja Multi-Config. " + "You are using: '${CMAKE_GENERATOR}' instead. " + "Thus, you might encounter issues. Use at your own risk.") + endif() +endfunction() + +macro(qt_internal_set_qt_building_qt) + # Set the QT_BUILDING_QT variable so we can verify whether we are building + # Qt from source. + # Make sure not to set it when building a standalone test, otherwise + # upon reconfiguration we get an error about qt_internal_add_test + # not being found due the if(NOT QT_BUILDING_QT) check we have + # in each standalone test. + if(NOT QT_INTERNAL_IS_STANDALONE_TEST) + set(QT_BUILDING_QT TRUE CACHE BOOL + "When this is present and set to true, it signals that we are building Qt from source.") + endif() +endmacro() + +macro(qt_internal_unset_extra_build_internals_vars) + # Reset content of extra build internal vars for each inclusion of QtSetup. + unset(QT_EXTRA_BUILD_INTERNALS_VARS) +endmacro() + +macro(qt_internal_get_generator_is_multi_config) + # Save the global property in a variable to make it available to feature conditions. + get_property(QT_GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +endmacro() + +macro(qt_internal_setup_position_independent_code) + ## Position independent code: + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + + # Does the linker support position independent code? + include(CheckPIESupported) + check_pie_supported() +endmacro() + +macro(qt_internal_set_link_depends_no_shared) + # Do not relink dependent libraries when no header has changed: + set(CMAKE_LINK_DEPENDS_NO_SHARED ON) +endmacro() + +macro(qt_internal_set_qt_source_tree_var) + # Specify the QT_SOURCE_TREE only when building qtbase. Needed by some tests when the tests are + # built as part of the project, and not standalone. For standalone tests, the value is set in + # QtBuildInternalsExtra.cmake. + if(PROJECT_NAME STREQUAL "QtBase") + set(QT_SOURCE_TREE "${QtBase_SOURCE_DIR}" CACHE PATH + "A path to the source tree of the previously configured QtBase project." FORCE) + endif() +endmacro() + +macro(qt_internal_include_qt_platform_android) + ## Android platform settings + if(ANDROID) + include(QtPlatformAndroid) + endif() +endmacro() + +macro(qt_internal_set_compiler_optimization_flags) + include(QtCompilerOptimization) +endmacro() + +macro(qt_internal_set_compiler_warning_flags) + include(QtCompilerFlags) +endmacro() + +macro(qt_internal_set_skip_setup_deployment) + if(NOT QT_BUILD_EXAMPLES) + # Disable deployment setup to avoid warnings about missing patchelf with CMake < 3.21. + set(QT_SKIP_SETUP_DEPLOYMENT ON) + endif() +endmacro() + +macro(qt_internal_reset_global_state) + qt_internal_clear_qt_repo_known_modules() + qt_internal_clear_qt_repo_known_plugin_types() + qt_internal_set_qt_known_plugins("") + + set(QT_KNOWN_MODULES_WITH_TOOLS "" CACHE INTERNAL "Known Qt modules with tools" FORCE) +endmacro() + +macro(qt_internal_set_qt_path_separator) + # For adjusting variables when running tests, we need to know what + # the correct variable is for separating entries in PATH-alike + # variables. + if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + set(QT_PATH_SEPARATOR "\\;") + else() + set(QT_PATH_SEPARATOR ":") + endif() +endmacro() + +macro(qt_internal_set_internals_extra_cmake_code) + # This is used to hold extra cmake code that should be put into QtBuildInternalsExtra.cmake file + # at the QtPostProcess stage. + set(QT_BUILD_INTERNALS_EXTRA_CMAKE_CODE "") +endmacro() + +macro(qt_internal_set_top_level_source_dir) + # Save the value of the current first project source dir. + # This will be /path/to/qtbase for qtbase both in a super-build and a non super-build. + # This will be /path/to/qtbase/tests when building standalone tests. + set(QT_TOP_LEVEL_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") +endmacro() + +macro(qt_internal_set_apple_archiver_flags) + # Prevent warnings about object files without any symbols. This is a common + # thing in Qt as we tend to build files unconditionally, and then use ifdefs + # to compile out parts that are not relevant. + if(CMAKE_CXX_COMPILER_ID MATCHES "AppleClang") + foreach(lang ASM C CXX) + # We have to tell 'ar' to not run ranlib by itself, by passing the 'S' option + set(CMAKE_${lang}_ARCHIVE_CREATE "<CMAKE_AR> qcS <TARGET> <LINK_FLAGS> <OBJECTS>") + set(CMAKE_${lang}_ARCHIVE_APPEND "<CMAKE_AR> qS <TARGET> <LINK_FLAGS> <OBJECTS>") + set(CMAKE_${lang}_ARCHIVE_FINISH "<CMAKE_RANLIB> -no_warning_for_no_symbols <TARGET>") + endforeach() + endif() +endmacro() + +macro(qt_internal_set_apple_privacy_manifest target manifest_file) + set_target_properties(${target} PROPERTIES _qt_privacy_manifest "${manifest_file}") +endmacro() + +macro(qt_internal_set_debug_extend_target) + option(QT_CMAKE_DEBUG_EXTEND_TARGET "Debug extend_target calls in Qt's build system" OFF) +endmacro() + +# These upstream CMake modules will be automatically include()'d when doing +# find_package(Qt6 COMPONENTS BuildInternals). +function(qt_internal_get_qt_build_upstream_cmake_modules out_var) + set(${out_var} + CMakeFindBinUtils + CMakePackageConfigHelpers + CheckCXXSourceCompiles + FeatureSummary + PARENT_SCOPE + ) +endfunction() + +# These helpers will be installed when building qtbase, and they will be automatically include()'d +# when doing find_package(Qt6 COMPONENTS BuildInternals). +# The helpers are expected to exist under the qtbase/cmake sub-directory and their file name +# extension should be '.cmake'. +function(qt_internal_get_qt_build_private_helpers out_var) + set(${out_var} + Qt3rdPartyLibraryHelpers + QtAndroidHelpers + QtAppHelpers + QtAutoDetectHelpers + QtAutogenHelpers + QtBuildInformation + QtBuildOptionsHelpers + QtBuildPathsHelpers + QtBuildRepoExamplesHelpers + QtBuildRepoHelpers + QtCMakeHelpers + QtCMakeVersionHelpers + QtDbusHelpers + QtDeferredDependenciesHelpers + QtDocsHelpers + QtExecutableHelpers + QtFindPackageHelpers + QtFlagHandlingHelpers + QtFrameworkHelpers + QtGlobalStateHelpers + QtHeadersClean + QtInstallHelpers + QtJavaHelpers + QtLalrHelpers + QtMkspecHelpers + QtModuleHelpers + QtNoLinkTargetHelpers + QtPkgConfigHelpers + QtPlatformTargetHelpers + QtPluginHelpers + QtPostProcessHelpers + QtPrecompiledHeadersHelpers + QtPriHelpers + QtPrlHelpers + QtQmakeHelpers + QtResourceHelpers + QtRpathHelpers + QtSanitizerHelpers + QtScopeFinalizerHelpers + QtSeparateDebugInfo + QtSimdHelpers + QtSingleRepoTargetSetBuildHelpers + QtSyncQtHelpers + QtTargetHelpers + QtTestHelpers + QtToolHelpers + QtToolchainHelpers + QtUnityBuildHelpers + QtWasmHelpers + QtWrapperScriptHelpers + PARENT_SCOPE + ) +endfunction() + +# These files will be installed when building qtbase, but will NOT be automatically include()d +# when doing find_package(Qt6 COMPONENTS BuildInternals). +# The files are expected to exist under the qtbase/cmake sub-directory. +function(qt_internal_get_qt_build_private_files_to_install out_var) + set(${out_var} + ModuleDescription.json.in + PkgConfigLibrary.pc.in + Qt3rdPartyLibraryConfig.cmake.in + QtBaseTopLevelHelpers.cmake + QtBuild.cmake + QtBuildHelpers.cmake + QtCMakePackageVersionFile.cmake.in + QtCompilerFlags.cmake + QtCompilerOptimization.cmake + QtConfigDependencies.cmake.in + QtConfigureTimeExecutableCMakeLists.txt.in + QtFileConfigure.txt.in + QtFindWrapConfigExtra.cmake.in + QtFindWrapHelper.cmake + QtFinishPkgConfigFile.cmake + QtFinishPrlFile.cmake + QtGenerateExtPri.cmake + QtGenerateLibHelpers.cmake + QtGenerateLibPri.cmake + QtGenerateVersionScript.cmake + QtModuleConfig.cmake.in + QtModuleDependencies.cmake.in + QtModuleHeadersCheck.cmake + QtModuleToolsConfig.cmake.in + QtModuleToolsDependencies.cmake.in + QtModuleToolsVersionlessTargets.cmake.in + QtPlatformAndroid.cmake + QtPlatformSupport.cmake + QtPluginConfig.cmake.in + QtPluginDependencies.cmake.in + QtPlugins.cmake.in + QtPostProcess.cmake + QtProcessConfigureArgs.cmake + QtSeparateDebugInfo.Info.plist.in + QtSetup.cmake + QtStandaloneTestsConfig.cmake.in + QtVersionlessAliasTargets.cmake.in + QtVersionlessTargets.cmake.in + QtWriteArgsFile.cmake + modulecppexports.h.in + qbatchedtestrunner.in.cpp + PARENT_SCOPE + ) +endfunction() + +# These helpers will be installed when building qtbase, and they will be automatically include()'d +# when doing find_package(Qt6 COMPONENTS BuildInternals). +# The helpers are expected to exist under the qtbase/cmake sub-directory and their file name +# extension should be '.cmake'. +# In addition, they are meant to be included when doing find_package(Qt6) as well. +function(qt_internal_get_qt_build_public_helpers out_var) + set(${out_var} + QtFeature + QtFeatureCommon + QtPublicAppleHelpers + QtPublicCMakeHelpers + QtPublicCMakeVersionHelpers + QtPublicDependencyHelpers + QtPublicExternalProjectHelpers + QtPublicFinalizerHelpers + QtPublicFindPackageHelpers + QtPublicPluginHelpers + QtPublicTargetHelpers + QtPublicTestHelpers + QtPublicToolHelpers + QtPublicWalkLibsHelpers + PARENT_SCOPE + ) +endfunction() + +# These files will be installed when building qtbase, but will NOT be automatically include()d +# when doing find_package(Qt6) nor find_package(Qt6 COMPONENTS BuildInternals). +# The files are expected to exist under the qtbase/cmake sub-directory. +function(qt_internal_get_qt_build_public_files_to_install out_var) + set(${out_var} + QtCopyFileIfDifferent.cmake + QtInitProject.cmake + + # Public CMake files that are installed next Qt6Config.cmake, but are NOT included by it. + # Instead they are included by the generated CMake toolchain file. + QtPublicWasmToolchainHelpers.cmake + + PARENT_SCOPE + ) +endfunction() + +# Includes all Qt CMake helper files that define functions and macros. +macro(qt_internal_include_all_helpers) + # Upstream cmake modules. + qt_internal_get_qt_build_upstream_cmake_modules(__qt_upstream_helpers) + foreach(__qt_file_name IN LISTS __qt_upstream_helpers) + include("${__qt_file_name}") + endforeach() + + # Internal helpers available only while building Qt itself. + qt_internal_get_qt_build_private_helpers(__qt_private_helpers) + foreach(__qt_file_name IN LISTS __qt_private_helpers) + include("${__qt_file_name}") + endforeach() + + # Helpers that are available in public projects and while building Qt itself. + qt_internal_get_qt_build_public_helpers(__qt_public_helpers) + foreach(__qt_file_name IN LISTS __qt_public_helpers) + include("${__qt_file_name}") + endforeach() +endmacro() + +function(qt_internal_check_host_path_set_for_cross_compiling) + if(CMAKE_CROSSCOMPILING) + if(NOT IS_DIRECTORY "${QT_HOST_PATH}") + message(FATAL_ERROR "You need to set QT_HOST_PATH to cross compile Qt.") + endif() + endif() +endfunction() + +macro(qt_internal_setup_find_host_info_package) + _qt_internal_determine_if_host_info_package_needed(__qt_build_requires_host_info_package) + _qt_internal_find_host_info_package("${__qt_build_requires_host_info_package}") +endmacro() + +macro(qt_internal_setup_poor_mans_scope_finalizer) + # This sets up the poor man's scope finalizer mechanism. + # For newer CMake versions, we use cmake_language(DEFER CALL) instead. + if(CMAKE_VERSION VERSION_LESS "3.19.0") + variable_watch(CMAKE_CURRENT_LIST_DIR qt_watch_current_list_dir) + endif() +endmacro() + +macro(qt_internal_set_qt_namespace) + set(QT_NAMESPACE "" CACHE STRING "Qt Namespace") +endmacro() + +macro(qt_internal_set_qt_coord_type) + if(PROJECT_NAME STREQUAL "QtBase") + set(QT_COORD_TYPE double CACHE STRING "Type of qreal") + endif() +endmacro() + +function(qt_internal_check_macos_host_version) + # macOS versions 10.14 and less don't have the implementation of std::filesystem API. + if(CMAKE_HOST_APPLE AND CMAKE_HOST_SYSTEM_VERSION VERSION_LESS "19.0.0") + message(FATAL_ERROR "macOS versions less than 10.15 are not supported for building Qt.") + endif() +endfunction() + +function(qt_internal_setup_tool_path_command) + if(NOT CMAKE_HOST_WIN32) + return() + endif() + set(bindir "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}/${INSTALL_BINDIR}") + file(TO_NATIVE_PATH "${bindir}" bindir) + list(APPEND command COMMAND) + list(APPEND command set PATH=${bindir}$<SEMICOLON>%PATH%) + set(QT_TOOL_PATH_SETUP_COMMAND "${command}" CACHE INTERNAL + "internal command prefix for tool invocations" FORCE) + # QT_TOOL_PATH_SETUP_COMMAND is deprecated. Please use _qt_internal_get_wrap_tool_script_path + # instead. +endfunction() + +macro(qt_internal_setup_android_platform_specifics) + if(ANDROID) + qt_internal_setup_android_target_properties() + endif() +endmacro() + +macro(qt_internal_setup_build_and_global_variables) + qt_internal_validate_cmake_generator() + qt_internal_set_qt_building_qt() + qt_internal_set_cmake_build_type() + qt_internal_set_message_log_level(CMAKE_MESSAGE_LOG_LEVEL) + qt_internal_unset_extra_build_internals_vars() + qt_internal_get_generator_is_multi_config() + + # Depends on qt_internal_set_cmake_build_type + qt_internal_setup_cmake_config_postfix() + + qt_internal_setup_position_independent_code() + qt_internal_set_link_depends_no_shared() + qt_internal_setup_default_install_prefix() + qt_internal_set_qt_source_tree_var() + qt_internal_set_export_compile_commands() + qt_internal_set_configure_from_ide() + + # Depends on qt_internal_set_configure_from_ide + qt_internal_set_sync_headers_at_configure_time() + + qt_internal_setup_build_benchmarks() + + # Depends on qt_internal_setup_build_benchmarks + qt_internal_setup_build_tests() + + qt_internal_setup_build_tools() + + # Depends on qt_internal_setup_default_install_prefix + qt_internal_setup_build_examples() + + qt_internal_set_qt_host_path() + + qt_internal_include_qt_platform_android() + + # Depends on qt_internal_setup_default_install_prefix + qt_internal_setup_paths_and_prefixes() + + qt_internal_reset_global_state() + + # Depends on qt_internal_setup_paths_and_prefixes + qt_internal_set_mkspecs_dir() + qt_internal_setup_platform_definitions_and_mkspec() + + qt_internal_check_macos_host_version() + _qt_internal_check_apple_sdk_and_xcode_versions() + qt_internal_check_host_path_set_for_cross_compiling() + qt_internal_setup_android_platform_specifics() + qt_internal_setup_find_host_info_package() + qt_internal_setup_tool_path_command() + qt_internal_setup_default_target_function_options() + qt_internal_set_default_rpath_settings() + qt_internal_set_qt_namespace() + qt_internal_set_qt_coord_type() + qt_internal_set_qt_path_separator() + qt_internal_set_internals_extra_cmake_code() + qt_internal_set_top_level_source_dir() + qt_internal_set_apple_archiver_flags() + qt_internal_set_debug_extend_target() + qt_internal_setup_poor_mans_scope_finalizer() + + qt_internal_set_compiler_optimization_flags() + qt_internal_set_compiler_warning_flags() + + qt_set_language_standards() + qt_internal_set_use_ccache() + qt_internal_set_unity_build() + qt_internal_set_allow_symlink_in_paths() + qt_internal_set_skip_setup_deployment() + qt_internal_set_qt_allow_download() + + qt_internal_detect_dirty_features() +endmacro() + diff --git a/cmake/QtBuildInformation.cmake b/cmake/QtBuildInformation.cmake index 6929f9134e..11fa9996b1 100644 --- a/cmake/QtBuildInformation.cmake +++ b/cmake/QtBuildInformation.cmake @@ -1,6 +1,22 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +function(qt_internal_set_message_log_level out_var) + # Decide whether output should be verbose or not. + # Default to verbose (--log-level=STATUS) in a developer-build and + # non-verbose (--log-level=NOTICE) otherwise. + # If a custom CMAKE_MESSAGE_LOG_LEVEL was specified, it takes priority. + # Passing an explicit --log-level=Foo has the highest priority. + if(NOT CMAKE_MESSAGE_LOG_LEVEL) + if(FEATURE_developer_build OR QT_FEATURE_developer_build) + set(CMAKE_MESSAGE_LOG_LEVEL "STATUS") + else() + set(CMAKE_MESSAGE_LOG_LEVEL "NOTICE") + endif() + set(${out_var} "${CMAKE_MESSAGE_LOG_LEVEL}" PARENT_SCOPE) + endif() +endfunction() + function(qt_print_feature_summary) if(QT_SUPERBUILD) qt_internal_set_message_log_level(message_log_level) @@ -11,7 +27,6 @@ function(qt_print_feature_summary) endif() endif() - include(FeatureSummary) # Show which packages were found. feature_summary(INCLUDE_QUIET_PACKAGES WHAT PACKAGES_FOUND @@ -43,8 +58,7 @@ endfunction() function(qt_print_build_instructions) if((NOT PROJECT_NAME STREQUAL "QtBase" AND - NOT PROJECT_NAME STREQUAL "Qt") OR - QT_BUILD_STANDALONE_TESTS) + NOT PROJECT_NAME STREQUAL "Qt") OR QT_INTERNAL_BUILD_STANDALONE_PARTS) return() endif() @@ -147,11 +161,23 @@ function(qt_configure_print_summary) # Show Qt-specific configuration summary. if(__qt_configure_reports) + # The summary will only be printed for log level STATUS or above. + # Check whether the log level is sufficient for printing the summary. + set(log_level_sufficient_for_printed_summary TRUE) + if(CMAKE_VERSION GREATER_EQUAL "3.25") + cmake_language(GET_MESSAGE_LOG_LEVEL log_level) + set(sufficient_log_levels STATUS VERBOSE DEBUG TRACE) + if(NOT log_level IN_LIST sufficient_log_levels) + set(log_level_sufficient_for_printed_summary FALSE) + endif() + endif() + # We want to show the configuration summary file and log level message only on # first configuration or when we detect a feature change, to keep most # reconfiguration output as quiet as possible. # Currently feature change detection is not entirely reliable. - if(NOT QT_INTERNAL_SUMMARY_INSTRUCTIONS_SHOWN OR features_possibly_changed) + if(log_level_sufficient_for_printed_summary + AND (NOT QT_INTERNAL_SUMMARY_INSTRUCTIONS_SHOWN OR features_possibly_changed)) set(force_show_summary TRUE) message( "\n" diff --git a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake index e487633210..129f1ebb77 100644 --- a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake +++ b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake @@ -4,66 +4,25 @@ # These values should be kept in sync with those in qtbase/.cmake.conf cmake_minimum_required(VERSION 3.16...3.21) -############################################### -# -# Macros and functions for building Qt modules -# -############################################### - -# Recursively reads the dependencies section from dependencies.yaml in ${repo_dir} and returns the -# list of dependencies, including transitive ones, in out_var. -# -# The returned dependencies are topologically sorted. -# -# Example output for qtdeclarative: -# qtbase;qtimageformats;qtlanguageserver;qtshadertools;qtsvg -# -function(qt_internal_read_repo_dependencies out_var repo_dir) - set(seen ${ARGN}) - set(dependencies "") - set(in_dependencies_section FALSE) - set(dependencies_file "${repo_dir}/dependencies.yaml") - if(EXISTS "${dependencies_file}") - file(STRINGS "${dependencies_file}" lines) - foreach(line IN LISTS lines) - if(line MATCHES "^([^ ]+):") - if(CMAKE_MATCH_1 STREQUAL "dependencies") - set(in_dependencies_section TRUE) - else() - set(in_dependencies_section FALSE) - endif() - elseif(in_dependencies_section AND line MATCHES "^ (.+):$") - set(dependency "${CMAKE_MATCH_1}") - set(dependency_repo_dir "${repo_dir}/${dependency}") - string(REGEX MATCH "[^/]+$" dependency "${dependency}") - if(NOT dependency IN_LIST seen) - qt_internal_read_repo_dependencies(subdeps "${dependency_repo_dir}" - ${seen} ${dependency}) - list(APPEND dependencies ${subdeps} ${dependency}) - endif() - endif() - endforeach() - list(REMOVE_DUPLICATES dependencies) - endif() - set(${out_var} "${dependencies}" PARENT_SCOPE) -endfunction() - set(QT_BACKUP_CMAKE_INSTALL_PREFIX_BEFORE_EXTRA_INCLUDE "${CMAKE_INSTALL_PREFIX}") +# This depends on qt_internal_read_repo_dependencies existing. if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/QtBuildInternalsExtra.cmake") include(${CMAKE_CURRENT_LIST_DIR}/QtBuildInternalsExtra.cmake) endif() -# The variables might have already been set in QtBuildInternalsExtra.cmake if the file is included -# while building a new module and not QtBase. In that case, stop overriding the value. -if(NOT INSTALL_CMAKE_NAMESPACE) - set(INSTALL_CMAKE_NAMESPACE "Qt${PROJECT_VERSION_MAJOR}" - CACHE STRING "CMake namespace [Qt${PROJECT_VERSION_MAJOR}]") -endif() -if(NOT QT_CMAKE_EXPORT_NAMESPACE) - set(QT_CMAKE_EXPORT_NAMESPACE "Qt${PROJECT_VERSION_MAJOR}" - CACHE STRING "CMake namespace used when exporting targets [Qt${PROJECT_VERSION_MAJOR}]") -endif() +macro(qt_internal_setup_cmake_and_export_namespace) + # The variables might have already been set in QtBuildInternalsExtra.cmake if the file is + # included while building a new module and not QtBase. In that case, stop overriding the value. + if(NOT INSTALL_CMAKE_NAMESPACE) + set(INSTALL_CMAKE_NAMESPACE "Qt${PROJECT_VERSION_MAJOR}" + CACHE STRING "CMake namespace [Qt${PROJECT_VERSION_MAJOR}]") + endif() + if(NOT QT_CMAKE_EXPORT_NAMESPACE) + set(QT_CMAKE_EXPORT_NAMESPACE "Qt${PROJECT_VERSION_MAJOR}" + CACHE STRING "CMake namespace used when exporting targets [Qt${PROJECT_VERSION_MAJOR}]") + endif() +endmacro() 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 @@ -95,1359 +54,15 @@ macro(qt_set_up_build_internals_paths) endif() endmacro() +qt_internal_setup_cmake_and_export_namespace() + # Set up the build internal paths unless explicitly requested not to. if(NOT QT_BUILD_INTERNALS_SKIP_CMAKE_MODULE_PATH_ADDITION) + # Depends on qt_internal_setup_cmake_and_export_namespace 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) - -function(qt_build_internals_disable_pkg_config_if_needed) - # pkg-config should not be used by default on Darwin and Windows platforms (and QNX), as defined - # in the qtbase/configure.json. Unfortunately by the time the feature is evaluated there are - # already a few find_package() calls that try to use the FindPkgConfig module. - # Thus, we have to duplicate the condition logic here and disable pkg-config for those platforms - # by default. - # We also need to check if the pkg-config executable exists, to mirror the condition test in - # configure.json. We do that by trying to find the executable ourselves, and not delegating to - # the FindPkgConfig module because that has more unwanted side-effects. - # - # Note that on macOS, if the pkg-config feature is enabled by the user explicitly, we will also - # tell CMake to consider paths like /usr/local (Homebrew) as system paths when looking for - # packages. - # We have to do that because disabling these paths but keeping pkg-config - # enabled won't enable finding all system libraries via pkg-config alone, many libraries can - # only be found via FooConfig.cmake files which means /usr/local should be in the system prefix - # path. - - set(pkg_config_enabled ON) - qt_build_internals_find_pkg_config_executable() - - if(APPLE OR WIN32 OR QNX OR ANDROID OR WASM OR (NOT PKG_CONFIG_EXECUTABLE)) - set(pkg_config_enabled OFF) - endif() - - # Features won't have been evaluated yet if this is the first run, have to evaluate this here - if ((NOT DEFINED "FEATURE_pkg_config") AND (DEFINED "INPUT_pkg_config") - AND (NOT "${INPUT_pkg_config}" STREQUAL "undefined") - AND (NOT "${INPUT_pkg_config}" STREQUAL "")) - if(INPUT_pkg_config) - set(FEATURE_pkg_config ON) - else() - set(FEATURE_pkg_config OFF) - endif() - endif() - - # If user explicitly specified a value for the feature, honor it, even if it might break - # the build. - if(DEFINED FEATURE_pkg_config) - if(FEATURE_pkg_config) - set(pkg_config_enabled ON) - else() - set(pkg_config_enabled OFF) - endif() - endif() - - set(FEATURE_pkg_config "${pkg_config_enabled}" CACHE STRING "Using pkg-config") - if(NOT pkg_config_enabled) - qt_build_internals_disable_pkg_config() - else() - unset(PKG_CONFIG_EXECUTABLE CACHE) - endif() -endfunction() - -# This is a copy of the first few lines in FindPkgConfig.cmake. -function(qt_build_internals_find_pkg_config_executable) - # find pkg-config, use PKG_CONFIG if set - if((NOT PKG_CONFIG_EXECUTABLE) AND (NOT "$ENV{PKG_CONFIG}" STREQUAL "")) - set(PKG_CONFIG_EXECUTABLE "$ENV{PKG_CONFIG}" CACHE FILEPATH "pkg-config executable") - endif() - find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config DOC "pkg-config executable") - mark_as_advanced(PKG_CONFIG_EXECUTABLE) -endfunction() - -function(qt_build_internals_disable_pkg_config) - # Disable pkg-config by setting an empty executable path. There's no documented way to - # mark the package as not found, but we can force all pkg_check_modules calls to do nothing - # by setting the variable to an empty value. - set(PKG_CONFIG_EXECUTABLE "" CACHE STRING "Disabled pkg-config usage." FORCE) -endfunction() - -if(NOT QT_BUILD_INTERNALS_SKIP_PKG_CONFIG_ADJUSTMENT) - qt_build_internals_disable_pkg_config_if_needed() -endif() - -macro(qt_build_internals_find_pkg_config) - # Find package config once before any system prefix modifications. - find_package(PkgConfig QUIET) -endmacro() - -if(NOT QT_BUILD_INTERNALS_SKIP_FIND_PKG_CONFIG) - qt_build_internals_find_pkg_config() -endif() - -function(qt_build_internals_set_up_system_prefixes) - if(APPLE AND NOT FEATURE_pkg_config) - # Remove /usr/local and other paths like that which CMake considers as system prefixes on - # darwin platforms. CMake considers them as system prefixes, but in qmake / Qt land we only - # consider the SDK path as a system prefix. - # 3rd party libraries in these locations should not be picked up when building Qt, - # unless opted-in via the pkg-config feature, which in turn will disable this behavior. - # - # Note that we can't remove /usr as a system prefix path, because many programs won't be - # found then (e.g. perl). - set(QT_CMAKE_SYSTEM_PREFIX_PATH_BACKUP "${CMAKE_SYSTEM_PREFIX_PATH}" PARENT_SCOPE) - set(QT_CMAKE_SYSTEM_FRAMEWORK_PATH_BACKUP "${CMAKE_SYSTEM_FRAMEWORK_PATH}" PARENT_SCOPE) - - list(REMOVE_ITEM CMAKE_SYSTEM_PREFIX_PATH - "/usr/local" # Homebrew - "/opt/homebrew" # Apple Silicon Homebrew - "/usr/X11R6" - "/usr/pkg" - "/opt" - "/sw" # Fink - "/opt/local" # MacPorts - ) - if(_CMAKE_INSTALL_DIR) - list(REMOVE_ITEM CMAKE_SYSTEM_PREFIX_PATH "${_CMAKE_INSTALL_DIR}") - endif() - list(REMOVE_ITEM CMAKE_SYSTEM_FRAMEWORK_PATH "~/Library/Frameworks") - set(CMAKE_SYSTEM_PREFIX_PATH "${CMAKE_SYSTEM_PREFIX_PATH}" PARENT_SCOPE) - set(CMAKE_SYSTEM_FRAMEWORK_PATH "${CMAKE_SYSTEM_FRAMEWORK_PATH}" PARENT_SCOPE) - - # Also tell qt_find_package() not to use PATH when looking for packages. - # We can't simply set CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH to OFF because that will break - # find_program(), and for instance ccache won't be found. - # That's why we set a different variable which is used by qt_find_package. - set(QT_NO_USE_FIND_PACKAGE_SYSTEM_ENVIRONMENT_PATH "ON" PARENT_SCOPE) - endif() -endfunction() - -if(NOT QT_BUILD_INTERNALS_SKIP_SYSTEM_PREFIX_ADJUSTMENT) - qt_build_internals_set_up_system_prefixes() -endif() - -# The macro sets all the necessary pre-conditions and setup consistent environment for building -# the Qt repository. It has to be called right after the find_package(Qt6 COMPONENTS BuildInternals) -# call. Otherwise we cannot make sure that all the required policies will be applied to the Qt -# components that are involved in build procedure. -macro(qt_internal_project_setup) - # Check for the minimum CMake version. - include(QtCMakeVersionHelpers) - qt_internal_require_suitable_cmake_version() - qt_internal_upgrade_cmake_policies() -endmacro() - -macro(qt_build_internals_set_up_private_api) - # TODO: this call needs to be removed once all repositories got the qtbase update - qt_internal_project_setup() - - # 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() - -# find all targets defined in $subdir by recursing through all added subdirectories -# populates $qt_repo_targets with a ;-list of non-UTILITY targets -macro(qt_build_internals_get_repo_targets subdir) - get_directory_property(_targets DIRECTORY "${subdir}" BUILDSYSTEM_TARGETS) - if(_targets) - foreach(_target IN LISTS _targets) - get_target_property(_type ${_target} TYPE) - if(NOT ${_type} STREQUAL "UTILITY") - list(APPEND qt_repo_targets "${_target}") - endif() - endforeach() - endif() - - get_directory_property(_directories DIRECTORY "${subdir}" SUBDIRECTORIES) - if (_directories) - foreach(_directory IN LISTS _directories) - qt_build_internals_get_repo_targets("${_directory}") - endforeach() - endif() -endmacro() - -# add toplevel targets for each subdirectory, e.g. qtbase_src -function(qt_build_internals_add_toplevel_targets) - set(qt_repo_target_all "") - get_directory_property(directories DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" SUBDIRECTORIES) - foreach(directory IN LISTS directories) - set(qt_repo_targets "") - get_filename_component(qt_repo_target_basename ${directory} NAME) - qt_build_internals_get_repo_targets("${directory}") - if (qt_repo_targets) - set(qt_repo_target_name "${qt_repo_targets_name}_${qt_repo_target_basename}") - message(DEBUG "${qt_repo_target_name} depends on ${qt_repo_targets}") - add_custom_target("${qt_repo_target_name}" - COMMENT "Building everything in ${qt_repo_targets_name}/${qt_repo_target_basename}") - add_dependencies("${qt_repo_target_name}" ${qt_repo_targets}) - list(APPEND qt_repo_target_all "${qt_repo_target_name}") - - # Create special dependency target for External Project examples excluding targets - # marked as skipped. - set(qt_repo_target_name - "${qt_repo_targets_name}_${qt_repo_target_basename}_for_examples") - add_custom_target("${qt_repo_target_name}") - - set(unskipped_targets "") - foreach(target IN LISTS qt_repo_targets) - if(TARGET "${target}") - qt_internal_is_target_skipped_for_examples("${target}" is_skipped) - if(NOT is_skipped) - list(APPEND unskipped_targets "${target}") - endif() - endif() - endforeach() - if(unskipped_targets) - add_dependencies("${qt_repo_target_name}" ${unskipped_targets}) - endif() - endif() - - endforeach() - if (qt_repo_target_all) - # Note qt_repo_targets_name is different from qt_repo_target_name that is used above. - add_custom_target("${qt_repo_targets_name}" - COMMENT "Building everything in ${qt_repo_targets_name}") - add_dependencies("${qt_repo_targets_name}" ${qt_repo_target_all}) - message(DEBUG "${qt_repo_targets_name} depends on ${qt_repo_target_all}") - endif() -endfunction() - -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() - - # The qtbase call is handled in qtbase/CMakeLists.txt. - # This call is used for projects other than qtbase, including for other project's standalone - # tests. - # Because the function uses QT_FEATURE_foo values, it's important that find_package(Qt6Core) is - # called before this function. but that's usually the case for Qt repos. - if(NOT PROJECT_NAME STREQUAL "QtBase") - qt_internal_set_up_config_optimizations_like_in_qmake() - endif() -endmacro() - -# Minimum setup required to have any CMakeList.txt build as as a standalone -# project after importing BuildInternals -macro(qt_prepare_standalone_project) - qt_set_up_build_internals_paths() - qt_build_internals_set_up_private_api() - qt_enable_cmake_languages() -endmacro() - -# Define a repo target set, and store accompanying information. -# -# A repo target set is a subset of targets in a Qt module repository. To build a repo target set, -# set QT_BUILD_SINGLE_REPO_TARGET_SET to the name of the repo target set. -# -# This function is to be called in the top-level project file of a repository, -# before qt_internal_prepare_single_repo_target_set_build() -# -# This function stores information in variables of the parent scope. -# -# Positional Arguments: -# name - The name of this repo target set. -# -# Named Arguments: -# DEPENDS - List of Qt6 COMPONENTS that are build dependencies of this repo target set. -function(qt_internal_define_repo_target_set name) - set(oneValueArgs DEPENDS) - set(prefix QT_REPO_TARGET_SET_) - cmake_parse_arguments(${prefix}${name} "" ${oneValueArgs} "" ${ARGN}) - foreach(arg IN LISTS oneValueArgs) - set(${prefix}${name}_${arg} ${${prefix}${name}_${arg}} PARENT_SCOPE) - endforeach() - set(QT_REPO_KNOWN_TARGET_SETS "${QT_REPO_KNOWN_TARGET_SETS};${name}" PARENT_SCOPE) -endfunction() - -# Setup a single repo target set build if QT_BUILD_SINGLE_REPO_TARGET_SET is defined. -# -# This macro must be called in the top-level project file of the repository after all repo target -# sets have been defined. -macro(qt_internal_prepare_single_repo_target_set_build) - if(DEFINED QT_BUILD_SINGLE_REPO_TARGET_SET) - if(NOT QT_BUILD_SINGLE_REPO_TARGET_SET IN_LIST QT_REPO_KNOWN_TARGET_SETS) - message(FATAL_ERROR - "Repo target set '${QT_BUILD_SINGLE_REPO_TARGET_SET}' is undefined.") - endif() - message(STATUS - "Preparing single repo target set build of ${QT_BUILD_SINGLE_REPO_TARGET_SET}") - if (NOT "${QT_REPO_TARGET_SET_${QT_BUILD_SINGLE_REPO_TARGET_SET}_DEPENDS}" STREQUAL "") - find_package(${INSTALL_CMAKE_NAMESPACE} ${PROJECT_VERSION} CONFIG REQUIRED - COMPONENTS ${QT_REPO_TARGET_SET_${QT_BUILD_SINGLE_REPO_TARGET_SET}_DEPENDS}) - endif() - endif() -endmacro() - -macro(qt_build_repo_begin) - list(APPEND CMAKE_MESSAGE_CONTEXT "${PROJECT_NAME}") - - qt_build_internals_set_up_private_api() - - # Prevent installation in non-prefix builds. - # We need to associate targets with export names, and that is only possible to do with the - # install(TARGETS) command. But in a non-prefix build, we don't want to install anything. - # To make sure that developers don't accidentally run make install, add bail out code to - # cmake_install.cmake. - if(NOT QT_WILL_INSTALL) - # In a top-level build, print a message only in qtbase, which is the first repository. - if(NOT QT_SUPERBUILD OR (PROJECT_NAME STREQUAL "QtBase")) - install(CODE [[message(FATAL_ERROR - "Qt was configured as non-prefix build. " - "Installation is not supported for this arrangement.")]]) - endif() - - install(CODE [[return()]]) - endif() - - qt_enable_cmake_languages() - - qt_internal_generate_binary_strip_wrapper() - - # 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) - add_custom_target(install_qch_docs) - add_custom_target(install_docs) - add_dependencies(html_docs generate_docs) - add_dependencies(docs html_docs qch_docs) - add_dependencies(install_docs install_html_docs install_qch_docs) - endif() - - if(NOT TARGET sync_headers) - add_custom_target(sync_headers) - endif() - - # Add global qt_plugins, qpa_plugins and qpa_default_plugins convenience custom targets. - # Internal executables will add a dependency on the qpa_default_plugins target, - # so that building and running a test ensures it won't fail at runtime due to a missing qpa - # plugin. - if(NOT TARGET qt_plugins) - add_custom_target(qt_plugins) - add_custom_target(qpa_plugins) - add_custom_target(qpa_default_plugins) - endif() - - string(TOLOWER ${PROJECT_NAME} project_name_lower) - - # Target to build all plugins that are part of the current repo. - set(qt_repo_plugins "qt_plugins_${project_name_lower}") - if(NOT TARGET ${qt_repo_plugins}) - add_custom_target(${qt_repo_plugins}) - endif() - - # Target to build all plugins that are part of the current repo and the current repo's - # dependencies plugins. Used for external project example dependencies. - set(qt_repo_plugins_recursive "${qt_repo_plugins}_recursive") - if(NOT TARGET ${qt_repo_plugins_recursive}) - add_custom_target(${qt_repo_plugins_recursive}) - add_dependencies(${qt_repo_plugins_recursive} "${qt_repo_plugins}") - endif() - - qt_internal_read_repo_dependencies(qt_repo_deps "${PROJECT_SOURCE_DIR}") - if(qt_repo_deps) - foreach(qt_repo_dep IN LISTS qt_repo_deps) - if(TARGET qt_plugins_${qt_repo_dep}) - message(DEBUG - "${qt_repo_plugins_recursive} depends on qt_plugins_${qt_repo_dep}") - add_dependencies(${qt_repo_plugins_recursive} "qt_plugins_${qt_repo_dep}") - endif() - endforeach() - endif() - - set(qt_repo_targets_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_target_name} ${qt_docs_html_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 top-level prepare_docs target depend on the repository-level prepare_docs_<repo> target. - add_dependencies(prepare_docs ${qt_docs_prepare_target_name}) - - # Make top-level install_*_docs targets depend on the repository-level install_*_docs targets. - add_dependencies(install_html_docs ${qt_docs_install_html_target_name}) - add_dependencies(install_qch_docs ${qt_docs_install_qch_target_name}) - - # Add host_tools meta target, so that developrs can easily build only tools and their - # dependencies when working in qtbase. - if(NOT TARGET host_tools) - add_custom_target(host_tools) - add_custom_target(bootstrap_tools) - endif() - - # Add benchmark meta target. It's collection of all benchmarks added/registered by - # 'qt_internal_add_benchmark' helper. - if(NOT TARGET benchmark) - add_custom_target(benchmark) - endif() - - if(QT_INTERNAL_SYNCED_MODULES) - set_property(GLOBAL PROPERTY _qt_synced_modules ${QT_INTERNAL_SYNCED_MODULES}) - endif() -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}) - qt_path_join(__qt_repo_build_dir ${QT_CONFIG_BUILD_DIR} ${INSTALL_CMAKE_NAMESPACE}) - - if(NOT PROJECT_NAME STREQUAL "QtBase") - if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cmake") - qt_copy_or_install(DIRECTORY cmake/ - DESTINATION "${__qt_repo_install_dir}" - FILES_MATCHING PATTERN "Find*.cmake" - ) - if(QT_SUPERBUILD AND QT_WILL_INSTALL) - file(COPY cmake/ - DESTINATION "${__qt_repo_build_dir}" - FILES_MATCHING PATTERN "Find*.cmake" - ) - endif() - endif() - endif() - - if(NOT QT_SUPERBUILD) - qt_print_feature_summary() - endif() - endif() - - qt_build_internals_add_toplevel_targets() - - if(NOT QT_SUPERBUILD) - qt_print_build_instructions() - endif() - - get_property(synced_modules GLOBAL PROPERTY _qt_synced_modules) - if(synced_modules) - set(QT_INTERNAL_SYNCED_MODULES ${synced_modules} CACHE INTERNAL - "List of the synced modules. Prevents running syncqt.cpp after the first configuring.") - endif() - - if(NOT QT_SUPERBUILD) - qt_internal_save_previously_visited_packages() - endif() - - if(QT_INTERNAL_FRESH_REQUESTED) - set(QT_INTERNAL_FRESH_REQUESTED "FALSE" CACHE INTERNAL "") - endif() - - list(POP_BACK CMAKE_MESSAGE_CONTEXT) -endmacro() - -macro(qt_build_repo) - qt_build_repo_begin(${ARGN}) - - qt_build_repo_impl_find_package_tests() - qt_build_repo_impl_src() - qt_build_repo_impl_tools() - qt_build_repo_impl_tests() - - qt_build_repo_end() - - qt_build_repo_impl_examples() -endmacro() - -macro(qt_build_repo_impl_find_package_tests) - # 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 (QT_BUILD_TESTS AND NOT QT_BUILD_STANDALONE_TESTS) - # When looking for the Test package, do it using the Qt6 package version, in case if - # PROJECT_VERSION is following a different versioning scheme. - if(Qt6_VERSION) - set(_qt_build_repo_impl_find_package_tests_version "${Qt6_VERSION}") - else() - set(_qt_build_repo_impl_find_package_tests_version "${PROJECT_VERSION}") - endif() - - find_package(Qt6 - "${_qt_build_repo_impl_find_package_tests_version}" - CONFIG REQUIRED COMPONENTS Test) - unset(_qt_build_repo_impl_find_package_tests_version) - endif() -endmacro() - -macro(qt_build_repo_impl_src) - if(NOT QT_BUILD_STANDALONE_TESTS) - if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/CMakeLists.txt") - add_subdirectory(src) - endif() - endif() - if(QT_FEATURE_lttng AND NOT TARGET LTTng::UST) - qt_find_package(LTTngUST PROVIDED_TARGETS LTTng::UST - MODULE_NAME global QMAKE_LIB lttng-ust) - endif() -endmacro() - -macro(qt_build_repo_impl_tools) - if(NOT QT_BUILD_STANDALONE_TESTS) - if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tools/CMakeLists.txt") - add_subdirectory(tools) - endif() - endif() -endmacro() - -macro(qt_build_repo_impl_tests) - if (QT_BUILD_TESTS AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt") - add_subdirectory(tests) - if(NOT QT_BUILD_TESTS_BY_DEFAULT) - set_property(DIRECTORY tests PROPERTY EXCLUDE_FROM_ALL TRUE) - endif() - endif() -endmacro() +include(QtBuildHelpers) -macro(qt_build_repo_impl_examples) - if(QT_BUILD_EXAMPLES - AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/examples/CMakeLists.txt" - AND NOT QT_BUILD_STANDALONE_TESTS) - message(STATUS "Configuring examples.") - add_subdirectory(examples) - 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_config_files_path out_var) - set(path "${QT_CONFIG_INSTALL_DIR}/${INSTALL_CMAKE_NAMESPACE}BuildInternals/StandaloneTests") - - # QT_CONFIG_INSTALL_DIR is relative in prefix builds. - if(QT_WILL_INSTALL) - if(DEFINED CMAKE_STAGING_PREFIX) - qt_path_join(path "${CMAKE_STAGING_PREFIX}" "${path}") - else() - qt_path_join(path "${CMAKE_INSTALL_PREFIX}" "${path}") - endif() - endif() - - set("${out_var}" "${path}" PARENT_SCOPE) -endfunction() - -function(qt_internal_get_standalone_tests_config_file_name out_var) - # When doing a "single repo target set" build (like in qtscxqml) ensure we use a unique tests - # config file for each repo target set. Using the PROJECT_NAME only is not enough because - # the same file will be overridden with different content on each repo set install. - set(tests_config_file_name "${PROJECT_NAME}") - - if(QT_BUILD_SINGLE_REPO_TARGET_SET) - string(APPEND tests_config_file_name "RepoSet${QT_BUILD_SINGLE_REPO_TARGET_SET}") - endif() - string(APPEND tests_config_file_name "TestsConfig.cmake") - - set(${out_var} "${tests_config_file_name}" PARENT_SCOPE) -endfunction() - -macro(qt_build_tests) - set(CMAKE_UNITY_BUILD OFF) - - 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_config_files_path(_qt_build_tests_install_prefix) - - qt_internal_get_standalone_tests_config_file_name(_qt_tests_config_file_name) - set(_qt_standalone_tests_config_file_path - "${_qt_build_tests_install_prefix}/${_qt_tests_config_file_name}") - include("${_qt_standalone_tests_config_file_path}" - OPTIONAL - RESULT_VARIABLE _qt_standalone_tests_included) - if(NOT _qt_standalone_tests_included) - message(DEBUG - "Standalone tests config file not included because it does not exist: " - "${_qt_standalone_tests_config_file_path}" - ) - else() - message(DEBUG - "Standalone tests config file included successfully: " - "${_qt_standalone_tests_config_file_path}" - ) - endif() - - unset(_qt_standalone_tests_config_file_path) - unset(_qt_standalone_tests_included) - unset(_qt_tests_config_file_name) - - # Of course we always need the test module as well. - # When looking for the Test package, do it using the Qt6 package version, in case if - # PROJECT_VERSION is following a different versioning scheme. - if(Qt6_VERSION) - set(_qt_build_tests_package_version "${Qt6_VERSION}") - else() - set(_qt_build_tests_package_version "${PROJECT_VERSION}") - endif() - find_package(Qt6 "${_qt_build_tests_package_version}" CONFIG REQUIRED COMPONENTS Test) - unset(_qt_build_tests_package_version) - - # 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() - - if(NOT QT_SUPERBUILD) - # Set up fake standalone tests install prefix, so we don't pollute the Qt install - # prefix. For super builds it needs to be done in qt5/CMakeLists.txt. - qt_set_up_fake_standalone_tests_install_prefix() - endif() - else() - if(ANDROID) - # When building in-tree tests we need to specify the QT_ANDROID_ABIS list. Since we - # build Qt for the single ABI, build tests for this ABI only. - set(QT_ANDROID_ABIS "${CMAKE_ANDROID_ARCH_ABI}") - endif() - endif() - - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/auto/CMakeLists.txt") - add_subdirectory(auto) - endif() - if(NOT QT_BUILD_MINIMAL_STATIC_TESTS AND NOT QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS) - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/baseline/CMakeLists.txt") - add_subdirectory(baseline) - 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" AND QT_BUILD_MANUAL_TESTS) - add_subdirectory(manual) - endif() - endif() - - set(CMAKE_UNITY_BUILD ${QT_UNITY_BUILD}) -endmacro() - -function(qt_compute_relative_path_from_cmake_config_dir_to_prefix) - # Compute the reverse relative path from the CMake config dir to the install prefix. - # This is used in QtBuildInternalsExtras to create a relocatable relative install prefix path. - # This path is used for finding syncqt and other things, regardless of initial install prefix - # (e.g installed Qt was archived and unpacked to a different path on a different machine). - # - # This is meant to be called only once when configuring qtbase. - # - # Similar code exists in Qt6CoreConfigExtras.cmake.in and src/corelib/CMakeLists.txt which - # might not be needed anymore. - if(CMAKE_STAGING_PREFIX) - set(__qt_prefix "${CMAKE_STAGING_PREFIX}") - else() - set(__qt_prefix "${CMAKE_INSTALL_PREFIX}") - endif() - - if(QT_WILL_INSTALL) - get_filename_component(clean_config_prefix - "${__qt_prefix}/${QT_CONFIG_INSTALL_DIR}" ABSOLUTE) - else() - get_filename_component(clean_config_prefix "${QT_CONFIG_BUILD_DIR}" ABSOLUTE) - endif() - file(RELATIVE_PATH - qt_path_from_cmake_config_dir_to_prefix - "${clean_config_prefix}" "${__qt_prefix}") - set(qt_path_from_cmake_config_dir_to_prefix "${qt_path_from_cmake_config_dir_to_prefix}" - PARENT_SCOPE) -endfunction() - -function(qt_get_relocatable_install_prefix out_var) - # We need to compute it only once while building qtbase. Afterwards it's loaded from - # QtBuildInternalsExtras.cmake. - if(QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX) - return() - endif() - # The QtBuildInternalsExtras value is dynamically computed, whereas the initial qtbase - # configuration uses an absolute path. - set(${out_var} "${CMAKE_INSTALL_PREFIX}" PARENT_SCOPE) -endfunction() - -function(qt_set_up_fake_standalone_tests_install_prefix) - # Set a fake local (non-cache) CMAKE_INSTALL_PREFIX. - # Needed for standalone tests, we don't want to accidentally install a test into the Qt prefix. - # Allow opt-out, if a user knows what they're doing. - if(QT_NO_FAKE_STANDALONE_TESTS_INSTALL_PREFIX) - return() - endif() - set(new_install_prefix "${CMAKE_BINARY_DIR}/fake_prefix") - - # It's IMPORTANT that this is not a cache variable. Otherwise - # qt_get_standalone_tests_confg_files_path() will not work on re-configuration. - message(STATUS - "Setting local standalone test install prefix (non-cached) to '${new_install_prefix}'.") - set(CMAKE_INSTALL_PREFIX "${new_install_prefix}" PARENT_SCOPE) - - # We also need to clear the staging prefix if it's set, otherwise CMake will modify any computed - # rpaths containing the staging prefix to point to the new fake prefix, which is not what we - # want. This replacement is done in cmComputeLinkInformation::GetRPath(). - # - # By clearing the staging prefix for the standalone tests, any detected link time - # rpaths will be embedded as-is, which will point to the place where Qt was installed (aka - # the staging prefix). - if(DEFINED CMAKE_STAGING_PREFIX) - message(STATUS "Clearing local standalone test staging prefix (non-cached).") - set(CMAKE_STAGING_PREFIX "" PARENT_SCOPE) - endif() -endfunction() - -# Mean to be called when configuring examples as part of the main build tree, as well as for CMake -# tests (tests that call CMake to try and build CMake applications). -macro(qt_internal_set_up_build_dir_package_paths) - list(PREPEND CMAKE_PREFIX_PATH "${QT_BUILD_DIR}/${INSTALL_LIBDIR}/cmake") - # Make sure the CMake config files do not recreate the already-existing targets - set(QT_NO_CREATE_TARGETS TRUE) -endmacro() - -macro(qt_examples_build_begin) - set(options EXTERNAL_BUILD) - set(singleOpts "") - set(multiOpts DEPENDS) - - cmake_parse_arguments(arg "${options}" "${singleOpts}" "${multiOpts}" ${ARGN}) - - set(CMAKE_UNITY_BUILD OFF) - - # Use by qt_internal_add_example. - set(QT_EXAMPLE_BASE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") - - 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_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. - # 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_EXAMPLES_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). - - # This function gets all targets below this directory (excluding custom targets and aliases) - 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(_real_targets "") - if(_sub_targets) - foreach(__target IN LISTS _sub_targets) - get_target_property(target_type ${__target} TYPE) - if(NOT target_type STREQUAL "UTILITY" AND NOT target_type STREQUAL "ALIAS") - list(APPEND _real_targets ${__target}) - endif() - endforeach() - endif() - set(${_result} ${${_result}} ${_real_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() - 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() - -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}) - endif() -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 - # ${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. -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}") - - # 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} ${ARGN}) -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}") - - 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, - # 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}") - - # 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. - # Both are handled by this assignment. - set(qt_prefixes "${QT_BUILD_DIR}") - - # Appending to QT_ADDITIONAL_PACKAGES_PREFIX_PATH helps find Qt6 components in - # non-qtbase prefix builds because we use NO_DEFAULT_PATH in find_package calls. - # It also handles the cross-compiling scenario where we need to adjust both the root path - # and prefixes, with the prefixes containing lib/cmake. This leverages the infrastructure - # previously added for Conan. - list(APPEND QT_ADDITIONAL_PACKAGES_PREFIX_PATH ${qt_prefixes}) - - # In a prefix build, look up all repo Config files in the install prefix, - # except for the current repo, which will look in the build dir (handled above). - if(QT_WILL_INSTALL) - list(APPEND qt_prefixes "${QT6_INSTALL_PREFIX}") - endif() - endif() - - 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() - - # 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_EXAMPLES_CMAKE_PREFIX_PATH:STRING - QT_ADDITIONAL_PACKAGES_PREFIX_PATH:STRING - CMAKE_FIND_ROOT_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 - ) - - foreach(var_with_type IN LISTS vars_to_pass_if_defined) - 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. - - # 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}") - - 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_prefix}" - 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() - -if ("STANDALONE_TEST" IN_LIST Qt6BuildInternals_FIND_COMPONENTS) - include(${CMAKE_CURRENT_LIST_DIR}/QtStandaloneTestTemplateProject/Main.cmake) - if (NOT PROJECT_VERSION_MAJOR) - get_property(_qt_major_version TARGET ${QT_CMAKE_EXPORT_NAMESPACE}::Core PROPERTY INTERFACE_QT_MAJOR_VERSION) - set(PROJECT_VERSION ${Qt${_qt_major_version}Core_VERSION}) - - string(REPLACE "." ";" _qt_core_version_list ${PROJECT_VERSION}) - list(GET _qt_core_version_list 0 PROJECT_VERSION_MAJOR) - list(GET _qt_core_version_list 1 PROJECT_VERSION_MINOR) - list(GET _qt_core_version_list 2 PROJECT_VERSION_PATCH) - endif() -endif() - -function(qt_internal_static_link_order_test) - # The CMake versions greater than 3.21 take care about the resource object files order in a - # linker line, it's expected that all object files are located at the beginning of the linker - # line. - # No need to run the test. - if(CMAKE_VERSION VERSION_LESS 3.21) - __qt_internal_check_link_order_matters(link_order_matters) - if(link_order_matters) - set(summary_message "no") - else() - set(summary_message "yes") - endif() - else() - set(summary_message "yes") - endif() - qt_configure_add_summary_entry(TYPE "message" - ARGS "Linker can resolve circular dependencies" - MESSAGE "${summary_message}" - ) -endfunction() - -function(qt_internal_check_cmp0099_available) - # Don't care about CMP0099 in CMake versions greater than or equal to 3.21 - if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.21) - return() - endif() - - __qt_internal_check_cmp0099_available(result) - if(result) - set(summary_message "yes") - else() - set(summary_message "no") - endif() - qt_configure_add_summary_entry(TYPE "message" - ARGS "CMake policy CMP0099 is supported" - MESSAGE "${summary_message}" - ) -endfunction() - -function(qt_internal_run_common_config_tests) - qt_configure_add_summary_section(NAME "Common build options") - qt_internal_static_link_order_test() - qt_internal_check_cmp0099_available() - qt_configure_end_summary_section() -endfunction() - -# It is used in QtWebEngine to replace the REALPATH with ABSOLUTE path, which is -# useful for building Qt in Homebrew. -function(qt_internal_get_filename_path_mode out_var) - set(mode REALPATH) - if(APPLE AND QT_ALLOW_SYMLINK_IN_PATHS) - set(mode ABSOLUTE) - message(WARNING - "QT_ALLOW_SYMLINK_IN_PATHS is enabled. " - "This is not recommended, and it may lead to unexpected issues. " - "E.g., When building QtWebEngine, enabling this option may result in build " - "issues in certain platforms. See https://bugreports.qt.io/browse/QTBUG-59769." - ) - endif() - set(${out_var} ${mode} PARENT_SCOPE) -endfunction() +qt_internal_include_all_helpers() +qt_internal_setup_build_internals() diff --git a/cmake/QtBuildInternals/QtStandaloneTestTemplateProject/CMakeLists.txt b/cmake/QtBuildInternals/QtStandaloneTestTemplateProject/CMakeLists.txt index 09c4dad4bb..766e372666 100644 --- a/cmake/QtBuildInternals/QtStandaloneTestTemplateProject/CMakeLists.txt +++ b/cmake/QtBuildInternals/QtStandaloneTestTemplateProject/CMakeLists.txt @@ -2,11 +2,6 @@ # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16) -project(qt_single_test VERSION 6.0.0 LANGUAGES C CXX ASM) - -find_package(Qt6 REQUIRED COMPONENTS BuildInternals Core) - -include(${CMAKE_CURRENT_LIST_DIR}/Main.cmake NO_POLICY_SCOPE) # 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. @@ -30,5 +25,14 @@ if(NOT IS_DIRECTORY "${absolute_project_path}") endif() endif() +# Get the project name base on test directory name +get_filename_component(project_name "${absolute_project_path}" NAME) + +project(${project_name} VERSION 6.0.0 LANGUAGES C CXX ASM) + +find_package(Qt6 REQUIRED COMPONENTS BuildInternals Core) + +include(${CMAKE_CURRENT_LIST_DIR}/Main.cmake NO_POLICY_SCOPE) + # Add the test project path as a subdirectory project. add_subdirectory("${absolute_project_path}" "build_dir") diff --git a/cmake/QtBuildInternals/QtStandaloneTestTemplateProject/Main.cmake b/cmake/QtBuildInternals/QtStandaloneTestTemplateProject/Main.cmake index c7222c8b71..bd0984f314 100644 --- a/cmake/QtBuildInternals/QtStandaloneTestTemplateProject/Main.cmake +++ b/cmake/QtBuildInternals/QtStandaloneTestTemplateProject/Main.cmake @@ -2,6 +2,7 @@ # SPDX-License-Identifier: BSD-3-Clause # Includes QtSetup and friends for private CMake API. +set(QT_INTERNAL_IS_STANDALONE_TEST TRUE) qt_internal_project_setup() qt_build_internals_set_up_private_api() @@ -9,9 +10,9 @@ qt_build_internals_set_up_private_api() # 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_config_files_path(standalone_tests_config_path) +qt_get_standalone_parts_config_files_path(standalone_parts_config_path) -file(GLOB config_files "${standalone_tests_config_path}/*") +file(GLOB config_files "${standalone_parts_config_path}/*") foreach(file ${config_files}) include("${file}") endforeach() @@ -22,5 +23,5 @@ qt_set_language_standards() # Just before adding the test, change the local (non-cache) install prefix to something other than # the Qt install prefix, so that tests don't try to install and pollute the Qt install prefix. -# Needs to be called after qt_get_standalone_tests_confg_files_path(). -qt_set_up_fake_standalone_tests_install_prefix() +# Needs to be called after qt_get_standalone_parts_config_files_path(). +qt_internal_set_up_fake_standalone_parts_install_prefix() diff --git a/cmake/QtBuildInternalsExtra.cmake.in b/cmake/QtBuildInternalsExtra.cmake.in index 2dc906b6c6..8985f8178a 100644 --- a/cmake/QtBuildInternalsExtra.cmake.in +++ b/cmake/QtBuildInternalsExtra.cmake.in @@ -1,3 +1,6 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # Propagate common variables via BuildInternals package. set(QT_BUILD_SHARED_LIBS @BUILD_SHARED_LIBS@) option(BUILD_SHARED_LIBS "Build Qt statically or dynamically" @BUILD_SHARED_LIBS@) @@ -103,6 +106,9 @@ set(QT_BUILD_EXAMPLES_AS_EXTERNAL "@QT_BUILD_EXAMPLES_AS_EXTERNAL@" CACHE BOOL # Propagate usage of ccache. set(QT_USE_CCACHE @QT_USE_CCACHE@ CACHE BOOL "Enable the use of ccache") +# Propagate usage of vcpkg, ON by default. +set(QT_USE_VCPKG @QT_USE_VCPKG@ CACHE BOOL "Enable the use of vcpkg") + # Propagate usage of unity build. set(QT_UNITY_BUILD @QT_UNITY_BUILD@ CACHE BOOL "Enable unity (jumbo) build") set(QT_UNITY_BUILD_BATCH_SIZE "@QT_UNITY_BUILD_BATCH_SIZE@" CACHE STRING "Unity build batch size") @@ -129,42 +135,5 @@ if(NOT DEFINED QT_MAX_NEW_POLICY_CMAKE_VERSION) set(QT_MAX_NEW_POLICY_CMAKE_VERSION "@max_new_policy_version@") endif() -get_property(__qt_internal_extras_is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) - -# We want the same build type to be used when configuring all Qt repos or standalone -# tests or single tests. -# To do that, we need to force-set the CMAKE_BUILD_TYPE cache var because CMake itself -# initializes it with the value of CMAKE_BUILD_TYPE_INIT at the start of project -# configuration, so we need to override it. -# Note the value of CMAKE_BUILD_TYPE_INIT is different based on the platform, most -# Linux and macOS platforms will have it empty, but Windows platforms will have a value. -# -# We can't reliably differentiate between a value set on the command line by the user -# and one set by CMake, so we use a few heuristics: -# 1) When using a qt.toolchain.cmake file, we rely on the toolchain file to tell us -# if a value was set by the user at initial configure time. On a 2nd run there will -# always be a value in the cache, but at that point we've already set it to whatever it needs -# to be. -# 2) If a toolchain file is not used, we rely on the value of the CMake internal -# CMAKE_BUILD_TYPE_INIT variable. -# This won't work reliably on Windows where CMAKE_BUILD_TYPE_INIT is non-empty. -# -# Both cases won't handle an empty "" config set by the user, but we claim that's an -# unsupported config when building Qt. -# -# Allow an opt out when QT_NO_FORCE_SET_CMAKE_BUILD_TYPE is set. -# Finally, don't set the variable if a multi-config generator is used. This can happen -# when qtbase is built with a single config, but a test is built with a multi-config generator. -function(qt_internal_force_set_cmake_build_type_conditionally value) - # STREQUAL check needs to be expanded variables because an undefined var is not equal to an - # empty defined var. - if("${CMAKE_BUILD_TYPE}" STREQUAL "${CMAKE_BUILD_TYPE_INIT}" - AND NOT __qt_toolchain_cmake_build_type_before_project_call - AND NOT QT_NO_FORCE_SET_CMAKE_BUILD_TYPE - AND NOT __qt_internal_extras_is_multi_config) - set(CMAKE_BUILD_TYPE "${value}" CACHE STRING "Choose the type of build." FORCE) - endif() -endfunction() - # Extra set of exported variables @QT_EXTRA_BUILD_INTERNALS_VARS@ diff --git a/cmake/QtBuildOptionsHelpers.cmake b/cmake/QtBuildOptionsHelpers.cmake new file mode 100644 index 0000000000..3879920f65 --- /dev/null +++ b/cmake/QtBuildOptionsHelpers.cmake @@ -0,0 +1,383 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# Try to detect if CMAKE_BUILD_TYPE is default initialized by CMake, or it was set by the user. +# +# CMake initializes CMAKE_BUILD_TYPE to the value of CMAKE_BUILD_TYPE_INIT during the first +# project() call if CMAKE_BUILD_TYPE is empty. +# +# Unfortunately on most Windows platforms, it defaults to 'Debug', so we can't differentiate +# between a 'Debug' value set on the command line by the user, a value set by the project, or if it +# was default initialized. +# We need to rely on heuristics to determine that. +# +# We try to check the value of CMAKE_BUILD_TYPE before the first project() call by inspecting +# various variables: +# 1) When using a qt.toolchain.cmake file, we rely on the toolchain file to tell us +# if a value was set by the user at initial configure time via the +# __qt_toolchain_cmake_build_type_before_project_call variable. On a 2nd run there will +# always be a value in the cache, but at that point we've already set it to whatever it needs +# to be. +# 2) Whe configuring qtbase, a top-level qt, or a standalone project we rely on one of the following +# variables being set: +# - __qt_auto_detect_cmake_build_type_before_project_call (e.g for qtbase) +# - __qt_internal_standalone_project_cmake_build_type_before_project_call (e.g for sqldrivers) +# 3) When using a multi-config generator, we assume that the CMAKE_BUILD_TYPE is not default +# initialized. +# 4) The user can also force the build type to be considered non-default-initialized by setting +# QT_NO_FORCE_SET_CMAKE_BUILD_TYPE to TRUE. It has weird naming that doesn't quite correspond +# to the meaning, but it's been called like that for a while now and I'm hesitant to change +# the name in case it's used by various projects. +# +# The code doesn't handle an empty "" config set by the user, but we claim that's an +# unsupported config when building Qt. +function(qt_internal_is_cmake_build_type_default_initialized_heuristic out_var) + get_property(is_multi_config GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) + get_cmake_property(aready_force_set _qt_build_internals_cmake_build_type_set) + + if( + # Set by CMake's Platform/Windows-MSVC.cmake when CMAKE_BUILD_TYPE is empty + # The STREQUAL check needs to have expanded variables because an undefined var is not equal + # to an empty defined var. + "${CMAKE_BUILD_TYPE}" STREQUAL "${CMAKE_BUILD_TYPE_INIT}" + + # Set by qt_internal_force_set_cmake_build_type() + AND aready_force_set MATCHES "NOTFOUND" + + # Set by qt_auto_detect_cmake_build_type() + AND NOT __qt_auto_detect_cmake_build_type_before_project_call + + # Set by sqldrivers project + AND NOT __qt_internal_standalone_project_cmake_build_type_before_project_call + + # Set by qt.toolchain.cmake + AND NOT __qt_toolchain_cmake_build_type_before_project_call + + # Set by user explicitily + AND NOT QT_NO_FORCE_SET_CMAKE_BUILD_TYPE + + # Set in multi-config builds + AND NOT is_multi_config) + + set(${out_var} TRUE PARENT_SCOPE) + else() + set(${out_var} FALSE PARENT_SCOPE) + endif() +endfunction() + +function(qt_internal_force_set_cmake_build_type value) + cmake_parse_arguments(PARSE_ARGV 1 arg + "SHOW_MESSAGE" + "" + "" + ) + _qt_internal_validate_all_args_are_parsed(arg) + + set(CMAKE_BUILD_TYPE "${value}" CACHE STRING "Choose the type of build." FORCE) + set_property(CACHE CMAKE_BUILD_TYPE + PROPERTY STRINGS + "Debug" "Release" "MinSizeRel" "RelWithDebInfo") # Set the possible values for cmake-gui. + if(arg_SHOW_MESSAGE) + message(STATUS "Force setting build type to '${value}'.") + endif() + set_property(GLOBAL PROPERTY _qt_build_internals_cmake_build_type_set "${value}") +endfunction() + +# Only override the build type if it was default initialized by CMake. +function(qt_internal_force_set_cmake_build_type_if_cmake_default_initialized value) + qt_internal_is_cmake_build_type_default_initialized_heuristic(is_default_cmake_build_type) + if(is_default_cmake_build_type) + qt_internal_force_set_cmake_build_type("${value}" SHOW_MESSAGE) + endif() +endfunction() + +function(qt_internal_set_cmake_build_type) + # When building standalone tests against a multi-config Qt, we want to configure the + # tests / examples with + # the first multi-config configuration, rather than use CMake's default configuration. + # In the case of Windows, we definitely don't want it to default to Debug, because that causes + # issues in the CI. + if(QT_INTERNAL_BUILD_STANDALONE_PARTS AND QT_MULTI_CONFIG_FIRST_CONFIG) + qt_internal_force_set_cmake_build_type_if_cmake_default_initialized( + "${QT_MULTI_CONFIG_FIRST_CONFIG}") + + # We want the same build type to be used when configuring all Qt repos or standalone + # tests or single tests, so we reuse the initial build type set by qtbase. + # __qt_internal_initial_qt_cmake_build_type is saved in QtBuildInternalsExtra.cmake.in. + elseif(__qt_internal_initial_qt_cmake_build_type) + qt_internal_force_set_cmake_build_type_if_cmake_default_initialized( + "${__qt_internal_initial_qt_cmake_build_type}") + + # Default to something sensible when configuring qtbase / top-level. + else() + qt_internal_set_qt_appropriate_default_cmake_build_type() + endif() +endfunction() + +# Sets a default cmake build type for qtbase / top-level. +macro(qt_internal_set_qt_appropriate_default_cmake_build_type) + set(_default_build_type "Release") + if(FEATURE_developer_build) + set(_default_build_type "Debug") + endif() + + qt_internal_is_cmake_build_type_default_initialized_heuristic(is_default_cmake_build_type) + if(is_default_cmake_build_type) + qt_internal_force_set_cmake_build_type("${_default_build_type}") + message(STATUS "Setting build type to '${_default_build_type}' as none was specified.") + elseif(CMAKE_CONFIGURATION_TYPES) + message(STATUS "Building for multiple configurations: ${CMAKE_CONFIGURATION_TYPES}.") + message(STATUS "Main configuration is: ${QT_MULTI_CONFIG_FIRST_CONFIG}.") + if(CMAKE_NINJA_MULTI_DEFAULT_BUILD_TYPE) + message(STATUS + "Default build configuration set to '${CMAKE_NINJA_MULTI_DEFAULT_BUILD_TYPE}'.") + endif() + if(CMAKE_GENERATOR STREQUAL "Ninja") + message(FATAL_ERROR + "It's not possible to build multiple configurations with the single config Ninja " + "generator. Consider configuring with -G\"Ninja Multi-Config\" instead of -GNinja." + ) + endif() + else() + message(STATUS "CMAKE_BUILD_TYPE was already explicitly set to: '${CMAKE_BUILD_TYPE}'") + endif() +endmacro() + +macro(qt_internal_set_configure_from_ide) + # QT_INTERNAL_CONFIGURE_FROM_IDE is set to TRUE for the following known IDE applications: + # - Qt Creator, detected by QTC_RUN environment variable + # - CLion, detected by CLION_IDE environment variable + # - Visual Studio Code, detected by VSCODE_CLI environment variable + if("$ENV{QTC_RUN}" OR "$ENV{CLION_IDE}" OR "$ENV{VSCODE_CLI}") + set(QT_INTERNAL_CONFIGURE_FROM_IDE TRUE CACHE INTERNAL "Configuring Qt Project from IDE") + else() + set(QT_INTERNAL_CONFIGURE_FROM_IDE FALSE CACHE INTERNAL "Configuring Qt Project from IDE") + endif() +endmacro() + +macro(qt_internal_set_sync_headers_at_configure_time) + set(_qt_sync_headers_at_configure_time_default ${QT_INTERNAL_CONFIGURE_FROM_IDE}) + + if(FEATURE_developer_build) + # Sync headers during the initial configuration of a -developer-build to facilitate code + # navigation for code editors that use an LSP-based code model. + set(_qt_sync_headers_at_configure_time_default TRUE) + endif() + + # Sync Qt header files at configure time + option(QT_SYNC_HEADERS_AT_CONFIGURE_TIME "Run syncqt at configure time already" + ${_qt_sync_headers_at_configure_time_default}) + unset(_qt_sync_headers_at_configure_time_default) + + # In static Ninja Multi-Config builds the sync_headers dependencies(and other autogen + # dependencies are not added to '_autogen/timestamp' targets. See QTBUG-113974. + if(CMAKE_GENERATOR STREQUAL "Ninja Multi-Config" AND NOT QT_BUILD_SHARED_LIBS) + set(QT_SYNC_HEADERS_AT_CONFIGURE_TIME TRUE CACHE BOOL "" FORCE) + endif() +endmacro() + +macro(qt_internal_set_export_compile_commands) + if(FEATURE_developer_build) + if(DEFINED QT_CMAKE_EXPORT_COMPILE_COMMANDS) + set(CMAKE_EXPORT_COMPILE_COMMANDS ${QT_CMAKE_EXPORT_COMPILE_COMMANDS}) + else() + set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + endif() + endif() +endmacro() + +macro(qt_internal_setup_build_benchmarks) + if(FEATURE_developer_build) + set(__build_benchmarks ON) + + # Disable benchmarks for single configuration generators which do not build + # with release configuration. + if(CMAKE_BUILD_TYPE AND CMAKE_BUILD_TYPE STREQUAL Debug) + set(__build_benchmarks OFF) + endif() + else() + set(__build_benchmarks OFF) + endif() + + # Build Benchmarks + option(QT_BUILD_BENCHMARKS "Build Qt Benchmarks" ${__build_benchmarks}) +endmacro() + +macro(qt_internal_setup_build_tests) + if(FEATURE_developer_build) + set(_qt_build_tests_default ON) + + # Tests are not built by default with qmake for iOS and friends, and thus the overall build + # tends to fail. Disable them by default when targeting uikit. + if(UIKIT OR ANDROID) + set(_qt_build_tests_default OFF) + endif() + else() + set(_qt_build_tests_default OFF) + endif() + + # If benchmarks are explicitly enabled, force tests to also be built, even if they might + # not work on the platform. + if(QT_BUILD_BENCHMARKS) + set(_qt_build_tests_default ON) + endif() + + ## Set up testing + option(QT_BUILD_TESTS "Build the testing tree." ${_qt_build_tests_default}) + unset(_qt_build_tests_default) + option(QT_BUILD_TESTS_BY_DEFAULT + "Should tests be built as part of the default 'all' target." ON) + if(QT_BUILD_STANDALONE_TESTS) + # BuildInternals might have set it to OFF on initial configuration. So force it to ON when + # building standalone tests. + set(QT_BUILD_TESTS ON CACHE BOOL "Build the testing tree." FORCE) + + # Also force the tests to be built as part of the default build target. + set(QT_BUILD_TESTS_BY_DEFAULT ON CACHE BOOL + "Should tests be built as part of the default 'all' target." FORCE) + endif() + set(BUILD_TESTING ${QT_BUILD_TESTS} CACHE INTERNAL "") + + if(WASM) + set(_qt_batch_tests ON) + else() + set(_qt_batch_tests OFF) + endif() + + if(DEFINED INPUT_batch_tests) + if (${INPUT_batch_tests}) + set(_qt_batch_tests ON) + else() + set(_qt_batch_tests OFF) + endif() + endif() + + option(QT_BUILD_TESTS_BATCHED "Link all tests into a single binary." ${_qt_batch_tests}) + + if(QT_BUILD_TESTS AND QT_BUILD_TESTS_BATCHED AND CMAKE_VERSION VERSION_LESS "3.19") + message(FATAL_ERROR + "Test batching requires at least CMake 3.19, due to requiring per-source " + "TARGET_DIRECTORY assignments and DEFER calls.") + endif() + + option(QT_BUILD_MANUAL_TESTS "Build Qt manual tests" OFF) + + if(WASM AND _qt_batch_tests) + set(_qt_wasm_and_batch_tests ON) + else() + set(_qt_wasm_and_batch_tests OFF) + endif() + + option(QT_BUILD_MINIMAL_STATIC_TESTS "Build minimal subset of tests for static Qt builds" ${_qt_wasm_and_batch_tests}) + + option(QT_BUILD_WASM_BATCHED_TESTS "Build subset of tests for wasm batched tests" ${_qt_wasm_and_batch_tests}) + + option(QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS + "Build minimal subset of tests for Android multi-ABI Qt builds" OFF) + + include(CTest) + enable_testing() +endmacro() + +macro(qt_internal_setup_build_tools) + # QT_BUILD_TOOLS_WHEN_CROSSCOMPILING -> QT_FORCE_BUILD_TOOLS + # pre-6.4 compatibility flag (remove sometime in the future) + if(CMAKE_CROSSCOMPILING AND QT_BUILD_TOOLS_WHEN_CROSSCOMPILING) + message(WARNING "QT_BUILD_TOOLS_WHEN_CROSSCOMPILING is deprecated. " + "Please use QT_FORCE_BUILD_TOOLS instead.") + set(QT_FORCE_BUILD_TOOLS TRUE CACHE INTERNAL "" FORCE) + endif() + + # When cross-building, we don't build tools by default. Sometimes this also covers Qt apps as + # well. Like in qttools/assistant/assistant.pro, load(qt_app), which is guarded by a + # qtNomakeTools() call. + set(_qt_build_tools_by_default_default ON) + if(CMAKE_CROSSCOMPILING AND NOT QT_FORCE_BUILD_TOOLS) + set(_qt_build_tools_by_default_default OFF) + endif() + option(QT_BUILD_TOOLS_BY_DEFAULT "Should tools be built as part of the default 'all' target." + "${_qt_build_tools_by_default_default}") + unset(_qt_build_tools_by_default_default) +endmacro() + +macro(qt_internal_setup_build_examples) + 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) + + # We need a way to force disable building in-tree examples in the CI, so that we instead build + # standalone examples. Because the Coin yaml instructions don't allow us to remove + # -make examples from from the configure args, we instead read a variable that only Coin sets. + if(QT_INTERNAL_CI_NO_BUILD_IN_TREE_EXAMPLES) + set(QT_BUILD_EXAMPLES OFF CACHE BOOL "Build Qt examples" FORCE) + endif() + + if(QT_BUILD_STANDALONE_EXAMPLES) + # BuildInternals might have set it to OFF on initial configuration. So force it to ON when + # building standalone examples. + set(QT_BUILD_EXAMPLES ON CACHE BOOL "Build Qt examples" FORCE) + + # Also force the examples to be built as part of the default build target. + set(QT_BUILD_EXAMPLES_BY_DEFAULT ON CACHE BOOL + "Should examples be built as part of the default 'all' target." FORCE) + endif() + + option(QT_DEPLOY_MINIMAL_EXAMPLES + "Deploy minimal subset of examples to save time and space" OFF) + + # FIXME: Support prefix builds as well QTBUG-96232 + # We don't want to enable EP examples with -debug-and-release because starting with CMake 3.24 + # ExternalProject_Add ends up creating build rules twice, once for each configuration, in the + # same build dir, which ends up causing various issues due to concurrent builds as well as + # clobbered CMakeCache.txt and ninja files. + if(QT_WILL_INSTALL OR QT_FEATURE_debug_and_release) + set(_qt_build_examples_as_external OFF) + else() + set(_qt_build_examples_as_external ON) + endif() + option(QT_BUILD_EXAMPLES_AS_EXTERNAL "Should examples be built as ExternalProjects." + ${_qt_build_examples_as_external}) + unset(_qt_build_examples_as_external) +endmacro() + +macro(qt_internal_set_qt_host_path) + ## Path used to find host tools, either when cross-compiling or just when using the tools from + ## a different host build. + set(QT_HOST_PATH "$ENV{QT_HOST_PATH}" CACHE PATH + "Installed Qt host directory path, used for cross compiling.") +endmacro() + +macro(qt_internal_set_use_ccache) + option(QT_USE_CCACHE "Enable the use of ccache") + if(QT_USE_CCACHE) + find_program(CCACHE_PROGRAM ccache) + if(CCACHE_PROGRAM) + set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") + set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") + set(CMAKE_OBJC_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") + set(CMAKE_OBJCXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") + else() + message(FATAL_ERROR "Ccache use was requested, but the program was not found.") + endif() + endif() +endmacro() + +macro(qt_internal_set_unity_build) + option(QT_UNITY_BUILD "Enable unity (jumbo) build") + set(QT_UNITY_BUILD_BATCH_SIZE "32" CACHE STRING "Unity build batch size") + if(QT_UNITY_BUILD) + set(CMAKE_UNITY_BUILD ON) + set(CMAKE_UNITY_BUILD_BATCH_SIZE "${QT_UNITY_BUILD_BATCH_SIZE}") + endif() +endmacro() + +macro(qt_internal_set_allow_symlink_in_paths) + option(QT_ALLOW_SYMLINK_IN_PATHS "Allows symlinks in paths." OFF) +endmacro() + +macro(qt_internal_set_qt_allow_download) + option(QT_ALLOW_DOWNLOAD "Allows files to be downloaded when building Qt." OFF) +endmacro() diff --git a/cmake/QtBuildPathsHelpers.cmake b/cmake/QtBuildPathsHelpers.cmake new file mode 100644 index 0000000000..6431fa1937 --- /dev/null +++ b/cmake/QtBuildPathsHelpers.cmake @@ -0,0 +1,247 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +macro(qt_internal_setup_default_install_prefix) + # Detect non-prefix builds: either when the qtbase install prefix is set to the binary dir + # or when a developer build is explicitly enabled and no install prefix (or staging prefix) + # is specified. + # This detection only happens when building qtbase, and later is propagated via the generated + # QtBuildInternalsExtra.cmake file. + if(PROJECT_NAME STREQUAL "QtBase" AND NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) + if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + # Handle both FEATURE_ and QT_FEATURE_ cases when they are specified on the command line + # explicitly. It's possible for one to be set, but not the other, because + # qtbase/configure.cmake is not processed by this point. + if((FEATURE_developer_build + OR QT_FEATURE_developer_build + OR FEATURE_no_prefix + OR QT_FEATURE_no_prefix + ) + AND NOT CMAKE_STAGING_PREFIX) + # Handle non-prefix builds by setting the CMake install prefix to point to qtbase's + # build dir. While building another repo (like qtsvg) the CMAKE_PREFIX_PATH should + # be set on the command line to point to the qtbase build dir. + set(__qt_default_prefix "${QtBase_BINARY_DIR}") + else() + if(CMAKE_HOST_WIN32) + set(__qt_default_prefix "C:/Qt/") + else() + set(__qt_default_prefix "/usr/local/") + endif() + string(APPEND __qt_default_prefix + "Qt-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") + endif() + set(CMAKE_INSTALL_PREFIX ${__qt_default_prefix} CACHE PATH + "Install path prefix, prepended onto install directories." FORCE) + unset(__qt_default_prefix) + endif() + if(CMAKE_STAGING_PREFIX) + set(__qt_prefix "${CMAKE_STAGING_PREFIX}") + else() + set(__qt_prefix "${CMAKE_INSTALL_PREFIX}") + endif() + if(__qt_prefix STREQUAL QtBase_BINARY_DIR) + set(__qt_will_install_value OFF) + else() + set(__qt_will_install_value ON) + endif() + set(QT_WILL_INSTALL ${__qt_will_install_value} CACHE BOOL + "Boolean indicating if doing a Qt prefix build (vs non-prefix build)." FORCE) + unset(__qt_prefix) + unset(__qt_will_install_value) + endif() +endmacro() + +function(qt_internal_setup_build_and_install_paths) + # Compute the values of QT_BUILD_DIR, QT_INSTALL_DIR, QT_CONFIG_BUILD_DIR, QT_CONFIG_INSTALL_DIR + # taking into account whether the current build is a prefix build or a non-prefix build, + # and whether it is a superbuild or non-superbuild. + # A third case is when another module or standalone tests/examples are built against a + # super-built Qt. + # The layout for the third case is the same as for non-superbuilds. + # + # These values should be prepended to file paths in commands or properties, + # in order to correctly place generated Config files, generated Targets files, + # executables / libraries, when copying / installing files, etc. + # + # The build dir variables will always be absolute paths. + # The QT_INSTALL_DIR variable will have a relative path in a prefix build, + # which means that it can be empty, so use qt_join_path to prevent accidental absolute paths. + if(QT_SUPERBUILD) + # In this case, we always copy all the build products in qtbase/{bin,lib,...} + if(QT_WILL_INSTALL) + set(QT_BUILD_DIR "${QtBase_BINARY_DIR}") + set(QT_INSTALL_DIR "") + else() + if("${CMAKE_STAGING_PREFIX}" STREQUAL "") + set(QT_BUILD_DIR "${QtBase_BINARY_DIR}") + set(QT_INSTALL_DIR "${QtBase_BINARY_DIR}") + else() + set(QT_BUILD_DIR "${CMAKE_STAGING_PREFIX}") + set(QT_INSTALL_DIR "${CMAKE_STAGING_PREFIX}") + endif() + endif() + else() + if(QT_WILL_INSTALL) + # In the usual prefix build case, the build dir is the current module build dir, + # and the install dir is the prefix, so we don't set it. + set(QT_BUILD_DIR "${CMAKE_BINARY_DIR}") + set(QT_INSTALL_DIR "") + else() + # When doing a non-prefix build, both the build dir and install dir are the same, + # pointing to the qtbase build dir. + set(QT_BUILD_DIR "${QT_STAGING_PREFIX}") + set(QT_INSTALL_DIR "${QT_BUILD_DIR}") + endif() + endif() + + set(__config_path_part "${INSTALL_LIBDIR}/cmake") + set(QT_CONFIG_BUILD_DIR "${QT_BUILD_DIR}/${__config_path_part}") + set(QT_CONFIG_INSTALL_DIR "${QT_INSTALL_DIR}") + if(QT_CONFIG_INSTALL_DIR) + string(APPEND QT_CONFIG_INSTALL_DIR "/") + endif() + string(APPEND QT_CONFIG_INSTALL_DIR ${__config_path_part}) + + set(QT_BUILD_DIR "${QT_BUILD_DIR}" PARENT_SCOPE) + set(QT_INSTALL_DIR "${QT_INSTALL_DIR}" PARENT_SCOPE) + set(QT_CONFIG_BUILD_DIR "${QT_CONFIG_BUILD_DIR}" PARENT_SCOPE) + set(QT_CONFIG_INSTALL_DIR "${QT_CONFIG_INSTALL_DIR}" PARENT_SCOPE) +endfunction() + +function(qt_configure_process_path name default docstring) + # Values are computed once for qtbase, and then exported and reused for other projects. + if(NOT PROJECT_NAME STREQUAL "QtBase") + return() + endif() + + # No value provided, set the default. + if(NOT DEFINED "${name}") + set("${name}" "${default}" CACHE STRING "${docstring}") + else() + get_filename_component(given_path_as_abs "${${name}}" ABSOLUTE BASE_DIR + "${CMAKE_INSTALL_PREFIX}") + file(RELATIVE_PATH rel_path "${CMAKE_INSTALL_PREFIX}" + "${given_path_as_abs}") + + # If absolute path given, check that it's inside the prefix (error out if not). + # TODO: Figure out if we need to support paths that are outside the prefix. + # + # If relative path given, it's relative to the install prefix (rather than the binary dir, + # which is what qmake does for some reason). + # In both cases, store the value as a relative path. + if("${rel_path}" STREQUAL "") + # file(RELATIVE_PATH) returns an empty string if the given absolute paths are equal + set(rel_path ".") + elseif(rel_path MATCHES "^\.\./") + # INSTALL_SYSCONFDIR is allowed to be outside the prefix. + if(NOT name STREQUAL "INSTALL_SYSCONFDIR") + message(FATAL_ERROR + "Path component '${name}' is outside computed install prefix: ${rel_path} ") + return() + endif() + set("${name}" "${${name}}" CACHE STRING "${docstring}" FORCE) + else() + set("${name}" "${rel_path}" CACHE STRING "${docstring}" FORCE) + endif() + endif() +endfunction() + +macro(qt_internal_setup_configure_install_paths) + # Install locations: + qt_configure_process_path(INSTALL_BINDIR "bin" "Executables [PREFIX/bin]") + qt_configure_process_path(INSTALL_INCLUDEDIR "include" "Header files [PREFIX/include]") + qt_configure_process_path(INSTALL_LIBDIR "lib" "Libraries [PREFIX/lib]") + qt_configure_process_path(INSTALL_MKSPECSDIR "mkspecs" "Mkspecs files [PREFIX/mkspecs]") + qt_configure_process_path(INSTALL_ARCHDATADIR "." "Arch-dependent data [PREFIX]") + qt_configure_process_path(INSTALL_PLUGINSDIR + "${INSTALL_ARCHDATADIR}/plugins" + "Plugins [ARCHDATADIR/plugins]") + + if(NOT INSTALL_MKSPECSDIR MATCHES "(^|/)mkspecs") + message(FATAL_ERROR "INSTALL_MKSPECSDIR must end with '/mkspecs'") + endif() + + if (WIN32) + set(_default_libexec "${INSTALL_ARCHDATADIR}/bin") + else() + set(_default_libexec "${INSTALL_ARCHDATADIR}/libexec") + endif() + + qt_configure_process_path( + INSTALL_LIBEXECDIR + "${_default_libexec}" + "Helper programs [ARCHDATADIR/bin on Windows, ARCHDATADIR/libexec otherwise]") + qt_configure_process_path(INSTALL_QMLDIR + "${INSTALL_ARCHDATADIR}/qml" + "QML imports [ARCHDATADIR/qml]") + qt_configure_process_path(INSTALL_DATADIR "." "Arch-independent data [PREFIX]") + qt_configure_process_path(INSTALL_DOCDIR "${INSTALL_DATADIR}/doc" "Documentation [DATADIR/doc]") + qt_configure_process_path(INSTALL_TRANSLATIONSDIR "${INSTALL_DATADIR}/translations" + "Translations [DATADIR/translations]") + if(APPLE) + set(QT_DEFAULT_SYS_CONF_DIR "/Library/Preferences/Qt") + else() + set(QT_DEFAULT_SYS_CONF_DIR "etc/xdg") + endif() + qt_configure_process_path( + INSTALL_SYSCONFDIR + "${QT_DEFAULT_SYS_CONF_DIR}" + "Settings used by Qt programs [PREFIX/etc/xdg]/[/Library/Preferences/Qt]") + qt_configure_process_path(INSTALL_EXAMPLESDIR "examples" "Examples [PREFIX/examples]") + qt_configure_process_path(INSTALL_TESTSDIR "tests" "Tests [PREFIX/tests]") + qt_configure_process_path(INSTALL_DESCRIPTIONSDIR + "${INSTALL_ARCHDATADIR}/modules" + "Module description files directory") +endmacro() + +macro(qt_internal_set_cmake_install_libdir) + # Ensure that GNUInstallDirs's CMAKE_INSTALL_LIBDIR points to the same lib dir that Qt was + # configured with. Currently this is important for QML plugins, which embed an rpath based + # on that value. + set(CMAKE_INSTALL_LIBDIR "${INSTALL_LIBDIR}") +endmacro() + +macro(qt_internal_set_qt_cmake_dir) + set(QT_CMAKE_DIR "${CMAKE_CURRENT_LIST_DIR}") +endmacro() + +macro(qt_internal_set_qt_apple_support_files_path) + # This is analogous to what we have in QtConfig.cmake.in. It's copied here so that iOS + # tests can be built in tree. + if(APPLE) + if(NOT CMAKE_SYSTEM_NAME OR CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(__qt_internal_cmake_apple_support_files_path "${QT_CMAKE_DIR}/macos") + elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS") + set(__qt_internal_cmake_apple_support_files_path "${QT_CMAKE_DIR}/ios") + elseif(CMAKE_SYSTEM_NAME STREQUAL "visionOS") + set(__qt_internal_cmake_apple_support_files_path "${QT_CMAKE_DIR}/visionos") + endif() + endif() +endmacro() + +macro(qt_internal_set_qt_staging_prefix) + if(NOT "${CMAKE_STAGING_PREFIX}" STREQUAL "") + set(QT_STAGING_PREFIX "${CMAKE_STAGING_PREFIX}") + else() + set(QT_STAGING_PREFIX "${CMAKE_INSTALL_PREFIX}") + endif() +endmacro() + +macro(qt_internal_setup_paths_and_prefixes) + qt_internal_setup_configure_install_paths() + + qt_internal_set_qt_staging_prefix() + + # Depends on QT_STAGING_PREFIX being set. + qt_internal_setup_build_and_install_paths() + + qt_get_relocatable_install_prefix(QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX) + + # Depends on INSTALL_LIBDIR being set. + qt_internal_set_cmake_install_libdir() + + qt_internal_set_qt_cmake_dir() + + qt_internal_set_qt_apple_support_files_path() +endmacro() 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() diff --git a/cmake/QtBuildRepoHelpers.cmake b/cmake/QtBuildRepoHelpers.cmake new file mode 100644 index 0000000000..8bd0615090 --- /dev/null +++ b/cmake/QtBuildRepoHelpers.cmake @@ -0,0 +1,1079 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# Macros and functions for building Qt submodules + +# The macro sets all the necessary pre-conditions and setup consistent environment for building +# the Qt repository. It has to be called right after the find_package(Qt6 COMPONENTS BuildInternals) +# call. Otherwise we cannot make sure that all the required policies will be applied to the Qt +# components that are involved in build procedure. +macro(qt_internal_project_setup) + # Check for the minimum CMake version. + qt_internal_require_suitable_cmake_version() + qt_internal_upgrade_cmake_policies() + qt_internal_promote_platform_targets_to_global() +endmacro() + +macro(qt_build_internals_set_up_private_api) + # TODO: this call needs to be removed once all repositories got the qtbase update + qt_internal_project_setup() + + # Qt specific setup common for all modules: + include(QtSetup) + + # 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() + +# add toplevel targets for each subdirectory, e.g. qtbase_src +function(qt_build_internals_add_toplevel_targets qt_repo_targets_name) + set(qt_repo_target_all "") + get_directory_property(directories DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" SUBDIRECTORIES) + foreach(directory IN LISTS directories) + set(qt_repo_targets "") + get_filename_component(qt_repo_target_basename ${directory} NAME) + _qt_internal_collect_buildsystem_targets(qt_repo_targets "${directory}" EXCLUDE UTILITY) + if (qt_repo_targets) + set(qt_repo_target_name "${qt_repo_targets_name}_${qt_repo_target_basename}") + message(DEBUG "${qt_repo_target_name} depends on ${qt_repo_targets}") + add_custom_target("${qt_repo_target_name}" + COMMENT "Building everything in ${qt_repo_targets_name}/${qt_repo_target_basename}") + add_dependencies("${qt_repo_target_name}" ${qt_repo_targets}) + list(APPEND qt_repo_target_all "${qt_repo_target_name}") + + # Create special dependency target for External Project examples excluding targets + # marked as skipped. + if(qt_repo_target_basename STREQUAL "src") + set(qt_repo_target_name + "${qt_repo_targets_name}_${qt_repo_target_basename}_for_examples") + add_custom_target("${qt_repo_target_name}") + + set(unskipped_targets "") + foreach(target IN LISTS qt_repo_targets) + if(TARGET "${target}") + qt_internal_is_target_skipped_for_examples("${target}" is_skipped) + if(NOT is_skipped) + list(APPEND unskipped_targets "${target}") + endif() + endif() + endforeach() + if(unskipped_targets) + add_dependencies("${qt_repo_target_name}" ${unskipped_targets}) + endif() + endif() + endif() + + endforeach() + if (qt_repo_target_all) + # Note qt_repo_targets_name is different from qt_repo_target_name that is used above. + add_custom_target("${qt_repo_targets_name}" + COMMENT "Building everything in ${qt_repo_targets_name}") + add_dependencies("${qt_repo_targets_name}" ${qt_repo_target_all}) + message(DEBUG "${qt_repo_targets_name} depends on ${qt_repo_target_all}") + endif() +endfunction() + +macro(qt_enable_cmake_languages) + set(__qt_required_language_list C CXX) + set(__qt_platform_required_language_list ) + + if(APPLE) + list(APPEND __qt_platform_required_language_list OBJC OBJCXX) + endif() + + foreach(__qt_lang ${__qt_required_language_list}) + enable_language(${__qt_lang}) + endforeach() + + foreach(__qt_lang ${__qt_platform_required_language_list}) + enable_language(${__qt_lang}) + endforeach() + + # The qtbase call is handled in qtbase/CMakeLists.txt. + # This call is used for projects other than qtbase, including for other project's standalone + # tests/examples. + # Because the function uses QT_FEATURE_foo values, it's important that find_package(Qt6Core) is + # called before this function. but that's usually the case for Qt repos. + if(NOT PROJECT_NAME STREQUAL "QtBase") + qt_internal_set_up_config_optimizations_like_in_qmake() + endif() +endmacro() + +# Minimum setup required to have any CMakeList.txt build as as a standalone +# project after importing BuildInternals +macro(qt_prepare_standalone_project) + qt_set_up_build_internals_paths() + qt_build_internals_set_up_private_api() + qt_enable_cmake_languages() +endmacro() + +# Define a repo target set, and store accompanying information. +# +# A repo target set is a subset of targets in a Qt module repository. To build a repo target set, +# set QT_BUILD_SINGLE_REPO_TARGET_SET to the name of the repo target set. +# +# This function is to be called in the top-level project file of a repository, +# before qt_internal_prepare_single_repo_target_set_build() +# +# This function stores information in variables of the parent scope. +# +# Positional Arguments: +# name - The name of this repo target set. +# +# Named Arguments: +# DEPENDS - List of Qt6 COMPONENTS that are build dependencies of this repo target set. +function(qt_internal_define_repo_target_set name) + set(oneValueArgs DEPENDS) + set(prefix QT_REPO_TARGET_SET_) + cmake_parse_arguments(${prefix}${name} "" ${oneValueArgs} "" ${ARGN}) + foreach(arg IN LISTS oneValueArgs) + set(${prefix}${name}_${arg} ${${prefix}${name}_${arg}} PARENT_SCOPE) + endforeach() + set(QT_REPO_KNOWN_TARGET_SETS "${QT_REPO_KNOWN_TARGET_SETS};${name}" PARENT_SCOPE) +endfunction() + +# Setup a single repo target set build if QT_BUILD_SINGLE_REPO_TARGET_SET is defined. +# +# This macro must be called in the top-level project file of the repository after all repo target +# sets have been defined. +macro(qt_internal_prepare_single_repo_target_set_build) + if(DEFINED QT_BUILD_SINGLE_REPO_TARGET_SET) + if(NOT QT_BUILD_SINGLE_REPO_TARGET_SET IN_LIST QT_REPO_KNOWN_TARGET_SETS) + message(FATAL_ERROR + "Repo target set '${QT_BUILD_SINGLE_REPO_TARGET_SET}' is undefined.") + endif() + message(STATUS + "Preparing single repo target set build of ${QT_BUILD_SINGLE_REPO_TARGET_SET}") + if (NOT "${QT_REPO_TARGET_SET_${QT_BUILD_SINGLE_REPO_TARGET_SET}_DEPENDS}" STREQUAL "") + find_package(${INSTALL_CMAKE_NAMESPACE} ${PROJECT_VERSION} CONFIG REQUIRED + COMPONENTS ${QT_REPO_TARGET_SET_${QT_BUILD_SINGLE_REPO_TARGET_SET}_DEPENDS}) + endif() + endif() +endmacro() + +# There are three necessary copies of this macro in +# qtbase/cmake/QtBaseHelpers.cmake +# qtbase/cmake/QtBaseTopLevelHelpers.cmake +# qtbase/cmake/QtBuildRepoHelpers.cmake +macro(qt_internal_setup_standalone_parts) + # A generic marker for any kind of standalone builds, either tests or examples. + if(NOT DEFINED QT_INTERNAL_BUILD_STANDALONE_PARTS + AND (QT_BUILD_STANDALONE_TESTS OR QT_BUILD_STANDALONE_EXAMPLES)) + set(QT_INTERNAL_BUILD_STANDALONE_PARTS TRUE CACHE INTERNAL + "Whether standalone tests or examples are being built") + endif() +endmacro() + +macro(qt_build_repo_begin) + qt_internal_setup_standalone_parts() + + set(QT_INTERNAL_REPO_POST_PROCESS_CALLED FALSE) + list(APPEND CMAKE_MESSAGE_CONTEXT "${PROJECT_NAME}") + + qt_build_internals_set_up_private_api() + + # Prevent installation in non-prefix builds. + # We need to associate targets with export names, and that is only possible to do with the + # install(TARGETS) command. But in a non-prefix build, we don't want to install anything. + # To make sure that developers don't accidentally run make install, add bail out code to + # cmake_install.cmake. + if(NOT QT_WILL_INSTALL) + # In a top-level build, print a message only in qtbase, which is the first repository. + if(NOT QT_SUPERBUILD OR (PROJECT_NAME STREQUAL "QtBase")) + install(CODE [[message(FATAL_ERROR + "Qt was configured as non-prefix build. " + "Installation is not supported for this arrangement.")]]) + endif() + + install(CODE [[return()]]) + endif() + + qt_enable_cmake_languages() + + qt_internal_generate_binary_strip_wrapper() + + # 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) + add_custom_target(install_qch_docs) + add_custom_target(install_docs) + add_dependencies(html_docs generate_docs) + add_dependencies(docs html_docs qch_docs) + add_dependencies(install_docs install_html_docs install_qch_docs) + endif() + + if(NOT TARGET sync_headers) + add_custom_target(sync_headers) + endif() + + # The special target that we use to sync 3rd-party headers before the gn run when building + # qtwebengine in top-level builds. + if(NOT TARGET thirdparty_sync_headers) + add_custom_target(thirdparty_sync_headers) + endif() + + # Add global qt_plugins, qpa_plugins and qpa_default_plugins convenience custom targets. + # Internal executables will add a dependency on the qpa_default_plugins target, + # so that building and running a test ensures it won't fail at runtime due to a missing qpa + # plugin. + if(NOT TARGET qt_plugins) + add_custom_target(qt_plugins) + add_custom_target(qpa_plugins) + add_custom_target(qpa_default_plugins) + endif() + + string(TOLOWER ${PROJECT_NAME} project_name_lower) + + # Target to build all plugins that are part of the current repo. + set(qt_repo_plugins "qt_plugins_${project_name_lower}") + if(NOT TARGET ${qt_repo_plugins}) + add_custom_target(${qt_repo_plugins}) + endif() + + # Target to build all plugins that are part of the current repo and the current repo's + # dependencies plugins. Used for external project example dependencies. + set(qt_repo_plugins_recursive "${qt_repo_plugins}_recursive") + if(NOT TARGET ${qt_repo_plugins_recursive}) + add_custom_target(${qt_repo_plugins_recursive}) + add_dependencies(${qt_repo_plugins_recursive} "${qt_repo_plugins}") + endif() + + qt_internal_read_repo_dependencies(qt_repo_deps "${PROJECT_SOURCE_DIR}") + if(qt_repo_deps) + foreach(qt_repo_dep IN LISTS qt_repo_deps) + if(TARGET qt_plugins_${qt_repo_dep}) + message(DEBUG + "${qt_repo_plugins_recursive} depends on qt_plugins_${qt_repo_dep}") + add_dependencies(${qt_repo_plugins_recursive} "qt_plugins_${qt_repo_dep}") + endif() + endforeach() + endif() + + set(qt_repo_targets_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_target_name} ${qt_docs_html_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 top-level prepare_docs target depend on the repository-level prepare_docs_<repo> target. + add_dependencies(prepare_docs ${qt_docs_prepare_target_name}) + + # Make top-level install_*_docs targets depend on the repository-level install_*_docs targets. + add_dependencies(install_html_docs ${qt_docs_install_html_target_name}) + add_dependencies(install_qch_docs ${qt_docs_install_qch_target_name}) + + # Add host_tools meta target, so that developrs can easily build only tools and their + # dependencies when working in qtbase. + if(NOT TARGET host_tools) + add_custom_target(host_tools) + add_custom_target(bootstrap_tools) + endif() + + # Add benchmark meta target. It's collection of all benchmarks added/registered by + # 'qt_internal_add_benchmark' helper. + if(NOT TARGET benchmark) + add_custom_target(benchmark) + endif() + + if(QT_INTERNAL_SYNCED_MODULES) + set_property(GLOBAL PROPERTY _qt_synced_modules ${QT_INTERNAL_SYNCED_MODULES}) + endif() +endmacro() + +# Runs delayed actions on some of the Qt targets. +# Can be called either explicitly or as part of qt_build_repo_end(). +macro(qt_build_repo_post_process) + if(NOT QT_INTERNAL_REPO_POST_PROCESS_CALLED) + set(QT_INTERNAL_REPO_POST_PROCESS_CALLED TRUE) + if(NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) + include(QtPostProcess) + endif() + endif() +endmacro() + +macro(qt_build_repo_end) + if(NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) + qt_build_repo_post_process() + + # Install the repo-specific cmake find modules. + qt_path_join(__qt_repo_install_dir ${QT_CONFIG_INSTALL_DIR} ${INSTALL_CMAKE_NAMESPACE}) + qt_path_join(__qt_repo_build_dir ${QT_CONFIG_BUILD_DIR} ${INSTALL_CMAKE_NAMESPACE}) + + if(NOT PROJECT_NAME STREQUAL "QtBase") + if(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + qt_copy_or_install(DIRECTORY cmake/ + DESTINATION "${__qt_repo_install_dir}" + FILES_MATCHING PATTERN "Find*.cmake" + ) + if(QT_SUPERBUILD AND QT_WILL_INSTALL) + file(COPY cmake/ + DESTINATION "${__qt_repo_build_dir}" + FILES_MATCHING PATTERN "Find*.cmake" + ) + endif() + endif() + endif() + + if(NOT QT_SUPERBUILD) + qt_print_feature_summary() + endif() + endif() + + qt_build_internals_add_toplevel_targets(${qt_repo_targets_name}) + + qt_internal_show_extra_ide_sources() + + if(NOT QT_SUPERBUILD) + qt_print_build_instructions() + endif() + + get_property(synced_modules GLOBAL PROPERTY _qt_synced_modules) + if(synced_modules) + set(QT_INTERNAL_SYNCED_MODULES ${synced_modules} CACHE INTERNAL + "List of the synced modules. Prevents running syncqt.cpp after the first configuring.") + endif() + + if(NOT QT_SUPERBUILD) + qt_internal_save_previously_visited_packages() + endif() + + if(QT_INTERNAL_FRESH_REQUESTED) + set(QT_INTERNAL_FRESH_REQUESTED "FALSE" CACHE INTERNAL "") + endif() + + if(NOT QT_SUPERBUILD) + qt_internal_qt_configure_end() + endif() + + list(POP_BACK CMAKE_MESSAGE_CONTEXT) +endmacro() + +function(qt_internal_show_extra_ide_sources) + if(CMAKE_VERSION VERSION_LESS 3.20) + set(ide_sources_default OFF) + else() + set(ide_sources_default ON) + endif() + + option(QT_SHOW_EXTRA_IDE_SOURCES "Generate CMake targets exposing non-source files to IDEs" ${ide_sources_default}) + if(CMAKE_VERSION VERSION_LESS 3.20 AND QT_SHOW_EXTRA_IDE_SOURCES) + message(WARNING "QT_SHOW_EXTRA_IDE_SOURCES requires cmake-3.20") + return() + endif() + + if(NOT QT_SHOW_EXTRA_IDE_SOURCES) + return() + endif() + + # coin + set(coin_target_name ${qt_repo_targets_name}_coin_files) + file(GLOB_RECURSE coin_files LIST_DIRECTORIES false FOLLOW_SYMLINKS coin/*) + if(coin_files) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/coin" FILES ${coin_files}) + add_custom_target(${coin_target_name} SOURCES ${coin_files}) + endif() + + # config.test + set(config_tests_target_name ${qt_repo_targets_name}_config_tests) + file(GLOB_RECURSE config_tests_file LIST_DIRECTORIES false FOLLOW_SYMLINKS config.tests/*) + if(config_tests_file) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/config.tests" FILES ${config_tests_file}) + add_custom_target(${config_tests_target_name} SOURCES ${config_tests_file}) + endif() + + # cmake + set(cmake_target_name ${qt_repo_targets_name}_cmake_files) + file(GLOB_RECURSE cmake_files LIST_DIRECTORIES false FOLLOW_SYMLINKS + cmake/* + configure.cmake + qt_cmdline.cmake + .cmake.conf + *.cmake + *.cmake.in) + foreach(cmake_file IN LISTS cmake_files) + if(NOT ((cmake_file IN_LIST coin_files) OR (file IN_LIST config_tests_files))) + list(APPEND cmake_target_files ${cmake_file}) + endif() + endforeach() + + if(cmake_target_files) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${cmake_target_files}) + add_custom_target(${cmake_target_name} SOURCES ${cmake_target_files}) + endif() + + # licenses + set(licenses_target_name ${qt_repo_targets_name}_licenses) + file(GLOB licenses_files LIST_DIRECTORIES false LICENSES/*) + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/licenseRule.json") + list(APPEND licenses_files "${CMAKE_CURRENT_SOURCE_DIR}/licenseRule.json") + endif() + if(licenses_files) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${licenses_files}) + add_custom_target(${licenses_target_name} SOURCES ${licenses_files}) + endif() + + # changelogs + set(changelogs_target_name ${qt_repo_targets_name}_changelogs) + file(GLOB change_logs_files LIST_DIRECTORIES false dist/*) + if(change_logs_files) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/dist" FILES ${change_logs_files}) + add_custom_target(${changelogs_target_name} SOURCES ${change_logs_files}) + endif() + + # extra files + set(target_name ${qt_repo_targets_name}_extra_files) + add_custom_target(${target_name}) + + set(recursive_glob_patterns + ${QT_BUILD_EXTRA_IDE_FILE_RECURSIVE_PATTERNS} + ) + set(simple_glob_patterns + .gitattributes + .gitignore + .tag + config_help.txt + ${QT_BUILD_EXTRA_IDE_FILE_PATTERNS} + ) + + if(recursive_glob_patterns) + file(GLOB_RECURSE files LIST_DIRECTORIES false FOLLOW_SYMLINKS ${recursive_glob_patterns}) + if(files) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${files}) + target_sources(${target_name} PRIVATE ${files}) + endif() + endif() + + file(GLOB files LIST_DIRECTORIES false ${simple_glob_patterns}) + if(files) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${files}) + target_sources(${target_name} PRIVATE ${files}) + endif() +endfunction() + + +# Function called either at the end of per-repo configuration, or at the end of configuration of +# a super build. +# At the moment it is called before examples are configured in a per-repo build. We might want +# to change that at some point if needed. +function(qt_internal_qt_configure_end) + # If Qt is configued via the configure script, remove the marker variable, so that any future + # reconfigurations that are done by calling cmake directly don't trigger configure specific + # logic. + if(QT_INTERNAL_CALLED_FROM_CONFIGURE) + unset(QT_INTERNAL_CALLED_FROM_CONFIGURE CACHE) + endif() +endfunction() + +macro(qt_build_repo) + qt_build_repo_begin(${ARGN}) + + qt_build_repo_impl_find_package_tests() + qt_build_repo_impl_src() + qt_build_repo_impl_tools() + + qt_build_repo_post_process() + qt_build_repo_impl_tests() + + qt_build_repo_end() + + qt_build_repo_impl_examples() +endmacro() + +macro(qt_build_repo_impl_find_package_tests) + # 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(QT_BUILD_TESTS AND NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) + # When looking for the Test package, do it using the Qt6 package version, in case if + # PROJECT_VERSION is following a different versioning scheme. + if(Qt6_VERSION) + set(_qt_build_repo_impl_find_package_tests_version "${Qt6_VERSION}") + else() + set(_qt_build_repo_impl_find_package_tests_version "${PROJECT_VERSION}") + endif() + + find_package(Qt6 + "${_qt_build_repo_impl_find_package_tests_version}" + CONFIG REQUIRED COMPONENTS Test) + unset(_qt_build_repo_impl_find_package_tests_version) + endif() +endmacro() + +macro(qt_build_repo_impl_src) + if(NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src/CMakeLists.txt") + add_subdirectory(src) + endif() + endif() + if(QT_FEATURE_lttng AND NOT TARGET LTTng::UST) + qt_find_package(LTTngUST PROVIDED_TARGETS LTTng::UST + MODULE_NAME global QMAKE_LIB lttng-ust) + endif() +endmacro() + +macro(qt_build_repo_impl_tools) + if(NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) + if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tools/CMakeLists.txt") + add_subdirectory(tools) + endif() + endif() +endmacro() + +macro(qt_build_repo_impl_tests) + if((QT_BUILD_TESTS OR QT_BUILD_STANDALONE_TESTS) + AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/tests/CMakeLists.txt") + if(QT_BUILD_STANDALONE_EXAMPLES) + message(FATAL_ERROR + "Can't build both standalone tests and standalone examples at once.") + endif() + option(QT_BUILD_TESTS_PROJECT_${PROJECT_NAME} "Configure tests for project ${PROJECT_NAME}" TRUE) + + if (QT_BUILD_TESTS_PROJECT_${PROJECT_NAME}) + add_subdirectory(tests) + if(NOT QT_BUILD_TESTS_BY_DEFAULT) + set_property(DIRECTORY tests PROPERTY EXCLUDE_FROM_ALL TRUE) + endif() + endif() + endif() +endmacro() + +macro(qt_build_repo_impl_examples) + if((QT_BUILD_EXAMPLES OR QT_BUILD_STANDALONE_EXAMPLES) + AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/examples/CMakeLists.txt") + if(QT_BUILD_STANDALONE_TESTS) + message(FATAL_ERROR + "Can't build both standalone tests and standalone examples at once.") + endif() + + message(STATUS "Configuring examples.") + + option(QT_BUILD_EXAMPLES_PROJECT_${PROJECT_NAME} "Configure examples for project ${PROJECT_NAME}" TRUE) + if(QT_BUILD_EXAMPLES_PROJECT_${PROJECT_NAME}) + + # Set this before any examples subdirectories are added, to warn about examples that are + # added via add_subdirectory() calls instead of qt_internal_add_example(). + if(QT_FEATURE_developer_build + AND NOT QT_NO_WARN_ABOUT_EXAMPLE_ADD_SUBDIRECTORY_WARNING) + set(QT_WARN_ABOUT_EXAMPLE_ADD_SUBDIRECTORY TRUE) + endif() + + add_subdirectory(examples) + 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_parts_config_files_path out_var) + # TODO: Rename this to StandaloneParts in some future Qt version, if it confuses people too + # much. Currently not renamed, not to break distro installation scripts that might exclude + # the files. + set(dir_name "StandaloneTests") + + set(path_suffix "${INSTALL_LIBDIR}/cmake/${INSTALL_CMAKE_NAMESPACE}BuildInternals/${dir_name}") + + # Each repo's standalone parts might be configured with a unique CMAKE_STAGING_PREFIX, + # different from any previous one, and it might not coincide with where the BuildInternals + # config file is. + if(QT_WILL_INSTALL AND CMAKE_STAGING_PREFIX) + qt_path_join(path "${CMAKE_STAGING_PREFIX}" "${path_suffix}") + else() + qt_path_join(path "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}" "${path_suffix}") + endif() + + set("${out_var}" "${path}" PARENT_SCOPE) +endfunction() + +function(qt_internal_get_standalone_parts_config_file_name out_var) + # When doing a "single repo target set" build (like in qtscxqml) ensure we use a unique tests + # config file for each repo target set. Using the PROJECT_NAME only is not enough because + # the same file will be overridden with different content on each repo set install. + set(tests_config_file_name "${PROJECT_NAME}") + + if(QT_BUILD_SINGLE_REPO_TARGET_SET) + string(APPEND tests_config_file_name "RepoSet${QT_BUILD_SINGLE_REPO_TARGET_SET}") + endif() + + # TODO: Rename this to StandalonePartsConfig.cmake in some future Qt version, if it confuses + # people too much. Currently not renamed, not to break distro installation scripts that might + # exclude # the files. + string(APPEND tests_config_file_name "TestsConfig.cmake") + + set(${out_var} "${tests_config_file_name}" PARENT_SCOPE) +endfunction() + +macro(qt_internal_find_standalone_test_config_file) + if(QT_INTERNAL_BUILD_STANDALONE_PARTS) + # Of course we always need the test module as well. + # When looking for the Test package, do it using the Qt6 package version, in case if + # PROJECT_VERSION is following a different versioning scheme. + if(Qt6_VERSION) + set(_qt_build_tests_package_version "${Qt6_VERSION}") + else() + set(_qt_build_tests_package_version "${PROJECT_VERSION}") + endif() + find_package(Qt6 "${_qt_build_tests_package_version}" CONFIG REQUIRED COMPONENTS Test) + unset(_qt_build_tests_package_version) + endif() +endmacro() + +# Used by standalone tests and standalone non-ExternalProject examples to find all installed qt +# packages. +macro(qt_internal_find_standalone_parts_config_files) + if(QT_INTERNAL_BUILD_STANDALONE_PARTS) + # Find location of TestsConfig.cmake. These contain the modules that need to be + # find_package'd when building tests or examples. + qt_get_standalone_parts_config_files_path(_qt_build_parts_install_prefix) + + qt_internal_get_standalone_parts_config_file_name(_qt_parts_config_file_name) + set(_qt_standalone_parts_config_file_path + "${_qt_build_parts_install_prefix}/${_qt_parts_config_file_name}") + include("${_qt_standalone_parts_config_file_path}" + OPTIONAL + RESULT_VARIABLE _qt_standalone_parts_included) + if(NOT _qt_standalone_parts_included) + message(DEBUG + "Standalone parts config file not included because it does not exist: " + "${_qt_standalone_parts_config_file_path}" + ) + else() + message(DEBUG + "Standalone parts config file included successfully: " + "${_qt_standalone_parts_config_file_path}" + ) + endif() + + unset(_qt_standalone_parts_config_file_path) + unset(_qt_standalone_parts_included) + unset(_qt_parts_config_file_name) + endif() +endmacro() + +macro(qt_build_tests) + # Tests are not unity-ready. + set(CMAKE_UNITY_BUILD OFF) + + # 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. + list(PREPEND CMAKE_FIND_ROOT_PATH "${QT_BUILD_DIR}") + list(PREPEND QT_BUILD_CMAKE_PREFIX_PATH "${QT_BUILD_DIR}/${INSTALL_LIBDIR}/cmake") + + qt_internal_find_standalone_parts_config_files() + qt_internal_find_standalone_test_config_file() + + if(QT_BUILD_STANDALONE_TESTS) + # 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() + + # Set up fake standalone parts install prefix, so we don't pollute the Qt install + # prefix with tests. + qt_internal_set_up_fake_standalone_parts_install_prefix() + else() + if(ANDROID) + # When building in-tree tests we need to specify the QT_ANDROID_ABIS list. Since we + # build Qt for the single ABI, build tests for this ABI only. + set(QT_ANDROID_ABIS "${CMAKE_ANDROID_ARCH_ABI}") + endif() + endif() + + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/auto/CMakeLists.txt") + add_subdirectory(auto) + endif() + if(NOT QT_BUILD_MINIMAL_STATIC_TESTS AND NOT QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/baseline/CMakeLists.txt") + add_subdirectory(baseline) + 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" AND QT_BUILD_MANUAL_TESTS) + add_subdirectory(manual) + # Adding this logic to all tests impacts the configure time ~3sec in addition. We still + # might want this in the future for other test types since currently we have a moderate + # subset of tests that require manual initialization of autotools. + _qt_internal_collect_buildsystem_targets(targets + "${CMAKE_CURRENT_SOURCE_DIR}/manual" 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() + endforeach() + endif() + endif() + + set(CMAKE_UNITY_BUILD ${QT_UNITY_BUILD}) +endmacro() + +function(qt_compute_relative_path_from_cmake_config_dir_to_prefix) + # Compute the reverse relative path from the CMake config dir to the install prefix. + # This is used in QtBuildInternalsExtras to create a relocatable relative install prefix path. + # This path is used for finding syncqt and other things, regardless of initial install prefix + # (e.g installed Qt was archived and unpacked to a different path on a different machine). + # + # This is meant to be called only once when configuring qtbase. + # + # Similar code exists in Qt6CoreConfigExtras.cmake.in and src/corelib/CMakeLists.txt which + # might not be needed anymore. + if(CMAKE_STAGING_PREFIX) + set(__qt_prefix "${CMAKE_STAGING_PREFIX}") + else() + set(__qt_prefix "${CMAKE_INSTALL_PREFIX}") + endif() + + if(QT_WILL_INSTALL) + get_filename_component(clean_config_prefix + "${__qt_prefix}/${QT_CONFIG_INSTALL_DIR}" ABSOLUTE) + else() + get_filename_component(clean_config_prefix "${QT_CONFIG_BUILD_DIR}" ABSOLUTE) + endif() + file(RELATIVE_PATH + qt_path_from_cmake_config_dir_to_prefix + "${clean_config_prefix}" "${__qt_prefix}") + set(qt_path_from_cmake_config_dir_to_prefix "${qt_path_from_cmake_config_dir_to_prefix}" + PARENT_SCOPE) +endfunction() + +function(qt_get_relocatable_install_prefix out_var) + # We need to compute it only once while building qtbase. Afterwards it's loaded from + # QtBuildInternalsExtras.cmake. + if(QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX) + return() + endif() + # The QtBuildInternalsExtras value is dynamically computed, whereas the initial qtbase + # configuration uses an absolute path. + set(${out_var} "${CMAKE_INSTALL_PREFIX}" PARENT_SCOPE) +endfunction() + +function(qt_internal_get_fake_standalone_install_prefix out_var) + set(new_install_prefix "${CMAKE_BINARY_DIR}/fake_prefix") + set(${out_var} "${new_install_prefix}" PARENT_SCOPE) +endfunction() + +function(qt_internal_set_up_fake_standalone_parts_install_prefix) + # Set a fake local (non-cache) CMAKE_INSTALL_PREFIX. + # Needed for standalone tests, we don't want to accidentally install a test into the Qt prefix. + # Allow opt-out, if a user knows what they're doing. + if(QT_NO_FAKE_STANDALONE_TESTS_INSTALL_PREFIX) + return() + endif() + qt_internal_get_fake_standalone_install_prefix(new_install_prefix) + + # It's IMPORTANT that this is not a cache variable. Otherwise + # qt_get_standalone_parts_config_files_path() will not work on re-configuration. + message(STATUS + "Setting local standalone test install prefix (non-cached) to '${new_install_prefix}'.") + set(CMAKE_INSTALL_PREFIX "${new_install_prefix}" PARENT_SCOPE) + + # We also need to clear the staging prefix if it's set, otherwise CMake will modify any computed + # rpaths containing the staging prefix to point to the new fake prefix, which is not what we + # want. This replacement is done in cmComputeLinkInformation::GetRPath(). + # + # By clearing the staging prefix for the standalone tests, any detected link time + # rpaths will be embedded as-is, which will point to the place where Qt was installed (aka + # the staging prefix). + if(DEFINED CMAKE_STAGING_PREFIX) + message(STATUS "Clearing local standalone test staging prefix (non-cached).") + set(CMAKE_STAGING_PREFIX "" PARENT_SCOPE) + endif() +endfunction() + +# Meant to be called when configuring examples as part of the main build tree (unless standalone +# examples are being built), as well as for CMake tests (tests that call CMake to try and build +# CMake applications). +macro(qt_internal_set_up_build_dir_package_paths) + list(PREPEND CMAKE_PREFIX_PATH "${QT_BUILD_DIR}/${INSTALL_LIBDIR}/cmake") + + # Make sure the CMake config files do not recreate the already-existing targets. + if(NOT QT_BUILD_STANDALONE_EXAMPLES) + set(QT_NO_CREATE_TARGETS TRUE) + endif() +endmacro() + +function(qt_internal_static_link_order_test) + # The CMake versions greater than 3.21 take care about the resource object files order in a + # linker line, it's expected that all object files are located at the beginning of the linker + # line. + # No need to run the test. + if(CMAKE_VERSION VERSION_LESS 3.21) + __qt_internal_check_link_order_matters(link_order_matters) + if(link_order_matters) + set(summary_message "no") + else() + set(summary_message "yes") + endif() + else() + set(summary_message "yes") + endif() + qt_configure_add_summary_entry(TYPE "message" + ARGS "Linker can resolve circular dependencies" + MESSAGE "${summary_message}" + ) +endfunction() + +function(qt_internal_check_cmp0099_available) + # Don't care about CMP0099 in CMake versions greater than or equal to 3.21 + if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.21) + return() + endif() + + __qt_internal_check_cmp0099_available(result) + if(result) + set(summary_message "yes") + else() + set(summary_message "no") + endif() + qt_configure_add_summary_entry(TYPE "message" + ARGS "CMake policy CMP0099 is supported" + MESSAGE "${summary_message}" + ) +endfunction() + +function(qt_internal_run_common_config_tests) + qt_configure_add_summary_section(NAME "Common build options") + qt_internal_static_link_order_test() + qt_internal_check_cmp0099_available() + qt_configure_end_summary_section() +endfunction() + +# It is used in QtWebEngine to replace the REALPATH with ABSOLUTE path, which is +# useful for building Qt in Homebrew. +function(qt_internal_get_filename_path_mode out_var) + set(mode REALPATH) + if(APPLE AND QT_ALLOW_SYMLINK_IN_PATHS) + set(mode ABSOLUTE) + endif() + set(${out_var} ${mode} PARENT_SCOPE) +endfunction() + +macro(qt_internal_setup_platform_support_variables) + # 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) +endmacro() + +function(qt_build_internals_set_up_system_prefixes) + if(APPLE AND NOT FEATURE_pkg_config) + # Remove /usr/local and other paths like that which CMake considers as system prefixes on + # darwin platforms. CMake considers them as system prefixes, but in qmake / Qt land we only + # consider the SDK path as a system prefix. + # 3rd party libraries in these locations should not be picked up when building Qt, + # unless opted-in via the pkg-config feature, which in turn will disable this behavior. + # + # Note that we can't remove /usr as a system prefix path, because many programs won't be + # found then (e.g. perl). + set(QT_CMAKE_SYSTEM_PREFIX_PATH_BACKUP "${CMAKE_SYSTEM_PREFIX_PATH}" PARENT_SCOPE) + set(QT_CMAKE_SYSTEM_FRAMEWORK_PATH_BACKUP "${CMAKE_SYSTEM_FRAMEWORK_PATH}" PARENT_SCOPE) + + list(REMOVE_ITEM CMAKE_SYSTEM_PREFIX_PATH + "/usr/local" # Homebrew + "/opt/homebrew" # Apple Silicon Homebrew + "/usr/X11R6" + "/usr/pkg" + "/opt" + "/sw" # Fink + "/opt/local" # MacPorts + ) + if(_CMAKE_INSTALL_DIR) + list(REMOVE_ITEM CMAKE_SYSTEM_PREFIX_PATH "${_CMAKE_INSTALL_DIR}") + endif() + list(REMOVE_ITEM CMAKE_SYSTEM_FRAMEWORK_PATH "~/Library/Frameworks") + set(CMAKE_SYSTEM_PREFIX_PATH "${CMAKE_SYSTEM_PREFIX_PATH}" PARENT_SCOPE) + set(CMAKE_SYSTEM_FRAMEWORK_PATH "${CMAKE_SYSTEM_FRAMEWORK_PATH}" PARENT_SCOPE) + + # Also tell qt_find_package() not to use PATH when looking for packages. + # We can't simply set CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH to OFF because that will break + # find_program(), and for instance ccache won't be found. + # That's why we set a different variable which is used by qt_find_package. + set(QT_NO_USE_FIND_PACKAGE_SYSTEM_ENVIRONMENT_PATH "ON" PARENT_SCOPE) + endif() +endfunction() + +function(qt_build_internals_disable_pkg_config_if_needed) + # pkg-config should not be used by default on Darwin and Windows platforms (and QNX), as defined + # in the qtbase/configure.json. Unfortunately by the time the feature is evaluated there are + # already a few find_package() calls that try to use the FindPkgConfig module. + # Thus, we have to duplicate the condition logic here and disable pkg-config for those platforms + # by default. + # We also need to check if the pkg-config executable exists, to mirror the condition test in + # configure.json. We do that by trying to find the executable ourselves, and not delegating to + # the FindPkgConfig module because that has more unwanted side-effects. + # + # Note that on macOS, if the pkg-config feature is enabled by the user explicitly, we will also + # tell CMake to consider paths like /usr/local (Homebrew) as system paths when looking for + # packages. + # We have to do that because disabling these paths but keeping pkg-config + # enabled won't enable finding all system libraries via pkg-config alone, many libraries can + # only be found via FooConfig.cmake files which means /usr/local should be in the system prefix + # path. + + set(pkg_config_enabled ON) + qt_build_internals_find_pkg_config_executable() + + if(APPLE OR WIN32 OR QNX OR ANDROID OR WASM OR (NOT PKG_CONFIG_EXECUTABLE)) + set(pkg_config_enabled OFF) + endif() + + # If user explicitly specified a value for the feature, honor it, even if it might break + # the build. + if(DEFINED FEATURE_pkg_config) + if(FEATURE_pkg_config) + set(pkg_config_enabled ON) + else() + set(pkg_config_enabled OFF) + endif() + endif() + + set(FEATURE_pkg_config "${pkg_config_enabled}" CACHE STRING "Using pkg-config") + if(NOT pkg_config_enabled) + qt_build_internals_disable_pkg_config() + else() + unset(PKG_CONFIG_EXECUTABLE CACHE) + endif() +endfunction() + +# This is a copy of the first few lines in FindPkgConfig.cmake. +function(qt_build_internals_find_pkg_config_executable) + # find pkg-config, use PKG_CONFIG if set + if((NOT PKG_CONFIG_EXECUTABLE) AND (NOT "$ENV{PKG_CONFIG}" STREQUAL "")) + set(PKG_CONFIG_EXECUTABLE "$ENV{PKG_CONFIG}" CACHE FILEPATH "pkg-config executable") + endif() + find_program(PKG_CONFIG_EXECUTABLE NAMES pkg-config DOC "pkg-config executable") + mark_as_advanced(PKG_CONFIG_EXECUTABLE) +endfunction() + +function(qt_build_internals_disable_pkg_config) + # Disable pkg-config by setting an empty executable path. There's no documented way to + # mark the package as not found, but we can force all pkg_check_modules calls to do nothing + # by setting the variable to an empty value. + set(PKG_CONFIG_EXECUTABLE "" CACHE STRING "Disabled pkg-config usage." FORCE) +endfunction() + +macro(qt_build_internals_find_pkg_config) + # Find package config once before any system prefix modifications. + find_package(PkgConfig QUIET) +endmacro() + + +macro(qt_internal_setup_pkg_config_and_system_prefixes) + if(NOT QT_BUILD_INTERNALS_SKIP_PKG_CONFIG_ADJUSTMENT) + qt_build_internals_disable_pkg_config_if_needed() + endif() + + if(NOT QT_BUILD_INTERNALS_SKIP_FIND_PKG_CONFIG) + qt_build_internals_find_pkg_config() + endif() + + if(NOT QT_BUILD_INTERNALS_SKIP_SYSTEM_PREFIX_ADJUSTMENT) + qt_build_internals_set_up_system_prefixes() + endif() +endmacro() + +macro(qt_internal_setup_standalone_test_when_called_as_a_find_package_component) + if ("STANDALONE_TEST" IN_LIST Qt6BuildInternals_FIND_COMPONENTS) + include(${CMAKE_CURRENT_LIST_DIR}/QtStandaloneTestTemplateProject/Main.cmake) + if (NOT PROJECT_VERSION_MAJOR) + get_property(_qt_major_version TARGET ${QT_CMAKE_EXPORT_NAMESPACE}::Core PROPERTY INTERFACE_QT_MAJOR_VERSION) + set(PROJECT_VERSION ${Qt${_qt_major_version}Core_VERSION}) + + string(REPLACE "." ";" _qt_core_version_list ${PROJECT_VERSION}) + list(GET _qt_core_version_list 0 PROJECT_VERSION_MAJOR) + list(GET _qt_core_version_list 1 PROJECT_VERSION_MINOR) + list(GET _qt_core_version_list 2 PROJECT_VERSION_PATCH) + endif() + endif() +endmacro() + +macro(qt_internal_setup_build_internals) + qt_internal_set_qt_repo_dependencies() + qt_internal_setup_platform_support_variables() + qt_internal_setup_pkg_config_and_system_prefixes() + qt_internal_setup_standalone_test_when_called_as_a_find_package_component() +endmacro() + +# Recursively reads the dependencies section from dependencies.yaml in ${repo_dir} and returns the +# list of dependencies, including transitive ones, in out_var. +# +# The returned dependencies are topologically sorted. +# +# Example output for qtdeclarative: +# qtbase;qtimageformats;qtlanguageserver;qtshadertools;qtsvg +# +function(qt_internal_read_repo_dependencies out_var repo_dir) + set(seen ${ARGN}) + set(dependencies "") + set(in_dependencies_section FALSE) + set(dependencies_file "${repo_dir}/dependencies.yaml") + if(EXISTS "${dependencies_file}") + file(STRINGS "${dependencies_file}" lines) + foreach(line IN LISTS lines) + if(line MATCHES "^([^ ]+):") + if(CMAKE_MATCH_1 STREQUAL "dependencies") + set(in_dependencies_section TRUE) + else() + set(in_dependencies_section FALSE) + endif() + elseif(in_dependencies_section AND line MATCHES "^ (.+):$") + set(dependency "${CMAKE_MATCH_1}") + set(dependency_repo_dir "${repo_dir}/${dependency}") + string(REGEX MATCH "[^/]+$" dependency "${dependency}") + if(NOT dependency IN_LIST seen) + qt_internal_read_repo_dependencies(subdeps "${dependency_repo_dir}" + ${seen} ${dependency}) + if(dependency MATCHES "^tqtc-(.+)") + set(dependency "${CMAKE_MATCH_1}") + endif() + list(APPEND dependencies ${subdeps} ${dependency}) + endif() + endif() + endforeach() + list(REMOVE_DUPLICATES dependencies) + endif() + set(${out_var} "${dependencies}" PARENT_SCOPE) +endfunction() + +macro(qt_internal_set_qt_repo_dependencies) + # The top-level check needs to happen because it's possible + # to configure a top-level build with a few repos and then configure another repo + # using qt-configure-module in a separate build dir, where QT_SUPERBUILD will not + # be set anymore. + if(DEFINED QT_REPO_MODULE_VERSION AND NOT DEFINED QT_REPO_DEPENDENCIES AND NOT QT_SUPERBUILD) + qt_internal_read_repo_dependencies(QT_REPO_DEPENDENCIES "${PROJECT_SOURCE_DIR}") + endif() +endmacro() diff --git a/cmake/QtCMakeHelpers.cmake b/cmake/QtCMakeHelpers.cmake index 142a003cbc..0fe95f32fd 100644 --- a/cmake/QtCMakeHelpers.cmake +++ b/cmake/QtCMakeHelpers.cmake @@ -61,14 +61,6 @@ macro(qt_parse_all_arguments result type flags options multiopts) endif() endmacro() -# Checks whether any unparsed arguments have been passed to the function at the call site. -# Use this right after `cmake_parse_arguments`. -function(_qt_internal_validate_all_args_are_parsed prefix) - if(DEFINED ${prefix}_UNPARSED_ARGUMENTS) - message(FATAL_ERROR "Unknown arguments: (${${prefix}_UNPARSED_ARGUMENTS})") - endif() -endfunction() - # Print all variables defined in the current scope. macro(qt_debug_print_variables) cmake_parse_arguments(__arg "DEDUP" "" "MATCH;IGNORE" ${ARGN}) @@ -116,63 +108,12 @@ endmacro() # Takes a list of path components and joins them into one path separated by forward slashes "/", # and saves the path in out_var. function(qt_path_join out_var) - string(JOIN "/" path ${ARGN}) + _qt_internal_path_join(path ${ARGN}) set(${out_var} ${path} PARENT_SCOPE) endfunction() -# qt_remove_args can remove arguments from an existing list of function -# arguments in order to pass a filtered list of arguments to a different function. -# Parameters: -# out_var: result of remove all arguments specified by ARGS_TO_REMOVE from ALL_ARGS -# ARGS_TO_REMOVE: Arguments to remove. -# ALL_ARGS: All arguments supplied to cmake_parse_arguments -# from which ARGS_TO_REMOVE should be removed from. We require all the -# arguments or we can't properly identify the range of the arguments detailed -# in ARGS_TO_REMOVE. -# ARGS: Arguments passed into the function, usually ${ARGV} -# -# E.g.: -# We want to forward all arguments from foo to bar, execpt ZZZ since it will -# trigger an error in bar. -# -# foo(target BAR .... ZZZ .... WWW ...) -# bar(target BAR.... WWW...) -# -# function(foo target) -# cmake_parse_arguments(PARSE_ARGV 1 arg "" "" "BAR;ZZZ;WWW") -# qt_remove_args(forward_args -# ARGS_TO_REMOVE ${target} ZZZ -# ALL_ARGS ${target} BAR ZZZ WWW -# ARGS ${ARGV} -# ) -# bar(${target} ${forward_args}) -# endfunction() -# function(qt_remove_args out_var) - cmake_parse_arguments(arg "" "" "ARGS_TO_REMOVE;ALL_ARGS;ARGS" ${ARGN}) - set(result ${arg_ARGS}) - foreach(arg IN LISTS arg_ARGS_TO_REMOVE) - # find arg - list(FIND result ${arg} find_result) - if (NOT find_result EQUAL -1) - # remove arg - list(REMOVE_AT result ${find_result}) - list(LENGTH result result_len) - if(find_result EQUAL result_len) - # We removed the last argument, could have been an option keyword - continue() - endif() - list(GET result ${find_result} arg_current) - # remove values until we hit another arg or the end of the list - while(NOT "${arg_current}" IN_LIST arg_ALL_ARGS AND find_result LESS result_len) - list(REMOVE_AT result ${find_result}) - list(LENGTH result result_len) - if (NOT find_result EQUAL result_len) - list(GET result ${find_result} arg_current) - endif() - endwhile() - endif() - endforeach() + _qt_internal_remove_args(result ${ARGN}) set(${out_var} "${result}" PARENT_SCOPE) endfunction() diff --git a/cmake/QtCMakePackageVersionFile.cmake.in b/cmake/QtCMakePackageVersionFile.cmake.in index 185bdb491f..d4c30b33ac 100644 --- a/cmake/QtCMakePackageVersionFile.cmake.in +++ b/cmake/QtCMakePackageVersionFile.cmake.in @@ -1,3 +1,6 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # Include the basic version config file to get results of regular version checking. include("${CMAKE_CURRENT_LIST_DIR}/@package_name@ConfigVersionImpl.cmake") diff --git a/cmake/QtCMakeVersionHelpers.cmake b/cmake/QtCMakeVersionHelpers.cmake index c17a75e29f..322e58eed1 100644 --- a/cmake/QtCMakeVersionHelpers.cmake +++ b/cmake/QtCMakeVersionHelpers.cmake @@ -14,6 +14,8 @@ function(qt_internal_get_supported_min_cmake_version_for_building_qt out_var) set(supported_version "${QT_SUPPORTED_MIN_CMAKE_VERSION_FOR_BUILDING_QT}") # We're building qtbase so the values come from .cmake.conf. + elseif(APPLE) + set(supported_version "${QT_SUPPORTED_MIN_CMAKE_VERSION_FOR_BUILDING_QT_APPLE}") elseif(BUILD_SHARED_LIBS) set(supported_version "${QT_SUPPORTED_MIN_CMAKE_VERSION_FOR_BUILDING_QT_SHARED}") else() @@ -30,7 +32,9 @@ function(qt_internal_get_supported_min_cmake_version_for_using_qt out_var) "It should have been set by this point.") endif() - if(BUILD_SHARED_LIBS) + if(APPLE) + set(supported_version "${QT_SUPPORTED_MIN_CMAKE_VERSION_FOR_USING_QT_APPLE}") + elseif(BUILD_SHARED_LIBS) set(supported_version "${QT_SUPPORTED_MIN_CMAKE_VERSION_FOR_USING_QT_SHARED}") else() set(supported_version "${QT_SUPPORTED_MIN_CMAKE_VERSION_FOR_USING_QT_STATIC}") diff --git a/cmake/QtCompilerFlags.cmake b/cmake/QtCompilerFlags.cmake index 62bd63bd22..f58f36b7a2 100644 --- a/cmake/QtCompilerFlags.cmake +++ b/cmake/QtCompilerFlags.cmake @@ -8,18 +8,16 @@ # property checked below, and is equivalent to qmake's CONFIG += warn_off. set(_qt_compiler_warning_flags_on "") -set(_qt_compiler_warning_flags_off "") +set(_qt_compiler_warning_flags_off -w) if (MSVC) list(APPEND _qt_compiler_warning_flags_on /W3) - list(APPEND _qt_compiler_warning_flags_off -W0) else() if(CMAKE_CXX_COMPILER_ID STREQUAL "GHS") # There is no -Wextra flag for GHS compiler. list(APPEND _qt_compiler_warning_flags_on -Wall) else() list(APPEND _qt_compiler_warning_flags_on -Wall -Wextra) endif() - list(APPEND _qt_compiler_warning_flags_off -w) endif() set(_qt_compiler_warning_flags_condition @@ -27,8 +25,15 @@ set(_qt_compiler_warning_flags_condition set(_qt_compiler_warning_flags_genex "$<IF:${_qt_compiler_warning_flags_condition},${_qt_compiler_warning_flags_off},${_qt_compiler_warning_flags_on}>") +set(_qt_compiler_warning_flags_language_condition + "$<COMPILE_LANGUAGE:CXX,C,OBJC,OBJCXX>") +set(_qt_compiler_warning_flags_language_conditional_genex + "$<${_qt_compiler_warning_flags_language_condition}:${_qt_compiler_warning_flags_genex}>") + + # Need to replace semicolons so that the list is not wrongly expanded in the add_compile_options # call. string(REPLACE ";" "$<SEMICOLON>" - _qt_compiler_warning_flags_genex "${_qt_compiler_warning_flags_genex}") -add_compile_options(${_qt_compiler_warning_flags_genex}) + _qt_compiler_warning_flags_language_conditional_genex + "${_qt_compiler_warning_flags_language_conditional_genex}") +add_compile_options(${_qt_compiler_warning_flags_language_conditional_genex}) diff --git a/cmake/QtCompilerOptimization.cmake b/cmake/QtCompilerOptimization.cmake index e1d363afab..ac542e9451 100644 --- a/cmake/QtCompilerOptimization.cmake +++ b/cmake/QtCompilerOptimization.cmake @@ -35,35 +35,40 @@ if (MSVC) endif() if(GCC OR CLANG OR QCC) - set(QT_CFLAGS_SSE2 "-msse2") - set(QT_CFLAGS_SSE3 "-msse3") - set(QT_CFLAGS_SSSE3 "-mssse3") - set(QT_CFLAGS_SSE4_1 "-msse4.1") - set(QT_CFLAGS_SSE4_2 "-msse4.2") - set(QT_CFLAGS_F16C "-mf16c") - set(QT_CFLAGS_RDRND "-mrdrnd") - set(QT_CFLAGS_RDSEED "-mrdseed") - set(QT_CFLAGS_AVX "-mavx") - set(QT_CFLAGS_AVX2 "-mavx2") - set(QT_CFLAGS_ARCH_HASWELL "-march=haswell") - set(QT_CFLAGS_AVX512F "-mavx512f") - set(QT_CFLAGS_AVX512ER "-mavx512er") - set(QT_CFLAGS_AVX512CD "-mavx512cd") - set(QT_CFLAGS_AVX512PF "-mavx512pf") - set(QT_CFLAGS_AVX512DQ "-mavx512dq") - set(QT_CFLAGS_AVX512BW "-mavx512bw") - set(QT_CFLAGS_AVX512VL "-mavx512vl") - set(QT_CFLAGS_AVX512IFMA "-mavx512ifma") - set(QT_CFLAGS_AVX512VBMI "-mavx512vbmi") - set(QT_CFLAGS_AVX512VBMI2 "-mavx512vbmi2") - set(QT_CFLAGS_AESNI "-maes") - set(QT_CFLAGS_SHANI "-msha") - set(QT_CFLAGS_VAES "-mvaes") + set(__prefix) + if(MSVC AND CLANG) + set(__prefix "/clang:") + endif() + set(QT_CFLAGS_SSE2 "${__prefix}-msse2") + set(QT_CFLAGS_SSE3 "${__prefix}-msse3") + set(QT_CFLAGS_SSSE3 "${__prefix}-mssse3") + set(QT_CFLAGS_SSE4_1 "${__prefix}-msse4.1") + set(QT_CFLAGS_SSE4_2 "${__prefix}-msse4.2") + set(QT_CFLAGS_F16C "${__prefix}-mf16c") + set(QT_CFLAGS_RDRND "${__prefix}-mrdrnd") + set(QT_CFLAGS_RDSEED "${__prefix}-mrdseed") + set(QT_CFLAGS_AVX "${__prefix}-mavx") + set(QT_CFLAGS_AVX2 "${__prefix}-mavx2") + set(QT_CFLAGS_ARCH_HASWELL "${__prefix}-march=haswell") + set(QT_CFLAGS_AVX512F "${__prefix}-mavx512f") + set(QT_CFLAGS_AVX512ER "${__prefix}-mavx512er") + set(QT_CFLAGS_AVX512CD "${__prefix}-mavx512cd") + set(QT_CFLAGS_AVX512PF "${__prefix}-mavx512pf") + set(QT_CFLAGS_AVX512DQ "${__prefix}-mavx512dq") + set(QT_CFLAGS_AVX512BW "${__prefix}-mavx512bw") + set(QT_CFLAGS_AVX512VL "${__prefix}-mavx512vl") + set(QT_CFLAGS_AVX512IFMA "${__prefix}-mavx512ifma") + set(QT_CFLAGS_AVX512VBMI "${__prefix}-mavx512vbmi") + set(QT_CFLAGS_AVX512VBMI2 "${__prefix}-mavx512vbmi2") + set(QT_CFLAGS_AESNI "${__prefix}-maes") + set(QT_CFLAGS_SHANI "${__prefix}-msha") + set(QT_CFLAGS_VAES "${__prefix}-mvaes") if(NOT UIKIT AND NOT QT_64BIT) - set(QT_CFLAGS_NEON "-mfpu=neon") + set(QT_CFLAGS_NEON "${__prefix}-mfpu=neon") endif() - set(QT_CFLAGS_MIPS_DSP "-mdsp") - set(QT_CFLAGS_MIPS_DSPR2 "-mdspr2") + set(QT_CFLAGS_MIPS_DSP "${__prefix}-mdsp") + set(QT_CFLAGS_MIPS_DSPR2 "${__prefix}-mdspr2") + unset(__prefix) endif() # Fall through is important, so that more specific flags that might be missing are set by the @@ -92,7 +97,12 @@ endif() # Windows MSVC if(MSVC) - set(QT_CFLAGS_OPTIMIZE "-O2 -Ob3") # -Ob3 was introduced in Visual Studio 2019 version 16.0 + set(QT_CFLAGS_OPTIMIZE "-O2") + if(NOT CLANG) + # -Ob3 was introduced in Visual Studio 2019 version 16.0 + # However clang-cl can't recognize it. + string(APPEND QT_CFLAGS_OPTIMIZE " -Ob3 ") + endif() set(QT_CFLAGS_OPTIMIZE_DEBUG "-Od") set(QT_CFLAGS_OPTIMIZE_SIZE "-O1") set(QT_CFLAGS_OPTIMIZE_VALID_VALUES "/O2" "/O1" "/Od" "/Ob0" "/Ob1" "/Ob2" "/Ob3" "/O0" "-O0") diff --git a/cmake/QtConfig.cmake.in b/cmake/QtConfig.cmake.in index ba28744713..b5a21e391d 100644 --- a/cmake/QtConfig.cmake.in +++ b/cmake/QtConfig.cmake.in @@ -1,9 +1,15 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + @PACKAGE_INIT@ cmake_minimum_required(VERSION @min_new_policy_version@...@max_new_policy_version@) include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@ConfigExtras.cmake") include("${CMAKE_CURRENT_LIST_DIR}/QtPublicCMakeVersionHelpers.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/QtPublicCMakeHelpers.cmake") +include("${CMAKE_CURRENT_LIST_DIR}/QtInstallPaths.cmake") + __qt_internal_require_suitable_cmake_version_for_using_qt() get_filename_component(_qt_cmake_dir "${CMAKE_CURRENT_LIST_DIR}/.." ABSOLUTE) @@ -12,7 +18,11 @@ set(_qt_@PROJECT_VERSION_MAJOR@_config_cmake_dir "${CMAKE_CURRENT_LIST_DIR}") if (NOT QT_NO_CREATE_TARGETS) include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@Targets.cmake") if(NOT QT_NO_CREATE_VERSIONLESS_TARGETS) - include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@VersionlessTargets.cmake") + if(CMAKE_VERSION VERSION_LESS 3.18 OR QT_USE_OLD_VERSION_LESS_TARGETS) + include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@VersionlessTargets.cmake") + else() + include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@VersionlessAliasTargets.cmake") + endif() endif() else() # For examples using `find_package(...)` inside their CMakeLists.txt files: @@ -34,21 +44,22 @@ if(APPLE) set(__qt_internal_cmake_apple_support_files_path "${_qt_import_prefix}/macos") elseif(CMAKE_SYSTEM_NAME STREQUAL "iOS") set(__qt_internal_cmake_apple_support_files_path "${_qt_import_prefix}/ios") + elseif(CMAKE_SYSTEM_NAME STREQUAL "visionOS") + set(__qt_internal_cmake_apple_support_files_path "${_qt_import_prefix}/visionos") endif() endif() # Public helpers available to all Qt packages. -include("${CMAKE_CURRENT_LIST_DIR}/QtFeature.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/QtPublicAppleHelpers.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/QtPublicFinalizerHelpers.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/QtPublicPluginHelpers.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/QtPublicTargetHelpers.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/QtPublicWalkLibsHelpers.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/QtPublicFindPackageHelpers.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/QtPublicDependencyHelpers.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/QtPublicTestHelpers.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/QtPublicToolHelpers.cmake") -include("${CMAKE_CURRENT_LIST_DIR}/QtPublicCMakeHelpers.cmake") +set(__qt_public_files_to_include + @QT_PUBLIC_FILES_TO_INCLUDE@ +) +foreach(__qt_public_file_to_include IN LISTS __qt_public_files_to_include) + include("${__qt_public_file_to_include}") +endforeach() + +if(NOT DEFINED QT_CMAKE_EXPORT_NAMESPACE) + set(QT_CMAKE_EXPORT_NAMESPACE @QT_CMAKE_EXPORT_NAMESPACE@) +endif() set(QT_ADDITIONAL_PACKAGES_PREFIX_PATH "" CACHE STRING "Additional directories where find(Qt6 ...) components are searched") @@ -63,9 +74,7 @@ __qt_internal_collect_additional_prefix_paths(_qt_additional_host_packages_prefi __qt_internal_prefix_paths_to_roots(_qt_additional_host_packages_root_paths "${_qt_additional_host_packages_prefix_paths}") -if(NOT DEFINED QT_CMAKE_EXPORT_NAMESPACE) - set(QT_CMAKE_EXPORT_NAMESPACE @QT_CMAKE_EXPORT_NAMESPACE@) -endif() +__qt_internal_collect_additional_module_paths() # Propagate sanitizer flags to both internal Qt builds and user projects. # Allow opt-out in case if downstream projects handle it in a different way. @@ -133,9 +142,9 @@ foreach(module ${@INSTALL_CMAKE_NAMESPACE@_FIND_COMPONENTS}) ${@INSTALL_CMAKE_NAMESPACE@_FIND_VERSION} ${_@INSTALL_CMAKE_NAMESPACE@_FIND_PARTS_QUIET} PATHS + ${QT_BUILD_CMAKE_PREFIX_PATH} ${_qt_cmake_dir} ${_qt_additional_packages_prefix_paths} - ${QT_EXAMPLES_CMAKE_PREFIX_PATH} ${__qt_find_package_host_qt_path} ${_qt_additional_host_packages_prefix_paths} ${__qt_use_no_default_path_for_qt_packages} diff --git a/cmake/QtConfigDependencies.cmake.in b/cmake/QtConfigDependencies.cmake.in index c17ae28bac..9824f8525c 100644 --- a/cmake/QtConfigDependencies.cmake.in +++ b/cmake/QtConfigDependencies.cmake.in @@ -1,3 +1,6 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + set(@INSTALL_CMAKE_NAMESPACE@_FOUND FALSE) set(__qt_platform_requires_host_info_package "@platform_requires_host_info_package@") diff --git a/cmake/QtConfigExtras.cmake.in b/cmake/QtConfigExtras.cmake.in index bde3460cdc..06acd33c82 100644 --- a/cmake/QtConfigExtras.cmake.in +++ b/cmake/QtConfigExtras.cmake.in @@ -1,2 +1,7 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + set(QT_SUPPORTED_MIN_CMAKE_VERSION_FOR_USING_QT "@supported_min_version_for_using_qt@") set(QT_COMPUTED_MIN_CMAKE_VERSION_FOR_USING_QT "@computed_min_version_for_using_qt@") + +@QT_CONFIG_EXTRAS_CODE@ diff --git a/cmake/QtConfigureTimeExecutableCMakeLists.txt.in b/cmake/QtConfigureTimeExecutableCMakeLists.txt.in index a36299706c..17acb37f0e 100644 --- a/cmake/QtConfigureTimeExecutableCMakeLists.txt.in +++ b/cmake/QtConfigureTimeExecutableCMakeLists.txt.in @@ -1,3 +1,6 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + cmake_minimum_required(VERSION 3.16) project(@configure_time_target@ LANGUAGES CXX) diff --git a/cmake/QtDocsHelpers.cmake b/cmake/QtDocsHelpers.cmake index 48ed5a324b..8b631e88ca 100644 --- a/cmake/QtDocsHelpers.cmake +++ b/cmake/QtDocsHelpers.cmake @@ -21,12 +21,37 @@ function(qt_internal_add_docs) # Function called from old generated CMakeLists.txt that was missing the target parameter return() endif() - if(NOT ${ARGC} EQUAL 2) - message(FATAL_ERROR "qt_add_docs called with the wrong number of arguments. Should be qt_add_docs(target path_to_project.qdocconf).") + set(error_msg "qt_add_docs called with wrong number of arguments. ") + list(APPEND error_msg + "Should be qt_add_docs\(target_name qdocconf " + "\[INDEX_DIRECTORIES EXTRA_INDEX_DIRS_LIST_TO_ENABLE_QDOC_RESOLVE_LINKS\]\)") + if(NOT ${ARGC} GREATER_EQUAL 2) + message(FATAL_ERROR ${error_msg}) return() endif() + set(target ${ARGV0}) set(doc_project ${ARGV1}) + set(qdoc_extra_args "") + # Check if there are more than 2 arguments and pass them + # as extra --indexdir arguments to qdoc in prepare and + # generate phases. + if (${ARGC} GREATER 2) + # The INDEX_DIRECTORIES key should enable passing a list of index + # directories as extra command-line arguments to qdoc. + set(qdocExtraArgs INDEX_DIRECTORIES) + cmake_parse_arguments(PARSE_ARGV 2 arg "" "" "${qdocExtraArgs}") + if(arg_UNPARSED_ARGUMENTS) + message(FATAL_ERROR ${error_msg}) + return() + endif() + if(arg_INDEX_DIRECTORIES) + foreach(index_directory ${arg_INDEX_DIRECTORIES}) + list(APPEND qdoc_extra_args "--indexdir" ${index_directory}) + endforeach() + endif() + endif() + # If a target is not built (which can happen for tools when crosscompiling), we shouldn't try # to generate docs. @@ -114,13 +139,14 @@ function(qt_internal_add_docs) if(NOT QT_BUILD_ONLINE_DOCS) list(PREPEND prepare_qdoc_args -installdir "${QT_INSTALL_DIR}/${INSTALL_DOCDIR}" + ${qdoc_extra_args} ) endif() - if(QT_SUPERBUILD) + if(DEFINED ENV{QT_INSTALL_DOCS}) + set(qt_install_docs_env "$ENV{QT_INSTALL_DOCS}") + elseif(QT_SUPERBUILD OR "${PROJECT_NAME}" STREQUAL "QtBase") set(qt_install_docs_env "${QtBase_BINARY_DIR}/${INSTALL_DOCDIR}") - elseif(QT_WILL_INSTALL) - set(qt_install_docs_env "${CMAKE_INSTALL_PREFIX}/${INSTALL_DOCDIR}") else() set(qt_install_docs_env "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}/${INSTALL_DOCDIR}") endif() @@ -156,6 +182,7 @@ function(qt_internal_add_docs) if(NOT QT_BUILD_ONLINE_DOCS) list(PREPEND generate_qdoc_args -installdir "${QT_INSTALL_DIR}/${INSTALL_DOCDIR}" + ${qdoc_extra_args} ) endif() diff --git a/cmake/QtExecutableHelpers.cmake b/cmake/QtExecutableHelpers.cmake index 1541010295..08da17b7e0 100644 --- a/cmake/QtExecutableHelpers.cmake +++ b/cmake/QtExecutableHelpers.cmake @@ -30,6 +30,7 @@ function(qt_internal_add_executable name) endif() _qt_internal_create_executable(${name}) + qt_internal_mark_as_internal_target(${name}) if(ANDROID) _qt_internal_android_executable_finalizer(${name}) endif() @@ -119,6 +120,7 @@ function(qt_internal_add_executable name) qt_internal_extend_target("${name}" ${arg_NO_UNITY_BUILD} SOURCES ${arg_SOURCES} + NO_PCH_SOURCES ${arg_NO_PCH_SOURCES} NO_UNITY_BUILD_SOURCES ${arg_NO_UNITY_BUILD_SOURCES} INCLUDE_DIRECTORIES ${private_includes} DEFINES ${arg_DEFINES} @@ -144,12 +146,7 @@ function(qt_internal_add_executable name) MACOSX_BUNDLE "${arg_GUI}" ) - if(WASM) - # WASM unconditionally sets DISABLE_EXCEPTION_CATCHING=1 - qt_internal_set_exceptions_flags("${name}" FALSE) - else() - qt_internal_set_exceptions_flags("${name}" ${arg_EXCEPTIONS}) - endif() + qt_internal_set_exceptions_flags("${name}" ${arg_EXCEPTIONS}) if(WASM) qt_internal_wasm_add_finalizers("${name}") @@ -180,7 +177,7 @@ function(qt_internal_add_executable name) qt_get_install_target_default_args( OUT_VAR install_targets_default_args CMAKE_CONFIG "${cmake_config}" - ALL_CMAKE_CONFIGS "${cmake_configs}" + ALL_CMAKE_CONFIGS ${cmake_configs} RUNTIME "${arg_INSTALL_DIRECTORY}" LIBRARY "${arg_INSTALL_DIRECTORY}" BUNDLE "${arg_INSTALL_DIRECTORY}") @@ -210,103 +207,6 @@ function(qt_internal_add_executable name) ADDITIONAL_INSTALL_ARGS ${additional_install_args}) qt_internal_install_pdb_files(${name} "${arg_INSTALL_DIRECTORY}") endif() - - # If linking against Gui, make sure to also build the default QPA plugin. - # This makes the experience of an initial Qt configuration to build and run one single - # test / executable nicer. - get_target_property(linked_libs "${name}" LINK_LIBRARIES) - if("Qt::Gui" IN_LIST linked_libs AND TARGET qpa_default_plugins) - add_dependencies("${name}" qpa_default_plugins) - endif() - - # For static plugins, we need to explicitly link to plugins we want to be - # loaded with the executable. User projects get that automatically, but - # for tools built as part of Qt, we can't use that mechanism because it - # would pollute the targets we export as part of an install and lead to - # circular dependencies. The logic here is a simpler equivalent of the - # more dynamic logic in QtPlugins.cmake.in, but restricted to only - # adding plugins that are provided by the same module as the module - # libraries the executable links to. - set(libs - ${arg_LIBRARIES} - ${arg_PUBLIC_LIBRARIES} - ${extra_libraries} - Qt::PlatformCommonInternal - ) - - set(deduped_libs "") - foreach(lib IN LISTS libs) - if(NOT TARGET "${lib}") - continue() - endif() - - # Normalize module by stripping any leading "Qt::", because properties are set on the - # versioned target (either Gui when building the module, or Qt6::Gui when it's - # imported). - if(lib MATCHES "Qt::([-_A-Za-z0-9]+)") - set(new_lib "${QT_CMAKE_EXPORT_NAMESPACE}::${CMAKE_MATCH_1}") - if(TARGET "${new_lib}") - set(lib "${new_lib}") - endif() - endif() - - # Unalias the target. - get_target_property(aliased_target ${lib} ALIASED_TARGET) - if(aliased_target) - set(lib ${aliased_target}) - endif() - - list(APPEND deduped_libs "${lib}") - endforeach() - - list(REMOVE_DUPLICATES deduped_libs) - - foreach(lib IN LISTS deduped_libs) - string(MAKE_C_IDENTIFIER "${name}_plugin_imports_${lib}" out_file) - string(APPEND out_file .cpp) - - # Initialize plugins that are built in the same repository as the Qt module 'lib'. - set(class_names_regular - "$<GENEX_EVAL:$<TARGET_PROPERTY:${lib},_qt_initial_repo_plugin_class_names>>") - - # Initialize plugins that are built in the current Qt repository, but are associated - # with a Qt module from a different repository (qtsvg's QSvgPlugin associated with - # qtbase's QtGui). - string(MAKE_C_IDENTIFIER "${PROJECT_NAME}" current_project_name) - set(prop_prefix "_qt_repo_${current_project_name}") - set(class_names_current_project - "$<GENEX_EVAL:$<TARGET_PROPERTY:${lib},${prop_prefix}_plugin_class_names>>") - - # Only add separator if first list is not empty, so we don't trigger the file generation - # when all lists are empty. - set(class_names_separator "$<$<NOT:$<STREQUAL:${class_names_regular},>>:;>" ) - set(class_names - "${class_names_regular}${class_names_separator}${class_names_current_project}") - - set(out_file_path "${CMAKE_CURRENT_BINARY_DIR}/${out_file}") - - file(GENERATE OUTPUT "${out_file_path}" CONTENT -"// This file is auto-generated. Do not edit. -#include <QtPlugin> - -Q_IMPORT_PLUGIN($<JOIN:${class_names},)\nQ_IMPORT_PLUGIN(>) -" - CONDITION "$<NOT:$<STREQUAL:${class_names},>>" - ) - - # CMake versions earlier than 3.18.0 can't find the generated file for some reason, - # failing at generation phase. - # Explicitly marking the file as GENERATED fixes the issue. - set_source_files_properties("${out_file_path}" PROPERTIES GENERATED TRUE) - - target_sources(${name} PRIVATE - "$<$<NOT:$<STREQUAL:${class_names},>>:${out_file_path}>" - ) - target_link_libraries(${name} PRIVATE - "$<TARGET_PROPERTY:${lib},_qt_initial_repo_plugins>" - "$<TARGET_PROPERTY:${lib},${prop_prefix}_plugins>") - endforeach() - endfunction() # This function compiles the target at configure time the very first time and creates the custom @@ -369,6 +269,7 @@ function(qt_internal_add_configure_time_executable target) set(target_binary_dir "${CMAKE_CURRENT_BINARY_DIR}/configure_time_bins") if(arg_CONFIG) set(CMAKE_TRY_COMPILE_CONFIGURATION "${arg_CONFIG}") + string(TOUPPER "_${arg_CONFIG}" config_suffix) endif() get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) @@ -387,9 +288,15 @@ function(qt_internal_add_configure_time_executable target) if(arg_INSTALL_DIRECTORY) set(install_dir "${arg_INSTALL_DIRECTORY}") endif() + + set(output_directory_relative "${install_dir}") set(output_directory "${QT_BUILD_DIR}/${install_dir}") + + set(target_binary_path_relative + "${output_directory_relative}/${configuration_path}${target_binary}") set(target_binary_path "${output_directory}/${configuration_path}${target_binary}") + get_filename_component(target_binary_path "${target_binary_path}" ABSOLUTE) if(NOT DEFINED arg_SOURCES) @@ -419,7 +326,8 @@ function(qt_internal_add_configure_time_executable target) ) set(should_build_at_configure_time TRUE) - if(EXISTS "${target_binary_path}") + if(QT_INTERNAL_HAVE_CONFIGURE_TIME_${target} AND + EXISTS "${target_binary_path}" AND EXISTS "${timestamp_file}") set(last_ts 0) foreach(source IN LISTS sources) file(TIMESTAMP "${source}" ts "%s") @@ -434,6 +342,37 @@ function(qt_internal_add_configure_time_executable target) endif() endif() + set(cmake_flags_arg "") + if(arg_CMAKE_FLAGS) + set(cmake_flags_arg CMAKE_FLAGS "${arg_CMAKE_FLAGS}") + endif() + + qt_internal_get_enabled_languages_for_flag_manipulation(enabled_languages) + foreach(lang IN LISTS enabled_languages) + set(compiler_flags_var "CMAKE_${lang}_FLAGS") + list(APPEND cmake_flags_arg "-D${compiler_flags_var}:STRING=${${compiler_flags_var}}") + if(arg_CONFIG) + set(compiler_flags_var_config "${compiler_flags_var}${config_suffix}") + list(APPEND cmake_flags_arg + "-D${compiler_flags_var_config}:STRING=${${compiler_flags_var_config}}") + endif() + endforeach() + + qt_internal_get_target_link_types_for_flag_manipulation(target_link_types) + foreach(linker_type IN LISTS target_link_types) + set(linker_flags_var "CMAKE_${linker_type}_LINKER_FLAGS") + list(APPEND cmake_flags_arg "-D${linker_flags_var}:STRING=${${linker_flags_var}}") + if(arg_CONFIG) + set(linker_flags_var_config "${linker_flags_var}${config_suffix}") + list(APPEND cmake_flags_arg + "-D${linker_flags_var_config}:STRING=${${linker_flags_var_config}}") + endif() + endforeach() + + if(NOT "${QT_INTERNAL_CMAKE_FLAGS_CONFIGURE_TIME_TOOL_${target}}" STREQUAL "${cmake_flags_arg}") + set(should_build_at_configure_time TRUE) + endif() + if(should_build_at_configure_time) foreach(arg IN LISTS multi_value_args) string(TOLOWER "${arg}" template_arg_name) @@ -457,11 +396,12 @@ function(qt_internal_add_configure_time_executable target) set(template "${arg_CMAKELISTS_TEMPLATE}") endif() - set(cmake_flags_arg) - if(arg_CMAKE_FLAGS) - set(cmake_flags_arg CMAKE_FLAGS "${arg_CMAKE_FLAGS}") - endif() configure_file("${template}" "${target_binary_dir}/CMakeLists.txt" @ONLY) + + if(EXISTS "${target_binary_dir}/CMakeCache.txt") + file(REMOVE "${target_binary_dir}/CMakeCache.txt") + endif() + try_compile(result "${target_binary_dir}" "${target_binary_dir}" @@ -470,7 +410,12 @@ function(qt_internal_add_configure_time_executable target) OUTPUT_VARIABLE try_compile_output ) + set(QT_INTERNAL_CMAKE_FLAGS_CONFIGURE_TIME_TOOL_${target} + "${cmake_flags_arg}" CACHE INTERNAL "") + file(WRITE "${timestamp_file}" "") + set(QT_INTERNAL_HAVE_CONFIGURE_TIME_${target} ${result} CACHE INTERNAL + "Indicates that the configure-time target ${target} was built") if(NOT result) message(FATAL_ERROR "Unable to build ${target}: ${try_compile_output}") endif() @@ -480,7 +425,9 @@ function(qt_internal_add_configure_time_executable target) add_executable(${QT_CMAKE_EXPORT_NAMESPACE}::${target} ALIAS ${target}) set_target_properties(${target} PROPERTIES _qt_internal_configure_time_target TRUE - IMPORTED_LOCATION "${target_binary_path}") + _qt_internal_configure_time_target_build_location "${target_binary_path_relative}" + IMPORTED_LOCATION "${target_binary_path}" + ) if(NOT arg_NO_INSTALL) set_target_properties(${target} PROPERTIES diff --git a/cmake/QtFeature.cmake b/cmake/QtFeature.cmake index 199b844cf8..96cb308b2c 100644 --- a/cmake/QtFeature.cmake +++ b/cmake/QtFeature.cmake @@ -1,7 +1,6 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -include(QtFeatureCommon) include(CheckCXXCompilerFlag) function(qt_feature_module_begin) @@ -81,45 +80,25 @@ function(qt_evaluate_to_boolean expressionVar) endif() endfunction() -function(qt_evaluate_config_expression resultVar) +function(qt_internal_evaluate_config_expression resultVar outIdx startIdx) set(result "") - set(nestingLevel 0) - set(skipNext OFF) set(expression "${ARGN}") list(LENGTH expression length) + math(EXPR memberIdx "${startIdx} - 1") math(EXPR length "${length}-1") - foreach(memberIdx RANGE ${length}) - if(${skipNext}) - set(skipNext OFF) - continue() - endif() - + while(memberIdx LESS ${length}) + math(EXPR memberIdx "${memberIdx} + 1") list(GET expression ${memberIdx} member) if("${member}" STREQUAL "(") - if(${nestingLevel} GREATER 0) - list(APPEND result ${member}) - endif() - math(EXPR nestingLevel "${nestingLevel} + 1") - continue() + math(EXPR memberIdx "${memberIdx} + 1") + qt_internal_evaluate_config_expression(sub_result memberIdx ${memberIdx} ${expression}) + list(APPEND result ${sub_result}) elseif("${member}" STREQUAL ")") - math(EXPR nestingLevel "${nestingLevel} - 1") - if(nestingLevel LESS 0) - break() - endif() - if(${nestingLevel} EQUAL 0) - qt_evaluate_config_expression(result ${result}) - else() - list(APPEND result ${member}) - endif() - continue() - elseif(${nestingLevel} GREATER 0) - list(APPEND result ${member}) - continue() + break() elseif("${member}" STREQUAL "NOT") list(APPEND result ${member}) - continue() elseif("${member}" STREQUAL "AND") qt_evaluate_to_boolean(result) if(NOT ${result}) @@ -144,7 +123,7 @@ function(qt_evaluate_config_expression resultVar) set(lhs "${${lhs}}") math(EXPR rhsIndex "${memberIdx}+1") - set(skipNext ON) + set(memberIdx ${rhsIndex}) list(GET expression ${rhsIndex} rhs) # We can't pass through an empty string with double quotes through various @@ -164,7 +143,7 @@ function(qt_evaluate_config_expression resultVar) list(APPEND result ${member}) endif() - endforeach() + endwhile() # The 'TARGET Gui' case is handled by qt_evaluate_to_boolean, by passing those tokens verbatim # to if(). @@ -174,18 +153,48 @@ function(qt_evaluate_config_expression resultVar) qt_evaluate_to_boolean(result) endif() + # When in recursion, we must skip to the next closing parenthesis on nesting level 0. The outIdx + # must point to the matching closing parenthesis, and that's not the case if we're early exiting + # in AND/OR. + if(startIdx GREATER 0) + set(nestingLevel 1) + while(TRUE) + list(GET expression ${memberIdx} member) + if("${member}" STREQUAL ")") + math(EXPR nestingLevel "${nestingLevel} - 1") + if(nestingLevel EQUAL 0) + break() + endif() + elseif("${member}" STREQUAL "(") + math(EXPR nestingLevel "${nestingLevel} + 1") + endif() + math(EXPR memberIdx "${memberIdx} + 1") + endwhile() + endif() + + set(${outIdx} ${memberIdx} PARENT_SCOPE) set(${resultVar} ${result} PARENT_SCOPE) endfunction() -function(_qt_internal_dump_expression_values expression_dump expression) - set(dump "") - set(skipNext FALSE) - set(isTargetExpression FALSE) +function(qt_evaluate_config_expression resultVar) + qt_internal_evaluate_config_expression(result unused 0 ${ARGN}) + set("${resultVar}" "${result}" PARENT_SCOPE) +endfunction() +function(_qt_internal_get_feature_condition_keywords out_var) set(keywords "EQUAL" "LESS" "LESS_EQUAL" "GREATER" "GREATER_EQUAL" "STREQUAL" "STRLESS" "STRLESS_EQUAL" "STRGREATER" "STRGREATER_EQUAL" "VERSION_EQUAL" "VERSION_LESS" "VERSION_LESS_EQUAL" "VERSION_GREATER" "VERSION_GREATER_EQUAL" "MATCHES" "EXISTS" "COMMAND" "DEFINED" "NOT" "AND" "OR" "TARGET" "EXISTS" "IN_LIST" "(" ")") + set(${out_var} "${keywords}" PARENT_SCOPE) +endfunction() + +function(_qt_internal_dump_expression_values expression_dump expression) + set(dump "") + set(skipNext FALSE) + set(isTargetExpression FALSE) + + _qt_internal_get_feature_condition_keywords(keywords) list(LENGTH expression length) math(EXPR length "${length}-1") @@ -239,19 +248,44 @@ endfunction() # ${computed} is also stored when reconfiguring and the condition does not align with the user # provided value. # -function(qt_feature_check_and_save_user_provided_value resultVar feature condition computed label) +function(qt_feature_check_and_save_user_provided_value + resultVar feature condition condition_expression computed label) if (DEFINED "FEATURE_${feature}") # Revisit new user provided value set(user_value "${FEATURE_${feature}}") - string(TOUPPER "${user_value}" result) + string(TOUPPER "${user_value}" user_value_upper) + set(result "${user_value_upper}") - # If the build is marked as dirty and the user_value doesn't meet the new condition, - # reset it to the computed one. + # If ${feature} depends on another dirty feature, reset the ${feature} value to + # ${computed}. get_property(dirty_build GLOBAL PROPERTY _qt_dirty_build) - if(NOT condition AND result AND dirty_build) - set(result "${computed}") - message(WARNING "Reset FEATURE_${feature} value to ${result}, because it doesn't \ -meet its condition after reconfiguration.") + if(dirty_build) + _qt_internal_feature_compute_feature_dependencies(deps "${feature}") + if(deps) + get_property(dirty_features GLOBAL PROPERTY _qt_dirty_features) + foreach(dirty_feature ${dirty_features}) + if(dirty_feature IN_LIST deps AND NOT "${result}" STREQUAL "${computed}") + set(result "${computed}") + message(WARNING + "Auto-resetting 'FEATURE_${feature}' from '${user_value_upper}' to " + "'${computed}', " + "because the dependent feature '${dirty_feature}' was marked dirty.") + + # Append ${feature} as a new dirty feature. + set_property(GLOBAL APPEND PROPERTY _qt_dirty_features "${feature}") + break() + endif() + endforeach() + endif() + + # If the build is marked as dirty and the feature doesn't meet its condition, + # reset its value to the computed one, which is likely OFF. + if(NOT condition AND result) + set(result "${computed}") + message(WARNING "Resetting 'FEATURE_${feature}' from '${user_value_upper}' to " + "'${computed}' because it doesn't meet its condition after reconfiguration. " + "Condition expression is: '${condition_expression}'") + endif() endif() set(bool_values OFF NO FALSE N ON YES TRUE Y) @@ -299,13 +333,21 @@ condition:\n ${conditionString}\nCondition values dump:\n ${conditionDump} set(QT_KNOWN_FEATURES "${QT_KNOWN_FEATURES}" CACHE INTERNAL "" FORCE) endmacro() +macro(_qt_internal_parse_feature_definition feature) + cmake_parse_arguments(arg + "PRIVATE;PUBLIC" + "LABEL;PURPOSE;SECTION;" + "AUTODETECT;CONDITION;ENABLE;DISABLE;EMIT_IF" + ${_QT_FEATURE_DEFINITION_${feature}}) +endmacro() + # The build system stores 2 CMake cache variables for each feature, to allow detecting value changes # during subsequent reconfigurations. # # # `FEATURE_foo` stores the user provided feature value for the current configuration run. -# It can be set directly by the user, or derived from INPUT_foo (also set by the user). +# It can be set directly by the user. # # If a value is not provided on initial configuration, the value will be auto-computed based on the # various conditions of the feature. @@ -334,9 +376,7 @@ function(qt_evaluate_feature feature) message(FATAL_ERROR "Attempting to evaluate feature ${feature} but its definition is missing. Either the feature does not exist or a dependency to the module that defines it is missing") endif() - cmake_parse_arguments(arg - "PRIVATE;PUBLIC" - "LABEL;PURPOSE;SECTION;" "AUTODETECT;CONDITION;ENABLE;DISABLE;EMIT_IF" ${_QT_FEATURE_DEFINITION_${feature}}) + _qt_internal_parse_feature_definition("${feature}") if("${arg_ENABLE}" STREQUAL "") set(arg_ENABLE OFF) @@ -374,17 +414,6 @@ function(qt_evaluate_feature feature) qt_evaluate_config_expression(emit_if ${arg_EMIT_IF}) endif() - # If FEATURE_ is not defined trying to use INPUT_ variable to enable/disable feature. - if ((NOT DEFINED "FEATURE_${feature}") AND (DEFINED "INPUT_${feature}") - AND (NOT "${INPUT_${feature}}" STREQUAL "undefined") - AND (NOT "${INPUT_${feature}}" STREQUAL "")) - if(INPUT_${feature}) - set(FEATURE_${feature} ON) - else() - set(FEATURE_${feature} OFF) - endif() - endif() - # Warn about a feature which is not emitted, but the user explicitly provided a value for it. if(NOT emit_if AND DEFINED FEATURE_${feature}) set(msg "") @@ -401,7 +430,8 @@ function(qt_evaluate_feature feature) # Only save the user provided value if the feature was emitted. if(emit_if) qt_feature_check_and_save_user_provided_value( - saved_user_value "${feature}" "${condition}" "${computed}" "${arg_LABEL}") + saved_user_value + "${feature}" "${condition}" "${arg_CONDITION}" "${computed}" "${arg_LABEL}") else() # Make sure the feature internal value is OFF if not emitted. set(saved_user_value OFF) @@ -414,6 +444,60 @@ function(qt_evaluate_feature feature) set(QT_FEATURE_LABEL_${feature} "${arg_LABEL}" CACHE INTERNAL "") endfunction() +# Collect feature names that ${feature} depends on, by inspecting the given expression. +function(_qt_internal_feature_extract_feature_dependencies_from_expression out_var expression) + list(LENGTH expression length) + math(EXPR length "${length}-1") + + if(length LESS 0) + set(${out_var} "" PARENT_SCOPE) + return() + endif() + + set(deps "") + + foreach(memberIdx RANGE ${length}) + list(GET expression ${memberIdx} member) + if(member MATCHES "^QT_FEATURE_(.+)") + list(APPEND deps "${CMAKE_MATCH_1}") + endif() + endforeach() + set(${out_var} "${deps}" PARENT_SCOPE) +endfunction() + +# Collect feature names that ${feature} depends on, based on feature names that appear +# in the ${feature}'s condition expressions. +function(_qt_internal_feature_compute_feature_dependencies out_var feature) + # Only compute the deps once per feature. + get_property(deps_computed GLOBAL PROPERTY _qt_feature_deps_computed_${feature}) + if(deps_computed) + get_property(deps GLOBAL PROPERTY _qt_feature_deps_${feature}) + set(${out_var} "${deps}" PARENT_SCOPE) + return() + endif() + + _qt_internal_parse_feature_definition("${feature}") + + set(options_to_check AUTODETECT CONDITION ENABLE DISABLE EMIT_IF) + set(deps "") + + # Go through each option that takes condition expressions and collect the feature names. + foreach(option ${options_to_check}) + set(option_value "${arg_${option}}") + if(option_value) + _qt_internal_feature_extract_feature_dependencies_from_expression( + option_deps "${option_value}") + if(option_deps) + list(APPEND deps ${option_deps}) + endif() + endif() + endforeach() + + set_property(GLOBAL PROPERTY _qt_feature_deps_computed_${feature} TRUE) + set_property(GLOBAL PROPERTY _qt_feature_deps_${feature} "${deps}") + set(${out_var} "${deps}" PARENT_SCOPE) +endfunction() + function(qt_feature_config feature config_var_name) qt_feature_normalize_name("${feature}" feature) cmake_parse_arguments(PARSE_ARGV 2 arg @@ -786,6 +870,40 @@ function(qt_feature_copy_global_config_features_to_core target) endif() endfunction() +function(qt_internal_detect_dirty_features) + # We need to clean up QT_FEATURE_*, but only once per configuration cycle + get_property(qt_feature_clean GLOBAL PROPERTY _qt_feature_clean) + if(NOT qt_feature_clean AND NOT QT_NO_FEATURE_AUTO_RESET) + message(STATUS "Checking for feature set changes") + set_property(GLOBAL PROPERTY _qt_feature_clean TRUE) + foreach(feature ${QT_KNOWN_FEATURES}) + if(DEFINED "FEATURE_${feature}" AND + NOT "${QT_FEATURE_${feature}}" STREQUAL "${FEATURE_${feature}}") + message(" '${feature}' was changed from ${QT_FEATURE_${feature}} " + "to ${FEATURE_${feature}}") + set(dirty_build TRUE) + set_property(GLOBAL APPEND PROPERTY _qt_dirty_features "${feature}") + endif() + unset("QT_FEATURE_${feature}" CACHE) + endforeach() + + set(QT_KNOWN_FEATURES "" CACHE INTERNAL "" FORCE) + + if(dirty_build) + set_property(GLOBAL PROPERTY _qt_dirty_build TRUE) + message(WARNING + "Due to detected feature set changes, dependent features " + "will be re-computed automatically. This might cause a lot of files to be rebuilt. " + "To disable this behavior, configure with -DQT_NO_FEATURE_AUTO_RESET=ON") + endif() + endif() +endfunction() + +# Builds either a string of source code or a whole project to determine whether the build is +# successful. +# +# Sets a TEST_${name}_OUTPUT variable with the build output, to the scope of the calling function. +# Sets a TEST_${name} cache variable to either TRUE or FALSE if the build is successful or not. function(qt_config_compile_test name) if(DEFINED "TEST_${name}") return() @@ -908,8 +1026,11 @@ function(qt_config_compile_test name) get_filename_component(arg_PROJECT_PATH "${arg_PROJECT_PATH}" REALPATH) endif() - try_compile(HAVE_${name} "${CMAKE_BINARY_DIR}/config.tests/${name}" "${arg_PROJECT_PATH}" - "${name}" CMAKE_FLAGS ${flags} ${arg_CMAKE_FLAGS}) + try_compile( + HAVE_${name} "${CMAKE_BINARY_DIR}/config.tests/${name}" "${arg_PROJECT_PATH}" "${name}" + CMAKE_FLAGS ${flags} ${arg_CMAKE_FLAGS} + OUTPUT_VARIABLE try_compile_output + ) if(${HAVE_${name}}) set(status_label "Success") @@ -954,7 +1075,7 @@ function(qt_config_compile_test name) set(CMAKE_REQUIRED_FLAGS ${arg_COMPILE_OPTIONS}) # Pass -stdlib=libc++ on if necessary - if (INPUT_stdlib_libcpp OR QT_FEATURE_stdlib_libcpp) + if (QT_FEATURE_stdlib_libcpp) list(APPEND CMAKE_REQUIRED_FLAGS "-stdlib=libc++") endif() @@ -975,7 +1096,19 @@ function(qt_config_compile_test name) set(_save_CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}") set(CMAKE_REQUIRED_LIBRARIES "${arg_LIBRARIES}") - check_cxx_source_compiles("${arg_UNPARSED_ARGUMENTS} ${arg_CODE}" HAVE_${name}) + + # OUTPUT_VARIABLE is an internal undocumented variable of check_cxx_source_compiles + # since 3.23. Allow an opt out in case this breaks in the future. + set(try_compile_output "") + set(output_var "") + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.23" + AND NOT QT_INTERNAL_NO_TRY_COMPILE_OUTPUT_VARIABLE) + set(output_var OUTPUT_VARIABLE try_compile_output) + endif() + + check_cxx_source_compiles( + "${arg_UNPARSED_ARGUMENTS} ${arg_CODE}" HAVE_${name} ${output_var} + ) set(CMAKE_REQUIRED_LIBRARIES "${_save_CMAKE_REQUIRED_LIBRARIES}") set(CMAKE_C_STANDARD "${_save_CMAKE_C_STANDARD}") @@ -987,6 +1120,7 @@ function(qt_config_compile_test name) endif() endif() + set(TEST_${name}_OUTPUT "${try_compile_output}" PARENT_SCOPE) set(TEST_${name} "${HAVE_${name}}" CACHE INTERNAL "${arg_LABEL}") endfunction() @@ -1024,7 +1158,7 @@ function(qt_get_platform_try_compile_vars out_var) list(APPEND flags "CMAKE_CXX_STANDARD_REQUIRED") # Pass -stdlib=libc++ on if necessary - if (INPUT_stdlib_libcpp OR QT_FEATURE_stdlib_libcpp) + if (QT_FEATURE_stdlib_libcpp) if(CMAKE_CXX_FLAGS) string(APPEND CMAKE_CXX_FLAGS " -stdlib=libc++") else() @@ -1058,8 +1192,8 @@ function(qt_get_platform_try_compile_vars out_var) if(UIKIT) # Specify the sysroot, but only if not doing a simulator_and_device build. # So keep the sysroot empty for simulator_and_device builds. - if(QT_UIKIT_SDK) - list(APPEND flags_cmd_line "-DCMAKE_OSX_SYSROOT:STRING=${QT_UIKIT_SDK}") + if(QT_APPLE_SDK) + list(APPEND flags_cmd_line "-DCMAKE_OSX_SYSROOT:STRING=${QT_APPLE_SDK}") endif() endif() if(QT_NO_USE_FIND_PACKAGE_SYSTEM_ENVIRONMENT_PATH) @@ -1225,7 +1359,16 @@ function(qt_config_linker_supports_flag_test name) endif() cmake_parse_arguments(arg "" "LABEL;FLAG" "" ${ARGN}) - set(flags "-Wl,${arg_FLAG}") + if(GCC OR CLANG) + set(flags "-Wl,--fatal-warnings,${arg_FLAG}") + elseif(MSVC) + set(flags "${arg_FLAG}") + else() + # We don't know how to pass linker options in a way that + # it reliably fails, so assume the detection failed. + set(TEST_${name} "0" CACHE INTERNAL "${label}") + return() + endif() # Pass the linker that the main project uses to the compile test. qt_internal_get_active_linker_flags(linker_flags) diff --git a/cmake/QtFindPackageHelpers.cmake b/cmake/QtFindPackageHelpers.cmake index 92fd0719ec..af6057bf51 100644 --- a/cmake/QtFindPackageHelpers.cmake +++ b/cmake/QtFindPackageHelpers.cmake @@ -190,6 +190,15 @@ macro(qt_find_package) PROPERTIES INTERFACE_QT_PACKAGE_VERSION ${ARGV1}) endif() + # Save the retrieved package version. + set(_qt_find_package_found_version "") + if(${ARGV0}_VERSION) + set(_qt_find_package_found_version "${${ARGV0}_VERSION}") + set_target_properties(${qt_find_package_target_name} + PROPERTIES + _qt_package_found_version "${_qt_find_package_found_version}") + endif() + if(arg_COMPONENTS) string(REPLACE ";" " " components_as_string "${arg_COMPONENTS}") set_property(TARGET ${qt_find_package_target_name} @@ -203,6 +212,18 @@ macro(qt_find_package) ${components_as_string}) endif() + # Work around: QTBUG-125371 + if(NOT "${ARGV0}" STREQUAL "Qt6") + # Record the package + component + optional component provided targets. + qt_internal_record_package_component_provided_targets( + PACKAGE_NAME "${ARGV0}" + ON_TARGET ${qt_find_package_target_name} + PROVIDED_TARGETS ${arg_PROVIDED_TARGETS} + COMPONENTS ${arg_COMPONENTS} + OPTIONAL_COMPONENTS ${arg_OPTIONAL_COMPONENTS} + ) + endif() + get_property(is_global TARGET ${qt_find_package_target_name} PROPERTY IMPORTED_GLOBAL) qt_internal_should_not_promote_package_target_to_global( @@ -228,6 +249,56 @@ macro(qt_find_package) endif() endmacro() +# Records information about a package's provided targets, given a specific list of components. +# +# A package might contain multiple components, and create only a subset of targets based on which +# components are looked for. +# This function computes a unique key / id using the package name and the components that are +# passed. +# Then it saves the id in a property on the ON_TARGET target. The ON_TARGET target is one +# of the provided targets for that package id. This allows us to create a relationship to find +# the package id, given a target. +# The function also appends the list of provided targets for that package id to a global property. +# This information will later be saved into the module Dependencies.cmake file. +function(qt_internal_record_package_component_provided_targets) + set(opt_args "") + set(single_args + PACKAGE_NAME + ON_TARGET + ) + set(multi_args + COMPONENTS + OPTIONAL_COMPONENTS + PROVIDED_TARGETS + ) + cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}") + _qt_internal_validate_all_args_are_parsed(arg) + + if(NOT arg_PACKAGE_NAME) + message(FATAL_ERROR "PACKAGE_NAME is required.") + endif() + + if(NOT arg_ON_TARGET) + message(FATAL_ERROR "ON_TARGET is required.") + endif() + + _qt_internal_get_package_components_id( + PACKAGE_NAME "${arg_PACKAGE_NAME}" + COMPONENTS ${arg_COMPONENTS} + OPTIONAL_COMPONENTS ${arg_OPTIONAL_COMPONENTS} + OUT_VAR_KEY package_key + ) + + set_target_properties(${arg_ON_TARGET} PROPERTIES + _qt_package_components_id "${package_key}" + ) + + _qt_internal_append_to_cmake_property_without_duplicates( + _qt_find_package_${package_key}_provided_targets + "${arg_PROVIDED_TARGETS}" + ) +endfunction() + # Save found packages in the cache. They will be read on next reconfiguration to skip looking # for packages that were not previously found. # Only applies to -developer-builds by default. @@ -515,8 +586,15 @@ function(qt_register_target_dependencies target public_libs private_libs) endif() foreach(lib IN LISTS lib_list) - if ("${lib}" MATCHES "^Qt::(.*)") + if("${lib}" MATCHES "^Qt::(.*)") + set(lib "${CMAKE_MATCH_1}") + elseif("${lib}" MATCHES "^${QT_CMAKE_EXPORT_NAMESPACE}::(.*)") set(lib "${CMAKE_MATCH_1}") + else() + set(lib "") + endif() + + if(lib) qt_internal_get_package_name_of_target("${lib}" package_name) qt_internal_get_package_version_of_target("${lib}" package_version) list(APPEND target_deps "${package_name}\;${package_version}") @@ -532,8 +610,16 @@ function(qt_register_target_dependencies target public_libs private_libs) # INTERFACE libraries. INTERFACE libraries in most cases will be FooPrivate libraries. if(target_is_shared AND private_libs) foreach(lib IN LISTS private_libs) - if ("${lib}" MATCHES "^Qt::(.*)") - set(lib_namespaced "${lib}") + set(lib_namespaced "${lib}") + if("${lib}" MATCHES "^Qt::(.*)") + set(lib "${CMAKE_MATCH_1}") + elseif("${lib}" MATCHES "^${QT_CMAKE_EXPORT_NAMESPACE}::(.*)") + set(lib "${CMAKE_MATCH_1}") + else() + set(lib "") + endif() + + if(lib) set(lib "${CMAKE_MATCH_1}") qt_internal_is_lib_part_of_qt6_package("${lib}" is_part_of_qt6) diff --git a/cmake/QtFindWrapConfigExtra.cmake.in b/cmake/QtFindWrapConfigExtra.cmake.in index 6ecf43512c..d1706d7cab 100644 --- a/cmake/QtFindWrapConfigExtra.cmake.in +++ b/cmake/QtFindWrapConfigExtra.cmake.in @@ -1 +1,4 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + @extra_cmake_code@ diff --git a/cmake/QtFindWrapHelper.cmake b/cmake/QtFindWrapHelper.cmake index a8d3da49d1..b17f2133bb 100644 --- a/cmake/QtFindWrapHelper.cmake +++ b/cmake/QtFindWrapHelper.cmake @@ -80,10 +80,39 @@ macro(qt_find_package_system_or_bundled _unique_prefix) INTERFACE_QT_3RD_PARTY_PACKAGE_TYPE "${${_unique_prefix}_qt_package_type}") + + # This might not be set, because qt_find_package() is called from a configure.cmake file via + # qt_feature_evaluate_features, which means the _VERSION var is confined to the function + # scope. + if(${${_unique_prefix}_qt_package_name_to_use}_VERSION) + set(_qfwrap_${_unique_prefix}_package_version + "${${${_unique_prefix}_qt_package_name_to_use}_VERSION}" + ) + else() + # We set this in qt_find_package, so try to retrieve it. + get_target_property(_qfwrap_${_unique_prefix}_package_version_from_prop + "${${_unique_prefix}_qt_package_target_to_use}" _qt_package_found_version) + if(_qfwrap_${_unique_prefix}_package_version_from_prop) + set(_qfwrap_${_unique_prefix}_package_version + "${_qfwrap_${_unique_prefix}_package_version_from_prop}" + ) + else() + set(_qfwrap_${_unique_prefix}_package_version "") + endif() + endif() + + if(_qfwrap_${_unique_prefix}_package_version) + set(_qfwrap_${_unique_prefix}_package_version_option + VERSION_VAR "_qfwrap_${_unique_prefix}_package_version" + ) + endif() + include(FindPackageHandleStandardArgs) find_package_handle_standard_args( Wrap${_qfwrap_${_unique_prefix}_FRIENDLY_PACKAGE_NAME} - DEFAULT_MSG ${_qfwrap_${_unique_prefix}_WRAP_PACKAGE_FOUND_VAR_NAME}) + REQUIRED_VARS ${_qfwrap_${_unique_prefix}_WRAP_PACKAGE_FOUND_VAR_NAME} + ${_qfwrap_${_unique_prefix}_package_version_option} + ) elseif(${_unique_prefix}_qt_package_type STREQUAL "bundled") message(FATAL_ERROR "Can't find ${${_unique_prefix}_qt_package_target_to_use}.") endif() diff --git a/cmake/QtFlagHandlingHelpers.cmake b/cmake/QtFlagHandlingHelpers.cmake index 3674abc94c..251a356df0 100644 --- a/cmake/QtFlagHandlingHelpers.cmake +++ b/cmake/QtFlagHandlingHelpers.cmake @@ -1,6 +1,23 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause +# Sets '${var}' to a genex that extracts the target's property. +# Sets 'have_${var}' to a genex that checks that the property has a +# non-empty value. +macro(qt_internal_genex_get_property var target property) + set(${var} "$<TARGET_PROPERTY:${target},${property}>") + set(have_${var} "$<BOOL:${${var}}>") +endmacro() + +# Sets '${var}' to a genex that will join the given property values +# using '${glue}' and will surround the entire output with '${prefix}' +# and '${suffix}'. +macro(qt_internal_genex_get_joined_property var target property prefix suffix glue) + qt_internal_genex_get_property("${var}" "${target}" "${property}") + set(${var} + "$<${have_${var}}:${prefix}$<JOIN:${${var}},${glue}>${suffix}>") +endmacro() + # This function generates LD version script for the target and uses it in the target linker line. # Function has two modes dependending on the specified arguments. # Arguments: @@ -23,7 +40,40 @@ function(qt_internal_add_linker_version_script target) endif() if(TEST_ld_version_script) - set(contents "Qt_${PROJECT_VERSION_MAJOR}_PRIVATE_API {\n qt_private_api_tag*;\n") + # Create a list of mangled symbol matches for all "std::" symbols. This + # list will catch most symbols, but will miss global-namespace symbols + # that only have std parameters. + # See https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangle.name for reference + set(contents "NonQt {\nlocal:") + + # For types: vtable, VTT, typeinfo, typeinfo name + foreach(ptrqualifier "" "P" "PK") # T, T *, const T * (volatile ignored) + string(APPEND contents "\n _ZT[VTIS]${ptrqualifier}S*;" + "_ZT[VTIS]${ptrqualifier}NS*;") + endforeach() + + # For functions and variables + foreach(special "" + "G[VR]" # guard variables, extended-lifetime references + "GTt") # transaction entry points + foreach(cvqualifier "" "[VK]" "VK") # plain, const|volatile, const volatile + string(APPEND contents "\n ") + foreach(refqualifier "" "[RO]") # plain, & or && + # For names in the std:: namespace, compression applies + # (https://itanium-cxx-abi.github.io/cxx-abi/abi.html#mangling-compression) + string(APPEND contents + " _Z${special}${cvqualifier}${refqualifier}S*;" # plain + " _Z${special}N${cvqualifier}${refqualifier}S*;" # nested name + ) + endforeach() + endforeach() + endforeach() + + string(APPEND contents "\n};\nQt_${PROJECT_VERSION_MAJOR}") + if(QT_FEATURE_elf_private_full_version) + string(APPEND contents ".${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") + endif() + string(APPEND contents "_PRIVATE_API { qt_private_api_tag*;\n") if(arg_PRIVATE_HEADERS) foreach(ph ${arg_PRIVATE_HEADERS}) string(APPEND contents " @FILE:${ph}@\n") @@ -33,9 +83,21 @@ function(qt_internal_add_linker_version_script target) endif() string(APPEND contents "};\n") set(current "Qt_${PROJECT_VERSION_MAJOR}") - string(APPEND contents "${current} { *; };\n") + string(APPEND contents "${current} {\n *;") + + get_target_property(target_type ${target} TYPE) + if(NOT target_type STREQUAL "INTERFACE_LIBRARY") + set(genex_prefix "\n ") + set(genex_glue "$<SEMICOLON>\n ") + set(genex_suffix "$<SEMICOLON>") + qt_internal_genex_get_joined_property( + linker_exports "${target}" _qt_extra_linker_script_exports + "${genex_prefix}" "${genex_suffix}" "${genex_glue}" + ) + string(APPEND contents "${linker_exports}") + endif() + string(APPEND contents "\n};\n") - get_target_property(type ${target} TYPE) if(NOT target_type STREQUAL "INTERFACE_LIBRARY") set(property_genex "$<TARGET_PROPERTY:${target},_qt_extra_linker_script_content>") set(check_genex "$<BOOL:${property_genex}>") @@ -80,6 +142,17 @@ function(qt_internal_add_link_flags_no_undefined target) if (NOT QT_BUILD_SHARED_LIBS OR WASM) return() endif() + if (VXWORKS) + # VxWorks requires thread_local-related symbols to be found at + # runtime, resulting in linker error when no-undefined flag is + # set and thread_local is used + return() + endif() + if(CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang") + # ld64 defaults to -undefined,error, and in Xcode 15 + # passing this option is deprecated, causing a warning. + return() + endif() if ((GCC OR CLANG) AND NOT MSVC) if(CLANG AND QT_FEATURE_sanitizer) return() @@ -117,11 +190,20 @@ endfunction() function(qt_internal_apply_gc_binaries target visibility) set(possible_visibilities PRIVATE INTERFACE PUBLIC) - list(FIND possible_visibilities "${visibility}" known_visibility) - if (known_visibility EQUAL "-1") + if(NOT visibility IN_LIST possible_visibilities) message(FATAL_ERROR "Visibitily setting must be one of PRIVATE, INTERFACE or PUBLIC.") endif() + string(JOIN "" clang_or_gcc_begin + "$<$<OR:" + "$<CXX_COMPILER_ID:GNU>," + "$<CXX_COMPILER_ID:Clang>," + "$<CXX_COMPILER_ID:AppleClang>," + "$<CXX_COMPILER_ID:IntelLLVM>" + ">:" + ) + set(clang_or_gcc_end ">") + if ((GCC OR CLANG) AND NOT WASM AND NOT UIKIT AND NOT MSVC) if(APPLE) set(gc_sections_flag "-Wl,-dead_strip") @@ -130,16 +212,26 @@ function(qt_internal_apply_gc_binaries target visibility) elseif(LINUX OR BSD OR WIN32 OR ANDROID) set(gc_sections_flag "-Wl,--gc-sections") endif() + + # Save the flag value with and without genex wrapping, so we can remove the wrapping + # when generating .pc pkgconfig files. + set_property(GLOBAL PROPERTY _qt_internal_gc_sections_without_genex "${gc_sections_flag}") + + set(gc_sections_flag + "${clang_or_gcc_begin}${gc_sections_flag}${clang_or_gcc_end}") + + set_property(GLOBAL PROPERTY _qt_internal_gc_sections_with_genex "${gc_sections_flag}") endif() if(gc_sections_flag) target_link_options("${target}" ${visibility} "${gc_sections_flag}") endif() if((GCC OR CLANG) AND NOT WASM AND NOT UIKIT AND NOT MSVC) - set(split_sections_flags "-ffunction-sections" "-fdata-sections") + set(split_sections_flags + "${clang_or_gcc_begin}-ffunction-sections;-fdata-sections${clang_or_gcc_end}") endif() if(split_sections_flags) - target_compile_options("${target}" ${visibility} ${split_sections_flags}) + target_compile_options("${target}" ${visibility} "${split_sections_flags}") endif() endfunction() @@ -149,42 +241,23 @@ function(qt_internal_apply_intel_cet target visibility) endif() set(possible_visibilities PRIVATE INTERFACE PUBLIC) - list(FIND possible_visibilities "${visibility}" known_visibility) - if (known_visibility EQUAL "-1") + if(NOT visibility IN_LIST possible_visibilities) message(FATAL_ERROR "Visibitily setting must be one of PRIVATE, INTERFACE or PUBLIC.") endif() if(GCC) - set(flags "-mshstk") + string(JOIN "" flags + "$<$<OR:" + "$<CXX_COMPILER_ID:GNU>," + "$<CXX_COMPILER_ID:Clang>," + "$<CXX_COMPILER_ID:AppleClang>" + ">:-mshstk>") endif() if(flags) target_compile_options("${target}" ${visibility} "${flags}") endif() endfunction() -function(qt_internal_library_deprecation_level result) - # QT_DISABLE_DEPRECATED_UP_TO controls which version we use as a cut-off - # compiling in to the library. E.g. if it is set to QT_VERSION then no - # code which was deprecated before QT_VERSION will be compiled in. - if (NOT DEFINED QT_DISABLE_DEPRECATED_UP_TO) - if(WIN32) - # On Windows, due to the way DLLs work, we need to export all functions, - # including the inlines - list(APPEND deprecations "QT_DISABLE_DEPRECATED_UP_TO=0x040800") - else() - # On other platforms, Qt's own compilation does need to compile the Qt 5.0 API - list(APPEND deprecations "QT_DISABLE_DEPRECATED_UP_TO=0x050000") - endif() - else() - list(APPEND deprecations "QT_DISABLE_DEPRECATED_UP_TO=${QT_DISABLE_DEPRECATED_UP_TO}") - endif() - # QT_WARN_DEPRECATED_UP_TO controls the upper-bound of deprecation - # warnings that are emitted. E.g. if it is set to 0x060500 then all use of - # things deprecated in or before 6.5.0 will be warned against. - list(APPEND deprecations "QT_WARN_DEPRECATED_UP_TO=0x070000") - set("${result}" "${deprecations}" PARENT_SCOPE) -endfunction() - # Sets the exceptions flags for the given target according to exceptions_on function(qt_internal_set_exceptions_flags target exceptions_on) set(_defs "") @@ -193,21 +266,21 @@ function(qt_internal_set_exceptions_flags target exceptions_on) if(MSVC) set(_flag "/EHsc") if((MSVC_VERSION GREATER_EQUAL 1929) AND NOT CLANG) + # Use the undocumented compiler flag to make our binary smaller on x64. + # https://devblogs.microsoft.com/cppblog/making-cpp-exception-handling-smaller-x64/ + # NOTE: It seems we'll use this new exception handling model unconditionally without + # this hack since some unknown MSVC version. set(_flag ${_flag} "/d2FH4") endif() + else() + set(_flag "-fexceptions") endif() else() set(_defs "QT_NO_EXCEPTIONS") - if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") + if(MSVC) set(_flag "/EHs-c-" "/wd4530" "/wd4577") - elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU|AppleClang|InteLLLVM") + else() set(_flag "-fno-exceptions") - elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - if (MSVC) - set(_flag "/EHs-c-" "/wd4530" "/wd4577") - else() - set(_flag "-fno-exceptions") - endif() endif() endif() @@ -308,7 +381,7 @@ function(qt_internal_enable_unicode_defines) set(no_unicode_condition "$<NOT:$<BOOL:$<TARGET_PROPERTY:QT_NO_UNICODE_DEFINES>>>") target_compile_definitions(Platform - INTERFACE "$<${no_unicode_condition}:UNICODE;_UNICODE>") + INTERFACE "$<${no_unicode_condition}:UNICODE$<SEMICOLON>_UNICODE>") endif() endfunction() @@ -993,6 +1066,26 @@ function(qt_internal_set_up_config_optimizations_like_in_qmake) IN_CACHE) endif() + # Legacy Android toolchain file adds the `-g` flag to CMAKE_<LANG>_FLAGS, as a + # result, our release build ends up containing debug symbols. To avoid that, we + # remove the flag from CMAKE_<LANGL>_FLAGS and add + # it to CMAKE_<LANG>_FLAGS_DEBUG. + # + # Note: + # The new `android.toolchain.cmake` file does not have this problem, but + # it has other issues, eg., https://github.com/android/ndk/issues/1693, so we + # cannot force it. While we do load the new toolchain, it automatically falls + # back to the legacy toolchain, ie., `android-legacy.toolchain.cmake` which + # has the problem described above. + # + # Todo: + # When the new toolchain is fixed, and it doesn't fall back to the legacy + # anymore by default, then we should be able to remove this workaround. + if(ANDROID AND ANDROID_COMPILER_FLAGS MATCHES "(^| )-g") + qt_internal_remove_compiler_flags("-g") + qt_internal_add_compiler_flags(FLAGS "-g" CONFIGS DEBUG RELWITHDEBINFO) + endif() + # Update all relevant flags in the calling scope foreach(lang ${enabled_languages}) set(flag_var_name "CMAKE_${lang}_FLAGS") diff --git a/cmake/QtFrameworkHelpers.cmake b/cmake/QtFrameworkHelpers.cmake index 6d67bc4a11..0a37573ff6 100644 --- a/cmake/QtFrameworkHelpers.cmake +++ b/cmake/QtFrameworkHelpers.cmake @@ -37,6 +37,7 @@ macro(qt_find_apple_system_frameworks) qt_internal_find_apple_system_framework(FWContacts Contacts) qt_internal_find_apple_system_framework(FWEventKit EventKit) qt_internal_find_apple_system_framework(FWHealthKit HealthKit) + qt_internal_find_apple_system_framework(FWUniformTypeIdentifiers UniformTypeIdentifiers) endif() endmacro() @@ -72,7 +73,7 @@ function(qt_copy_framework_headers target) set(options) set(oneValueArgs) - set(multiValueArgs PUBLIC PRIVATE QPA RHI) + set(multiValueArgs PUBLIC PRIVATE QPA RHI SSG) cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) qt_internal_get_framework_info(fw ${target}) @@ -81,27 +82,81 @@ function(qt_copy_framework_headers target) set(output_dir_PRIVATE "${output_dir}/${fw_private_module_header_dir}/private") set(output_dir_QPA "${output_dir}/${fw_private_module_header_dir}/qpa") set(output_dir_RHI "${output_dir}/${fw_private_module_header_dir}/rhi") + set(output_dir_SSG "${output_dir}/${fw_private_module_header_dir}/ssg") + qt_internal_module_info(module "${target}") - set(out_files) - foreach(type IN ITEMS PUBLIC PRIVATE QPA RHI) + set(out_files "") + set(in_files "") + set(out_dirs "") + set(copy_commands "") + foreach(type IN ITEMS PUBLIC PRIVATE QPA RHI SSG) + set(in_files_${type} "") set(fw_output_header_dir "${output_dir_${type}}") + list(APPEND out_dirs "${fw_output_header_dir}") foreach(hdr IN LISTS arg_${type}) get_filename_component(in_file_path ${hdr} ABSOLUTE) get_filename_component(in_file_name ${hdr} NAME) set(out_file_path "${fw_output_header_dir}/${in_file_name}") - add_custom_command( - OUTPUT ${out_file_path} - DEPENDS ${in_file_path} - COMMAND ${CMAKE_COMMAND} -E make_directory "${fw_output_header_dir}" - COMMAND ${CMAKE_COMMAND} -E copy "${in_file_path}" "${fw_output_header_dir}" - VERBATIM) list(APPEND out_files ${out_file_path}) + list(APPEND in_files_${type} "${in_file_path}") endforeach() + if(in_files_${type}) + list(APPEND copy_commands + COMMAND ${CMAKE_COMMAND} -E copy ${in_files_${type}} "${fw_output_header_dir}") + list(APPEND in_files ${in_files_${type}}) + endif() endforeach() - set_property(TARGET ${target} APPEND PROPERTY - QT_COPIED_FRAMEWORK_HEADERS "${out_files}") + list(REMOVE_DUPLICATES out_files) + list(REMOVE_DUPLICATES in_files) + + set(copy_fw_sync_headers_command + "${CMAKE_COMMAND}" -E copy_directory + "${module_build_interface_include_dir}/.syncqt_staging" + "${output_dir}/${fw_versioned_header_dir}" + ) + + set(copy_fw_sync_headers_marker_file + "${CMAKE_CURRENT_BINARY_DIR}/${target}_fw_sync_headers_marker_file" + ) + + set(copy_fw_sync_headers_marker_file_command + "${CMAKE_COMMAND}" -E touch "${copy_fw_sync_headers_marker_file}" + ) + + if(CMAKE_GENERATOR MATCHES "^Ninja") + add_custom_command( + OUTPUT + "${output_dir}/${fw_versioned_header_dir}" + "${copy_fw_sync_headers_marker_file}" + DEPENDS ${target}_sync_headers + COMMAND ${copy_fw_sync_headers_command} + COMMAND ${copy_fw_sync_headers_marker_file_command} + VERBATIM + ) + add_custom_target(${target}_copy_fw_sync_headers + DEPENDS "${output_dir}/${fw_versioned_header_dir}") + else() + add_custom_target(${target}_copy_fw_sync_headers + COMMAND ${copy_fw_sync_headers_command} + COMMAND ${copy_fw_sync_headers_marker_file_command} + ) + endif() + + if(out_files) + add_custom_command( + OUTPUT ${out_files} + DEPENDS ${target}_copy_fw_sync_headers ${in_files} + COMMAND + ${CMAKE_COMMAND} -E make_directory ${out_dirs} + ${copy_commands} + VERBATIM + COMMENT "Copy the ${target} header files to the framework directory" + ) + set_property(TARGET ${target} APPEND PROPERTY + QT_COPIED_FRAMEWORK_HEADERS "${out_files}") + endif() endfunction() function(qt_internal_generate_fake_framework_header target) @@ -143,6 +198,8 @@ endfunction() # <out_var>_version framework version, e.g. 'A', 'B' etc. # <out_var>_bundle_version framework bundle version, same as the PROJECT_VERSION, e.g. '6.0.0'. # <out_var>_header_dir top-level header directory, e.g. 'QtCore.framework/Headers'. +# <out_var>_versioned_binary_dir versioned directory that contains the framework binary, +# e.g. 'QtCore.framework/Versions/A' # <out_var>_versioned_header_dir header directory for specific framework version, # e.g. 'QtCore.framework/Versions/A/Headers' # <out_var>_private_header_dir header directory for the specific framework version and @@ -151,6 +208,12 @@ endfunction() # version, framework bundle version and tailing module name, e.g. # 'QtCore.framework/Versions/A/Headers/6.0.0/Core' function(qt_internal_get_framework_info out_var target) + # Avoid "INTERFACE_LIBRARY targets may only have whitelisted properties" error on CMake < 3.17. + get_target_property(target_type ${target} TYPE) + if("${target_type}" STREQUAL "INTERFACE_LIBRARY") + return() + endif() + get_target_property(${out_var}_version ${target} FRAMEWORK_VERSION) get_target_property(${out_var}_bundle_version ${target} MACOSX_FRAMEWORK_BUNDLE_VERSION) @@ -166,11 +229,14 @@ function(qt_internal_get_framework_info out_var target) set(${out_var}_name "${module}") set(${out_var}_dir "${${out_var}_name}.framework") set(${out_var}_header_dir "${${out_var}_dir}/Headers") + if(UIKIT) - # iOS frameworks do not version their headers + # iOS frameworks do not have a Versions sub-directory + set(${out_var}_versioned_binary_dir "${${out_var}_dir}") set(${out_var}_versioned_header_dir "${${out_var}_header_dir}") else() - set(${out_var}_versioned_header_dir "${${out_var}_dir}/Versions/${${out_var}_version}/Headers") + set(${out_var}_versioned_binary_dir "${${out_var}_dir}/Versions/${${out_var}_version}") + set(${out_var}_versioned_header_dir "${${out_var}_versioned_binary_dir}/Headers") endif() set(${out_var}_private_header_dir "${${out_var}_versioned_header_dir}/${${out_var}_bundle_version}") set(${out_var}_private_module_header_dir "${${out_var}_private_header_dir}/${module}") @@ -180,6 +246,7 @@ function(qt_internal_get_framework_info out_var target) set(${out_var}_header_dir "${${out_var}_header_dir}" PARENT_SCOPE) set(${out_var}_version "${${out_var}_version}" PARENT_SCOPE) set(${out_var}_bundle_version "${${out_var}_bundle_version}" PARENT_SCOPE) + set(${out_var}_versioned_binary_dir "${${out_var}_versioned_binary_dir}" PARENT_SCOPE) set(${out_var}_versioned_header_dir "${${out_var}_versioned_header_dir}" PARENT_SCOPE) set(${out_var}_private_header_dir "${${out_var}_private_header_dir}" PARENT_SCOPE) set(${out_var}_private_module_header_dir "${${out_var}_private_module_header_dir}" PARENT_SCOPE) diff --git a/cmake/QtHeadersClean.cmake b/cmake/QtHeadersClean.cmake index 98e3d0fa47..a1ebfcef28 100644 --- a/cmake/QtHeadersClean.cmake +++ b/cmake/QtHeadersClean.cmake @@ -5,6 +5,10 @@ # ${module_headers} with a custom set of defines. This makes sure our public headers # are self-contained, and also compile with more strict compiler options. function(qt_internal_add_headersclean_target module_target module_headers) + if(INPUT_headersclean AND WASM) + message(FATAL_ERROR "The headersclean targets are not supported on WASM platform.") + endif() + get_target_property(no_headersclean_check ${module_target} _qt_no_headersclean_check) if(no_headersclean_check) return() @@ -23,6 +27,7 @@ function(qt_internal_add_headersclean_target module_target module_headers) -DQT_NO_CAST_FROM_ASCII -DQT_NO_URL_CAST_FROM_STRING -DQT_NO_CAST_FROM_BYTEARRAY + -DQT_NO_CONTEXTLESS_CONNECT -DQT_NO_KEYWORDS -DQT_TYPESAFE_FLAGS -DQT_USE_QSTRINGBUILDER @@ -100,42 +105,35 @@ function(qt_internal_add_headersclean_target module_target module_headers) if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang|IntelLLVM") - # Turn on some extra warnings not found in -Wall -Wextra. - set(hcleanFLAGS -Wall -Wextra -Werror -Woverloaded-virtual -Wshadow -Wundef -Wfloat-equal - -Wnon-virtual-dtor -Wpointer-arith -Wformat-security -Wno-long-long -Wno-variadic-macros - -fno-operator-names - -pedantic-errors) + # Compile header in strict C++20 mode. Enable further warnings. + set(hcleanFLAGS -std=c++2a + -Wall -Wextra -Werror -pedantic-errors + -Woverloaded-virtual -Wshadow -Wundef -Wfloat-equal + -Wnon-virtual-dtor -Wpointer-arith -Wformat-security + -Wchar-subscripts -Wold-style-cast + -Wredundant-decls # QTBUG-115583 + -fno-operator-names) if(QT_FEATURE_reduce_relocations AND UNIX) list(APPEND hcleanFLAGS -fPIC) endif() - # options accepted by GCC and Clang - list(APPEND hcleanFLAGS -Wchar-subscripts -Wold-style-cast) - if (NOT ((TEST_architecture_arch STREQUAL arm) OR (TEST_architecture_arch STREQUAL mips))) list(APPEND hcleanFLAGS -Wcast-align) endif() if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - list(APPEND hcleanFLAGS -Wzero-as-null-pointer-constant) - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 4.5) - list(APPEND hcleanFLAGS -Wdouble-promotion) - endif() - if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 4.9) - list(APPEND hcleanFLAGS -Wfloat-conversion) - endif() + list(APPEND hcleanFLAGS -Wzero-as-null-pointer-constant + -Wdouble-promotion -Wfloat-conversion) endif() if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang|IntelLLVM") - list(APPEND hcleanFLAGS -Wshorten-64-to-32) + list(APPEND hcleanFLAGS -Wshorten-64-to-32 + -Wweak-vtables) endif() - # Use strict mode C++20, with no GNU extensions (see -pedantic-errors above). - list(APPEND hcleanFLAGS -std=c++2a) - separate_arguments(cxx_flags NATIVE_COMMAND ${CMAKE_CXX_FLAGS}) if(APPLE AND CMAKE_OSX_SYSROOT) @@ -151,10 +149,7 @@ function(qt_internal_add_headersclean_target module_target module_headers) # If additional package prefixes are provided, we consider they can contain frameworks # as well. foreach(prefix IN LISTS _qt_additional_packages_prefix_paths) - if(prefix MATCHES "/lib/cmake$") # Cut CMake files path - string(APPEND prefix "/../..") - endif() - get_filename_component(prefix "${prefix}" ABSOLUTE) + __qt_internal_reverse_prefix_path_from_cmake_dir(path "${path}") set(libdir "${prefix}/${INSTALL_LIBDIR}") if(EXISTS "${libdir}") @@ -182,7 +177,10 @@ function(qt_internal_add_headersclean_target module_target module_headers) ) set(input_header_path_type ABSOLUTE) elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - set(hcleanFLAGS -std:c++latest -Zc:__cplusplus -Za -WX -W3 -EHsc) + # Note we can't enable -Za, as it does not support certain key Microsoft SDK header files + # we use. Microsoft suggests to use /permissive- instead, which is implicity set by + # -std:c++latest. + set(hcleanFLAGS -std:c++latest -Zc:__cplusplus -WX -W4 -EHsc) # Because we now add `-DNOMINMAX` to `PlatformCommonInternal`. set(hcleanUDEFS -UNOMINMAX) diff --git a/cmake/QtHostInfoConfig.cmake.in b/cmake/QtHostInfoConfig.cmake.in index 7933b8b500..a1d069ec01 100644 --- a/cmake/QtHostInfoConfig.cmake.in +++ b/cmake/QtHostInfoConfig.cmake.in @@ -1,3 +1,6 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + @PACKAGE_INIT@ set(@var_prefix@BINDIR "@INSTALL_BINDIR@") diff --git a/cmake/QtInstallPaths.cmake.in b/cmake/QtInstallPaths.cmake.in new file mode 100644 index 0000000000..977fffd0e4 --- /dev/null +++ b/cmake/QtInstallPaths.cmake.in @@ -0,0 +1,16 @@ +# install layout information, following what qmake -query provides +get_filename_component(QT@PROJECT_VERSION_MAJOR@_INSTALL_PREFIX + ${CMAKE_CURRENT_LIST_DIR}/../@QT_INVERSE_CONFIG_INSTALL_DIR@ ABSOLUTE) +set(QT@PROJECT_VERSION_MAJOR@_INSTALL_ARCHDATA "@INSTALL_ARCHDATADIR@") +set(QT@PROJECT_VERSION_MAJOR@_INSTALL_BINS "@INSTALL_BINDIR@") +set(QT@PROJECT_VERSION_MAJOR@_INSTALL_CONFIGURATION "@INSTALL_SYSCONFDIR@") +set(QT@PROJECT_VERSION_MAJOR@_INSTALL_DATA "@INSTALL_DATADIR@") +set(QT@PROJECT_VERSION_MAJOR@_INSTALL_DOCS "@INSTALL_DOCDIR@") +set(QT@PROJECT_VERSION_MAJOR@_INSTALL_EXAMPLES "@INSTALL_EXAMPLESDIR@") +set(QT@PROJECT_VERSION_MAJOR@_INSTALL_HEADERS "@INSTALL_INCLUDEDIR@") +set(QT@PROJECT_VERSION_MAJOR@_INSTALL_LIBS "@INSTALL_LIBDIR@") +set(QT@PROJECT_VERSION_MAJOR@_INSTALL_LIBEXECS "@INSTALL_LIBEXECDIR@") +set(QT@PROJECT_VERSION_MAJOR@_INSTALL_PLUGINS "@INSTALL_PLUGINSDIR@") +set(QT@PROJECT_VERSION_MAJOR@_INSTALL_QML "@INSTALL_QMLDIR@") +set(QT@PROJECT_VERSION_MAJOR@_INSTALL_TESTS "@INSTALL_TESTSDIR@") +set(QT@PROJECT_VERSION_MAJOR@_INSTALL_TRANSLATIONS "@INSTALL_TRANSLATIONSDIR@") diff --git a/cmake/QtInternalTargets.cmake b/cmake/QtInternalTargets.cmake index bc515ed56b..d7eadc1a73 100644 --- a/cmake/QtInternalTargets.cmake +++ b/cmake/QtInternalTargets.cmake @@ -102,7 +102,7 @@ function(qt_internal_add_global_definition definition) set(optional_args) set(single_value_args VALUE) set(multi_value_args SCOPE) - cmake_parse_arguments(args + cmake_parse_arguments(arg "${optional_args}" "${single_value_args}" "${multi_value_args}" @@ -156,10 +156,12 @@ qt_internal_add_target_aliases(PlatformToolInternal) target_link_libraries(PlatformToolInternal INTERFACE PlatformAppInternal) qt_internal_add_global_definition(QT_NO_JAVA_STYLE_ITERATORS) -qt_internal_add_global_definition(QT_NO_AS_CONST) +qt_internal_add_global_definition(QT_NO_QASCONST) qt_internal_add_global_definition(QT_NO_QEXCHANGE) qt_internal_add_global_definition(QT_NO_NARROWING_CONVERSIONS_IN_CONNECT) qt_internal_add_global_definition(QT_EXPLICIT_QFILE_CONSTRUCTION_FROM_PATH) +qt_internal_add_global_definition(QT_USE_QSTRINGBUILDER SCOPE PLUGIN TOOL MODULE) +qt_internal_add_global_definition(QT_NO_FOREACH) if(WARNINGS_ARE_ERRORS) qt_internal_set_warnings_are_errors_flags(PlatformModuleInternal INTERFACE) @@ -209,6 +211,14 @@ function(qt_internal_apply_bitcode_flags target) target_compile_options("${target}" INTERFACE ${bitcode_flags}) endfunction() +# Function guards linker options that are applicable for internal Qt targets only from propagating +# them to user projects. +function(qt_internal_platform_link_options target scope) + set(options ${ARGN}) + set(is_internal_target_genex "$<BOOL:$<TARGET_PROPERTY:_qt_is_internal_target>>") + target_link_options(${target} ${scope} "$<${is_internal_target_genex}:${options}>") +endfunction() + # Apple deprecated the entire OpenGL API in favor of Metal, which # we are aware of, so silence the deprecation warnings in code. # This does not apply to user-code, which will need to silence @@ -219,19 +229,29 @@ elseif(UIKIT) target_compile_definitions(PlatformCommonInternal INTERFACE GLES_SILENCE_DEPRECATION) endif() -if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang" - AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "14.0.0" -) - # Xcode 14's Clang will emit objc_msgSend stubs by default, which ld - # from earlier Xcode versions will fail to understand when linking - # against static libraries with these stubs. Disable the stubs explicitly, - # for as long as we do support Xcode < 14. - set(is_static_lib "$<STREQUAL:$<TARGET_PROPERTY:TYPE>,STATIC_LIBRARY>") - set(is_objc "$<COMPILE_LANGUAGE:OBJC,OBJCXX>") - set(is_static_and_objc "$<AND:${is_static_lib},${is_objc}>") - target_compile_options(PlatformCommonInternal INTERFACE - "$<${is_static_and_objc}:-fno-objc-msgsend-selector-stubs>" - ) +if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") + if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "14.0.0") + # Xcode 14's Clang will emit objc_msgSend stubs by default, which ld + # from earlier Xcode versions will fail to understand when linking + # against static libraries with these stubs. Disable the stubs explicitly, + # for as long as we do support Xcode < 14. + set(is_static_lib "$<STREQUAL:$<TARGET_PROPERTY:TYPE>,STATIC_LIBRARY>") + set(is_objc "$<COMPILE_LANGUAGE:OBJC,OBJCXX>") + set(is_static_and_objc "$<AND:${is_static_lib},${is_objc}>") + target_compile_options(PlatformCommonInternal INTERFACE + "$<${is_static_and_objc}:-fno-objc-msgsend-selector-stubs>" + ) + endif() + + # A bug in Xcode 15 adds duplicate flags to the linker. In addition, the + # `-warn_duplicate_libraries` is now enabled by default which may result + # in several 'duplicate libraries warning'. + # - https://gitlab.kitware.com/cmake/cmake/-/issues/25297 and + # - https://indiestack.com/2023/10/xcode-15-duplicate-library-linker-warnings/ + set(is_xcode15 "$<VERSION_GREATER_EQUAL:$<CXX_COMPILER_VERSION>,15>") + set(not_disabled "$<NOT:$<BOOL:$<TARGET_PROPERTY:QT_NO_DISABLE_WARN_DUPLICATE_LIBRARIES>>>") + target_link_options(PlatformCommonInternal INTERFACE + "$<$<AND:${not_disabled},${is_xcode15}>:LINKER:-no_warn_duplicate_libraries>") endif() if(MSVC) @@ -246,7 +266,7 @@ if(WASM AND QT_FEATURE_sse2) endif() # Taken from mkspecs/common/msvc-version.conf and mkspecs/common/msvc-desktop.conf -if (MSVC) +if (MSVC AND NOT CLANG) if (MSVC_VERSION GREATER_EQUAL 1799) target_compile_options(PlatformCommonInternal INTERFACE -FS @@ -257,20 +277,16 @@ if (MSVC) if (MSVC_VERSION GREATER_EQUAL 1899) target_compile_options(PlatformCommonInternal INTERFACE -Zc:strictStrings + -Zc:throwingNew ) - if (NOT CLANG) - target_compile_options(PlatformCommonInternal INTERFACE - -Zc:throwingNew - ) - endif() endif() - if (MSVC_VERSION GREATER_EQUAL 1909 AND NOT CLANG) # MSVC 2017 + if (MSVC_VERSION GREATER_EQUAL 1909) # MSVC 2017 target_compile_options(PlatformCommonInternal INTERFACE -Zc:referenceBinding -Zc:ternary ) endif() - if (MSVC_VERSION GREATER_EQUAL 1919 AND NOT CLANG) # MSVC 2019 + if (MSVC_VERSION GREATER_EQUAL 1919) # MSVC 2019 target_compile_options(PlatformCommonInternal INTERFACE -Zc:externConstexpr #-Zc:lambda # Buggy. TODO: Enable again when stable enough. @@ -287,7 +303,7 @@ if (MSVC) $<$<NOT:$<CONFIG:Debug>>:-guard:cf -Gw> ) - target_link_options(PlatformCommonInternal INTERFACE + qt_internal_platform_link_options(PlatformCommonInternal INTERFACE -DYNAMICBASE -NXCOMPAT -LARGEADDRESSAWARE $<$<NOT:$<CONFIG:Debug>>:-OPT:REF -OPT:ICF -GUARD:CF> ) @@ -301,18 +317,52 @@ if (GCC AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "9.2") target_compile_options(PlatformCommonInternal INTERFACE $<$<COMPILE_LANGUAGE:CXX>:-Wsuggest-override>) endif() +# Hardening options if(QT_FEATURE_intelcet) if(MSVC) - target_link_options(PlatformCommonInternal INTERFACE - -CETCOMPAT - ) + qt_internal_platform_link_options(PlatformCommonInternal INTERFACE -CETCOMPAT) else() - target_compile_options(PlatformCommonInternal INTERFACE - -fcf-protection=full - ) + target_compile_options(PlatformCommonInternal INTERFACE -fcf-protection=full) + endif() +endif() + +if(QT_FEATURE_glibc_fortify_source) + set(is_optimized_build "$<OR:$<NOT:$<CONFIG:Debug>>,$<BOOL:${QT_FEATURE_optimize_debug}>>") + # Some compilers may define _FORTIFY_SOURCE by default when optimizing, remove it + # before defining our own + target_compile_options(PlatformCommonInternal BEFORE INTERFACE "$<${is_optimized_build}:-U_FORTIFY_SOURCE>") + if(TEST_glibc_234) + target_compile_options(PlatformCommonInternal INTERFACE "$<${is_optimized_build}:-D_FORTIFY_SOURCE=3>") + else() + target_compile_options(PlatformCommonInternal INTERFACE "$<${is_optimized_build}:-D_FORTIFY_SOURCE=2>") endif() endif() +if(QT_FEATURE_trivial_auto_var_init_pattern) + target_compile_options(PlatformCommonInternal INTERFACE -ftrivial-auto-var-init=pattern) +endif() + +if(QT_FEATURE_stack_protector) + target_compile_options(PlatformCommonInternal INTERFACE -fstack-protector-strong) +endif() + +if(QT_FEATURE_stack_clash_protection) + target_compile_options(PlatformCommonInternal INTERFACE -fstack-clash-protection) +endif() + +if(QT_FEATURE_libstdcpp_assertions) + target_compile_definitions(PlatformCommonInternal INTERFACE _GLIBCXX_ASSERTIONS) +endif() + +if(QT_FEATURE_libcpp_hardening) + target_compile_definitions(PlatformCommonInternal INTERFACE -D_LIBCPP_HARDENING_MODE=$<IF:$<CONFIG:Debug>,_LIBCPP_HARDENING_MODE_EXTENSIVE,_LIBCPP_HARDENING_MODE_FAST>) +endif() + +if(QT_FEATURE_relro_now_linker) + qt_internal_platform_link_options(PlatformCommonInternal INTERFACE "-Wl,-z,relro,-z,now") +endif() + + if(QT_FEATURE_force_asserts) target_compile_definitions(PlatformCommonInternal INTERFACE QT_FORCE_ASSERTS) endif() @@ -332,24 +382,34 @@ endif() if(DEFINED QT_EXTRA_FRAMEWORKPATHS AND APPLE) list(TRANSFORM QT_EXTRA_FRAMEWORKPATHS PREPEND "-F" OUTPUT_VARIABLE __qt_fw_flags) target_compile_options(PlatformCommonInternal INTERFACE ${__qt_fw_flags}) - target_link_options(PlatformCommonInternal INTERFACE ${__qt_fw_flags}) + qt_internal_platform_link_options(PlatformCommonInternal INTERFACE ${__qt_fw_flags}) unset(__qt_fw_flags) endif() qt_internal_get_active_linker_flags(__qt_internal_active_linker_flags) if(__qt_internal_active_linker_flags) - target_link_options(PlatformCommonInternal INTERFACE "${__qt_internal_active_linker_flags}") + qt_internal_platform_link_options(PlatformCommonInternal INTERFACE + "${__qt_internal_active_linker_flags}") endif() unset(__qt_internal_active_linker_flags) if(QT_FEATURE_enable_gdb_index) - target_link_options(PlatformCommonInternal INTERFACE "-Wl,--gdb-index") + qt_internal_platform_link_options(PlatformCommonInternal INTERFACE "-Wl,--gdb-index") endif() if(QT_FEATURE_enable_new_dtags) - target_link_options(PlatformCommonInternal INTERFACE "-Wl,--enable-new-dtags") + qt_internal_platform_link_options(PlatformCommonInternal INTERFACE "-Wl,--enable-new-dtags") endif() +function(qt_internal_apply_coverage_flags) + if(QT_FEATURE_coverage_gcov) + target_compile_options(PlatformCommonInternal INTERFACE + "$<$<CONFIG:Debug>:--coverage>") + target_link_options(PlatformCommonInternal INTERFACE "$<$<CONFIG:Debug>:--coverage>") + endif() +endfunction() +qt_internal_apply_coverage_flags() + function(qt_get_implicit_sse2_genex_condition out_var) set(is_shared_lib "$<STREQUAL:$<TARGET_PROPERTY:TYPE>,SHARED_LIBRARY>") set(is_static_lib "$<STREQUAL:$<TARGET_PROPERTY:TYPE>,STATIC_LIBRARY>") diff --git a/cmake/QtJavaHelpers.cmake b/cmake/QtJavaHelpers.cmake index edf4f54b9a..ec9b611c5e 100644 --- a/cmake/QtJavaHelpers.cmake +++ b/cmake/QtJavaHelpers.cmake @@ -4,6 +4,10 @@ # This function can be used to compile java sources into a jar package. function(qt_internal_add_jar target) + set(options) + set(oneValueArgs OUTPUT_DIR) + set(multiValueArgs INCLUDE_JARS SOURCES) + cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(javac_target_version "${QT_ANDROID_JAVAC_TARGET}") if (NOT javac_target_version) @@ -15,7 +19,12 @@ function(qt_internal_add_jar target) set(javac_source_version "8") endif() - set(CMAKE_JAVA_COMPILE_FLAGS -source "${javac_source_version}" -target "${javac_target_version}" -Xlint:unchecked -bootclasspath "${QT_ANDROID_JAR}") + set(CMAKE_JAVA_COMPILE_FLAGS -source "${javac_source_version}" -target "${javac_target_version}" + -Xlint:unchecked,cast,divzero,fallthrough,overrides,path -classpath "${QT_ANDROID_JAR}") add_jar(${ARGV}) + foreach(f IN LISTS arg_SOURCES) + _qt_internal_expose_source_file_to_ide(${target} "${f}") + endforeach() + endfunction() diff --git a/cmake/QtMkspecHelpers.cmake b/cmake/QtMkspecHelpers.cmake new file mode 100644 index 0000000000..cd08daa2c4 --- /dev/null +++ b/cmake/QtMkspecHelpers.cmake @@ -0,0 +1,146 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +macro(qt_internal_set_mkspecs_dir) + # Find the path to mkspecs/, depending on whether we are building as part of a standard qtbuild, + # or a module against an already installed version of qt. + if(NOT QT_MKSPECS_DIR) + if("${QT_BUILD_INTERNALS_PATH}" STREQUAL "") + get_filename_component(QT_MKSPECS_DIR "${CMAKE_CURRENT_LIST_DIR}/../mkspecs" ABSOLUTE) + else() + # We can rely on QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX being set by + # QtBuildInternalsExtra.cmake. + get_filename_component( + QT_MKSPECS_DIR + "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}/${INSTALL_MKSPECSDIR}" ABSOLUTE) + endif() + set(QT_MKSPECS_DIR "${QT_MKSPECS_DIR}" CACHE INTERNAL "") + endif() +endmacro() + +macro(qt_internal_setup_platform_definitions_and_mkspec) + # Platform define path, etc. + if(WIN32) + set(QT_DEFAULT_PLATFORM_DEFINITIONS WIN32 _ENABLE_EXTENDED_ALIGNED_STORAGE) + if(QT_64BIT) + list(APPEND QT_DEFAULT_PLATFORM_DEFINITIONS WIN64 _WIN64) + endif() + + if(CLANG) + if(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "MSVC" OR MSVC) + set(QT_DEFAULT_MKSPEC win32-clang-msvc) + elseif(CMAKE_CXX_COMPILER_FRONTEND_VARIANT STREQUAL "GNU" OR MINGW) + set(QT_DEFAULT_MKSPEC win32-clang-g++) + endif() + elseif(MSVC) + if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") + set(QT_DEFAULT_MKSPEC win32-arm64-msvc) + else() + set(QT_DEFAULT_MKSPEC win32-msvc) + endif() + elseif(MINGW) + set(QT_DEFAULT_MKSPEC win32-g++) + list(APPEND QT_DEFAULT_PLATFORM_DEFINITIONS MINGW_HAS_SECURE_API=1) + endif() + elseif(LINUX) + if(GCC) + set(QT_DEFAULT_MKSPEC linux-g++) + elseif(CLANG) + set(QT_DEFAULT_MKSPEC linux-clang) + endif() + elseif(ANDROID) + if(GCC) + set(QT_DEFAULT_MKSPEC android-g++) + elseif(CLANG) + set(QT_DEFAULT_MKSPEC android-clang) + endif() + elseif(IOS) + set(QT_DEFAULT_MKSPEC macx-ios-clang) + elseif(APPLE) + set(QT_DEFAULT_MKSPEC macx-clang) + elseif(WASM) + if(WASM64) + set(QT_DEFAULT_MKSPEC wasm-emscripten-64) + else() + set(QT_DEFAULT_MKSPEC wasm-emscripten) + endif() + elseif(QNX) + # Certain POSIX defines are not set if we don't compile with -std=gnuXX + set(QT_ENABLE_CXX_EXTENSIONS ON) + + list(APPEND QT_DEFAULT_PLATFORM_DEFINITIONS _FORTIFY_SOURCE=2 _REENTRANT) + + set(compiler_aarch64le aarch64le) + set(compiler_armle-v7 armv7le) + set(compiler_x86-64 x86_64) + set(compiler_x86 x86) + foreach(arch aarch64le armle-v7 x86-64 x86) + if (CMAKE_CXX_COMPILER_TARGET MATCHES "${compiler_${arch}}$") + set(QT_DEFAULT_MKSPEC qnx-${arch}-qcc) + endif() + endforeach() + elseif(FREEBSD) + if(CLANG) + set(QT_DEFAULT_MKSPEC freebsd-clang) + elseif(GCC) + set(QT_DEFAULT_MKSPEC freebsd-g++) + endif() + elseif(NETBSD) + set(QT_DEFAULT_MKSPEC netbsd-g++) + elseif(OPENBSD) + set(QT_DEFAULT_MKSPEC openbsd-g++) + elseif(SOLARIS) + if(GCC) + if(QT_64BIT) + set(QT_DEFAULT_MKSPEC solaris-g++-64) + else() + set(QT_DEFAULT_MKSPEC solaris-g++) + endif() + else() + if(QT_64BIT) + set(QT_DEFAULT_MKSPEC solaris-cc-64) + else() + set(QT_DEFAULT_MKSPEC solaris-cc) + endif() + endif() + elseif(HURD) + set(QT_DEFAULT_MKSPEC hurd-g++) + endif() + + if(NOT QT_QMAKE_TARGET_MKSPEC) + set(QT_QMAKE_TARGET_MKSPEC "${QT_DEFAULT_MKSPEC}" CACHE STRING "QMake target mkspec") + endif() + + if(CMAKE_CROSSCOMPILING) + set(QT_QMAKE_HOST_MKSPEC "${QT${PROJECT_VERSION_MAJOR}_HOST_INFO_QMAKE_MKSPEC}") + else() + set(QT_QMAKE_HOST_MKSPEC "${QT_QMAKE_TARGET_MKSPEC}") + endif() + + if(NOT QT_QMAKE_TARGET_MKSPEC OR NOT EXISTS "${QT_MKSPECS_DIR}/${QT_QMAKE_TARGET_MKSPEC}") + if(NOT QT_QMAKE_TARGET_MKSPEC) + set(reason + "Platform is not detected. Please make sure your build environment is configured" + " properly or specify it manually using QT_QMAKE_TARGET_MKSPEC variable and one of" + " the known platforms.") + else() + set(reason "Unknown platform ${QT_QMAKE_TARGET_MKSPEC}") + endif() + + file(GLOB known_platforms + LIST_DIRECTORIES true + RELATIVE "${QT_MKSPECS_DIR}" + "${QT_MKSPECS_DIR}/*" + ) + list(JOIN known_platforms "\n " known_platforms) + message(FATAL_ERROR "${reason}\n" + "Known platforms:\n ${known_platforms}") + endif() + + if(NOT DEFINED QT_DEFAULT_PLATFORM_DEFINITIONS) + set(QT_DEFAULT_PLATFORM_DEFINITIONS "") + endif() + + set(QT_PLATFORM_DEFINITIONS ${QT_DEFAULT_PLATFORM_DEFINITIONS} + CACHE STRING "Qt platform specific pre-processor defines") +endmacro() diff --git a/cmake/QtModuleConfig.cmake.in b/cmake/QtModuleConfig.cmake.in index cdb79f4bbb..06a7daad71 100644 --- a/cmake/QtModuleConfig.cmake.in +++ b/cmake/QtModuleConfig.cmake.in @@ -1,3 +1,6 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + @PACKAGE_INIT@ cmake_minimum_required(VERSION @min_new_policy_version@...@max_new_policy_version@) @@ -29,9 +32,6 @@ if (NOT QT_NO_CREATE_TARGETS AND @INSTALL_CMAKE_NAMESPACE@@target@_FOUND) include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@AdditionalTargetInfo.cmake") include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@ExtraProperties.cmake" OPTIONAL) - if(NOT QT_NO_CREATE_VERSIONLESS_TARGETS) - include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@VersionlessTargets.cmake") - endif() # DEPRECATED # Provide old style variables for includes, compile definitions, etc. @@ -74,15 +74,25 @@ if (NOT QT_NO_CREATE_TARGETS AND @INSTALL_CMAKE_NAMESPACE@@target@_FOUND) ${_@QT_CMAKE_EXPORT_NAMESPACE@@target@_OWN_PRIVATE_INCLUDE_DIRS}) foreach(_module_dep ${_@QT_CMAKE_EXPORT_NAMESPACE@@target@_MODULE_DEPENDENCIES}) - list(APPEND @QT_CMAKE_EXPORT_NAMESPACE@@target@_INCLUDE_DIRS + if(_module_dep MATCHES ".+Private$") + set(_private_suffix "Private") + else() + set(_private_suffix "") + endif() + list(APPEND @QT_CMAKE_EXPORT_NAMESPACE@@target@${_private_suffix}_INCLUDE_DIRS ${@QT_CMAKE_EXPORT_NAMESPACE@${_module_dep}_INCLUDE_DIRS}) - list(APPEND @QT_CMAKE_EXPORT_NAMESPACE@@target@_PRIVATE_INCLUDE_DIRS + list(APPEND @QT_CMAKE_EXPORT_NAMESPACE@@target@${_private_suffix}_PRIVATE_INCLUDE_DIRS ${@QT_CMAKE_EXPORT_NAMESPACE@${_module_dep}_PRIVATE_INCLUDE_DIRS}) - list(APPEND @QT_CMAKE_EXPORT_NAMESPACE@@target@_DEFINITIONS + if(_private_suffix) + list(APPEND @QT_CMAKE_EXPORT_NAMESPACE@@target@_PRIVATE_INCLUDE_DIRS + ${@QT_CMAKE_EXPORT_NAMESPACE@${_module_dep}_PRIVATE_INCLUDE_DIRS}) + endif() + list(APPEND @QT_CMAKE_EXPORT_NAMESPACE@@target@${_private_suffix}_DEFINITIONS ${@QT_CMAKE_EXPORT_NAMESPACE@${_module_dep}_DEFINITIONS}) - list(APPEND @QT_CMAKE_EXPORT_NAMESPACE@@target@_COMPILE_DEFINITIONS + list(APPEND @QT_CMAKE_EXPORT_NAMESPACE@@target@${_private_suffix}_COMPILE_DEFINITIONS ${@QT_CMAKE_EXPORT_NAMESPACE@${_module_dep}_COMPILE_DEFINITIONS}) endforeach() + unset(_private_suffix) list(REMOVE_DUPLICATES @QT_CMAKE_EXPORT_NAMESPACE@@target@_INCLUDE_DIRS) list(REMOVE_DUPLICATES @QT_CMAKE_EXPORT_NAMESPACE@@target@_PRIVATE_INCLUDE_DIRS) @@ -118,6 +128,14 @@ if (TARGET @QT_CMAKE_EXPORT_NAMESPACE@::@target@) EXISTS "${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@BuildInternals.cmake") include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@BuildInternals.cmake") endif() + + if(NOT QT_NO_CREATE_VERSIONLESS_TARGETS) + if(CMAKE_VERSION VERSION_LESS 3.18 OR QT_USE_OLD_VERSION_LESS_TARGETS) + include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@VersionlessTargets.cmake") + else() + include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@VersionlessAliasTargets.cmake") + endif() + endif() else() set(@INSTALL_CMAKE_NAMESPACE@@target@_FOUND FALSE) diff --git a/cmake/QtModuleDependencies.cmake.in b/cmake/QtModuleDependencies.cmake.in index c6c68ceb49..4a82ad3bd3 100644 --- a/cmake/QtModuleDependencies.cmake.in +++ b/cmake/QtModuleDependencies.cmake.in @@ -1,3 +1,6 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # Make sure @INSTALL_CMAKE_NAMESPACE@ is found before anything else. set(@INSTALL_CMAKE_NAMESPACE@@target@_FOUND FALSE) @@ -16,10 +19,10 @@ set(${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED FALSE) if(NOT @INSTALL_CMAKE_NAMESPACE@_FOUND) find_dependency(@INSTALL_CMAKE_NAMESPACE@ @main_qt_package_version@ PATHS + ${QT_BUILD_CMAKE_PREFIX_PATH} "${CMAKE_CURRENT_LIST_DIR}/.." "${_qt_cmake_dir}" ${_qt_additional_packages_prefix_paths} - ${QT_EXAMPLES_CMAKE_PREFIX_PATH} ${__qt_use_no_default_path_for_qt_packages} ) endif() @@ -27,6 +30,7 @@ endif() # note: _third_party_deps example: "ICU\\;FALSE\\;1.0\\;i18n uc data;ZLIB\\;FALSE\\;\\;" set(__qt_@target@_third_party_deps "@third_party_deps@") +@third_party_deps_extra_info@ _qt_internal_find_third_party_dependencies("@target@" __qt_@target@_third_party_deps) # Find Qt tool package. diff --git a/cmake/QtModuleHeadersCheck.cmake b/cmake/QtModuleHeadersCheck.cmake index d241f5bb55..39053f3e10 100644 --- a/cmake/QtModuleHeadersCheck.cmake +++ b/cmake/QtModuleHeadersCheck.cmake @@ -11,7 +11,8 @@ if(EXISTS ${HEADER_CHECK_EXCEPTIONS}) file(READ ${HEADER_CHECK_EXCEPTIONS} header_check_exception_list) endif() -file(TO_CMAKE_PATH "${INPUT_HEADER_FILE}" header) +get_filename_component(header "${INPUT_HEADER_FILE}" REALPATH) +file(TO_CMAKE_PATH "${header}" header) foreach(exception IN LISTS header_check_exception_list) file(TO_CMAKE_PATH "${exception}" exception) if(exception STREQUAL header) diff --git a/cmake/QtModuleHelpers.cmake b/cmake/QtModuleHelpers.cmake index c368cebb2e..d7957fa4bc 100644 --- a/cmake/QtModuleHelpers.cmake +++ b/cmake/QtModuleHelpers.cmake @@ -16,8 +16,8 @@ macro(qt_internal_get_internal_add_module_keywords option_args single_args multi NO_ADDITIONAL_TARGET_INFO NO_GENERATE_METATYPES NO_HEADERSCLEAN_CHECK - GENERATE_CPP_EXPORTS - GENERATE_PRIVATE_CPP_EXPORTS + GENERATE_CPP_EXPORTS # deprecated + NO_GENERATE_CPP_EXPORTS NO_UNITY_BUILD ) set(${single_args} @@ -31,6 +31,7 @@ macro(qt_internal_get_internal_add_module_keywords option_args single_args multi PRIVATE_HEADER_FILTERS QPA_HEADER_FILTERS RHI_HEADER_FILTERS + SSG_HEADER_FILTERS HEADER_SYNC_SOURCE_DIRECTORY ${__default_target_info_args} ) @@ -38,7 +39,6 @@ macro(qt_internal_get_internal_add_module_keywords option_args single_args multi QMAKE_MODULE_CONFIG EXTRA_CMAKE_FILES EXTRA_CMAKE_INCLUDES - NO_PCH_SOURCES EXTERNAL_HEADERS POLICIES ${__default_private_args} @@ -120,6 +120,10 @@ endfunction() # The regular expressions that filter RHI header files out of target sources. # The value must use the following format 'regex1|regex2|regex3'. # +# SSG_HEADER_FILTERS +# The regular expressions that filter ssg header files out of target sources. +# The value must use the following format 'regex1|regex2|regex3'. +# # HEADER_SYNC_SOURCE_DIRECTORY # The source directory for header sync procedure. Header files outside this directory will be # ignored by syncqt. The specifying this directory allows to skip the parsing of the whole @@ -419,18 +423,15 @@ function(qt_internal_add_module target) # We should not generate export headers if module is defined as pure STATIC. # Static libraries don't need to export their symbols, and corner cases when sources are # also used in shared libraries, should be handled manually. - if(arg_GENERATE_CPP_EXPORTS AND NOT arg_STATIC) + if((NOT arg_NO_GENERATE_CPP_EXPORTS OR arg_GENERATE_CPP_EXPORTS) AND NOT arg_STATIC + AND NOT arg_HEADER_MODULE) if(arg_CPP_EXPORT_HEADER_BASE_NAME) set(cpp_export_header_base_name "CPP_EXPORT_HEADER_BASE_NAME;${arg_CPP_EXPORT_HEADER_BASE_NAME}" ) endif() - if(arg_GENERATE_PRIVATE_CPP_EXPORTS) - set(generate_private_cpp_export "GENERATE_PRIVATE_CPP_EXPORTS") - endif() qt_internal_generate_cpp_global_exports(${target} ${module_define_infix} "${cpp_export_header_base_name}" - "${generate_private_cpp_export}" ) endif() @@ -459,6 +460,13 @@ function(qt_internal_add_module target) set_target_properties(${target} PROPERTIES _qt_module_rhi_headers_filter_regex "${rhi_filter_regex}") + set(ssg_filter_regex "") + if(arg_SSG_HEADER_FILTERS) + set(ssg_filter_regex "${arg_SSG_HEADER_FILTERS}") + endif() + set_target_properties(${target} + PROPERTIES _qt_module_ssg_headers_filter_regex "${ssg_filter_regex}") + set(private_filter_regex ".+_p(ch)?\\.h") if(arg_PRIVATE_HEADER_FILTERS) set(private_filter_regex "${private_filter_regex}|${arg_PRIVATE_HEADER_FILTERS}") @@ -469,6 +477,8 @@ function(qt_internal_add_module target) # If EXTERNAL_HEADERS_DIR is set we install the specified directory and keep the structure # without taking into the account the CMake source tree and syncqt outputs. if(arg_EXTERNAL_HEADERS_DIR) + set_property(TARGET ${target} + PROPERTY _qt_external_headers_dir "${arg_EXTERNAL_HEADERS_DIR}") qt_install(DIRECTORY "${arg_EXTERNAL_HEADERS_DIR}/" DESTINATION "${module_install_interface_include_dir}" ) @@ -476,7 +486,7 @@ function(qt_internal_add_module target) endif() if(arg_NO_HEADERSCLEAN_CHECK OR arg_NO_MODULE_HEADERS OR arg_NO_SYNC_QT - OR NOT QT_FEATURE_headersclean) + OR NOT INPUT_headersclean) set_target_properties("${target}" PROPERTIES _qt_no_headersclean_check ON) endif() @@ -572,10 +582,10 @@ function(qt_internal_add_module target) list(APPEND defines_for_extend_target QT_NO_CAST_TO_ASCII QT_ASCII_CAST_WARNINGS QT_MOC_COMPAT #we don't need warnings from calling moc code in our generated code - QT_USE_QSTRINGBUILDER QT_DEPRECATED_WARNINGS QT_BUILDING_QT QT_BUILD_${module_define_infix}_LIB ### FIXME: use QT_BUILD_ADDON for Add-ons or remove if we don't have add-ons anymore + ${deprecation_define} ) list(APPEND arg_LIBRARIES Qt::PlatformModuleInternal) endif() @@ -673,8 +683,21 @@ function(qt_internal_add_module target) list(APPEND extra_cmake_files "${CMAKE_CURRENT_LIST_DIR}/${INSTALL_CMAKE_NAMESPACE}${target}Macros.cmake") list(APPEND extra_cmake_includes "${INSTALL_CMAKE_NAMESPACE}${target}Macros.cmake") endif() + if (EXISTS "${CMAKE_CURRENT_LIST_DIR}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigExtras.cmake.in") if(target STREQUAL Core) + if(NOT "${QT_NAMESPACE}" STREQUAL "") + string(MAKE_C_IDENTIFIER "${QT_NAMESPACE}" qt_namespace_sanity) + if(NOT "${QT_NAMESPACE}" STREQUAL "${qt_namespace_sanity}") + message(FATAL_ERROR "QT_NAMESPACE is not a valid C++ identifier: " + "${QT_NAMESPACE}.") + endif() + string(JOIN "" qtcore_namespace_definition + "set_property(TARGET \${__qt_core_target} " + "APPEND PROPERTY INTERFACE_COMPILE_DEFINITIONS QT_NAMESPACE=${QT_NAMESPACE})" + ) + endif() + set(extra_cmake_code "") # Add some variables for compatibility with Qt5 config files. if(QT_FEATURE_reduce_exports) @@ -704,6 +727,9 @@ set(QT_ALLOW_MISSING_TOOLS_PACKAGES TRUE)") get_filename_component(basename ${cmake_file} NAME) file(COPY ${cmake_file} DESTINATION ${config_build_dir}) list(APPEND extra_cmake_files "${config_build_dir}/${basename}") + + # Make sure touched extra cmake files cause a reconfigure, so they get re-copied. + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${cmake_file}") endforeach() list(APPEND extra_cmake_includes ${arg_EXTRA_CMAKE_INCLUDES}) @@ -809,7 +835,9 @@ set(QT_ALLOW_MISSING_TOOLS_PACKAGES TRUE)") qt_internal_export_modern_cmake_config_targets_file( TARGETS ${exported_targets} EXPORT_NAME_PREFIX ${INSTALL_CMAKE_NAMESPACE}${target} - CONFIG_INSTALL_DIR "${config_install_dir}") + CONFIG_BUILD_DIR "${config_build_dir}" + CONFIG_INSTALL_DIR "${config_install_dir}" + ) qt_internal_export_genex_properties(TARGETS ${target} EXPORT_NAME_PREFIX ${INSTALL_CMAKE_NAMESPACE}${target} @@ -829,7 +857,7 @@ set(QT_ALLOW_MISSING_TOOLS_PACKAGES TRUE)") if(NOT arg_NO_SYNC_QT AND NOT arg_NO_MODULE_HEADERS) list(APPEND interface_includes "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>") - # syncqt.pl does not create a private header directory like 'include/6.0/QtFoo' unless + # syncqt does not create a private header directory like 'include/6.0/QtFoo' unless # the module has foo_p.h header files. For QtZlib, there are no such private headers, so we # need to make sure not to add such include paths unless the directory exists, otherwise # consumers of the module will fail at CMake generation time stating that @@ -886,6 +914,28 @@ set(QT_ALLOW_MISSING_TOOLS_PACKAGES TRUE)") qt_add_list_file_finalizer(qt_finalize_module ${target} ${arg_INTERNAL_MODULE} ${arg_NO_PRIVATE_MODULE}) endfunction() +function(qt_internal_apply_apple_privacy_manifest target) + # Avoid "INTERFACE_LIBRARY targets may only have whitelisted properties" error on CMake < 3.17. + get_target_property(target_type ${target} TYPE) + if("${target_type}" STREQUAL "INTERFACE_LIBRARY") + return() + endif() + + if(APPLE) + # Privacy manifest + get_target_property(is_framework ${target} FRAMEWORK) + if(is_framework) + get_target_property(privacy_manifest ${target} _qt_privacy_manifest) + if(NOT privacy_manifest) + set(privacy_manifest + "${__qt_internal_cmake_apple_support_files_path}/PrivacyInfo.xcprivacy") + endif() + target_sources("${target}" PRIVATE "${privacy_manifest}") + set_property(TARGET "${target}" APPEND PROPERTY RESOURCE "${privacy_manifest}") + endif() + endif() +endfunction() + function(qt_finalize_module target) qt_internal_collect_module_headers(module_headers ${target}) @@ -902,12 +952,14 @@ function(qt_finalize_module target) PRIVATE ${module_headers_private} QPA ${module_headers_qpa} RHI ${module_headers_rhi} + SSG ${module_headers_ssg} ) qt_finalize_framework_headers_copy(${target}) qt_generate_prl_file(${target} "${INSTALL_LIBDIR}") qt_generate_module_pri_file("${target}" ${ARGN}) qt_internal_generate_pkg_config_file(${target}) + qt_internal_apply_apple_privacy_manifest(${target}) endfunction() # Get a set of Qt module related values based on the target. @@ -935,6 +987,7 @@ endfunction() # * foo_private_include_dir with the value "QtCore/6.2.0/QtCore/private" # * foo_qpa_include_dir with the value "QtCore/6.2.0/QtCore/qpa" # * foo_rhi_include_dir with the value "QtCore/6.2.0/QtCore/rhi" +# * foo_ssg_include_dir with the value "QtQuick3D/6.2.0/QtQuick3D/ssg" # * foo_interface_name the interface name of the module stored in _qt_module_interface_name # property, e.g. Core. # @@ -960,6 +1013,9 @@ endfunction() # * foo_<build|install>_rhi_include_dir with # qtbase_build_dir/include/QtCore/6.2.0/QtCore/rhi for build interface and # include/QtCore/6.2.0/QtCore/rhi for install interface. +# * foo_<build|install>_ssg_include_dir with +# qtbase_build_dir/include/<module>/x.y.z/<module>/ssg for build interface and +# include/<module>/x.y.z/<module>/ssg for install interface. # The following values are set by the function and might be useful in caller's scope: # * repo_install_interface_include_dir contains path to the top-level repository include directory, # e.g. qtbase_build_dir/include @@ -996,6 +1052,8 @@ the different base name for the module info variables.") "${${result}_versioned_inner_include_dir}/qpa") set("${result}_rhi_include_dir" "${${result}_versioned_inner_include_dir}/rhi") + set("${result}_ssg_include_dir" + "${${result}_versioned_inner_include_dir}/ssg") # Module build interface directories set(repo_build_interface_include_dir "${QT_BUILD_DIR}/include") @@ -1011,6 +1069,8 @@ the different base name for the module info variables.") "${repo_build_interface_include_dir}/${${result}_qpa_include_dir}") set("${result}_build_interface_rhi_include_dir" "${repo_build_interface_include_dir}/${${result}_rhi_include_dir}") + set("${result}_build_interface_ssg_include_dir" + "${repo_build_interface_include_dir}/${${result}_ssg_include_dir}") # Module install interface directories set(repo_install_interface_include_dir "${INSTALL_INCLUDEDIR}") @@ -1026,6 +1086,8 @@ the different base name for the module info variables.") "${repo_install_interface_include_dir}/${${result}_qpa_include_dir}") set("${result}_install_interface_rhi_include_dir" "${repo_install_interface_include_dir}/${${result}_rhi_include_dir}") + set("${result}_install_interface_ssg_include_dir" + "${repo_install_interface_include_dir}/${${result}_ssg_include_dir}") set("${result}" "${module}" PARENT_SCOPE) set("${result}_versioned" "${module_versioned}" PARENT_SCOPE) @@ -1040,6 +1102,7 @@ the different base name for the module info variables.") set("${result}_private_include_dir" "${${result}_private_include_dir}" PARENT_SCOPE) set("${result}_qpa_include_dir" "${${result}_qpa_include_dir}" PARENT_SCOPE) set("${result}_rhi_include_dir" "${${result}_rhi_include_dir}" PARENT_SCOPE) + set("${result}_ssg_include_dir" "${${result}_ssg_include_dir}" PARENT_SCOPE) set("${result}_interface_name" "${module_interface_name}" PARENT_SCOPE) # Setting module build interface directories in parent scope @@ -1056,6 +1119,8 @@ the different base name for the module info variables.") "${${result}_build_interface_qpa_include_dir}" PARENT_SCOPE) set("${result}_build_interface_rhi_include_dir" "${${result}_build_interface_rhi_include_dir}" PARENT_SCOPE) + set("${result}_build_interface_ssg_include_dir" + "${${result}_build_interface_ssg_include_dir}" PARENT_SCOPE) # Setting module install interface directories in parent scope set(repo_install_interface_include_dir "${repo_install_interface_include_dir}" PARENT_SCOPE) @@ -1071,6 +1136,8 @@ the different base name for the module info variables.") "${${result}_install_interface_qpa_include_dir}" PARENT_SCOPE) set("${result}_install_interface_rhi_include_dir" "${${result}_install_interface_rhi_include_dir}" PARENT_SCOPE) + set("${result}_install_interface_ssg_include_dir" + "${${result}_install_interface_ssg_include_dir}" PARENT_SCOPE) endfunction() function(qt_internal_list_to_json_array out_var list_var) @@ -1134,7 +1201,7 @@ endfunction() function(qt_internal_generate_cpp_global_exports target module_define_infix) cmake_parse_arguments(arg - "GENERATE_PRIVATE_CPP_EXPORTS" + "" "CPP_EXPORT_HEADER_BASE_NAME" "" ${ARGN} ) @@ -1159,26 +1226,12 @@ function(qt_internal_generate_cpp_global_exports target module_define_infix) set(${out_public_header} "${generated_header_path}" PARENT_SCOPE) target_sources(${target} PRIVATE "${generated_header_path}") set_source_files_properties("${generated_header_path}" PROPERTIES GENERATED TRUE) - - if(arg_GENERATE_PRIVATE_CPP_EXPORTS) - set(generated_private_header_path - "${module_build_interface_private_include_dir}/${header_base_name}_p.h" - ) - - configure_file("${QT_CMAKE_DIR}/modulecppexports_p.h.in" - "${generated_private_header_path}" @ONLY - ) - - set(${out_private_header} "${generated_private_header_path}" PARENT_SCOPE) - target_sources(${target} PRIVATE "${generated_private_header_path}") - set_source_files_properties("${generated_private_header_path}" PROPERTIES GENERATED TRUE) - endif() endfunction() function(qt_internal_install_module_headers target) set(options) set(one_value_args) - set(multi_value_args PUBLIC PRIVATE QPA RHI) + set(multi_value_args PUBLIC PRIVATE QPA RHI SSG) cmake_parse_arguments(arg "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN}) qt_internal_module_info(module ${target}) @@ -1204,6 +1257,7 @@ function(qt_internal_install_module_headers target) PRIVATE ${arg_PRIVATE} QPA ${arg_QPA} RHI ${arg_RHI} + SSG ${arg_SSG} ) else() if(arg_PUBLIC) @@ -1220,6 +1274,9 @@ function(qt_internal_install_module_headers target) if(arg_RHI) qt_install(FILES ${arg_RHI} DESTINATION "${module_install_interface_rhi_include_dir}") endif() + if(arg_SSG) + qt_install(FILES ${arg_SSG} DESTINATION "${module_install_interface_ssg_include_dir}") + endif() endif() endfunction() @@ -1228,6 +1285,7 @@ function(qt_internal_collect_module_headers out_var target) set(${out_var}_private "") set(${out_var}_qpa "") set(${out_var}_rhi "") + set(${out_var}_ssg "") set(${out_var}_all "") qt_internal_get_target_sources(sources ${target}) @@ -1235,10 +1293,13 @@ function(qt_internal_collect_module_headers out_var target) get_target_property(target_type ${target} TYPE) if(target_type STREQUAL "INTERFACE_LIBRARY") set(source_dir "${CMAKE_CURRENT_SOURCE_DIR}") + set(binary_dir "${CMAKE_CURRENT_BINARY_DIR}") else() get_target_property(source_dir ${target} SOURCE_DIR) + get_target_property(binary_dir ${target} BINARY_DIR) endif() get_filename_component(source_dir "${source_dir}" ABSOLUTE) + get_filename_component(binary_dir "${binary_dir}" ABSOLUTE) get_target_property(is_3rdparty_library ${target} _qt_module_is_3rdparty_header_library) @@ -1246,6 +1307,7 @@ function(qt_internal_collect_module_headers out_var target) get_target_property(private_filter ${target} _qt_module_private_headers_filter_regex) get_target_property(qpa_filter ${target} _qt_module_qpa_headers_filter_regex) get_target_property(rhi_filter ${target} _qt_module_rhi_headers_filter_regex) + get_target_property(ssg_filter ${target} _qt_module_ssg_headers_filter_regex) set(condition_independent_headers_warning "") foreach(file_path IN LISTS sources) @@ -1282,7 +1344,14 @@ function(qt_internal_collect_module_headers out_var target) "\nCondition:\n ${condition_string}") endif() - if(file_path MATCHES "3rdparty/.+" AND NOT is_3rdparty_library) + if(is_outside_module_source_dir) + set(base_dir "${binary_dir}") + else() + set(base_dir "${source_dir}") + endif() + + file(RELATIVE_PATH file_path_rel "${base_dir}" "${file_path}") + if(file_path_rel MATCHES "3rdparty/.+" AND NOT is_3rdparty_library) set(is_3rdparty_header TRUE) else() set(is_3rdparty_header FALSE) @@ -1292,6 +1361,8 @@ function(qt_internal_collect_module_headers out_var target) list(APPEND ${out_var}_qpa "${file_path}") elseif(rhi_filter AND file_name MATCHES "${rhi_filter}") list(APPEND ${out_var}_rhi "${file_path}") + elseif(ssg_filter AND file_name MATCHES "${ssg_filter}") + list(APPEND ${out_var}_ssg "${file_path}") elseif(private_filter AND file_name MATCHES "${private_filter}") list(APPEND ${out_var}_private "${file_path}") elseif((NOT public_filter OR file_name MATCHES "${public_filter}") @@ -1315,7 +1386,7 @@ function(qt_internal_collect_module_headers out_var target) endif() - set(header_types public private qpa rhi) + set(header_types public private qpa rhi ssg) set(has_header_types_properties "") foreach(header_type IN LISTS header_types) get_target_property(current_propety_value ${target} _qt_module_has_${header_type}_headers) @@ -1338,5 +1409,6 @@ function(qt_internal_collect_module_headers out_var target) _qt_module_has_private_headers _qt_module_has_qpa_headers _qt_module_has_rhi_headers + _qt_module_has_ssg_headers ) endfunction() diff --git a/cmake/QtModuleToolsConfig.cmake.in b/cmake/QtModuleToolsConfig.cmake.in index 4d852dc98e..ec447aa55b 100644 --- a/cmake/QtModuleToolsConfig.cmake.in +++ b/cmake/QtModuleToolsConfig.cmake.in @@ -1,3 +1,6 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + @PACKAGE_INIT@ cmake_minimum_required(VERSION @min_new_policy_version@...@max_new_policy_version@) diff --git a/cmake/QtModuleToolsDependencies.cmake.in b/cmake/QtModuleToolsDependencies.cmake.in index 2eaaf74976..b2504a0943 100644 --- a/cmake/QtModuleToolsDependencies.cmake.in +++ b/cmake/QtModuleToolsDependencies.cmake.in @@ -1,3 +1,6 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # Find "ModuleTools" dependencies, which are other ModuleTools packages. set(@INSTALL_CMAKE_NAMESPACE@@target@_FOUND FALSE) set(__qt_@target@_tool_deps "@package_deps@") diff --git a/cmake/QtModuleToolsVersionlessTargets.cmake.in b/cmake/QtModuleToolsVersionlessTargets.cmake.in index 0cf554c34a..bc861a77b6 100644 --- a/cmake/QtModuleToolsVersionlessTargets.cmake.in +++ b/cmake/QtModuleToolsVersionlessTargets.cmake.in @@ -1,3 +1,6 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + foreach(__qt_tool @tool_targets_non_prefixed@) if(NOT TARGET Qt::${__qt_tool} AND TARGET Qt6::${__qt_tool}) add_executable(Qt::${__qt_tool} IMPORTED GLOBAL) diff --git a/cmake/QtPkgConfigHelpers.cmake b/cmake/QtPkgConfigHelpers.cmake index 370ff607c4..ea28516941 100644 --- a/cmake/QtPkgConfigHelpers.cmake +++ b/cmake/QtPkgConfigHelpers.cmake @@ -51,6 +51,17 @@ function(qt_internal_generate_pkg_config_file module) list(TRANSFORM loose_include_dirs REPLACE "${INSTALL_INCLUDEDIR}" "\${includedir}") list(TRANSFORM loose_include_dirs REPLACE "${INSTALL_MKSPECSDIR}" "\${mkspecsdir}") + # Remove genex wrapping around gc_sections flag because we can't evaluate genexes like + # $<CXX_COMPILER_ID> in file(GENERATE). And given that .pc files don't support dynamic + # evaluation like the $<CXX_COMPILER_ID> genex, distros will be expected to patch the .pc + # files according to which compiler they intend to be used with. + get_property(gc_sections_with_genex GLOBAL PROPERTY _qt_internal_gc_sections_with_genex) + get_property(gc_sections_without_genex GLOBAL PROPERTY _qt_internal_gc_sections_without_genex) + if(loose_link_options AND gc_sections_with_genex AND gc_sections_without_genex) + string(REPLACE "${gc_sections_with_genex}" "${gc_sections_without_genex}" + loose_link_options "${loose_link_options}") + endif() + qt_internal_set_pkg_config_cpp_flags(link_options "${loose_link_options}" "") qt_internal_set_pkg_config_cpp_flags(compile_defs "${loose_compile_defs}" -D) qt_internal_set_pkg_config_cpp_flags(include_dirs "${loose_include_dirs}" -I) @@ -63,7 +74,7 @@ function(qt_internal_generate_pkg_config_file module) foreach(dep IN LISTS loose_target_requires) if(dep MATCHES "^Qt::") string(REGEX REPLACE "Qt" "${QT_CMAKE_EXPORT_NAMESPACE}" dep ${dep}) - else() + elseif(NOT dep MATCHES "^${QT_CMAKE_EXPORT_NAMESPACE}::") # TODO: Figure out a way to get non-Qt requirements PkgConfig files. continue() endif() diff --git a/cmake/QtPlatformAndroid.cmake b/cmake/QtPlatformAndroid.cmake index 65095de329..6b8bd744c1 100644 --- a/cmake/QtPlatformAndroid.cmake +++ b/cmake/QtPlatformAndroid.cmake @@ -36,7 +36,7 @@ function(qt_get_android_sdk_jar_for_api api out_jar_location) endfunction() # Minimum recommend android SDK api version -set(QT_ANDROID_API_VERSION "android-33") +set(QT_ANDROID_API_VERSION "android-34") function(qt_internal_sort_android_platforms out_var) if(CMAKE_VERSION GREATER_EQUAL 3.18) @@ -185,7 +185,7 @@ define_property(TARGET ) # Returns test execution arguments for Android targets -function(qt_internal_android_test_arguments target out_test_runner out_test_arguments) +function(qt_internal_android_test_arguments target timeout out_test_runner out_test_arguments) set(${out_test_runner} "${QT_HOST_PATH}/${QT${PROJECT_VERSION_MAJOR}_HOST_INFO_BINDIR}/androidtestrunner" PARENT_SCOPE) set(deployment_tool "${QT_HOST_PATH}/${QT${PROJECT_VERSION_MAJOR}_HOST_INFO_BINDIR}/androiddeployqt") @@ -195,15 +195,19 @@ function(qt_internal_android_test_arguments target out_test_runner out_test_argu endif() set(target_binary_dir "$<TARGET_PROPERTY:${target},BINARY_DIR>") - set(apk_dir "${target_binary_dir}/android-build") - + if(QT_USE_TARGET_ANDROID_BUILD_DIR) + set(apk_dir "${target_binary_dir}/android-build-${target}") + else() + set(apk_dir "${target_binary_dir}/android-build") + endif() set(${out_test_arguments} "--path" "${apk_dir}" "--adb" "${ANDROID_SDK_ROOT}/platform-tools/adb" "--skip-install-root" - "--make" "${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target ${target}_make_apk" + "--make" "\"${CMAKE_COMMAND}\" --build ${CMAKE_BINARY_DIR} --target ${target}_make_apk" "--apk" "${apk_dir}/${target}.apk" - "--timeout" "-1" + "--ndk-stack" "${ANDROID_NDK_ROOT}/ndk-stack" + "--timeout" "${timeout}" "--verbose" PARENT_SCOPE ) diff --git a/cmake/QtPlatformSupport.cmake b/cmake/QtPlatformSupport.cmake index 16a5a1fe0a..9f8498e42c 100644 --- a/cmake/QtPlatformSupport.cmake +++ b/cmake/QtPlatformSupport.cmake @@ -12,7 +12,6 @@ endfunction() qt_set01(LINUX CMAKE_SYSTEM_NAME STREQUAL "Linux") qt_set01(HPUX CMAKE_SYSTEM_NAME STREQUAL "HPUX") qt_set01(ANDROID CMAKE_SYSTEM_NAME STREQUAL "Android") # FIXME: How to identify this? -qt_set01(NACL CMAKE_SYSTEM_NAME STREQUAL "NaCl") # FIXME: How to identify this? qt_set01(INTEGRITY CMAKE_SYSTEM_NAME STREQUAL "Integrity") # FIXME: How to identify this? qt_set01(VXWORKS CMAKE_SYSTEM_NAME STREQUAL "VxWorks") # FIXME: How to identify this? qt_set01(QNX CMAKE_SYSTEM_NAME STREQUAL "QNX") # FIXME: How to identify this? @@ -33,7 +32,8 @@ qt_set01(BSD APPLE OR OPENBSD OR FREEBSD OR NETBSD) qt_set01(IOS APPLE AND CMAKE_SYSTEM_NAME STREQUAL "iOS") qt_set01(TVOS APPLE AND CMAKE_SYSTEM_NAME STREQUAL "tvOS") qt_set01(WATCHOS APPLE AND CMAKE_SYSTEM_NAME STREQUAL "watchOS") -qt_set01(UIKIT APPLE AND (IOS OR TVOS OR WATCHOS)) +qt_set01(VISIONOS APPLE AND CMAKE_SYSTEM_NAME STREQUAL "visionOS") +qt_set01(UIKIT APPLE AND (IOS OR TVOS OR WATCHOS OR VISIONOS)) qt_set01(MACOS APPLE AND NOT UIKIT) qt_set01(GCC CMAKE_CXX_COMPILER_ID STREQUAL "GNU") diff --git a/cmake/QtPlatformTargetHelpers.cmake b/cmake/QtPlatformTargetHelpers.cmake index b1a78e69c7..f1976b9975 100644 --- a/cmake/QtPlatformTargetHelpers.cmake +++ b/cmake/QtPlatformTargetHelpers.cmake @@ -49,6 +49,15 @@ function(qt_internal_setup_public_platform_target) target_compile_options(Platform INTERFACE "$<$<CXX_COMPILER_ID:Clang>:-fno-direct-access-external-data>") endif() + # Qt checks if a given platform supports 128 bit integers + # by checking if __SIZEOF_128__ is defined + # VXWORKS doesn't support 128 bit integers + # but it uses clang which defines __SIZEOF_128__ + # which breaks the detection mechanism + if(VXWORKS) + target_compile_definitions(Platform INTERFACE "-DQT_NO_INT128") + endif() + qt_set_msvc_cplusplus_options(Platform INTERFACE) # Propagate minimum C++ 17 via Platform to Qt consumers (apps), after the global features @@ -63,10 +72,6 @@ function(qt_internal_setup_public_platform_target) # Generate a pkgconfig for Qt::Platform. qt_internal_generate_pkg_config_file(Platform) - - # Make sure Qt users use the same symbols as how we build Qt. - qt_internal_library_deprecation_level(deprecation_defines) - target_compile_definitions(Platform INTERFACE ${deprecation_defines}) endfunction() function(qt_internal_get_platform_definition_include_dir install_interface build_interface) diff --git a/cmake/QtPluginConfig.cmake.in b/cmake/QtPluginConfig.cmake.in index 7bd9a1d579..1dc30b0338 100644 --- a/cmake/QtPluginConfig.cmake.in +++ b/cmake/QtPluginConfig.cmake.in @@ -1,6 +1,9 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include_guard(DIRECTORY) -if(DEFINED QT_REPO_DEPENDENCIES AND NOT QT_BUILD_STANDALONE_TESTS) +if(DEFINED QT_REPO_DEPENDENCIES AND NOT QT_INTERNAL_BUILD_STANDALONE_PARTS) # We're building a Qt repository. # Skip this plugin if it has not been provided by one of this repo's dependencies. string(TOLOWER "@PROJECT_NAME@" lower_case_project_name) diff --git a/cmake/QtPluginDependencies.cmake.in b/cmake/QtPluginDependencies.cmake.in index c9dd1873d8..bcbb9bb5db 100644 --- a/cmake/QtPluginDependencies.cmake.in +++ b/cmake/QtPluginDependencies.cmake.in @@ -1,3 +1,6 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + set(@target@_FOUND FALSE) # note: _third_party_deps example: "ICU\\;FALSE\\;1.0\\;i18n uc data;ZLIB\\;FALSE\\;\\;" diff --git a/cmake/QtPluginHelpers.cmake b/cmake/QtPluginHelpers.cmake index b4aaffb8bb..a4188b7289 100644 --- a/cmake/QtPluginHelpers.cmake +++ b/cmake/QtPluginHelpers.cmake @@ -91,6 +91,11 @@ function(qt_internal_add_plugin target) qt6_add_plugin(${target} ${plugin_args}) qt_internal_mark_as_internal_library(${target}) + get_target_property(target_type "${target}" TYPE) + if(plugin_init_target AND TARGET "${plugin_init_target}") + qt_internal_add_target_aliases("${plugin_init_target}") + endif() + set(plugin_type "") # TODO: Transitional: Remove the TYPE option handling after all repos have been converted to use # PLUGIN_TYPE. @@ -165,8 +170,6 @@ function(qt_internal_add_plugin target) qt_handle_multi_config_output_dirs("${target}") - qt_internal_library_deprecation_level(deprecation_define) - qt_autogen_tools_initial_setup(${target}) unset(plugin_install_package_suffix) @@ -211,73 +214,17 @@ function(qt_internal_add_plugin target) # This QT_PLUGINS assignment is only used by QtPostProcessHelpers to decide if a # QtModulePlugins.cmake file should be generated. set_property(TARGET "${qt_module_target}" APPEND PROPERTY QT_PLUGINS "${target}") + else() + # The _qt_plugins property is considered when collecting the plugins in + # deployment process. The usecase is following: + # QtModuleX is built separately and installed, so it's imported. + # The plugin is built in some application build tree and its PLUGIN_TYPE is associated + # with QtModuleX. + set_property(TARGET "${qt_module_target}" APPEND PROPERTY _qt_plugins "${target}") endif() set(plugin_target_versioned "${QT_CMAKE_EXPORT_NAMESPACE}::${target}") get_target_property(type "${plugin_target_versioned}" TYPE) - if(type STREQUAL STATIC_LIBRARY) - # Associate plugin with its Qt module when both are both built in the same repository. - # Check that by comparing the PROJECT_NAME of each. - # This covers auto-linking of the majority of plugins to executables and in-tree tests. - # Linking of plugins in standalone tests (when the Qt module will be an imported target) - # is handled instead by the complicated genex logic in QtModulePlugins.cmake.in. - set(is_plugin_and_module_in_same_project FALSE) - if(NOT is_imported_qt_module) - get_target_property(module_source_dir ${qt_module_target} SOURCE_DIR) - get_directory_property(module_project_name - DIRECTORY ${module_source_dir} - DEFINITION PROJECT_NAME - ) - if(module_project_name STREQUAL PROJECT_NAME) - set(is_plugin_and_module_in_same_project TRUE) - endif() - - # When linking static plugins with the special logic in qt_internal_add_executable, - # make sure to skip non-default plugins. - if(is_plugin_and_module_in_same_project AND _default_plugin) - set_property(TARGET ${qt_module_target} APPEND PROPERTY - _qt_initial_repo_plugins - "${target}") - set_property(TARGET ${qt_module_target} APPEND PROPERTY - _qt_initial_repo_plugin_class_names - "$<TARGET_PROPERTY:${target},QT_PLUGIN_CLASS_NAME>" - ) - endif() - endif() - - # Associate plugin with its Qt module when the plugin is built in the current repository - # but the module is built in a different repository (qtsvg's QSvgPlugin associated with - # qtbase's QtGui). - # The association is done in a separate property, to ensure that reconfiguring in-tree tests - # in qtbase doesn't accidentally cause linking to a plugin from a previously built qtsvg. - # Needed for in-tree tests like in qtsvg, qtimageformats. - # This is done for each Qt module regardless if it's an imported target or not, to handle - # both per-repo and top-level builds (in per-repo build of qtsvg QtGui is imported, in a - # top-level build Gui is not imported, but in both cases qtsvg tests need to link to - # QSvgPlugin). - # - # TODO: Top-level in-tree tests and qdeclarative per-repo in-tree tests that depend on - # static Qml plugins won't work due to the requirement of running qmlimportscanner - # at configure time, but qmlimportscanner is not built at that point. Moving the - # execution of qmlimportscanner to build time is non-trivial because qmlimportscanner - # not only generates a cpp file to compile but also outputs a list of static plugins - # that should be linked and there is no straightforward way to tell CMake to link - # against a list of libraries that was discovered at build time (apart from - # response files, which apparently might not work on all platforms). - # qmake doesn't have this problem because each project is configured separately so - # qmlimportscanner is always built by the time it needs to be run for a test. - if(NOT is_plugin_and_module_in_same_project AND _default_plugin) - string(MAKE_C_IDENTIFIER "${PROJECT_NAME}" current_project_name) - set(prop_prefix "_qt_repo_${current_project_name}") - set_property(TARGET ${qt_module_target} APPEND PROPERTY - ${prop_prefix}_plugins "${target}") - set_property(TARGET ${qt_module_target} APPEND PROPERTY - ${prop_prefix}_plugin_class_names - "$<TARGET_PROPERTY:${target},QT_PLUGIN_CLASS_NAME>" - ) - endif() - endif() - qt_internal_add_autogen_sync_header_dependencies(${target} ${qt_module_target}) endif() @@ -336,6 +283,8 @@ function(qt_internal_add_plugin target) qt_internal_extend_target("${target}" ${arg_NO_UNITY_BUILD} SOURCES ${arg_SOURCES} + NO_PCH_SOURCES + ${arg_NO_PCH_SOURCES} NO_UNITY_BUILD_SOURCES ${arg_NO_UNITY_BUILD_SOURCES} INCLUDE_DIRECTORIES @@ -381,7 +330,6 @@ function(qt_internal_add_plugin target) qt_register_target_dependencies("${target}" "${arg_PUBLIC_LIBRARIES}" "${qt_libs_private}") - get_target_property(target_type "${target}" TYPE) if(target_type STREQUAL STATIC_LIBRARY) if(qt_module_target) qt_internal_link_internal_platform_for_object_library("${plugin_init_target}") @@ -545,6 +493,7 @@ function(qt_internal_add_darwin_permission_plugin permission) Qt::Core Qt::CorePrivate ${FWFoundation} + NO_UNITY_BUILD # disable unity build: the same file is built with two different preprocessor defines. ) # Disable PCH since CMake falls over on single .mm source targets @@ -585,10 +534,12 @@ function(qt_internal_add_darwin_permission_plugin permission) ) if(CMAKE_VERSION VERSION_LESS "3.18") set_property(SOURCE "${separate_request_source_file}" PROPERTY GENERATED TRUE) + set_property(SOURCE "${separate_request_source_file}" PROPERTY SKIP_UNITY_BUILD_INCLUSION TRUE) endif() target_sources(${plugin_target} PRIVATE "$<${separate_request_genex}:${separate_request_source_file}>" ) + set_property(TARGET ${plugin_target} APPEND PROPERTY EXPORT_PROPERTIES _qt_darwin_permissison_separate_request ) @@ -607,3 +558,48 @@ function(qt_internal_add_darwin_permission_plugin permission) QT_PLUGIN_PRI_EXTRA_CONTENT ${extra_plugin_pri_content} ) endfunction() + +# The function looks and links the static plugins that the target depends on. The function behaves +# similar to qt_import_plugins, but should be used when building Qt executable or shared libraries. +# It's expected that all dependencies are valid targets at the time when the function is called. +# If not their plugins will be not collected for linking. +function(qt_internal_import_plugins target) + set(plugin_targets "") + foreach(dep_target IN LISTS ARGN) + if(dep_target AND TARGET ${dep_target}) + get_target_property(plugins ${dep_target} _qt_plugins) + if(plugins) + list(APPEND plugin_targets ${plugins}) + else() + # Fallback should be remove in Qt 7. + get_target_property(target_type ${dep_target} TYPE) + if(NOT "${target_type}" STREQUAL "INTERFACE_LIBRARY") + get_target_property(plugins ${dep_target} QT_PLUGINS) + if(plugins) + list(APPEND plugin_targets ${plugins}) + endif() + endif() + endif() + endif() + endforeach() + + set(non_imported_plugin_targets "") + foreach(plugin_target IN LISTS plugin_targets) + if(NOT TARGET ${plugin_target} OR "${plugin_target}" IN_LIST non_imported_plugin_targets) + continue() + endif() + + get_target_property(is_imported ${plugin_target} IMPORTED) + if(NOT is_imported) + list(APPEND non_imported_plugin_targets "${plugin_target}") + endif() + endforeach() + + if(plugin_targets) + __qt_internal_collect_plugin_init_libraries("${non_imported_plugin_targets}" init_libraries) + __qt_internal_collect_plugin_libraries("${non_imported_plugin_targets}" plugin_libraries) + if(plugin_libraries OR init_libraries) + target_link_libraries(${target} PRIVATE ${plugin_libraries} ${init_libraries}) + endif() + endif() +endfunction() diff --git a/cmake/QtPlugins.cmake.in b/cmake/QtPlugins.cmake.in index 405d5f37b9..e668a4cbef 100644 --- a/cmake/QtPlugins.cmake.in +++ b/cmake/QtPlugins.cmake.in @@ -1,6 +1,14 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + include_guard(DIRECTORY) @QT_MODULE_PLUGIN_INCLUDES@ +# Distributions should probably change this default. +if(NOT DEFINED QT_SKIP_AUTO_PLUGIN_INCLUSION) + set(QT_SKIP_AUTO_PLUGIN_INCLUSION OFF) +endif() + if(NOT QT_NO_CREATE_TARGETS AND NOT QT_SKIP_AUTO_PLUGIN_INCLUSION) __qt_internal_include_plugin_packages(@QT_MODULE@) endif() diff --git a/cmake/QtPostProcess.cmake b/cmake/QtPostProcess.cmake index b178372c60..f69448c14a 100644 --- a/cmake/QtPostProcess.cmake +++ b/cmake/QtPostProcess.cmake @@ -1,8 +1,6 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -include(QtPostProcessHelpers) - qt_internal_create_depends_files() qt_generate_build_internals_extra_cmake_code() qt_internal_create_plugins_auto_inclusion_files() diff --git a/cmake/QtPostProcessHelpers.cmake b/cmake/QtPostProcessHelpers.cmake index 83bcabe49b..9654b18664 100644 --- a/cmake/QtPostProcessHelpers.cmake +++ b/cmake/QtPostProcessHelpers.cmake @@ -67,6 +67,11 @@ macro(qt_collect_third_party_deps target) set(package_optional_components "") endif() + get_target_property(package_components_id ${dep} _qt_package_components_id) + if(package_components_id) + list(APPEND third_party_deps_package_components_ids ${package_components_id}) + endif() + list(APPEND third_party_deps "${package_name}\;${package_is_optional}\;${package_version}\;${package_components}\;${package_optional_components}") endif() @@ -74,6 +79,42 @@ macro(qt_collect_third_party_deps target) endforeach() endmacro() +# Collect provided targets for the given list of package component ids. +# +# ${target} is merely used as a key infix to avoid name clashes in the Dependencies.cmake files. +# package_component_ids is a list of '${package_name}-${components}-${optional_components}' keys +# that are sanitized not to contain spaces or semicolons. +# +# The output is a list of variable assignments to add to the dependencies file. +# Each variable assignment is the list of provided targets for a given package component id. +# +# We use these extra assignments instead of adding the info to the existing 'third_party_deps' list +# to make the information more readable. That list already has 5 items per package, making it +# quite hard to read. +function(qt_internal_collect_third_party_dep_packages_info + target + package_components_ids + out_packages_info) + + # There might be multiple calls to find the same package, so remove the duplicates. + list(REMOVE_DUPLICATES package_components_ids) + + set(packages_info "") + + foreach(package_key IN LISTS package_components_ids) + get_cmake_property(provided_targets _qt_find_package_${package_key}_provided_targets) + if(provided_targets) + set(key "__qt_${target}_third_party_package_${package_key}_provided_targets") + + # Escape the semicolon, so it is preserved in the list(JOIN) below + string(REPLACE ";" "\;" provided_targets "${provided_targets}") + string(APPEND packages_info "set(${key} \"${provided_targets}\")\n") + endif() + endforeach() + + set(${out_packages_info} "${packages_info}" PARENT_SCOPE) +endfunction() + # Filter the dependency targets to collect unique set of the dependencies. # non-Private and Private targets are treated as the single object in this context # since they are defined by the same CMake package. For internal modules @@ -149,6 +190,7 @@ function(qt_internal_create_module_depends_file target) # ModuleDependencies.cmake. set(third_party_deps "") set(third_party_deps_seen "") + set(third_party_deps_package_components_ids "") # Used for collecting Qt tool dependencies that should be find_package()'d in # ModuleToolsDependencies.cmake. @@ -216,6 +258,14 @@ function(qt_internal_create_module_depends_file target) endforeach() qt_collect_third_party_deps(${target}) + qt_internal_collect_third_party_dep_packages_info(${target} + "${third_party_deps_package_components_ids}" + packages_info) + + set(third_party_deps_extra_info "") + if(packages_info) + string(APPEND third_party_deps_extra_info "${packages_info}") + endif() # Add dependency to the main ModuleTool package to ModuleDependencies file. if(${target} IN_LIST QT_KNOWN_MODULES_WITH_TOOLS) @@ -446,7 +496,33 @@ function(qt_internal_create_plugins_auto_inclusion_files) # TODO: Find a better way to deal with this, perhaps by using find_package() instead of include # for the Qml PluginConfig.cmake files. -file(GLOB __qt_qml_plugins_config_file_list \"\${CMAKE_CURRENT_LIST_DIR}/QmlPlugins/${INSTALL_CMAKE_NAMESPACE}*Config.cmake\") +# Distributions should probably change this default. +if(NOT DEFINED QT_SKIP_AUTO_QML_PLUGIN_INCLUSION) + set(QT_SKIP_AUTO_QML_PLUGIN_INCLUSION OFF) +endif() + +set(__qt_qml_plugins_config_file_list \"\") +set(__qt_qml_plugins_glob_prefixes \"\${CMAKE_CURRENT_LIST_DIR}\") + +# Allow passing additional prefixes where we will glob for PluginConfig.cmake files. +if(QT_ADDITIONAL_QML_PLUGIN_GLOB_PREFIXES) + foreach(__qt_qml_plugin_glob_prefix IN LISTS QT_ADDITIONAL_QML_PLUGIN_GLOB_PREFIXES) + if(__qt_qml_plugin_glob_prefix) + list(APPEND __qt_qml_plugins_glob_prefixes \"\${__qt_qml_plugin_glob_prefix}\") + endif() + endforeach() +endif() + +list(REMOVE_DUPLICATES __qt_qml_plugins_glob_prefixes) + +foreach(__qt_qml_plugin_glob_prefix IN LISTS __qt_qml_plugins_glob_prefixes) + file(GLOB __qt_qml_plugins_glob_config_file_list + \"\${__qt_qml_plugin_glob_prefix}/QmlPlugins/${INSTALL_CMAKE_NAMESPACE}*Config.cmake\") + if(__qt_qml_plugins_glob_config_file_list) + list(APPEND __qt_qml_plugins_config_file_list \${__qt_qml_plugins_glob_config_file_list}) + endif() +endforeach() + if (__qt_qml_plugins_config_file_list AND NOT QT_SKIP_AUTO_QML_PLUGIN_INCLUSION) # First round of inclusions ensure all qml plugin targets are brought into scope. foreach(__qt_qml_plugin_config_file \${__qt_qml_plugins_config_file_list}) @@ -472,8 +548,8 @@ if (__qt_qml_plugins_config_file_list AND NOT QT_SKIP_AUTO_QML_PLUGIN_INCLUSION) endif()") endif() - get_target_property(qt_plugins "${QT_MODULE}" QT_PLUGINS) - if(qt_plugins OR QT_MODULE_PLUGIN_INCLUDES) + get_target_property(module_plugin_types "${QT_MODULE}" MODULE_PLUGIN_TYPES) + if(module_plugin_types OR QT_MODULE_PLUGIN_INCLUDES) list(APPEND modules_with_plugins "${QT_MODULE}") configure_file( "${QT_CMAKE_DIR}/QtPlugins.cmake.in" @@ -551,9 +627,8 @@ function(qt_generate_build_internals_extra_cmake_code) if(CMAKE_BUILD_TYPE) string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS " +# Used by qt_internal_set_cmake_build_type. set(__qt_internal_initial_qt_cmake_build_type \"${CMAKE_BUILD_TYPE}\") -qt_internal_force_set_cmake_build_type_conditionally( - \"\${__qt_internal_initial_qt_cmake_build_type}\") ") endif() if(CMAKE_CONFIGURATION_TYPES) @@ -575,17 +650,6 @@ qt_internal_force_set_cmake_build_type_conditionally( string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS "\nset(QT_MULTI_CONFIG_FIRST_CONFIG \"${QT_MULTI_CONFIG_FIRST_CONFIG}\")\n") endif() - # When building standalone tests against a multi-config Qt, we want to choose the first - # configuration, rather than use CMake's default value. - # In the case of Windows, we definitely don't it to default to Debug, because that causes - # issues in the CI. - if(multi_config_specific) - string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS " -if(QT_BUILD_STANDALONE_TESTS) - qt_internal_force_set_cmake_build_type_conditionally( - \"\${QT_MULTI_CONFIG_FIRST_CONFIG}\") -endif()\n") - endif() if(CMAKE_CROSS_CONFIGS) string(APPEND ninja_multi_config_specific @@ -616,9 +680,9 @@ endif()\n") "set(QT_IS_MACOS_UNIVERSAL \"${QT_IS_MACOS_UNIVERSAL}\" CACHE BOOL \"\")\n") endif() - if(DEFINED QT_UIKIT_SDK) + if(DEFINED QT_APPLE_SDK) string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS - "set(QT_UIKIT_SDK \"${QT_UIKIT_SDK}\" CACHE BOOL \"\")\n") + "set(QT_APPLE_SDK \"${QT_APPLE_SDK}\" CACHE BOOL \"\")\n") endif() if(QT_FORCE_FIND_TOOLS) @@ -639,13 +703,19 @@ endif()\n") endif() # Save the default qpa platform. - # Used by qtwayland/src/plugins/platforms/qwayland-generic/CMakeLists.txt. Otherwise - # the DEFAULT_IF condition is evaluated incorrectly. if(DEFINED QT_QPA_DEFAULT_PLATFORM) string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS "set(QT_QPA_DEFAULT_PLATFORM \"${QT_QPA_DEFAULT_PLATFORM}\" CACHE STRING \"\")\n") endif() + # Save the list of default qpa platforms. + # Used by qtwayland/src/plugins/platforms/qwayland-generic/CMakeLists.txt. Otherwise + # the DEFAULT_IF condition is evaluated incorrectly. + if(DEFINED QT_QPA_PLATFORMS) + string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS + "set(QT_QPA_PLATFORMS \"${QT_QPA_PLATFORMS}\" CACHE STRING \"\")\n") + endif() + # Save minimum and policy-related CMake versions to ensure the same minimum is # checked for when building other downstream repos (qtsvg, etc) and the policy settings # will be consistent unless the downstream repos explicitly override them. @@ -713,17 +783,6 @@ endif()\n") string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS "${install_prefix_content}") - # The top-level check needs to happen inside QtBuildInternals, because it's possible - # to configure a top-level build with a few repos and then configure another repo - # using qt-configure-module in a separate build dir, where QT_SUPERBUILD will not - # be set anymore. - string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS - " -if(DEFINED QT_REPO_MODULE_VERSION AND NOT DEFINED QT_REPO_DEPENDENCIES AND NOT QT_SUPERBUILD) - qt_internal_read_repo_dependencies(QT_REPO_DEPENDENCIES \"$\{PROJECT_SOURCE_DIR}\") -endif() -") - if(DEFINED OpenGL_GL_PREFERENCE) string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS " @@ -734,10 +793,22 @@ set(OpenGL_GL_PREFERENCE \"${OpenGL_GL_PREFERENCE}\" CACHE STRING \"\") string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS " -set(QT_COPYRIGHT_YEAR \"${QT_COPYRIGHT_YEAR}\" CACHE STRING \"\") set(QT_COPYRIGHT \"${QT_COPYRIGHT}\" CACHE STRING \"\") ") + # Add the apple version requirements to the BuildInternals extra code, so the info is + # available when configuring a standalone test. + # Otherwise when QtSetup is included after a + # find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) + # call, Qt6ConfigExtras.cmake is not included yet, the requirements are not available and + # _qt_internal_check_apple_sdk_and_xcode_versions() would fail. + _qt_internal_export_apple_sdk_and_xcode_version_requirements(apple_requirements) + if(apple_requirements) + string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS " +${apple_requirements} +") + endif() + qt_compute_relative_path_from_cmake_config_dir_to_prefix() configure_file( "${CMAKE_CURRENT_LIST_DIR}/QtBuildInternalsExtra.cmake.in" @@ -804,7 +875,7 @@ function(qt_internal_create_config_file_for_standalone_tests) # Create a Config file that calls find_package on the modules that were built as part # of the current repo. This is used for standalone tests. - qt_internal_get_standalone_tests_config_file_name(tests_config_file_name) + qt_internal_get_standalone_parts_config_file_name(tests_config_file_name) # Standalone tests Config files should follow the main versioning scheme. qt_internal_get_package_version_of_target(Platform main_qt_package_version) @@ -848,8 +919,15 @@ function(qt_internal_generate_user_facing_tools_info) if(NOT filename) set(filename ${target}) endif() + set(linkname ${filename}) + if(APPLE) + get_target_property(is_macos_bundle ${target} MACOSX_BUNDLE ) + if(is_macos_bundle) + set(filename "${filename}.app/Contents/MacOS/${filename}") + endif() + endif() qt_path_join(tool_target_path "${CMAKE_INSTALL_PREFIX}" "${INSTALL_BINDIR}" "${filename}") - qt_path_join(tool_link_path "${INSTALL_PUBLICBINDIR}" "${filename}${PROJECT_VERSION_MAJOR}") + qt_path_join(tool_link_path "${INSTALL_PUBLICBINDIR}" "${linkname}${PROJECT_VERSION_MAJOR}") list(APPEND lines "${tool_target_path} ${tool_link_path}") endforeach() string(REPLACE ";" "\n" content "${lines}") diff --git a/cmake/QtPriHelpers.cmake b/cmake/QtPriHelpers.cmake index c619a50f30..f29b55b349 100644 --- a/cmake/QtPriHelpers.cmake +++ b/cmake/QtPriHelpers.cmake @@ -26,9 +26,14 @@ function(qt_generate_qmake_libraries_pri_content module_name output_root_dir out set(lib_incdir "") set(lib_libdir "") set(lib_libs "") + set(seen_targets "") while(lib_targets) list(POP_BACK lib_targets lib_target) if(TARGET ${lib_target}) + if(${lib_target} IN_LIST seen_targets) + continue() + endif() + list(APPEND seen_targets ${lib_target}) get_target_property(lib_target_type ${lib_target} TYPE) if(lib_target_type STREQUAL "INTERFACE_LIBRARY") get_target_property(iface_libs ${lib_target} INTERFACE_LINK_LIBRARIES) @@ -139,13 +144,10 @@ function(qt_get_direct_module_dependencies target out_var) continue() endif() get_target_property(lib_type ${lib} TYPE) - get_target_property(is_versionless_target ${lib} _qt_is_versionless_target) - if (lib_type STREQUAL "INTERFACE_LIBRARY" AND is_versionless_target) - # Found a version-less target like Qt::Core outside of qtbase. - # Skip this one and use what this target points to, e.g. Qt6::Core. - # Make sure to process Private interface libraries as-is. - get_target_property(ifacelibs ${lib} INTERFACE_LINK_LIBRARIES) - list(PREPEND libs ${ifacelibs}) + get_target_property(aliased_target ${lib} ALIASED_TARGET) + if(TARGET "${aliased_target}") + # If versionless target is alias, use what the alias points to. + list(PREPEND libs "${aliased_target}") continue() endif() if(lib_type STREQUAL "OBJECT_LIBRARY") @@ -776,8 +778,23 @@ QT_PATCH_VERSION = ${PROJECT_VERSION_PATCH} if(APPLE) list(APPEND extra_statements "QT_MAC_SDK_VERSION = ${QT_MAC_SDK_VERSION}") - list(APPEND extra_statements - "QMAKE_MACOSX_DEPLOYMENT_TARGET = ${CMAKE_OSX_DEPLOYMENT_TARGET}") + if(NOT CMAKE_SYSTEM_NAME OR CMAKE_SYSTEM_NAME STREQUAL "Darwin") + # macOS + list(APPEND extra_statements + "QMAKE_MACOSX_DEPLOYMENT_TARGET = ${CMAKE_OSX_DEPLOYMENT_TARGET}") + list(APPEND extra_statements + "QT_MAC_SDK_VERSION_MIN = ${QT_SUPPORTED_MIN_MACOS_SDK_VERSION}") + list(APPEND extra_statements + "QT_MAC_SDK_VERSION_MAX = ${QT_SUPPORTED_MAX_MACOS_SDK_VERSION}") + elseif(CMAKE_SYSTEM_NAME STREQUAL iOS) + list(APPEND extra_statements + "QMAKE_IOS_DEPLOYMENT_TARGET = ${CMAKE_OSX_DEPLOYMENT_TARGET}") + list(APPEND extra_statements + "QT_MAC_SDK_VERSION_MIN = ${QT_SUPPORTED_MIN_IOS_SDK_VERSION}") + list(APPEND extra_statements + "QT_MAC_SDK_VERSION_MAX = ${QT_SUPPORTED_MAX_IOS_SDK_VERSION}") + endif() + if (CMAKE_OSX_ARCHITECTURES) list(APPEND architectures "${CMAKE_OSX_ARCHITECTURES}") string (REPLACE ";" " " architectures "${architectures}") @@ -787,8 +804,6 @@ QT_PATCH_VERSION = ${PROJECT_VERSION_PATCH} list(APPEND extra_statements "QT_ARCHS = ${architectures}") endif() - list(APPEND extra_statements "QT_EDITION = Open Source") - if(WASM) list(APPEND extra_statements "QT_EMCC_VERSION = ${EMCC_VERSION}") @@ -826,7 +841,7 @@ function(qt_generate_global_device_pri_file) file(TO_CMAKE_PATH ${ANDROID_NDK} ANDROID_NDK) string(APPEND content "DEFAULT_ANDROID_NDK_ROOT = ${ANDROID_NDK}\n") - set(android_platform "android-23") + set(android_platform "android-28") if(ANDROID_PLATFORM) set(android_platform "${ANDROID_PLATFORM}") elseif(ANDROID_NATIVE_API_LEVEL) @@ -847,8 +862,8 @@ function(qt_generate_global_device_pri_file) endif() endif() - if(QT_UIKIT_SDK) - string(APPEND content "QMAKE_MAC_SDK = ${QT_UIKIT_SDK}\n") + if(QT_APPLE_SDK) + string(APPEND content "QMAKE_MAC_SDK = ${QT_APPLE_SDK}\n") endif() set(gcc_machine_dump "") diff --git a/cmake/QtProcessConfigureArgs.cmake b/cmake/QtProcessConfigureArgs.cmake index 8dd72ca5ea..df0dfe48de 100644 --- a/cmake/QtProcessConfigureArgs.cmake +++ b/cmake/QtProcessConfigureArgs.cmake @@ -85,11 +85,16 @@ list(TRANSFORM configure_args STRIP) unset(generator) set(auto_detect_compiler TRUE) set(auto_detect_generator ${qtbase_or_top_level_build}) +set(no_prefix_option FALSE) unset(device_options) unset(options_json_file) set_property(GLOBAL PROPERTY UNHANDLED_ARGS "") while(NOT "${configure_args}" STREQUAL "") - list(POP_FRONT configure_args arg) + list(POP_FRONT configure_args raw_arg) + + # Condense '--foo-bar' arguments into '-foo-bar'. + string(REGEX REPLACE "^--([^-])" "-\\1" arg "${raw_arg}") + if(arg STREQUAL "-cmake-generator") list(POP_FRONT configure_args generator) elseif(arg STREQUAL "-cmake-use-default-generator") @@ -110,6 +115,20 @@ while(NOT "${configure_args}" STREQUAL "") foreach(qtrepo IN LISTS qtrepos) push("-DBUILD_${qtrepo}=OFF") endforeach() + elseif(arg STREQUAL "-skip-tests") + list(POP_FRONT configure_args qtrepos) + is_non_empty_valid_arg("${arg}" "${qtrepos}") + list(TRANSFORM qtrepos REPLACE "," ";") + foreach(qtrepo IN LISTS qtrepos) + push("-DQT_BUILD_TESTS_PROJECT_${qtrepo}=OFF") + endforeach() + elseif(arg STREQUAL "-skip-examples") + list(POP_FRONT configure_args qtrepos) + is_non_empty_valid_arg("${arg}" "${qtrepos}") + list(TRANSFORM qtrepos REPLACE "," ";") + foreach(qtrepo IN LISTS qtrepos) + push("-DQT_BUILD_EXAMPLES_PROJECT_${qtrepo}=OFF") + endforeach() elseif(arg STREQUAL "-submodules") warn_in_per_repo_build("${arg}") list(POP_FRONT configure_args submodules) @@ -127,8 +146,10 @@ while(NOT "${configure_args}" STREQUAL "") push("-DINSTALL_MKSPECSDIR=${path}") elseif(arg STREQUAL "-developer-build") set(developer_build TRUE) - # Treat this argument as "unhandled" to process it further. - set_property(GLOBAL APPEND PROPERTY UNHANDLED_ARGS "${arg}") + push("-DFEATURE_developer_build=ON") + elseif(arg STREQUAL "-no-prefix") + set(no_prefix_option TRUE) + push("-DFEATURE_no_prefix=ON") elseif(arg STREQUAL "-cmake-file-api") set(cmake_file_api TRUE) elseif(arg STREQUAL "-no-cmake-file-api") @@ -139,14 +160,7 @@ while(NOT "${configure_args}" STREQUAL "") list(POP_FRONT configure_args version) is_valid_qt_hex_version("${arg}" "${version}") push("-DQT_DISABLE_DEPRECATED_UP_TO=${version}") - elseif(arg STREQUAL "-unity-build") - push("-DQT_UNITY_BUILD=ON") - # QT_UNITY_BUILD_BATCH_SIZE will be set to 8, CMake's default. - elseif(arg STREQUAL "-unity-build-batch-size") - list(POP_FRONT configure_args unity_build_batch_size) - is_non_empty_valid_arg("${arg}" "${unity_build_batch_size}") - push("-DQT_UNITY_BUILD_BATCH_SIZE=${unity_build_batch_size}") - elseif(arg STREQUAL "--") + elseif(raw_arg STREQUAL "--") # Everything after this argument will be passed to CMake verbatim. list(APPEND cmake_args "${configure_args}") break() @@ -155,6 +169,41 @@ while(NOT "${configure_args}" STREQUAL "") endif() endwhile() +# Read the specified manually generator value from CMake command line. +# The '-cmake-generator' argument has higher priority than CMake command line. +if(NOT generator) + set(is_next_arg_generator_name FALSE) + foreach(arg IN LISTS cmake_args) + if(is_next_arg_generator_name) + set(is_next_arg_generator_name FALSE) + if(NOT arg MATCHES "^-.*") + set(generator "${arg}") + set(auto_detect_generator FALSE) + endif() + elseif(arg MATCHES "^-G(.*)") + set(generator "${CMAKE_MATCH_1}") + if(generator) + set(auto_detect_generator FALSE) + else() + set(is_next_arg_generator_name TRUE) + endif() + endif() + endforeach() +endif() + +# Attempt to detect the generator type, either single or multi-config +if("${generator}" STREQUAL "Xcode" + OR "${generator}" STREQUAL "Ninja Multi-Config" + OR "${generator}" MATCHES "^Visual Studio") + set(multi_config ON) +else() + set(multi_config OFF) +endif() + +# Tell the build system we are configuring via the configure script so we can act on that. +# The cache variable is unset at the end of configuration. +push("-DQT_INTERNAL_CALLED_FROM_CONFIGURE:BOOL=TRUE") + if(FRESH_REQUESTED) push("-DQT_INTERNAL_FRESH_REQUESTED:BOOL=TRUE") if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24") @@ -230,16 +279,23 @@ macro(qt_commandline_custom handler) endmacro() function(qt_commandline_option name) - set(options) + set(options CONTROLS_FEATURE) set(oneValueArgs TYPE NAME VALUE) set(multiValueArgs VALUES MAPPING) cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(commandline_known_options "${commandline_known_options};${name}" PARENT_SCOPE) set(commandline_option_${name} "${arg_TYPE}" PARENT_SCOPE) + set(input_name ${name}) if(NOT "${arg_NAME}" STREQUAL "") + set(input_name ${arg_NAME}) set(commandline_option_${name}_variable "${arg_NAME}" PARENT_SCOPE) endif() + set(mapping_type "${arg_TYPE}") + if(arg_CONTROLS_FEATURE) + set(mapping_type "boolean") + endif() + set_property(GLOBAL PROPERTY INPUTTYPE_${input_name} "${mapping_type}") if(NOT "${arg_VALUE}" STREQUAL "") set(commandline_option_${name}_value "${arg_VALUE}" PARENT_SCOPE) endif() @@ -250,6 +306,11 @@ function(qt_commandline_option name) endif() endfunction() +# Add the common command line options for every qt repo. +macro(qt_add_common_commandline_options) + qt_commandline_option(headersclean TYPE boolean) +endmacro() + function(qt_commandline_prefix arg var) set(idx ${commandline_nr_of_prefixes}) set(commandline_prefix_${idx} "${arg}" "${var}" PARENT_SCOPE) @@ -266,6 +327,8 @@ set(QT_CONFIGURE_RUNNING ON) # Load qt_cmdline.cmake files #################################################################################################### +qt_add_common_commandline_options() + while(commandline_files) list(POP_FRONT commandline_files commandline_file) get_filename_component(commandline_file_directory "${commandline_file}" DIRECTORY) @@ -330,6 +393,23 @@ function(qtConfCommandlineAppendInput name val) qtConfCommandlineSetInput(${name} "${val}") endfunction() +function(qtConfCommandlineSetInputType input_name type_name) + set_property(GLOBAL PROPERTY INPUTTYPE_${input_name} "${type_name}") +endfunction() + +function(qtConfCommandlineSetBooleanInput name val) + qtConfCommandlineSetInput(${name} ${val}) + qtConfCommandlineSetInputType(${name} boolean) +endfunction() + +function(qtConfCommandlineEnableFeature name) + qtConfCommandlineSetBooleanInput(${name} yes) +endfunction() + +function(qtConfCommandlineDisableFeature name) + qtConfCommandlineSetBooleanInput(${name} no) +endfunction() + function(qtConfValidateValue opt val out_var) set(${out_var} TRUE PARENT_SCOPE) @@ -346,7 +426,9 @@ function(qtConfValidateValue opt val out_var) endforeach() set(${out_var} FALSE PARENT_SCOPE) - qtConfAddError("Invalid value '${val}' supplied to command line option '${opt}'.") + list(JOIN valid_values " " valid_values_str) + qtConfAddError("Invalid value '${val}' supplied to command line option '${opt}'." + "\nAllowed values: ${valid_values_str}\n") endfunction() function(qt_commandline_mapped_enum_value opt key out_var) @@ -634,10 +716,20 @@ while(1) if(arg MATCHES "^--?enable-(.*)") set(opt "${CMAKE_MATCH_1}") set(val "yes") - # Handle -no-prefix so it's not interpreted as the negation of -prefix - elseif(arg MATCHES "-(no-prefix)") - set(opt "${CMAKE_MATCH_1}") - set(val "") + # Handle builtin [-no]-feature-xxx + elseif(arg MATCHES "^--?(no-)?feature-(.*)") + set(opt "${CMAKE_MATCH_2}") + if(NOT opt IN_LIST commandline_known_features) + qtConfAddError("Enabling/Disabling unknown feature '${opt}'.") + endif() + if("${CMAKE_MATCH_1}" STREQUAL "") + set(val "ON") + else() + set(val "OFF") + endif() + qt_feature_normalize_name("${opt}" normalized_feature_name) + push(-DFEATURE_${normalized_feature_name}=${val}) + continue() elseif(arg MATCHES "^--?(disable|no)-(.*)") set(opt "${CMAKE_MATCH_2}") set(val "no") @@ -654,7 +746,6 @@ while(1) if(NOT DEFINED commandline_option_${opt} AND opt MATCHES "(qt|system)-(.*)") set(opt "${CMAKE_MATCH_2}") set(val "${CMAKE_MATCH_1}") - message("opt: ${opt} val: ${val}") endif() else() qtConfAddError("Invalid command line parameter '${arg}'.") @@ -675,15 +766,6 @@ while(1) endforeach() endif() - # Handle builtin [-no]-feature-xxx - if("${type}" STREQUAL "" AND opt MATCHES "^feature-(.*)") - set(opt "${CMAKE_MATCH_1}") - if(NOT opt IN_LIST commandline_known_features) - qtConfAddError("Enabling/Disabling unknown feature '${opt}'.") - endif() - set(type boolean) - endif() - if("${type}" STREQUAL "") qtConfAddError("Unknown command line option '${arg}'.") endif() @@ -703,6 +785,9 @@ get_property(config_inputs GLOBAL PROPERTY CONFIG_INPUTS) list(REMOVE_DUPLICATES config_inputs) foreach(var ${config_inputs}) get_property(INPUT_${var} GLOBAL PROPERTY INPUT_${var}) + if("${commandline_input_type}" STREQUAL "") + get_property(commandline_input_${var}_type GLOBAL PROPERTY INPUTTYPE_${var}) + endif() endforeach() macro(drop_input name) @@ -798,7 +883,7 @@ function(guess_compiler_from_mkspec) push("-DCMAKE_CXX_COMPILER=${cxx_compiler}") endif() if(mkspec MATCHES "-libc\\+\\+$") - push("-DINPUT_stdlib_libcpp=ON") + push("-DFEATURE_stdlib_libcpp=ON") endif() set(cmake_args "${cmake_args}" PARENT_SCOPE) endfunction() @@ -830,9 +915,13 @@ endfunction() drop_input(commercial) drop_input(confirm-license) translate_boolean_input(precompile_header BUILD_WITH_PCH) +translate_boolean_input(unity_build QT_UNITY_BUILD) +translate_string_input(unity_build_batch_size QT_UNITY_BUILD_BATCH_SIZE) translate_boolean_input(ccache QT_USE_CCACHE) +translate_boolean_input(vcpkg QT_USE_VCPKG) translate_boolean_input(shared BUILD_SHARED_LIBS) translate_boolean_input(warnings_are_errors WARNINGS_ARE_ERRORS) +translate_boolean_input(qtinlinenamespace QT_INLINE_NAMESPACE) translate_string_input(qt_namespace QT_NAMESPACE) translate_string_input(qt_libinfix QT_LIBINFIX) translate_string_input(qreal QT_COORD_TYPE) @@ -854,6 +943,7 @@ translate_string_input(platform QT_QMAKE_TARGET_MKSPEC) translate_string_input(xplatform QT_QMAKE_TARGET_MKSPEC) guess_compiler_from_mkspec() translate_string_input(qpa_default_platform QT_QPA_DEFAULT_PLATFORM) +translate_list_input(qpa_platforms QT_QPA_PLATFORMS) translate_path_input(android-sdk ANDROID_SDK_ROOT) translate_path_input(android-ndk ANDROID_NDK_ROOT) @@ -871,15 +961,11 @@ endif() translate_string_input(android-javac-source QT_ANDROID_JAVAC_SOURCE) translate_string_input(android-javac-target QT_ANDROID_JAVAC_TARGET) -# FIXME: config_help.txt says -sdk should apply to macOS as well. -translate_string_input(sdk QT_UIKIT_SDK) -if(DEFINED INPUT_sdk OR (DEFINED INPUT_xplatform AND INPUT_xplatform STREQUAL "macx-ios-clang") - OR (DEFINED INPUT_platform AND INPUT_platform STREQUAL "macx-ios-clang")) - push("-DCMAKE_SYSTEM_NAME=iOS") -endif() +translate_string_input(sdk QT_APPLE_SDK) 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) @@ -900,10 +986,33 @@ if(INPUT_force_debug_info) list(TRANSFORM build_configs REPLACE "^Release$" "RelWithDebInfo") endif() +# Code coverage handling +drop_input(gcov) +if(INPUT_gcov) + if(NOT "${INPUT_coverage}" STREQUAL "") + if(NOT "${INPUT_coverage}" STREQUAL "gcov") + qtConfAddError("The -gcov argument is provided, but -coverage is set" + " to ${INPUT_coverage}") + endif() + else() + set(INPUT_coverage "gcov") + list(APPEND config_inputs coverage) + endif() +endif() +if(NOT "${INPUT_coverage}" STREQUAL "") + if(build_configs) + if(NOT "Debug" IN_LIST build_configs) + qtConfAddError("The -coverage argument requires Qt configured with 'Debug' config.") + endif() + else() + set(build_configs "Debug") + endif() +endif() + list(LENGTH build_configs nr_of_build_configs) -if(nr_of_build_configs EQUAL 1) +if(nr_of_build_configs EQUAL 1 AND NOT multi_config) push("-DCMAKE_BUILD_TYPE=${build_configs}") -elseif(nr_of_build_configs GREATER 1) +elseif(nr_of_build_configs GREATER 1 OR multi_config) set(multi_config ON) string(REPLACE ";" "[[;]]" escaped_build_configs "${build_configs}") # We must not use the push macro here to avoid variable expansion. @@ -934,12 +1043,20 @@ if(cmake_file_api OR (developer_build AND NOT DEFINED cmake_file_api)) endforeach() endif() +# Translate unhandled input variables to either -DINPUT_foo=value or -DFEATURE_foo=ON/OFF. If the +# input's name matches a feature name and the corresponding command-line option's type is boolean +# then we assume it's controlling a feature. foreach(input ${config_inputs}) qt_feature_normalize_name("${input}" cmake_input) - push("-DINPUT_${cmake_input}=${INPUT_${input}}") + if("${commandline_input_${input}_type}" STREQUAL "boolean" + AND input IN_LIST commandline_known_features) + translate_boolean_input("${input}" "FEATURE_${cmake_input}") + else() + push("-DINPUT_${cmake_input}=${INPUT_${input}}") + endif() endforeach() -if(DEFINED INPUT_no-prefix AND DEFINED INPUT_prefix) +if(no_prefix_option AND DEFINED INPUT_prefix) qtConfAddError("Can't specify both -prefix and -no-prefix options at the same time.") endif() diff --git a/cmake/QtPublicAppleHelpers.cmake b/cmake/QtPublicAppleHelpers.cmake index e0d10531e5..5f7a7719b5 100644 --- a/cmake/QtPublicAppleHelpers.cmake +++ b/cmake/QtPublicAppleHelpers.cmake @@ -61,13 +61,7 @@ function(_qt_internal_handle_ios_launch_screen target) file(MAKE_DIRECTORY "${launch_screen_out_dir}") - # Replaces the value in the default template. - set(QT_IOS_LAUNCH_SCREEN_TEXT "${target}") - configure_file( - "${launch_screen_in_path}" - "${launch_screen_out_path}" - @ONLY - ) + configure_file("${launch_screen_in_path}" "${launch_screen_out_path}" COPYONLY) set(final_launch_screen_path "${launch_screen_out_path}") else() @@ -283,13 +277,15 @@ function(_qt_internal_get_default_apple_bundle_identifier target out_var) string(APPEND prefix ".${infix}") endif() - message(WARNING - "No organization bundle identifier prefix could be retrieved from Xcode preferences. \ - This can lead to code signing issues due to a non-unique bundle \ - identifier. Please set up an organization prefix by creating a new project within \ - Xcode, or consider providing a custom bundle identifier by specifying the \ - MACOSX_BUNDLE_GUI_IDENTIFIER or XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER property." - ) + if(CMAKE_GENERATOR STREQUAL "Xcode") + message(WARNING + "No organization bundle identifier prefix could be retrieved from Xcode preferences. \ + This can lead to code signing issues due to a non-unique bundle \ + identifier. Please set up an organization prefix by creating a new project within \ + Xcode, or consider providing a custom bundle identifier by specifying the \ + MACOSX_BUNDLE_GUI_IDENTIFIER or XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER property." + ) + endif() endif() # Escape the prefix according to rfc 1034, it's important for code-signing. If an invalid @@ -616,9 +612,12 @@ function(_qt_internal_set_apple_localizations target) return() endif() - get_target_property(supported_languages "${target}" _qt_apple_supported_languages) - if("${supported_languages}" STREQUAL "supported_languages-NOTFOUND") - return() + set(supported_languages "${QT_I18N_TRANSLATED_LANGUAGES}") + if("${QT_I18N_TRANSLATED_LANGUAGES}" STREQUAL "") + get_target_property(supported_languages "${target}" _qt_apple_supported_languages) + if("${supported_languages}" STREQUAL "supported_languages-NOTFOUND") + return() + endif() endif() get_target_property(plist_file "${target}" MACOSX_BUNDLE_INFO_PLIST) if (NOT plist_file) @@ -670,8 +669,235 @@ function(_qt_internal_set_ios_simulator_arch target) "x86_64") endfunction() +# Export Apple platform sdk and xcode version requirements to Qt6ConfigExtras.cmake. +function(_qt_internal_export_apple_sdk_and_xcode_version_requirements out_var) + if(NOT APPLE) + return() + endif() + + if(IOS) + set(vars_to_assign + QT_SUPPORTED_MIN_IOS_SDK_VERSION + QT_SUPPORTED_MAX_IOS_SDK_VERSION + QT_SUPPORTED_MIN_IOS_XCODE_VERSION + ) + elseif(VISIONOS) + set(vars_to_assign + QT_SUPPORTED_MIN_VISIONOS_SDK_VERSION + QT_SUPPORTED_MAX_VISIONOS_SDK_VERSION + QT_SUPPORTED_MIN_VISIONOS_XCODE_VERSION + ) + else() + set(vars_to_assign + QT_SUPPORTED_MIN_MACOS_SDK_VERSION + QT_SUPPORTED_MAX_MACOS_SDK_VERSION + QT_SUPPORTED_MIN_MACOS_XCODE_VERSION + ) + endif() + + set(assignments "") + foreach(var IN LISTS vars_to_assign) + set(value "${${var}}") + list(APPEND assignments + " +if(NOT ${var}) + set(${var} \"${value}\") +endif()") + endforeach() + + list(JOIN assignments "\n" assignments) + set(${out_var} "${assignments}" PARENT_SCOPE) +endfunction() + +function(_qt_internal_get_apple_sdk_version out_var) + if(APPLE) + if(CMAKE_SYSTEM_NAME STREQUAL iOS) + set(sdk_name "iphoneos") + elseif(CMAKE_SYSTEM_NAME STREQUAL visionOS) + set(sdk_name "xros") + else() + # Default to macOS + set(sdk_name "macosx") + endif() + set(xcrun_version_arg "--show-sdk-version") + execute_process(COMMAND /usr/bin/xcrun --sdk ${sdk_name} ${xcrun_version_arg} + OUTPUT_VARIABLE sdk_version + ERROR_VARIABLE xcrun_error) + if(NOT sdk_version) + message(FATAL_ERROR + "Can't determine darwin ${sdk_name} SDK version. Error: ${xcrun_error}") + endif() + string(STRIP "${sdk_version}" sdk_version) + set(${out_var} "${sdk_version}" PARENT_SCOPE) + endif() +endfunction() + +function(_qt_internal_get_xcode_version_raw out_var) + if(APPLE) + execute_process(COMMAND /usr/bin/xcrun xcodebuild -version + OUTPUT_VARIABLE xcode_version + ERROR_VARIABLE xcrun_error) + string(REPLACE "\n" " " xcode_version "${xcode_version}") + string(STRIP "${xcode_version}" xcode_version) + set(${out_var} "${xcode_version}" PARENT_SCOPE) + endif() +endfunction() + +function(_qt_internal_get_xcode_version out_var) + if(APPLE) + _qt_internal_get_xcode_version_raw(xcode_version_raw) + + # The raw output is something like after the newlines are replaced with spaces: + # Xcode 14.3 Build version 14E222b + # We want only the '14.3' part. We could be more specific with the regex to match only + # digits separated by dots, but you never know how Apple might change the format. + string(REGEX REPLACE "Xcode (([^ ])+)" "\\2" xcode_version "${xcode_version_raw}") + if(xcode_version_raw MATCHES "Xcode ([^ ]+)") + set(xcode_version "${CMAKE_MATCH_1}") + else() + message(DEBUG "Failed to extract Xcode version from '${xcode_version_raw}'") + set(xcode_version "${xcode_version_raw}") + endif() + + set(${out_var} "${xcode_version}" PARENT_SCOPE) + endif() +endfunction() + +function(_qt_internal_get_cached_apple_sdk_version out_var) + if(QT_INTERNAL_APPLE_SDK_VERSION) + set(sdk_version "${QT_INTERNAL_APPLE_SDK_VERSION}") + else() + _qt_internal_get_apple_sdk_version(sdk_version) + set(QT_INTERNAL_APPLE_SDK_VERSION "${sdk_version}" CACHE STRING "Apple SDK version") + endif() + + set(${out_var} "${sdk_version}" PARENT_SCOPE) +endfunction() + +function(_qt_internal_get_cached_xcode_version out_var) + if(QT_INTERNAL_XCODE_VERSION) + set(xcode_version "${QT_INTERNAL_XCODE_VERSION}") + else() + _qt_internal_get_xcode_version(xcode_version) + set(QT_INTERNAL_XCODE_VERSION "${xcode_version}" CACHE STRING "Xcode version") + endif() + + set(${out_var} "${xcode_version}" PARENT_SCOPE) +endfunction() + +# Warn when the platform SDK or Xcode version are not supported. +# +# The warnings are currently only shown when building Qt, not when building user projects +# with CMake. +# The warnings ARE shown for qmake user projects. +# +# The qmake equivalent for user projects is in mkspecs/features/mac/default_post.prf. +function(_qt_internal_check_apple_sdk_and_xcode_versions) + if(NOT APPLE) + return() + endif() + + if(QT_NO_APPLE_SDK_AND_XCODE_CHECK) + return() + endif() + + # Only run the check once in a top-level build. + get_property(check_done GLOBAL PROPERTY _qt_internal_apple_sdk_and_xcode_check_done) + if(check_done) + return() + endif() + set_property(GLOBAL PROPERTY _qt_internal_apple_sdk_and_xcode_check_done "TRUE") + + if(IOS) + set(min_sdk_version "${QT_SUPPORTED_MIN_IOS_SDK_VERSION}") + set(max_sdk_version "${QT_SUPPORTED_MAX_IOS_SDK_VERSION}") + set(min_xcode_version "${QT_SUPPORTED_MIN_IOS_XCODE_VERSION}") + elseif(VISIONOS) + set(min_sdk_version "${QT_SUPPORTED_MIN_VISIONOS_SDK_VERSION}") + set(max_sdk_version "${QT_SUPPORTED_MAX_VISIONOS_SDK_VERSION}") + set(min_xcode_version "${QT_SUPPORTED_MIN_VISIONOS_XCODE_VERSION}") + else() + set(min_sdk_version "${QT_SUPPORTED_MIN_MACOS_SDK_VERSION}") + set(max_sdk_version "${QT_SUPPORTED_MAX_MACOS_SDK_VERSION}") + set(min_xcode_version "${QT_SUPPORTED_MIN_MACOS_XCODE_VERSION}") + endif() + + _qt_internal_get_cached_apple_sdk_version(sdk_version) + _qt_internal_get_cached_xcode_version(xcode_version) + + if(NOT max_sdk_version MATCHES "^[0-9]+$") + message(FATAL_ERROR + "Invalid max SDK version: ${max_sdk_version} " + "It should be a major version number, without minor or patch version components.") + endif() + + # The default differs in different branches. + set(failed_check_should_error FALSE) + + if(failed_check_should_error) + # Allow downgrading the error into a warning. + if(QT_FORCE_WARN_APPLE_SDK_AND_XCODE_CHECK) + set(message_type WARNING) + set(extra_message " Due to QT_FORCE_WARN_APPLE_SDK_AND_XCODE_CHECK being ON " + "the build will continue, but it will likely fail. Use at your own risk.") + else() + set(message_type FATAL_ERROR) + set(extra_message " You can turn this error into a warning by configuring with " + "-DQT_FORCE_WARN_APPLE_SDK_AND_XCODE_CHECK=ON, but the build will likely fail. " + "Use at your own risk.") + endif() + else() + # Allow upgrading the warning into an error. + if(QT_FORCE_FATAL_APPLE_SDK_AND_XCODE_CHECK) + set(message_type FATAL_ERROR) + set(extra_message " Erroring out due to QT_FORCE_FATAL_APPLE_SDK_AND_XCODE_CHECK " + "being ON.") + else() + set(message_type WARNING) + set(extra_message " You can turn this warning into an error by configuring with " + "-DQT_FORCE_FATAL_APPLE_SDK_AND_XCODE_CHECK=ON. ") + endif() + endif() + + if(sdk_version VERSION_LESS min_sdk_version AND NOT QT_NO_APPLE_SDK_MIN_VERSION_CHECK) + message(${message_type} + "Qt requires at least version ${min_sdk_version} of the platform SDK, " + "you're building against version ${sdk_version}. Please upgrade." + ${extra_message} + ) + endif() + + if(xcode_version VERSION_LESS min_xcode_version AND NOT QT_NO_XCODE_MIN_VERSION_CHECK) + message(${message_type} + "Qt requires at least version ${min_xcode_version} of Xcode, " + "you're building against version ${xcode_version}. Please upgrade." + ${extra_message} + ) + endif() + + if(QT_NO_APPLE_SDK_MAX_VERSION_CHECK) + return() + endif() + + # Make sure we warn only when the current version is greater than the max supported version. + math(EXPR next_after_max_sdk_version "${max_sdk_version} + 1") + if(sdk_version VERSION_GREATER_EQUAL next_after_max_sdk_version) + message(WARNING + "Qt has only been tested with version ${max_sdk_version} " + "of the platform SDK, you're using ${sdk_version}. " + "This is an unsupported configuration. You may experience build issues, " + "and by using " + "the ${sdk_version} SDK you are opting in to new features " + "that Qt has not been prepared for. " + "Please downgrade the SDK you use to build your app to version " + "${max_sdk_version}, or configure " + "with -DQT_NO_APPLE_SDK_MAX_VERSION_CHECK=ON to silence this warning." + ) + endif() +endfunction() + function(_qt_internal_finalize_apple_app target) - # Shared between macOS and iOS apps + # Shared between macOS and UIKit apps _qt_internal_copy_info_plist("${target}") _qt_internal_set_apple_localizations("${target}") @@ -691,6 +917,14 @@ function(_qt_internal_finalize_apple_app target) _qt_internal_set_placeholder_apple_bundle_version("${target}") endfunction() +function(_qt_internal_finalize_uikit_app target) + if(CMAKE_SYSTEM_NAME STREQUAL iOS) + _qt_internal_finalize_ios_app("${target}") + else() + _qt_internal_finalize_apple_app("${target}") + endif() +endfunction() + function(_qt_internal_finalize_ios_app target) # Must be called before we generate the Info.plist _qt_internal_handle_ios_launch_screen("${target}") diff --git a/cmake/QtPublicCMakeHelpers.cmake b/cmake/QtPublicCMakeHelpers.cmake index 2e469da8cb..1a9cdc5a07 100644 --- a/cmake/QtPublicCMakeHelpers.cmake +++ b/cmake/QtPublicCMakeHelpers.cmake @@ -25,10 +25,13 @@ endfunction() # The function checks if add_custom_command has the support of the DEPFILE argument. function(_qt_internal_check_depfile_support out_var) if(CMAKE_GENERATOR MATCHES "Ninja" OR - CMAKE_VERSION VERSION_GREATER_EQUAL 3.20 AND CMAKE_GENERATOR MATCHES "Makefiles" - OR CMAKE_VERSION VERSION_GREATER_EQUAL 3.21 + (CMAKE_VERSION VERSION_GREATER_EQUAL 3.20 AND CMAKE_GENERATOR MATCHES "Makefiles") + OR (CMAKE_VERSION VERSION_GREATER_EQUAL 3.21 AND (CMAKE_GENERATOR MATCHES "Xcode" - OR CMAKE_GENERATOR MATCHES "Visual Studio ([0-9]+)" AND CMAKE_MATCH_1 GREATER_EQUAL 12)) + OR (CMAKE_GENERATOR MATCHES "Visual Studio ([0-9]+)" AND CMAKE_MATCH_1 GREATER_EQUAL 12) + ) + ) + ) set(${out_var} TRUE) else() set(${out_var} FALSE) @@ -36,6 +39,65 @@ function(_qt_internal_check_depfile_support out_var) set(${out_var} "${${out_var}}" PARENT_SCOPE) endfunction() +# Checks if the path points to the cmake directory, like lib/cmake. +function(__qt_internal_check_path_points_to_cmake_dir result path) + string(TOUPPER "${QT_CMAKE_EXPORT_NAMESPACE}" export_namespace_upper) + if((INSTALL_LIBDIR AND path MATCHES "/${INSTALL_LIBDIR}/cmake$") OR + (${export_namespace_upper}_INSTALL_LIBS AND + path MATCHES "/${${export_namespace_upper}_INSTALL_LIBS}/cmake$") OR + path MATCHES "/lib/cmake$" + ) + set(${result} TRUE PARENT_SCOPE) + else() + set(${result} FALSE PARENT_SCOPE) + endif() +endfunction() + +# Creates a reverse path to prefix from possible cmake directories. Returns the unchanged path +# if it doesn't point to cmake directory. +function(__qt_internal_reverse_prefix_path_from_cmake_dir result cmake_path) + string(TOUPPER "${QT_CMAKE_EXPORT_NAMESPACE}" export_namespace_upper) + if(INSTALL_LIBDIR AND cmake_path MATCHES "(.+)/${INSTALL_LIBDIR}/cmake$") + if(CMAKE_MATCH_1) + set(${result} "${CMAKE_MATCH_1}" PARENT_SCOPE) + endif() + elseif(${export_namespace_upper}_INSTALL_LIBS AND + cmake_path MATCHES "(.+)/${${export_namespace_upper}_INSTALL_LIBS}/cmake$") + if(CMAKE_MATCH_1) + set(${result} "${CMAKE_MATCH_1}" PARENT_SCOPE) + endif() + elseif(result MATCHES "(.+)/lib/cmake$") + if(CMAKE_MATCH_1) + set(${result} "${CMAKE_MATCH_1}" PARENT_SCOPE) + endif() + else() + set(${result} "${cmake_path}" PARENT_SCOPE) + endif() +endfunction() + +# Returns the possible cmake directories based on prefix_path. +function(__qt_internal_get_possible_cmake_dirs out_paths prefix_path) + set(${out_paths} "") + + if(EXISTS "${prefix_path}/lib/cmake") + list(APPEND ${out_paths} "${prefix_path}/lib/cmake") + endif() + + string(TOUPPER "${QT_CMAKE_EXPORT_NAMESPACE}" export_namespace_upper) + set(next_path "${prefix_path}/${${export_namespace_upper}_INSTALL_LIBS}/cmake") + if(${export_namespace_upper}_INSTALL_LIBS AND EXISTS "${next_path}") + list(APPEND ${out_paths} "${next_path}") + endif() + + set(next_path "${prefix_path}/${INSTALL_LIBDIR}/cmake") + if(INSTALL_LIBDIR AND EXISTS "${next_path}") + list(APPEND ${out_paths} "${next_path}") + endif() + + list(REMOVE_DUPLICATES ${out_paths}) + set(${out_paths} "${${out_paths}}" PARENT_SCOPE) +endfunction() + # Collect additional package prefix paths to look for Qt packages, both from command line and the # env variable ${prefixes_var}. The result is stored in ${out_var} and is a list of paths ending # with "/lib/cmake". @@ -69,25 +131,530 @@ function(__qt_internal_collect_additional_prefix_paths out_var prefixes_var) # NO_DEFAULT_PATH, and thus CMAKE_PREFIX_PATH values are discarded. # CMAKE_FIND_ROOT_PATH values are not discarded and togegher with the PATHS option, it # ensures packages from additional prefixes are found. - if(NOT additional_path MATCHES "/lib/cmake$") - string(APPEND additional_path "/lib/cmake") + __qt_internal_check_path_points_to_cmake_dir(is_path_to_cmake "${additional_path}") + if(is_path_to_cmake) + list(APPEND additional_packages_prefix_paths "${additional_path}") + else() + __qt_internal_get_possible_cmake_dirs(additional_cmake_dirs "${additional_path}") + list(APPEND additional_packages_prefix_paths ${additional_cmake_dirs}) endif() - list(APPEND additional_packages_prefix_paths "${additional_path}") endforeach() set("${out_var}" "${additional_packages_prefix_paths}" PARENT_SCOPE) endfunction() +# Collects CMAKE_MODULE_PATH from QT_ADDITIONAL_PACKAGES_PREFIX_PATH +function(__qt_internal_collect_additional_module_paths) + if(__qt_additional_module_paths_set) + return() + endif() + foreach(prefix_path IN LISTS QT_ADDITIONAL_PACKAGES_PREFIX_PATH) + __qt_internal_check_path_points_to_cmake_dir(is_path_to_cmake "${prefix_path}") + if(is_path_to_cmake) + list(APPEND CMAKE_MODULE_PATH "${prefix_path}/${QT_CMAKE_EXPORT_NAMESPACE}") + else() + __qt_internal_get_possible_cmake_dirs(additional_cmake_dirs "${additional_path}") + list(TRANSFORM additional_cmake_dirs APPEND "/${QT_CMAKE_EXPORT_NAMESPACE}") + list(APPEND CMAKE_MODULE_PATH ${additional_cmake_dirs}) + endif() + endforeach() + list(REMOVE_DUPLICATES CMAKE_MODULE_PATH) + set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" PARENT_SCOPE) + set(__qt_additional_module_paths_set TRUE PARENT_SCOPE) +endfunction() + # Take a list of prefix paths ending with "/lib/cmake", and return a list of absolute paths with # "/lib/cmake" removed. function(__qt_internal_prefix_paths_to_roots out_var prefix_paths) set(result "") foreach(path IN LISTS prefix_paths) - if(path MATCHES "/lib/cmake$") - string(APPEND path "/../..") - endif() - get_filename_component(path "${path}" ABSOLUTE) + __qt_internal_reverse_prefix_path_from_cmake_dir(path "${path}") list(APPEND result "${path}") endforeach() set("${out_var}" "${result}" PARENT_SCOPE) endfunction() + +# This function gets all targets below this directory +# +# Multi-value Arguments: +# EXCLUDE list of target types that should be filtered from resulting list. +# +# INCLUDE list of target types that should be filtered from resulting list. +# EXCLUDE has higher priority than INCLUDE. +function(_qt_internal_collect_buildsystem_targets result dir) + cmake_parse_arguments(arg "" "" "EXCLUDE;INCLUDE" ${ARGN}) + + if(NOT _qt_internal_collect_buildsystem_targets_inner) + set(${result} "") + set(_qt_internal_collect_buildsystem_targets_inner TRUE) + endif() + + set(forward_args "") + if(arg_EXCLUDE) + set(forward_args APPEND EXCLUDE ${arg_EXCLUDE}) + endif() + + if(arg_INCLUDE) + set(forward_args APPEND INCLUDE ${arg_INCLUDE}) + endif() + + get_property(subdirs DIRECTORY "${dir}" PROPERTY SUBDIRECTORIES) + + # Make sure that we don't hit endless recursion when running qt-cmake-standalone-test on a + # in-source test dir, where the currently processed directory lists itself in its SUBDIRECTORIES + # property. + # See https://bugreports.qt.io/browse/QTBUG-119998 + # and https://gitlab.kitware.com/cmake/cmake/-/issues/25489 + # Do it only when QT_INTERNAL_IS_STANDALONE_TEST is set, to avoid the possible slowdown when + # processing many subdirectores when configuring all standalone tests rather than just one. + if(QT_INTERNAL_IS_STANDALONE_TEST) + list(REMOVE_ITEM subdirs "${dir}") + endif() + + foreach(subdir IN LISTS subdirs) + _qt_internal_collect_buildsystem_targets(${result} "${subdir}" ${forward_args}) + endforeach() + get_property(sub_targets DIRECTORY "${dir}" PROPERTY BUILDSYSTEM_TARGETS) + set(real_targets "") + if(sub_targets) + foreach(target IN LISTS sub_targets) + get_target_property(target_type ${target} TYPE) + if((NOT arg_INCLUDE OR target_type IN_LIST arg_INCLUDE) AND + (NOT arg_EXCLUDE OR (NOT target_type IN_LIST arg_EXCLUDE))) + list(APPEND real_targets ${target}) + endif() + endforeach() + endif() + set(${result} ${${result}} ${real_targets} PARENT_SCOPE) +endfunction() + +# Add a custom target ${target} that is *not* added to the default build target in a safe way. +# Dependencies must then be added with _qt_internal_add_phony_target_dependencies. +# +# What's "safe" in this context? For the Visual Studio generators, we cannot use add_dependencies, +# because this would enable the dependencies in the default build of the solution. See QTBUG-115166 +# and upstream CMake issue #16668 for details. Instead, we record the dependencies (added with +# _qt_internal_add_phony_target_dependencies) and create the target at the end of the top-level +# directory scope. +# +# This only works if at least CMake 3.19 is used. Older CMake versions will trigger a warning that +# can be turned off with the variable ${WARNING_VARIABLE}. +# +# For other generators, this is just a call to add_custom_target, unless the target already exists, +# followed by add_dependencies. +# +# Use this function for targets that are not part of the default build, i.e. that should be +# triggered by the user. +# +# TARGET_CREATED_HOOK is the name of a function that is called after the target has been created. +# It takes the target's name as first and only argument. +# +# Example: +# _qt_internal_add_phony_target(update_translations +# WARNING_VARIABLE QT_NO_GLOBAL_LUPDATE_TARGET_CREATED_WARNING +# ) +# _qt_internal_add_phony_target_dependencies(update_translations +# narf_lupdate_zort_lupdate +# ) +# +function(_qt_internal_add_phony_target target) + set(no_value_options "") + set(single_value_options + TARGET_CREATED_HOOK + WARNING_VARIABLE + ) + set(multi_value_options "") + cmake_parse_arguments(PARSE_ARGV 0 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + if("${arg_WARNING_VARIABLE}" STREQUAL "") + message(FATAL_ERROR "WARNING_VARIABLE must be provided.") + endif() + if(CMAKE_GENERATOR MATCHES "^Visual Studio ") + if(${CMAKE_VERSION} VERSION_LESS "3.19.0") + if(NOT ${${arg_WARNING_VARIABLE}}) + message(WARNING + "Cannot create target ${target} with this CMake version. " + "Please upgrade to CMake 3.19.0 or newer. " + "Set ${WARNING_VARIABLE} to ON to disable this warning." + ) + endif() + return() + endif() + + get_property(already_deferred GLOBAL PROPERTY _qt_target_${target}_creation_deferred) + if(NOT already_deferred) + cmake_language(EVAL CODE + "cmake_language(DEFER DIRECTORY \"${CMAKE_SOURCE_DIR}\" CALL _qt_internal_add_phony_target_deferred \"${target}\")" + ) + if(DEFINED arg_TARGET_CREATED_HOOK) + set_property(GLOBAL + PROPERTY _qt_target_${target}_creation_hook ${arg_TARGET_CREATED_HOOK} + ) + endif() + endif() + set_property(GLOBAL APPEND PROPERTY _qt_target_${target}_creation_deferred ON) + else() + if(NOT TARGET ${target}) + add_custom_target(${target}) + if(DEFINED arg_TARGET_CREATED_HOOK) + if(CMAKE_VERSION VERSION_LESS "3.19") + set(incfile + "${CMAKE_CURRENT_BINARY_DIR}/.qt_internal_add_phony_target.cmake" + ) + file(WRITE "${incfile}" "${arg_TARGET_CREATED_HOOK}(${target})") + include("${incfile}") + file(REMOVE "${incfile}") + else() + cmake_language(CALL "${arg_TARGET_CREATED_HOOK}" "${target}") + endif() + endif() + endif() + endif() +endfunction() + +# Adds dependencies to a custom target that has been created with +# _qt_internal_add_phony_target. See the docstring at _qt_internal_add_phony_target for +# more details. +function(_qt_internal_add_phony_target_dependencies target) + set(dependencies ${ARGN}) + if(CMAKE_GENERATOR MATCHES "^Visual Studio ") + set_property(GLOBAL APPEND PROPERTY _qt_target_${target}_dependencies ${dependencies}) + + # Exclude the dependencies from the solution's default build to avoid them being enabled + # accidentally should the user add another dependency to them. + set_target_properties(${dependencies} PROPERTIES EXCLUDE_FROM_DEFAULT_BUILD ON) + else() + add_dependencies(${target} ${dependencies}) + endif() +endfunction() + +# Hack for the Visual Studio generator. Create the custom target named ${target} and work +# around the lack of a working add_dependencies by calling 'cmake --build' for every dependency. +function(_qt_internal_add_phony_target_deferred target) + get_property(target_dependencies GLOBAL PROPERTY _qt_target_${target}_dependencies) + set(target_commands "") + foreach(dependency IN LISTS target_dependencies) + list(APPEND target_commands + COMMAND "${CMAKE_COMMAND}" --build "${CMAKE_BINARY_DIR}" -t ${dependency} + ) + endforeach() + add_custom_target(${target} ${target_commands}) + get_property(creation_hook GLOBAL PROPERTY _qt_target_${target}_creation_hook) + if(creation_hook) + cmake_language(CALL ${creation_hook} ${target}) + endif() +endfunction() + +# The helper function that checks if module was included multiple times, and has the inconsistent +# set of targets that belong to the module. It's expected that either all 'targets' or none of them +# will be written to the 'targets_not_defined' variable, if the module was not or was +# searched before accordingly. +function(_qt_internal_check_multiple_inclusion targets_not_defined targets) + set(targets_defined "") + set(${targets_not_defined} "") + set(expected_targets "") + foreach(expected_target ${targets}) + list(APPEND expected_targets ${expected_target}) + if(NOT TARGET Qt::${expected_target}) + list(APPEND ${targets_not_defined} ${expected_target}) + endif() + if(TARGET Qt::${expected_target}) + list(APPEND targets_defined ${expected_target}) + endif() + endforeach() + if("${targets_defined}" STREQUAL "${expected_targets}") + set(${targets_not_defined} "" PARENT_SCOPE) + return() + endif() + if(NOT "${targets_defined}" STREQUAL "") + message(FATAL_ERROR "Some (but not all) targets in this export set were already defined." + "\nTargets Defined: ${targets_defined}\nTargets not yet defined: " + "${${targets_not_defined}}\n" + ) + endif() + set(${targets_not_defined} "${${targets_not_defined}}" PARENT_SCOPE) +endfunction() + +# The function is used when creating version less targets using ALIASes. +function(_qt_internal_create_versionless_alias_targets targets install_namespace) + foreach(target IN LISTS targets) + add_library(Qt::${target} ALIAS ${install_namespace}::${target}) + endforeach() +endfunction() + +# The function is used when creating version less targets from scratch but not using ALIASes. +# It assigns the known properties from the versioned targets to the versionless created in this +# function. This allows versionless targets mimic the versioned. +function(_qt_internal_create_versionless_targets targets install_namespace) + set(known_interface_properties + QT_MAJOR_VERSION + AUTOMOC_MACRO_NAMES + AUTOUIC_OPTIONS + COMPILE_DEFINITIONS + COMPILE_FEATURES + COMPILE_OPTIONS + CXX_MODULE_SETS + HEADER_SETS + HEADER_SETS_TO_VERIFY + INCLUDE_DIRECTORIES + LINK_DEPENDS + LINK_DIRECTORIES + LINK_LIBRARIES + LINK_LIBRARIES_DIRECT + LINK_LIBRARIES_DIRECT_EXCLUDE + LINK_OPTIONS + POSITION_INDEPENDENT_CODE + PRECOMPILE_HEADERS + SOURCES + SYSTEM_INCLUDE_DIRECTORIES + ) + + set(known_qt_exported_properties + MODULE_PLUGIN_TYPES + QT_DISABLED_PRIVATE_FEATURES + QT_DISABLED_PUBLIC_FEATURES + QT_ENABLED_PRIVATE_FEATURES + QT_ENABLED_PUBLIC_FEATURES + QT_QMAKE_PRIVATE_CONFIG + QT_QMAKE_PUBLIC_CONFIG + QT_QMAKE_PUBLIC_QT_CONFIG + ) + + set(known_qt_exported_properties_interface_allowed + _qt_config_module_name + _qt_is_public_module + _qt_module_has_headers + _qt_module_has_private_headers + _qt_module_has_public_headers + _qt_module_has_qpa_headers + _qt_module_has_rhi_headers + _qt_module_include_name + _qt_module_interface_name + _qt_package_name + _qt_package_version + _qt_private_module_target_name + ) + + set(supported_target_types STATIC_LIBRARY MODULE_LIBRARY SHARED_LIBRARY OBJECT_LIBRARY + INTERFACE_LIBRARY) + + foreach(target IN LISTS targets) + if(NOT TARGET ${install_namespace}::${target}) + message(FATAL_ERROR "${install_namespace}::${target} is not a target, can not extend" + " an alias target") + endif() + + get_target_property(type ${install_namespace}::${target} TYPE) + if(NOT type) + message(FATAL_ERROR "Cannot get the ${install_namespace}::${target} target type.") + endif() + + if(NOT "${type}" IN_LIST supported_target_types) + message(AUTHOR_WARNING "${install_namespace}::${target} requires the versionless" + " target creation, but it has incompatible type ${type}.") + continue() + endif() + + string(REPLACE "_LIBRARY" "" creation_type "${type}") + add_library(Qt::${target} ${creation_type} IMPORTED) + + if(NOT "${type}" STREQUAL "INTERFACE_LIBRARY") + foreach(config "" _RELEASE _DEBUG _RELWITHDEBINFO _MINSIZEREL) + get_target_property(target_imported_location + ${install_namespace}::${target} IMPORTED_LOCATION${config}) + if(NOT target_imported_location) + if("${config}" STREQUAL "") + message(FATAL_ERROR "Cannot create versionless target for" + " ${install_namespace}::${target}. IMPORTED_LOCATION property is " + "missing." + ) + else() + continue() + endif() + endif() + set_property(TARGET Qt::${target} PROPERTY + IMPORTED_LOCATION${config} "${target_imported_location}") + endforeach() + + foreach(property IN LISTS known_qt_exported_properties) + get_target_property(exported_property_value + ${install_namespace}::${target} ${property}) + if(exported_property_value) + set_property(TARGET Qt::${target} APPEND PROPERTY + ${property} "${exported_property_value}") + endif() + endforeach() + endif() + + foreach(property IN LISTS known_interface_properties) + get_target_property(interface_property_value + ${install_namespace}::${target} INTERFACE_${property}) + if(interface_property_value) + set_property(TARGET Qt::${target} APPEND PROPERTY + INTERFACE_${property} "${interface_property_value}") + endif() + endforeach() + + foreach(property IN LISTS known_qt_exported_properties_interface_allowed) + get_target_property(exported_property_value + ${install_namespace}::${target} ${property}) + if(exported_property_value) + set_property(TARGET Qt::${target} APPEND PROPERTY + ${property} "${exported_property_value}") + endif() + endforeach() + + set_property(TARGET Qt::${target} PROPERTY _qt_is_versionless_target TRUE) + endforeach() +endfunction() + +# Checks whether any unparsed arguments have been passed to the function at the call site. +# Use this right after `cmake_parse_arguments`. +function(_qt_internal_validate_all_args_are_parsed prefix) + if(DEFINED ${prefix}_UNPARSED_ARGUMENTS) + message(FATAL_ERROR "Unknown arguments: (${${prefix}_UNPARSED_ARGUMENTS})") + endif() +endfunction() + +# Takes a list of path components and joins them into one path separated by forward slashes "/", +# and saves the path in out_var. +function(_qt_internal_path_join out_var) + string(JOIN "/" path ${ARGN}) + set(${out_var} ${path} PARENT_SCOPE) +endfunction() + +# _qt_internal_qt_remove_args can remove arguments from an existing list of function +# arguments in order to pass a filtered list of arguments to a different function. +# Parameters: +# out_var: result of remove all arguments specified by ARGS_TO_REMOVE from ALL_ARGS +# ARGS_TO_REMOVE: Arguments to remove. +# ALL_ARGS: All arguments supplied to cmake_parse_arguments +# from which ARGS_TO_REMOVE should be removed from. We require all the +# arguments or we can't properly identify the range of the arguments detailed +# in ARGS_TO_REMOVE. +# ARGS: Arguments passed into the function, usually ${ARGV} +# +# E.g.: +# We want to forward all arguments from foo to bar, execpt ZZZ since it will +# trigger an error in bar. +# +# foo(target BAR .... ZZZ .... WWW ...) +# bar(target BAR.... WWW...) +# +# function(foo target) +# cmake_parse_arguments(PARSE_ARGV 1 arg "" "" "BAR;ZZZ;WWW") +# qt_remove_args(forward_args +# ARGS_TO_REMOVE ${target} ZZZ +# ALL_ARGS ${target} BAR ZZZ WWW +# ARGS ${ARGV} +# ) +# bar(${target} ${forward_args}) +# endfunction() +# +function(_qt_internal_remove_args out_var) + cmake_parse_arguments(arg "" "" "ARGS_TO_REMOVE;ALL_ARGS;ARGS" ${ARGN}) + set(result ${arg_ARGS}) + foreach(arg IN LISTS arg_ARGS_TO_REMOVE) + # find arg + list(FIND result ${arg} find_result) + if (NOT find_result EQUAL -1) + # remove arg + list(REMOVE_AT result ${find_result}) + list(LENGTH result result_len) + if(find_result EQUAL result_len) + # We removed the last argument, could have been an option keyword + continue() + endif() + list(GET result ${find_result} arg_current) + # remove values until we hit another arg or the end of the list + while(NOT "${arg_current}" IN_LIST arg_ALL_ARGS AND find_result LESS result_len) + list(REMOVE_AT result ${find_result}) + list(LENGTH result result_len) + if (NOT find_result EQUAL result_len) + list(GET result ${find_result} arg_current) + endif() + endwhile() + endif() + endforeach() + set(${out_var} "${result}" PARENT_SCOPE) +endfunction() + +# Append ${ARGN} to ${target}'s ${property_name} property, removing duplicates. +function(_qt_internal_append_to_target_property_without_duplicates target property_name) + get_target_property(property "${target}" "${property_name}") + if(NOT property) + set(property "") + endif() + list(APPEND property ${ARGN}) + list(REMOVE_DUPLICATES property) + set_property(TARGET "${target}" PROPERTY "${property_name}" "${property}") +endfunction() + +# Append ${ARGN} to global CMake ${property_name} property, removing duplicates. +function(_qt_internal_append_to_cmake_property_without_duplicates property_name) + get_cmake_property(property "${property_name}") + if(NOT property) + set(property "") + endif() + list(APPEND property ${ARGN}) + list(REMOVE_DUPLICATES property) + set_property(GLOBAL PROPERTY "${property_name}" "${property}") +endfunction() + +# Helper function to forward options from one function to another. +# +# This is somewhat the opposite of _qt_internal_remove_args. +# +# Parameters: +# FORWARD_PREFIX is usually arg because we pass cmake_parse_arguments(PARSE_ARGV 0 arg) in most code +# FORWARD_OPTIONS, FORWARD_SINGLE, FORWARD_MULTI are the options that should be forwarded. +# +# The forwarded args will be either set in arg_FORWARD_OUT_VAR or appended if FORWARD_APPEND is set. +# +# The function reads the options like ${arg_FORWARD_PREFIX}_${option} in the parent scope. +function(_qt_internal_forward_function_args) + set(opt_args + FORWARD_APPEND + ) + set(single_args + FORWARD_PREFIX + ) + set(multi_args + FORWARD_OPTIONS + FORWARD_SINGLE + FORWARD_MULTI + FORWARD_OUT_VAR + ) + cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}") + _qt_internal_validate_all_args_are_parsed(arg) + + if(NOT arg_FORWARD_OUT_VAR) + message(FATAL_ERROR "FORWARD_OUT_VAR must be provided.") + endif() + + set(forward_args "") + foreach(option_name IN LISTS arg_FORWARD_OPTIONS) + if(${arg_FORWARD_PREFIX}_${option_name}) + list(APPEND forward_args "${option_name}") + endif() + endforeach() + + foreach(option_name IN LISTS arg_FORWARD_SINGLE) + if(${arg_FORWARD_PREFIX}_${option_name}) + list(APPEND forward_args "${option_name}" "${${arg_FORWARD_PREFIX}_${option_name}}") + endif() + endforeach() + + foreach(option_name IN LISTS arg_FORWARD_MULTI) + if(${arg_FORWARD_PREFIX}_${option_name}) + list(APPEND forward_args "${option_name}" ${${arg_FORWARD_PREFIX}_${option_name}}) + endif() + endforeach() + + if(arg_FORWARD_APPEND) + set(forward_args ${${arg_FORWARD_OUT_VAR}} "${forward_args}") + endif() + + set(${arg_FORWARD_OUT_VAR} "${forward_args}" PARENT_SCOPE) +endfunction() diff --git a/cmake/QtPublicDependencyHelpers.cmake b/cmake/QtPublicDependencyHelpers.cmake index de62eb1c10..bd8b4a55c4 100644 --- a/cmake/QtPublicDependencyHelpers.cmake +++ b/cmake/QtPublicDependencyHelpers.cmake @@ -112,9 +112,9 @@ macro(_qt_internal_find_qt_dependencies target target_dep_list find_dependency_p NAMES ${__qt_${target}_pkg_names} PATHS + ${QT_BUILD_CMAKE_PREFIX_PATH} ${${find_dependency_path_list}} ${_qt_additional_packages_prefix_paths} - ${QT_EXAMPLES_CMAKE_PREFIX_PATH} ${__qt_use_no_default_path_for_qt_packages} ) endif() diff --git a/cmake/QtPublicExternalProjectHelpers.cmake b/cmake/QtPublicExternalProjectHelpers.cmake new file mode 100644 index 0000000000..07096523d0 --- /dev/null +++ b/cmake/QtPublicExternalProjectHelpers.cmake @@ -0,0 +1,86 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +include_guard() + +# Get CMake variables that are needed to build external projects such as examples or CMake test +# projects. +# +# CMAKE_DIR_VAR: Variable name to store the path to the Qt6 CMake config module. +# +# PREFIXES_VAR: Variable name to store the prefixes that can be passed as CMAKE_PREFIX_PATH. +# +# ADDITIONAL_PACKAGES_PREFIXES_VAR: Variable name to store the prefixes that can be appended to +# QT_ADDITIONAL_PACKAGES_PREFIX_PATH. +function(_qt_internal_get_build_vars_for_external_projects) + set(no_value_options "") + set(single_value_options + CMAKE_DIR_VAR + PREFIXES_VAR + ADDITIONAL_PACKAGES_PREFIXES_VAR + ) + set(multi_value_options "") + cmake_parse_arguments(PARSE_ARGV 0 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + + # Standalone tests and examples have QT_BUILD_DIR pointing to the fake standalone prefix. + # Use instead the relocatable prefix, because qt must have been built / installed by this point. + if(QT_INTERNAL_BUILD_STANDALONE_PARTS) + qt_path_join(qt_cmake_dir + "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}" + "${INSTALL_LIBDIR}/cmake/${QT_CMAKE_EXPORT_NAMESPACE}" + ) + + set(qt_prefixes "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}") + set(qt_additional_packages_prefixes "${qt_prefixes}") + + if(QT_WILL_INSTALL) + list(APPEND qt_prefixes "${QT6_INSTALL_PREFIX}") + endif() + # TODO: Fix example/test builds when using Conan / install prefixes are different for each repo. + elseif(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}") + set(qt_additional_packages_prefixes "") + 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}") + + # 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. + # Both are handled by this assignment. + set(qt_prefixes "${QT_BUILD_DIR}") + + # Appending to QT_ADDITIONAL_PACKAGES_PREFIX_PATH helps find Qt6 components in + # non-qtbase prefix builds because we use NO_DEFAULT_PATH in find_package calls. + # It also handles the cross-compiling scenario where we need to adjust both the root path + # and prefixes, with the prefixes containing lib/cmake. This leverages the infrastructure + # previously added for Conan. + set(qt_additional_packages_prefixes "${qt_prefixes}") + + # In a prefix build, look up all repo Config files in the install prefix, + # except for the current repo, which will look in the build dir (handled above). + if(QT_WILL_INSTALL) + list(APPEND qt_prefixes "${QT6_INSTALL_PREFIX}") + endif() + endif() + + if(arg_CMAKE_DIR_VAR) + set("${arg_CMAKE_DIR_VAR}" "${qt_cmake_dir}" PARENT_SCOPE) + endif() + if(arg_PREFIXES_VAR) + set("${arg_PREFIXES_VAR}" "${qt_prefixes}" PARENT_SCOPE) + endif() + if(arg_ADDITIONAL_PACKAGES_PREFIXES_VAR) + set("${arg_ADDITIONAL_PACKAGES_PREFIXES_VAR}" "${qt_additional_packages_prefixes}" + PARENT_SCOPE) + endif() +endfunction() diff --git a/cmake/QtPublicFindPackageHelpers.cmake b/cmake/QtPublicFindPackageHelpers.cmake index 69f4f69e40..e463ea8d5d 100644 --- a/cmake/QtPublicFindPackageHelpers.cmake +++ b/cmake/QtPublicFindPackageHelpers.cmake @@ -4,3 +4,52 @@ function(qt_internal_disable_find_package_global_promotion target) set_target_properties("${target}" PROPERTIES _qt_no_promote_global TRUE) endfunction() + +# Transforms a list of package components into a string, so it can serve as a valid infix +# in a property name. +function(_qt_internal_get_package_components_as_valid_key_infix value out_var) + string(REPLACE ";" "_" valid_value "${value}") + set(${out_var} "${valid_value}" PARENT_SCOPE) +endfunction() + +# This function computes a unique key / id using the package name and the components that are +# passed. +# The result is later used as property name, to store provided targets for a specific +# package + components combination. +function(_qt_internal_get_package_components_id) + set(opt_args "") + set(single_args + PACKAGE_NAME + OUT_VAR_KEY + ) + set(multi_args + COMPONENTS + OPTIONAL_COMPONENTS + ) + cmake_parse_arguments(PARSE_ARGV 0 arg "${opt_args}" "${single_args}" "${multi_args}") + _qt_internal_validate_all_args_are_parsed(arg) + + if(NOT arg_PACKAGE_NAME) + message(FATAL_ERROR "PACKAGE_NAME is required") + endif() + + if(NOT arg_OUT_VAR_KEY) + message(FATAL_ERROR "OUT_VAR_KEY is required") + endif() + + set(key "${arg_PACKAGE_NAME}") + + if(arg_COMPONENTS) + _qt_internal_get_package_components_as_valid_key_infix("${arg_COMPONENTS}" + components_as_string) + string(APPEND key "-${components_as_string}") + endif() + + if(arg_OPTIONAL_COMPONENTS) + _qt_internal_get_package_components_as_valid_key_infix("${arg_OPTIONAL_COMPONENTS}" + components_as_string) + string(APPEND key "-${components_as_string}") + endif() + + set(${arg_OUT_VAR_KEY} "${key}" PARENT_SCOPE) +endfunction() diff --git a/cmake/QtPublicPluginHelpers.cmake b/cmake/QtPublicPluginHelpers.cmake index 5a4777a076..7087c31234 100644 --- a/cmake/QtPublicPluginHelpers.cmake +++ b/cmake/QtPublicPluginHelpers.cmake @@ -187,7 +187,7 @@ function(__qt_internal_get_plugin_import_macro plugin_target out_var) set(class_name "${class_name_prefixed}") endif() - set(${out_var} "Q_IMPORT_PLUGIN(${class_name})" PARENT_SCOPE) + set(${out_var} "Q_IMPORT_PLUGIN(${class_name})\n" PARENT_SCOPE) endfunction() function(__qt_internal_get_plugin_include_prelude out_var) diff --git a/cmake/QtPublicTargetHelpers.cmake b/cmake/QtPublicTargetHelpers.cmake index 6f0d3bb2c9..02d5546560 100644 --- a/cmake/QtPublicTargetHelpers.cmake +++ b/cmake/QtPublicTargetHelpers.cmake @@ -307,7 +307,31 @@ function(_qt_internal_set_up_static_runtime_library target) set_property(TARGET ${target} PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") elseif(MINGW) - target_link_options(${target} INTERFACE "LINKER:-Bstatic") + get_target_property(target_type ${target} TYPE) + if(target_type STREQUAL "EXECUTABLE") + set(link_option PRIVATE) + else() + set(link_option INTERFACE) + endif() + if(CLANG) + target_link_options(${target} ${link_option} "LINKER:-Bstatic") + else() + target_link_options(${target} ${link_option} "-static") + endif() endif() endif() endfunction() + +function(_qt_internal_warn_about_example_add_subdirectory) + # This is set by qt_build_repo_impl_examples() in QtBuildRepoHelpers.cmake, only for developer + # builds, to catch examples that are added via add_subdirectory instead of via + # qt_internal_add_example. + if(QT_WARN_ABOUT_EXAMPLE_ADD_SUBDIRECTORY) + get_filename_component(dir_name "${PROJECT_SOURCE_DIR}" NAME) + message(AUTHOR_WARNING + "It looks like this example project was added via add_subdirectory instead of via " + "qt_internal_add_example. This causes issues in certain build configurations. Please " + "change the code to use\n qt_internal_add_example(${dir_name})\ninstead." + ) + endif() +endfunction() diff --git a/cmake/QtPublicTestHelpers.cmake b/cmake/QtPublicTestHelpers.cmake index 0cfbfab404..771911c5d5 100644 --- a/cmake/QtPublicTestHelpers.cmake +++ b/cmake/QtPublicTestHelpers.cmake @@ -64,7 +64,7 @@ is not specified") #Escaping environment variables before expand them by file GENERATE string(REPLACE "\\" "\\\\" environment_extras "${environment_extras}") - if(WIN32) + if(CMAKE_HOST_WIN32) # It's necessary to call actual test inside 'cmd.exe', because 'execute_process' uses # SW_HIDE to avoid showing a console window, it affects other GUI as well. # See https://gitlab.kitware.com/cmake/cmake/-/issues/17690 for details. @@ -97,7 +97,7 @@ execute_process(COMMAND ${extra_runner} ${arg_COMMAND} ) ${post_run} if(NOT result EQUAL 0) - string(JOIN \" \" full_command ${arg_COMMAND}) + string(JOIN \" \" full_command ${extra_runner} ${arg_COMMAND}) message(FATAL_ERROR \"\${full_command} execution failed with exit code \${result}.\") endif()" ) diff --git a/cmake/QtPublicWalkLibsHelpers.cmake b/cmake/QtPublicWalkLibsHelpers.cmake index f0d9280637..959283aca1 100644 --- a/cmake/QtPublicWalkLibsHelpers.cmake +++ b/cmake/QtPublicWalkLibsHelpers.cmake @@ -184,13 +184,13 @@ function(__qt_internal_walk_libs if(lib_target MATCHES "^::@") continue() elseif(TARGET ${lib_target}) - if ("${lib_target}" MATCHES "^Qt::(.*)") - # If both, Qt::Foo and Foo targets exist, prefer the target name without + if(NOT "${lib_target}" MATCHES "^(Qt|${QT_CMAKE_EXPORT_NAMESPACE})::.+") + # If both, Qt::Foo and Foo targets exist, prefer the target name with versioned # namespace. Which one is preferred doesn't really matter. This code exists to # avoid ending up with both, Qt::Foo and Foo in our dependencies. - set(namespaceless_lib_target "${CMAKE_MATCH_1}") - if(TARGET namespaceless_lib_target) - set(lib_target ${namespaceless_lib_target}) + set(versioned_qt_target "${QT_CMAKE_EXPORT_NAMESPACE}::${lib_target}") + if(TARGET "${versioned_qt_target}") + set(lib_target ${versioned_qt_target}) endif() endif() get_target_property(lib_target_type ${lib_target} TYPE) @@ -255,8 +255,8 @@ function(__qt_internal_walk_libs __qt_internal_promote_target_to_global(${lib_target_unaliased}) endif() endif() - elseif("${lib_target}" MATCHES "^Qt::(.*)") - message(FATAL_ERROR "The ${CMAKE_MATCH_1} target is mentioned as a dependency for \ + elseif("${lib_target}" MATCHES "^(Qt|${QT_CMAKE_EXPORT_NAMESPACE})::(.*)") + message(FATAL_ERROR "The ${CMAKE_MATCH_2} target is mentioned as a dependency for \ ${target}, but not declared.") else() if(NOT operation MATCHES "^(collect|direct)_targets$") diff --git a/cmake/QtPublicWasmToolchainHelpers.cmake b/cmake/QtPublicWasmToolchainHelpers.cmake index 798258db94..a8994520e2 100644 --- a/cmake/QtPublicWasmToolchainHelpers.cmake +++ b/cmake/QtPublicWasmToolchainHelpers.cmake @@ -30,7 +30,7 @@ function(__qt_internal_query_emsdk_version emroot_path is_fatal out_var) set(EXECUTE_COMMANDPATH "$ENV{EMSDK}/${emroot_path}/emcc") endif() - file(TO_NATIVE_PATH "${EXECUTE_COMMANDPATH}" EXECUTE_COMMAND) + file(TO_CMAKE_PATH "${EXECUTE_COMMANDPATH}" EXECUTE_COMMAND) execute_process(COMMAND ${EXECUTE_COMMAND} --version OUTPUT_VARIABLE emOutput OUTPUT_STRIP_TRAILING_WHITESPACE @@ -53,7 +53,7 @@ endfunction() function(__qt_internal_get_emcc_recommended_version out_var) # This version of Qt needs this version of emscripten. - set(QT_EMCC_RECOMMENDED_VERSION "3.1.25") + set(QT_EMCC_RECOMMENDED_VERSION "3.1.50") set(${out_var} "${QT_EMCC_RECOMMENDED_VERSION}" PARENT_SCOPE) endfunction() @@ -81,12 +81,14 @@ function(__qt_internal_get_qt_build_emsdk_version out_var) endif() if(EXISTS "${WASM_BUILD_DIR}/src/corelib/global/qconfig.h") file(READ "${WASM_BUILD_DIR}/src/corelib/global/qconfig.h" ver) - else() + elseif(EXISTS "${WASM_BUILD_DIR}/include/QtCore/qconfig.h") file(READ "${WASM_BUILD_DIR}/include/QtCore/qconfig.h" ver) endif() - string(REGEX MATCH "#define QT_EMCC_VERSION.\"[0-9]+\\.[0-9]+\\.[0-9]+\"" emOutput ${ver}) - string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" build_emcc_version "${emOutput}") - set(${out_var} "${build_emcc_version}" PARENT_SCOPE) + if (ver) + string(REGEX MATCH "#define QT_EMCC_VERSION.\"[0-9]+\\.[0-9]+\\.[0-9]+\"" emOutput ${ver}) + string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" build_emcc_version "${emOutput}") + set(${out_var} "${build_emcc_version}" PARENT_SCOPE) + endif() endfunction() function(_qt_test_emscripten_version) @@ -95,7 +97,7 @@ function(_qt_test_emscripten_version) __qt_internal_query_emsdk_version("${emroot_path}" TRUE current_emsdk_ver) __qt_internal_get_qt_build_emsdk_version(qt_build_emcc_version) - if(NOT "${qt_build_emcc_version}" STREQUAL "${current_emsdk_ver}") + if(NOT "${qt_build_emcc_version}" STREQUAL "" AND NOT "${qt_build_emcc_version}" STREQUAL "${current_emsdk_ver}") message("Qt Wasm built with Emscripten version: ${qt_build_emcc_version}") message("You are using Emscripten version: ${current_emsdk_ver}") message("The recommended version of Emscripten for this Qt is: ${_recommended_emver}") diff --git a/cmake/QtQmakeHelpers.cmake b/cmake/QtQmakeHelpers.cmake index 89284b0990..c618fa0510 100644 --- a/cmake/QtQmakeHelpers.cmake +++ b/cmake/QtQmakeHelpers.cmake @@ -80,7 +80,7 @@ endfunction() # up the host qmake's properties for cross-compiling with this Qt # build. function(qt_generate_qmake_and_qtpaths_wrapper_for_target) - if(NOT CMAKE_CROSSCOMPILING) + if(NOT CMAKE_CROSSCOMPILING OR QT_NO_GENERATE_QMAKE_WRAPPER_FOR_TARGET) return() endif() @@ -212,6 +212,7 @@ function(qt_get_qmake_module_name result module) string(REGEX REPLACE "Private$" "_private" module "${module}") string(REGEX REPLACE "Qpa$" "_qpa_lib_private" module "${module}") string(REGEX REPLACE "Rhi$" "_rhi_lib_private" module "${module}") + string(REGEX REPLACE "Ssg$" "_ssg_lib_private" module "${module}") string(TOLOWER "${module}" module) set(${result} ${module} PARENT_SCOPE) endfunction() diff --git a/cmake/QtRpathHelpers.cmake b/cmake/QtRpathHelpers.cmake index 7228ffbbaa..da6c8715a8 100644 --- a/cmake/QtRpathHelpers.cmake +++ b/cmake/QtRpathHelpers.cmake @@ -192,6 +192,51 @@ function(qt_apply_rpaths) endif() endfunction() +macro(qt_internal_set_default_rpath_settings) + # the default RPATH to be used when installing, but only if it's not a system directory + list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES + "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIBDIR}" isSystemDir) + if("${isSystemDir}" STREQUAL "-1") + set(_default_install_rpath "${CMAKE_INSTALL_PREFIX}/${INSTALL_LIBDIR}") + endif("${isSystemDir}" STREQUAL "-1") + + # The default rpath settings for installed targets is empty. + # The rpaths will instead be computed for each target separately using qt_apply_rpaths(). + # Additional rpaths can be passed via QT_EXTRA_RPATHS. + # By default this will include $ORIGIN / @loader_path, so the installation is relocatable. + # Bottom line: No need to pass anything to CMAKE_INSTALL_RPATH. + set(CMAKE_INSTALL_RPATH "" CACHE STRING "RPATH for installed binaries") + + # By default, don't embed auto-determined RPATHs pointing to directories + # outside of the build tree, into the installed binaries. + # This ended up adding rpaths like ${CMAKE_INSTALL_PREFIX}/lib (or /Users/qt/work/install/lib + # into the official libraries created by the CI) into the non-qtbase libraries, plugins, etc. + # + # It should not be necessary, given that qt_apply_rpaths() already adds the necessary rpaths, + # either relocatable ones or absolute ones, depending on what the platform supports. + if(NOT QT_NO_DISABLE_CMAKE_INSTALL_RPATH_USE_LINK_PATH) + set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE) + endif() + + # If Qt is built without rpath support, we should not add "user-project default rpaths" to + # qt qml plugins. Do this by setting QT_NO_QML_PLUGIN_RPATH to TRUE, which is + # then read by qt6_add_qml_plugin. + # We do this as part of the internal API, because we still want to allow user project qml + # plugins to have sensible default rpaths, even if Qt qml plugins were built without support + # for rpaths. + # + # Note that feature evaluation is not done yet in qtbase at this point, so we check both + # feature variable variants. In practice it doesn't really matter, because the variable is only + # read during qtdeclarative configuration time when the feature is already evaluated. + # + # We also make sure not to set it as a cache var just in case somebody wants to override it + # per directory scope. + if(NOT DEFINED QT_NO_QML_PLUGIN_RPATH + AND (QT_DISABLE_RPATH OR (NOT FEATURE_rpath) OR (NOT QT_FEATURE_rpath))) + set(QT_NO_QML_PLUGIN_RPATH "TRUE") + endif() +endmacro() + # Overrides the CMAKE_STAGING_PREFIX in a subdirectory scope, to stop CMake from rewriting build # rpaths to point into the original staging prefix, and thus breaking running executables from # the build directory. diff --git a/cmake/QtSanitizerHelpers.cmake b/cmake/QtSanitizerHelpers.cmake index 775706e721..2a933b2222 100644 --- a/cmake/QtSanitizerHelpers.cmake +++ b/cmake/QtSanitizerHelpers.cmake @@ -26,3 +26,35 @@ function(qt_internal_set_up_sanitizer_options) set(ECM_ENABLE_SANITIZERS "${enabled_sanitizer_features}" PARENT_SCOPE) endif() endfunction() + +# This function clears the previously set sanitizer flags from CMAKE_<C|CXX>_FLAGS +function(qt_internal_skip_sanitizer) + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU" OR + CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR + CMAKE_CXX_COMPILER_ID MATCHES "MSVC") + foreach(sanitizer ${ECM_ENABLE_SANITIZERS}) + string(TOLOWER "${sanitizer}" sanitizer) + enable_sanitizer_flags("${sanitizer}") + qt_internal_remove_compiler_flags(${XSAN_COMPILE_FLAGS}) + endforeach() + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}" PARENT_SCOPE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}" PARENT_SCOPE) + endif() +endfunction() + +# This function disables the sanitizer library linking to all targets created in a subdirectory +# where the function is called. Note that the function should be called after all involved targets +# are created, to make sure they are collected by the function. +function(qt_internal_skip_linking_sanitizer) + _qt_internal_collect_buildsystem_targets(all_targets "${CMAKE_CURRENT_SOURCE_DIR}" + INCLUDE + STATIC_LIBRARY + MODULE_LIBRARY + SHARED_LIBRARY + OBJECT_LIBRARY + EXECUTABLE + ) + foreach(t IN LISTS all_targets) + set_property(TARGET ${t} PROPERTY SKIP_SANITIZER TRUE) + endforeach() +endfunction() diff --git a/cmake/QtSeparateDebugInfo.Info.plist.in b/cmake/QtSeparateDebugInfo.Info.plist.in index d3e8dfbe7f..27489e29bb 100644 --- a/cmake/QtSeparateDebugInfo.Info.plist.in +++ b/cmake/QtSeparateDebugInfo.Info.plist.in @@ -1,3 +1,8 @@ +<!-- +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause +--> + <?xml version=\"1.0\" encoding=\"UTF-8\"?> <!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\"> <plist version=\"1.0\"> diff --git a/cmake/QtSeparateDebugInfo.cmake b/cmake/QtSeparateDebugInfo.cmake index ff225ad5de..61f62207fa 100644 --- a/cmake/QtSeparateDebugInfo.cmake +++ b/cmake/QtSeparateDebugInfo.cmake @@ -1,8 +1,6 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -include(CMakeFindBinUtils) - if(CMAKE_VERSION VERSION_LESS 3.17.0) set(CMAKE_CURRENT_FUNCTION_LIST_DIR ${CMAKE_CURRENT_LIST_DIR}) endif() @@ -201,7 +199,11 @@ endfunction() # Enable separate debug information for the given target function(qt_enable_separate_debug_info target installDestination) set(flags QT_EXECUTABLE) - set(options) + if(APPLE) + set(options DSYM_OUTPUT_DIR) + else() + set(options) + endif() set(multiopts ADDITIONAL_INSTALL_ARGS) cmake_parse_arguments(arg "${flags}" "${options}" "${multiopts}" ${ARGN}) @@ -248,12 +250,20 @@ function(qt_enable_separate_debug_info target installDestination) get_target_property(is_framework ${target} FRAMEWORK) if(is_framework) qt_internal_get_framework_info(fw ${target}) - set(debug_info_bundle_dir "$<TARGET_BUNDLE_DIR:${target}>.${debug_info_suffix}") set(BUNDLE_ID ${fw_name}) else() - set(debug_info_bundle_dir "$<TARGET_FILE:${target}>.${debug_info_suffix}") set(BUNDLE_ID ${target}) endif() + + if (NOT "x${arg_DSYM_OUTPUT_DIR}" STREQUAL "x") + set(debug_info_bundle_dir "${arg_DSYM_OUTPUT_DIR}/${target}") + elseif(is_framework) + set(debug_info_bundle_dir "$<TARGET_BUNDLE_DIR:${target}>") + else() + set(debug_info_bundle_dir "$<TARGET_FILE:${target}>") + endif() + set(debug_info_bundle_dir "${debug_info_bundle_dir}.${debug_info_suffix}") + set(debug_info_contents_dir "${debug_info_bundle_dir}/Contents") set(debug_info_target_dir "${debug_info_contents_dir}/Resources/DWARF") configure_file( diff --git a/cmake/QtSetup.cmake b/cmake/QtSetup.cmake index caf342214e..a420495756 100644 --- a/cmake/QtSetup.cmake +++ b/cmake/QtSetup.cmake @@ -1,386 +1,6 @@ # Copyright (C) 2022 The Qt Company Ltd. # SPDX-License-Identifier: BSD-3-Clause -## Set a default build type if none was specified - -# Set the QT_IS_BUILDING_QT variable so we can verify whether we are building -# Qt from source -set(QT_BUILDING_QT TRUE CACHE - TYPE STRING "When this is present and set to true, it signals that we are building Qt from source.") - -# Pre-calculate the developer_build feature if it's set by the user via INPUT_developer_build -if(NOT FEATURE_developer_build AND INPUT_developer_build - AND NOT "${INPUT_developer_build}" STREQUAL "undefined") - set(FEATURE_developer_build ON) -endif() - -# Pre-calculate the no_prefix feature if it's set by configure via INPUT_no_prefix. -# This needs to be done before qtbase/configure.cmake is processed. -if(NOT FEATURE_no_prefix AND INPUT_no_prefix - AND NOT "${INPUT_no_prefix}" STREQUAL "undefined") - set(FEATURE_no_prefix ON) -endif() - -set(_default_build_type "Release") -if(FEATURE_developer_build) - set(_default_build_type "Debug") -endif() - -function(qt_internal_set_message_log_level out_var) - # Decide whether output should be verbose or not. - # Default to verbose (--log-level=STATUS) in a developer-build and - # non-verbose (--log-level=NOTICE) otherwise. - # If a custom CMAKE_MESSAGE_LOG_LEVEL was specified, it takes priority. - # Passing an explicit --log-level=Foo has the highest priority. - if(NOT CMAKE_MESSAGE_LOG_LEVEL) - if(FEATURE_developer_build OR QT_FEATURE_developer_build) - set(CMAKE_MESSAGE_LOG_LEVEL "STATUS") - else() - set(CMAKE_MESSAGE_LOG_LEVEL "NOTICE") - endif() - set(${out_var} "${CMAKE_MESSAGE_LOG_LEVEL}" PARENT_SCOPE) - endif() -endfunction() -qt_internal_set_message_log_level(CMAKE_MESSAGE_LOG_LEVEL) - -# Reset content of extra build internal vars for each inclusion of QtSetup. -unset(QT_EXTRA_BUILD_INTERNALS_VARS) - -# Save the global property in a variable to make it available to feature conditions. -get_property(QT_GENERATOR_IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) - -if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "Setting build type to '${_default_build_type}' as none was specified.") - set(CMAKE_BUILD_TYPE "${_default_build_type}" CACHE STRING "Choose the type of build." FORCE) - set_property(CACHE CMAKE_BUILD_TYPE - PROPERTY STRINGS - "Debug" "Release" "MinSizeRel" "RelWithDebInfo") # Set the possible values for cmake-gui. -elseif(CMAKE_CONFIGURATION_TYPES) - message(STATUS "Building for multiple configurations: ${CMAKE_CONFIGURATION_TYPES}.") - message(STATUS "Main configuration is: ${QT_MULTI_CONFIG_FIRST_CONFIG}.") - if(CMAKE_NINJA_MULTI_DEFAULT_BUILD_TYPE) - message(STATUS - "Default build configuration set to '${CMAKE_NINJA_MULTI_DEFAULT_BUILD_TYPE}'.") - endif() - if(CMAKE_GENERATOR STREQUAL "Ninja") - message(FATAL_ERROR - "It's not possible to build multiple configurations with the single config Ninja " - "generator. Consider configuring with -G\"Ninja Multi-Config\" instead of -GNinja." - ) - endif() -else() - message(STATUS "CMAKE_BUILD_TYPE was set to: '${CMAKE_BUILD_TYPE}'") -endif() - -# Append a config-specific postfix to library names to ensure distinct names -# in a multi-config build. -# e.g. lib/libQt6DBus_relwithdebinfo.6.3.0.dylib -# Don't apply the postfix to the first encountered release-like config, so we have at least one -# config without a postifx. -if(QT_GENERATOR_IS_MULTI_CONFIG AND CMAKE_CONFIGURATION_TYPES) - set(__qt_setup_release_configs Release RelWithDebInfo MinSizeRel) - set(__qt_setup_found_first_release_config FALSE) - foreach(__qt_setup_config_type IN LISTS CMAKE_CONFIGURATION_TYPES) - # Skip assigning postfix for the first release-like config. - if(NOT __qt_setup_found_first_release_config - AND __qt_setup_config_type IN_LIST __qt_setup_release_configs) - set(__qt_setup_found_first_release_config TRUE) - continue() - endif() - - string(TOLOWER "${__qt_setup_config_type}" __qt_setup_config_type_lower) - string(TOUPPER "${__qt_setup_config_type}" __qt_setup_config_type_upper) - set(CMAKE_${__qt_setup_config_type_upper}_POSTFIX "_${__qt_setup_config_type_lower}") - if(APPLE) - set(CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_${__qt_setup_config_type_upper} - "_${__qt_setup_config_type_lower}") - endif() - endforeach() -endif() - -# Override the generic debug postfixes above with custom debug postfixes (even in a single config -# build) to follow the conventions we had since Qt 5. -# e.g. lib/libQt6DBus_debug.6.3.0.dylib -if(WIN32) - if(MINGW) - # On MinGW we don't have "d" suffix for debug libraries like on Linux, - # unless we're building debug and release libraries in one go. - if(QT_GENERATOR_IS_MULTI_CONFIG) - set(CMAKE_DEBUG_POSTFIX "d") - endif() - else() - set(CMAKE_DEBUG_POSTFIX "d") - endif() -elseif(APPLE) - set(CMAKE_DEBUG_POSTFIX "_debug") - set(CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_DEBUG "_debug") -endif() - -## Position independent code: -set(CMAKE_POSITION_INDEPENDENT_CODE ON) - -# Does the linker support position independent code? -include(CheckPIESupported) -check_pie_supported() - -# Do not relink dependent libraries when no header has changed: -set(CMAKE_LINK_DEPENDS_NO_SHARED ON) - -# Detect non-prefix builds: either when the qtbase install prefix is set to the binary dir -# or when a developer build is explicitly enabled and no install prefix (or staging prefix) -# is specified. -# This detection only happens when building qtbase, and later is propagated via the generated -# QtBuildInternalsExtra.cmake file. -if (PROJECT_NAME STREQUAL "QtBase" AND NOT QT_BUILD_STANDALONE_TESTS) - if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) - # Handle both FEATURE_ and QT_FEATURE_ cases when they are specified on the command line - # explicitly. It's possible for one to be set, but not the other, because - # qtbase/configure.cmake is not processed by this point. - if((FEATURE_developer_build - OR QT_FEATURE_developer_build - OR FEATURE_no_prefix - OR QT_FEATURE_no_prefix - ) - AND NOT CMAKE_STAGING_PREFIX) - # Handle non-prefix builds by setting the CMake install prefix to point to qtbase's - # build dir. While building another repo (like qtsvg) the CMAKE_PREFIX_PATH should be - # set on the command line to point to the qtbase build dir. - set(__qt_default_prefix "${QtBase_BINARY_DIR}") - else() - if(CMAKE_HOST_WIN32) - set(__qt_default_prefix "C:/Qt/") - else() - set(__qt_default_prefix "/usr/local/") - endif() - string(APPEND __qt_default_prefix - "Qt-${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") - endif() - set(CMAKE_INSTALL_PREFIX ${__qt_default_prefix} CACHE PATH - "Install path prefix, prepended onto install directories." FORCE) - unset(__qt_default_prefix) - endif() - if(CMAKE_STAGING_PREFIX) - set(__qt_prefix "${CMAKE_STAGING_PREFIX}") - else() - set(__qt_prefix "${CMAKE_INSTALL_PREFIX}") - endif() - if(__qt_prefix STREQUAL QtBase_BINARY_DIR) - set(__qt_will_install_value OFF) - else() - set(__qt_will_install_value ON) - endif() - set(QT_WILL_INSTALL ${__qt_will_install_value} CACHE BOOL - "Boolean indicating if doing a Qt prefix build (vs non-prefix build)." FORCE) - unset(__qt_prefix) - unset(__qt_will_install_value) -endif() - -# Specify the QT_SOURCE_TREE only when building qtbase. Needed by some tests when the tests are -# built as part of the project, and not standalone. For standalone tests, the value is set in -# QtBuildInternalsExtra.cmake. -if(PROJECT_NAME STREQUAL "QtBase") - set(QT_SOURCE_TREE "${QtBase_SOURCE_DIR}" CACHE PATH - "A path to the source tree of the previously configured QtBase project." FORCE) -endif() - -# QT_INTERNAL_CONFIGURE_FROM_IDE is set to TRUE for the following known IDE applications: -# - Qt Creator, detected by QTC_RUN environment variable -# - CLion, detected by CLION_IDE environment variable -# - Visual Studio Code, detected by VSCODE_CLI environment variable -if("$ENV{QTC_RUN}" OR "$ENV{CLION_IDE}" OR "$ENV{VSCODE_CLI}") - set(QT_INTERNAL_CONFIGURE_FROM_IDE TRUE CACHE INTERNAL "Configuring Qt Project from IDE") -else() - set(QT_INTERNAL_CONFIGURE_FROM_IDE FALSE CACHE INTERNAL "Configuring Qt Project from IDE") -endif() - -if(FEATURE_developer_build) - if(DEFINED QT_CMAKE_EXPORT_COMPILE_COMMANDS) - set(CMAKE_EXPORT_COMPILE_COMMANDS ${QT_CMAKE_EXPORT_COMPILE_COMMANDS}) - else() - set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - endif() - set(_qt_build_tests_default ON) - set(__build_benchmarks ON) - - # Tests are not built by default with qmake for iOS and friends, and thus the overall build - # tends to fail. Disable them by default when targeting uikit. - if(UIKIT OR ANDROID) - set(_qt_build_tests_default OFF) - endif() - - # Disable benchmarks for single configuration generators which do not build - # with release configuration. - if (CMAKE_BUILD_TYPE AND CMAKE_BUILD_TYPE STREQUAL Debug) - set(__build_benchmarks OFF) - endif() -else() - set(_qt_build_tests_default OFF) - set(__build_benchmarks OFF) -endif() - -# Build Benchmarks -option(QT_BUILD_BENCHMARKS "Build Qt Benchmarks" ${__build_benchmarks}) -if(QT_BUILD_BENCHMARKS) - set(_qt_build_tests_default ON) -endif() - -## Set up testing -option(QT_BUILD_TESTS "Build the testing tree." ${_qt_build_tests_default}) -unset(_qt_build_tests_default) -option(QT_BUILD_TESTS_BY_DEFAULT "Should tests be built as part of the default 'all' target." ON) -if(QT_BUILD_STANDALONE_TESTS) - # BuildInternals might have set it to OFF on initial configuration. So force it to ON when - # building standalone tests. - set(QT_BUILD_TESTS ON CACHE BOOL "Build the testing tree." FORCE) - - # Also force the tests to be built as part of the default build target. - set(QT_BUILD_TESTS_BY_DEFAULT ON CACHE BOOL - "Should tests be built as part of the default 'all' target." FORCE) -endif() -set(BUILD_TESTING ${QT_BUILD_TESTS} CACHE INTERNAL "") - -if (WASM) - set(_qt_batch_tests ON) -else() - set(_qt_batch_tests OFF) -endif() - -if(DEFINED INPUT_batch_tests) - if (${INPUT_batch_tests}) - set(_qt_batch_tests ON) - else() - set(_qt_batch_tests OFF) - endif() -endif() - -option(QT_BUILD_TESTS_BATCHED "Link all tests into a single binary." ${_qt_batch_tests}) - -if(QT_BUILD_TESTS AND QT_BUILD_TESTS_BATCHED AND CMAKE_VERSION VERSION_LESS "3.19") - message(FATAL_ERROR - "Test batching requires at least CMake 3.19, due to requiring per-source " - "TARGET_DIRECTORY assignments and DEFER calls.") -endif() - -# QT_BUILD_TOOLS_WHEN_CROSSCOMPILING -> QT_FORCE_BUILD_TOOLS -# pre-6.4 compatibility flag (remove sometime in the future) -if(CMAKE_CROSSCOMPILING AND QT_BUILD_TOOLS_WHEN_CROSSCOMPILING) - message(WARNING "QT_BUILD_TOOLS_WHEN_CROSSCOMPILING is deprecated. " - "Please use QT_FORCE_BUILD_TOOLS instead.") - set(QT_FORCE_BUILD_TOOLS TRUE CACHE INTERNAL "" FORCE) -endif() - -# When cross-building, we don't build tools by default. Sometimes this also covers Qt apps as well. -# Like in qttools/assistant/assistant.pro, load(qt_app), which is guarded by a qtNomakeTools() call. - -set(_qt_build_tools_by_default_default ON) -if(CMAKE_CROSSCOMPILING AND NOT QT_FORCE_BUILD_TOOLS) - set(_qt_build_tools_by_default_default OFF) -endif() -option(QT_BUILD_TOOLS_BY_DEFAULT "Should tools be built as part of the default 'all' target." - "${_qt_build_tools_by_default_default}") -unset(_qt_build_tools_by_default_default) - -include(CTest) -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) - -# FIXME: Support prefix builds as well QTBUG-96232 -if(QT_WILL_INSTALL) - set(_qt_build_examples_as_external OFF) -else() - set(_qt_build_examples_as_external ON) -endif() -option(QT_BUILD_EXAMPLES_AS_EXTERNAL "Should examples be built as ExternalProjects." - ${_qt_build_examples_as_external}) -unset(_qt_build_examples_as_external) - -option(QT_BUILD_MANUAL_TESTS "Build Qt manual tests" OFF) - -if(WASM) - option(QT_BUILD_MINIMAL_STATIC_TESTS "Build minimal subset of tests for static Qt builds" ON) -else() - option(QT_BUILD_MINIMAL_STATIC_TESTS "Build minimal subset of tests for static Qt builds" OFF) -endif() - -option(QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS - "Build minimal subset of tests for Android multi-ABI Qt builds" OFF) - -## Path used to find host tools, either when cross-compiling or just when using the tools from -## a different host build. -set(QT_HOST_PATH "$ENV{QT_HOST_PATH}" CACHE PATH - "Installed Qt host directory path, used for cross compiling.") - -## Android platform settings -if(ANDROID) - include(QtPlatformAndroid) -endif() - -## qt_add_module and co.: +# Any new code should go into QtBuildHelpers.cmake or other appropriate files and then called in +# qt_internal_setup_build_and_global_variables(). include(QtBuild) - -## Qt Feature support: -include(QtBuildInformation) -include(QtFeature) - -## Compiler optimization flags: -include(QtCompilerOptimization) - -## Compiler flags: -include(QtCompilerFlags) - -qt_set_language_standards() - -option(QT_USE_CCACHE "Enable the use of ccache") -if(QT_USE_CCACHE) - find_program(CCACHE_PROGRAM ccache) - if(CCACHE_PROGRAM) - set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") - set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") - set(CMAKE_OBJC_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") - set(CMAKE_OBJCXX_COMPILER_LAUNCHER "${CCACHE_PROGRAM}") - else() - message(FATAL_ERROR "Ccache use was requested, but the program was not found.") - endif() -endif() - -option(QT_UNITY_BUILD "Enable unity (jumbo) build") -set(QT_UNITY_BUILD_BATCH_SIZE "32" CACHE STRING "Unity build batch size") -if(QT_UNITY_BUILD) - set(CMAKE_UNITY_BUILD ON) - set(CMAKE_UNITY_BUILD_BATCH_SIZE "${QT_UNITY_BUILD_BATCH_SIZE}") -endif() - -option(QT_ALLOW_SYMLINK_IN_PATHS "Allows symlinks in paths." OFF) - -# We need to clean up QT_FEATURE_*, but only once per configuration cycle -get_property(qt_feature_clean GLOBAL PROPERTY _qt_feature_clean) -if(NOT qt_feature_clean) - message(STATUS "Check for feature set changes") - set_property(GLOBAL PROPERTY _qt_feature_clean TRUE) - foreach(feature ${QT_KNOWN_FEATURES}) - if(DEFINED "FEATURE_${feature}" AND - NOT "${QT_FEATURE_${feature}}" STREQUAL "${FEATURE_${feature}}") - message(" '${feature}' is changed from ${QT_FEATURE_${feature}} \ -to ${FEATURE_${feature}}") - set(dirty_build TRUE) - endif() - unset("QT_FEATURE_${feature}" CACHE) - endforeach() - - set(QT_KNOWN_FEATURES "" CACHE INTERNAL "" FORCE) - - if(dirty_build) - set_property(GLOBAL PROPERTY _qt_dirty_build TRUE) - message(WARNING "Re-configuring in existing build folder. \ -Some features will be re-evaluated automatically.") - endif() -endif() - -if(NOT QT_BUILD_EXAMPLES) - # Disable deployment setup to avoid warnings about missing patchelf with CMake < 3.21. - set(QT_SKIP_SETUP_DEPLOYMENT ON) -endif() - -option(QT_ALLOW_DOWNLOAD "Allows files to be downloaded when building Qt." OFF) diff --git a/cmake/QtStandaloneTestsConfig.cmake.in b/cmake/QtStandaloneTestsConfig.cmake.in index afa9d2ee79..39200167a5 100644 --- a/cmake/QtStandaloneTestsConfig.cmake.in +++ b/cmake/QtStandaloneTestsConfig.cmake.in @@ -1,5 +1,8 @@ +# Copyright (C) 2024 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + # TODO: Ideally this should look for each Qt module separately, with each module's specific version, # bypassing the Qt6 Config file, aka find_package(Qt6SpecificFoo) repated x times. But it's not # critical. find_package(@INSTALL_CMAKE_NAMESPACE@ @main_qt_package_version@ - REQUIRED COMPONENTS @QT_REPO_KNOWN_MODULES_STRING@) + COMPONENTS @QT_REPO_KNOWN_MODULES_STRING@) diff --git a/cmake/QtSyncQtHelpers.cmake b/cmake/QtSyncQtHelpers.cmake index de78edbbc4..0188b87c6a 100644 --- a/cmake/QtSyncQtHelpers.cmake +++ b/cmake/QtSyncQtHelpers.cmake @@ -61,13 +61,6 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge set(is_framework FALSE) if(NOT is_interface_lib) get_target_property(is_framework ${target} FRAMEWORK) - if(is_framework) - qt_internal_get_framework_info(fw ${target}) - get_target_property(fw_output_base_dir ${target} LIBRARY_OUTPUT_DIRECTORY) - set(framework_args "-framework" - "-frameworkIncludeDir" "${fw_output_base_dir}/${fw_versioned_header_dir}" - ) - endif() endif() qt_internal_get_qt_all_known_modules(known_modules) @@ -80,6 +73,7 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge get_target_property(qpa_filter_regex ${target} _qt_module_qpa_headers_filter_regex) get_target_property(rhi_filter_regex ${target} _qt_module_rhi_headers_filter_regex) + get_target_property(ssg_filter_regex ${target} _qt_module_ssg_headers_filter_regex) get_target_property(private_filter_regex ${target} _qt_module_private_headers_filter_regex) # We need to use the real paths since otherwise it may lead to the invalid work of the @@ -103,6 +97,12 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge ) endif() + if(ssg_filter_regex) + set(ssg_filter_argument + -ssgHeadersFilter "${ssg_filter_regex}" + ) + endif() + set(common_syncqt_arguments -module "${module}" -sourceDir "${source_dir_real}" @@ -112,9 +112,11 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge -privateIncludeDir "${module_build_interface_private_include_dir}" -qpaIncludeDir "${module_build_interface_qpa_include_dir}" -rhiIncludeDir "${module_build_interface_rhi_include_dir}" + -ssgIncludeDir "${module_build_interface_ssg_include_dir}" -generatedHeaders ${module_headers_generated} ${qpa_filter_argument} ${rhi_filter_argument} + ${ssg_filter_argument} ${public_namespaces_filter} ${non_qt_module_argument} ${internal_module_argument} @@ -151,13 +153,27 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge -headers ${module_headers} -stagingDir "${syncqt_staging_dir}" -knownModules ${known_modules} - ${framework_args} ${version_script_args} ) list(JOIN syncqt_args "\n" syncqt_args_string) set(syncqt_args_rsp "${binary_dir_real}/${target}_syncqt_args") qt_configure_file(OUTPUT "${syncqt_args_rsp}" CONTENT "${syncqt_args_string}") + get_target_property(external_headers_dir ${target} _qt_external_headers_dir) + if(external_headers_dir) + if(NOT IS_ABSOLUTE "${external_headers_dir}") + get_filename_component(external_headers_dir "${external_headers_dir}" ABSOLUTE) + endif() + if(EXISTS "${external_headers_dir}") + set(external_headers_dir_copy_cmd + COMMAND + ${CMAKE_COMMAND} + -E copy_directory + "${external_headers_dir}" + "${module_build_interface_include_dir}" + ) + endif() + endif() add_custom_command( OUTPUT ${syncqt_outputs} @@ -165,6 +181,7 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge ${QT_CMAKE_EXPORT_NAMESPACE}::syncqt "@${syncqt_args_rsp}" ${build_time_syncqt_arguments} + ${external_headers_dir_copy_cmd} COMMAND ${CMAKE_COMMAND} -E touch "${syncqt_timestamp}" DEPENDS @@ -176,7 +193,14 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge "Running syncqt.cpp for module: ${module}" VERBATIM ) + + set(add_sync_headers_to_all "") + if(is_interface_lib) + set(add_sync_headers_to_all ALL) + endif() + add_custom_target(${target}_sync_headers + ${add_sync_headers_to_all} DEPENDS ${syncqt_outputs} ) @@ -184,6 +208,9 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge set_target_properties(${target} PROPERTIES _qt_internal_sync_headers_target ${target}_sync_headers) + if(is_3rd_party_library) + add_dependencies(thirdparty_sync_headers ${target}_sync_headers) + endif() # This target is required when building docs, to make all header files and their aliases # available for qdoc. # ${target}_sync_headers is added as dependency to make sure that @@ -196,6 +223,7 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge COMMAND ${QT_CMAKE_EXPORT_NAMESPACE}::syncqt "@${syncqt_all_args_rsp}" + ${external_headers_dir_copy_cmd} DEPENDS ${module_headers} ${syncqt_all_args_rsp} @@ -231,7 +259,7 @@ function(qt_internal_target_sync_headers target module_headers module_headers_ge # Run sync Qt first time at configure step to make all header files available for the code model # of IDEs. get_property(synced_modules GLOBAL PROPERTY _qt_synced_modules) - if(NOT "${module}" IN_LIST synced_modules) + if(NOT "${module}" IN_LIST synced_modules AND QT_SYNC_HEADERS_AT_CONFIGURE_TIME) message(STATUS "Running syncqt.cpp for module: ${module}") get_target_property(syncqt_location ${QT_CMAKE_EXPORT_NAMESPACE}::syncqt LOCATION) execute_process( diff --git a/cmake/QtTargetHelpers.cmake b/cmake/QtTargetHelpers.cmake index 2c2b324182..d361a8096d 100644 --- a/cmake/QtTargetHelpers.cmake +++ b/cmake/QtTargetHelpers.cmake @@ -16,8 +16,10 @@ # Custom compilation flags. # EXTRA_LINKER_SCRIPT_CONTENT # Extra content that should be appended to a target linker script. Applicable for ld only. +# EXTRA_LINKER_SCRIPT_EXPORTS +# Extra content that should be added to export section of the linker script. # NO_PCH_SOURCES -# Skip the specified source files by PRECOMPILE_HEADERS feature. +# Exclude the specified source files from PRECOMPILE_HEADERS and UNITY_BUILD builds. function(qt_internal_extend_target target) if(NOT TARGET "${target}") message(FATAL_ERROR "${target} is not a target.") @@ -51,7 +53,7 @@ function(qt_internal_extend_target target) CONDITION CONDITION_INDEPENDENT_SOURCES COMPILE_FLAGS - NO_PCH_SOURCES + EXTRA_LINKER_SCRIPT_EXPORTS ) cmake_parse_arguments(PARSE_ARGV 1 arg @@ -97,10 +99,13 @@ function(qt_internal_extend_target target) get_target_property(target_type ${target} TYPE) set(is_library FALSE) set(is_interface_lib FALSE) + set(is_executable FALSE) if(${target_type} STREQUAL "STATIC_LIBRARY" OR ${target_type} STREQUAL "SHARED_LIBRARY") set(is_library TRUE) elseif(target_type STREQUAL "INTERFACE_LIBRARY") set(is_interface_lib TRUE) + elseif(target_type STREQUAL "EXECUTABLE") + set(is_executable TRUE) endif() foreach(lib ${arg_PUBLIC_LIBRARIES} ${arg_LIBRARIES}) @@ -119,12 +124,22 @@ function(qt_internal_extend_target target) # heuristic way of building the dependency tree between the _sync_headers targets of # different Qt modules. if(TARGET "${lib}") - set(out_genex "$<TARGET_PROPERTY:${lib},_qt_internal_sync_headers_target>") - set_property(TARGET ${target} - APPEND PROPERTY _qt_internal_sync_headers_deps "${out_genex}") + get_target_property(is_imported ${lib} IMPORTED) + if(NOT is_imported) + get_target_property(is_private ${lib} _qt_is_private_module) + if(is_private) + get_target_property(lib ${lib} _qt_public_module_target_name) + endif() + set(out_genex "$<TARGET_PROPERTY:${lib},_qt_internal_sync_headers_target>") + set_property(TARGET ${target} + APPEND PROPERTY _qt_internal_sync_headers_deps "${out_genex}") + endif() endif() endforeach() + list(TRANSFORM arg_PUBLIC_LIBRARIES REPLACE "^Qt::" "${QT_CMAKE_EXPORT_NAMESPACE}::") + list(TRANSFORM arg_LIBRARIES REPLACE "^Qt::" "${QT_CMAKE_EXPORT_NAMESPACE}::") + # Set-up the target # CMake versions less than 3.19 don't support adding the source files to the PRIVATE scope @@ -257,6 +272,234 @@ function(qt_internal_extend_target target) set_target_properties(${target} PROPERTIES _qt_extra_linker_script_content "${arg_EXTRA_LINKER_SCRIPT_CONTENT}") endif() + if(arg_EXTRA_LINKER_SCRIPT_EXPORTS) + set_target_properties(${target} PROPERTIES + _qt_extra_linker_script_exports "${arg_EXTRA_LINKER_SCRIPT_EXPORTS}") + endif() + + if(is_executable) + # If linking against Gui, make sure to also build the default QPA plugin. + # This makes the experience of an initial Qt configuration to build and run one single + # test / executable nicer. + set(linked_libs ${arg_PUBLIC_LIBRARIES} ${arg_LIBRARIES}) + if(linked_libs MATCHES "(^|;)(${QT_CMAKE_EXPORT_NAMESPACE}::|Qt::)?Gui($|;)" AND + TARGET qpa_default_plugins) + add_dependencies("${target}" qpa_default_plugins) + endif() + + # For executables collect static plugins that these targets depend on. + qt_internal_import_plugins(${target} ${linked_libs}) + endif() +endfunction() + +# Given CMAKE_CONFIG and ALL_CMAKE_CONFIGS, determines if a directory suffix needs to be appended +# to each destination, and sets the computed install target destination arguments in OUT_VAR. +# Defaults used for each of the destination types, and can be configured per destination type. +function(qt_get_install_target_default_args) + cmake_parse_arguments(PARSE_ARGV 0 arg + "" + "OUT_VAR;CMAKE_CONFIG;RUNTIME;LIBRARY;ARCHIVE;INCLUDES;BUNDLE" + "ALL_CMAKE_CONFIGS") + _qt_internal_validate_all_args_are_parsed(arg) + + if(NOT arg_CMAKE_CONFIG) + message(FATAL_ERROR "No value given for CMAKE_CONFIG.") + endif() + if(NOT arg_ALL_CMAKE_CONFIGS) + message(FATAL_ERROR "No value given for ALL_CMAKE_CONFIGS.") + endif() + list(LENGTH arg_ALL_CMAKE_CONFIGS all_configs_count) + list(GET arg_ALL_CMAKE_CONFIGS 0 first_config) + + set(suffix "") + if(all_configs_count GREATER 1 AND NOT arg_CMAKE_CONFIG STREQUAL first_config) + set(suffix "/${arg_CMAKE_CONFIG}") + endif() + + set(runtime "${INSTALL_BINDIR}") + if(arg_RUNTIME) + set(runtime "${arg_RUNTIME}") + endif() + + set(library "${INSTALL_LIBDIR}") + if(arg_LIBRARY) + set(library "${arg_LIBRARY}") + endif() + + set(archive "${INSTALL_LIBDIR}") + if(arg_ARCHIVE) + set(archive "${arg_ARCHIVE}") + endif() + + set(includes "${INSTALL_INCLUDEDIR}") + if(arg_INCLUDES) + set(includes "${arg_INCLUDES}") + endif() + + set(bundle "${INSTALL_BINDIR}") + if(arg_BUNDLE) + set(bundle "${arg_BUNDLE}") + endif() + + set(args + RUNTIME DESTINATION "${runtime}${suffix}" + LIBRARY DESTINATION "${library}${suffix}" + ARCHIVE DESTINATION "${archive}${suffix}" COMPONENT Devel + BUNDLE DESTINATION "${bundle}${suffix}" + INCLUDES DESTINATION "${includes}${suffix}") + set(${arg_OUT_VAR} "${args}" PARENT_SCOPE) +endfunction() + +macro(qt_internal_setup_default_target_function_options) + set(__default_private_args + SOURCES + LIBRARIES + INCLUDE_DIRECTORIES + SYSTEM_INCLUDE_DIRECTORIES + DEFINES + DBUS_ADAPTOR_BASENAME + DBUS_ADAPTOR_FLAGS + DBUS_ADAPTOR_SOURCES + DBUS_INTERFACE_BASENAME + DBUS_INTERFACE_FLAGS + DBUS_INTERFACE_SOURCES + FEATURE_DEPENDENCIES + COMPILE_OPTIONS + LINK_OPTIONS + MOC_OPTIONS + DISABLE_AUTOGEN_TOOLS + ENABLE_AUTOGEN_TOOLS + PLUGIN_TYPES + NO_PCH_SOURCES + NO_UNITY_BUILD_SOURCES + ) + set(__default_public_args + PUBLIC_LIBRARIES + PUBLIC_INCLUDE_DIRECTORIES + PUBLIC_DEFINES + PUBLIC_COMPILE_OPTIONS + PUBLIC_LINK_OPTIONS + ) + set(__default_private_module_args + PRIVATE_MODULE_INTERFACE + ) + set(__default_target_info_args + TARGET_VERSION + TARGET_PRODUCT + TARGET_DESCRIPTION + TARGET_COMPANY + TARGET_COPYRIGHT + ) + + # Collection of arguments so they can be shared across qt_internal_add_executable + # and qt_internal_add_test_helper. + set(__qt_internal_add_executable_optional_args + GUI + NO_INSTALL + EXCEPTIONS + DELAY_RC + DELAY_TARGET_INFO + QT_APP + NO_UNITY_BUILD + ) + set(__qt_internal_add_executable_single_args + CORE_LIBRARY + OUTPUT_DIRECTORY + INSTALL_DIRECTORY + VERSION + ${__default_target_info_args} + ) + set(__qt_internal_add_executable_multi_args + ${__default_private_args} + ${__default_public_args} + ) +endmacro() + +# Append a config-specific postfix to library names to ensure distinct names +# in a multi-config build. +# e.g. lib/libQt6DBus_relwithdebinfo.6.3.0.dylib +# Don't apply the postfix to the first encountered release-like config, so we have at least one +# config without a postifx. +# If postfixes are set by user warn about potential issues. +function(qt_internal_setup_cmake_config_postfix) + # Collect configuration that require postfix in Qt library names. + if(QT_GENERATOR_IS_MULTI_CONFIG) + set(postfix_configurations ${CMAKE_CONFIGURATION_TYPES}) + else() + set(postfix_configurations ${CMAKE_BUILD_TYPE}) + + # Set the default postfix to empty by default for single-config builds. + string(TOLOWER "${CMAKE_BUILD_TYPE}" build_type_lower) + set(default_cmake_${build_type_lower}_postfix "") + endif() + + # Override the generic debug postfixes above with custom debug postfixes (even in a single + # config build) to follow the conventions we had since Qt 5. + # e.g. lib/libQt6DBus_debug.6.3.0.dylib + if(WIN32) + if(MINGW) + # On MinGW we don't have "d" suffix for debug libraries like on Linux, + # unless we're building debug and release libraries in one go. + if(QT_GENERATOR_IS_MULTI_CONFIG) + set(default_cmake_debug_postfix "d") + endif() + else() + set(default_cmake_debug_postfix "d") + endif() + elseif(APPLE) + set(default_cmake_debug_postfix "_debug") + endif() + + set(custom_postfix_vars "") + set(release_configs Release RelWithDebInfo MinSizeRel) + set(found_first_release_config FALSE) + foreach(config_type IN LISTS postfix_configurations) + string(TOLOWER "${config_type}" config_type_lower) + string(TOUPPER "${config_type}" config_type_upper) + set(postfix_var CMAKE_${config_type_upper}_POSTFIX) + + # Skip assigning postfix for the first release-like config. + if(NOT found_first_release_config + AND config_type IN_LIST release_configs) + set(found_first_release_config TRUE) + if(NOT "${${postfix_var}}" STREQUAL "") + list(APPEND custom_postfix_vars ${postfix_var}) + endif() + continue() + endif() + + # Check if the default postfix is set, use '_<config_type_lower>' otherwise. + set(default_postfix_var + default_cmake_${config_type_lower}_postfix) + if(NOT DEFINED ${default_postfix_var}) + set(${default_postfix_var} + "_${config_type_lower}") + endif() + + # If postfix is set by user avoid changing it, but save postfix variable that has + # a non-default value for further warning. + if("${${postfix_var}}" STREQUAL "") + set(${postfix_var} "${${default_postfix_var}}") + set(${postfix_var} "${${default_postfix_var}}" PARENT_SCOPE) + elseif(NOT "${${postfix_var}}" STREQUAL "${${default_postfix_var}}") + list(APPEND custom_postfix_vars ${postfix_var}) + endif() + + # Adjust framework postfixes accordingly + if(APPLE) + set(CMAKE_FRAMEWORK_MULTI_CONFIG_POSTFIX_${config_type_upper} + "${${postfix_var}}" PARENT_SCOPE) + endif() + endforeach() + if(custom_postfix_vars) + list(REMOVE_DUPLICATES custom_postfix_vars) + list(JOIN custom_postfix_vars ", " postfix_vars_string) + + message(WARNING "You are using custom library postfixes: '${postfix_vars_string}' which are" + " considered experimental and are not officially supported by Qt." + " Expect unforeseen issues and user projects built with qmake to be broken." + ) + endif() endfunction() function(qt_is_imported_target target out_var) @@ -589,9 +832,8 @@ endif() endif() # INTERFACE libraries don't have IMPORTED_LOCATION-like properties. - # OBJECT libraries have properties like IMPORTED_OBJECTS instead. # Skip the rest of the processing for those. - if(target_type STREQUAL "INTERFACE_LIBRARY" OR target_type STREQUAL "OBJECT_LIBRARY") + if(target_type STREQUAL "INTERFACE_LIBRARY") continue() endif() @@ -602,19 +844,46 @@ endif() # For Multi-config developer builds we should simply reuse IMPORTED_LOCATION of the # target. if(NOT QT_WILL_INSTALL AND QT_FEATURE_debug_and_release) + set(configure_time_target_build_location "") get_target_property(configure_time_target_install_location ${target} IMPORTED_LOCATION) else() + if(IS_ABSOLUTE "${arg_CONFIG_INSTALL_DIR}") + file(RELATIVE_PATH reverse_relative_prefix_path + "${arg_CONFIG_INSTALL_DIR}" "${CMAKE_INSTALL_PREFIX}") + else() + file(RELATIVE_PATH reverse_relative_prefix_path + "${CMAKE_INSTALL_PREFIX}/${arg_CONFIG_INSTALL_DIR}" + "${CMAKE_INSTALL_PREFIX}") + endif() + + get_target_property(configure_time_target_build_location ${target} + _qt_internal_configure_time_target_build_location) + string(TOUPPER "${QT_CMAKE_EXPORT_NAMESPACE}_INSTALL_PREFIX" install_prefix_var) + string(JOIN "" configure_time_target_build_location + "$\{CMAKE_CURRENT_LIST_DIR}/" + "${reverse_relative_prefix_path}" + "${configure_time_target_build_location}") + get_target_property(configure_time_target_install_location ${target} _qt_internal_configure_time_target_install_location) - set(configure_time_target_install_location - "$\{PACKAGE_PREFIX_DIR}/${configure_time_target_install_location}") + + string(JOIN "" configure_time_target_install_location + "$\{CMAKE_CURRENT_LIST_DIR}/" + "${reverse_relative_prefix_path}" + "${configure_time_target_install_location}") endif() if(configure_time_target_install_location) string(APPEND content " # Import configure-time executable ${full_target} if(NOT TARGET ${full_target}) - set(_qt_imported_location \"${configure_time_target_install_location}\") + set(_qt_imported_build_location \"${configure_time_target_build_location}\") + set(_qt_imported_install_location \"${configure_time_target_install_location}\") + set(_qt_imported_location \"\${_qt_imported_install_location}\") + if(NOT EXISTS \"$\{_qt_imported_location}\" + AND NOT \"$\{_qt_imported_build_location}\" STREQUAL \"\") + set(_qt_imported_location \"\${_qt_imported_build_location}\") + endif() if(NOT EXISTS \"$\{_qt_imported_location}\") message(FATAL_ERROR \"Unable to add configure time executable ${full_target}\" \" $\{_qt_imported_location} doesn't exists\") @@ -625,6 +894,8 @@ if(NOT TARGET ${full_target}) \"$\{_qt_imported_location}\") set_property(TARGET ${full_target} PROPERTY IMPORTED_GLOBAL TRUE) unset(_qt_imported_location) + unset(_qt_imported_build_location) + unset(_qt_imported_install_location) endif() \n") endif() @@ -634,7 +905,7 @@ endif() # the target. It is not built by default. if(NOT QT_WILL_INSTALL AND QT_FEATURE_debug_and_release) get_target_property(excluded_genex ${target} EXCLUDE_FROM_ALL) - if(NOT excluded_genex STREQUAL "") + if(excluded_genex) string(APPEND content " # ${full_target} is not built by default in the Debug configuration. Check existence. get_target_property(_qt_imported_location ${full_target} IMPORTED_LOCATION_DEBUG) @@ -649,6 +920,9 @@ endif()\n") set(write_implib FALSE) set(write_soname FALSE) + set(write_objects FALSE) + set(write_location TRUE) + if(target_type STREQUAL "SHARED_LIBRARY") if(WIN32) set(write_implib TRUE) @@ -657,24 +931,41 @@ endif()\n") else() set(write_soname TRUE) endif() + elseif(target_type STREQUAL "OBJECT_LIBRARY") + set(write_objects TRUE) + set(write_location FALSE) endif() if(NOT "${uc_release_cfg}" STREQUAL "") - string(APPEND content "get_target_property(_qt_imported_location ${full_target} IMPORTED_LOCATION_${uc_release_cfg})\n") + if(write_location) + string(APPEND content "get_target_property(_qt_imported_location ${full_target} IMPORTED_LOCATION_${uc_release_cfg})\n") + endif() if(write_implib) string(APPEND content "get_target_property(_qt_imported_implib ${full_target} IMPORTED_IMPLIB_${uc_release_cfg})\n") endif() if(write_soname) string(APPEND content "get_target_property(_qt_imported_soname ${full_target} IMPORTED_SONAME_${uc_release_cfg})\n") endif() + if(write_objects) + string(APPEND content "get_target_property(_qt_imported_objects ${full_target} IMPORTED_OBJECTS_${uc_release_cfg})\n") + # We generate CLR props as well, because that's what CMake generates for object + # libraries with CMake 3.27. They are usually empty strings though, aka "". + string(APPEND content "get_target_property(_qt_imported_clr ${full_target} IMPORTED_COMMON_LANGUAGE_RUNTIME_${uc_release_cfg})\n") + endif() + endif() + if(write_location) + string(APPEND content "get_target_property(_qt_imported_location_default ${full_target} IMPORTED_LOCATION_$\{QT_DEFAULT_IMPORT_CONFIGURATION})\n") endif() - string(APPEND content "get_target_property(_qt_imported_location_default ${full_target} IMPORTED_LOCATION_$\{QT_DEFAULT_IMPORT_CONFIGURATION})\n") if(write_implib) string(APPEND content "get_target_property(_qt_imported_implib_default ${full_target} IMPORTED_IMPLIB_$\{QT_DEFAULT_IMPORT_CONFIGURATION})\n") endif() if(write_soname) string(APPEND content "get_target_property(_qt_imported_soname_default ${full_target} IMPORTED_SONAME_$\{QT_DEFAULT_IMPORT_CONFIGURATION})\n") endif() + if(write_objects) + string(APPEND content "get_target_property(_qt_imported_objects_default ${full_target} IMPORTED_OBJECTS_$\{QT_DEFAULT_IMPORT_CONFIGURATION})\n") + string(APPEND content "get_target_property(_qt_imported_clr_default ${full_target} IMPORTED_COMMON_LANGUAGE_RUNTIME_$\{QT_DEFAULT_IMPORT_CONFIGURATION})\n") + endif() foreach(config ${configurations_to_export} "") string(TOUPPER "${config}" ucconfig) if("${config}" STREQUAL "") @@ -689,10 +980,12 @@ endif()\n") set_property(TARGET ${full_target} APPEND PROPERTY IMPORTED_CONFIGURATIONS ${ucconfig}) ") endif() - string(APPEND content " + if(write_location) + string(APPEND content " if(_qt_imported_location${var_suffix}) set_property(TARGET ${full_target} PROPERTY IMPORTED_LOCATION${property_suffix} \"$\{_qt_imported_location${var_suffix}}\") endif()") + endif() if(write_implib) string(APPEND content " if(_qt_imported_implib${var_suffix}) @@ -705,6 +998,16 @@ if(_qt_imported_soname${var_suffix}) set_property(TARGET ${full_target} PROPERTY IMPORTED_SONAME${property_suffix} \"$\{_qt_imported_soname${var_suffix}}\") endif()") endif() + if(write_objects) + string(APPEND content " +if(_qt_imported_objects${var_suffix}) + set_property(TARGET ${full_target} PROPERTY IMPORTED_OBJECTS${property_suffix} \"$\{_qt_imported_objects${var_suffix}}\") +endif()") + string(APPEND content " +if(_qt_imported_clr${var_suffix}) + set_property(TARGET ${full_target} PROPERTY IMPORTED_COMMON_LANGUAGE_RUNTIME${property_suffix} \"$\{_qt_imported_clr${var_suffix}}\") +endif()") + endif() string(APPEND content "\n") endforeach() endforeach() @@ -715,6 +1018,10 @@ unset(_qt_imported_location) unset(_qt_imported_location_default) unset(_qt_imported_soname) unset(_qt_imported_soname_default) +unset(_qt_imported_objects) +unset(_qt_imported_objects_default) +unset(_qt_imported_clr) +unset(_qt_imported_clr_default) unset(_qt_imported_configs)") endif() @@ -728,25 +1035,51 @@ unset(_qt_imported_configs)") endfunction() function(qt_internal_export_modern_cmake_config_targets_file) - cmake_parse_arguments(__arg "" "EXPORT_NAME_PREFIX;CONFIG_INSTALL_DIR" "TARGETS" ${ARGN}) + cmake_parse_arguments(arg + "" + "EXPORT_NAME_PREFIX;CONFIG_BUILD_DIR;CONFIG_INSTALL_DIR" + "TARGETS" + ${ARGN} + ) - set(export_name "${__arg_EXPORT_NAME_PREFIX}VersionlessTargets") - foreach(target ${__arg_TARGETS}) - if (TARGET "${target}Versionless") - continue() - endif() + if("${arg_TARGETS}" STREQUAL "") + message(FATAL_ERROR "Target list is empty") + endif() - add_library("${target}Versionless" INTERFACE) - target_link_libraries("${target}Versionless" INTERFACE "${target}") - set_target_properties("${target}Versionless" PROPERTIES - EXPORT_NAME "${target}" - _qt_is_versionless_target "TRUE") - set_property(TARGET "${target}Versionless" - APPEND PROPERTY EXPORT_PROPERTIES _qt_is_versionless_target) + if("${arg_CONFIG_BUILD_DIR}" STREQUAL "") + message(FATAL_ERROR "CONFIG_BUILD_DIR is not specified") + endif() - qt_install(TARGETS "${target}Versionless" EXPORT ${export_name}) - endforeach() - qt_install(EXPORT ${export_name} NAMESPACE Qt:: DESTINATION "${__arg_CONFIG_INSTALL_DIR}") + if("${arg_CONFIG_INSTALL_DIR}" STREQUAL "") + message(FATAL_ERROR "CONFIG_INSTALL_DIR is not specified") + endif() + + if("${arg_EXPORT_NAME_PREFIX}" STREQUAL "") + message(FATAL_ERROR "EXPORT_NAME_PREFIX is not specified") + endif() + + set(versionless_targets ${arg_TARGETS}) + + # CMake versions < 3.18 compatibility code. Creates the mimics of the versioned libraries. + set(versionless_targets_export "${arg_CONFIG_BUILD_DIR}/${arg_EXPORT_NAME_PREFIX}VersionlessTargets.cmake") + configure_file("${QT_CMAKE_DIR}/QtVersionlessTargets.cmake.in" + "${versionless_targets_export}" + @ONLY + ) + + # CMake versions >= 3.18 code. Create the versionless ALIAS targets. + set(alias_export "${arg_CONFIG_BUILD_DIR}/${arg_EXPORT_NAME_PREFIX}VersionlessAliasTargets.cmake") + configure_file("${QT_CMAKE_DIR}/QtVersionlessAliasTargets.cmake.in" + "${alias_export}" + @ONLY + ) + + qt_install(FILES + "${alias_export}" + "${versionless_targets_export}" + DESTINATION "${arg_CONFIG_INSTALL_DIR}" + COMPONENT Devel + ) endfunction() function(qt_internal_create_tracepoints name tracepoints_file) @@ -1021,6 +1354,15 @@ endfunction() # Needed to allow selectively applying certain flags via PlatformXInternal targets. function(qt_internal_mark_as_internal_library target) set_target_properties(${target} PROPERTIES _qt_is_internal_library TRUE) + qt_internal_mark_as_internal_target(${target}) +endfunction() + +# Marks a target with a property that it was built using the internal Qt API (qt_internal_*) as +# opposed to it being a user project library or executable(qt_add_*, etc). +# +# Needed to allow selectively applying certain flags via PlatformXInternal targets. +function(qt_internal_mark_as_internal_target target) + set_target_properties(${target} PROPERTIES _qt_is_internal_target TRUE) endfunction() # Marks a target with a property to skip it adding it as a dependency when building examples as @@ -1316,3 +1658,32 @@ function(qt_internal_export_genex_properties) COMPONENT Devel ) endfunction() + +# The macro promotes the Qt platform targets and their dependencies to global. The macro shouldn't +# be called explicitly in regular cases. It's called right after the first find_package(Qt ...) +# call in the qt_internal_project_setup macro. +# This allows using the qt_find_package(Wrap<3rdparty> PROVIDED_TARGETS ...) function, +# without the risk of having duplicated global promotion of Qt internals. This is especially +# sensitive for the bundled 3rdparty libraries. +macro(qt_internal_promote_platform_targets_to_global) + if(TARGET Qt6::Platform) + get_target_property(is_imported Qt6::Platform IMPORTED) + if(is_imported) + set(known_platform_targets + Platform + PlatformCommonInternal + PlatformModuleInternal + PlatformPluginInternal + PlatformAppInternal + PlatformToolInternal + ) + set(versionless_platform_targets ${known_platform_targets}) + + list(TRANSFORM known_platform_targets PREPEND Qt6::) + list(TRANSFORM versionless_platform_targets PREPEND Qt::) + qt_find_package(Qt6 PROVIDED_TARGETS + ${known_platform_targets} + ${versionless_platform_targets}) + endif() + endif() +endmacro() diff --git a/cmake/QtTestHelpers.cmake b/cmake/QtTestHelpers.cmake index 967d8ff99b..62ee91296f 100644 --- a/cmake/QtTestHelpers.cmake +++ b/cmake/QtTestHelpers.cmake @@ -31,11 +31,13 @@ function(qt_internal_add_benchmark target) ) if(NOT arg_OUTPUT_DIRECTORY) - set(arg_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + if(CMAKE_RUNTIME_OUTPUT_DIRECTORY) + set(arg_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") + else() + set(arg_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + endif() endif() - qt_internal_library_deprecation_level(deprecation_define) - qt_internal_add_executable(${target} NO_INSTALL # we don't install benchmarks NO_UNITY_BUILD # excluded by default @@ -214,6 +216,7 @@ function(qt_internal_get_test_arg_definitions optional_args single_value_args mu MANUAL NO_BATCH NO_INSTALL + BUNDLE_ANDROID_OPENSSL_LIBS PARENT_SCOPE ) set(${single_value_args} @@ -246,7 +249,6 @@ function(qt_internal_add_test_to_batch batch_name name) # Lazy-init the test batch if(NOT TARGET ${target}) - qt_internal_library_deprecation_level(deprecation_define) qt_internal_add_executable(${target} ${exceptions_text} ${gui_text} @@ -314,6 +316,34 @@ function(qt_internal_add_test_to_batch batch_name name) list(PREPEND batched_test_list ${name}) set_property(GLOBAL PROPERTY _qt_batched_test_list_property ${batched_test_list}) + # Test batching produces single executable which can result in one source file being added + # multiple times (with different definitions) to one translation unit. This is not supported by + # CMake so instead we try to detect such situation and rename file every time it's added + # to the build more than once. This avoids filenames collisions in one translation unit. + get_property(batched_test_sources_list GLOBAL PROPERTY _qt_batched_test_sources_list_property) + if(NOT batched_test_sources_list) + set_property(GLOBAL PROPERTY _qt_batched_test_sources_list_property "") + set(batched_test_sources_list "") + endif() + foreach(source ${arg_SOURCES}) + set(source_path ${source}) + if(${source} IN_LIST batched_test_sources_list) + set(new_filename ${name}.cpp) + configure_file(${source} ${new_filename}) + set(source_path ${CMAKE_CURRENT_BINARY_DIR}/${new_filename}) + set(skip_automoc ON) + list(APPEND arg_SOURCES ${source_path}) + else() + set(skip_automoc OFF) + list(APPEND batched_test_sources_list ${source}) + endif() + set_source_files_properties(${source_path} + TARGET_DIRECTORY ${target} PROPERTIES + SKIP_AUTOMOC ${skip_automoc} + COMPILE_DEFINITIONS "BATCHED_TEST_NAME=\"${name}\";${arg_DEFINES}") + endforeach() + set_property(GLOBAL PROPERTY _qt_batched_test_sources_list_property ${batched_test_sources_list}) + # Merge the current test with the rest of the batch qt_internal_extend_target(${target} INCLUDE_DIRECTORIES ${arg_INCLUDE_DIRECTORIES} @@ -329,15 +359,6 @@ function(qt_internal_add_test_to_batch batch_name name) NO_UNITY_BUILD # Tests should not be built using UNITY_BUILD ) - foreach(source ${arg_SOURCES}) - # We define the test name which is later used to launch this test using - # commandline parameters. Target directory is that of the target test_batch, - # otherwise the batch won't honor our choices of compile definitions. - set_source_files_properties(${source} - TARGET_DIRECTORY ${target} - PROPERTIES COMPILE_DEFINITIONS - "BATCHED_TEST_NAME=\"${name}\";${arg_DEFINES}" ) - endforeach() set(${batch_name} ${target} PARENT_SCOPE) # Add a dummy target so that new tests don't have problems with a nonexistent @@ -457,8 +478,12 @@ function(qt_internal_add_test name) endif() endif() - if (NOT arg_OUTPUT_DIRECTORY) - set(arg_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + if(NOT arg_OUTPUT_DIRECTORY) + if(CMAKE_RUNTIME_OUTPUT_DIRECTORY) + set(arg_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") + else() + set(arg_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + endif() endif() set(private_includes @@ -485,7 +510,6 @@ function(qt_internal_add_test name) list(APPEND private_includes ${arg_INCLUDE_DIRECTORIES}) qt_internal_prepare_test_target_flags(version_arg exceptions_text gui_text ${ARGN}) - qt_internal_library_deprecation_level(deprecation_define) qt_internal_add_executable("${name}" ${exceptions_text} @@ -538,12 +562,14 @@ function(qt_internal_add_test name) LIBRARIES ${QT_CMAKE_EXPORT_NAMESPACE}::QuickTest ) - qt_internal_extend_target("${name}" CONDITION arg_QMLTEST AND NOT ANDROID + qt_internal_extend_target("${name}" + CONDITION arg_QMLTEST AND NOT ANDROID AND NOT QT_FORCE_BUILTIN_TESTDATA DEFINES QUICK_TEST_SOURCE_DIR="${CMAKE_CURRENT_SOURCE_DIR}" ) - qt_internal_extend_target("${name}" CONDITION arg_QMLTEST AND ANDROID + qt_internal_extend_target("${name}" + CONDITION arg_QMLTEST AND (ANDROID OR QT_FORCE_BUILTIN_TESTDATA) DEFINES QUICK_TEST_SOURCE_DIR=":/" ) @@ -554,6 +580,11 @@ function(qt_internal_add_test name) ) set_target_properties(${name} PROPERTIES _qt_is_test_executable TRUE) set_target_properties(${name} PROPERTIES _qt_is_manual_test ${arg_MANUAL}) + + set(blacklist_file "${CMAKE_CURRENT_SOURCE_DIR}/BLACKLIST") + if(EXISTS ${blacklist_file}) + _qt_internal_expose_source_file_to_ide("${name}" ${blacklist_file}) + endif() endif() foreach(path IN LISTS arg_QML_IMPORTPATH) @@ -579,7 +610,43 @@ function(qt_internal_add_test name) endif() if (ANDROID) - qt_internal_android_test_arguments("${name}" test_executable extra_test_args) + # Pass 95% of the timeout to allow the test runner time to do any cleanup + # before being killed. + set(percentage "95") + qt_internal_get_android_test_timeout("${arg_TIMEOUT}" "${percentage}" android_timeout) + + if(arg_BUNDLE_ANDROID_OPENSSL_LIBS) + if(EXISTS "${OPENSSL_ROOT_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libcrypto_3.so") + message(STATUS "Looking for OpenSSL in ${OPENSSL_ROOT_DIR}") + set_property(TARGET ${name} APPEND PROPERTY QT_ANDROID_EXTRA_LIBS + "${OPENSSL_ROOT_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libcrypto_3.so" + "${OPENSSL_ROOT_DIR}/${CMAKE_ANDROID_ARCH_ABI}/libssl_3.so") + elseif(QT_USE_VCPKG AND DEFINED ENV{VCPKG_ROOT}) + message(STATUS "Looking for OpenSSL in $ENV{VCPKG_ROOT}") + if (CMAKE_ANDROID_ARCH_ABI MATCHES "arm64-v8a") + set(coin_vcpkg_target_triplet "arm64-android-dynamic") + elseif(CMAKE_ANDROID_ARCH_ABI MATCHES "armeabi-v7a") + set(coin_vcpkg_target_triplet "arm-neon-android-dynamic") + elseif(CMAKE_ANDROID_ARCH_ABI MATCHES "x86_64") + set(coin_vcpkg_target_triplet "x64-android-dynamic") + elseif(CMAKE_ANDROID_ARCH_ABI MATCHES "x86") + set(coin_vcpkg_target_triplet "x86-android-dynamic") + endif() + if(EXISTS "$ENV{VCPKG_ROOT}/installed/${coin_vcpkg_target_triplet}/lib/libcrypto.so") + message(STATUS "Found OpenSSL in $ENV{VCPKG_ROOT}/installed/${coin_vcpkg_target_triplet}/lib") + set_property(TARGET ${name} APPEND PROPERTY QT_ANDROID_EXTRA_LIBS + "$ENV{VCPKG_ROOT}/installed/${coin_vcpkg_target_triplet}/lib/libcrypto.so" + "$ENV{VCPKG_ROOT}/installed/${coin_vcpkg_target_triplet}/lib/libssl.so") + endif() + else() + message(STATUS "The argument BUNDLE_ANDROID_OPENSSL_LIBS is set " + "but OPENSSL_ROOT_DIR parameter is not set. " + "Test should bundle OpenSSL libraries but they are not found. " + "This is fine if OpenSSL was built statically.") + endif() + endif() + qt_internal_android_test_arguments( + "${name}" "${android_timeout}" test_executable extra_test_args) set(test_working_dir "${CMAKE_CURRENT_BINARY_DIR}") elseif(QNX) set(test_working_dir "") @@ -602,14 +669,21 @@ function(qt_internal_add_test name) list(APPEND extra_test_args "--browser_args=\"--password-store=basic\"") list(APPEND extra_test_args "--kill_exit") - # We always want to enable asyncify for tests, as some of them use exec + # Tests may require asyncify if they use exec(). Enable asyncify for + # batched tests since this is the configuration used on the CI system. # Optimize for size (-Os), since asyncify tends to make the resulting # binary very large - target_link_options("${name}" PRIVATE "SHELL:-s ASYNCIFY" "-Os") + if(batch_current_test) + target_link_options("${name}" PRIVATE "SHELL:-s ASYNCIFY" "-Os") + endif() # This tells cmake to run the tests with this script, since wasm files can't be # executed directly - set_property(TARGET "${name}" PROPERTY CROSSCOMPILING_EMULATOR "emrun") + if (CMAKE_HOST_WIN32) + set_property(TARGET "${name}" PROPERTY CROSSCOMPILING_EMULATOR "emrun.bat") + else() + set_property(TARGET "${name}" PROPERTY CROSSCOMPILING_EMULATOR "emrun") + endif() else() if(arg_QMLTEST AND NOT arg_SOURCES) set(test_working_dir "${CMAKE_CURRENT_SOURCE_DIR}") @@ -670,6 +744,14 @@ function(qt_internal_add_test name) set_tests_properties(${testname} PROPERTIES TIMEOUT ${arg_TIMEOUT}) endif() + if(ANDROID AND NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + # Set timeout signal and some time for androidtestrunner to do cleanup + set_tests_properties(${testname} PROPERTIES + TIMEOUT_SIGNAL_NAME "SIGINT" + TIMEOUT_SIGNAL_GRACE_PERIOD 10.0 + ) + endif() + # Add a ${target}/check makefile target, to more easily test one test. set(test_config_options "") @@ -690,7 +772,7 @@ function(qt_internal_add_test name) endif() endif() - if(ANDROID OR IOS OR WASM OR INTEGRITY OR arg_BUILTIN_TESTDATA) + if(ANDROID OR IOS OR WASM OR INTEGRITY OR arg_BUILTIN_TESTDATA OR QT_FORCE_BUILTIN_TESTDATA) set(builtin_testdata TRUE) endif() @@ -744,15 +826,27 @@ function(qt_internal_add_test name) endif() endif() else() - # Install test data - file(RELATIVE_PATH relative_path_to_test_project - "${QT_TOP_LEVEL_SOURCE_DIR}" - "${CMAKE_CURRENT_SOURCE_DIR}") - qt_path_join(testdata_install_dir ${QT_INSTALL_DIR} - "${relative_path_to_test_project}") - if (testdata_install_dir) + # Install test data, when tests are built in-tree or as standalone tests, but not as a + # single standalone test, which is checked by the existence of the QT_TOP_LEVEL_SOURCE_DIR + # variable. + # TODO: Shouldn't we also handle the single standalone test case? + # TODO: Does installing even makes sense, given where QFINDTESTDATA looks for installed + # test data, and where we end up installing it? See QTBUG-117098. + if(QT_TOP_LEVEL_SOURCE_DIR) foreach(testdata IN LISTS arg_TESTDATA) set(testdata "${CMAKE_CURRENT_SOURCE_DIR}/${testdata}") + + # Get the relative source dir for each test data entry, because it might contain a + # subdirectory. + file(RELATIVE_PATH relative_path_to_test_project + "${QT_TOP_LEVEL_SOURCE_DIR}" + "${testdata}") + get_filename_component(relative_path_to_test_project + "${relative_path_to_test_project}" DIRECTORY) + + qt_path_join(testdata_install_dir ${QT_INSTALL_DIR} + "${relative_path_to_test_project}") + if (IS_DIRECTORY "${testdata}") qt_install( DIRECTORY "${testdata}" @@ -769,6 +863,35 @@ function(qt_internal_add_test name) qt_internal_add_test_finalizers("${name}") endfunction() +# Given an optional test timeout value (specified via qt_internal_add_test's TIMEOUT option) +# returns a percentage of the final timeout to be passed to the androidtestrunner executable. +# +# When the optional timeout is empty, default to cmake's defaults for getting the timeout. +function(qt_internal_get_android_test_timeout input_timeout percentage output_timeout_var) + set(actual_timeout "${input_timeout}") + if(NOT actual_timeout) + # we have coin ci timeout set use that to avoid having the emulator killed + # so we can at least get some logs from the android test runner. + set(coin_timeout $ENV{COIN_COMMAND_OUTPUT_TIMEOUT}) + if(coin_timeout) + set(actual_timeout "${coin_timeout}") + elseif(DART_TESTING_TIMEOUT) + # Related: https://gitlab.kitware.com/cmake/cmake/-/issues/20450 + set(actual_timeout "${DART_TESTING_TIMEOUT}") + elseif(CTEST_TEST_TIMEOUT) + set(actual_timeout "${CTEST_TEST_TIMEOUT}") + else() + # Default DART_TESTING_TIMEOUT is 25 minutes, specified in seconds + # https://github.com/Kitware/CMake/blob/master/Modules/CTest.cmake#L167C16-L167C16 + set(actual_timeout "1500") + endif() + endif() + + math(EXPR calculated_timeout "${actual_timeout} * ${percentage} / 100") + + set(${output_timeout_var} "${calculated_timeout}" PARENT_SCOPE) +endfunction() + # This function adds test with specified NAME and wraps given test COMMAND with standalone cmake # script. # diff --git a/cmake/QtToolHelpers.cmake b/cmake/QtToolHelpers.cmake index 21c608e304..0aef6a43a3 100644 --- a/cmake/QtToolHelpers.cmake +++ b/cmake/QtToolHelpers.cmake @@ -18,7 +18,10 @@ # TRY_RUN # On Windows, it creates a helper batch script that tests whether the tool can be executed # successfully or not. If not, build halts and an error will be show, with tips on what -# might be cause, and how to fix it. +# might be cause, and how to fix it. TRY_RUN is disabled when cross-compiling. +# TRY_RUN_FLAGS +# Command line flags that are going to be passed to the tool for testing its correctness. +# If no flags were given, we default to `-v`. # # One-value Arguments: # EXTRA_CMAKE_FILES @@ -52,6 +55,7 @@ function(qt_internal_add_tool target_name) TOOLS_TARGET INSTALL_DIR CORE_LIBRARY + TRY_RUN_FLAGS ${__default_target_info_args}) set(multi_value_keywords EXTRA_CMAKE_FILES @@ -96,8 +100,6 @@ function(qt_internal_add_tool target_name) "removed in a future Qt version. Use the LIBRARIES option instead.") endif() - qt_internal_library_deprecation_level(deprecation_define) - if(arg_NO_UNITY_BUILD) set(arg_NO_UNITY_BUILD "NO_UNITY_BUILD") else() @@ -110,11 +112,11 @@ function(qt_internal_add_tool target_name) NO_INSTALL ${arg_NO_UNITY_BUILD} SOURCES ${arg_SOURCES} + NO_PCH_SOURCES ${arg_NO_PCH_SOURCES} NO_UNITY_BUILD_SOURCES ${arg_NO_UNITY_BUILD_SOURCES} INCLUDE_DIRECTORIES ${arg_INCLUDE_DIRECTORIES} DEFINES - QT_USE_QSTRINGBUILDER ${arg_DEFINES} ${deprecation_define} ${corelib} @@ -202,7 +204,7 @@ function(qt_internal_add_tool target_name) OUT_VAR install_targets_default_args RUNTIME "${install_dir}" CMAKE_CONFIG "${cmake_config}" - ALL_CMAKE_CONFIGS "${cmake_configs}") + ALL_CMAKE_CONFIGS ${cmake_configs}) # Make installation optional for targets that are not built by default in this config if(QT_FEATURE_debug_and_release @@ -229,15 +231,18 @@ function(qt_internal_add_tool target_name) qt_internal_apply_staging_prefix_build_rpath_workaround() endif() - if(arg_TRY_RUN AND WIN32) - _qt_internal_add_try_run_post_build(${target_name}) + if(arg_TRY_RUN AND WIN32 AND NOT CMAKE_CROSSCOMPILING) + if(NOT arg_TRY_RUN_FLAGS) + set(arg_TRY_RUN_FLAGS "-v") + endif() + _qt_internal_add_try_run_post_build("${target_name}" "${arg_TRY_RUN_FLAGS}") endif() qt_enable_separate_debug_info(${target_name} "${install_dir}" QT_EXECUTABLE) qt_internal_install_pdb_files(${target_name} "${install_dir}") endfunction() -function(_qt_internal_add_try_run_post_build target) +function(_qt_internal_add_try_run_post_build target try_run_flags) qt_internal_get_upper_case_main_cmake_configuration(main_cmake_configuration) get_target_property(target_out_dir ${target} RUNTIME_OUTPUT_DIRECTORY_${main_cmake_configuration}) @@ -251,7 +256,7 @@ function(_qt_internal_add_try_run_post_build target) qt_configure_file(OUTPUT "${try_run_scripts_path}" CONTENT "@echo off -${target_out_dir}/${target}.exe -h > nul 2>&1 +${target_out_dir}/${target}.exe ${try_run_flags} > nul 2>&1 if \"%errorlevel%\" == \"-1073741515\" ( echo @@ -271,7 +276,8 @@ echo. > ${target_bin_dir}/${target}_try_run_passed" DEPENDS ${target} COMMAND - cmd /c ${try_run_scripts_path} + ${CMAKE_COMMAND} -E env QT_COMMAND_LINE_PARSER_NO_GUI_MESSAGE_BOXES=1 + ${try_run_scripts_path} COMMENT "Testing ${target} by trying to run it." VERBATIM @@ -340,7 +346,7 @@ function(qt_export_tools module_name) string(REGEX REPLACE "_native$" "" tool_name ${tool_name}) endif() set(extra_cmake_statements "${extra_cmake_statements} -if (NOT QT_NO_CREATE_TARGETS) +if(NOT QT_NO_CREATE_TARGETS AND ${INSTALL_CMAKE_NAMESPACE}${target}_FOUND) __qt_internal_promote_target_to_global(${INSTALL_CMAKE_NAMESPACE}::${tool_name}) endif() ") @@ -489,25 +495,34 @@ endfunction() # Sets QT_WILL_BUILD_TOOLS if tools will be built and QT_WILL_RENAME_TOOL_TARGETS # if those tools have replaced naming. function(qt_check_if_tools_will_be_built) - # By default, we build our own tools unless we're cross-building. + # By default, we build our own tools unless we're cross-building or QT_HOST_PATH is set. set(need_target_rename FALSE) + set(require_find_tools FALSE) if(CMAKE_CROSSCOMPILING) set(will_build_tools FALSE) if(QT_FORCE_BUILD_TOOLS) set(will_build_tools TRUE) set(need_target_rename TRUE) endif() + set(require_find_tools TRUE) else() - set(will_build_tools TRUE) + if(QT_HOST_PATH) + set(will_build_tools FALSE) + else() + set(will_build_tools TRUE) + endif() if(QT_FORCE_FIND_TOOLS) set(will_build_tools FALSE) - if(QT_FORCE_BUILD_TOOLS) - set(will_build_tools TRUE) - set(need_target_rename TRUE) - endif() + set(require_find_tools TRUE) + endif() + if(QT_FORCE_BUILD_TOOLS) + set(will_build_tools TRUE) + set(need_target_rename TRUE) endif() endif() + set_property(GLOBAL PROPERTY qt_require_find_tools "${require_find_tools}") + set(QT_WILL_BUILD_TOOLS ${will_build_tools} CACHE INTERNAL "Are tools going to be built" FORCE) set(QT_WILL_RENAME_TOOL_TARGETS ${need_target_rename} CACHE INTERNAL "Do tool targets need to be renamed" FORCE) @@ -546,6 +561,15 @@ function(qt_internal_find_tool out_var target_name tools_target) " (QT_WILL_BUILD_TOOLS is ${QT_WILL_BUILD_TOOLS}).") endif() + if(NOT CMAKE_CROSSCOMPILING) + if(QT_INTERNAL_FORCE_FIND_HOST_TOOLS_MODULE_LIST AND + NOT "${tools_target}" IN_LIST QT_INTERNAL_FORCE_FIND_HOST_TOOLS_MODULE_LIST) + message(STATUS "Tool '${full_name}' will be built from source.") + set(${out_var} "TRUE" PARENT_SCOPE) + return() + endif() + endif() + if(QT_WILL_RENAME_TOOL_TARGETS AND (name STREQUAL target_name)) message(FATAL_ERROR "qt_internal_add_tool must be passed a target obtained from qt_get_tool_target_name.") @@ -659,7 +683,8 @@ function(qt_internal_find_tool out_var target_name tools_target) endif() endif() - if(NOT QT_WILL_BUILD_TOOLS) + get_property(require_find_tools GLOBAL PROPERTY qt_require_find_tools) + if(require_find_tools AND NOT TARGET ${full_name}) if(${${tools_package_name}_FOUND}) set(pkg_found_msg "") string(APPEND pkg_found_msg @@ -676,7 +701,9 @@ function(qt_internal_find_tool out_var target_name tools_target) message(FATAL_ERROR "Failed to find the host tool \"${full_name}\". It is part of " ${pkg_found_msg}) - else() + endif() + + if(QT_WILL_BUILD_TOOLS) message(STATUS "Tool '${full_name}' will be built from source.") endif() set(${out_var} "TRUE" PARENT_SCOPE) diff --git a/cmake/QtToolchainHelpers.cmake b/cmake/QtToolchainHelpers.cmake index b17aa5c075..26b44bb10c 100644 --- a/cmake/QtToolchainHelpers.cmake +++ b/cmake/QtToolchainHelpers.cmake @@ -129,39 +129,41 @@ set(__qt_chainload_toolchain_file \"\${__qt_initially_configured_toolchain_file} endif() if(__qt_embed_toolchain_compilers) list(APPEND init_platform " - set(__qt_initial_c_compiler \"${CMAKE_C_COMPILER}\") - set(__qt_initial_cxx_compiler \"${CMAKE_CXX_COMPILER}\") - if(NOT DEFINED CMAKE_C_COMPILER AND EXISTS \"\${__qt_initial_c_compiler}\") - set(CMAKE_C_COMPILER \"\${__qt_initial_c_compiler}\" CACHE STRING \"\") - endif() - if(NOT DEFINED CMAKE_CXX_COMPILER AND EXISTS \"\${__qt_initial_cxx_compiler}\") - set(CMAKE_CXX_COMPILER \"\${__qt_initial_cxx_compiler}\" CACHE STRING \"\") - endif()") +set(__qt_initial_c_compiler \"${CMAKE_C_COMPILER}\") +set(__qt_initial_cxx_compiler \"${CMAKE_CXX_COMPILER}\") +if(QT_USE_ORIGINAL_COMPILER AND NOT DEFINED CMAKE_C_COMPILER + AND EXISTS \"\${__qt_initial_c_compiler}\") + set(CMAKE_C_COMPILER \"\${__qt_initial_c_compiler}\" CACHE STRING \"\") +endif() +if(QT_USE_ORIGINAL_COMPILER AND NOT DEFINED CMAKE_CXX_COMPILER + AND EXISTS \"\${__qt_initial_cxx_compiler}\") + set(CMAKE_CXX_COMPILER \"\${__qt_initial_cxx_compiler}\" CACHE STRING \"\") +endif()") endif() unset(init_additional_used_variables) if(APPLE) - # For an iOS simulator_and_device build, we should not explicitly set the sysroot, but let - # CMake do it's universal build magic to use one sysroot / sdk per-arch. - # For a single arch / sysroot iOS build, try to use the initially configured sysroot - # path if it exists, otherwise just set the name of the sdk to be used. - # The latter "name" part is important for user projects so that running 'xcodebuild' from - # the command line chooses the correct sdk. + # For an iOS simulator_and_device build, we should not explicitly set the sysroot, + # but let CMake do it's universal build magic to use one sysroot / sdk per-arch. + # For a single arch / sysroot build, try to use the initially configured sysroot + # by name. + # # Also allow to opt out just in case. # # TODO: Figure out if the same should apply to universal macOS builds. + # We want to preserve the sysroot as an SDK name, instead of the path + # that CMake transforms it into in Darwin-initialize.cmake, so we pick + # it out from the cache, where it hasn't been touched by CMake. + set(cmake_sysroot_name "$CACHE{CMAKE_OSX_SYSROOT}") + list(LENGTH CMAKE_OSX_ARCHITECTURES _qt_osx_architectures_count) - if(CMAKE_OSX_SYSROOT AND NOT _qt_osx_architectures_count GREATER 1 AND UIKIT) + if(cmake_sysroot_name AND NOT _qt_osx_architectures_count GREATER 1 AND UIKIT) list(APPEND init_platform " - set(__qt_uikit_sdk \"${QT_UIKIT_SDK}\") - set(__qt_initial_cmake_osx_sysroot \"${CMAKE_OSX_SYSROOT}\") - if(NOT DEFINED CMAKE_OSX_SYSROOT AND EXISTS \"\${__qt_initial_cmake_osx_sysroot}\") - set(CMAKE_OSX_SYSROOT \"\${__qt_initial_cmake_osx_sysroot}\" CACHE PATH \"\") - elseif(NOT DEFINED CMAKE_OSX_SYSROOT AND NOT QT_NO_SET_OSX_SYSROOT) - set(CMAKE_OSX_SYSROOT \"\${__qt_uikit_sdk}\" CACHE PATH \"\") - endif()") +if(NOT DEFINED CMAKE_OSX_SYSROOT) + set(CMAKE_OSX_SYSROOT \"${cmake_sysroot_name}\" CACHE STRING \"\") +endif()") endif() if(CMAKE_OSX_DEPLOYMENT_TARGET) @@ -216,7 +218,7 @@ set(__qt_chainload_toolchain_file \"\${__qt_initially_configured_toolchain_file} qt_internal_get_first_osx_arch(osx_first_arch) list(APPEND init_platform "if((NOT CMAKE_GENERATOR STREQUAL \"Xcode\" AND NOT __qt_toolchain_building_qt_repo) - OR (CMAKE_GENERATOR STREQUAL \"Xcode\" AND __qt_uikit_sdk AND NOT QT_NO_SET_OSX_ARCHITECTURES))") + OR (CMAKE_GENERATOR STREQUAL \"Xcode\" AND __qt_apple_sdk AND NOT QT_NO_SET_OSX_ARCHITECTURES))") list(APPEND init_platform " set(CMAKE_OSX_ARCHITECTURES \"${osx_first_arch}\" CACHE STRING \"\")") list(APPEND init_platform "endif()") diff --git a/cmake/QtUnityBuildHelpers.cmake b/cmake/QtUnityBuildHelpers.cmake index b66c6765ba..e804396f59 100644 --- a/cmake/QtUnityBuildHelpers.cmake +++ b/cmake/QtUnityBuildHelpers.cmake @@ -11,7 +11,14 @@ function(_qt_internal_validate_no_unity_build prefix) endfunction() function(qt_update_ignore_unity_build_sources target sources) - if (sources) - set_source_files_properties(${sources} PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) + if(sources) + # We need to add the TARGET_DIRECTORY scope for targets that have qt_internal_extend_target + # calls in different subdirectories, like in qtgraphs. + set(scope_args) + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18") + set(scope_args TARGET_DIRECTORY ${target}) + endif() + set_source_files_properties(${sources} ${scope_args} + PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON) endif() endfunction() diff --git a/cmake/QtVersionlessAliasTargets.cmake.in b/cmake/QtVersionlessAliasTargets.cmake.in new file mode 100644 index 0000000000..fcf182941c --- /dev/null +++ b/cmake/QtVersionlessAliasTargets.cmake.in @@ -0,0 +1,7 @@ +# Protect against multiple inclusion, which would fail when already imported targets are +# added once more. +_qt_internal_check_multiple_inclusion(_targets_not_defined "@versionless_targets@") + +_qt_internal_create_versionless_alias_targets("${_targets_not_defined}" @INSTALL_CMAKE_NAMESPACE@) + +unset(_targets_not_defined) diff --git a/cmake/QtVersionlessTargets.cmake.in b/cmake/QtVersionlessTargets.cmake.in new file mode 100644 index 0000000000..c809915e65 --- /dev/null +++ b/cmake/QtVersionlessTargets.cmake.in @@ -0,0 +1,7 @@ +# Protect against multiple inclusion, which would fail when already imported targets are +# added once more. +_qt_internal_check_multiple_inclusion(_targets_not_defined "@versionless_targets@") + +_qt_internal_create_versionless_targets("${_targets_not_defined}" @INSTALL_CMAKE_NAMESPACE@) + +unset(_targets_not_defined) diff --git a/cmake/QtWasmHelpers.cmake b/cmake/QtWasmHelpers.cmake index 3eb97fa3cb..41ef5cb0ba 100644 --- a/cmake/QtWasmHelpers.cmake +++ b/cmake/QtWasmHelpers.cmake @@ -18,14 +18,9 @@ function (qt_internal_setup_wasm_target_properties wasmTarget) target_compile_options("${wasmTarget}" INTERFACE "SHELL:-s MEMORY64=1" ) target_link_options("${wasmTarget}" INTERFACE "SHELL:-s MEMORY64=1" -mwasm64) endif() - # Enable MODULARIZE and set EXPORT_NAME, which makes it possible to - # create application instances using a global constructor function, - # e.g. let app_instance = await createQtAppInstance(). - # (as opposed to MODULARIZE=0, where Emscripten creates a global app - # instance object at Javascript eval time) - target_link_options("${wasmTarget}" INTERFACE - "SHELL:-s MODULARIZE=1" - "SHELL:-s EXPORT_NAME=createQtAppInstance") + # Enable MODULARIZE so that we are able to set EXPORT_NAME later and instantiate on demand (with + # MODULARIZE=0, emscripten creates a global app instance object at Javascript eval time) + target_link_options("${wasmTarget}" INTERFACE "SHELL:-s MODULARIZE=1") #simd if (QT_FEATURE_wasm_simd128) @@ -39,8 +34,6 @@ function (qt_internal_setup_wasm_target_properties wasmTarget) if (QT_FEATURE_wasm_exceptions) target_compile_options("${wasmTarget}" INTERFACE -fwasm-exceptions) target_link_options("${wasmTarget}" INTERFACE -fwasm-exceptions) - else() - target_link_options("${wasmTarget}" INTERFACE "SHELL:-s DISABLE_EXCEPTION_CATCHING=1") endif() if (QT_FEATURE_thread) @@ -126,6 +119,7 @@ function (qt_internal_setup_wasm_target_properties wasmTarget) endfunction() function(qt_internal_wasm_add_finalizers target) + qt_add_list_file_finalizer(_qt_internal_set_wasm_export_name ${target}) qt_add_list_file_finalizer(_qt_internal_add_wasm_extra_exported_methods ${target}) qt_add_list_file_finalizer(_qt_internal_wasm_add_target_helpers ${target}) endfunction() diff --git a/cmake/QtWrapperScriptHelpers.cmake b/cmake/QtWrapperScriptHelpers.cmake index e87d0ad2e5..8eb4416e6d 100644 --- a/cmake/QtWrapperScriptHelpers.cmake +++ b/cmake/QtWrapperScriptHelpers.cmake @@ -19,13 +19,18 @@ function(qt_internal_create_wrapper_scripts) set(generate_non_unix TRUE) endif() + set(extra_qt_cmake_code "") if(generate_unix) - if(IOS) - set(infix ".ios") - else() - set(infix "") + + if(UIKIT) + set(extra_qt_cmake_code [=[ +# Specify Xcode as the default generator by assigning it to the CMAKE_GENERATOR env var. +# An explicit -G or -D CMAKE_GENERATOR given on the command line will still take precedence. +export CMAKE_GENERATOR=Xcode +]=]) endif() - configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bin/qt-cmake${infix}.in" + + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bin/qt-cmake.in" "${QT_BUILD_DIR}/${INSTALL_BINDIR}/qt-cmake" @ONLY NEWLINE_STYLE LF) qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_BINDIR}/qt-cmake" @@ -53,15 +58,20 @@ function(qt_internal_create_wrapper_scripts) qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_BINDIR}/qt-cmake-create.bat" DESTINATION "${INSTALL_BINDIR}") endif() - # Provide a private convenience wrapper with options which should not be propagated via the + + # Reset the contents for the next script. + set(extra_qt_cmake_code "") + + # Provide a private convenience wrapper with options that should not be propagated via the # public qt-cmake wrapper e.g. CMAKE_GENERATOR. # These options can not be set in a toolchain file, but only on the command line. # These options should not be in the public wrapper, because a consumer of Qt might want to # build their CMake app with the Unix Makefiles generator, while Qt should be built with the - # Ninja generator. - # The private wrapper is more conveient for building Qt itself, because a developer doesn't need - # to specify the same options for each qt module built. - set(__qt_cmake_extra "-G\"${CMAKE_GENERATOR}\"") + # Ninja generator. In a similar vein, we do want to use the same compiler for all Qt modules, + # but not for user applications. + # The private wrapper is more convenient for building Qt itself, because a developer doesn't + # need to specify the same options for each qt module built. + set(__qt_cmake_extra "-G\"${CMAKE_GENERATOR}\" -DQT_USE_ORIGINAL_COMPILER=ON") if(generate_unix) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bin/qt-cmake.in" "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/qt-cmake-private" @ONLY @@ -202,16 +212,33 @@ function(qt_internal_create_wrapper_scripts) elseif(CMAKE_BUILD_TYPE) set(__qt_configured_configs "${CMAKE_BUILD_TYPE}") endif() + + if( + # Skip stripping pure debug builds so it's easier to debug issues in CI VMs. + (NOT QT_FEATURE_debug_and_release + AND QT_FEATURE_debug + AND NOT QT_FEATURE_separate_debug_info) + + # Skip stripping on MSVC because ${CMAKE_STRIP} might contain a MinGW strip binary + # and the breaks the linker version flag embedded in the binary and causes Qt Creator + # to mis-identify the Kit ABI. + OR MSVC + ) + set(__qt_skip_strip_installed_artifacts TRUE) + else() + set(__qt_skip_strip_installed_artifacts FALSE) + endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bin/${__qt_cmake_install_script_name}.in" "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/${__qt_cmake_install_script_name}" @ONLY) qt_install(FILES "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/${__qt_cmake_install_script_name}" DESTINATION "${INSTALL_LIBEXECDIR}") - qt_internal_create_qt_configure_tests_wrapper_script() - qt_internal_install_android_helper_scripts() + qt_internal_create_qt_configure_part_wrapper_script("STANDALONE_TESTS") + qt_internal_create_qt_configure_part_wrapper_script("STANDALONE_EXAMPLES") + qt_internal_create_qt_configure_redo_script() endfunction() -function(qt_internal_create_qt_configure_tests_wrapper_script) +function(qt_internal_create_qt_configure_part_wrapper_script component) if(QT_GENERATE_WRAPPER_SCRIPTS_FOR_ALL_HOSTS) set(generate_unix TRUE) set(generate_non_unix TRUE) @@ -221,17 +248,27 @@ function(qt_internal_create_qt_configure_tests_wrapper_script) set(generate_non_unix TRUE) endif() - # Create a private wrapper script to configure and build all standalone tests. + # Create a private wrapper script to configure and build all standalone tests / examples. # # The script uses qt-cmake instead of qt-cmake-private on purpose. That's to ensure we build # only one configuration of tests (e.g RelWithDebInfo only) when Qt is configured with more # than one configuration (RelWithDebInfo;Debug). # Meant to be used by our CI instructions. # - # The script takes a path to the repo for which the standalone tests will be configured. - set(script_name "qt-internal-configure-tests") + # The script takes a path to the repo for which the standalone tests / examples will be + # configured. - set(script_passed_args "-DQT_BUILD_STANDALONE_TESTS=ON") + if(component STREQUAL "STANDALONE_TESTS") + set(script_name "qt-internal-configure-tests") + set(script_passed_args "-DQT_BUILD_STANDALONE_TESTS=ON -DQT_BUILD_EXAMPLES=OFF") + elseif(component STREQUAL "STANDALONE_EXAMPLES") + set(script_name "qt-internal-configure-examples") + set(script_passed_args "-DQT_BUILD_STANDALONE_EXAMPLES=ON -DQT_BUILD_TESTS=OFF") + else() + message(FATAL_ERROR "Invalid component type: ${component}") + endif() + + string(APPEND script_passed_args " -DQT_USE_ORIGINAL_COMPILER=ON") file(RELATIVE_PATH relative_path_from_libexec_dir_to_bin_dir ${__qt_libexec_dir_absolute} @@ -257,8 +294,40 @@ function(qt_internal_create_qt_configure_tests_wrapper_script) endif() endfunction() -function(qt_internal_install_android_helper_scripts) - qt_path_join(destination "${QT_INSTALL_DIR}" "${INSTALL_LIBEXECDIR}") - qt_copy_or_install(PROGRAMS "${CMAKE_CURRENT_SOURCE_DIR}/util/android/android_emulator_launcher.sh" - DESTINATION "${destination}") +# Create a shell wrapper script to reconfigure Qt with the original configure arguments and +# any additional ones passed. +# +# Removes CMakeCache.txt and friends, either manually, or using CMake's --fresh. +# +# The script is created in the root of the build dir and is called config.redo +# It has the same contents as the 'config.status' script we created in qt 5. +function(qt_internal_create_qt_configure_redo_script) + set(input_script_name "qt-internal-config.redo") + set(input_script_path "${CMAKE_CURRENT_SOURCE_DIR}/libexec/${input_script_name}") + + # We don't use QT_BUILD_DIR because we want the file in the root of the build dir in a top-level + # build. + set(output_script_name "config.redo") + set(output_path "${CMAKE_BINARY_DIR}/${output_script_name}") + + if(QT_SUPERBUILD) + set(configure_script_path "${Qt_SOURCE_DIR}") + else() + set(configure_script_path "${QtBase_SOURCE_DIR}") + endif() + string(APPEND configure_script_path "/configure") + + # Used in the file contents. + file(TO_NATIVE_PATH "${configure_script_path}" configure_path) + + if(CMAKE_HOST_UNIX) + string(APPEND input_script_path ".in") + set(newline_style "LF") + else() + string(APPEND input_script_path ".bat.in") + string(APPEND output_path ".bat") + set(newline_style "CRLF") + endif() + + configure_file("${input_script_path}" "${output_path}" @ONLY NEWLINE_STYLE ${newline_style}) endfunction() diff --git a/cmake/README.md b/cmake/README.md index ac9d1012cf..0d8179a32c 100644 --- a/cmake/README.md +++ b/cmake/README.md @@ -4,6 +4,11 @@ This document gives an overview of the Qt 6 build system. For a hands-on guide o to build Qt 6, see https://doc.qt.io/qt-6/build-sources.html and https://wiki.qt.io/Building_Qt_6_from_Git +# Contributing + +See qtbase/cmake/CODESTYLE.md for the code style you should follow when contributing +to Qt's cmake files. + # CMake Versions * You need CMake 3.16.0 or later for most platforms (due to new AUTOMOC json feature). @@ -185,7 +190,7 @@ If you don't supply the configuration argument ``-DANDROID_ABI=...``, it will de * x86: ``-DANDROID_ABI=x86`` * x86_64: ``-DANDROID_ABI=x86_64`` -By default we set the android API level to 23. Should you need to change this supply the following +By default we set the android API level to 28. Should you need to change this supply the following configuration argument to the above CMake call: ``-DANDROID_PLATFORM=android-${API_LEVEL}``. ### Cross compiling for iOS @@ -195,13 +200,13 @@ In order to cross-compile Qt to iOS, you need a host macOS build. When running cmake in qtbase, pass ``-DCMAKE_SYSTEM_NAME=iOS -DQT_HOST_PATH=/path/to/your/host/build -DCMAKE_INSTALL_PREFIX=$INSTALL_PATH`` -If you don't supply the configuration argument ``-DQT_UIKIT_SDK=...``, CMake will build a +If you don't supply the configuration argument ``-DQT_APPLE_SDK=...``, CMake will build a multi-arch simulator_and_device iOS build. To target another SDK / device type, use one of the following values: - * iphonesimulator: ``-DQT_UIKIT_SDK=iphonesimulator`` - * iphoneos: ``-DQT_UIKIT_SDK=iphoneos`` + * iphonesimulator: ``-DQT_APPLE_SDK=iphonesimulator`` + * iphoneos: ``-DQT_APPLE_SDK=iphoneos`` -Depending on what value you pass to ``-DQT_UIKIT_SDK=`` a list of target architectures is chosen +Depending on what value you pass to ``-DQT_APPLE_SDK=`` a list of target architectures is chosen by default: * iphonesimulator: ``x86_64`` * iphoneos: ``arm64`` diff --git a/cmake/configure-cmake-mapping.md b/cmake/configure-cmake-mapping.md index 27743cc4e8..6a184f6119 100644 --- a/cmake/configure-cmake-mapping.md +++ b/cmake/configure-cmake-mapping.md @@ -8,6 +8,7 @@ The following table describes the mapping of configure options to CMake argument | -extprefix /opt/qt6 | -DCMAKE_STAGING_PREFIX=/opt/qt6 | | | -bindir <dir> | -DINSTALL_BINDIR=<dir> | similar for -headerdir -libdir and so on | | -hostdatadir <dir> | -DINSTALL_MKSPECSDIR=<dir> | | +| -qt-host-path <dir> | -DQT_HOST_PATH=<dir> | | | -help | n/a | Handled by configure[.bat]. | | -verbose | --log-level=STATUS | Sets the CMake log level to STATUS. The default one is NOTICE. | | -continue | | | @@ -44,10 +45,11 @@ The following table describes the mapping of configure options to CMake argument | -device-option <key=value> | -DQT_QMAKE_DEVICE_OPTIONS=key1=value1;key2=value2 | Only used for generation qmake-compatibility files. | | | | The device options are written into mkspecs/qdevice.pri. | | -appstore-compliant | -DFEATURE_appstore_compliant=ON | | +| -qtinlinenamespace | -DQT_INLINE_NAMESPACE=ON | Make the namespace specified by -qtnamespace an inline one. | | -qtnamespace <name> | -DQT_NAMESPACE=<name> | | | -qtlibinfix <infix> | -DQT_LIBINFIX=<infix> | | -| -testcocoon | | | -| -gcov | | | +| -coverage <tool> | -DINPUT_coverage=<tool> | Enables code coverage using the specified tool. | +| -gcov | -DINPUT_coverage=gcov | Enables code coverage using the gcov tool. | | -trace [backend] | -DINPUT_trace=yes or -DINPUT_trace=<backend> | | | | or -DFEATURE_<backend> | | | -sanitize address -sanitize undefined | -DFEATURE_sanitize_address=ON | Directly setting -DECM_ENABLE_SANITIZERS=foo is not supported | @@ -77,22 +79,25 @@ The following table describes the mapping of configure options to CMake argument | -unity-build-batch-size <int> | -DQT_UNITY_BUILD_BATCH_SIZE=<int> | | | -warnings-are-errors | -DWARNINGS_ARE_ERRORS=ON | | | -no-pkg-config | -DFEATURE_pkg_config=OFF | | +| -vcpkg | -DQT_USE_VCPKG=ON | | | -D <string> | -DQT_EXTRA_DEFINES=<string1>;<string2> | | | -I <string> | -DQT_EXTRA_INCLUDEPATHS=<string1>;<string2> | | | -L <string> | -DQT_EXTRA_LIBDIRS=<string1>;<string2> | | | -F <string> | -DQT_EXTRA_FRAMEWORKPATHS=<string1>;<string2> | | -| -sdk <sdk> | -DQT_UIKIT_SDK=<value> | Should be provided a value like 'iphoneos' or 'iphonesimulator' | +| -sdk <sdk> | -DQT_APPLE_SDK=<value> | Should be provided a value like 'iphoneos' or 'iphonesimulator' | | | | If no value is provided, a simulator_and_device build is | | | | assumed. | | -android-sdk <path> | -DANDROID_SDK_ROOT=<path> | | | -android-ndk <path> | -DCMAKE_TOOLCHAIN_FILE=<toolchain file in NDK> | | -| -android-ndk-platform android-23 | -DANDROID_PLATFORM=android-23 | | +| -android-ndk-platform android-28 | -DANDROID_PLATFORM=android-28 | | | -android-abis <abi_1>,...,<abi_n> | -DANDROID_ABI=<abi_1> | only one ABI can be specified | | -android-style-assets | -DFEATURE_android_style_assets=ON | | | -android-javac-source | -DQT_ANDROID_JAVAC_SOURCE=7 | Set the javac build source version. | | -android-javac-target | -DQT_ANDROID_JAVAC_TARGET=7 | Set the javac build target version. | | -skip <repo>,...,<repo_n> | -DBUILD_<repo>=OFF | | -| -submodules <repo>,...,<repo_n> | -DQT_BUILD_SUBMODULES=<repo>;...;<repo> | | +| -skip-tests <repo>,...,<repo_n> | -DQT_BUILD_TESTS_PROJECT_<repo>=OFF | | +| -skip-examples <repo>,...,<repo_n> | -DQT_BUILD_EXAMPLES_PROJECT_<repo>=OFF | | +| -submodules <repo>,...,<repo_n> | -DQT_BUILD_SUBMODULES=<repo>;...;<repo> | | | -make <part> | -DQT_BUILD_TESTS=ON | A way to turn on tools explicitly is missing. If tests/examples | | | -DQT_BUILD_EXAMPLES=ON | are enabled, you can disable their building as part of the | | | | 'all' target by also passing -DQT_BUILD_TESTS_BY_DEFAULT=OFF or | @@ -102,6 +107,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 | | @@ -112,7 +118,6 @@ The following table describes the mapping of configure options to CMake argument | -doubleconversion | -DFEATURE_doubleconversion=ON | | | | -DFEATURE_system_doubleconversion=ON/OFF | | | -glib | -DFEATURE_glib=ON | | -| -eventfd | -DFEATURE_eventfd=ON | | | -inotify | -DFEATURE_inotify=ON | | | -icu | -DFEATURE_icu=ON | | | -pcre | -DFEATURE_pcre2=ON | | @@ -140,7 +145,8 @@ The following table describes the mapping of configure options to CMake argument | -opengl <api> | -DINPUT_opengl=<api> | | | -opengles3 | -DFEATURE_opengles3=ON | | | -egl | -DFEATURE_egl=ON | | -| -qpa <name> | -DQT_QPA_DEFAULT_PLATFORM=<name> | | +| -qpa <name>;...;<name_n> | -DQT_QPA_PLATFORMS=<name>;...;<name_n> | | +| -default-qpa <name> | -DQT_QPA_DEFAULT_PLATFORM=<name> | | | -xcb-xlib | -DFEATURE_xcb_xlib=ON | | | -direct2d | -DFEATURE_direct2d=ON | | | -directfb | -DFEATURE_directfb=ON | | @@ -159,8 +165,8 @@ The following table describes the mapping of configure options to CMake argument | -xkbcommon | -DFEATURE_xkbcommon=ON | | | -gif | -DFEATURE_gif=ON | | | -ico | -DFEATURE_ico=ON | | -| -libpng | -DFEATURE_libpng=ON | | -| -libjpeg | -DFEATURE_libjpeg=ON | | +| -libpng | -DFEATURE_png=ON | | +| -libjpeg | -DFEATURE_jpeg=ON | | | -sql-<driver> | -DFEATURE_sql_<driver>=ON | | | -sqlite [qt/system] | -DFEATURE_system_sqlite=OFF/ON | | | -disable-deprecated-up-to <hex_version> | -DQT_DISABLE_DEPRECATED_UP_TO=<hex_version> | | diff --git a/cmake/ios/LaunchScreen.storyboard b/cmake/ios/LaunchScreen.storyboard index cfe5d26eba..83df24b618 100644 --- a/cmake/ios/LaunchScreen.storyboard +++ b/cmake/ios/LaunchScreen.storyboard @@ -14,29 +14,7 @@ <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3"> <rect key="frame" x="0.0" y="0.0" width="375" height="667"/> <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/> - <subviews> - <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="obG-Y5-kRd"> - <rect key="frame" x="0.0" y="626.5" width="375" height="20.5"/> - <fontDescription key="fontDescription" type="system" pointSize="17"/> - <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <nil key="highlightedColor"/> - </label> - <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="@QT_IOS_LAUNCH_SCREEN_TEXT@" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb"> - <rect key="frame" x="0.0" y="202" width="375" height="43"/> - <fontDescription key="fontDescription" type="boldSystem" pointSize="36"/> - <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <nil key="highlightedColor"/> - </label> - </subviews> - <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/> - <constraints> - <constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="obG-Y5-kRd" secondAttribute="centerX" id="5cz-MP-9tL"/> - <constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="GJd-Yh-RWb" secondAttribute="centerX" id="Q3B-4B-g5h"/> - <constraint firstItem="obG-Y5-kRd" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" constant="20" symbolic="YES" id="SfN-ll-jLj"/> - <constraint firstAttribute="bottom" secondItem="obG-Y5-kRd" secondAttribute="bottom" constant="20" id="Y44-ml-fuU"/> - <constraint firstItem="GJd-Yh-RWb" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="bottom" multiplier="1/3" constant="1" id="moa-c2-u7t"/> - <constraint firstItem="GJd-Yh-RWb" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" constant="20" symbolic="YES" id="x7j-FC-K8j"/> - </constraints> + <color key="backgroundColor" systemColor="systemBackgroundColor"/> <viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/> </view> </viewController> @@ -45,4 +23,9 @@ <point key="canvasLocation" x="53" y="375"/> </scene> </scenes> + <resources> + <systemColor name="systemBackgroundColor"> + <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/> + </systemColor> + </resources> </document> diff --git a/cmake/ios/PrivacyInfo.xcprivacy b/cmake/ios/PrivacyInfo.xcprivacy new file mode 100644 index 0000000000..d75908da05 --- /dev/null +++ b/cmake/ios/PrivacyInfo.xcprivacy @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>NSPrivacyTracking</key> + <false/> + <key>NSPrivacyCollectedDataTypes</key> + <array/> + <key>NSPrivacyTrackingDomains</key> + <array/> + <key>NSPrivacyAccessedAPITypes</key> + <array/> +</dict> +</plist> diff --git a/cmake/macos/PrivacyInfo.xcprivacy b/cmake/macos/PrivacyInfo.xcprivacy new file mode 100644 index 0000000000..96aff954ea --- /dev/null +++ b/cmake/macos/PrivacyInfo.xcprivacy @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>NSPrivacyTracking</key> + <false/> + <key>NSPrivacyCollectedDataTypes</key> + <array/> + <key>NSPrivacyTrackingDomains</key> + <array/> +</dict> +</plist> diff --git a/cmake/modulecppexports.h.in b/cmake/modulecppexports.h.in index 324e86faa7..4d41a3a2a2 100644 --- a/cmake/modulecppexports.h.in +++ b/cmake/modulecppexports.h.in @@ -19,22 +19,22 @@ #endif #if !defined(QT_BUILD_@module_define_infix@_LIB) && !defined(QT_STATIC) -/* outside library → inline decl + defi */ +/* outside library -> inline decl + defi */ /* static builds treat everything as part of the library, so they never inline */ # define QT_@module_define_infix@_INLINE_SINCE(major, minor) inline # define QT_@module_define_infix@_INLINE_IMPL_SINCE(major, minor) 1 #elif defined(QT_@module_define_infix@_BUILD_REMOVED_API) /* inside library, inside removed_api.cpp: - * keep deprecated API → non-inline decl; - * remove deprecated API → inline decl; + * keep deprecated API -> non-inline decl; + * remove deprecated API -> inline decl; * definition is always available */ # define QT_@module_define_infix@_INLINE_SINCE(major, minor) \ QT_IF_DEPRECATED_SINCE(major, minor, inline, /* not inline */) # define QT_@module_define_infix@_INLINE_IMPL_SINCE(major, minor) 1 #else /* inside library, outside removed_api.cpp: - * keep deprecated API → non-inline decl, no defi; - * remove deprecated API → inline decl, defi */ + * keep deprecated API -> non-inline decl, no defi; + * remove deprecated API -> inline decl, defi */ # define QT_@module_define_infix@_INLINE_SINCE(major, minor) \ QT_IF_DEPRECATED_SINCE(major, minor, inline, /* not inline */) # define QT_@module_define_infix@_INLINE_IMPL_SINCE(major, minor) \ diff --git a/cmake/modulecppexports_p.h.in b/cmake/modulecppexports_p.h.in deleted file mode 100644 index c4e029d5e1..0000000000 --- a/cmake/modulecppexports_p.h.in +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright (C) 2021 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only - -#ifndef @header_base_name_upper@_P_H -#define @header_base_name_upper@_P_H - -// This file is autogenerated. Changes will be overwritten. - -// -// W A R N I N G -// ------------- -// -// This file is not part of the Qt API. It exists purely as an -// implementation detail. This header file may change from version to -// version without notice, or even be removed. -// -// We mean it. -// - -#include <@module_include_name@/@header_base_name@.h> - -#define Q_@module_define_infix@_PRIVATE_EXPORT Q_@module_define_infix@_EXPORT - -#endif // @header_base_name_upper@_P_H diff --git a/cmake/qbatchedtestrunner.in.cpp b/cmake/qbatchedtestrunner.in.cpp index fb49cbbb98..cc49b77e0a 100644 --- a/cmake/qbatchedtestrunner.in.cpp +++ b/cmake/qbatchedtestrunner.in.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2022 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QList> #include <QString> diff --git a/cmake/tests/main.cpp b/cmake/tests/main.cpp index a9b8738990..2cd8b7a103 100644 --- a/cmake/tests/main.cpp +++ b/cmake/tests/main.cpp @@ -1 +1,3 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: BSD-3-Clause int main(int argc, char** argv) { return 0; } diff --git a/cmake/visionos/Info.plist.app.in b/cmake/visionos/Info.plist.app.in new file mode 100644 index 0000000000..984b7aa10b --- /dev/null +++ b/cmake/visionos/Info.plist.app.in @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> + + <key>CFBundlePackageType</key> + <string>APPL</string> + + <key>CFBundleName</key> + <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> + + <key>CFBundleDisplayName</key> + <string>${QT_INTERNAL_DOLLAR_VAR}{PRODUCT_NAME}</string> + + <key>CFBundleIdentifier</key> + <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> + + <key>CFBundleExecutable</key> + <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> + + <key>CFBundleVersion</key> + <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> + + <key>CFBundleShortVersionString</key> + <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> + + <key>CFBundleIconFile</key> + <string>${MACOSX_BUNDLE_ICON_FILE}</string> + + <key>CFBundleDevelopmentRegion</key> + <string>$(DEVELOPMENT_LANGUAGE)</string> + <key>CFBundleAllowMixedLocalizations</key> + <true/> + + <key>CFBundleSupportedPlatforms</key> + <array> + <string>XROS</string> + </array> + + <key>UIApplicationSceneManifest</key> + <dict> + <key>UIApplicationSupportsMultipleScenes</key> + <true/> + <key>UISceneConfigurations</key> + <dict/> + </dict> +</dict> +</plist> diff --git a/cmake/visionos/PrivacyInfo.xcprivacy b/cmake/visionos/PrivacyInfo.xcprivacy new file mode 100644 index 0000000000..d75908da05 --- /dev/null +++ b/cmake/visionos/PrivacyInfo.xcprivacy @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>NSPrivacyTracking</key> + <false/> + <key>NSPrivacyCollectedDataTypes</key> + <array/> + <key>NSPrivacyTrackingDomains</key> + <array/> + <key>NSPrivacyAccessedAPITypes</key> + <array/> +</dict> +</plist> |