diff options
Diffstat (limited to 'cmake')
194 files changed, 12149 insertions, 4933 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/FindWaylandScanner.cmake b/cmake/3rdparty/extra-cmake-modules/find-modules/FindWaylandScanner.cmake index 13291afcda..60adaf4184 100644 --- a/cmake/3rdparty/extra-cmake-modules/find-modules/FindWaylandScanner.cmake +++ b/cmake/3rdparty/extra-cmake-modules/find-modules/FindWaylandScanner.cmake @@ -107,9 +107,6 @@ set_package_properties(WaylandScanner PROPERTIES DESCRIPTION "Executable that converts XML protocol files to C code" ) - -include(CMakeParseArguments) - function(ecm_add_wayland_client_protocol out_var) # Parse arguments set(oneValueArgs PROTOCOL BASENAME) diff --git a/cmake/3rdparty/extra-cmake-modules/find-modules/FindXCB.cmake b/cmake/3rdparty/extra-cmake-modules/find-modules/FindXCB.cmake index d530d2d7f4..26b9bf8963 100644 --- a/cmake/3rdparty/extra-cmake-modules/find-modules/FindXCB.cmake +++ b/cmake/3rdparty/extra-cmake-modules/find-modules/FindXCB.cmake @@ -129,10 +129,6 @@ set(XCB_known_components XVMC ) -# XINPUT is unstable; do not include it by default -set(XCB_default_components ${XCB_known_components}) -list(REMOVE_ITEM XCB_default_components "XINPUT") - # default component info: xcb components have fairly predictable # header files, library names and pkg-config names foreach(_comp ${XCB_known_components}) @@ -141,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) @@ -175,11 +174,6 @@ ecm_find_package_parse_components(XCB DEFAULT_COMPONENTS ${XCB_default_components} ) -list(FIND XCB_components "XINPUT" _XCB_XINPUT_index) -if (NOT _XCB_XINPUT_index EQUAL -1) - message(AUTHOR_WARNING "XINPUT from XCB was requested: this is EXPERIMENTAL and is likely to unavailable on many systems!") -endif() - ecm_find_package_handle_library_components(XCB COMPONENTS ${XCB_components} ) @@ -196,6 +190,6 @@ find_package_handle_standard_args(XCB include(FeatureSummary) set_package_properties(XCB PROPERTIES - URL "http://xcb.freedesktop.org" + URL "https://xcb.freedesktop.org/" DESCRIPTION "X protocol C-language Binding" ) 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/3rdparty/extra-cmake-modules/modules/ECMFindModuleHelpers.cmake b/cmake/3rdparty/extra-cmake-modules/modules/ECMFindModuleHelpers.cmake index 841453998a..a5dabb1074 100644 --- a/cmake/3rdparty/extra-cmake-modules/modules/ECMFindModuleHelpers.cmake +++ b/cmake/3rdparty/extra-cmake-modules/modules/ECMFindModuleHelpers.cmake @@ -123,8 +123,6 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -include(CMakeParseArguments) - macro(ecm_find_package_version_check module_name) if(CMAKE_VERSION VERSION_LESS 2.8.12) message(FATAL_ERROR "CMake 2.8.12 is required by Find${module_name}.cmake") diff --git a/cmake/3rdparty/extra-cmake-modules/qt_attribution.json b/cmake/3rdparty/extra-cmake-modules/qt_attribution.json index 941fb9ea11..1839af3690 100644 --- a/cmake/3rdparty/extra-cmake-modules/qt_attribution.json +++ b/cmake/3rdparty/extra-cmake-modules/qt_attribution.json @@ -9,7 +9,7 @@ "Version": "5.84.0", "License": "BSD-3-Clause", - "LicenseId": "BSD 3-Clause License", + "LicenseId": "BSD-3-Clause", "LicenseFile": "COPYING-CMAKE-SCRIPTS", "Copyright": "Copyright © 2011-2018 The KDE community" } diff --git a/cmake/3rdparty/kwin/qt_attribution.json b/cmake/3rdparty/kwin/qt_attribution.json index 5c22641132..fef6bbe4c3 100644 --- a/cmake/3rdparty/kwin/qt_attribution.json +++ b/cmake/3rdparty/kwin/qt_attribution.json @@ -9,7 +9,7 @@ "Version": "5.13.4", "License": "BSD-3-Clause", - "LicenseId": "BSD 3-Clause License", + "LicenseId": "BSD-3-Clause", "LicenseFile": "COPYING-CMAKE-SCRIPTS", "Copyright": "Copyright 2014 Alex Merry <alex.merry@kde.org> Copyright 2014 Martin Gräßlin <mgraesslin@kde.org>, 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/FindATSPI2.cmake b/cmake/FindATSPI2.cmake index edc6277693..80176db579 100644 --- a/cmake/FindATSPI2.cmake +++ b/cmake/FindATSPI2.cmake @@ -1,9 +1,9 @@ # 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: BSD-3-Clause find_package(PkgConfig QUIET) -pkg_check_modules(ATSPI2 atspi-2 IMPORTED_TARGET) +pkg_check_modules(ATSPI2 IMPORTED_TARGET "atspi-2") if (NOT TARGET PkgConfig::ATSPI2) set(ATSPI2_FOUND 0) diff --git a/cmake/FindDB2.cmake b/cmake/FindDB2.cmake index f67f8479db..31f7bbcae9 100644 --- a/cmake/FindDB2.cmake +++ b/cmake/FindDB2.cmake @@ -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: BSD-3-Clause #.rst: # FindDB2 diff --git a/cmake/FindDirectFB.cmake b/cmake/FindDirectFB.cmake index ea2c53aa25..8c5711f178 100644 --- a/cmake/FindDirectFB.cmake +++ b/cmake/FindDirectFB.cmake @@ -1,9 +1,9 @@ # 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: BSD-3-Clause find_package(PkgConfig QUIET) -pkg_check_modules(DirectFB directfb IMPORTED_TARGET) +pkg_check_modules(DirectFB IMPORTED_TARGET "directfb") if (NOT TARGET PkgConfig::DirectFB) set(DirectFB_FOUND 0) diff --git a/cmake/FindGLESv2.cmake b/cmake/FindGLESv2.cmake index 9a9d079180..4730418209 100644 --- a/cmake/FindGLESv2.cmake +++ b/cmake/FindGLESv2.cmake @@ -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: BSD-3-Clause include(CheckCXXSourceCompiles) diff --git a/cmake/FindGSSAPI.cmake b/cmake/FindGSSAPI.cmake index 8b7cf7d2ee..44594941e3 100644 --- a/cmake/FindGSSAPI.cmake +++ b/cmake/FindGSSAPI.cmake @@ -1,10 +1,10 @@ # 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: BSD-3-Clause find_package(PkgConfig QUIET) -pkg_check_modules(PC_GSSAPI QUIET krb5-gssapi) +pkg_check_modules(PC_GSSAPI QUIET "krb5-gssapi") if (NOT PC_GSSAPI_FOUND) - pkg_check_modules(PC_GSSAPI QUIET mit-krb5-gssapi) + pkg_check_modules(PC_GSSAPI QUIET "mit-krb5-gssapi") endif() find_path(GSSAPI_INCLUDE_DIRS @@ -12,12 +12,24 @@ find_path(GSSAPI_INCLUDE_DIRS HINTS ${PC_GSSAPI_INCLUDEDIR} ) +# On macOS, vcpkg opts for finding frameworks LAST. This is generally fine; +# however, in the case of GSSAPI, `usr/lib/libgssapi_krb5.tbd` which is a +# symlink to `Kerberos.framework` misses a few symols, e.g., +# `___gss_c_nt_hostbased_service_oid_desc`, and it causes build failure. +# So, we need to make sure that we find `GSS.framework`. +set(gssapi_library_names + GSS # framework + gss # solaris + gssapi # FreeBSD + gssapi_krb5 +) +if(APPLE) + list(REMOVE_ITEM gssapi_library_names "gssapi_krb5") +endif() + find_library(GSSAPI_LIBRARIES NAMES - GSS # framework - gss # solaris - gssapi # FreeBSD - gssapi_krb5 + ${gssapi_library_names} HINTS ${PC_GSSAPI_LIBDIR} ) @@ -44,4 +56,3 @@ mark_as_advanced(GSSAPI_INCLUDE_DIRS GSSAPI_LIBRARIES) include(FeatureSummary) set_package_properties(GSSAPI PROPERTIES DESCRIPTION "Generic Security Services Application Program Interface") - diff --git a/cmake/FindGTK3.cmake b/cmake/FindGTK3.cmake index e7ac17d258..221d5a34d1 100644 --- a/cmake/FindGTK3.cmake +++ b/cmake/FindGTK3.cmake @@ -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: BSD-3-Clause find_package(PkgConfig QUIET) @@ -7,7 +7,7 @@ set(__gtk3_required_version "${${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION}") if(__gtk3_required_version) set(__gtk3_required_version " >= ${__gtk3_required_version}") endif() -pkg_check_modules(GTK3 "gtk+-3.0${__gtk3_required_version}" IMPORTED_TARGET) +pkg_check_modules(GTK3 IMPORTED_TARGET "gtk+-3.0${__gtk3_required_version}") if (NOT TARGET PkgConfig::GTK3) set(GTK3_FOUND 0) diff --git a/cmake/FindInterbase.cmake b/cmake/FindInterbase.cmake index 2cef1754c8..95b735e56a 100644 --- a/cmake/FindInterbase.cmake +++ b/cmake/FindInterbase.cmake @@ -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: BSD-3-Clause #.rst: # FindInterbase @@ -24,6 +24,7 @@ find_path(Interbase_INCLUDE_DIR NAMES ibase.h HINTS ${Interbase_INCLUDEDIR} + PATH_SUFFIXES firebird ) find_library(Interbase_LIBRARY diff --git a/cmake/FindLibb2.cmake b/cmake/FindLibb2.cmake index b8704d0da4..bf51c37aed 100644 --- a/cmake/FindLibb2.cmake +++ b/cmake/FindLibb2.cmake @@ -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: BSD-3-Clause # Blake2 contains a reference implementation, libb2 is a more efficient # implementation of a subset of Blake2 functions and should be preferred. @@ -13,7 +13,7 @@ endif() find_package(PkgConfig QUIET) if(PkgConfig_FOUND) - pkg_check_modules(Libb2 libb2 IMPORTED_TARGET) + pkg_check_modules(Libb2 IMPORTED_TARGET "libb2") if (TARGET PkgConfig::Libb2) add_library(Libb2::Libb2 INTERFACE IMPORTED) diff --git a/cmake/FindLibproxy.cmake b/cmake/FindLibproxy.cmake index d4edc9a192..4ee3943220 100644 --- a/cmake/FindLibproxy.cmake +++ b/cmake/FindLibproxy.cmake @@ -1,9 +1,9 @@ # 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: BSD-3-Clause find_package(PkgConfig QUIET) -pkg_check_modules(Libproxy libproxy-1.0 IMPORTED_TARGET) +pkg_check_modules(Libproxy IMPORTED_TARGET "libproxy-1.0") if (NOT TARGET PkgConfig::Libproxy) set(Libproxy_FOUND 0) diff --git a/cmake/FindLibsystemd.cmake b/cmake/FindLibsystemd.cmake index 1a77c53f22..372a718027 100644 --- a/cmake/FindLibsystemd.cmake +++ b/cmake/FindLibsystemd.cmake @@ -1,9 +1,9 @@ # 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: BSD-3-Clause find_package(PkgConfig QUIET) -pkg_check_modules(Libsystemd libsystemd IMPORTED_TARGET) +pkg_check_modules(Libsystemd IMPORTED_TARGET "libsystemd") if (NOT TARGET PkgConfig::Libsystemd) set(Libsystemd_FOUND 0) diff --git a/cmake/FindLibudev.cmake b/cmake/FindLibudev.cmake index df2827c921..57e34659d1 100644 --- a/cmake/FindLibudev.cmake +++ b/cmake/FindLibudev.cmake @@ -1,9 +1,9 @@ # 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: BSD-3-Clause find_package(PkgConfig QUIET) -pkg_check_modules(Libudev libudev IMPORTED_TARGET) +pkg_check_modules(Libudev IMPORTED_TARGET "libudev") if (NOT TARGET PkgConfig::Libudev) set(Libudev_FOUND 0) diff --git a/cmake/FindMimer.cmake b/cmake/FindMimer.cmake new file mode 100644 index 0000000000..5cbc6e6907 --- /dev/null +++ b/cmake/FindMimer.cmake @@ -0,0 +1,98 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# Copyright (C) 2022 Mimer Information Technology +# SPDX-License-Identifier: BSD-3-Clause + +# 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() + +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 ${MimerSQL_include_dir_hints}) + +if(WIN32) + if(CMAKE_SYSTEM_PROCESSOR MATCHES "^(x86|X86)$") + set(MIMER_LIBS_NAMES mimapi32) + elseif(CMAKE_SYSTEM_PROCESSOR MATCHES "^(amd64|AMD64)$") + set(MIMER_LIBS_NAMES mimapi64) + endif() +else() + set(MIMER_LIBS_NAMES mimerapi) +endif() + +find_library(Mimer_LIBRARIES + NAMES ${MIMER_LIBS_NAMES} + HINTS ${MimerSQL_library_hints}) + +include(FindPackageHandleStandardArgs) + +find_package_handle_standard_args(Mimer + REQUIRED_VARS Mimer_LIBRARIES Mimer_INCLUDE_DIR) + + + +# Now try to get the include and library path. +if(Mimer_FOUND) + set(Mimer_INCLUDE_DIRS ${Mimer_INCLUDE_DIR}) + set(Mimer_LIBRARY_DIRS ${Mimer_LIBRARIES}) + if (NOT TARGET MimerSQL::MimerSQL) + add_library(MimerSQL::MimerSQL UNKNOWN IMPORTED) + set_target_properties(MimerSQL::MimerSQL PROPERTIES + IMPORTED_LOCATION "${Mimer_LIBRARY_DIRS}" + INTERFACE_INCLUDE_DIRECTORIES "${Mimer_INCLUDE_DIRS}") + endif () +endif() + +mark_as_advanced(Mimer_INCLUDE_DIR Mimer_LIBRARIES) + +include(FeatureSummary) +set_package_properties(MimerSQL PROPERTIES + URL "https://www.mimer.com" + DESCRIPTION "Mimer client library") diff --git a/cmake/FindMtdev.cmake b/cmake/FindMtdev.cmake index 0a9c1ba27f..489a87f48b 100644 --- a/cmake/FindMtdev.cmake +++ b/cmake/FindMtdev.cmake @@ -1,9 +1,9 @@ # 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: BSD-3-Clause find_package(PkgConfig QUIET) -pkg_check_modules(Mtdev mtdev IMPORTED_TARGET) +pkg_check_modules(Mtdev IMPORTED_TARGET "mtdev") if (NOT TARGET PkgConfig::Mtdev) set(Mtdev_FOUND 0) diff --git a/cmake/FindMySQL.cmake b/cmake/FindMySQL.cmake index 8b41d26a5a..d191d1ac0a 100644 --- a/cmake/FindMySQL.cmake +++ b/cmake/FindMySQL.cmake @@ -1,12 +1,30 @@ # 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: BSD-3-Clause #.rst: # FindMySQL # --------- # # Try to locate the mysql client library. -# If found, this will define the following variables: +# +# By default, pkg-config is used, if available. +# If pkg-config is not available or if you want to disable it, set the ``MySQL_ROOT`` variable. +# The following variables can be set to control the behavior of this find module. +# +# ``MySQL_ROOT`` +# The root directory of the mysql client library's installation. +# ``MySQL_INCLUDE_DIR`` +# The directory containing the include files of the mysql client library. +# If not set, the directory is detected within ``MySQL_ROOT`` using find_path. +# ``MySQL_LIBRARY_DIR`` +# The directory containing the binaries of the mysql client library. +# This is used to detect ``MySQL_LIBRARY`` and passed as HINT to find_library. +# ``MySQL_LIBRARY`` +# The file path to the mysql client library. +# ``MySQL_LIBRARY_DEBUG`` +# The file path to the mysql client library for the DEBUG configuration. +# +# If the mysql client library is found, this will define the following variables: # # ``MySQL_FOUND`` # True if the mysql library is available @@ -21,17 +39,39 @@ # ``MySQL::MySQL`` # The mysql client library -find_package(PkgConfig QUIET) -pkg_check_modules(PC_MySQL QUIET mysqlclient) +if(NOT DEFINED MySQL_ROOT) + find_package(PkgConfig QUIET) +endif() +if(PkgConfig_FOUND AND NOT DEFINED MySQL_ROOT) + pkg_check_modules(PC_MySQL QUIET "mysqlclient") + set(MySQL_include_dir_hints ${PC_MySQL_INCLUDEDIR}) + set(MySQL_library_hints ${PC_MySQL_LIBDIR}) + set(MySQL_library_hints_debug "") +else() + set(MySQL_include_dir_hints "") + if(NOT DEFINED MySQL_LIBRARY_DIR) + set(MySQL_LIBRARY_DIR "${MySQL_ROOT}/lib") + endif() + set(MySQL_library_hints "${MySQL_LIBRARY_DIR}") + set(MySQL_library_hints_debug "${MySQL_LIBRARY_DIR}/debug") +endif() find_path(MySQL_INCLUDE_DIR NAMES mysql.h - HINTS ${PC_MySQL_INCLUDEDIR} + HINTS "${MySQL_include_dir_hints}" PATH_SUFFIXES mysql mariadb) find_library(MySQL_LIBRARY + NO_PACKAGE_ROOT_PATH NAMES libmysql mysql mysqlclient libmariadb mariadb - HINTS ${PC_MySQL_LIBDIR}) + HINTS ${MySQL_library_hints}) + +if(MySQL_library_hints_debug) + find_library(MySQL_LIBRARY_DEBUG + NO_PACKAGE_ROOT_PATH + NAMES libmysql mysql mysqlclient libmariadb mariadb + HINTS ${MySQL_library_hints_debug}) +endif() include(FindPackageHandleStandardArgs) find_package_handle_standard_args(MySQL DEFAULT_MSG MySQL_LIBRARY MySQL_INCLUDE_DIR) @@ -44,6 +84,10 @@ if(MySQL_FOUND) set_target_properties(MySQL::MySQL PROPERTIES IMPORTED_LOCATION "${MySQL_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${MySQL_INCLUDE_DIRS}") + if(MySQL_LIBRARY_DEBUG) + set_target_properties(MySQL::MySQL PROPERTIES + IMPORTED_LOCATION_DEBUG "${MySQL_LIBRARY_DEBUG}") + endif() endif() endif() diff --git a/cmake/FindOracle.cmake b/cmake/FindOracle.cmake index 1a19c015c1..e05bdd749e 100644 --- a/cmake/FindOracle.cmake +++ b/cmake/FindOracle.cmake @@ -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: BSD-3-Clause #.rst: # FindOracle diff --git a/cmake/FindPPS.cmake b/cmake/FindPPS.cmake index 334f577151..099019243c 100644 --- a/cmake/FindPPS.cmake +++ b/cmake/FindPPS.cmake @@ -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: BSD-3-Clause # Find the PPS library @@ -18,9 +18,7 @@ find_package_handle_standard_args(PPS DEFAULT_MSG PPS_INCLUDE_DIR PPS_LIBRARY) mark_as_advanced(PPS_INCLUDE_DIR PPS_LIBRARY) if(PPS_FOUND) - add_library(__PPS INTERFACE IMPORTED) - target_link_libraries(__PPS INTERFACE ${PPS_LIBRARY}) - target_include_directories(__PPS INTERFACE ${PPS_INCLUDE_DIR}) - - add_library(PPS::PPS ALIAS __PPS) + add_library(PPS::PPS INTERFACE IMPORTED) + target_link_libraries(PPS::PPS INTERFACE "${PPS_LIBRARY}") + target_include_directories(PPS::PPS INTERFACE "${PPS_INCLUDE_DIR}") endif() diff --git a/cmake/FindPostgreSQL.cmake b/cmake/FindPostgreSQL.cmake index 63dcebf812..a61bec9337 100644 --- a/cmake/FindPostgreSQL.cmake +++ b/cmake/FindPostgreSQL.cmake @@ -1,6 +1,6 @@ # Copyright (C) 2000-2022 Kitware, Inc. and Contributors. # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: (LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0) AND BSD-3-Clause +# SPDX-License-Identifier: BSD-3-Clause #[=======================================================================[.rst: FindPostgreSQL @@ -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/FindSlog2.cmake b/cmake/FindSlog2.cmake index c24eafe260..18c10773f9 100644 --- a/cmake/FindSlog2.cmake +++ b/cmake/FindSlog2.cmake @@ -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: BSD-3-Clause # Find the Slog2 library diff --git a/cmake/FindTslib.cmake b/cmake/FindTslib.cmake index e150023262..f5243dfa7f 100644 --- a/cmake/FindTslib.cmake +++ b/cmake/FindTslib.cmake @@ -1,9 +1,9 @@ # 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: BSD-3-Clause find_package(PkgConfig QUIET) -pkg_check_modules(Tslib tslib IMPORTED_TARGET) +pkg_check_modules(Tslib IMPORTED_TARGET "tslib") if (NOT TARGET PkgConfig::Tslib) set(Tslib_FOUND 0) diff --git a/cmake/FindWrapAtomic.cmake b/cmake/FindWrapAtomic.cmake index b1f9db2368..04768e6490 100644 --- a/cmake/FindWrapAtomic.cmake +++ b/cmake/FindWrapAtomic.cmake @@ -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: BSD-3-Clause # We can't create the same interface imported target multiple times, CMake will complain if we do # that. This can happen if the find_package call is done in multiple different subdirectories. diff --git a/cmake/FindWrapBacktrace.cmake b/cmake/FindWrapBacktrace.cmake index d6048ae4fa..3cc5748dd1 100644 --- a/cmake/FindWrapBacktrace.cmake +++ b/cmake/FindWrapBacktrace.cmake @@ -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: BSD-3-Clause if(TARGET WrapBacktrace::WrapBacktrace) set(WrapBacktrace_FOUND ON) diff --git a/cmake/FindWrapBrotli.cmake b/cmake/FindWrapBrotli.cmake index 2f3c3f747a..e2d7b564f6 100644 --- a/cmake/FindWrapBrotli.cmake +++ b/cmake/FindWrapBrotli.cmake @@ -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: BSD-3-Clause if(TARGET WrapBrotli::WrapBrotliDec) set(WrapBrotli_FOUND ON) @@ -22,21 +22,21 @@ if (unofficial-brotli_FOUND) else() find_package(PkgConfig QUIET) if (PKG_CONFIG_FOUND) - pkg_check_modules(libbrotlidec QUIET libbrotlidec IMPORTED_TARGET) + pkg_check_modules(libbrotlidec QUIET IMPORTED_TARGET "libbrotlidec") if (libbrotlidec_FOUND) add_library(WrapBrotli::WrapBrotliDec INTERFACE IMPORTED) target_link_libraries(WrapBrotli::WrapBrotliDec INTERFACE PkgConfig::libbrotlidec) set(WrapBrotli_FOUND ON) endif() - pkg_check_modules(libbrotlienc QUIET libbrotlienc IMPORTED_TARGET) + pkg_check_modules(libbrotlienc QUIET IMPORTED_TARGET "libbrotlienc") if (libbrotlienc_FOUND) add_library(WrapBrotli::WrapBrotliEnc INTERFACE IMPORTED) target_link_libraries(WrapBrotli::WrapBrotliEnc INTERFACE PkgConfig::libbrotlienc) set(WrapBrotli_FOUND ON) endif() - pkg_check_modules(libbrotlicommon QUIET libbrotlicommon IMPORTED_TARGET) + pkg_check_modules(libbrotlicommon QUIET IMPORTED_TARGET "libbrotlicommon") if (libbrotlicommon_FOUND) add_library(WrapBrotli::WrapBrotliCommon INTERFACE IMPORTED) target_link_libraries(WrapBrotli::WrapBrotliCommon INTERFACE PkgConfig::libbrotlicommon) diff --git a/cmake/FindWrapDBus1.cmake b/cmake/FindWrapDBus1.cmake index 8b6952711e..e2a58790c4 100644 --- a/cmake/FindWrapDBus1.cmake +++ b/cmake/FindWrapDBus1.cmake @@ -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: BSD-3-Clause # DBus1 is buggy and breaks PKG_CONFIG environment. # Work around that:-/ diff --git a/cmake/FindWrapFreetype.cmake b/cmake/FindWrapFreetype.cmake index 8584776a0a..22ae040f62 100644 --- a/cmake/FindWrapFreetype.cmake +++ b/cmake/FindWrapFreetype.cmake @@ -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: BSD-3-Clause include(QtFindWrapHelper NO_POLICY_SCOPE) diff --git a/cmake/FindWrapHarfbuzz.cmake b/cmake/FindWrapHarfbuzz.cmake index c01b208c5c..60b0a97195 100644 --- a/cmake/FindWrapHarfbuzz.cmake +++ b/cmake/FindWrapHarfbuzz.cmake @@ -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: BSD-3-Clause include(QtFindWrapHelper NO_POLICY_SCOPE) diff --git a/cmake/FindWrapJpeg.cmake b/cmake/FindWrapJpeg.cmake index 1bae65858c..5714c1dc34 100644 --- a/cmake/FindWrapJpeg.cmake +++ b/cmake/FindWrapJpeg.cmake @@ -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: BSD-3-Clause include(QtFindWrapHelper NO_POLICY_SCOPE) diff --git a/cmake/FindWrapOpenGL.cmake b/cmake/FindWrapOpenGL.cmake index 538bb021a7..7295a159ca 100644 --- a/cmake/FindWrapOpenGL.cmake +++ b/cmake/FindWrapOpenGL.cmake @@ -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: BSD-3-Clause # We can't create the same interface imported target multiple times, CMake will complain if we do # that. This can happen if the find_package call is done in multiple different subdirectories. @@ -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/FindWrapOpenSSL.cmake b/cmake/FindWrapOpenSSL.cmake index 15ae1086e6..a4341271fe 100644 --- a/cmake/FindWrapOpenSSL.cmake +++ b/cmake/FindWrapOpenSSL.cmake @@ -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: BSD-3-Clause # We can't create the same interface imported target multiple times, CMake will complain if we do # that. This can happen if the find_package call is done in multiple different subdirectories. diff --git a/cmake/FindWrapOpenSSLHeaders.cmake b/cmake/FindWrapOpenSSLHeaders.cmake index dcc5eda798..510ae10854 100644 --- a/cmake/FindWrapOpenSSLHeaders.cmake +++ b/cmake/FindWrapOpenSSLHeaders.cmake @@ -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: BSD-3-Clause # We can't create the same interface imported target multiple times, CMake will complain if we do # that. This can happen if the find_package call is done in multiple different subdirectories. diff --git a/cmake/FindWrapPCRE2.cmake b/cmake/FindWrapPCRE2.cmake index cd9f08ecaa..024a9f084a 100644 --- a/cmake/FindWrapPCRE2.cmake +++ b/cmake/FindWrapPCRE2.cmake @@ -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: BSD-3-Clause include(QtFindWrapHelper NO_POLICY_SCOPE) diff --git a/cmake/FindWrapPNG.cmake b/cmake/FindWrapPNG.cmake index 566427a76a..ba69628ac1 100644 --- a/cmake/FindWrapPNG.cmake +++ b/cmake/FindWrapPNG.cmake @@ -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: BSD-3-Clause include(QtFindWrapHelper NO_POLICY_SCOPE) diff --git a/cmake/FindWrapResolv.cmake b/cmake/FindWrapResolv.cmake new file mode 100644 index 0000000000..1afd253ee8 --- /dev/null +++ b/cmake/FindWrapResolv.cmake @@ -0,0 +1,53 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# Copyright (C) 2023 Intel Corpotation. +# SPDX-License-Identifier: BSD-3-Clause + +# We can't create the same interface imported target multiple times, CMake will complain if we do +# that. This can happen if the find_package call is done in multiple different subdirectories. +if(TARGET WrapResolv::WrapResolv) + set(WrapResolv_FOUND ON) + return() +endif() + +set(WrapResolv_FOUND OFF) + +include(CheckCXXSourceCompiles) +include(CMakePushCheckState) + +if(QNX) + find_library(LIBRESOLV socket) +else() + find_library(LIBRESOLV resolv) +endif() + +cmake_push_check_state() +if(LIBRESOLV) + list(APPEND CMAKE_REQUIRED_LIBRARIES "${LIBRESOLV}") +endif() + +check_cxx_source_compiles(" +#include <netinet/in.h> +#include <resolv.h> + +int main(int, char **argv) +{ + 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); + return n; +} +" HAVE_LIBRESOLV_FUNCTIONS) + +cmake_pop_check_state() + +if(HAVE_LIBRESOLV_FUNCTIONS) + set(WrapResolv_FOUND ON) + add_library(WrapResolv::WrapResolv INTERFACE IMPORTED) + if(LIBRESOLV) + target_link_libraries(WrapResolv::WrapResolv INTERFACE "${LIBRESOLV}") + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(WrapResolv DEFAULT_MSG WrapResolv_FOUND) diff --git a/cmake/FindWrapRt.cmake b/cmake/FindWrapRt.cmake index b05ddaad8f..b394b062da 100644 --- a/cmake/FindWrapRt.cmake +++ b/cmake/FindWrapRt.cmake @@ -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: BSD-3-Clause # We can't create the same interface imported target multiple times, CMake will complain if we do # that. This can happen if the find_package call is done in multiple different subdirectories. @@ -21,17 +21,31 @@ if(LIBRT) endif() check_cxx_source_compiles(" -#include <unistd.h> #include <time.h> +#include <unistd.h> int main(int, char **) { - timespec ts; clock_gettime(CLOCK_REALTIME, &ts); -}" HAVE_GETTIME) + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + return 0; +} +" HAVE_GETTIME) -cmake_pop_check_state() +check_cxx_source_compiles(" +#include <sys/types.h> +#include <sys/mman.h> +#include <fcntl.h> +int main(int, char **) { + shm_open(\"test\", O_RDWR | O_CREAT | O_EXCL, 0666); + shm_unlink(\"test\"); + return 0; +} +" HAVE_SHM_OPEN_SHM_UNLINK) + +cmake_pop_check_state() -if(HAVE_GETTIME) +if(HAVE_GETTIME OR HAVE_SHM_OPEN_SHM_UNLINK) set(WrapRt_FOUND ON) add_library(WrapRt::WrapRt INTERFACE IMPORTED) if (LIBRT) diff --git a/cmake/FindWrapSystemDoubleConversion.cmake b/cmake/FindWrapSystemDoubleConversion.cmake index cb3a16c2c7..e0cbc946df 100644 --- a/cmake/FindWrapSystemDoubleConversion.cmake +++ b/cmake/FindWrapSystemDoubleConversion.cmake @@ -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: BSD-3-Clause # We can't create the same interface imported target multiple times, CMake will complain if we do # that. This can happen if the find_package call is done in multiple different subdirectories. diff --git a/cmake/FindWrapSystemFreetype.cmake b/cmake/FindWrapSystemFreetype.cmake index 1a56b8773c..d0c27a9f0f 100644 --- a/cmake/FindWrapSystemFreetype.cmake +++ b/cmake/FindWrapSystemFreetype.cmake @@ -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: BSD-3-Clause # We can't create the same interface imported target multiple times, CMake will complain if we do # that. This can happen if the find_package call is done in multiple different subdirectories. diff --git a/cmake/FindWrapSystemHarfbuzz.cmake b/cmake/FindWrapSystemHarfbuzz.cmake index b6461be4d3..07b3405bc0 100644 --- a/cmake/FindWrapSystemHarfbuzz.cmake +++ b/cmake/FindWrapSystemHarfbuzz.cmake @@ -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: BSD-3-Clause # We can't create the same interface imported target multiple times, CMake will complain if we do # that. This can happen if the find_package call is done in multiple different subdirectories. @@ -32,7 +32,7 @@ endif() if(__harfbuzz_broken_config_file OR NOT __harfbuzz_found) find_package(PkgConfig QUIET) - pkg_check_modules(PC_HARFBUZZ harfbuzz IMPORTED_TARGET) + pkg_check_modules(PC_HARFBUZZ IMPORTED_TARGET "harfbuzz") if(PC_HARFBUZZ_FOUND) set(__harfbuzz_target_name "PkgConfig::PC_HARFBUZZ") set(__harfbuzz_find_include_dirs_hints diff --git a/cmake/FindWrapSystemJpeg.cmake b/cmake/FindWrapSystemJpeg.cmake index e9bfd3f50e..16a9e5ab23 100644 --- a/cmake/FindWrapSystemJpeg.cmake +++ b/cmake/FindWrapSystemJpeg.cmake @@ -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: BSD-3-Clause if(TARGET WrapSystemJpeg::WrapSystemJpeg) set(WrapSystemJpeg_FOUND TRUE) diff --git a/cmake/FindWrapSystemMd4c.cmake b/cmake/FindWrapSystemMd4c.cmake index 538a356b7b..5ac3ded975 100644 --- a/cmake/FindWrapSystemMd4c.cmake +++ b/cmake/FindWrapSystemMd4c.cmake @@ -1,27 +1,43 @@ # 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: BSD-3-Clause if(TARGET WrapSystemMd4c::WrapSystemMd4c) set(WrapSystemMd4c_FOUND TRUE) return() endif() +set(WrapSystemMd4c_REQUIRED_VARS __md4c_found) -find_package(md4c CONFIG) +find_package(md4c ${${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION} QUIET) -# md4c provides a md4c::md4c target but -# older versions create a md4c target without -# namespace. If we find the old variant create -# a namespaced target out of the md4c target. -if(TARGET md4c AND NOT TARGET md4c::md4c) - add_library(md4c::md4c INTERFACE IMPORTED) - target_link_libraries(md4c::md4c INTERFACE md4c) +set(__md4c_target_name "md4c::md4c") + +if(md4c_FOUND) + set(__md4c_found TRUE) + + # md4c provides a md4c::md4c target but + # older versions create a md4c target without + # namespace. If we find the old variant create + # a namespaced target out of the md4c target. + if(TARGET md4c AND NOT TARGET ${__md4c_target_name}) + add_library(${__md4c_target_name} INTERFACE IMPORTED) + target_link_libraries(${__md4c_target_name} INTERFACE md4c) + endif() + + if(md4c_VERSION) + set(WrapSystemMd4c_VERSION "${md4c_VERSION}") + endif() endif() -if(TARGET md4c::md4c) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(WrapSystemMd4c + REQUIRED_VARS ${WrapSystemMd4c_REQUIRED_VARS} + VERSION_VAR WrapSystemMd4c_VERSION) + +if(WrapSystemMd4c_FOUND) add_library(WrapSystemMd4c::WrapSystemMd4c INTERFACE IMPORTED) - target_link_libraries(WrapSystemMd4c::WrapSystemMd4c INTERFACE md4c::md4c) + target_link_libraries(WrapSystemMd4c::WrapSystemMd4c + INTERFACE "${__md4c_target_name}") endif() -if(TARGET WrapSystemMd4c::WrapSystemMd4c) - set(WrapSystemMd4c_FOUND TRUE) -endif() +unset(__md4c_found) +unset(__md4c_target_name) diff --git a/cmake/FindWrapSystemPCRE2.cmake b/cmake/FindWrapSystemPCRE2.cmake index cdf2cbfc54..61e0d2fb5b 100644 --- a/cmake/FindWrapSystemPCRE2.cmake +++ b/cmake/FindWrapSystemPCRE2.cmake @@ -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: BSD-3-Clause if(TARGET WrapSystemPCRE2::WrapSystemPCRE2) set(WrapSystemPCRE2_FOUND TRUE) @@ -9,11 +9,7 @@ set(WrapSystemPCRE2_REQUIRED_VARS __pcre2_found) find_package(PCRE2 ${${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION} COMPONENTS 16BIT QUIET) -# TODO: pcre2-16 is not the target name provided by the upstream Config file. It is PCRE2::16BIT. -# https://github.com/PCRE2Project/pcre2/blob/2410fbe3869cab403f02b94caa9ab37ee9f5854b/cmake/pcre2-config.cmake.in#L122 -# We don't strictly need to handle that though, because the pkg-config code path below still -# finds the correct libraries. -set(__pcre2_target_name "PCRE2::pcre2-16") +set(__pcre2_target_name "PCRE2::16BIT") if(PCRE2_FOUND AND TARGET "${__pcre2_target_name}") # Hunter case. set(__pcre2_found TRUE) @@ -26,7 +22,7 @@ if(NOT __pcre2_found) list(PREPEND WrapSystemPCRE2_REQUIRED_VARS PCRE2_LIBRARIES PCRE2_INCLUDE_DIRS) find_package(PkgConfig QUIET) - pkg_check_modules(PC_PCRE2 QUIET libpcre2-16) + pkg_check_modules(PC_PCRE2 QUIET "libpcre2-16") find_path(PCRE2_INCLUDE_DIRS NAMES pcre2.h diff --git a/cmake/FindWrapSystemPNG.cmake b/cmake/FindWrapSystemPNG.cmake index c7cc0b98f0..967ccc5c02 100644 --- a/cmake/FindWrapSystemPNG.cmake +++ b/cmake/FindWrapSystemPNG.cmake @@ -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: BSD-3-Clause # We can't create the same interface imported target multiple times, CMake will complain if we do # that. This can happen if the find_package call is done in multiple different subdirectories. diff --git a/cmake/FindWrapSystemZLIB.cmake b/cmake/FindWrapSystemZLIB.cmake index 62c9947df1..5db43db626 100644 --- a/cmake/FindWrapSystemZLIB.cmake +++ b/cmake/FindWrapSystemZLIB.cmake @@ -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: BSD-3-Clause # We can't create the same interface imported target multiple times, CMake will complain if we do # that. This can happen if the find_package call is done in multiple different subdirectories. diff --git a/cmake/FindWrapVulkan.cmake b/cmake/FindWrapVulkan.cmake index 4b4de35952..843ca4c202 100644 --- a/cmake/FindWrapVulkan.cmake +++ b/cmake/FindWrapVulkan.cmake @@ -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: BSD-3-Clause # We can't create the same interface imported target multiple times, CMake will complain if we do # that. This can happen if the find_package call is done in multiple different subdirectories. diff --git a/cmake/FindWrapVulkanHeaders.cmake b/cmake/FindWrapVulkanHeaders.cmake index fdee976c60..92510ae000 100644 --- a/cmake/FindWrapVulkanHeaders.cmake +++ b/cmake/FindWrapVulkanHeaders.cmake @@ -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: BSD-3-Clause # We can't create the same interface imported target multiple times, CMake will complain if we do # that. This can happen if the find_package call is done in multiple different subdirectories. @@ -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/FindWrapZLIB.cmake b/cmake/FindWrapZLIB.cmake index e01a9aaef8..6cf60fab9f 100644 --- a/cmake/FindWrapZLIB.cmake +++ b/cmake/FindWrapZLIB.cmake @@ -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: BSD-3-Clause include(QtFindWrapHelper NO_POLICY_SCOPE) diff --git a/cmake/FindWrapZSTD.cmake b/cmake/FindWrapZSTD.cmake index 7c82190c21..fb424236b8 100644 --- a/cmake/FindWrapZSTD.cmake +++ b/cmake/FindWrapZSTD.cmake @@ -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: BSD-3-Clause #.rst: # FindZstd @@ -28,10 +28,10 @@ include(FindPackageHandleStandardArgs) if(TARGET zstd::libzstd_static OR TARGET zstd::libzstd_shared) find_package_handle_standard_args(WrapZSTD REQUIRED_VARS zstd_VERSION VERSION_VAR zstd_VERSION) - if(TARGET zstd::libzstd_static) - set(zstdtargetsuffix "_static") - else() + if(TARGET zstd::libzstd_shared) set(zstdtargetsuffix "_shared") + else() + set(zstdtargetsuffix "_static") endif() if(NOT TARGET WrapZSTD::WrapZSTD) add_library(WrapZSTD::WrapZSTD INTERFACE IMPORTED) @@ -40,7 +40,7 @@ if(TARGET zstd::libzstd_static OR TARGET zstd::libzstd_shared) endif() else() find_package(PkgConfig QUIET) - pkg_check_modules(PC_ZSTD QUIET libzstd) + pkg_check_modules(PC_ZSTD QUIET "libzstd") find_path(ZSTD_INCLUDE_DIRS NAMES zstd.h diff --git a/cmake/FindXKB_COMMON_X11.cmake b/cmake/FindXKB_COMMON_X11.cmake index 03fdd89fa4..a00acb3cb0 100644 --- a/cmake/FindXKB_COMMON_X11.cmake +++ b/cmake/FindXKB_COMMON_X11.cmake @@ -1,9 +1,9 @@ # 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: BSD-3-Clause find_package(PkgConfig QUIET) -pkg_check_modules(XKB_COMMON_X11 "xkbcommon-x11>=0.4.1" IMPORTED_TARGET) +pkg_check_modules(XKB_COMMON_X11 IMPORTED_TARGET "xkbcommon-x11>=0.4.1") if (NOT TARGET PkgConfig::XKB_COMMON_X11) set(XKB_COMMON_X11_FOUND 0) diff --git a/cmake/FindXRender.cmake b/cmake/FindXRender.cmake index 35467375b8..6908cc45fb 100644 --- a/cmake/FindXRender.cmake +++ b/cmake/FindXRender.cmake @@ -1,10 +1,10 @@ # 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: BSD-3-Clause find_package(PkgConfig QUIET) if(NOT TARGET PkgConfig::XRender) - pkg_check_modules(XRender xrender IMPORTED_TARGET) + pkg_check_modules(XRender IMPORTED_TARGET "xrender") if (NOT TARGET PkgConfig::XRender) set(XRender_FOUND 0) diff --git a/cmake/ModuleDescription.json.in b/cmake/ModuleDescription.json.in index 6aae9a4a59..93f9a5ed25 100644 --- a/cmake/ModuleDescription.json.in +++ b/cmake/ModuleDescription.json.in @@ -1,11 +1,13 @@ { - "module_name": "${target}", - "version": "${PROJECT_VERSION}", - "built_with": { + "name": "${target}", + "repository": "${lower_case_project_name}", + "version": "${PROJECT_VERSION}",${extra_module_information} + "built_with": {${extra_build_information} "compiler_id": "${CMAKE_CXX_COMPILER_ID}", "compiler_target": "${CMAKE_CXX_COMPILER_TARGET}", "compiler_version": "${CMAKE_CXX_COMPILER_VERSION}", "cross_compiled": ${cross_compilation}, - "target_system": "${CMAKE_SYSTEM_NAME}" + "target_system": "${CMAKE_SYSTEM_NAME}", + "architecture": "${TEST_architecture_arch}" } } 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 a118997bcf..924db182be 100644 --- a/cmake/Qt3rdPartyLibraryHelpers.cmake +++ b/cmake/Qt3rdPartyLibraryHelpers.cmake @@ -4,6 +4,7 @@ macro(qt_internal_get_add_library_option_args option_args) STATIC MODULE INTERFACE + NO_UNITY_BUILD ) endmacro() @@ -14,12 +15,12 @@ endmacro() # Everything else is just prepation for option validating. function(qt_internal_add_common_qt_library_helper target) qt_internal_get_add_library_option_args(option_args) - qt_parse_all_arguments(arg "qt_internal_add_common_qt_library_helper" + cmake_parse_arguments(PARSE_ARGV 1 arg "${option_args}" "" "" - ${ARGN} ) + _qt_internal_validate_all_args_are_parsed(arg) if(arg_SHARED) set(arg_SHARED SHARED) @@ -50,6 +51,11 @@ function(qt_internal_add_common_qt_library_helper target) endif() _qt_internal_add_library(${target} ${arg_STATIC} ${arg_SHARED} ${arg_MODULE} ${arg_INTERFACE}) + + if(arg_NO_UNITY_BUILD) + set_property(TARGET "${target}" PROPERTY UNITY_BUILD OFF) + endif() + qt_internal_mark_as_internal_library(${target}) endfunction() @@ -67,12 +73,13 @@ function(qt_internal_add_cmake_library target) ${__default_public_args} ) - qt_parse_all_arguments(arg "qt_add_cmake_library" + cmake_parse_arguments(PARSE_ARGV 1 arg "${option_args}" "${single_args}" "${multi_args}" - ${ARGN} ) + _qt_internal_validate_all_args_are_parsed(arg) + _qt_internal_validate_no_unity_build(arg) qt_remove_args(library_helper_args ARGS_TO_REMOVE @@ -102,6 +109,8 @@ function(qt_internal_add_cmake_library target) SOURCES ${arg_SOURCES} INCLUDE_DIRECTORIES ${arg_INCLUDE_DIRECTORIES} + SYSTEM_INCLUDE_DIRECTORIES + ${arg_SYSTEM_INCLUDE_DIRECTORIES} PUBLIC_INCLUDE_DIRECTORIES ${arg_PUBLIC_INCLUDE_DIRECTORIES} PUBLIC_DEFINES @@ -117,6 +126,7 @@ function(qt_internal_add_cmake_library target) MOC_OPTIONS ${arg_MOC_OPTIONS} ENABLE_AUTOGEN_TOOLS ${arg_ENABLE_AUTOGEN_TOOLS} DISABLE_AUTOGEN_TOOLS ${arg_DISABLE_AUTOGEN_TOOLS} + NO_UNITY_BUILD # Disabled by default ) endfunction() @@ -139,12 +149,13 @@ function(qt_internal_add_3rdparty_library target) ${__default_public_args} ) - qt_parse_all_arguments(arg "qt_internal_add_3rdparty_library" + cmake_parse_arguments(PARSE_ARGV 1 arg "${library_option_args};${option_args}" "${single_args}" "${multi_args}" - ${ARGN} ) + _qt_internal_validate_all_args_are_parsed(arg) + _qt_internal_validate_no_unity_build(arg) qt_remove_args(library_helper_args ARGS_TO_REMOVE @@ -238,6 +249,7 @@ function(qt_internal_add_3rdparty_library target) MOC_OPTIONS ${arg_MOC_OPTIONS} ENABLE_AUTOGEN_TOOLS ${arg_ENABLE_AUTOGEN_TOOLS} DISABLE_AUTOGEN_TOOLS ${arg_DISABLE_AUTOGEN_TOOLS} + NO_UNITY_BUILD ) if(NOT BUILD_SHARED_LIBS OR arg_INSTALL) @@ -298,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}" ) @@ -308,6 +321,12 @@ function(qt_internal_add_3rdparty_library target) qt_enable_separate_debug_info(${target} "${debug_install_dir}") qt_internal_install_pdb_files(${target} "${INSTALL_LIBDIR}") endif() + + if(BUILD_SHARED_LIBS AND MSVC) + set_target_properties(${target} PROPERTIES + INTERPROCEDURAL_OPTIMIZATION OFF + ) + endif() endfunction() function(qt_install_3rdparty_library_wrap_config_extra_file target) @@ -341,12 +360,13 @@ function(qt_internal_add_3rdparty_header_module target) set(multi_args EXTERNAL_HEADERS ) - qt_parse_all_arguments(arg "qt_internal_add_header_module" + cmake_parse_arguments(PARSE_ARGV 1 arg "${option_args}" "${single_args}" "${multi_args}" - ${ARGN} ) + _qt_internal_validate_all_args_are_parsed(arg) + qt_internal_add_module(${target} INTERNAL_MODULE HEADER_MODULE diff --git a/cmake/QtAndroidHelpers.cmake b/cmake/QtAndroidHelpers.cmake index d86fc5f96c..0743fe41a9 100644 --- a/cmake/QtAndroidHelpers.cmake +++ b/cmake/QtAndroidHelpers.cmake @@ -1,91 +1,92 @@ # 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: BSD-3-Clause # # 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) @@ -130,8 +131,9 @@ function(qt_internal_android_dependencies_content target file_content_out) if (init_class) set(init_class "initClass=\"${init_class}\"") endif() - file(TO_NATIVE_PATH ${jar_file} jar_file_native) - string(APPEND file_contents "<jar file=\"${jar_file_native}\" ${init_class} />\n") + # Use unix path to allow using files on any host platform. + file(TO_CMAKE_PATH ${jar_file} jar_file_unix_path) + string(APPEND file_contents "<jar file=\"${jar_file_unix_path}\" ${init_class} />\n") endforeach() endif() @@ -142,8 +144,10 @@ function(qt_internal_android_dependencies_content target file_content_out) if (init_class) set(init_class "initClass=\"${init_class}\"") endif() - file(TO_NATIVE_PATH ${bundle_file} jar_bundle_native) - string(APPEND file_contents "<jar bundling=\"1\" file=\"${jar_bundle_native}\" ${init_class} />\n") + # Use unix path to allow using files on any host platform. + file(TO_CMAKE_PATH ${bundle_file} jar_bundle_unix_path) + string(APPEND file_contents + "<jar bundling=\"1\" file=\"${jar_bundle_unix_path}\" ${init_class} />\n") endforeach() endif() @@ -155,8 +159,9 @@ function(qt_internal_android_dependencies_content target file_content_out) if (lib_extends) set(lib_extends "extends=\"${lib_extends}\"") endif() - file(TO_NATIVE_PATH ${lib_file} lib_file_native) - string(APPEND file_contents "<lib file=\"${lib_file_native}\" ${lib_extends} />\n") + # Use unix path to allow using files on any host platform. + file(TO_CMAKE_PATH ${lib_file} lib_file_unix_path) + string(APPEND file_contents "<lib file=\"${lib_file_unix_path}\" ${lib_extends} />\n") endforeach() endif() @@ -166,19 +171,23 @@ function(qt_internal_android_dependencies_content target file_content_out) string(REPLACE ".so" "_${CMAKE_ANDROID_ARCH_ABI}.so" lib ${lib}) section(${lib} ":" lib_file lib_replacement) if (lib_replacement) - file(TO_NATIVE_PATH ${lib_replacement} lib_replacement_native) - set(lib_replacement "replaces=\"${lib_replacement_native}\"") + # Use unix path to allow using files on any host platform. + file(TO_CMAKE_PATH ${lib_replacement} lib_replacement_unix_path) + set(lib_replacement "replaces=\"${lib_replacement_unix_path}\"") endif() - file(TO_NATIVE_PATH ${lib_file} lib_file_native) - string(APPEND file_contents "<lib file=\"${lib_file_native}\" ${lib_replacement} />\n") + # Use unix path to allow using files on any host platform. + file(TO_CMAKE_PATH ${lib_file} lib_file_unix_path) + string(APPEND file_contents + "<lib file=\"${lib_file_unix_path}\" ${lib_replacement} />\n") endforeach() endif() # Bundled files if(arg_BUNDLED_FILES) foreach(bundled_file IN LISTS arg_BUNDLED_FILES) - file(TO_NATIVE_PATH ${bundled_file} file_native) - string(APPEND file_contents "<bundled file=\"${file_native}\" />\n") + # Use unix path to allow using files on any host platform. + file(TO_CMAKE_PATH ${bundled_file} file_unix_path) + string(APPEND file_contents "<bundled file=\"${file_unix_path}\" />\n") endforeach() endif() @@ -244,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=\"plugins/${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 bb35aac5d1..c0ad53ab9e 100644 --- a/cmake/QtAppHelpers.cmake +++ b/cmake/QtAppHelpers.cmake @@ -1,15 +1,15 @@ # 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: BSD-3-Clause # This function creates a CMake target for a Qt internal app. # Such projects had a load(qt_app) command. function(qt_internal_add_app target) - qt_parse_all_arguments(arg - "qt_internal_add_app" - "NO_INSTALL;INSTALL_VERSIONED_LINK;EXCEPTIONS" + cmake_parse_arguments(PARSE_ARGV 1 arg + "NO_INSTALL;INSTALL_VERSIONED_LINK;EXCEPTIONS;NO_UNITY_BUILD" "${__default_target_info_args};INSTALL_DIR" "${__default_private_args};PUBLIC_LIBRARIES" - ${ARGN}) + ) + _qt_internal_validate_all_args_are_parsed(arg) set(exceptions "") if(arg_EXCEPTIONS) @@ -35,6 +35,14 @@ 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() + set(arg_NO_UNITY_BUILD "") + endif() + qt_internal_add_executable("${target}" QT_APP DELAY_RC @@ -42,12 +50,16 @@ function(qt_internal_add_app target) OUTPUT_DIRECTORY "${output_directory}" ${exceptions} ${no_install} + ${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} DEFINES ${arg_DEFINES} + ${deprecation_define} LIBRARIES ${arg_LIBRARIES} ${arg_PUBLIC_LIBRARIES} @@ -57,11 +69,13 @@ function(qt_internal_add_app target) MOC_OPTIONS ${arg_MOC_OPTIONS} ENABLE_AUTOGEN_TOOLS ${arg_ENABLE_AUTOGEN_TOOLS} DISABLE_AUTOGEN_TOOLS ${arg_DISABLE_AUTOGEN_TOOLS} - TARGET_VERSION "${arg_TARGET_VERSION}" - TARGET_PRODUCT "${arg_TARGET_PRODUCT}" - TARGET_DESCRIPTION "${arg_TARGET_DESCRIPTION}" - TARGET_COMPANY "${arg_TARGET_COMPANY}" - TARGET_COPYRIGHT "${arg_TARGET_COPYRIGHT}" + TARGET_VERSION ${arg_TARGET_VERSION} + TARGET_PRODUCT ${arg_TARGET_PRODUCT} + TARGET_DESCRIPTION ${arg_TARGET_DESCRIPTION} + TARGET_COMPANY ${arg_TARGET_COMPANY} + TARGET_COPYRIGHT ${arg_TARGET_COPYRIGHT} + # If you are putting anything after these, make sure that + # qt_set_target_info_properties knows how to process them ) qt_internal_add_target_aliases("${target}") _qt_internal_apply_strict_cpp("${target}") @@ -80,7 +94,8 @@ function(qt_internal_add_app target) # Install versioned link if requested. if(NOT arg_NO_INSTALL AND arg_INSTALL_VERSIONED_LINK) - qt_internal_install_versioned_link("${arg_INSTALL_DIR}" ${target}) + qt_internal_install_versioned_link(WORKING_DIRECTORY "${arg_INSTALL_DIR}" + TARGETS ${target}) endif() qt_add_list_file_finalizer(qt_internal_finalize_app ${target}) diff --git a/cmake/QtAutoDetect.cmake b/cmake/QtAutoDetect.cmake index 10f91cd047..464e8e900b 100644 --- a/cmake/QtAutoDetect.cmake +++ b/cmake/QtAutoDetect.cmake @@ -1,541 +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: 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") - 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) - - # 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_ensure_static_qt_config() - - __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 - OR CMAKE_SYSTEM_NAME STREQUAL watchOS - OR CMAKE_SYSTEM_NAME STREQUAL tvOS) - 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(CMAKE_SYSTEM_NAME STREQUAL iOS) - 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 proviude 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() - elseif(CMAKE_SYSTEM_NAME STREQUAL tvOS) - if(simulator_and_device) - set(osx_architectures "arm64;x86_64") - elseif(QT_UIKIT_SDK STREQUAL "appletvos") - set(osx_architectures "arm64") - elseif(QT_UIKIT_SDK STREQUAL "appletvsimulator") - set(osx_architectures "x86_64") - else() - if(NOT DEFINED QT_UIKIT_SDK) - message(FATAL_ERROR "Please proviude a value for -DQT_UIKIT_SDK." - " Possible values: appletvos, appletvsimulator.") - else() - message(FATAL_ERROR - "Unknown SDK argument given to QT_UIKIT_SDK: ${QT_UIKIT_SDK}.") - endif() - endif() - elseif(CMAKE_SYSTEM_NAME STREQUAL watchOS) - if(simulator_and_device) - set(osx_architectures "armv7k;i386") - elseif(QT_UIKIT_SDK STREQUAL "watchos") - set(osx_architectures "armv7k") - elseif(QT_UIKIT_SDK STREQUAL "watchsimulator") - set(osx_architectures "i386") - else() - if(NOT DEFINED QT_UIKIT_SDK) - message(FATAL_ERROR "Please proviude a value for -DQT_UIKIT_SDK." - " Possible values: watchos, watchsimulator.") - else() - message(FATAL_ERROR - "Unknown SDK argument given to QT_UIKIT_SDK: ${QT_UIKIT_SDK}.") - endif() - 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 "") - - qt_internal_ensure_static_qt_config() - - # 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(IOS) - set(sdk_name "iphoneos") - elseif(TVOS) - set(sdk_name "appletvos") - elseif(WATCHOS) - set(sdk_name "watchos") - 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 "10.14") - elseif(CMAKE_SYSTEM_NAME STREQUAL iOS) - set(version "13.0") - elseif(CMAKE_SYSTEM_NAME STREQUAL watchOS) - set(version "6.0") - elseif(CMAKE_SYSTEM_NAME STREQUAL tvOS) - set(version "13.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.") - - set(device_names "iOS" "watchOS" "tvOS") - list(LENGTH CMAKE_OSX_ARCHITECTURES arch_count) - if(NOT CMAKE_SYSTEM_NAME IN_LIST device_names 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) - set(device_names "iOS" "watchOS" "tvOS") - if(APPLE AND NOT CMAKE_SYSTEM_NAME IN_LIST device_names) - 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..ad0764b804 --- /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 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(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 0e827fbb14..029a709e90 100644 --- a/cmake/QtAutogenHelpers.cmake +++ b/cmake/QtAutogenHelpers.cmake @@ -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: BSD-3-Clause # Initial autogen setup for a target to specify certain CMake properties which are common # to all autogen tools. Also enable AUTOMOC by default. @@ -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) @@ -61,7 +60,8 @@ endfunction() # This function adds or removes additional AUTOGEN tools to a target: AUTOMOC/UIC/RCC function(qt_autogen_tools target) - qt_parse_all_arguments(arg "qt_autogen_tools" "" "" "${__default_private_args}" ${ARGN}) + cmake_parse_arguments(PARSE_ARGV 1 arg "" "" "${__default_private_args}") + _qt_internal_validate_all_args_are_parsed(arg) if(arg_ENABLE_AUTOGEN_TOOLS) foreach(tool ${arg_ENABLE_AUTOGEN_TOOLS}) @@ -78,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) @@ -94,7 +103,7 @@ function(qt_manual_moc result) "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_BINARY_DIR}" outfile) list(APPEND moc_files "${outfile}") - set(moc_parameters_file "${outfile}_parameters$<$<BOOL:$<CONFIGURATION>>:_$<CONFIGURATION>>") + set(moc_parameters_file "${outfile}_parameters$<$<BOOL:$<CONFIG>>:_$<CONFIG>>") set(moc_parameters ${arg_FLAGS} -o "${outfile}" "${infile}") foreach(dir IN ITEMS ${arg_INCLUDE_DIRECTORIES}) @@ -102,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>>") @@ -128,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/QtBaseCMakeTesting.cmake b/cmake/QtBaseCMakeTesting.cmake index c06827a948..db64b49189 100644 --- a/cmake/QtBaseCMakeTesting.cmake +++ b/cmake/QtBaseCMakeTesting.cmake @@ -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: BSD-3-Clause ## Test the cmake build system: option(BUILD_CMAKE_TESTING "Build tests for the Qt build system" OFF) diff --git a/cmake/QtBaseConfigureTests.cmake b/cmake/QtBaseConfigureTests.cmake index acc5bdbcb1..66a0b3b6dd 100644 --- a/cmake/QtBaseConfigureTests.cmake +++ b/cmake/QtBaseConfigureTests.cmake @@ -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: BSD-3-Clause include(CheckCXXSourceCompiles) @@ -12,10 +12,14 @@ function(qt_run_config_test_architecture) qt_get_platform_try_compile_vars(platform_try_compile_vars) list(APPEND flags ${platform_try_compile_vars}) - list(TRANSFORM flags PREPEND " " OUTPUT_VARIABLE flags_indented) + list(TRANSFORM flags PREPEND " " OUTPUT_VARIABLE flags_indented) list(JOIN flags_indented "\n" flags_indented) + message(STATUS - "Building architecture extraction project with the following CMake arguments:\n${flags_indented}") + "Building architecture extraction project with the following CMake arguments:") + list(POP_BACK CMAKE_MESSAGE_CONTEXT _context) + message(NOTICE ${flags_indented}) + list(APPEND CMAKE_MESSAGE_CONTEXT ${_context}) try_compile( _arch_result @@ -58,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) @@ -69,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}") @@ -111,41 +120,38 @@ endfunction() function(qt_run_linker_version_script_support) - file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/version_flag.map" "VERS_1 { global: sym; }; -VERS_2 { global: sym; } -VERS_1; -") - if(DEFINED CMAKE_REQUIRED_FLAGS) - set(CMAKE_REQUIRED_FLAGS_SAVE ${CMAKE_REQUIRED_FLAGS}) - else() - set(CMAKE_REQUIRED_FLAGS "") - endif() - set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS} "-Wl,--version-script=\"${CMAKE_CURRENT_BINARY_DIR}/version_flag.map\"") - - # Pass the linker that the main project uses to the version script compile test. - qt_internal_get_active_linker_flags(linker_flags) - if(linker_flags) - set(CMAKE_REQUIRED_LINK_OPTIONS ${linker_flags}) - endif() - - check_cxx_source_compiles("int main(void){return 0;}" HAVE_LD_VERSION_SCRIPT) - if(DEFINED CMAKE_REQUIRED_FLAGS_SAVE) - set(CMAKE_REQUIRED_FLAGS ${CMAKE_REQUIRED_FLAGS_SAVE}) - endif() - file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/conftest.map") - # For some reason the linker command line written by the XCode generator, which is # subsequently executed by xcodebuild, ignores the linker flag, and thus the test # seemingly succeeds. Explicitly disable the version script test on darwin platforms. - if(APPLE) - set(HAVE_LD_VERSION_SCRIPT OFF) - endif() # Also makes no sense with MSVC-style command-line - if(MSVC) + if(NOT APPLE AND NOT MSVC) + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/version_flag.map" [=[ + VERS_1 { global: sym1; }; + VERS_2 { global: sym2; } VERS_1; + ]=]) + set(CMAKE_REQUIRED_LINK_OPTIONS "") + list(APPEND CMAKE_REQUIRED_LINK_OPTIONS + "-Wl,--version-script=${CMAKE_CURRENT_BINARY_DIR}/version_flag.map") + # Pass the linker that the main project uses to the version script compile test. + qt_internal_get_active_linker_flags(linker_flags) + if(linker_flags) + list(APPEND CMAKE_REQUIRED_LINK_OPTIONS ${linker_flags}) + endif() + check_cxx_source_compiles([=[ + int sym1; + int sym2; + int main(void) { return 0; } + ]=] HAVE_LD_VERSION_SCRIPT) + file(REMOVE "${CMAKE_CURRENT_BINARY_DIR}/version_flag.map") + else() set(HAVE_LD_VERSION_SCRIPT OFF) endif() - set(TEST_ld_version_script "${HAVE_LD_VERSION_SCRIPT}" CACHE INTERNAL "linker version script support") + set(TEST_ld_version_script "${HAVE_LD_VERSION_SCRIPT}" + CACHE INTERNAL "linker version script support") + list(APPEND QT_BASE_CONFIGURE_TESTS_VARS_TO_EXPORT TEST_ld_version_script) + set(QT_BASE_CONFIGURE_TESTS_VARS_TO_EXPORT ${QT_BASE_CONFIGURE_TESTS_VARS_TO_EXPORT} + CACHE INTERNAL "Test variables that should be exported") endfunction() function(qt_internal_ensure_latest_win_nt_api) @@ -194,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}\"") @@ -202,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 8552137de7..1e604559ed 100644 --- a/cmake/QtBaseGlobalTargets.cmake +++ b/cmake/QtBaseGlobalTargets.cmake @@ -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: BSD-3-Clause set(__GlobalConfig_path_suffix "${INSTALL_CMAKE_NAMESPACE}") qt_path_join(__GlobalConfig_build_dir ${QT_CONFIG_BUILD_DIR} ${__GlobalConfig_path_suffix}) @@ -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 @@ -101,7 +97,8 @@ if(MACOS AND QT_IS_MACOS_UNIVERSAL QT_FEATURE_x86intrin) endif() -if(MACOS AND QT_IS_MACOS_UNIVERSAL AND __qt_osx_first_arch STREQUAL "x86_64") +if(MACOS AND QT_IS_MACOS_UNIVERSAL AND + (__qt_osx_first_arch STREQUAL "x86_64" OR __qt_osx_first_arch STREQUAL "x86_64h")) set(QT_FORCE_FEATURE_neon ON CACHE INTERNAL "Force enable neon due to platform requirements.") set(__QtFeature_custom_enabled_cache_variables TEST_subarch_neon @@ -121,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) @@ -132,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 @@ -159,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) @@ -171,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( @@ -179,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" @@ -195,97 +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/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/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/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 + ${__private_files} DESTINATION "${__GlobalConfig_install_dir}" ) @@ -319,41 +278,32 @@ 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/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() -# TODO: Check whether this is the right place to install these qt_copy_or_install(DIRECTORY "cmake/3rdparty" DESTINATION "${__GlobalConfig_install_dir}") # In prefix builds we also need to copy the files into the build config directory, so that the @@ -369,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 @@ -379,20 +334,50 @@ 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() -if(MACOS) - qt_copy_or_install(FILES - cmake/macos/MacOSXBundleInfo.plist.in - DESTINATION "${__GlobalConfig_install_dir}/macos" +if(APPLE) + if(MACOS) + 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}" + ) + # For examples built as part of prefix build before install + file(COPY "cmake/${platform_shortname}/Info.plist.app.in" + DESTINATION "${__GlobalConfig_build_dir}/${platform_shortname}" ) -elseif(IOS) - qt_copy_or_install(FILES - cmake/ios/Info.plist.app.in - cmake/ios/LaunchScreen.storyboard - DESTINATION "${__GlobalConfig_install_dir}/ios" + + # 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" + ) + # For examples built as part of prefix build before install + file(COPY "cmake/ios/LaunchScreen.storyboard" + DESTINATION "${__GlobalConfig_build_dir}/ios" + ) + endif() elseif(WASM) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/util/wasm/wasmtestrunner/qt-wasmtestrunner.py" "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/qt-wasmtestrunner.py" @ONLY) 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 4c3654ba50..ee24ba188e 100644 --- a/cmake/QtBuild.cmake +++ b/cmake/QtBuild.cmake @@ -1,576 +1,4 @@ # 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: 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) - qt_parse_all_arguments(arg "qt_get_install_target_default_args" - "" "OUT_VAR;CMAKE_CONFIG;RUNTIME;LIBRARY;ARCHIVE;INCLUDES;BUNDLE" - "ALL_CMAKE_CONFIGS" ${ARGN}) - - 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_DATADIR}/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() - -# 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_wrap_tool_command - # instead. -endfunction() -qt_setup_tool_path_command() - -function(qt_internal_generate_tool_command_wrapper) - get_property(is_called GLOBAL PROPERTY _qt_internal_generate_tool_command_wrapper_called) - if(NOT CMAKE_HOST_WIN32 OR is_called) - return() - endif() - set(bindir "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}/${INSTALL_BINDIR}") - file(TO_NATIVE_PATH "${bindir}" bindir) - set(tool_command_wrapper_path "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/qt_setup_tool_path.bat") - file(WRITE "${tool_command_wrapper_path}" "@echo off -set PATH=${bindir};%PATH% -%*") - set(QT_TOOL_COMMAND_WRAPPER_PATH "${tool_command_wrapper_path}" - CACHE INTERNAL "Path to the wrapper of the tool commands") - set_property(GLOBAL PROPERTY _qt_internal_generate_tool_command_wrapper_called TRUE) -endfunction() -qt_internal_generate_tool_command_wrapper() - -# 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(MSVC) - if (CLANG) - set(QT_DEFAULT_MKSPEC win32-clang-msvc) - elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") - set(QT_DEFAULT_MKSPEC win32-arm64-msvc) - else() - set(QT_DEFAULT_MKSPEC win32-msvc) - endif() - elseif(CLANG AND MINGW) - set(QT_DEFAULT_MKSPEC win32-clang-g++) - elseif(MINGW) - set(QT_DEFAULT_MKSPEC win32-g++) - endif() - - if (MINGW) - 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) - set(QT_DEFAULT_MKSPEC wasm-emscripten) -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 EXISTS "${QT_MKSPECS_DIR}/${QT_QMAKE_TARGET_MKSPEC}") - file(GLOB known_platforms - LIST_DIRECTORIES true - RELATIVE "${QT_MKSPECS_DIR}" - "${QT_MKSPECS_DIR}/*" - ) - list(JOIN known_platforms "\n " known_platforms) - message(FATAL_ERROR "Unknown platform ${QT_QMAKE_TARGET_MKSPEC}\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_HOST_APPLE AND APPLE) - 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 - 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 -) -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 -) -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(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(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 a81ec70cb3..11fa9996b1 100644 --- a/cmake/QtBuildInformation.cmake +++ b/cmake/QtBuildInformation.cmake @@ -1,5 +1,21 @@ # 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: 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) @@ -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() @@ -93,7 +107,13 @@ function(qt_print_build_instructions) "\nTo configure and build other Qt modules, you can use the following convenience script: ${local_install_prefix}/${INSTALL_BINDIR}/${configure_module_command}") list(APPEND msg "\nIf reconfiguration fails for some reason, try removing 'CMakeCache.txt' \ -from the build directory \n") +from the build directory") + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.24") + list(APPEND msg "Alternatively, you can add the --fresh flag to your CMake flags.\n") + else() + list(APPEND msg "\n") + endif() + list(JOIN msg "\n" msg) if(NOT QT_INTERNAL_BUILD_INSTRUCTIONS_SHOWN) @@ -101,6 +121,10 @@ from the build directory \n") endif() set(QT_INTERNAL_BUILD_INSTRUCTIONS_SHOWN "TRUE" CACHE STRING "" FORCE) + + if(QT_SUPERBUILD) + qt_internal_save_previously_visited_packages() + endif() endfunction() function(qt_configure_print_summary_helper summary_reports force_show) @@ -108,8 +132,15 @@ function(qt_configure_print_summary_helper summary_reports force_show) # current log level. if(force_show) set(CMAKE_MESSAGE_LOG_LEVEL "STATUS") + + # Need 2 flushes to ensure no interleaved input is printed due to a mix of message(STATUS) + # and message(NOTICE) calls. + execute_process(COMMAND ${CMAKE_COMMAND} -E echo " ") + + message(STATUS "Configure summary:\n${summary_reports}") + + execute_process(COMMAND ${CMAKE_COMMAND} -E echo " ") endif() - message(STATUS "Configure summary:\n${__qt_configure_reports}") endfunction() function(qt_configure_print_build_instructions_helper msg) @@ -130,39 +161,44 @@ function(qt_configure_print_summary) # Show Qt-specific configuration summary. if(__qt_configure_reports) - # We want to show the the summary file and log level messages 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) - message("") + # 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(log_level_sufficient_for_printed_summary + AND (NOT QT_INTERNAL_SUMMARY_INSTRUCTIONS_SHOWN OR features_possibly_changed)) + set(force_show_summary TRUE) message( + "\n" "-- Configuration summary shown below. It has also been written to" " ${CMAKE_BINARY_DIR}/config.summary") message( "-- Configure with --log-level=STATUS or higher to increase " "CMake's message verbosity. " "The log level does not persist across reconfigurations.") - endif() - - # Need 2 flushes to ensure no interleaved input is printed due to a mix of message(STATUS) - # and message(NOTICE) calls. - execute_process(COMMAND ${CMAKE_COMMAND} -E echo " ") - - # We want to show the configuration summary 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) - set(force_show_summary TRUE) else() set(force_show_summary FALSE) + message( + "\n" + "-- Configuration summary has been written to" + " ${CMAKE_BINARY_DIR}/config.summary") endif() qt_configure_print_summary_helper( - "Configuration summary:\n${__qt_configure_reports}" + "${__qt_configure_reports}" ${force_show_summary}) - execute_process(COMMAND ${CMAKE_COMMAND} -E echo " ") - file(APPEND "${summary_file}" "${__qt_configure_reports}") endif() @@ -303,9 +339,11 @@ function(qt_configure_add_summary_entry) endfunction() function(qt_configure_process_add_summary_entry) - qt_parse_all_arguments(arg "qt_configure_add_summary_entry" + cmake_parse_arguments(PARSE_ARGV 0 arg "" - "ARGS;TYPE;MESSAGE" "CONDITION" ${ARGN}) + "ARGS;TYPE;MESSAGE" + "CONDITION") + _qt_internal_validate_all_args_are_parsed(arg) if(NOT arg_TYPE) set(arg_TYPE "feature") @@ -462,8 +500,11 @@ function(qt_configure_add_summary_section) endfunction() function(qt_configure_process_add_summary_section) - qt_parse_all_arguments(arg "qt_configure_add_summary_section" - "" "NAME" "" ${ARGN}) + cmake_parse_arguments(PARSE_ARGV 0 arg + "" + "NAME" + "") + _qt_internal_validate_all_args_are_parsed(arg) qt_configure_add_report("${__qt_configure_indent}${arg_NAME}:") if(NOT DEFINED __qt_configure_indent) @@ -495,9 +536,11 @@ function(qt_configure_add_report_error error) endfunction() function(qt_configure_process_add_report_entry) - qt_parse_all_arguments(arg "qt_configure_add_report_entry" + cmake_parse_arguments(PARSE_ARGV 0 arg "" - "TYPE;MESSAGE" "CONDITION" ${ARGN}) + "TYPE;MESSAGE" + "CONDITION") + _qt_internal_validate_all_args_are_parsed(arg) set(possible_types NOTE WARNING ERROR FATAL_ERROR) if(NOT "${arg_TYPE}" IN_LIST possible_types) diff --git a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake index 63f9050bd2..129f1ebb77 100644 --- a/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake +++ b/cmake/QtBuildInternals/QtBuildInternalsConfig.cmake @@ -1,69 +1,28 @@ # 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: BSD-3-Clause # These values should be kept in sync with those in qtbase/.cmake.conf -cmake_minimum_required(VERSION 3.16...3.20) - -############################################### -# -# 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 qtimageformats: -# qtbase;qtshadertools;qtsvg;qtdeclarative;qttools -# -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() +cmake_minimum_required(VERSION 3.16...3.21) 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,1249 +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() - -macro(qt_build_internals_set_up_private_api) - # Check for the minimum CMake version. - include(QtCMakeVersionHelpers) - qt_internal_require_suitable_cmake_version() - qt_internal_upgrade_cmake_policies() - - # 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" OR ${_type} STREQUAL "INTERFACE")) - 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}") - 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) - 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() - - # 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) - - 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() -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() -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() -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() - -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) - 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) - 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() -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}) - - # 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 always depend on all plugins so as 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_plugins ${arg_DEPENDS}) - - if(TARGET ${qt_repo_targets_name}_src) - list(APPEND QT_EXAMPLE_DEPENDENCIES ${qt_repo_targets_name}_src) - 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}") - set(QT_DISABLE_QT_ADD_PLUGIN_COMPATIBILITY TRUE) - - 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() - endforeach() - - install(CODE " -# Restore backed up CMAKE_INSTALL_PREFIX. -set(CMAKE_INSTALL_PREFIX \"\${_qt_internal_examples_cmake_install_prefix_backup}\") -") -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() +include(QtBuildHelpers) -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() +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 1f7425982b..766e372666 100644 --- a/cmake/QtBuildInternals/QtStandaloneTestTemplateProject/CMakeLists.txt +++ b/cmake/QtBuildInternals/QtStandaloneTestTemplateProject/CMakeLists.txt @@ -1,12 +1,7 @@ # 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: 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) # 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. @@ -18,5 +13,26 @@ else() set(absolute_project_path "${QT_STANDALONE_TEST_PATH}") endif() +# If path does not include the drive letter, we try to add it. +get_filename_component(absolute_project_path "." REALPATH BASE_DIR "${absolute_project_path}") + +if(NOT IS_DIRECTORY "${absolute_project_path}") + get_filename_component(filename "${absolute_project_path}" NAME) + get_filename_component(directory "${absolute_project_path}" DIRECTORY) + + if(filename STREQUAL "CMakeLists.txt") + set(absolute_project_path "${directory}") + 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 77e0148a0c..bd0984f314 100644 --- a/cmake/QtBuildInternals/QtStandaloneTestTemplateProject/Main.cmake +++ b/cmake/QtBuildInternals/QtStandaloneTestTemplateProject/Main.cmake @@ -1,16 +1,18 @@ # 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: 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() # Find all StandaloneTestsConfig.cmake files, and include them # This will find all Qt packages that are required for standalone tests. # It will find more packages that needed for a certain test, but will ensure any test can # be built. -qt_get_standalone_tests_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() @@ -21,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 3297d85c52..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@) @@ -87,6 +90,9 @@ set(QT_BUILD_MINIMAL_STATIC_TESTS @QT_BUILD_MINIMAL_STATIC_TESTS@ CACHE BOOL set(QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS @QT_BUILD_MINIMAL_ANDROID_MULTI_ABI_TESTS@ CACHE BOOL "Build minimal subset of tests for Android multi-ABI Qt builds") +set(QT_BUILD_TESTS_BATCHED @QT_BUILD_TESTS_BATCHED@ CACHE BOOL + "Should all tests be batched into a single binary.") + set(QT_BUILD_TESTS_BY_DEFAULT @QT_BUILD_TESTS_BY_DEFAULT@ CACHE BOOL "Should tests be built as part of the default 'all' target.") set(QT_BUILD_EXAMPLES_BY_DEFAULT @QT_BUILD_EXAMPLES_BY_DEFAULT@ CACHE BOOL @@ -100,6 +106,16 @@ 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") + +# Propragate the value of WARNINGS_ARE_ERRORS. +set(WARNINGS_ARE_ERRORS "@WARNINGS_ARE_ERRORS@" CACHE BOOL "Build Qt with warnings as errors") + # Propagate usage of versioned hard link. set(QT_CREATE_VERSIONED_HARD_LINK "@QT_CREATE_VERSIONED_HARD_LINK@" CACHE BOOL "Enable the use of versioned hard link") @@ -119,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 cc183068d8..142a003cbc 100644 --- a/cmake/QtCMakeHelpers.cmake +++ b/cmake/QtCMakeHelpers.cmake @@ -1,22 +1,19 @@ # Copyright (C) 2022 The Qt Company Ltd. -# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 - -# qt_configure_file(OUTPUT output-file <INPUT input-file | CONTENT content>) -# input-file is relative to ${CMAKE_CURRENT_SOURCE_DIR} -# output-file is relative to ${CMAKE_CURRENT_BINARY_DIR} -# -# This function is similar to file(GENERATE OUTPUT) except it writes the content -# to the file at configure time, rather than at generate time. Once CMake 3.18 is released, it can -# use file(CONFIGURE) in its implementation. Until then, it uses configure_file() with a generic -# input file as source, when used with the CONTENT signature. -function(qt_configure_file) - qt_parse_all_arguments(arg "qt_configure_file" "" "OUTPUT;INPUT;CONTENT" "" ${ARGN}) +# SPDX-License-Identifier: BSD-3-Clause +# The common implementation of qt_configure_file functionality. +macro(qt_configure_file_impl) if(NOT arg_OUTPUT) message(FATAL_ERROR "No output file provided to qt_configure_file.") endif() - if(arg_CONTENT) + # We use this check for the cases when the specified CONTENT is empty. The value of arg_CONTENT + # is undefined, but we still want to create a file with empty content. + if(NOT "CONTENT" IN_LIST arg_KEYWORDS_MISSING_VALUES) + if(arg_INPUT) + message(WARNING "Both CONTENT and INPUT are specified. CONTENT will be used to generate" + " output") + endif() set(template_name "QtFileConfigure.txt.in") # When building qtbase, use the source template file. # Otherwise use the installed file (basically wherever Qt6 package is found). @@ -34,10 +31,29 @@ function(qt_configure_file) endif() configure_file("${input_file}" "${arg_OUTPUT}" @ONLY) +endmacro() + +# qt_configure_file(OUTPUT output-file <INPUT input-file | CONTENT content>) +# input-file is relative to ${CMAKE_CURRENT_SOURCE_DIR} +# output-file is relative to ${CMAKE_CURRENT_BINARY_DIR} +# +# This function is similar to file(GENERATE OUTPUT) except it writes the content +# to the file at configure time, rather than at generate time. +# +# TODO: Once we require 3.18+, this can use file(CONFIGURE) in its implementation, +# or maybe its usage can be replaced by file(CONFIGURE). Until then, it uses +# configure_file() with a generic input file as source, when used with the CONTENT +# signature. +function(qt_configure_file) + cmake_parse_arguments(PARSE_ARGV 0 arg "" "OUTPUT;INPUT;CONTENT" "") + qt_configure_file_impl() endfunction() # A version of cmake_parse_arguments that makes sure all arguments are processed and errors out # with a message about ${type} having received unknown arguments. +# +# TODO: Remove when all usage of qt_parse_all_arguments were replaced by +# cmake_parse_all_arguments(PARSEARGV) instances macro(qt_parse_all_arguments result type flags options multiopts) cmake_parse_arguments(${result} "${flags}" "${options}" "${multiopts}" ${ARGN}) if(DEFINED ${result}_UNPARSED_ARGUMENTS) @@ -45,6 +61,14 @@ 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}) @@ -101,8 +125,7 @@ endfunction() # 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 or -# qt_parse_all_arguments +# 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. @@ -116,7 +139,7 @@ endfunction() # bar(target BAR.... WWW...) # # function(foo target) -# qt_parse_all_arguments(arg "" "" "BAR;ZZZ;WWW ${ARGV}) +# 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 diff --git a/cmake/QtCMakePackageVersionFile.cmake.in b/cmake/QtCMakePackageVersionFile.cmake.in index f63a498b40..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") @@ -8,7 +11,9 @@ if(QT_NO_PACKAGE_VERSION_CHECK) set(__qt_disable_package_version_check TRUE) endif() +# Extra CMake code begin @extra_code@ +# Extra CMake code end if((NOT PACKAGE_VERSION_COMPATIBLE) OR PACKAGE_VERSION_UNSUITABLE) set(__qt_package_version_incompatible TRUE) diff --git a/cmake/QtCMakeVersionHelpers.cmake b/cmake/QtCMakeVersionHelpers.cmake index bcbd69057b..322e58eed1 100644 --- a/cmake/QtCMakeVersionHelpers.cmake +++ b/cmake/QtCMakeVersionHelpers.cmake @@ -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: BSD-3-Clause # Returns the minimum supported CMake version required to /build/ Qt as originally advertised by Qt. function(qt_internal_get_supported_min_cmake_version_for_building_qt out_var) @@ -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}") @@ -198,19 +202,6 @@ function(qt_internal_warn_about_buggy_cmake_versions) # Fixed in 3.21.2. list(APPEND unsuitable_versions "3.21.1") - # qt_ensure_perl fails to find perl in host PATH via find_program - # due to Android Platform module setting CMAKE_FIND_USE_SYSTEM_ENVIRONMENT_PATH to OFF - # Fixed in 3.20.6, 3.21.3. not a problem in CMake versions earlier than 3.20.0 - # https://gitlab.kitware.com/cmake/cmake/-/issues/22634 - # https://gitlab.kitware.com/cmake/cmake/-/merge_requests/5357 - # https://gitlab.kitware.com/cmake/cmake/-/merge_requests/6528 - if(ANDROID AND CMAKE_HOST_WIN32) - list(APPEND unsuitable_versions - "3.20.0" "3.20.1" "3.20.2" "3.20.3" "3.20.4" "3.20.5" - "3.21.0" "3.21.1" "3.21.2" - ) - endif() - foreach(unsuitable_version ${unsuitable_versions}) if(CMAKE_VERSION VERSION_EQUAL unsuitable_version) message(WARNING diff --git a/cmake/QtCompilerFlags.cmake b/cmake/QtCompilerFlags.cmake index 15327374b3..f58f36b7a2 100644 --- a/cmake/QtCompilerFlags.cmake +++ b/cmake/QtCompilerFlags.cmake @@ -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: BSD-3-Clause # Enable compiler warnings by default. All compilers except MSVC support -Wall -Wextra # @@ -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 813b16af69..ac542e9451 100644 --- a/cmake/QtCompilerOptimization.cmake +++ b/cmake/QtCompilerOptimization.cmake @@ -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: BSD-3-Clause if (MSVC) if (QT_64BIT) @@ -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 @@ -73,7 +78,7 @@ endif() # TODO: Missing mkspecs flags we don't handle below: win32-clang-g++, win32-clang-msvc, rtems-base # # gcc and clang base -if(GCC OR CLANG AND NOT WASM) +if(GCC OR CLANG) set(QT_CFLAGS_OPTIMIZE "-O2") set(QT_CFLAGS_OPTIMIZE_FULL "-O3") set(QT_CFLAGS_OPTIMIZE_DEBUG "-Og") @@ -93,9 +98,14 @@ endif() # Windows MSVC if(MSVC) 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" "/O0" "-O0") + set(QT_CFLAGS_OPTIMIZE_VALID_VALUES "/O2" "/O1" "/Od" "/Ob0" "/Ob1" "/Ob2" "/Ob3" "/O0" "-O0") if(CLANG) set(QT_CFLAGS_OPTIMIZE_FULL "/clang:-O3") @@ -121,12 +131,8 @@ if (QCC) set(QT_CFLAGS_OPTIMIZE_FULL "-O3") endif() +# Emscripten Clang if(WASM) - set(QT_CFLAGS_OPTIMIZE "-O2") - set(QT_CFLAGS_OPTIMIZE_FULL "-O3") - set(QT_CFLAGS_OPTIMIZE_SIZE "-Os") - set(QT_CFLAGS_OPTIMIZE_DEBUG "-g2") - - set(QT_CFLAGS_SSE2 -O2 -msimd128 -msse -msse2) - + set(QT_CFLAGS_OPTIMIZE_DEBUG "-O2 -g") # -Og is not supported + set(QT_CFLAGS_SSE2 "-O2 -msimd128 -msse -msse2") endif() diff --git a/cmake/QtConfig.cmake.in b/cmake/QtConfig.cmake.in index ea92d180b5..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: @@ -29,13 +39,26 @@ list(APPEND CMAKE_MODULE_PATH "${_qt_import_prefix}") list(APPEND CMAKE_MODULE_PATH "${_qt_import_prefix}/3rdparty/extra-cmake-modules/find-modules") list(APPEND CMAKE_MODULE_PATH "${_qt_import_prefix}/3rdparty/kwin") -if(APPLE AND (NOT CMAKE_SYSTEM_NAME OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")) - # Add module directory to pick up custom Info.plist template for macOS - list(APPEND CMAKE_MODULE_PATH "${_qt_import_prefix}/macos") -elseif(APPLE AND CMAKE_SYSTEM_NAME STREQUAL "iOS") - # Add module directory to pick up custom Info.plist template for iOS - set(__qt_internal_cmake_ios_support_files_path "${_qt_import_prefix}/ios") - list(APPEND CMAKE_MODULE_PATH "${__qt_internal_cmake_ios_support_files_path}") +if(APPLE) + if(NOT CMAKE_SYSTEM_NAME OR CMAKE_SYSTEM_NAME STREQUAL "Darwin") + 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. +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 @@ -43,85 +66,15 @@ set(QT_ADDITIONAL_PACKAGES_PREFIX_PATH "" CACHE STRING set(QT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH "" CACHE STRING "Additional directories where find(Qt6 ...) host Qt components are searched") -# 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". -function(__qt_internal_collect_additional_prefix_paths out_var prefixes_var) - if(DEFINED "${out_var}") - return() - endif() - - set(additional_packages_prefix_paths "") - - set(additional_packages_prefixes "") - if(${prefixes_var}) - list(APPEND additional_packages_prefixes ${${prefixes_var}}) - endif() - if(DEFINED ENV{${prefixes_var}} - AND NOT "$ENV{${prefixes_var}}" STREQUAL "") - set(prefixes_from_env "$ENV{${prefixes_var}}") - if(NOT CMAKE_HOST_WIN32) - string(REPLACE ":" ";" prefixes_from_env "${prefixes_from_env}") - endif() - list(APPEND additional_packages_prefixes ${prefixes_from_env}) - endif() - - foreach(additional_path IN LISTS additional_packages_prefixes) - file(TO_CMAKE_PATH "${additional_path}" additional_path) - - # The prefix paths need to end with lib/cmake to ensure the packages are found when - # cross compiling. Search for REROOT_PATH_ISSUE_MARKER in the qt.toolchain.cmake file for - # details. - # We must pass the values via the PATHS options because the main find_package call uses - # 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") - endif() - list(APPEND additional_packages_prefix_paths "${additional_path}") - endforeach() - - set("${out_var}" "${additional_packages_prefix_paths}" PARENT_SCOPE) -endfunction() - __qt_internal_collect_additional_prefix_paths(_qt_additional_packages_prefix_paths QT_ADDITIONAL_PACKAGES_PREFIX_PATH) __qt_internal_collect_additional_prefix_paths(_qt_additional_host_packages_prefix_paths QT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH) -# 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) - list(APPEND result "${path}") - endforeach() - set("${out_var}" "${result}" PARENT_SCOPE) -endfunction() - __qt_internal_prefix_paths_to_roots(_qt_additional_host_packages_root_paths "${_qt_additional_host_packages_prefix_paths}") -# Public helpers available to all Qt packages. -include("${CMAKE_CURRENT_LIST_DIR}/QtFeature.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") - -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. @@ -189,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 new file mode 100644 index 0000000000..17acb37f0e --- /dev/null +++ b/cmake/QtConfigureTimeExecutableCMakeLists.txt.in @@ -0,0 +1,26 @@ +# 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) + +set(packages "@packages@") +set(defines @defines@) +set(compile_options @compile_options@) +set(link_options @link_options@) +set(output_directory @output_directory@) + +foreach(package IN LISTS packages) + find_package(${package} REQUIRED) +endforeach() + +add_executable(@configure_time_target@ @win32@ @macosx_bundle@ @sources@) +set_target_properties(@configure_time_target@ PROPERTIES + INCLUDE_DIRECTORIES "@include_directories@" + RUNTIME_OUTPUT_DIRECTORY "${output_directory}" +) + +target_compile_options(@configure_time_target@ PRIVATE ${compile_options}) +target_compile_definitions(@configure_time_target@ PRIVATE ${defines}) +target_link_options(@configure_time_target@ PRIVATE ${link_options}) diff --git a/cmake/QtCopyFileIfDifferent.cmake b/cmake/QtCopyFileIfDifferent.cmake index 351dc6e2d0..a27221ece8 100644 --- a/cmake/QtCopyFileIfDifferent.cmake +++ b/cmake/QtCopyFileIfDifferent.cmake @@ -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: BSD-3-Clause # copy_if_different works incorrect in Windows if file size if bigger than 2GB. # See https://gitlab.kitware.com/cmake/cmake/-/issues/23052 and QTBUG-99491 for details. diff --git a/cmake/QtDbusHelpers.cmake b/cmake/QtDbusHelpers.cmake index 81ebfa8b3d..26e98eff27 100644 --- a/cmake/QtDbusHelpers.cmake +++ b/cmake/QtDbusHelpers.cmake @@ -1,9 +1,14 @@ # 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: BSD-3-Clause # helper to set up a qdbusxml2cpp rule function(qt_create_qdbusxml2cpp_command target infile) - qt_parse_all_arguments(arg "qt_create_qdbusxml2cpp_command" "ADAPTOR;INTERFACE" "BASENAME" "FLAGS" ${ARGN}) + cmake_parse_arguments(PARSE_ARGV 2 arg + "ADAPTOR;INTERFACE" + "BASENAME" + "FLAGS") + _qt_internal_validate_all_args_are_parsed(arg) + if((arg_ADAPTOR AND arg_INTERFACE) OR (NOT arg_ADAPTOR AND NOT arg_INTERFACE)) message(FATAL_ERROR "qt_create_dbusxml2cpp_command needs either ADAPTOR or INTERFACE.") endif() @@ -55,6 +60,7 @@ function(qt_create_qdbusxml2cpp_command target infile) "${header_file}:${source_file}" "${absolute_in_file_path}" DEPENDS "${absolute_in_file_path}" ${QT_CMAKE_EXPORT_NAMESPACE}::qdbusxml2cpp WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + COMMAND_EXPAND_LISTS VERBATIM) target_sources("${target}" PRIVATE diff --git a/cmake/QtDeferredDependenciesHelpers.cmake b/cmake/QtDeferredDependenciesHelpers.cmake index 2391150f90..b233865727 100644 --- a/cmake/QtDeferredDependenciesHelpers.cmake +++ b/cmake/QtDeferredDependenciesHelpers.cmake @@ -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: BSD-3-Clause # Defers the connection 'dependent' -> 'dependency' # diff --git a/cmake/QtDocsHelpers.cmake b/cmake/QtDocsHelpers.cmake index d08c2c2b85..8b631e88ca 100644 --- a/cmake/QtDocsHelpers.cmake +++ b/cmake/QtDocsHelpers.cmake @@ -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: BSD-3-Clause # This function adds a dependency between a doc-generating target like 'generate_docs_Gui' # and the necessary tool target like 'qdoc'. @@ -21,14 +21,39 @@ 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 + # If a target is not built (which can happen for tools when crosscompiling), we shouldn't try # to generate docs. if(NOT TARGET "${target}") return() @@ -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() @@ -140,6 +166,10 @@ function(qt_internal_add_docs) ) add_dependencies(prepare_docs_${target} qattributionsscanner_${target}) + if(NOT TARGET sync_all_public_headers) + add_custom_target(sync_all_public_headers) + endif() + add_dependencies(prepare_docs_${target} sync_all_public_headers) # generate docs target set(generate_qdoc_args @@ -152,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 a235c9bb36..fb96ca4db2 100644 --- a/cmake/QtExecutableHelpers.cmake +++ b/cmake/QtExecutableHelpers.cmake @@ -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: BSD-3-Clause # This function creates a CMake target for a generic console or GUI binary. # Please consider to use a more specific version target like the one created @@ -12,11 +12,11 @@ # Qt::Core or Qt::Bootstrap libraries. Otherwise the Qt::Core library will be publicly # linked to the executable target by default. function(qt_internal_add_executable name) - qt_parse_all_arguments(arg "qt_internal_add_executable" + cmake_parse_arguments(PARSE_ARGV 1 arg "${__qt_internal_add_executable_optional_args}" "${__qt_internal_add_executable_single_args}" - "${__qt_internal_add_executable_multi_args}" - ${ARGN}) + "${__qt_internal_add_executable_multi_args}") + _qt_internal_validate_all_args_are_parsed(arg) if ("x${arg_OUTPUT_DIRECTORY}" STREQUAL "x") set(arg_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_BINDIR}") @@ -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() @@ -39,11 +40,6 @@ function(qt_internal_add_executable name) PROPERTY EXCLUDE_FROM_ALL "$<NOT:$<CONFIG:${QT_MULTI_CONFIG_FIRST_CONFIG}>>") endif() - if(WASM) - qt_internal_wasm_add_finalizers("${name}") - _qt_internal_wasm_add_target_helpers("${name}") - endif() - if (arg_VERSION) if(arg_VERSION MATCHES "[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+") # nothing to do @@ -69,12 +65,12 @@ function(qt_internal_add_executable name) QT_DELAYED_TARGET_COPYRIGHT "${arg_TARGET_COPYRIGHT}" ) else() - if("${arg_TARGET_DESCRIPTION}" STREQUAL "") + if(NOT arg_TARGET_DESCRIPTION) set(arg_TARGET_DESCRIPTION "Qt ${name}") endif() qt_set_target_info_properties(${name} ${ARGN} - TARGET_DESCRIPTION "${arg_TARGET_DESCRIPTION}" - TARGET_VERSION "${arg_VERSION}") + TARGET_DESCRIPTION ${arg_TARGET_DESCRIPTION} + TARGET_VERSION ${arg_VERSION}) endif() if (WIN32 AND NOT arg_DELAY_RC) @@ -115,8 +111,17 @@ function(qt_internal_add_executable name) "removed in a future Qt version. Use the LIBRARIES option instead.") endif() + if(arg_NO_UNITY_BUILD) + set(arg_NO_UNITY_BUILD "NO_UNITY_BUILD") + else() + set(arg_NO_UNITY_BUILD "") + endif() + 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} LIBRARIES @@ -124,10 +129,10 @@ function(qt_internal_add_executable name) ${arg_PUBLIC_LIBRARIES} Qt::PlatformCommonInternal ${extra_libraries} - DBUS_ADAPTOR_SOURCES "${arg_DBUS_ADAPTOR_SOURCES}" - DBUS_ADAPTOR_FLAGS "${arg_DBUS_ADAPTOR_FLAGS}" - DBUS_INTERFACE_SOURCES "${arg_DBUS_INTERFACE_SOURCES}" - DBUS_INTERFACE_FLAGS "${arg_DBUS_INTERFACE_FLAGS}" + DBUS_ADAPTOR_SOURCES ${arg_DBUS_ADAPTOR_SOURCES} + DBUS_ADAPTOR_FLAGS ${arg_DBUS_ADAPTOR_FLAGS} + DBUS_INTERFACE_SOURCES ${arg_DBUS_INTERFACE_SOURCES} + DBUS_INTERFACE_FLAGS ${arg_DBUS_INTERFACE_FLAGS} COMPILE_OPTIONS ${arg_COMPILE_OPTIONS} LINK_OPTIONS ${arg_LINK_OPTIONS} MOC_OPTIONS ${arg_MOC_OPTIONS} @@ -143,6 +148,10 @@ function(qt_internal_add_executable name) qt_internal_set_exceptions_flags("${name}" ${arg_EXCEPTIONS}) + if(WASM) + qt_internal_wasm_add_finalizers("${name}") + endif() + # Check if target needs to be excluded from all target. Also affects qt_install. # Set by qt_exclude_tool_directories_from_default_target. set(exclude_from_all FALSE) @@ -296,3 +305,233 @@ Q_IMPORT_PLUGIN($<JOIN:${class_names},)\nQ_IMPORT_PLUGIN(>) endforeach() endfunction() + +# This function compiles the target at configure time the very first time and creates the custom +# ${target}_build that re-runs compilation at build time if necessary. The resulting executable is +# imported under the provided target name. This function should only be used to compile tiny +# executables with system dependencies only. +# One-value Arguments: +# CMAKELISTS_TEMPLATE +# The CMakeLists.txt templated that is used to configure the project +# for an executable. By default the predefined template from the Qt installation is used. +# INSTALL_DIRECTORY +# installation directory of the executable. Ignored if NO_INSTALL is set. +# OUTPUT_NAME +# the output name of an executable +# CONFIG +# the name of configuration that tool needs to be build with. +# Multi-value Arguments: +# PACKAGES +# list of system packages are required to successfully build the project. +# INCLUDES +# list of include directories are required to successfully build the project. +# DEFINES +# list of definitions are required to successfully build the project. +# COMPILE_OPTIONS +# list of compiler options are required to successfully build the project. +# LINK_OPTIONS +# list of linker options are required to successfully build the project. +# SOURCES +# list of project sources. +# CMAKE_FLAGS +# specify flags of the form -DVAR:TYPE=VALUE to be passed to the cmake command-line used to +# drive the test build. +# Options: +# WIN32 +# reflects the corresponding add_executable argument. +# MACOSX_BUNDLE +# reflects the corresponding add_executable argument. +# NO_INSTALL +# avoids installing the tool. +function(qt_internal_add_configure_time_executable target) + set(one_value_args + CMAKELISTS_TEMPLATE + INSTALL_DIRECTORY + OUTPUT_NAME + CONFIG + ) + set(multi_value_args + PACKAGES + INCLUDES + DEFINES + COMPILE_OPTIONS + LINK_OPTIONS + SOURCES + CMAKE_FLAGS + ) + set(option_args WIN32 MACOSX_BUNDLE NO_INSTALL) + cmake_parse_arguments(PARSE_ARGV 1 arg + "${option_args}" "${one_value_args}" "${multi_value_args}") + + 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) + if(is_multi_config AND CMAKE_TRY_COMPILE_CONFIGURATION) + set(configuration_path "${CMAKE_TRY_COMPILE_CONFIGURATION}/") + set(config_build_arg "--config" "${CMAKE_TRY_COMPILE_CONFIGURATION}") + endif() + + set(configure_time_target "${target}") + if(arg_OUTPUT_NAME) + set(configure_time_target "${arg_OUTPUT_NAME}") + endif() + set(target_binary "${configure_time_target}${CMAKE_EXECUTABLE_SUFFIX}") + + set(install_dir "${INSTALL_BINDIR}") + 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) + message(FATAL_ERROR "No SOURCES given to target: ${target}") + endif() + set(sources "${arg_SOURCES}") + + # Timestamp file is required because CMake ignores 'add_custom_command' if we use only the + # binary file as the OUTPUT. + set(timestamp_file "${target_binary_dir}/${target_binary}_timestamp") + add_custom_command(OUTPUT "${target_binary_path}" "${timestamp_file}" + COMMAND + ${CMAKE_COMMAND} --build "${target_binary_dir}" --clean-first ${config_build_arg} + COMMAND + ${CMAKE_COMMAND} -E touch "${timestamp_file}" + DEPENDS + ${sources} + COMMENT + "Compiling ${target}" + VERBATIM + ) + + add_custom_target(${target}_build ALL + DEPENDS + "${target_binary_path}" + "${timestamp_file}" + ) + + set(should_build_at_configure_time TRUE) + 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") + if(${ts} GREATER ${last_ts}) + set(last_ts ${ts}) + endif() + endforeach() + + file(TIMESTAMP "${target_binary_path}" ts "%s") + if(${ts} GREATER_EQUAL ${last_ts}) + set(should_build_at_configure_time FALSE) + 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) + set(${template_arg_name} "") + if(DEFINED arg_${arg}) + set(${template_arg_name} "${arg_${arg}}") + endif() + endforeach() + + foreach(arg IN LISTS option_args) + string(TOLOWER "${arg}" template_arg_name) + set(${template_arg_name} "") + if(arg_${arg}) + set(${template_arg_name} "${arg}") + endif() + endforeach() + + file(MAKE_DIRECTORY "${target_binary_dir}") + set(template "${QT_CMAKE_DIR}/QtConfigureTimeExecutableCMakeLists.txt.in") + if(DEFINED arg_CMAKELISTS_TEMPLATE) + set(template "${arg_CMAKELISTS_TEMPLATE}") + 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}" + ${target} + ${cmake_flags_arg} + 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() + endif() + + add_executable(${target} IMPORTED GLOBAL) + add_executable(${QT_CMAKE_EXPORT_NAMESPACE}::${target} ALIAS ${target}) + set_target_properties(${target} PROPERTIES + _qt_internal_configure_time_target TRUE + _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 + _qt_internal_configure_time_target_install_location + "${install_dir}/${target_binary}" + ) + qt_path_join(target_install_dir ${QT_INSTALL_DIR} ${install_dir}) + qt_install(PROGRAMS "${target_binary_path}" DESTINATION "${target_install_dir}") + endif() +endfunction() diff --git a/cmake/QtFeature.cmake b/cmake/QtFeature.cmake index 89ae7598e5..96cb308b2c 100644 --- a/cmake/QtFeature.cmake +++ b/cmake/QtFeature.cmake @@ -1,13 +1,14 @@ # 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: BSD-3-Clause -include(QtFeatureCommon) include(CheckCXXCompilerFlag) function(qt_feature_module_begin) - qt_parse_all_arguments(arg "qt_feature_module_begin" + cmake_parse_arguments(PARSE_ARGV 0 arg "NO_MODULE;ONLY_EVALUATE_FEATURES" - "LIBRARY;PRIVATE_FILE;PUBLIC_FILE" "PUBLIC_DEPENDENCIES;PRIVATE_DEPENDENCIES" ${ARGN}) + "LIBRARY;PRIVATE_FILE;PUBLIC_FILE" + "PUBLIC_DEPENDENCIES;PRIVATE_DEPENDENCIES") + _qt_internal_validate_all_args_are_parsed(arg) if(NOT arg_ONLY_EVALUATE_FEATURES) if ("${arg_LIBRARY}" STREQUAL "" AND (NOT ${arg_NO_MODULE})) @@ -46,9 +47,11 @@ function(qt_feature feature) qt_feature_normalize_name("${feature}" feature) set_property(GLOBAL PROPERTY QT_FEATURE_ORIGINAL_NAME_${feature} "${original_name}") - qt_parse_all_arguments(arg "qt_feature" + cmake_parse_arguments(PARSE_ARGV 1 arg "PRIVATE;PUBLIC" - "LABEL;PURPOSE;SECTION;" "AUTODETECT;CONDITION;ENABLE;DISABLE;EMIT_IF" ${ARGN}) + "LABEL;PURPOSE;SECTION" + "AUTODETECT;CONDITION;ENABLE;DISABLE;EMIT_IF") + _qt_internal_validate_all_args_are_parsed(arg) set(_QT_FEATURE_DEFINITION_${feature} ${ARGN} PARENT_SCOPE) @@ -77,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}) @@ -140,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 @@ -160,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(). @@ -170,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") @@ -235,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}}") - set(result "${user_value}") + 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) @@ -295,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. @@ -330,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) @@ -370,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 "") @@ -397,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) @@ -410,9 +444,67 @@ 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) - qt_parse_all_arguments(arg "qt_feature_config" "NEGATE" "NAME" "" ${ARGN}) + cmake_parse_arguments(PARSE_ARGV 2 arg + "NEGATE" + "NAME" + "") + _qt_internal_validate_all_args_are_parsed(arg) # Store all the config related info in a unique variable key. set(key_name "_QT_FEATURE_CONFIG_DEFINITION_${feature}_${config_var_name}") @@ -472,7 +564,11 @@ endfunction() function(qt_feature_definition feature name) qt_feature_normalize_name("${feature}" feature) - qt_parse_all_arguments(arg "qt_feature_definition" "NEGATE" "VALUE;PREREQUISITE" "" ${ARGN}) + cmake_parse_arguments(PARSE_ARGV 2 arg + "NEGATE" + "VALUE;PREREQUISITE" + "") + _qt_internal_validate_all_args_are_parsed(arg) # Store all the define related info in a unique variable key. set(key_name "_QT_FEATURE_DEFINE_DEFINITION_${feature}_${name}") @@ -528,7 +624,11 @@ function(qt_evaluate_feature_definition key) endfunction() function(qt_extra_definition name value) - qt_parse_all_arguments(arg "qt_extra_definition" "PUBLIC;PRIVATE" "" "" ${ARGN}) + cmake_parse_arguments(PARSE_ARGV 2 arg + "PUBLIC;PRIVATE" + "" + "") + _qt_internal_validate_all_args_are_parsed(arg) if (arg_PUBLIC) string(APPEND __QtFeature_public_extra "\n#define ${name} ${value}\n") @@ -662,22 +762,6 @@ function(qt_feature_module_end) ) endif() - # Extra header injections which have to have forwarding headers created by - # qt_install_injections. - # Skip creating forwarding headers if qt_feature_module_begin was called with NO_MODULE, aka - # there is no include/<module_name> so there's no place to put the forwarding headers. - if(__QtFeature_library) - set(injections "") - qt_compute_injection_forwarding_header("${__QtFeature_library}" - SOURCE "${__QtFeature_public_file}" - OUT_VAR injections) - qt_compute_injection_forwarding_header("${__QtFeature_library}" - SOURCE "${__QtFeature_private_file}" PRIVATE - OUT_VAR injections) - - set(${arg_OUT_VAR_PREFIX}extra_library_injections ${injections} PARENT_SCOPE) - endif() - if (NOT ("${target}" STREQUAL "NO_MODULE") AND NOT arg_ONLY_EVALUATE_FEATURES) get_target_property(targetType "${target}" TYPE) if("${targetType}" STREQUAL "INTERFACE_LIBRARY") @@ -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") @@ -924,6 +1045,7 @@ function(qt_config_compile_test name) # fail instead of cmake abort later via CMAKE_REQUIRED_LIBRARIES. string(FIND "${library}" "::" cmake_target_namespace_separator) if(NOT cmake_target_namespace_separator EQUAL -1) + message(STATUS "Performing Test ${arg_LABEL} - Failed because ${library} not found") set(HAVE_${name} FALSE) break() endif() @@ -953,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() @@ -974,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}") @@ -986,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() @@ -1006,6 +1141,7 @@ function(qt_get_platform_try_compile_vars out_var) list(APPEND flags "CMAKE_CXX_FLAGS_RELEASE") list(APPEND flags "CMAKE_CXX_FLAGS_RELWITHDEBINFO") list(APPEND flags "CMAKE_OBJCOPY") + list(APPEND flags "CMAKE_EXE_LINKER_FLAGS") # Pass toolchain files. if(CMAKE_TOOLCHAIN_FILE) @@ -1022,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() @@ -1056,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) @@ -1223,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/QtFeatureCommon.cmake b/cmake/QtFeatureCommon.cmake index 097baa3401..9b49d4fcac 100644 --- a/cmake/QtFeatureCommon.cmake +++ b/cmake/QtFeatureCommon.cmake @@ -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: BSD-3-Clause function(qt_feature_normalize_name name out_var) # Normalize the feature name to something CMake can deal with. diff --git a/cmake/QtFindPackageHelpers.cmake b/cmake/QtFindPackageHelpers.cmake index 6fae5346da..dd10bde75a 100644 --- a/cmake/QtFindPackageHelpers.cmake +++ b/cmake/QtFindPackageHelpers.cmake @@ -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: BSD-3-Clause # This function recursively walks transitive link libraries of the given target # and promotes those targets to be IMPORTED_GLOBAL if they are not. @@ -16,6 +16,15 @@ function(qt_find_package_promote_targets_to_global_scope target) "qt_find_package_targets_dict" "promote_global") endfunction() +# As an optimization when using -developer-build, qt_find_package records which +# packages were found during the initial configuration. Then on subsequent +# reconfigurations it skips looking for packages that were not found on the +# initial run. +# For the build system to pick up a newly added qt_find_package call, you need to: +# - Start with a clean build dir +# - Or remove the <builddir>/CMakeCache.txt file and configure from scratch +# - Or remove the QT_INTERNAL_PREVIOUSLY_FOUND_PACKAGES cache variable (by +# editing CMakeCache.txt) and reconfigure. macro(qt_find_package) # Get the target names we expect to be provided by the package. set(find_package_options CONFIG NO_MODULE MODULE REQUIRED) @@ -34,6 +43,20 @@ macro(qt_find_package) # qt_find_package(PNG PROVIDED_TARGET PNG::PNG) still needs to succeed and register the provided # targets. To enable the debugging behavior, set QT_DEBUG_QT_FIND_PACKAGE to 1. set(_qt_find_package_skip_find_package FALSE) + + # Skip looking for packages that were not found on initial configuration, because they likely + # won't be found again, and only waste configuration time. + # Speeds up reconfiguration configuration for certain platforms and repos. + # Due to this behavior being different from what general CMake projects expect, it is only + # done for -developer-builds. + if(QT_INTERNAL_PREVIOUSLY_FOUND_PACKAGES AND + NOT "${ARGV0}" IN_LIST QT_INTERNAL_PREVIOUSLY_FOUND_PACKAGES + AND "${ARGV0}" IN_LIST QT_INTERNAL_PREVIOUSLY_SEARCHED_PACKAGES) + set(_qt_find_package_skip_find_package TRUE) + endif() + + set_property(GLOBAL APPEND PROPERTY _qt_previously_searched_packages "${ARGV0}") + if(QT_DEBUG_QT_FIND_PACKAGE AND ${ARGV0}_FOUND AND arg_PROVIDED_TARGETS) set(_qt_find_package_skip_find_package TRUE) foreach(qt_find_package_target_name ${arg_PROVIDED_TARGETS}) @@ -141,6 +164,11 @@ macro(qt_find_package) endif() endif() + if(${ARGV0}_FOUND) + # Record that the package was found, so that future reconfigurations can be sped up. + set_property(GLOBAL APPEND PROPERTY _qt_previously_found_packages "${ARGV0}") + endif() + if(${ARGV0}_FOUND AND arg_PROVIDED_TARGETS AND NOT _qt_find_package_skip_find_package) # If package was found, associate each target with its package name. This will be used # later when creating Config files for Qt libraries, to generate correct find_dependency() @@ -200,6 +228,46 @@ macro(qt_find_package) endif() endmacro() +# 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. +# Can also be opted in or opted out via QT_INTERNAL_SAVE_PREVIOUSLY_FOUND_PACKAGES. +# Opting out will need two reconfigurations to take effect. +function(qt_internal_save_previously_visited_packages) + if(DEFINED QT_INTERNAL_SAVE_PREVIOUSLY_FOUND_PACKAGES) + set(should_save "${QT_INTERNAL_SAVE_PREVIOUSLY_FOUND_PACKAGES}") + else() + if(FEATURE_developer_build OR QT_FEATURE_developer_build) + set(should_save ON) + else() + set(should_save OFF) + endif() + endif() + + if(NOT should_save) + # When the value is flipped to OFF, remove any previously saved packages. + unset(QT_INTERNAL_PREVIOUSLY_FOUND_PACKAGES CACHE) + unset(QT_INTERNAL_PREVIOUSLY_SEARCHED_PACKAGES CACHE) + return() + endif() + + get_property(_qt_previously_found_packages GLOBAL PROPERTY _qt_previously_found_packages) + if(_qt_previously_found_packages) + list(REMOVE_DUPLICATES _qt_previously_found_packages) + set(QT_INTERNAL_PREVIOUSLY_FOUND_PACKAGES "${_qt_previously_found_packages}" CACHE INTERNAL + "List of CMake packages found during configuration using qt_find_package.") + endif() + + get_property(_qt_previously_searched_packages GLOBAL PROPERTY _qt_previously_searched_packages) + if(_qt_previously_searched_packages) + list(REMOVE_DUPLICATES _qt_previously_searched_packages) + set(QT_INTERNAL_PREVIOUSLY_SEARCHED_PACKAGES + "${_qt_previously_searched_packages}" CACHE INTERNAL + "List of CMake packages searched during configuration using qt_find_package." + ) + endif() +endfunction() + # Return qmake library name for the given target, e.g. return "vulkan" for "Vulkan::Vulkan". function(qt_internal_map_target_to_qmake_lib target out_var) set(${out_var} "${QT_QMAKE_LIB_OF_TARGET_${target}}" PARENT_SCOPE) @@ -447,8 +515,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}") @@ -464,8 +539,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 e9923b358e..a8d3da49d1 100644 --- a/cmake/QtFindWrapHelper.cmake +++ b/cmake/QtFindWrapHelper.cmake @@ -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: BSD-3-Clause # Creates an imported wrapper target that links against either a Qt bundled package # or a system package. diff --git a/cmake/QtFinishPkgConfigFile.cmake b/cmake/QtFinishPkgConfigFile.cmake index 1c5b424d6c..3799621400 100644 --- a/cmake/QtFinishPkgConfigFile.cmake +++ b/cmake/QtFinishPkgConfigFile.cmake @@ -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: BSD-3-Clause # Finish a preliminary .pc file. # diff --git a/cmake/QtFinishPrlFile.cmake b/cmake/QtFinishPrlFile.cmake index 2ade4e625f..1cf9377e6c 100644 --- a/cmake/QtFinishPrlFile.cmake +++ b/cmake/QtFinishPrlFile.cmake @@ -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: BSD-3-Clause # Finish a preliminary .prl file. # diff --git a/cmake/QtFlagHandlingHelpers.cmake b/cmake/QtFlagHandlingHelpers.cmake index 708496fb85..6a62b85c03 100644 --- a/cmake/QtFlagHandlingHelpers.cmake +++ b/cmake/QtFlagHandlingHelpers.cmake @@ -1,27 +1,79 @@ # 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: 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: -# PRIVATE_HEADERS specifies the list of header files that are used to generate -# Qt_<version>_PRIVATE_API section. Requires perl. # PRIVATE_CONTENT_FILE specifies the pre-cooked content of Qt_<version>_PRIVATE_API section. # Requires the content file available at build time. function(qt_internal_add_linker_version_script target) - qt_parse_all_arguments(arg "qt_internal_add_linker_version_script" + if(WASM) + return() + endif() + + cmake_parse_arguments(PARSE_ARGV 1 arg "" "PRIVATE_CONTENT_FILE" "PRIVATE_HEADERS" - ${ARGN} ) + _qt_internal_validate_all_args_are_parsed(arg) if(arg_PRIVATE_CONTENT_FILE AND arg_PRIVATE_HEADERS) message(FATAL_ERROR "Both PRIVATE_CONTENT_FILE and PRIVATE_HEADERS are specified.") 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") @@ -31,53 +83,46 @@ function(qt_internal_add_linker_version_script target) endif() string(APPEND contents "};\n") set(current "Qt_${PROJECT_VERSION_MAJOR}") - if (QT_NAMESPACE STREQUAL "") - set(tag_symbol "qt_version_tag") - else() - set(tag_symbol "qt_version_tag_${QT_NAMESPACE}") + 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") + + if(NOT target_type STREQUAL "INTERFACE_LIBRARY") + set(property_genex "$<TARGET_PROPERTY:${target},_qt_extra_linker_script_content>") + set(check_genex "$<BOOL:${property_genex}>") + string(APPEND contents + "$<${check_genex}:${property_genex}>") endif() - string(APPEND contents "${current} { *; };\n") - - foreach(minor_version RANGE ${PROJECT_VERSION_MINOR}) - set(previous "${current}") - set(current "Qt_${PROJECT_VERSION_MAJOR}.${minor_version}") - if (minor_version EQUAL ${PROJECT_VERSION_MINOR}) - string(APPEND contents "${current} { ${tag_symbol}; } ${previous};\n") - else() - string(APPEND contents "${current} {} ${previous};\n") - endif() - endforeach() set(infile "${CMAKE_CURRENT_BINARY_DIR}/${target}.version.in") set(outfile "${CMAKE_CURRENT_BINARY_DIR}/${target}.version") file(GENERATE OUTPUT "${infile}" CONTENT "${contents}") - if(arg_PRIVATE_HEADERS) - qt_ensure_perl() - set(generator_command "${HOST_PERL}" - "${QT_MKSPECS_DIR}/features/data/unix/findclasslist.pl" - "<" "${infile}" ">" "${outfile}" - ) - set(generator_dependencies - "${infile}" - "${QT_MKSPECS_DIR}/features/data/unix/findclasslist.pl" - ) - else() - if(NOT arg_PRIVATE_CONTENT_FILE) - set(arg_PRIVATE_CONTENT_FILE "") - endif() - set(generator_command ${CMAKE_COMMAND} - "-DIN_FILE=${infile}" - "-DPRIVATE_CONTENT_FILE=${arg_PRIVATE_CONTENT_FILE}" - "-DOUT_FILE=${outfile}" - -P "${QT_CMAKE_DIR}/QtGenerateVersionScript.cmake" - ) - set(generator_dependencies - "${arg_PRIVATE_CONTENT_FILE}" - "${QT_CMAKE_DIR}/QtGenerateVersionScript.cmake" - ) + if(NOT arg_PRIVATE_CONTENT_FILE) + set(arg_PRIVATE_CONTENT_FILE "") endif() + set(generator_command ${CMAKE_COMMAND} + "-DIN_FILE=${infile}" + "-DPRIVATE_CONTENT_FILE=${arg_PRIVATE_CONTENT_FILE}" + "-DOUT_FILE=${outfile}" + -P "${QT_CMAKE_DIR}/QtGenerateVersionScript.cmake" + ) + set(generator_dependencies + "${arg_PRIVATE_CONTENT_FILE}" + "${QT_CMAKE_DIR}/QtGenerateVersionScript.cmake" + ) add_custom_command( OUTPUT "${outfile}" @@ -94,7 +139,18 @@ function(qt_internal_add_linker_version_script target) endfunction() function(qt_internal_add_link_flags_no_undefined target) - if (NOT QT_BUILD_SHARED_LIBS) + 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) @@ -134,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") @@ -147,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() @@ -166,13 +241,17 @@ 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}") @@ -210,21 +289,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() @@ -297,14 +376,15 @@ function(qt_set_msvc_cplusplus_options target visibility) # Check qt_config_compile_test for more info. if(MSVC AND MSVC_VERSION GREATER_EQUAL 1913) set(flags "-Zc:__cplusplus" "-permissive-") - target_compile_options("${target}" ${visibility} "$<$<COMPILE_LANGUAGE:CXX>:${flags}>") + target_compile_options("${target}" ${visibility} + "$<$<AND:$<CXX_COMPILER_ID:MSVC>,$<COMPILE_LANGUAGE:CXX>>:${flags}>") endif() endfunction() function(qt_enable_utf8_sources target) set(utf8_flags "") if(MSVC) - list(APPEND utf8_flags "-utf-8") + list(APPEND utf8_flags "$<$<CXX_COMPILER_ID:MSVC>:-utf-8>") endif() if(utf8_flags) @@ -324,7 +404,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() @@ -536,13 +616,11 @@ endfunction() # LANGUAGES - optional list of languages like 'C', 'CXX', for which to remove the flags # if not provided, defaults to the list of enabled C-like languages function(qt_internal_remove_known_optimization_flags) - qt_parse_all_arguments( - arg - "qt_internal_remove_known_optimization_flags" + cmake_parse_arguments(PARSE_ARGV 0 arg "IN_CACHE" "" - "CONFIGS;LANGUAGES" - ${ARGN}) + "CONFIGS;LANGUAGES") + _qt_internal_validate_all_args_are_parsed(arg) if(NOT arg_CONFIGS) message(FATAL_ERROR @@ -569,20 +647,27 @@ endfunction() # Removes specified flags from CMAKE_<LANGUAGES>_FLAGS[_CONFIGS] variables # -# IN_CACHE enables flags removal from CACHE -# CONFIGS list of configurations that need to clear flags. Clears all configs by default if not -# specified. -# LANGUAGES list of LANGUAGES that need clear flags. Clears all languages by default if not -# specified. -# REGEX enables the flag processing as a regular expression. +# Option Arguments: +# IN_CACHE +# Enables flags removal from CACHE +# REGEX +# Enables the flag processing as a regular expression. +# +# Multi-value Arguments: +# CONFIGS +# List of configurations that need to clear flags. Clears all configs by default if not +# specified. +# +# LANGUAGES +# List of LANGUAGES that need clear flags. Clears all languages by default if not +# specified. function(qt_internal_remove_compiler_flags flags) - qt_parse_all_arguments(arg - "qt_internal_remove_compiler_flags" + cmake_parse_arguments(PARSE_ARGV 1 arg "IN_CACHE;REGEX" "" "CONFIGS;LANGUAGES" - ${ARGN} ) + _qt_internal_validate_all_args_are_parsed(arg) if("${flags}" STREQUAL "") message(WARNING "qt_internal_remove_compiler_flags was called without any flags specified.") @@ -598,8 +683,7 @@ function(qt_internal_remove_compiler_flags flags) if(arg_CONFIGS) set(configs "${arg_CONFIGS}") else() - message(FATAL_ERROR - "You must specify at least one configuration for which to remove the flags.") + qt_internal_get_configs_for_flag_manipulation(configs) endif() if(arg_REGEX) @@ -637,13 +721,11 @@ endfunction() # LANGUAGES - optional list of languages like 'C', 'CXX', for which to add the flags # if not provided, defaults to the list of enabled C-like languages function(qt_internal_add_compiler_flags) - qt_parse_all_arguments( - arg - "qt_internal_add_compiler_flags" + cmake_parse_arguments(PARSE_ARGV 0 arg "IN_CACHE" "FLAGS" - "CONFIGS;LANGUAGES" - ${ARGN}) + "CONFIGS;LANGUAGES") + _qt_internal_validate_all_args_are_parsed(arg) if(NOT arg_CONFIGS) message(FATAL_ERROR @@ -680,13 +762,11 @@ endfunction() # LANGUAGES - optional list of languages like 'C', 'CXX', for which to add the flags # if not provided, defaults to the list of enabled C-like languages function(qt_internal_add_compiler_flags_for_release_configs) - qt_parse_all_arguments( - arg - "qt_internal_add_compiler_flags_for_release_configs" + cmake_parse_arguments(PARSE_ARGV 0 arg "IN_CACHE" "FLAGS" - "LANGUAGES" - ${ARGN}) + "LANGUAGES") + _qt_internal_validate_all_args_are_parsed(arg) set(args "") @@ -726,13 +806,11 @@ endfunction() # It is meant to be called in a subdirectory scope to enable full optimizations for a particular # Qt module, like Core or Gui. function(qt_internal_add_optimize_full_flags) - qt_parse_all_arguments( - arg - "qt_internal_add_optimize_full_flags" + cmake_parse_arguments(PARSE_ARGV 0 arg "IN_CACHE" "" - "" - ${ARGN}) + "") + _qt_internal_validate_all_args_are_parsed(arg) # QT_USE_DEFAULT_CMAKE_OPTIMIZATION_FLAGS disables forced full optimization. if(QT_USE_DEFAULT_CMAKE_OPTIMIZATION_FLAGS) @@ -788,13 +866,11 @@ endfunction() # LANGUAGES - optional list of languages like 'C', 'CXX', for which to replace the flags # if not provided, defaults to the list of enabled C-like languages function(qt_internal_replace_compiler_flags match_string replace_string) - qt_parse_all_arguments( - arg - "qt_internal_replace_compiler_flags" + cmake_parse_arguments(PARSE_ARGV 2 arg "IN_CACHE" "" - "CONFIGS;LANGUAGES" - ${ARGN}) + "CONFIGS;LANGUAGES") + _qt_internal_validate_all_args_are_parsed(arg) if(NOT arg_CONFIGS) message(FATAL_ERROR @@ -829,13 +905,11 @@ endfunction() # CMAKE_<LINKER_TYPE>_LINKER_FLAGS_<CONFIG> cache variable. # e.g EXE, MODULE, SHARED, STATIC. function(qt_internal_add_linker_flags) - qt_parse_all_arguments( - arg - "qt_internal_add_linker_flags" + cmake_parse_arguments(PARSE_ARGV 0 arg "IN_CACHE" "FLAGS" - "CONFIGS;TYPES" - ${ARGN}) + "CONFIGS;TYPES") + _qt_internal_validate_all_args_are_parsed(arg) if(NOT arg_TYPES) message(FATAL_ERROR @@ -876,13 +950,11 @@ endfunction() # CMAKE_<LINKER_TYPE>_LINKER_FLAGS_<CONFIG> cache variable. # e.g EXE, MODULE, SHARED, STATIC. function(qt_internal_replace_linker_flags match_string replace_string) - qt_parse_all_arguments( - arg - "qt_internal_replace_compiler_flags" + cmake_parse_arguments(PARSE_ARGV 2 arg "IN_CACHE" "" - "CONFIGS;TYPES" - ${ARGN}) + "CONFIGS;TYPES") + _qt_internal_validate_all_args_are_parsed(arg) if(NOT arg_TYPES) message(FATAL_ERROR @@ -1017,14 +1089,42 @@ 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(config ${configs}) - foreach(lang ${enabled_languages}) + foreach(lang ${enabled_languages}) + set(flag_var_name "CMAKE_${lang}_FLAGS") + set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) + + foreach(config ${configs}) set(flag_var_name "CMAKE_${lang}_FLAGS_${config}") set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) endforeach() + endforeach() - foreach(t ${target_link_types}) + foreach(t ${target_link_types}) + set(flag_var_name "CMAKE_${t}_LINKER_FLAGS") + set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) + + foreach(config ${configs}) set(flag_var_name "CMAKE_${t}_LINKER_FLAGS_${config}") set(${flag_var_name} "${${flag_var_name}}" PARENT_SCOPE) endforeach() diff --git a/cmake/QtFrameworkHelpers.cmake b/cmake/QtFrameworkHelpers.cmake index c90e2c1623..750caf2cb8 100644 --- a/cmake/QtFrameworkHelpers.cmake +++ b/cmake/QtFrameworkHelpers.cmake @@ -1,10 +1,12 @@ # 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: BSD-3-Clause macro(qt_find_apple_system_frameworks) if(APPLE) qt_internal_find_apple_system_framework(FWAppKit AppKit) + qt_internal_find_apple_system_framework(FWCFNetwork CFNetwork) qt_internal_find_apple_system_framework(FWAssetsLibrary AssetsLibrary) + qt_internal_find_apple_system_framework(FWPhotos Photos) qt_internal_find_apple_system_framework(FWAudioToolbox AudioToolbox) qt_internal_find_apple_system_framework(FWApplicationServices ApplicationServices) qt_internal_find_apple_system_framework(FWCarbon Carbon) @@ -31,6 +33,11 @@ macro(qt_find_apple_system_frameworks) qt_internal_find_apple_system_framework(FWWatchKit WatchKit) qt_internal_find_apple_system_framework(FWGameController GameController) qt_internal_find_apple_system_framework(FWCoreBluetooth CoreBluetooth) + qt_internal_find_apple_system_framework(FWAVFoundation AVFoundation) + 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() @@ -53,7 +60,7 @@ function(qt_internal_find_apple_system_framework out_var framework_name) endif() endfunction() -# Copy header files to QtXYZ.framework/Versions/A/Headers/ +# Copy header files to the framework's Headers directory # Use this function for header files that # - are not added as source files to the target # - are not marked as PUBLIC_HEADER @@ -66,7 +73,7 @@ function(qt_copy_framework_headers target) set(options) set(oneValueArgs) - set(multiValueArgs PUBLIC PRIVATE QPA) + set(multiValueArgs PUBLIC PRIVATE QPA RHI SSG) cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) qt_internal_get_framework_info(fw ${target}) @@ -74,27 +81,82 @@ function(qt_copy_framework_headers target) set(output_dir_PUBLIC "${output_dir}/${fw_versioned_header_dir}") 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) + 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}" + ) + + if(CMAKE_GENERATOR MATCHES "^Ninja") + add_custom_command( + OUTPUT "${output_dir}/${fw_versioned_header_dir}" + DEPENDS ${target}_sync_headers + COMMAND ${copy_fw_sync_headers_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}) + 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) + # Hack to create the "Headers" symlink in the framework: + # Create a fake header file and copy it into the framework by marking it as PUBLIC_HEADER. + # CMake now takes care of creating the symlink. + set(fake_header "${CMAKE_CURRENT_BINARY_DIR}/${target}_fake_header.h") + qt_internal_get_main_cmake_configuration(main_config) + file(GENERATE OUTPUT "${fake_header}" CONTENT "// ignore this file\n" + CONDITION "$<CONFIG:${main_config}>") + target_sources(${target} PRIVATE "${fake_header}") + set_source_files_properties("${fake_header}" PROPERTIES GENERATED ON) + set_property(TARGET ${target} APPEND PROPERTY PUBLIC_HEADER "${fake_header}") endfunction() function(qt_finalize_framework_headers_copy target) @@ -108,17 +170,7 @@ function(qt_finalize_framework_headers_copy target) endif() get_target_property(headers ${target} QT_COPIED_FRAMEWORK_HEADERS) if(headers) - # Hack to create the "Headers" symlink in the framework: - # Create a fake header file and copy it into the framework by marking it as PUBLIC_HEADER. - # CMake now takes care of creating the symlink. - set(fake_header ${target}_fake_header.h) - qt_internal_get_main_cmake_configuration(main_config) - file(GENERATE OUTPUT ${fake_header} CONTENT "// ignore this file\n" - CONDITION "$<CONFIG:${main_config}>") - string(PREPEND fake_header "${CMAKE_CURRENT_BINARY_DIR}/") - target_sources(${target} PRIVATE ${fake_header}) - set_source_files_properties(${fake_header} PROPERTIES GENERATED ON) - set_property(TARGET ${target} APPEND PROPERTY PUBLIC_HEADER ${fake_header}) + qt_internal_generate_fake_framework_header(${target}) # Add a target, e.g. Core_framework_headers, that triggers the header copy. add_custom_target(${target}_framework_headers DEPENDS ${headers}) @@ -156,8 +208,13 @@ 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") - set(${out_var}_versioned_header_dir "${${out_var}_dir}/Versions/${${out_var}_version}/Headers") - set(${out_var}_private_header_dir "${${out_var}_header_dir}/${${out_var}_bundle_version}") + if(UIKIT) + # iOS frameworks do not version their headers + 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") + 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}") set(${out_var}_name "${${out_var}_name}" PARENT_SCOPE) diff --git a/cmake/QtGenerateExtPri.cmake b/cmake/QtGenerateExtPri.cmake index c6f41ac809..b79ef2b721 100644 --- a/cmake/QtGenerateExtPri.cmake +++ b/cmake/QtGenerateExtPri.cmake @@ -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: BSD-3-Clause # Generate a qt_ext_XXX.pri file. # diff --git a/cmake/QtGenerateLibHelpers.cmake b/cmake/QtGenerateLibHelpers.cmake index d711574c10..3ffe354fd8 100644 --- a/cmake/QtGenerateLibHelpers.cmake +++ b/cmake/QtGenerateLibHelpers.cmake @@ -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: BSD-3-Clause # Given "/usr/lib/x86_64-linux-gnu/libcups.so" # Returns "cups" or an empty string if the file is not an absolute library path. diff --git a/cmake/QtGenerateLibPri.cmake b/cmake/QtGenerateLibPri.cmake index 309a218fd1..bde42b7723 100644 --- a/cmake/QtGenerateLibPri.cmake +++ b/cmake/QtGenerateLibPri.cmake @@ -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: BSD-3-Clause # Generate a qt_lib_XXX.pri file. # diff --git a/cmake/QtGlobalStateHelpers.cmake b/cmake/QtGlobalStateHelpers.cmake index 43f785c21e..2df89486c8 100644 --- a/cmake/QtGlobalStateHelpers.cmake +++ b/cmake/QtGlobalStateHelpers.cmake @@ -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: BSD-3-Clause function(qt_internal_clear_qt_repo_known_modules) set(QT_REPO_KNOWN_MODULES "" CACHE INTERNAL "Known current repo Qt modules" FORCE) diff --git a/cmake/QtHeadersClean.cmake b/cmake/QtHeadersClean.cmake index f73c7e409c..f47ac1754c 100644 --- a/cmake/QtHeadersClean.cmake +++ b/cmake/QtHeadersClean.cmake @@ -1,29 +1,25 @@ # 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: BSD-3-Clause # Add a custom ${module_target}_headersclean_check target that builds each header in # ${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_include_name - module_headers) - # module_headers is a list of strings of the form - # <headerfile>[:feature] +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() + endif() + set(hclean_headers "") - foreach(entry ${module_headers}) - string(REPLACE ":" ";" entry_list ${entry}) - list(LENGTH entry_list entry_list_length) - list(GET entry_list 0 entry_path) - - if (${entry_list_length} EQUAL 2) - list(GET entry_list 1 entry_feature) - if (NOT QT_FEATURE_${entry_feature}) - message(STATUS "headersclean: Ignoring header ${entry_path} because of missing feature ${entry_feature}") - continue() - endif() + foreach(header IN LISTS module_headers) + get_filename_component(header_name "${header}" NAME) + if(header_name MATCHES "^q[^_]+\\.h$" AND NOT header_name MATCHES ".*(global|exports)\\.h") + list(APPEND hclean_headers "${header}") endif() - list(APPEND hclean_headers ${entry_path}) endforeach() # Make sure that the header compiles with our strict options @@ -31,6 +27,7 @@ function(qt_internal_add_headersclean_target -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 @@ -42,7 +39,7 @@ function(qt_internal_add_headersclean_target endif() set(prop_prefix "") - get_target_property(target_type "${target}" TYPE) + get_target_property(target_type "${module_target}" TYPE) if(target_type STREQUAL "INTERFACE_LIBRARY") set(prop_prefix "INTERFACE_") endif() @@ -53,6 +50,12 @@ function(qt_internal_add_headersclean_target set(target_includes_joined_genex "$<${includes_exist_genex}:-I$<JOIN:${target_includes_genex},;-I>>") + get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) + if(is_multi_config) + list(GET CMAKE_CONFIGURATION_TYPES 0 first_config_type) + set(config_suffix "$<$<NOT:$<CONFIG:${first_config_type}>>:-$<CONFIG>>") + endif() + # qmake doesn't seem to add the defines that are set by the header_only_module when checking the # the cleanliness of the module's header files. # This allows us to bypass an error with CMake 3.18 and lower when trying to evaluate @@ -102,41 +105,33 @@ function(qt_internal_add_headersclean_target 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 - -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 + -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) 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) @@ -152,10 +147,7 @@ function(qt_internal_add_headersclean_target # 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}") @@ -165,76 +157,120 @@ function(qt_internal_add_headersclean_target endforeach() endif() - foreach(header ${hclean_headers}) - get_filename_component(input_path "${header}" ABSOLUTE) - set(artifact_path "header_check/${header}.o") - get_filename_component(artifact_directory "${artifact_path}" DIRECTORY) - set(comment_header_path "${CMAKE_CURRENT_SOURCE_DIR}/${header}") - file(RELATIVE_PATH comment_header_path "${PROJECT_SOURCE_DIR}" "${comment_header_path}") - - add_custom_command( - OUTPUT "${artifact_path}" - COMMENT "headersclean: Checking header ${comment_header_path}" - COMMAND ${CMAKE_COMMAND} -E make_directory "${artifact_directory}" - COMMAND - ${compiler_to_run} -c ${cxx_flags} - "${target_compile_flags_joined_genex}" - "${target_defines_joined_genex}" - ${hcleanFLAGS} - "${target_includes_joined_genex}" - ${framework_includes} - ${hcleanDEFS} - -xc++ "${input_path}" - -o${artifact_path} - IMPLICIT_DEPENDS CXX - VERBATIM - COMMAND_EXPAND_LISTS - DEPENDS "${input_path}" - ) - list(APPEND hclean_artifacts "${artifact_path}") - endforeach() + set(compiler_command_line + "${compiler_to_run}" "-c" "${cxx_flags}" + "${target_compile_flags_joined_genex}" + "${target_defines_joined_genex}" + "${hcleanFLAGS}" + "${target_includes_joined_genex}" + "${framework_includes}" + "${hcleanDEFS}" + ) + string(JOIN " " compiler_command_line_variables + "-xc++" + "\${INPUT_HEADER_FILE}" + "-o" + "\${OUTPUT_ARTIFACT}" + ) + set(input_header_path_type ABSOLUTE) elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - # -Za would enable strict standards behavior, but we can't add it because - # <windows.h> and <GL.h> violate the standards. - set(hcleanFLAGS -std:c++latest -Zc:__cplusplus -WX -W3) + # 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) # cl.exe needs a source path get_filename_component(source_path "${QT_MKSPECS_DIR}/features/data/dummy.cpp" REALPATH) - foreach(header ${hclean_headers}) - # We need realpath here to make sure path starts with drive letter - get_filename_component(input_path "${header}" REALPATH) - set(artifact_path "header_${header}.o") - set(comment_header_path "${CMAKE_CURRENT_SOURCE_DIR}/${header}") - file(RELATIVE_PATH comment_header_path "${PROJECT_SOURCE_DIR}" "${comment_header_path}") - - add_custom_command( - OUTPUT "${artifact_path}" - COMMENT "headersclean: Checking header ${comment_header_path}" - COMMAND - ${compiler_to_run} -nologo -c ${CMAKE_CXX_FLAGS} - "${target_compile_flags_joined_genex}" - "${target_defines_joined_genex}" - ${hcleanFLAGS} - "${target_includes_joined_genex}" - ${hcleanDEFS} - -FI "${input_path}" - -Fo${artifact_path} "${source_path}" - IMPLICIT_DEPENDS CXX - VERBATIM - COMMAND_EXPAND_LISTS - DEPENDS "${input_path}" - ) - list(APPEND hclean_artifacts "${artifact_path}") - endforeach() + set(compiler_command_line + "${compiler_to_run}" "-nologo" "-c" "${CMAKE_CXX_FLAGS}" + "${target_compile_flags_joined_genex}" + "${target_defines_joined_genex}" + "${hcleanFLAGS}" + "${target_includes_joined_genex}" + "${hcleanDEFS}" + "${hcleanUDEFS}" + ) + string(JOIN " " compiler_command_line_variables + "-FI" + "\${INPUT_HEADER_FILE}" + "-Fo\${OUTPUT_ARTIFACT}" + "${source_path}" + ) + + set(input_header_path_type REALPATH) else() message(FATAL_ERROR "CMAKE_CXX_COMPILER_ID \"${CMAKE_CXX_COMPILER_ID}\" is not supported" " for the headersclean check.") endif() + qt_internal_module_info(module ${module_target}) + + unset(header_check_exceptions) + set(header_check_exceptions + "${CMAKE_CURRENT_BINARY_DIR}/${module}_header_check_exceptions") + set(headers_check_parameters + "${CMAKE_CURRENT_BINARY_DIR}/${module_target}HeadersCheckParameters${config_suffix}.cmake") + string(JOIN "\n" headers_check_parameters_content + "set(HEADER_CHECK_EXCEPTIONS" + " \"${header_check_exceptions}\")" + "set(HEADER_CHECK_COMPILER_COMMAND_LINE" + " \[\[$<JOIN:${compiler_command_line},\]\]\n \[\[>\]\]\n" + " ${compiler_command_line_variables}" + ")" + ) + file(GENERATE OUTPUT "${headers_check_parameters}" + CONTENT "${headers_check_parameters_content}") + + set(sync_headers_dep "${module_target}_sync_headers") + + foreach(header ${hclean_headers}) + # We need realpath here to make sure path starts with drive letter + get_filename_component(input_path "${header}" ${input_header_path_type}) + + get_filename_component(input_file_name ${input_path} NAME) + set(artifact_path "${CMAKE_CURRENT_BINARY_DIR}/header_check/${input_file_name}.o") + + unset(input_base_dir) + if(input_path MATCHES "${CMAKE_BINARY_DIR}") + set(input_base_dir "${CMAKE_BINARY_DIR}") + elseif(input_path MATCHES "${CMAKE_SOURCE_DIR}") + set(input_base_dir "${CMAKE_SOURCE_DIR}") + endif() + + if(input_base_dir AND IS_ABSOLUTE "${input_base_dir}" AND IS_ABSOLUTE "${input_path}") + file(RELATIVE_PATH comment_header_path "${input_base_dir}" "${input_path}") + else() + set(comment_header_path "${input_path}") + endif() + + add_custom_command( + OUTPUT "${artifact_path}" + COMMENT "headersclean: Checking header ${comment_header_path}" + COMMAND ${CMAKE_COMMAND} -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/header_check" + COMMAND ${CMAKE_COMMAND} + -DINPUT_HEADER_FILE=${input_path} + -DOUTPUT_ARTIFACT=${artifact_path} + -DPARAMETERS=${headers_check_parameters} + -P "${QT_CMAKE_DIR}/QtModuleHeadersCheck.cmake" + IMPLICIT_DEPENDS CXX + VERBATIM + COMMAND_EXPAND_LISTS + DEPENDS + ${headers_check_parameters} + ${sync_headers_dep} + ${input_path} + ${header_check_exceptions} + ) + list(APPEND hclean_artifacts "${artifact_path}") + endforeach() + add_custom_target(${module_target}_headersclean_check - COMMENT "headersclean: Checking headers in ${module_include_name}" + COMMENT "headersclean: Checking headers in ${module}" DEPENDS ${hclean_artifacts} VERBATIM) 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/QtInitProject.cmake b/cmake/QtInitProject.cmake new file mode 100644 index 0000000000..a42f59f5c8 --- /dev/null +++ b/cmake/QtInitProject.cmake @@ -0,0 +1,214 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause +cmake_minimum_required(VERSION 3.16) + +if(NOT PROJECT_DIR) + set(PROJECT_DIR "${CMAKE_SOURCE_DIR}") +endif() + +get_filename_component(project_name "${PROJECT_DIR}" NAME) + +get_filename_component(project_abs_dir "${PROJECT_DIR}" ABSOLUTE) +if(NOT IS_DIRECTORY "${project_abs_dir}") + message(FATAL_ERROR "Unable to scan ${project_abs_dir}. The directory doesn't exist.") +endif() + +set(known_extensions "") +set(types "") + +# The function allows extending the capabilities of this script and establishes simple relation +# chains between the file types. +# Option Arguments: +# EXPERIMENTAL +# Marks that the support of the following files is experimental and the required Qt modules +# are in Technical preview state. +# DEPRECATED +# Marks that the support of the following files will be discontinued soon and the required +# Qt modules are deprecated. +# One-value Arguments: +# TEMPLATE +# The CMake code template. Use the '@files@' string for the files substitution. +# Multi-value Arguments: +# EXTENSIONS +# List of the file extensions treated as this source 'type'. +# MODULES +# List of Qt modules required for these file types. +# DEPENDS +# The prerequisite source 'type' needed by this source 'type' +macro(handle_type type) + cmake_parse_arguments(arg + "EXPERIMENTAL;DEPRECATED" + "TEMPLATE" + "EXTENSIONS;MODULES;DEPENDS" + ${ARGN} + ) + + if(NOT arg_EXTENSIONS) + message(FATAL_ERROR "Unexpected call handle_type of with no EXTENSIONS specified." + " This is the Qt issue, please report a bug at https://bugreports.qt.io.") + endif() + set(unique_extensions_subset "${known_extensions}") + list(REMOVE_ITEM unique_extensions_subset ${arg_EXTENSIONS}) + if(NOT "${known_extensions}" STREQUAL "${unique_extensions_subset}") + message(FATAL_ERROR "${type} contains duplicated extensions, this is not supported." + " This is the Qt issue, please report a bug at https://bugreports.qt.io.") + endif() + set(${type}_file_extensions "${arg_EXTENSIONS}") + + if(NOT arg_TEMPLATE) + message(FATAL_ERROR "Unexpected call handle_type of with no TEMPLATE specified." + " This is the Qt issue, please report a bug at https://bugreports.qt.io.") + endif() + set(${type}_template "${arg_TEMPLATE}") + + if(arg_MODULES) + set(${type}_required_modules "${arg_MODULES}") + endif() + + list(APPEND types ${type}) + + if(arg_EXPERIMENTAL) + set(${type}_is_experimental TRUE) + else() + set(${type}_is_experimental FALSE) + endif() + + if(arg_DEPRECATED) + set(${type}_is_deprecated TRUE) + else() + set(${type}_is_deprecated FALSE) + endif() + + if(arg_DEPENDS) + set(${type}_dependencies ${arg_DEPENDS}) + endif() +endmacro() + +handle_type(cpp EXTENSIONS .c .cc .cpp .cxx .h .hh .hxx .hpp MODULES Core TEMPLATE +"\n\nqt_add_executable(${project_name} + @files@ +)" +) + +handle_type(qml EXTENSIONS .qml .js .mjs MODULES Gui Qml Quick TEMPLATE +"\n\nqt_add_qml_module(${project_name} + URI ${project_name} + OUTPUT_DIRECTORY qml + VERSION 1.0 + RESOURCE_PREFIX /qt/qml + QML_FILES + @files@ +)" +) + +handle_type(ui EXTENSIONS .ui MODULES Gui Widgets DEPENDS cpp TEMPLATE +"\n\ntarget_sources(${project_name} + PRIVATE + @files@ +)" +) + +handle_type(qrc EXTENSIONS .qrc DEPENDS cpp TEMPLATE +"\n\nqt_add_resources(${project_name}_resources @files@) +target_sources(${project_name} + PRIVATE + \\\${${project_name}_resources} +)" +) + +handle_type(protobuf EXPERIMENTAL EXTENSIONS .proto MODULES Protobuf Grpc TEMPLATE +"\n\nqt_add_protobuf(${project_name} + GENERATE_PACKAGE_SUBFOLDERS + PROTO_FILES + @files@ +)" +) + +set(extra_packages "") +file(GLOB_RECURSE files RELATIVE "${project_abs_dir}" "${project_abs_dir}/*") +foreach(f IN LISTS files) + get_filename_component(file_extension "${f}" LAST_EXT) + string(TOLOWER "${file_extension}" file_extension) + + foreach(type IN LISTS types) + if(file_extension IN_LIST ${type}_file_extensions) + list(APPEND ${type}_sources "${f}") + list(APPEND packages ${${type}_required_modules}) + if(${type}_is_experimental) + message("We found files with the following extensions in your directory:" + " ${${type}_file_extensions}\n" + "Note that the modules ${${type}_required_modules} are" + " in the technical preview state.") + endif() + if(${type}_is_deprecated) + message("We found files with the following extensions in your directory:" + " ${${type}_file_extensions}\n" + "Note that the modules ${${type}_required_modules} are deprecated.") + endif() + break() + endif() + endforeach() +endforeach() + +if(packages) + list(REMOVE_DUPLICATES packages) + list(JOIN packages " " packages_string) + list(JOIN packages "\n Qt::" deps_string) + set(deps_string "Qt::${deps_string}") +endif() + +set(content +"cmake_minimum_required(VERSION 3.16) +project(${project_name} LANGUAGES CXX) + +find_package(Qt6 REQUIRED COMPONENTS ${packages_string}) +qt_standard_project_setup()" +) + +set(has_useful_sources FALSE) +foreach(type IN LISTS types) + if(${type}_sources) + set(skip FALSE) + foreach(dep IN LISTS ${type}_dependencies) + if(NOT ${dep}_sources) + set(skip TRUE) + message("Sources of type ${${type}_file_extensions} cannot live in the project" + " without ${${dep}_file_extensions} files. Skipping.") + break() + endif() + endforeach() + if(skip) + continue() + endif() + + set(has_useful_sources TRUE) + string(REGEX MATCH "( +)@files@" unused "${${type}_template}") + list(JOIN ${type}_sources "\n${CMAKE_MATCH_1}" ${type}_sources) + string(REPLACE "@files@" "${${type}_sources}" ${type}_content + "${${type}_template}") + string(APPEND content "${${type}_content}") + endif() +endforeach() + +string(APPEND content "\n\ntarget_link_libraries(${project_name} + PRIVATE + ${deps_string} +)\n" +) + +if(EXISTS "${project_abs_dir}/CMakeLists.txt") + message(FATAL_ERROR "Project is already initialized in current directory." + " Please remove CMakeLists.txt if you want to regenerate the project.") +endif() + +if(NOT has_useful_sources) + message(FATAL_ERROR "Could not find any files to generate the project.") +endif() +file(WRITE "${project_abs_dir}/CMakeLists.txt" "${content}") + +message("The project file is successfully generated. To build the project run:" + "\nmkdir build" + "\ncd build" + "\nqt-cmake ${project_abs_dir}" + "\ncmake --build ${project_abs_dir}" +) diff --git a/cmake/QtInstallHelpers.cmake b/cmake/QtInstallHelpers.cmake index 38441d828f..deab48cda5 100644 --- a/cmake/QtInstallHelpers.cmake +++ b/cmake/QtInstallHelpers.cmake @@ -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: BSD-3-Clause # Wraps install() command. In a prefix build, simply passes along arguments to install(). # In a non-prefix build, handles association of targets to export names, and also calls export(). @@ -95,12 +95,27 @@ function(qt_copy_or_install) qt_non_prefix_copy(COPY ${argv_copy} ${copy_arguments}) endfunction() -# Create a versioned hard-link for the given target. +# Create a versioned hard-link for the given target, or a program # E.g. "bin/qmake6" -> "bin/qmake". -# If no hard link can be created, make a copy instead. +# +# One-value Arguments: +# WORKING_DIRECTORY +# The directory where the original file is already placed. +# SUFFIX +# The program file extension, only used for PROGRAMS +# Multi-value Arguments: +# TARGETS +# List of targets for which the versioned link will be created. +# If targets are given, BASE_NAME and SUFFIX will be derived from it. +# PROGRAMS +# List of program file names for which the versioned link will be created. +# +# +# NOTE: This assumes that TARGETS, or PROGRAMS are already installed in the +# WORKING_DIRECTORY. # # In a multi-config build, create the link for the main config only. -function(qt_internal_install_versioned_link install_dir target) +function(qt_internal_install_versioned_link) if(NOT QT_WILL_INSTALL) return() endif() @@ -109,13 +124,41 @@ function(qt_internal_install_versioned_link install_dir target) return() endif() + set(options) + set(oneValueArgs "WORKING_DIRECTORY;SUFFIX") + set(multiValueArgs "TARGETS;PROGRAMS") + cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + if(arg_TARGETS) + foreach(target "${arg_TARGETS}") + _qt_internal_create_versioned_link_or_copy("${arg_WORKING_DIRECTORY}" + $<TARGET_FILE_BASE_NAME:${target}> + $<TARGET_FILE_SUFFIX:${target}>) + endforeach() + endif() + + if(arg_PROGRAMS) + foreach(program "${arg_PROGRAMS}") + _qt_internal_create_versioned_link_or_copy("${arg_WORKING_DIRECTORY}" + "${program}" + "${arg_SUFFIX}") + endforeach() + endif() +endfunction() + +# Generate a script for creating a hard-link between the base_name, and +# base_name${PROJECT_VERSION_MAJOR}. +# +# If no hard link can be created, make a copy instead. +function(_qt_internal_create_versioned_link_or_copy install_dir base_name suffix) qt_path_join(install_base_file_path "$\{qt_full_install_prefix}" - "${install_dir}" "$<TARGET_FILE_BASE_NAME:${target}>") - set(original "${install_base_file_path}$<TARGET_FILE_SUFFIX:${target}>") - set(linkname "${install_base_file_path}${PROJECT_VERSION_MAJOR}$<TARGET_FILE_SUFFIX:${target}>") + "${install_dir}" "${base_name}") + set(original "${install_base_file_path}${suffix}") + set(linkname "${install_base_file_path}${PROJECT_VERSION_MAJOR}${suffix}") set(code "set(qt_full_install_prefix \"$\{CMAKE_INSTALL_PREFIX}\")" " if(NOT \"$ENV\{DESTDIR}\" STREQUAL \"\")" ) + if(CMAKE_HOST_WIN32) list(APPEND code " if(qt_full_install_prefix MATCHES \"^[a-zA-Z]:\")" @@ -140,3 +183,67 @@ function(qt_internal_install_versioned_link install_dir target) list(JOIN code "\n" code) install(CODE "${code}") endfunction() + +# Use case is copying files or directories in a non-prefix build with each build, so that changes +# are available each time, this is useful for some Android templates that are needed for building, +# apks and need to sync changes each time a build is started +function(qt_internal_copy_at_build_time) + set(flags) + set(options TARGET DESTINATION) + set(multiopts FILES DIRECTORIES) + cmake_parse_arguments(arg "${flags}" "${options}" "${multiopts}" ${ARGN}) + + file(MAKE_DIRECTORY "${arg_DESTINATION}") + + unset(outputs) + foreach(dir_to_copy IN LISTS arg_DIRECTORIES) + get_filename_component(file_name "${dir_to_copy}" NAME) + set(destination_file_name "${arg_DESTINATION}/${file_name}") + + file(GLOB_RECURSE all_files_in_dir RELATIVE "${dir_to_copy}" "${dir_to_copy}/*") + set(dir_outputs ${all_files_in_dir}) + set(dir_deps ${all_files_in_dir}) + + list(TRANSFORM dir_outputs PREPEND "${destination_file_name}/") + list(TRANSFORM dir_deps PREPEND "${dir_to_copy}/") + + add_custom_command(OUTPUT ${dir_outputs} + COMMAND ${CMAKE_COMMAND} -E copy_directory ${dir_to_copy} "${destination_file_name}" + DEPENDS ${dir_deps} + COMMENT "Copying directory ${dir_to_copy} to ${arg_DESTINATION}." + ) + list(APPEND outputs ${dir_outputs}) + endforeach() + + unset(file_outputs) + unset(files_to_copy) + foreach(path_to_copy IN LISTS arg_FILES) + get_filename_component(file_name "${path_to_copy}" NAME) + set(destination_file_name "${arg_DESTINATION}/${file_name}") + + list(APPEND file_outputs "${destination_file_name}") + list(APPEND files_to_copy "${path_to_copy}") + endforeach() + + if(files_to_copy) + add_custom_command(OUTPUT ${file_outputs} + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${files_to_copy} ${arg_DESTINATION} + DEPENDS ${files_to_copy} + COMMENT "Copying files ${files_to_copy} to ${arg_DESTINATION}." + ) + list(APPEND outputs ${file_outputs}) + endif() + + get_property(count GLOBAL PROPERTY _qt_internal_copy_at_build_time_count) + if(NOT count) + set(count 0) + endif() + + add_custom_target(qt_internal_copy_at_build_time_${count} DEPENDS ${outputs}) + if(arg_TARGET) + add_dependencies(${arg_TARGET} qt_internal_copy_at_build_time_${count}) + endif() + + math(EXPR count "${count} + 1") + set_property(GLOBAL PROPERTY _qt_internal_copy_at_build_time_count ${count}) +endfunction() 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 9fcdb37b45..d7eadc1a73 100644 --- a/cmake/QtInternalTargets.cmake +++ b/cmake/QtInternalTargets.cmake @@ -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: BSD-3-Clause function(qt_internal_set_warnings_are_errors_flags target target_scope) @@ -48,6 +48,11 @@ function(qt_internal_set_warnings_are_errors_flags target target_scope) if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "11.0.0") # We do mixed enum arithmetic all over the place: list(APPEND flags -Wno-error=deprecated-enum-enum-conversion -Wno-error=deprecated-enum-float-conversion) + + # GCC has some false positive, and it specifically comes through in MINGW + if (MINGW) + list(APPEND flags -Wno-error=stringop-overread) + endif() endif() if (CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "11.0.0" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS "11.2.0") @@ -97,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}" @@ -151,7 +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_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) @@ -162,6 +172,8 @@ if(WIN32) # Needed for M_PI define. Same as mkspecs/features/qt_module.prf. # It's set for every module being built, but it's not propagated to user apps. target_compile_definitions(PlatformModuleInternal INTERFACE _USE_MATH_DEFINES) + # Not disabling min/max macros may result in unintended substitutions of std::min/max + target_compile_definitions(PlatformCommonInternal INTERFACE NOMINMAX) endif() if(FEATURE_largefile AND UNIX) target_compile_definitions(PlatformCommonInternal @@ -199,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 @@ -209,6 +229,31 @@ elseif(UIKIT) target_compile_definitions(PlatformCommonInternal INTERFACE GLES_SILENCE_DEPRECATION) endif() +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) target_compile_definitions(PlatformCommonInternal INTERFACE "_CRT_SECURE_NO_WARNINGS" @@ -221,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 @@ -232,21 +277,20 @@ 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) + 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) + if (MSVC_VERSION GREATER_EQUAL 1919) # MSVC 2019 target_compile_options(PlatformCommonInternal INTERFACE -Zc:externConstexpr + #-Zc:lambda # Buggy. TODO: Enable again when stable enough. + #-Zc:preprocessor # breaks build due to bug in default Windows SDK 10.0.19041 ) endif() @@ -259,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> ) @@ -273,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() @@ -304,30 +382,40 @@ 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>") set(is_static_qt_build "$<NOT:$<BOOL:${QT_BUILD_SHARED_LIBS}>>") - set(is_staitc_lib_during_static_qt_build "$<AND:${is_static_qt_build},${is_static_lib}>") - set(enable_sse2_condition "$<OR:${is_shared_lib},${is_staitc_lib_during_static_qt_build}>") + set(is_static_lib_during_static_qt_build "$<AND:${is_static_qt_build},${is_static_lib}>") + set(enable_sse2_condition "$<OR:${is_shared_lib},${is_static_lib_during_static_qt_build}>") set(${out_var} "${enable_sse2_condition}" PARENT_SCOPE) endfunction() diff --git a/cmake/QtJavaHelpers.cmake b/cmake/QtJavaHelpers.cmake index 48f056b230..ec9b611c5e 100644 --- a/cmake/QtJavaHelpers.cmake +++ b/cmake/QtJavaHelpers.cmake @@ -1,9 +1,13 @@ # 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: BSD-3-Clause # 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/QtLalrHelpers.cmake b/cmake/QtLalrHelpers.cmake index f67bbc489f..a63d8e9504 100644 --- a/cmake/QtLalrHelpers.cmake +++ b/cmake/QtLalrHelpers.cmake @@ -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: BSD-3-Clause # Match the pattern 'regex' in 'input_line', replace the match with 'replacement' # and set that result in 'out_var' in the parent scope. @@ -35,6 +35,15 @@ function(qt_process_qlalr consuming_target input_file_list flags) return() endif() + qt_internal_is_skipped_test(skipped ${consuming_target}) + if(skipped) + return() + endif() + qt_internal_is_in_test_batch(in_batch ${consuming_target}) + if(in_batch) + _qt_internal_test_batch_target_name(consuming_target) + endif() + foreach(input_file ${input_file_list}) file(STRINGS ${input_file} input_file_lines) qt_qlalr_find_option_in_list("${input_file_lines}" "^%parser(.+)" "parser") 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 8ea763d86e..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@) @@ -27,9 +30,8 @@ endif() if (NOT QT_NO_CREATE_TARGETS AND @INSTALL_CMAKE_NAMESPACE@@target@_FOUND) 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") - endif() + include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@ExtraProperties.cmake" + OPTIONAL) # DEPRECATED # Provide old style variables for includes, compile definitions, etc. @@ -72,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) @@ -89,12 +101,12 @@ if (NOT QT_NO_CREATE_TARGETS AND @INSTALL_CMAKE_NAMESPACE@@target@_FOUND) endif() if (TARGET @QT_CMAKE_EXPORT_NAMESPACE@::@target@) + qt_make_features_available(@QT_CMAKE_EXPORT_NAMESPACE@::@target@) + foreach(extra_cmake_include @extra_cmake_includes@) include("${CMAKE_CURRENT_LIST_DIR}/${extra_cmake_include}") endforeach() - qt_make_features_available(@QT_CMAKE_EXPORT_NAMESPACE@::@target@) - if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@Plugins.cmake") include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@Plugins.cmake") endif() @@ -116,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..ba5922d1e2 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() diff --git a/cmake/QtModuleHeadersCheck.cmake b/cmake/QtModuleHeadersCheck.cmake new file mode 100644 index 0000000000..39053f3e10 --- /dev/null +++ b/cmake/QtModuleHeadersCheck.cmake @@ -0,0 +1,34 @@ +cmake_minimum_required(VERSION 3.16) +# The PARAMETERS file should specify the following variables for the correct work of +# this script: +# HEADER_CHECK_EXCEPTIONS - path to file that contains exceptions. +# The file is created by syncqt. +# +# HEADER_CHECK_COMPILER_COMMAND_LINE - compiler command line +include("${PARAMETERS}") + +if(EXISTS ${HEADER_CHECK_EXCEPTIONS}) + file(READ ${HEADER_CHECK_EXCEPTIONS} header_check_exception_list) +endif() + +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) + file(WRITE "${OUTPUT_ARTIFACT}" "skipped") + return() + endif() +endforeach() + +execute_process(COMMAND ${HEADER_CHECK_COMPILER_COMMAND_LINE} + RESULT_VARIABLE result + OUTPUT_VARIABLE output + ERROR_VARIABLE output +) + +if(NOT result EQUAL 0) + message(FATAL_ERROR "${INPUT_HEADER_FILE} header check" + " failed: ${HEADER_CHECK_COMPILER_COMMAND_LINE}\n" + " ${output}") +endif() diff --git a/cmake/QtModuleHelpers.cmake b/cmake/QtModuleHelpers.cmake index ac6fd8a7f1..ba03173073 100644 --- a/cmake/QtModuleHelpers.cmake +++ b/cmake/QtModuleHelpers.cmake @@ -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: BSD-3-Clause macro(qt_internal_get_internal_add_module_keywords option_args single_args multi_args) set(${option_args} @@ -15,9 +15,10 @@ macro(qt_internal_get_internal_add_module_keywords option_args single_args multi NO_CONFIG_HEADER_FILE NO_ADDITIONAL_TARGET_INFO NO_GENERATE_METATYPES - GENERATE_CPP_EXPORTS # TODO: Rename to NO_GENERATE_CPP_EXPORTS once migration is done - GENERATE_METATYPES # TODO: Remove once it is not used anymore - GENERATE_PRIVATE_CPP_EXPORTS + NO_HEADERSCLEAN_CHECK + GENERATE_CPP_EXPORTS # deprecated + NO_GENERATE_CPP_EXPORTS + NO_UNITY_BUILD ) set(${single_args} MODULE_INCLUDE_NAME @@ -27,20 +28,43 @@ macro(qt_internal_get_internal_add_module_keywords option_args single_args multi CONFIGURE_FILE_PATH CPP_EXPORT_HEADER_BASE_NAME EXTERNAL_HEADERS_DIR + PRIVATE_HEADER_FILTERS + QPA_HEADER_FILTERS + RHI_HEADER_FILTERS + SSG_HEADER_FILTERS + HEADER_SYNC_SOURCE_DIRECTORY ${__default_target_info_args} ) set(${multi_args} QMAKE_MODULE_CONFIG EXTRA_CMAKE_FILES EXTRA_CMAKE_INCLUDES - NO_PCH_SOURCES EXTERNAL_HEADERS + POLICIES ${__default_private_args} ${__default_public_args} ${__default_private_module_args} ) endmacro() +# The function helps to wrap module include paths with the header existence check. +function(qt_internal_append_include_directories_with_headers_check target list_to_append type) + string(TOLOWER "${type}" type) + string(JOIN "" has_headers_check + "$<BOOL:" + "$<TARGET_PROPERTY:" + "$<TARGET_NAME:${target}>," + "_qt_module_has_${type}_headers" + ">" + ">" + ) + foreach(directory IN LISTS ARGN) + list(APPEND ${list_to_append} + "$<${has_headers_check}:${directory}>") + endforeach() + set(${list_to_append} "${${list_to_append}}" PARENT_SCOPE) +endfunction() + # This is the main entry function for creating a Qt module, that typically # consists of a library, public header files, private header files and configurable # features. @@ -83,7 +107,28 @@ endmacro() # A module directory with non qt headers (like 3rdparty) to be installed. # Note this option overrides install headers used as PUBLIC_HEADER by cmake install(TARGET) # otherwise set by syncqt. - +# +# PRIVATE_HEADER_FILTERS +# The regular expressions that filter private header files out of target sources. +# The value must use the following format 'regex1|regex2|regex3'. +# +# QPA_HEADER_FILTERS +# The regular expressions that filter QPA header files out of target sources. +# The value must use the following format 'regex1|regex2|regex3'. +# +# RHI_HEADER_FILTERS +# 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 +# CMAKE_CURRENT_SOURCE_DIR for the header files that needs to be synced and only parse the +# single subdirectory, that meanwhile can be outside the CMAKE_CURRENT_SOURCE_DIR tree. function(qt_internal_add_module target) qt_internal_get_internal_add_module_keywords( module_option_args @@ -91,12 +136,12 @@ function(qt_internal_add_module target) module_multi_args ) - qt_parse_all_arguments(arg "qt_internal_add_module" + cmake_parse_arguments(PARSE_ARGV 1 arg "${module_option_args}" "${module_single_args}" "${module_multi_args}" - ${ARGN} ) + _qt_internal_validate_all_args_are_parsed(arg) set(is_internal_module FALSE) if(arg_INTERNAL_MODULE) @@ -198,6 +243,13 @@ function(qt_internal_add_module target) set(module_config_header "qt${arg_CONFIG_MODULE_NAME}-config.h") set(module_config_private_header "qt${arg_CONFIG_MODULE_NAME}-config_p.h") + # qt<module>-config.h/-config_p.h header files are not marked as GENERATED automatically + # for old CMake versions. Set the property explicitly here. + set_source_files_properties("${module_config_header}" "${module_config_private_header}" + PROPERTIES + GENERATED TRUE + SKIP_AUTOGEN TRUE + ) # Module define needs to take into account the config module name. string(TOUPPER "${arg_CONFIG_MODULE_NAME}" module_define_infix) @@ -284,14 +336,24 @@ function(qt_internal_add_module target) EXPORT_PROPERTIES "${export_properties}") endif() + # FIXME: This workaround is needed because the deployment logic + # for iOS and WASM just copies/embeds the directly linked library, + # which will just be a versioned symlink to the actual library. + if((UIKIT OR WASM) AND BUILD_SHARED_LIBS) + set(version_args "") + else() + set(version_args + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR}) + endif() + if(NOT arg_HEADER_MODULE) set_target_properties(${target} PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_LIBDIR}" RUNTIME_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_BINDIR}" ARCHIVE_OUTPUT_DIRECTORY "${QT_BUILD_DIR}/${INSTALL_LIBDIR}" - VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION_MAJOR} - ) + ${version_args} + ) qt_set_target_info_properties(${target} ${ARGN}) qt_handle_multi_config_output_dirs("${target}") @@ -348,84 +410,86 @@ function(qt_internal_add_module target) else() set_property(TARGET ${target} APPEND PROPERTY EXPORT_PROPERTIES _qt_module_include_name) set_target_properties("${target}" PROPERTIES - _qt_module_include_name "${module_include_name}") - - # Use QT_BUILD_DIR for the syncqt call. - # So we either write the generated files into the qtbase non-prefix build root, or the - # module specific build root. - qt_ensure_sync_qt() - set(syncqt_full_command "${HOST_PERL}" -w "${QT_SYNCQT}" - -quiet - -check-includes - -module "${module_include_name}" - -version "${PROJECT_VERSION}" - -outdir "${QT_BUILD_DIR}" - -builddir "${PROJECT_BINARY_DIR}" - "${PROJECT_SOURCE_DIR}") - message(STATUS "Running syncqt for module: '${module_include_name}' ") - execute_process(COMMAND ${syncqt_full_command} RESULT_VARIABLE syncqt_ret) - if(NOT syncqt_ret EQUAL 0) - message(FATAL_ERROR "Failed to run syncqt, return code: ${syncqt_ret}") - endif() - - set_target_properties("${target}" PROPERTIES - _qt_module_has_headers ON) - - ### FIXME: Can we replace headers.pri? - qt_read_headers_pri("${module_build_interface_include_dir}" "module_headers") + _qt_module_include_name "${module_include_name}" + _qt_module_has_headers ON + ) - if(arg_EXTERNAL_HEADERS) - set(module_headers_public ${arg_EXTERNAL_HEADERS}) + set(sync_source_directory "${CMAKE_CURRENT_SOURCE_DIR}") + if(arg_HEADER_SYNC_SOURCE_DIRECTORY) + set(sync_source_directory "${arg_HEADER_SYNC_SOURCE_DIRECTORY}") endif() - - set_property(TARGET ${target} APPEND PROPERTY - _qt_module_timestamp_dependencies "${module_headers_public}") - + set_target_properties(${target} PROPERTIES + _qt_sync_source_directory "${sync_source_directory}") # 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() set(module_depends_header "${module_build_interface_include_dir}/${module_include_name}Depends") - if(is_framework) - if(NOT is_interface_lib) - qt_copy_framework_headers(${target} - PUBLIC "${module_headers_public};${module_depends_header}" - PRIVATE "${module_headers_private}" - QPA "${module_headers_qpa}" - ) - endif() - else() - set_property(TARGET ${target} APPEND PROPERTY PUBLIC_HEADER "${module_headers_public}") - set_property(TARGET ${target} APPEND PROPERTY PUBLIC_HEADER ${module_depends_header}) - set_property(TARGET ${target} APPEND PROPERTY PRIVATE_HEADER "${module_headers_private}") - endif() - if (NOT ${arg_HEADER_MODULE}) + set_source_files_properties("${module_depends_header}" PROPERTIES GENERATED TRUE) + set_target_properties(${target} PROPERTIES _qt_module_depends_header + "${module_depends_header}") + if(NOT ${arg_HEADER_MODULE}) + set(module_header "${module_build_interface_include_dir}/${module_include_name}") set_property(TARGET "${target}" PROPERTY MODULE_HEADER - "${module_build_interface_include_dir}/${module_include_name}") + "${module_header}") endif() - if(module_headers_qpa) - qt_install( - FILES ${module_headers_qpa} - DESTINATION "${module_install_interface_qpa_include_dir}") + set(qpa_filter_regex "") + if(arg_QPA_HEADER_FILTERS) + set(qpa_filter_regex "${arg_QPA_HEADER_FILTERS}") + endif() + set_target_properties(${target} + PROPERTIES _qt_module_qpa_headers_filter_regex "${qpa_filter_regex}") + + set(rhi_filter_regex "") + if(arg_RHI_HEADER_FILTERS) + set(rhi_filter_regex "${arg_RHI_HEADER_FILTERS}") + endif() + 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}") + endif() + set_target_properties(${target} + PROPERTIES _qt_module_private_headers_filter_regex "${private_filter_regex}") + + # 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}" + ) endif() endif() + if(arg_NO_HEADERSCLEAN_CHECK OR arg_NO_MODULE_HEADERS OR arg_NO_SYNC_QT + OR NOT INPUT_headersclean) + set_target_properties("${target}" PROPERTIES _qt_no_headersclean_check ON) + endif() + if(NOT arg_HEADER_MODULE) # Plugin types associated to a module if(NOT "x${arg_PLUGIN_TYPES}" STREQUAL "x") @@ -461,15 +525,15 @@ function(qt_internal_add_module target) # Make sure to create such paths for both the the BUILD_INTERFACE and the INSTALL_INTERFACE. # # Only add syncqt headers if they exist. - # This handles cases like QmlDevToolsPrivate which do not have their own headers, but borrow them - # from another module. + # This handles cases like QmlDevToolsPrivate which do not have their own headers, but borrow + # them from another module. if(NOT arg_NO_SYNC_QT AND NOT arg_NO_MODULE_HEADERS) # Don't include private headers unless they exist, aka syncqt created them. - if(module_headers_private) - list(APPEND private_includes - "$<BUILD_INTERFACE:${module_build_interface_versioned_include_dir}>" - "$<BUILD_INTERFACE:${module_build_interface_versioned_inner_include_dir}>") - endif() + qt_internal_append_include_directories_with_headers_check(${target} + private_includes PRIVATE + "$<BUILD_INTERFACE:${module_build_interface_versioned_include_dir}>" + "$<BUILD_INTERFACE:${module_build_interface_versioned_inner_include_dir}>" + ) list(APPEND public_includes # For the syncqt headers @@ -514,23 +578,12 @@ function(qt_internal_add_module target) endif() list(APPEND ${public_headers_list} ${arg_PUBLIC_INCLUDE_DIRECTORIES}) - if(arg_HEADER_MODULE) - # Provide a *_timestamp target that can be used to trigger the build of custom_commands. - set(timestamp_file "${CMAKE_CURRENT_BINARY_DIR}/timestamp") - add_custom_command(OUTPUT "${timestamp_file}" - COMMAND ${CMAKE_COMMAND} -E touch "${timestamp_file}" - DEPENDS "$<TARGET_PROPERTY:${target},_qt_module_timestamp_dependencies>" - VERBATIM) - add_custom_target(${target}_pri_dep_timestamp ALL DEPENDS "${timestamp_file}") - endif() - set(defines_for_extend_target "") if(NOT arg_HEADER_MODULE) 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 @@ -541,10 +594,27 @@ function(qt_internal_add_module target) qt_internal_add_repo_local_defines("${target}") + if(arg_NO_UNITY_BUILD) + set(arg_NO_UNITY_BUILD "NO_UNITY_BUILD") + else() + set(arg_NO_UNITY_BUILD "") + endif() + + if(NOT arg_EXTERNAL_HEADERS) + set(arg_EXTERNAL_HEADERS "") + endif() + qt_internal_extend_target("${target}" - SOURCES ${arg_SOURCES} + ${arg_NO_UNITY_BUILD} + SOURCES + ${arg_SOURCES} + ${arg_EXTERNAL_HEADERS} + NO_UNITY_BUILD_SOURCES + ${arg_NO_UNITY_BUILD_SOURCES} INCLUDE_DIRECTORIES ${private_includes} + SYSTEM_INCLUDE_DIRECTORIES + ${arg_SYSTEM_INCLUDE_DIRECTORIES} PUBLIC_INCLUDE_DIRECTORIES ${public_includes} PUBLIC_DEFINES @@ -597,39 +667,13 @@ function(qt_internal_add_module target) include(${configureFile}) qt_feature_module_end("${target}") - set_property(TARGET "${target}" APPEND PROPERTY - PUBLIC_HEADER "${CMAKE_CURRENT_BINARY_DIR}/${module_config_header}" - ) - set_property(TARGET "${target}" APPEND PROPERTY - PRIVATE_HEADER "${CMAKE_CURRENT_BINARY_DIR}/${module_config_private_header}" + qt_internal_extend_target("${target}" + SOURCES + "${CMAKE_CURRENT_BINARY_DIR}/${module_config_header}" + "${CMAKE_CURRENT_BINARY_DIR}/${module_config_private_header}" ) endif() - if(NOT arg_HEADER_MODULE) - if(DEFINED module_headers_private) - qt_internal_add_linker_version_script("${target}" PRIVATE_HEADERS ${module_headers_private} ${module_headers_qpa}) - else() - qt_internal_add_linker_version_script("${target}") - endif() - endif() - - # Handle injections. Aka create forwarding headers for certain headers that have been - # automatically generated in the build dir (for example qconfig.h, qtcore-config.h, - # qvulkanfunctions.h, etc) - # module_headers_injections come from the qt_read_headers_pri() call. - # extra_library_injections come from the qt_feature_module_end() call. - set(final_injections "") - if(module_headers_injections) - string(APPEND final_injections "${module_headers_injections} ") - endif() - if(extra_library_injections) - string(APPEND final_injections "${extra_library_injections} ") - endif() - - if(final_injections) - qt_install_injections(${target} "${QT_BUILD_DIR}" "${QT_INSTALL_DIR}" ${final_injections}) - endif() - # Handle creation of cmake files for consumers of find_package(). set(path_suffix "${INSTALL_CMAKE_NAMESPACE}${target}") qt_path_join(config_build_dir ${QT_CONFIG_BUILD_DIR} ${path_suffix}) @@ -641,8 +685,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) @@ -654,6 +711,11 @@ set(QT_VISIBILITY_AVAILABLE TRUE)") set(QT_LIBINFIX \"${QT_LIBINFIX}\")") endif() + # Store whether find_package(Qt6Foo) should succeed if Qt6FooTools is missing. + if(QT_ALLOW_MISSING_TOOLS_PACKAGES) + string(APPEND qtcore_extra_cmake_code " +set(QT_ALLOW_MISSING_TOOLS_PACKAGES TRUE)") + endif() endif() configure_file("${CMAKE_CURRENT_LIST_DIR}/${INSTALL_CMAKE_NAMESPACE}${target}ConfigExtras.cmake.in" @@ -667,29 +729,40 @@ set(QT_LIBINFIX \"${QT_LIBINFIX}\")") 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}) set(extra_cmake_code "") - # Generate metatypes - if(${arg_GENERATE_METATYPES}) - # No mention of NO_GENERATE_METATYPES. You should not use it. - message(WARNING "GENERATE_METATYPES is on by default for Qt modules. Please remove the manual specification.") + if(arg_POLICIES) + set(policies "") + foreach(policy IN LISTS arg_POLICIES) + list(APPEND policies "set(QT_KNOWN_POLICY_${policy} TRUE)") + + # When building Qt, tests and examples might expect a policy to be known, but they + # won't be known depending on which scope or when a find_package(Module) with the + # respective policy is called. Check the global list of known policies to accommodate + # that. + set_property(GLOBAL APPEND PROPERTY _qt_global_known_policies "${policy}") + endforeach() + list(JOIN policies "\n" policies_str) + string(APPEND extra_cmake_code "${policies_str}\n") endif() - if (NOT ${arg_NO_GENERATE_METATYPES}) - if (NOT target_type STREQUAL "INTERFACE_LIBRARY") - set(args "") - if(QT_WILL_INSTALL) - set(metatypes_install_dir "${INSTALL_LIBDIR}/metatypes") - list(APPEND args - __QT_INTERNAL_INSTALL __QT_INTERNAL_INSTALL_DIR "${metatypes_install_dir}") - endif() - qt6_extract_metatypes(${target} ${args}) - elseif(${arg_GENERATE_METATYPES}) - message(FATAL_ERROR "Meta types generation does not work on interface libraries") + + # Generate metatypes + if (NOT ${arg_NO_GENERATE_METATYPES} AND NOT target_type STREQUAL "INTERFACE_LIBRARY") + set(args "") + if(QT_WILL_INSTALL) + set(metatypes_install_dir "${INSTALL_ARCHDATADIR}/metatypes") + list(APPEND args + __QT_INTERNAL_INSTALL __QT_INTERNAL_INSTALL_DIR "${metatypes_install_dir}") endif() + qt6_extract_metatypes(${target} ${args}) endif() + qt_internal_get_min_new_policy_cmake_version(min_new_policy_version) qt_internal_get_max_new_policy_cmake_version(max_new_policy_version) configure_package_config_file( @@ -729,14 +802,6 @@ set(QT_LIBINFIX \"${QT_LIBINFIX}\")") list(APPEND exported_targets ${target_private}) endif() set(export_name "${INSTALL_CMAKE_NAMESPACE}${target}Targets") - if(arg_EXTERNAL_HEADERS_DIR) - qt_install(DIRECTORY "${arg_EXTERNAL_HEADERS_DIR}/" - DESTINATION "${module_install_interface_include_dir}" - ) - unset(public_header_destination) - else() - set(public_header_destination PUBLIC_HEADER DESTINATION "${module_install_interface_include_dir}") - endif() qt_install(TARGETS ${exported_targets} EXPORT ${export_name} @@ -744,9 +809,7 @@ set(QT_LIBINFIX \"${QT_LIBINFIX}\")") LIBRARY DESTINATION ${INSTALL_LIBDIR} ARCHIVE DESTINATION ${INSTALL_LIBDIR} FRAMEWORK DESTINATION ${INSTALL_LIBDIR} - PRIVATE_HEADER DESTINATION "${module_install_interface_private_include_dir}" - ${public_header_destination} - ) + ) if(BUILD_SHARED_LIBS) qt_apply_rpaths(TARGET "${target}" INSTALL_PATH "${INSTALL_LIBDIR}" RELATIVE_RPATH) @@ -774,7 +837,14 @@ set(QT_LIBINFIX \"${QT_LIBINFIX}\")") 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} + CONFIG_INSTALL_DIR "${config_install_dir}" + ) ### fixme: cmake is missing a built-in variable for this. We want to apply it only to modules and plugins # that belong to Qt. @@ -786,39 +856,36 @@ set(QT_LIBINFIX \"${QT_LIBINFIX}\")") # Handle cases like QmlDevToolsPrivate which do not have their own headers, but rather borrow them # from another module. - if(NOT arg_NO_SYNC_QT) + 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 # INTERFACE_INCLUDE_DIRECTORIES contains a non-existent path. - if(NOT arg_NO_MODULE_HEADERS - AND EXISTS "${module_build_interface_versioned_inner_include_dir}") - list(APPEND interface_includes - "$<BUILD_INTERFACE:${module_build_interface_versioned_include_dir}>" - "$<BUILD_INTERFACE:${module_build_interface_versioned_inner_include_dir}>") - - if(is_framework) - set(fw_install_private_header_dir "${INSTALL_LIBDIR}/${fw_private_header_dir}") - set(fw_install_private_module_header_dir "${INSTALL_LIBDIR}/${fw_private_module_header_dir}") - list(APPEND interface_includes - "$<INSTALL_INTERFACE:${fw_install_private_header_dir}>" - "$<INSTALL_INTERFACE:${fw_install_private_module_header_dir}>") - else() - list(APPEND interface_includes - "$<INSTALL_INTERFACE:${module_install_interface_versioned_include_dir}>" - "$<INSTALL_INTERFACE:${module_install_interface_versioned_inner_include_dir}>") - endif() - endif() - endif() + qt_internal_append_include_directories_with_headers_check(${target} + interface_includes PRIVATE + "$<BUILD_INTERFACE:${module_build_interface_versioned_include_dir}>" + "$<BUILD_INTERFACE:${module_build_interface_versioned_inner_include_dir}>" + ) - if(QT_FEATURE_headersclean AND NOT arg_NO_MODULE_HEADERS) - qt_internal_add_headersclean_target( - ${target} - "${module_include_name}" - "${module_headers_clean}") + if(is_framework) + set(fw_install_private_header_dir "${INSTALL_LIBDIR}/${fw_private_header_dir}") + set(fw_install_private_module_header_dir + "${INSTALL_LIBDIR}/${fw_private_module_header_dir}") + qt_internal_append_include_directories_with_headers_check(${target} + interface_includes PRIVATE + "$<INSTALL_INTERFACE:${fw_install_private_header_dir}>" + "$<INSTALL_INTERFACE:${fw_install_private_module_header_dir}>" + ) + else() + qt_internal_append_include_directories_with_headers_check(${target} + interface_includes PRIVATE + "$<INSTALL_INTERFACE:${module_install_interface_versioned_include_dir}>" + "$<INSTALL_INTERFACE:${module_install_interface_versioned_inner_include_dir}>" + ) + endif() endif() if(arg_INTERNAL_MODULE) @@ -849,11 +916,46 @@ set(QT_LIBINFIX \"${QT_LIBINFIX}\")") 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) + 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}) + + # qt_internal_install_module_headers needs to be called before + # qt_finalize_framework_headers_copy, because the last uses the QT_COPIED_FRAMEWORK_HEADERS + # property which supposed to be updated inside every qt_internal_install_module_headers + # call. + qt_internal_add_headersclean_target(${target} "${module_headers_public}") + qt_internal_target_sync_headers(${target} "${module_headers_all}" + "${module_headers_generated}") + get_target_property(module_depends_header ${target} _qt_module_depends_header) + qt_internal_install_module_headers(${target} + PUBLIC ${module_headers_public} "${module_depends_header}" + 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. @@ -880,6 +982,8 @@ endfunction() # * foo_versioned_inner_include_dir with the value "QtCore/6.2.0/QtCore" # * 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. # @@ -902,6 +1006,12 @@ endfunction() # * foo_<build|install>_qpa_include_dir with # qtbase_build_dir/include/QtCore/6.2.0/QtCore/qpa for build interface and # include/QtCore/6.2.0/QtCore/qpa for install interface. +# * 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 @@ -936,6 +1046,10 @@ the different base name for the module info variables.") "${${result}_versioned_inner_include_dir}/private") set("${result}_qpa_include_dir" "${${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") @@ -949,6 +1063,10 @@ the different base name for the module info variables.") "${repo_build_interface_include_dir}/${${result}_private_include_dir}") set("${result}_build_interface_qpa_include_dir" "${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}") @@ -962,6 +1080,10 @@ the different base name for the module info variables.") "${repo_install_interface_include_dir}/${${result}_private_include_dir}") set("${result}_install_interface_qpa_include_dir" "${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) @@ -975,6 +1097,8 @@ the different base name for the module info variables.") "${${result}_versioned_inner_include_dir}" PARENT_SCOPE) 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 @@ -989,6 +1113,10 @@ the different base name for the module info variables.") "${${result}_build_interface_private_include_dir}" PARENT_SCOPE) set("${result}_build_interface_qpa_include_dir" "${${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) @@ -1002,6 +1130,21 @@ the different base name for the module info variables.") "${${result}_install_interface_private_include_dir}" PARENT_SCOPE) set("${result}_install_interface_qpa_include_dir" "${${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) + set(result "") + foreach(item IN LISTS ${list_var}) + if(NOT "${result}" STREQUAL "") + string(APPEND result ", ") + endif() + string(APPEND result "\"${item}\"") + endforeach() + set("${out_var}" "[${result}]" PARENT_SCOPE) endfunction() # Generate a module description file based on the template in ModuleDescription.json.in @@ -1012,10 +1155,41 @@ function(qt_describe_module target) set(descfile_in "${QT_CMAKE_DIR}/ModuleDescription.json.in") set(descfile_out "${build_dir}/${target}.json") + string(TOLOWER "${PROJECT_NAME}" lower_case_project_name) set(cross_compilation "false") if(CMAKE_CROSSCOMPILING) set(cross_compilation "true") endif() + set(extra_module_information "") + + get_target_property(target_type ${target} TYPE) + if(NOT target_type STREQUAL "INTERFACE_LIBRARY") + get_target_property(plugin_types ${target} QMAKE_MODULE_PLUGIN_TYPES) + if(plugin_types) + qt_internal_list_to_json_array(plugin_types plugin_types) + string(APPEND extra_module_information "\n \"plugin_types\": ${plugin_types},") + endif() + endif() + + get_target_property(is_internal ${target} _qt_is_internal_module) + if(is_internal) + string(APPEND extra_module_information "\n \"internal\": true,") + endif() + + set(extra_build_information "") + if(NOT QT_NAMESPACE STREQUAL "") + string(APPEND extra_build_information " + \"namespace\": \"${QT_NAMESPACE}\",") + endif() + if(ANDROID) + string(APPEND extra_build_information " + \"android\": { + \"api_version\": \"${QT_ANDROID_API_VERSION}\", + \"ndk\": { + \"version\": \"${ANDROID_NDK_REVISION}\" + } + },") + endif() configure_file("${descfile_in}" "${descfile_out}") qt_install(FILES "${descfile_out}" DESTINATION "${install_dir}") @@ -1023,7 +1197,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} ) @@ -1047,49 +1221,190 @@ 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) +endfunction() - if(arg_GENERATE_PRIVATE_CPP_EXPORTS) - set(generated_private_header_path - "${module_build_interface_private_include_dir}/${header_base_name}_p.h" - ) +function(qt_internal_install_module_headers target) + set(options) + set(one_value_args) + set(multi_value_args PUBLIC PRIVATE QPA RHI SSG) + cmake_parse_arguments(arg "${options}" "${one_value_args}" "${multi_value_args}" ${ARGN}) - configure_file("${QT_CMAKE_DIR}/modulecppexports_p.h.in" - "${generated_private_header_path}" @ONLY - ) + qt_internal_module_info(module ${target}) - set(${out_private_header} "${generated_private_header_path}" PARENT_SCOPE) - target_sources(${target} PRIVATE "${generated_private_header_path}") + get_target_property(target_type ${target} TYPE) + set(is_interface_lib FALSE) + if(target_type STREQUAL "INTERFACE_LIBRARY") + set(is_interface_lib TRUE) + else() + get_target_property(is_framework ${target} FRAMEWORK) endif() - get_target_property(is_framework ${target} FRAMEWORK) + + foreach(header_type IN LISTS multi_value_args) + if(NOT arg_${header_type}) + set(arg_${header_type} "") + endif() + endforeach() + + if(is_framework) + qt_copy_framework_headers(${target} + PUBLIC ${arg_PUBLIC} + PRIVATE ${arg_PRIVATE} + QPA ${arg_QPA} + RHI ${arg_RHI} + SSG ${arg_SSG} + ) + else() + if(arg_PUBLIC) + qt_install(FILES ${arg_PUBLIC} + DESTINATION "${module_install_interface_include_dir}") + endif() + if(arg_PRIVATE) + qt_install(FILES ${arg_PRIVATE} + DESTINATION "${module_install_interface_private_include_dir}") + endif() + if(arg_QPA) + qt_install(FILES ${arg_QPA} DESTINATION "${module_install_interface_qpa_include_dir}") + endif() + 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() + +function(qt_internal_collect_module_headers out_var target) + set(${out_var}_public "") + 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}) get_target_property(target_type ${target} TYPE) - set(is_interface_lib 0) if(target_type STREQUAL "INTERFACE_LIBRARY") - set(is_interface_lib 1) + 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) + + get_target_property(public_filter ${target} _qt_module_public_headers_filter_regex) + 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) + get_filename_component(file_name "${file_path}" NAME) + if(NOT file_name MATCHES ".+\\.h$") + continue() + endif() - set_property(TARGET ${target} APPEND PROPERTY - _qt_module_timestamp_dependencies "${generated_header_path}") + get_source_file_property(non_module_header ${file_path} _qt_non_module_header) + if(non_module_header) + continue() + endif() - if(is_framework) - if(NOT is_interface_lib) - qt_copy_framework_headers(${target} PUBLIC "${generated_header_path}") + get_filename_component(file_path "${file_path}" ABSOLUTE) - if(arg_GENERATE_PRIVATE_CPP_EXPORTS) - qt_copy_framework_headers(${target} PRIVATE "${generated_private_header_path}") - endif() + string(FIND "${file_path}" "${source_dir}" source_dir_pos) + if(source_dir_pos EQUAL 0) + set(is_outside_module_source_dir FALSE) + else() + set(is_outside_module_source_dir TRUE) endif() - else() - set_property(TARGET ${target} APPEND PROPERTY PUBLIC_HEADER "${generated_header_path}") - qt_install(FILES "${generated_header_path}" - DESTINATION "${module_install_interface_include_dir}") - - if(arg_GENERATE_PRIVATE_CPP_EXPORTS) - set_property(TARGET ${target} APPEND PROPERTY PRIVATE_HEADER - "${generated_private_header_path}") - qt_install(FILES "${generated_private_header_path}" - DESTINATION "${module_install_interface_private_include_dir}") + + get_source_file_property(is_generated "${file_path}" GENERATED) + # Skip all header files outside the module source directory, except the generated files. + if(is_outside_module_source_dir AND NOT is_generated) + continue() endif() + + get_source_file_property(condition ${file_path} _qt_extend_target_condition) + if(NOT condition STREQUAL "" AND NOT condition STREQUAL "NOTFOUND") + list(JOIN condition " " condition_string) + string(APPEND condition_independent_headers_warning + "\nFile:\n ${file_path}" + "\nCondition:\n ${condition_string}") + endif() + + 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) + endif() + list(APPEND ${out_var}_all "${file_path}") + if(qpa_filter AND file_name MATCHES "${qpa_filter}") + 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}") + AND NOT is_3rdparty_header) + list(APPEND ${out_var}_public "${file_path}") + endif() + if(is_generated) + list(APPEND ${out_var}_generated "${file_path}") + endif() + endforeach() + + if(NOT condition_independent_headers_warning STREQUAL "" AND QT_FEATURE_developer_build) + message(AUTHOR_WARNING "Condition is ignored when adding the following header file(s) to" + " the ${target} module:" + "${condition_independent_headers_warning}" + "\nThe usage of the file(s) is not properly isolated in this or other modules according" + " to the condition. This warning is for the Qt maintainers. Please make sure that file" + " include(s) are guarded with the appropriate macros in the Qt code. If files should be" + " added to the module unconditionally, please move them to the common SOURCES section" + " in the qt_internal_add_module call.") + endif() + + + 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) + if(${out_var}_${header_type}) + list(APPEND has_header_types_properties + _qt_module_has_${header_type}_headers TRUE) + endif() + + set(${out_var}_${header_type} "${${out_var}_${header_type}}" PARENT_SCOPE) + endforeach() + set(${out_var}_all "${${out_var}_all}" PARENT_SCOPE) + set(${out_var}_generated "${${out_var}_generated}" PARENT_SCOPE) + + if(has_header_types_properties) + set_target_properties(${target} PROPERTIES ${has_header_types_properties}) endif() + set_property(TARGET ${target} APPEND PROPERTY + EXPORT_PROPERTIES + _qt_module_has_public_headers + _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 fdbc853646..ec447aa55b 100644 --- a/cmake/QtModuleToolsConfig.cmake.in +++ b/cmake/QtModuleToolsConfig.cmake.in @@ -1,15 +1,24 @@ +# 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(CMakeFindDependencyMacro) -if (NOT QT_NO_CREATE_TARGETS) - # Find required dependencies, if any. - if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@Dependencies.cmake") - include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@Dependencies.cmake") - endif() +# Find required dependencies, if any. +if(EXISTS "${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@Dependencies.cmake") + include("${CMAKE_CURRENT_LIST_DIR}/@INSTALL_CMAKE_NAMESPACE@@target@Dependencies.cmake") +endif() + +# If *Dependencies.cmake exists, the variable value will be defined there. +# Don't override it in that case. +if(NOT DEFINED "@INSTALL_CMAKE_NAMESPACE@@target@_FOUND") + set("@INSTALL_CMAKE_NAMESPACE@@target@_FOUND" TRUE) +endif() +if (NOT QT_NO_CREATE_TARGETS AND @INSTALL_CMAKE_NAMESPACE@@target@_FOUND) 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) 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/QtNoLinkTargetHelpers.cmake b/cmake/QtNoLinkTargetHelpers.cmake index 9b82d51434..59dc3ff5ac 100644 --- a/cmake/QtNoLinkTargetHelpers.cmake +++ b/cmake/QtNoLinkTargetHelpers.cmake @@ -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: BSD-3-Clause function(qt_create_nolink_target target dependee_target) if(NOT TARGET "${target}") diff --git a/cmake/QtPkgConfigHelpers.cmake b/cmake/QtPkgConfigHelpers.cmake index 34191c2cf5..dbe736c438 100644 --- a/cmake/QtPkgConfigHelpers.cmake +++ b/cmake/QtPkgConfigHelpers.cmake @@ -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: BSD-3-Clause function(qt_internal_collect_direct_target_dependencies target targets_out_var) __qt_internal_walk_libs("${target}" "${targets_out_var}" _rcc_objects @@ -22,7 +22,8 @@ endmacro() # Create a Qt6*.pc file intended for pkg-config consumption. function(qt_internal_generate_pkg_config_file module) # TODO: PkgConfig is supported under MSVC with pkgconf (github.com/pkgconf/pkgconf) - if((NOT UNIX OR QT_FEATURE_framework) AND NOT MINGW OR CMAKE_VERSION VERSION_LESS "3.20") + if((NOT UNIX OR QT_FEATURE_framework) + AND NOT MINGW OR CMAKE_VERSION VERSION_LESS "3.20" OR ANDROID) return() endif() if(NOT BUILD_SHARED_LIBS) @@ -50,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) diff --git a/cmake/QtPlatformAndroid.cmake b/cmake/QtPlatformAndroid.cmake index 8077000998..e4253b75d7 100644 --- a/cmake/QtPlatformAndroid.cmake +++ b/cmake/QtPlatformAndroid.cmake @@ -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: BSD-3-Clause # # Self contained Platform Settings for Android @@ -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-31") +set(QT_ANDROID_API_VERSION "android-33") 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,14 +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" + "--ndk-stack" "${ANDROID_NDK_ROOT}/ndk-stack" + "--timeout" "${timeout}" "--verbose" PARENT_SCOPE ) diff --git a/cmake/QtPlatformSupport.cmake b/cmake/QtPlatformSupport.cmake index b3176c942c..9f8498e42c 100644 --- a/cmake/QtPlatformSupport.cmake +++ b/cmake/QtPlatformSupport.cmake @@ -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: BSD-3-Clause function(qt_set01 result) if (${ARGN}) @@ -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? @@ -20,6 +19,7 @@ qt_set01(OPENBSD CMAKE_SYSTEM_NAME STREQUAL "OpenBSD") # FIXME: How to identify qt_set01(FREEBSD CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") # FIXME: How to identify this? qt_set01(NETBSD CMAKE_SYSTEM_NAME STREQUAL "NetBSD") # FIXME: How to identify this? qt_set01(WASM CMAKE_SYSTEM_NAME STREQUAL "Emscripten" OR EMSCRIPTEN) +qt_set01(WASM64 QT_QMAKE_TARGET_MKSPEC STREQUAL "wasm-emscripten-64") qt_set01(SOLARIS CMAKE_SYSTEM_NAME STREQUAL "SunOS") qt_set01(HURD CMAKE_SYSTEM_NAME STREQUAL "GNU") @@ -32,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 daca58aabd..f1976b9975 100644 --- a/cmake/QtPlatformTargetHelpers.cmake +++ b/cmake/QtPlatformTargetHelpers.cmake @@ -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: BSD-3-Clause # Defines the public Qt::Platform target, which serves as a dependency for all internal Qt target # as well as user projects consuming Qt. @@ -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 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 84060ae214..9437d4236d 100644 --- a/cmake/QtPluginHelpers.cmake +++ b/cmake/QtPluginHelpers.cmake @@ -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: BSD-3-Clause # Note that these are only the keywords that are unique to qt_internal_add_plugin(). # That function also supports the keywords defined by _qt_internal_get_add_plugin_keywords(). @@ -8,6 +8,7 @@ macro(qt_internal_get_internal_add_plugin_keywords option_args single_args multi EXCEPTIONS ALLOW_UNDEFINED_SYMBOLS SKIP_INSTALL + NO_UNITY_BUILD ) set(${single_args} OUTPUT_DIRECTORY @@ -43,12 +44,12 @@ function(qt_internal_add_plugin target) set(single_args ${public_single_args} ${internal_single_args}) set(multi_args ${public_multi_args} ${internal_multi_args}) - qt_parse_all_arguments(arg "qt_internal_add_plugin" + cmake_parse_arguments(PARSE_ARGV 1 arg "${option_args}" "${single_args}" "${multi_args}" - "${ARGN}" ) + _qt_internal_validate_all_args_are_parsed(arg) # Put this behind a cache option for now. It's too noisy for general use # until most repos are updated. @@ -90,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. @@ -210,6 +216,13 @@ 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}") @@ -276,6 +289,8 @@ function(qt_internal_add_plugin target) ) endif() endif() + + qt_internal_add_autogen_sync_header_dependencies(${target} ${qt_module_target}) endif() # Change the configuration file install location for qml plugins into the Qml package location. @@ -293,6 +308,12 @@ function(qt_internal_add_plugin target) if(TARGET qt_plugins) add_dependencies(qt_plugins "${target}") endif() + + # Record plugin for current repo. + if(qt_repo_plugins AND TARGET ${qt_repo_plugins}) + add_dependencies(${qt_repo_plugins} "${target}") + endif() + if(plugin_type STREQUAL "platforms") if(TARGET qpa_plugins) add_dependencies(qpa_plugins "${target}") @@ -318,10 +339,23 @@ function(qt_internal_add_plugin target) ${arg_PUBLIC_INCLUDE_DIRECTORIES} ) + if(arg_NO_UNITY_BUILD) + set(arg_NO_UNITY_BUILD "NO_UNITY_BUILD") + else() + set(arg_NO_UNITY_BUILD "") + endif() + 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 ${private_includes} + SYSTEM_INCLUDE_DIRECTORIES + ${arg_SYSTEM_INCLUDE_DIRECTORIES} PUBLIC_INCLUDE_DIRECTORIES ${public_includes} LIBRARIES ${arg_LIBRARIES} Qt::PlatformPluginInternal @@ -332,10 +366,10 @@ function(qt_internal_add_plugin target) PUBLIC_DEFINES ${arg_PUBLIC_DEFINES} FEATURE_DEPENDENCIES ${arg_FEATURE_DEPENDENCIES} - DBUS_ADAPTOR_SOURCES "${arg_DBUS_ADAPTOR_SOURCES}" - DBUS_ADAPTOR_FLAGS "${arg_DBUS_ADAPTOR_FLAGS}" - DBUS_INTERFACE_SOURCES "${arg_DBUS_INTERFACE_SOURCES}" - DBUS_INTERFACE_FLAGS "${arg_DBUS_INTERFACE_FLAGS}" + DBUS_ADAPTOR_SOURCES ${arg_DBUS_ADAPTOR_SOURCES} + DBUS_ADAPTOR_FLAGS ${arg_DBUS_ADAPTOR_FLAGS} + DBUS_INTERFACE_SOURCES ${arg_DBUS_INTERFACE_SOURCES} + DBUS_INTERFACE_FLAGS ${arg_DBUS_INTERFACE_FLAGS} COMPILE_OPTIONS ${arg_COMPILE_OPTIONS} PUBLIC_COMPILE_OPTIONS ${arg_PUBLIC_COMPILE_OPTIONS} LINK_OPTIONS ${arg_LINK_OPTIONS} @@ -361,7 +395,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}") @@ -505,3 +538,88 @@ function(qt_internal_get_module_for_plugin target target_type out_var) endforeach() message(FATAL_ERROR "The plug-in '${target}' does not belong to any Qt module.") endfunction() + +function(qt_internal_add_darwin_permission_plugin permission) + string(TOLOWER "${permission}" permission_lower) + string(TOUPPER "${permission}" permission_upper) + set(permission_source_file "platform/darwin/qdarwinpermissionplugin_${permission_lower}.mm") + set(plugin_target "QDarwin${permission}PermissionPlugin") + set(plugin_name "qdarwin${permission_lower}permission") + qt_internal_add_plugin(${plugin_target} + STATIC # Force static, even in shared builds + OUTPUT_NAME ${plugin_name} + PLUGIN_TYPE permissions + DEFAULT_IF FALSE + SOURCES + ${permission_source_file} + DEFINES + QT_DARWIN_PERMISSION_PLUGIN=${permission} + LIBRARIES + 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 + set_target_properties(${plugin_target} PROPERTIES + DISABLE_PRECOMPILE_HEADERS ON + ) + + # Generate plugin JSON file + set(content "{ \"Permissions\": [ \"Q${permission}Permission\" ] }") + get_target_property(plugin_build_dir "${plugin_target}" BINARY_DIR) + set(output_file "${plugin_build_dir}/${plugin_target}.json") + qt_configure_file(OUTPUT "${output_file}" CONTENT "${content}") + + # Associate required usage descriptions + set(usage_descriptions_property "_qt_info_plist_usage_descriptions") + set_target_properties(${plugin_target} PROPERTIES + ${usage_descriptions_property} "NS${permission}UsageDescription" + ) + set_property(TARGET ${plugin_target} APPEND PROPERTY + EXPORT_PROPERTIES ${usage_descriptions_property} + ) + set(usage_descriptions_genex "$<JOIN:$<TARGET_PROPERTY:${plugin_target},${usage_descriptions_property}>, >") + set(extra_plugin_pri_content + "QT_PLUGIN.${plugin_name}.usage_descriptions = ${usage_descriptions_genex}" + ) + + # Support granular check and request implementations + set(separate_request_source_file + "${plugin_build_dir}/qdarwinpermissionplugin_${permission_lower}_request.mm") + set(separate_request_genex + "$<BOOL:$<TARGET_PROPERTY:${plugin_target},_qt_darwin_permissison_separate_request>>") + file(GENERATE OUTPUT "${separate_request_source_file}" CONTENT + " + #define BUILDING_PERMISSION_REQUEST 1 + #include \"${CMAKE_CURRENT_SOURCE_DIR}/${permission_source_file}\" + " + CONDITION "${separate_request_genex}" + ) + 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 + ) + set(permission_request_symbol "_QDarwin${permission}PermissionRequest") + set(permission_request_flag "-Wl,-u,${permission_request_symbol}") + set(has_usage_description_property "_qt_has_${plugin_target}_usage_description") + set(has_usage_description_genex "$<BOOL:$<TARGET_PROPERTY:${has_usage_description_property}>>") + target_link_options(${plugin_target} INTERFACE + "$<$<AND:${separate_request_genex},${has_usage_description_genex}>:${permission_request_flag}>") + list(APPEND extra_plugin_pri_content + "QT_PLUGIN.${plugin_name}.request_flag = $<${separate_request_genex}:${permission_request_flag}>" + ) + + # Expose properties to qmake + set_property(TARGET ${plugin_target} PROPERTY + QT_PLUGIN_PRI_EXTRA_CONTENT ${extra_plugin_pri_content} + ) +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 718b0cd908..f69448c14a 100644 --- a/cmake/QtPostProcess.cmake +++ b/cmake/QtPostProcess.cmake @@ -1,7 +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 - -include(QtPostProcessHelpers) +# SPDX-License-Identifier: BSD-3-Clause qt_internal_create_depends_files() qt_generate_build_internals_extra_cmake_code() diff --git a/cmake/QtPostProcessHelpers.cmake b/cmake/QtPostProcessHelpers.cmake index d334741ecd..0a207f6634 100644 --- a/cmake/QtPostProcessHelpers.cmake +++ b/cmake/QtPostProcessHelpers.cmake @@ -1,8 +1,9 @@ # 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: BSD-3-Clause -function(qt_internal_write_depends_file target module_include_name) - set(outfile "${QT_BUILD_DIR}/include/${module_include_name}/${module_include_name}Depends") +function(qt_internal_write_depends_file target) + get_target_property(module_depends_header ${target} _qt_module_depends_header) + set(outfile "${module_depends_header}") set(contents "/* This file was generated by cmake with the info from ${target} target. */\n") string(APPEND contents "#ifdef __cplusplus /* create empty PCH in C mode */\n") foreach (m ${ARGN}) @@ -249,8 +250,7 @@ function(qt_internal_create_module_depends_file target) get_target_property(hasModuleHeaders "${target}" _qt_module_has_headers) if (${hasModuleHeaders}) - get_target_property(module_include_name "${target}" _qt_module_include_name) - qt_internal_write_depends_file(${target} ${module_include_name} ${qtdeps}) + qt_internal_write_depends_file(${target} ${qtdeps}) endif() if(third_party_deps OR main_module_tool_deps OR target_deps) @@ -446,7 +446,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 +498,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 +577,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 +600,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 +630,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) @@ -696,7 +710,7 @@ endif()\n") # find_package(Qt6Core) is called in case if the feature was disabled. string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS " if(NOT QT_SKIP_BUILD_INTERNALS_PKG_CONFIG_FEATURE) - set(FEATURE_pkg_config \"${FEATURE_pkg_config}\" CACHE STRING \"Using pkg-config\" FORCE) + set(FEATURE_pkg_config \"${FEATURE_pkg_config}\" CACHE BOOL \"Using pkg-config\" FORCE) endif()\n") # The OpenSSL root dir needs to be saved so that repos other than qtbase (like qtopcua) can @@ -713,17 +727,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 " @@ -732,6 +735,24 @@ set(OpenGL_GL_PREFERENCE \"${OpenGL_GL_PREFERENCE}\" CACHE STRING \"\") ") endif() + string(APPEND QT_EXTRA_BUILD_INTERNALS_VARS + " +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" @@ -798,7 +819,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) @@ -842,8 +863,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/QtPrecompiledHeadersHelpers.cmake b/cmake/QtPrecompiledHeadersHelpers.cmake index 7cf432ca72..fe38413407 100644 --- a/cmake/QtPrecompiledHeadersHelpers.cmake +++ b/cmake/QtPrecompiledHeadersHelpers.cmake @@ -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: BSD-3-Clause function(qt_update_precompiled_header target precompiled_header) if (precompiled_header AND BUILD_WITH_PCH) @@ -19,7 +19,9 @@ endfunction() function(qt_update_ignore_pch_source target sources) if (sources) - set_source_files_properties(${sources} PROPERTIES SKIP_PRECOMPILE_HEADERS ON) + set_source_files_properties(${sources} PROPERTIES + SKIP_PRECOMPILE_HEADERS ON + SKIP_UNITY_BUILD_INCLUSION ON) endif() endfunction() diff --git a/cmake/QtPriHelpers.cmake b/cmake/QtPriHelpers.cmake index 224eea8afd..f410dc9b2a 100644 --- a/cmake/QtPriHelpers.cmake +++ b/cmake/QtPriHelpers.cmake @@ -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: BSD-3-Clause # Extracts the 3rdparty libraries for the module ${module_name} # and stores the information in cmake language in @@ -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") @@ -323,7 +325,9 @@ function(qt_generate_module_pri_file target) list(APPEND module_internal_config staticlib) endif() - # TODO: Add the value 'ltcg' to module_internal_config if LTCG is turned on. + if(QT_FEATURE_ltcg) + list(APPEND module_internal_config ltcg) + endif() list(JOIN module_internal_config " " joined_module_internal_config) @@ -541,12 +545,15 @@ QT.${config_module_name}_private.disabled_features = ${disabled_private_features "-DIMPLICIT_LINK_DIRECTORIES=${implicit_link_directories}" -P "${QT_CMAKE_DIR}/QtGenerateLibPri.cmake" VERBATIM) - add_custom_target(${target}_lib_pri DEPENDS "${private_pri_file_path}") + # add_dependencies has no effect when adding interface libraries. So need to add the + # '_lib_pri' targets to ALL to make sure that the related rules executed. + unset(add_pri_target_to_all) if(is_interface_lib) - add_dependencies(${target}_pri_dep_timestamp ${target}_lib_pri) - else() - add_dependencies(${target} ${target}_lib_pri) + set(add_pri_target_to_all ALL) endif() + add_custom_target(${target}_lib_pri ${add_pri_target_to_all} + DEPENDS "${private_pri_file_path}") + add_dependencies(${target} ${target}_lib_pri) endif() qt_install(FILES "${pri_files}" DESTINATION ${INSTALL_MKSPECSDIR}/modules) @@ -771,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}") @@ -782,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}") @@ -842,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/QtPrlHelpers.cmake b/cmake/QtPrlHelpers.cmake index 22d241662d..45bfaedcdf 100644 --- a/cmake/QtPrlHelpers.cmake +++ b/cmake/QtPrlHelpers.cmake @@ -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: BSD-3-Clause # Collects the library dependencies of a target. # As well as rcc object file dependencies. @@ -108,7 +108,6 @@ function(qt_generate_prl_file target install_dir) # whose names we know, and can be used in add_custom_command. set(prl_step1_content "RCC_OBJECTS = ${rcc_objects} -QMAKE_PRL_BUILD_DIR = ${CMAKE_CURRENT_BINARY_DIR} QMAKE_PRL_TARGET = $<TARGET_LINKER_FILE_NAME:${target}> QMAKE_PRL_TARGET_PATH_FOR_CMAKE = $<TARGET_LINKER_FILE:${target}> QMAKE_PRL_CONFIG = ${prl_config} diff --git a/cmake/QtProcessConfigureArgs.cmake b/cmake/QtProcessConfigureArgs.cmake index 0830bd8977..53235ee9d9 100644 --- a/cmake/QtProcessConfigureArgs.cmake +++ b/cmake/QtProcessConfigureArgs.cmake @@ -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: BSD-3-Clause # This script reads Qt configure arguments from config.opt, # translates the arguments to CMake arguments and calls CMake. @@ -54,7 +54,11 @@ if("${MODULE_ROOT}" STREQUAL "") get_filename_component(MODULE_ROOT ".." ABSOLUTE BASE_DIR "${CMAKE_CURRENT_LIST_DIR}") set(qtbase_or_top_level_build TRUE) else() - file(TO_CMAKE_PATH "${MODULE_ROOT}" MODULE_ROOT) + # If MODULE_ROOT is passed without drive letter, we try to add it to the path. + # The check is necessary; otherwise, `get_filename_component` returns an empty string. + if(NOT MODULE_ROOT STREQUAL ".") + get_filename_component(MODULE_ROOT "." REALPATH BASE_DIR "${MODULE_ROOT}") + endif() set(qtbase_or_top_level_build FALSE) endif() set(configure_filename "configure.cmake") @@ -81,6 +85,7 @@ 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 "") @@ -106,6 +111,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) @@ -123,8 +142,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") @@ -144,6 +165,50 @@ 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") + push("--fresh") + else() + file(REMOVE_RECURSE "${CMAKE_BINARY_DIR}/CMakeCache.txt" + "${CMAKE_BINARY_DIR}/CMakeFiles") + endif() +endif() #################################################################################################### # Define functions/macros that are called in configure.cmake files @@ -210,16 +275,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() @@ -230,6 +302,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) @@ -246,6 +323,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) @@ -310,6 +389,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) @@ -326,7 +422,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) @@ -614,10 +712,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") @@ -634,7 +742,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}'.") @@ -655,15 +762,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() @@ -683,6 +781,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) @@ -778,7 +879,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() @@ -810,9 +911,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) @@ -851,15 +956,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) @@ -880,10 +981,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. @@ -914,12 +1038,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() @@ -968,6 +1100,14 @@ endforeach() push("${MODULE_ROOT}") +if(INPUT_sysroot) + qtConfAddWarning("The -sysroot option is deprecated and no longer has any effect. " + "It is recommended to use a toolchain file instead, i.e., " + "-DCMAKE_TOOLCHAIN_FILE=<filename>. " + "Alternatively, you may use -DCMAKE_SYSROOT option " + "to pass the sysroot to CMake.\n") +endif() + # Restore the escaped semicolons in arguments that are lists list(TRANSFORM cmake_args REPLACE "\\[\\[;\\]\\]" "\\\\;") diff --git a/cmake/QtProperties.cmake b/cmake/QtProperties.cmake index 12e0fe6b8b..708b60fe0d 100644 --- a/cmake/QtProperties.cmake +++ b/cmake/QtProperties.cmake @@ -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: BSD-3-Clause define_property(TARGET PROPERTY diff --git a/cmake/QtPublicAppleHelpers.cmake b/cmake/QtPublicAppleHelpers.cmake new file mode 100644 index 0000000000..5f7a7719b5 --- /dev/null +++ b/cmake/QtPublicAppleHelpers.cmake @@ -0,0 +1,954 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +function(_qt_internal_handle_ios_launch_screen target) + # Check if user provided a launch screen path via a variable. + set(launch_screen "") + + # Check if the project provided a launch screen path via a variable. + # This variable is currently in Technical Preview. + if(QT_IOS_LAUNCH_SCREEN) + set(launch_screen "${QT_IOS_LAUNCH_SCREEN}") + endif() + + # Check if the project provided a launch screen path via a target property, it takes precedence + # over the variable. + # This property is currently in Technical Preview. + get_target_property(launch_screen_from_prop "${target}" QT_IOS_LAUNCH_SCREEN) + if(launch_screen_from_prop) + set(launch_screen "${launch_screen_from_prop}") + endif() + + # If the project hasn't provided a launch screen file path, use a copy of the template + # that qmake uses. + # It needs to be a copy because configure_file can't handle all the escaped double quotes + # present in the qmake template file. + set(is_default_launch_screen FALSE) + if(NOT launch_screen AND NOT QT_NO_SET_DEFAULT_IOS_LAUNCH_SCREEN) + set(is_default_launch_screen TRUE) + set(launch_screen + "${__qt_internal_cmake_apple_support_files_path}/LaunchScreen.storyboard") + endif() + + # Check that the launch screen exists. + if(launch_screen) + if(NOT IS_ABSOLUTE "${launch_screen}") + message(FATAL_ERROR + "Provided launch screen value should be an absolute path: '${launch_screen}'") + endif() + + if(NOT EXISTS "${launch_screen}") + message(FATAL_ERROR + "Provided launch screen file does not exist: '${launch_screen}'") + endif() + endif() + + if(launch_screen AND NOT QT_NO_ADD_IOS_LAUNCH_SCREEN_TO_BUNDLE) + get_filename_component(launch_screen_name "${launch_screen}" NAME) + + # Make a copy of the default launch screen template for this target and replace the + # label inside the template with the target name. + if(is_default_launch_screen) + # Configure our default template and place it in the build dir. + set(launch_screen_in_path "${launch_screen}") + + string(MAKE_C_IDENTIFIER "${target}" target_identifier) + set(launch_screen_out_dir + "${CMAKE_CURRENT_BINARY_DIR}/.qt/launch_screen_storyboards/${target_identifier}") + + set(launch_screen_out_path + "${launch_screen_out_dir}/${launch_screen_name}") + + file(MAKE_DIRECTORY "${launch_screen_out_dir}") + + configure_file("${launch_screen_in_path}" "${launch_screen_out_path}" COPYONLY) + + set(final_launch_screen_path "${launch_screen_out_path}") + else() + set(final_launch_screen_path "${launch_screen}") + endif() + + # Add the launch screen storyboard file as a source file, otherwise CMake doesn't consider + # it as a resource file and MACOSX_PACKAGE_LOCATION processing will be skipped. + target_sources("${target}" PRIVATE "${final_launch_screen_path}") + + # Ensure Xcode compiles the storyboard file and installs the compiled storyboard .nib files + # into the app bundle. + # We use target_sources and the MACOSX_PACKAGE_LOCATION source file property for that + # instead of the RESOURCE target property, becaues the latter could potentially end up + # needlessly installing the source storyboard file. + # + # We can't rely on policy CMP0118 since user project controls it. + set(scope_args) + if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.18") + set(scope_args TARGET_DIRECTORY ${target}) + endif() + set_source_files_properties("${final_launch_screen_path}" ${scope_args} + PROPERTIES MACOSX_PACKAGE_LOCATION Resources) + + # Save the launch screen name, so its value is added as an UILaunchStoryboardName entry + # in the Qt generated Info.plist file. + # Xcode expects an Info.plist storyboard entry without an extension. + get_filename_component(launch_screen_base_name "${launch_screen}" NAME_WE) + set_target_properties("${target}" PROPERTIES + _qt_ios_launch_screen_name "${launch_screen_name}" + _qt_ios_launch_screen_base_name "${launch_screen_base_name}" + _qt_ios_launch_screen_path "${final_launch_screen_path}") + endif() +endfunction() + +function(_qt_internal_find_ios_development_team_id out_var) + get_property(team_id GLOBAL PROPERTY _qt_internal_ios_development_team_id) + get_property(team_id_computed GLOBAL PROPERTY _qt_internal_ios_development_team_id_computed) + if(team_id_computed) + # Just in case if the value is non-empty but still booly FALSE. + if(NOT team_id) + set(team_id "") + endif() + set("${out_var}" "${team_id}" PARENT_SCOPE) + return() + endif() + + set_property(GLOBAL PROPERTY _qt_internal_ios_development_team_id_computed "TRUE") + + set(home_dir "$ENV{HOME}") + set(xcode_preferences_path "${home_dir}/Library/Preferences/com.apple.dt.Xcode.plist") + + # Extract the first account name (email) from the user's Xcode preferences + message(DEBUG "Trying to extract an Xcode development team id from '${xcode_preferences_path}'") + execute_process(COMMAND "/usr/libexec/PlistBuddy" + -x -c "print IDEProvisioningTeams" "${xcode_preferences_path}" + OUTPUT_VARIABLE teams_xml + ERROR_VARIABLE plist_error) + + # Parsing state. + set(is_free "") + set(current_team_id "") + set(parsing_is_free FALSE) + set(parsing_team_id FALSE) + set(first_team_id "") + + # Parse the xml output and return the first encountered non-free team id. If no non-free team id + # is found, return the first encountered free team id. + # If no team is found, return an empty string. + # + # Example input: + #<plist version="1.0"> + #<dict> + # <key>marty@planet.local</key> + # <array> + # <dict> + # <key>isFreeProvisioningTeam</key> + # <false/> + # <key>teamID</key> + # <string>AAA</string> + # ... + # </dict> + # <dict> + # <key>isFreeProvisioningTeam</key> + # <true/> + # <key>teamID</key> + # <string>BBB</string> + # ... + # </dict> + # </array> + #</dict> + #</plist> + if(teams_xml AND NOT plist_error) + string(REPLACE "\n" ";" teams_xml_lines "${teams_xml}") + + foreach(xml_line ${teams_xml_lines}) + string(STRIP "${xml_line}" xml_line) + if(xml_line STREQUAL "<dict>") + # Clean any previously found values when a new team dict is matched. + set(is_free "") + set(current_team_id "") + + elseif(xml_line STREQUAL "<key>isFreeProvisioningTeam</key>") + set(parsing_is_free TRUE) + + elseif(parsing_is_free) + set(parsing_is_free FALSE) + + if(xml_line MATCHES "true") + set(is_free TRUE) + else() + set(is_free FALSE) + endif() + + elseif(xml_line STREQUAL "<key>teamID</key>") + set(parsing_team_id TRUE) + + elseif(parsing_team_id) + set(parsing_team_id FALSE) + if(xml_line MATCHES "<string>([^<]+)</string>") + set(current_team_id "${CMAKE_MATCH_1}") + else() + continue() + endif() + + string(STRIP "${current_team_id}" current_team_id) + + # If this is the first team id we found so far, remember that, regardless if's free + # or not. + if(NOT first_team_id AND current_team_id) + set(first_team_id "${current_team_id}") + endif() + + # Break early if we found a non-free team id and use it, because we prefer + # a non-free team for signing, just like qmake. + if(NOT is_free AND current_team_id) + set(first_team_id "${current_team_id}") + break() + endif() + endif() + endforeach() + endif() + + if(NOT first_team_id) + message(DEBUG "Failed to extract an Xcode development team id.") + set("${out_var}" "" PARENT_SCOPE) + else() + message(DEBUG "Successfully extracted the first encountered Xcode development team id.") + set_property(GLOBAL PROPERTY _qt_internal_ios_development_team_id "${first_team_id}") + set("${out_var}" "${first_team_id}" PARENT_SCOPE) + endif() +endfunction() + +function(_qt_internal_get_apple_bundle_identifier_prefix out_var) + get_property(prefix GLOBAL PROPERTY _qt_internal_ios_bundle_identifier_prefix) + get_property(prefix_computed GLOBAL PROPERTY + _qt_internal_ios_bundle_identifier_prefix_computed) + if(prefix_computed) + # Just in case if the value is non-empty but still booly FALSE. + if(NOT prefix) + set(prefix "") + endif() + set("${out_var}" "${prefix}" PARENT_SCOPE) + return() + endif() + + set_property(GLOBAL PROPERTY _qt_internal_ios_bundle_identifier_prefix_computed "TRUE") + + set(home_dir "$ENV{HOME}") + set(xcode_preferences_path "${home_dir}/Library/Preferences/com.apple.dt.Xcode.plist") + + message(DEBUG "Trying to extract the default bundle identifier prefix from Xcode preferences.") + execute_process(COMMAND "/usr/libexec/PlistBuddy" + -c "print IDETemplateOptions:bundleIdentifierPrefix" + "${xcode_preferences_path}" + OUTPUT_VARIABLE prefix + ERROR_VARIABLE prefix_error) + if(prefix AND NOT prefix_error) + message(DEBUG "Successfully extracted the default bundle identifier prefix.") + string(STRIP "${prefix}" prefix) + else() + message(DEBUG "Failed to extract the default bundle identifier prefix.") + endif() + + if(prefix AND NOT prefix_error) + set_property(GLOBAL PROPERTY _qt_internal_ios_bundle_identifier_prefix "${prefix}") + set("${out_var}" "${prefix}" PARENT_SCOPE) + else() + set("${out_var}" "" PARENT_SCOPE) + endif() +endfunction() + +function(_qt_internal_escape_rfc_1034_identifier value out_var) + # According to https://datatracker.ietf.org/doc/html/rfc1034#section-3.5 + # we can only use letters, digits, dot (.) and hyphens (-). + # Underscores are not allowed. + string(REGEX REPLACE "[^A-Za-z0-9.]" "-" value "${value}") + + set("${out_var}" "${value}" PARENT_SCOPE) +endfunction() + +function(_qt_internal_get_default_apple_bundle_identifier target out_var) + _qt_internal_get_apple_bundle_identifier_prefix(prefix) + if(NOT prefix) + set(prefix "com.yourcompany") + + # For a better out-of-the-box experience, try to create a unique prefix by appending + # the sha1 of the team id, if one is found. + _qt_internal_find_ios_development_team_id(team_id) + if(team_id) + string(SHA1 hash "${team_id}") + string(SUBSTRING "${hash}" 0 8 infix) + string(APPEND prefix ".${infix}") + endif() + + 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 + # identifier is used, calling xcodebuild on the command line says that no provisioning profile + # could be found, with no additional error message. If one opens the generated project with + # Xcode and clicks on 'Try again' to get a new profile, it shows a semi-useful error message + # that the identifier is invalid. + _qt_internal_escape_rfc_1034_identifier("${prefix}" prefix) + + if(CMAKE_GENERATOR STREQUAL "Xcode") + set(identifier "${prefix}.$(PRODUCT_NAME:rfc1034identifier)") + else() + set(identifier "${prefix}.${target}") + endif() + + set("${out_var}" "${identifier}" PARENT_SCOPE) +endfunction() + +function(_qt_internal_set_placeholder_apple_bundle_version target) + # If user hasn't provided neither a bundle version nor a bundle short version string for the + # app, set a placeholder value for both which will add them to the generated Info.plist file. + # This is required so that the app launches in the simulator (but apparently not for running + # on-device). + get_target_property(bundle_version "${target}" MACOSX_BUNDLE_BUNDLE_VERSION) + get_target_property(bundle_short_version "${target}" MACOSX_BUNDLE_SHORT_VERSION_STRING) + + if(NOT MACOSX_BUNDLE_BUNDLE_VERSION AND + NOT MACOSX_BUNDLE_SHORT_VERSION_STRING AND + NOT bundle_version AND + NOT bundle_short_version AND + NOT QT_NO_SET_XCODE_BUNDLE_VERSION + ) + get_target_property(version "${target}" VERSION) + if(NOT version) + set(version "${PROJECT_VERSION}") + if(NOT version) + set(version "1.0.0") + endif() + endif() + + # Use x.y for short version and x.y.z for full version + # Any versions longer than this will fail App Store + # submission. + string(REPLACE "." ";" version_list ${version}) + list(LENGTH version_list version_list_length) + list(GET version_list 0 version_major) + set(bundle_short_version "${version_major}") + if(version_list_length GREATER 1) + list(GET version_list 1 version_minor) + string(APPEND bundle_short_version ".${version_minor}") + endif() + set(bundle_version "${bundle_short_version}") + if(version_list_length GREATER 2) + list(GET version_list 2 version_patch) + string(APPEND bundle_version ".${version_patch}") + endif() + + + if(NOT CMAKE_XCODE_ATTRIBUTE_MARKETING_VERSION + AND NOT QT_NO_SET_XCODE_ATTRIBUTE_MARKETING_VERSION + AND NOT CMAKE_XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION + AND NOT QT_NO_SET_XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION + AND CMAKE_GENERATOR STREQUAL "Xcode") + get_target_property(marketing_version "${target}" + XCODE_ATTRIBUTE_MARKETING_VERSION) + get_target_property(current_project_version "${target}" + XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION) + if(NOT marketing_version AND NOT current_project_version) + set_target_properties("${target}" + PROPERTIES + XCODE_ATTRIBUTE_CURRENT_PROJECT_VERSION "${bundle_version}" + XCODE_ATTRIBUTE_MARKETING_VERSION "${bundle_short_version}" + ) + set(bundle_version "$(CURRENT_PROJECT_VERSION)") + set(bundle_short_version "$(MARKETING_VERSION)") + endif() + endif() + + set_target_properties("${target}" + PROPERTIES + MACOSX_BUNDLE_BUNDLE_VERSION "${bundle_version}" + MACOSX_BUNDLE_SHORT_VERSION_STRING "${bundle_short_version}" + ) + endif() +endfunction() + +function(_qt_internal_set_xcode_development_team_id target) + # If user hasn't provided a development team id, try to find the first one specified + # in the Xcode preferences. + if(NOT CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM AND NOT QT_NO_SET_XCODE_DEVELOPMENT_TEAM_ID) + get_target_property(existing_team_id "${target}" XCODE_ATTRIBUTE_DEVELOPMENT_TEAM) + if(NOT existing_team_id) + _qt_internal_find_ios_development_team_id(team_id) + set_target_properties("${target}" + PROPERTIES XCODE_ATTRIBUTE_DEVELOPMENT_TEAM "${team_id}") + endif() + endif() +endfunction() + +function(_qt_internal_set_apple_bundle_identifier target) + # Skip all logic if requested. + if(QT_NO_SET_XCODE_BUNDLE_IDENTIFIER) + return() + endif() + + # There are two fields to consider: the CFBundleIdentifier key (ie., cmake_bundle_identifier) + # to be written to Info.plist and the PRODUCT_BUNDLE_IDENTIFIER (ie., xcode_bundle_identifier) + # property to set in the Xcode project. The `cmake_bundle_identifier` set by + # MACOSX_BUNDLE_GUI_IDENTIFIER applies to both Xcode, and other generators, while + # `xcode_bundle_identifier` set by XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER is + # Xcode specific. + # + # If Ninja is the generator, we set the value of `MACOSX_BUNDLE_GUI_IDENTIFIER` + # and don't touch the `XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER`. + # If Xcode is the generator, we set the value of `XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER`, + # and additionally, to silence a Xcode's warning, we set the `MACOSX_BUNDLE_GUI_IDENTIFIER` to + # `${PRODUCT_BUNDLE_IDENTIFIER}` so that Xcode could sort it out. + + get_target_property(existing_cmake_bundle_identifier "${target}" + MACOSX_BUNDLE_GUI_IDENTIFIER) + get_target_property(existing_xcode_bundle_identifier "${target}" + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER) + + set(is_cmake_bundle_identifier_given FALSE) + if(existing_cmake_bundle_identifier) + set(is_cmake_bundle_identifier_given TRUE) + elseif(MACOSX_BUNDLE_GUI_IDENTIFIER) + set(is_cmake_bundle_identifier_given TRUE) + set(existing_cmake_bundle_identifier ${MACOSX_BUNDLE_GUI_IDENTIFIER}) + endif() + + set(is_xcode_bundle_identifier_given FALSE) + if(existing_xcode_bundle_identifier) + set(is_xcode_bundle_identifier_given TRUE) + elseif(CMAKE_XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER) + set(is_xcode_bundle_identifier_given TRUE) + set(existing_xcode_bundle_identifier ${CMAKE_XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER}) + endif() + + if(is_cmake_bundle_identifier_given + AND is_xcode_bundle_identifier_given + AND NOT existing_cmake_bundle_identifier STREQUAL existing_xcode_bundle_identifier) + message(WARNING + "MACOSX_BUNDLE_GUI_IDENTIFIER and XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER " + "are set to different values. You only need to set one of them. ") + endif() + + if(NOT is_xcode_bundle_identifier_given + AND NOT is_cmake_bundle_identifier_given) + _qt_internal_get_default_apple_bundle_identifier("${target}" bundle_id) + elseif(is_cmake_bundle_identifier_given) + set(bundle_id ${existing_cmake_bundle_identifier}) + elseif(is_xcode_bundle_identifier_given) + set(bundle_id ${existing_xcode_bundle_identifier}) + endif() + + if(CMAKE_GENERATOR STREQUAL "Xcode") + set_target_properties("${target}" + PROPERTIES + XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "${bundle_id}" + MACOSX_BUNDLE_GUI_IDENTIFIER "$(PRODUCT_BUNDLE_IDENTIFIER)") + else() + set_target_properties("${target}" + PROPERTIES + MACOSX_BUNDLE_GUI_IDENTIFIER "${bundle_id}") + endif() +endfunction() + +function(_qt_internal_set_xcode_targeted_device_family target) + if(NOT CMAKE_XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY + AND NOT QT_NO_SET_XCODE_TARGETED_DEVICE_FAMILY) + get_target_property(existing_device_family + "${target}" XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY) + if(NOT existing_device_family) + set(device_family_iphone_and_ipad "1,2") + set_target_properties("${target}" + PROPERTIES + XCODE_ATTRIBUTE_TARGETED_DEVICE_FAMILY + "${device_family_iphone_and_ipad}") + endif() + endif() +endfunction() + +function(_qt_internal_set_xcode_code_sign_style target) + if(NOT CMAKE_XCODE_ATTRIBUTE_CODE_SIGN_STYLE + AND NOT QT_NO_SET_XCODE_CODE_SIGN_STYLE) + get_target_property(existing_code_style + "${target}" XCODE_ATTRIBUTE_CODE_SIGN_STYLE) + if(NOT existing_code_style) + set(existing_code_style "Automatic") + set_target_properties("${target}" + PROPERTIES + XCODE_ATTRIBUTE_CODE_SIGN_STYLE + "${existing_code_style}") + endif() + endif() +endfunction() + +# Workaround for https://gitlab.kitware.com/cmake/cmake/-/issues/15183 +function(_qt_internal_set_xcode_install_path target) + if(NOT CMAKE_XCODE_ATTRIBUTE_INSTALL_PATH + AND NOT QT_NO_SET_XCODE_INSTALL_PATH) + get_target_property(existing_install_path + "${target}" XCODE_ATTRIBUTE_INSTALL_PATH) + if(NOT existing_install_path) + set_target_properties("${target}" + PROPERTIES + XCODE_ATTRIBUTE_INSTALL_PATH + "$(inherited)") + endif() + endif() +endfunction() + +function(_qt_internal_set_xcode_bundle_display_name target) + # We want the value of CFBundleDisplayName to be ${PRODUCT_NAME}, but we can't put that + # into the Info.plist.in template file directly, because the implicit configure_file(Info.plist) + # done by CMake is not using the @ONLY option, so CMake would treat the assignment as + # variable expansion. Escaping using backslashes does not help. + # Work around it by assigning the dollar char to a separate cache var, and expand it, so that + # the final value in the file will be ${PRODUCT_NAME}, to be evaluated at build time by Xcode. + set(QT_INTERNAL_DOLLAR_VAR "$" CACHE STRING "") +endfunction() + +# Adds ${PRODUCT_NAME} to the Info.plist file, which is then evaluated by Xcode itself. +function(_qt_internal_set_xcode_bundle_name target) + if(QT_NO_SET_XCODE_BUNDLE_NAME) + return() + endif() + + get_target_property(existing_bundle_name "${target}" MACOSX_BUNDLE_BUNDLE_NAME) + if(NOT MACOSX_BUNDLE_BUNDLE_NAME AND NOT existing_bundle_name) + if(CMAKE_GENERATOR STREQUAL Xcode) + set_target_properties("${target}" + PROPERTIES + MACOSX_BUNDLE_BUNDLE_NAME "$(PRODUCT_NAME)") + else() + set_target_properties("${target}" + PROPERTIES + MACOSX_BUNDLE_BUNDLE_NAME "${target}") + endif() + endif() +endfunction() + +function(_qt_internal_set_xcode_bitcode_enablement target) + if(CMAKE_XCODE_ATTRIBUTE_ENABLE_BITCODE + OR QT_NO_SET_XCODE_ENABLE_BITCODE) + return() + endif() + + get_target_property(existing_bitcode_enablement + "${target}" XCODE_ATTRIBUTE_ENABLE_BITCODE) + if(NOT existing_bitcode_enablement MATCHES "-NOTFOUND") + return() + endif() + + # Disable bitcode to match Xcode 14's new default + set_target_properties("${target}" + PROPERTIES + XCODE_ATTRIBUTE_ENABLE_BITCODE + "NO") +endfunction() + +function(_qt_internal_copy_info_plist target) + # If the project already specifies a custom file, we don't override it. + get_target_property(info_plist_in "${target}" MACOSX_BUNDLE_INFO_PLIST) + if(NOT info_plist_in) + set(info_plist_in "${__qt_internal_cmake_apple_support_files_path}/Info.plist.app.in") + endif() + + string(MAKE_C_IDENTIFIER "${target}" target_identifier) + set(info_plist_out_dir + "${CMAKE_CURRENT_BINARY_DIR}/.qt/info_plist/${target_identifier}") + set(info_plist_out "${info_plist_out_dir}/Info.plist") + + # Check if we need to specify a custom launch screen storyboard entry. + get_target_property(launch_screen_base_name "${target}" _qt_ios_launch_screen_base_name) + if(launch_screen_base_name) + set(qt_ios_launch_screen_plist_entry "${launch_screen_base_name}") + endif() + + # Call configure_file to substitute Qt-specific @FOO@ values, not ${FOO} values. + # + # The output file will be another template file to be fed to CMake via the + # MACOSX_BUNDLE_INFO_PLIST property. CMake will then call configure_file on it to provide + # content for regular entries like CFBundleName, etc. + # + # We require this extra configure_file call so we can create unique Info.plist files for each + # target in a project, while also providing a way to add Qt specific entries that CMake + # does not support out of the box (e.g. a launch screen name). + configure_file( + "${info_plist_in}" + "${info_plist_out}" + @ONLY + ) + + set_target_properties("${target}" PROPERTIES MACOSX_BUNDLE_INFO_PLIST "${info_plist_out}") +endfunction() + +function(_qt_internal_plist_buddy plist_file) + cmake_parse_arguments(PARSE_ARGV 1 arg + "" "OUTPUT_VARIABLE;ERROR_VARIABLE" "COMMANDS") + foreach(command ${arg_COMMANDS}) + execute_process(COMMAND "/usr/libexec/PlistBuddy" + -c "${command}" "${plist_file}" + OUTPUT_VARIABLE plist_buddy_output + ERROR_VARIABLE plist_buddy_error) + string(STRIP "${plist_buddy_output}" plist_buddy_output) + if(arg_OUTPUT_VARIABLE) + list(APPEND ${arg_OUTPUT_VARIABLE} ${plist_buddy_output}) + set(${arg_OUTPUT_VARIABLE} ${${arg_OUTPUT_VARIABLE}} PARENT_SCOPE) + endif() + if(arg_ERROR_VARIABLE) + list(APPEND ${arg_ERROR_VARIABLE} ${plist_buddy_error}) + set(${arg_ERROR_VARIABLE} ${${arg_ERROR_VARIABLE}} PARENT_SCOPE) + endif() + if(plist_buddy_error) + return() + endif() + endforeach() +endfunction() + +function(_qt_internal_set_apple_localizations target) + if(QT_NO_SET_PLIST_LOCALIZATIONS) + return() + endif() + + 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) + return() + endif() + + _qt_internal_plist_buddy("${plist_file}" + COMMANDS "print CFBundleLocalizations" + OUTPUT_VARIABLE existing_localizations + ) + if(existing_localizations) + return() + endif() + + list(TRANSFORM supported_languages PREPEND + "Add CFBundleLocalizations: string ") + + _qt_internal_plist_buddy("${plist_file}" + COMMANDS + "Add CFBundleLocalizations array" + ${supported_languages} + "Delete CFBundleAllowMixedLocalizations" + ) +endfunction() + +function(_qt_internal_set_ios_simulator_arch target) + if(CMAKE_XCODE_ATTRIBUTE_ARCHS + OR QT_NO_SET_XCODE_ARCHS) + return() + endif() + + get_target_property(existing_archs + "${target}" XCODE_ATTRIBUTE_ARCHS) + if(NOT existing_archs MATCHES "-NOTFOUND") + return() + endif() + + if(NOT x86_64 IN_LIST QT_OSX_ARCHITECTURES) + return() + endif() + + if(CMAKE_OSX_ARCHITECTURES AND NOT x86_64 IN_LIST CMAKE_OSX_ARCHITECTURES) + return() + endif() + + set_target_properties("${target}" + PROPERTIES + "XCODE_ATTRIBUTE_ARCHS[sdk=iphonesimulator*]" + "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 UIKit apps + + _qt_internal_copy_info_plist("${target}") + _qt_internal_set_apple_localizations("${target}") + + # Only set the various properties if targeting the Xcode generator, otherwise the various + # Xcode tokens are embedded as-is instead of being dynamically evaluated. + # This affects things like the version number or application name as reported by Qt API. + if(CMAKE_GENERATOR STREQUAL "Xcode") + _qt_internal_set_xcode_development_team_id("${target}") + _qt_internal_set_xcode_code_sign_style("${target}") + _qt_internal_set_xcode_bundle_display_name("${target}") + _qt_internal_set_xcode_install_path("${target}") + endif() + + _qt_internal_set_xcode_bundle_name("${target}") + _qt_internal_set_apple_bundle_identifier("${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}") + + _qt_internal_finalize_apple_app("${target}") + _qt_internal_set_xcode_targeted_device_family("${target}") + _qt_internal_set_xcode_bitcode_enablement("${target}") + _qt_internal_set_ios_simulator_arch("${target}") +endfunction() + +function(_qt_internal_finalize_macos_app target) + get_target_property(is_bundle ${target} MACOSX_BUNDLE) + if(NOT is_bundle) + return() + endif() + + _qt_internal_finalize_apple_app("${target}") + + # Make sure the install rpath has at least the minimum needed if the app + # has any non-static frameworks. We can't rigorously know if the app will + # have any, even with a static Qt, so always add this. If there are no + # frameworks, it won't do any harm. + get_property(install_rpath TARGET ${target} PROPERTY INSTALL_RPATH) + list(APPEND install_rpath "@executable_path/../Frameworks") + list(REMOVE_DUPLICATES install_rpath) + set_property(TARGET ${target} PROPERTY INSTALL_RPATH "${install_rpath}") +endfunction() diff --git a/cmake/QtPublicCMakeHelpers.cmake b/cmake/QtPublicCMakeHelpers.cmake index 06ce5fbd4c..ca091fcf9a 100644 --- a/cmake/QtPublicCMakeHelpers.cmake +++ b/cmake/QtPublicCMakeHelpers.cmake @@ -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: BSD-3-Clause # copy_if_different works incorrect in Windows if file size if bigger than 2GB. # See https://gitlab.kitware.com/cmake/cmake/-/issues/23052 and QTBUG-99491 for details. @@ -25,13 +25,486 @@ 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) endif() 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". +function(__qt_internal_collect_additional_prefix_paths out_var prefixes_var) + if(DEFINED "${out_var}") + return() + endif() + + set(additional_packages_prefix_paths "") + + set(additional_packages_prefixes "") + if(${prefixes_var}) + list(APPEND additional_packages_prefixes ${${prefixes_var}}) + endif() + if(DEFINED ENV{${prefixes_var}} + AND NOT "$ENV{${prefixes_var}}" STREQUAL "") + set(prefixes_from_env "$ENV{${prefixes_var}}") + if(NOT CMAKE_HOST_WIN32) + string(REPLACE ":" ";" prefixes_from_env "${prefixes_from_env}") + endif() + list(APPEND additional_packages_prefixes ${prefixes_from_env}) + endif() + + foreach(additional_path IN LISTS additional_packages_prefixes) + file(TO_CMAKE_PATH "${additional_path}" additional_path) + + # The prefix paths need to end with lib/cmake to ensure the packages are found when + # cross compiling. Search for REROOT_PATH_ISSUE_MARKER in the qt.toolchain.cmake file for + # details. + # We must pass the values via the PATHS options because the main find_package call uses + # 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. + __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() + 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) + __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() diff --git a/cmake/QtPublicCMakeVersionHelpers.cmake b/cmake/QtPublicCMakeVersionHelpers.cmake index 4b6137249e..de896d9d2f 100644 --- a/cmake/QtPublicCMakeVersionHelpers.cmake +++ b/cmake/QtPublicCMakeVersionHelpers.cmake @@ -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: BSD-3-Clause function(__qt_internal_get_supported_min_cmake_version_for_using_qt out_var) # This is recorded in Qt6ConfigExtras.cmake diff --git a/cmake/QtPublicDependencyHelpers.cmake b/cmake/QtPublicDependencyHelpers.cmake index dcf321d501..bd8b4a55c4 100644 --- a/cmake/QtPublicDependencyHelpers.cmake +++ b/cmake/QtPublicDependencyHelpers.cmake @@ -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: BSD-3-Clause # Note that target_dep_list does not accept a list of values, but a var name that contains the # list of dependencies. See foreach block for reference. @@ -70,7 +70,7 @@ macro(_qt_internal_find_tool_dependencies target target_dep_list) "${_qt_cmake_dir}" ${_qt_additional_packages_prefix_paths} ) - if (NOT ${__qt_${target}_pkg}_FOUND) + if (NOT ${__qt_${target}_pkg}_FOUND AND NOT QT_ALLOW_MISSING_TOOLS_PACKAGES) set(${CMAKE_FIND_PACKAGE_NAME}_FOUND FALSE) set(${CMAKE_FIND_PACKAGE_NAME}_NOT_FOUND_MESSAGE "${CMAKE_FIND_PACKAGE_NAME} could not be found because dependency \ @@ -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/QtPublicFinalizerHelpers.cmake b/cmake/QtPublicFinalizerHelpers.cmake index d26d81e24f..96b5c6c740 100644 --- a/cmake/QtPublicFinalizerHelpers.cmake +++ b/cmake/QtPublicFinalizerHelpers.cmake @@ -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: BSD-3-Clause # Helper to check if the finalizer mode should be used. # If true, use finalizer mode. diff --git a/cmake/QtPublicFindPackageHelpers.cmake b/cmake/QtPublicFindPackageHelpers.cmake index 9ae24b999e..69f4f69e40 100644 --- a/cmake/QtPublicFindPackageHelpers.cmake +++ b/cmake/QtPublicFindPackageHelpers.cmake @@ -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: BSD-3-Clause function(qt_internal_disable_find_package_global_promotion target) set_target_properties("${target}" PROPERTIES _qt_no_promote_global TRUE) diff --git a/cmake/QtPublicPluginHelpers.cmake b/cmake/QtPublicPluginHelpers.cmake index dec3f15222..7087c31234 100644 --- a/cmake/QtPublicPluginHelpers.cmake +++ b/cmake/QtPublicPluginHelpers.cmake @@ -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: BSD-3-Clause # Gets the qt plugin type of the given plugin into out_var_plugin_type. # Also sets out_var_has_plugin_type to TRUE or FALSE depending on whether the plugin type was found. @@ -39,22 +39,32 @@ endfunction() # # The conditions are based on the various properties set in qt_import_plugins. -# All the TARGET_PROPERTY genexes are evaluated in the context of the currently linked target. +# All the TARGET_PROPERTY genexes are evaluated in the context of the currently linked target, +# unless the TARGET argument is given. # # The genex is saved into out_var. function(__qt_internal_get_static_plugin_condition_genex plugin_target_unprefixed out_var) + set(options) + set(oneValueArgs TARGET) + set(multiValueArgs) + cmake_parse_arguments(arg "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) set(plugin_target "${QT_CMAKE_EXPORT_NAMESPACE}::${plugin_target_unprefixed}") set(plugin_target_versionless "Qt::${plugin_target_unprefixed}") get_target_property(_plugin_type "${plugin_target}" QT_PLUGIN_TYPE) + set(target_infix "") + if(arg_TARGET) + set(target_infix "${arg_TARGET},") + endif() + set(_default_plugins_are_enabled - "$<NOT:$<STREQUAL:$<GENEX_EVAL:$<TARGET_PROPERTY:QT_DEFAULT_PLUGINS>>,0>>") - set(_manual_plugins_genex "$<GENEX_EVAL:$<TARGET_PROPERTY:QT_PLUGINS>>") - set(_no_plugins_genex "$<GENEX_EVAL:$<TARGET_PROPERTY:QT_NO_PLUGINS>>") + "$<NOT:$<STREQUAL:$<GENEX_EVAL:$<TARGET_PROPERTY:${target_infix}QT_DEFAULT_PLUGINS>>,0>>") + set(_manual_plugins_genex "$<GENEX_EVAL:$<TARGET_PROPERTY:${target_infix}QT_PLUGINS>>") + set(_no_plugins_genex "$<GENEX_EVAL:$<TARGET_PROPERTY:${target_infix}QT_NO_PLUGINS>>") # Plugin genex marker for prl processing. set(_is_plugin_marker_genex "$<BOOL:QT_IS_PLUGIN_GENEX>") @@ -81,7 +91,7 @@ function(__qt_internal_get_static_plugin_condition_genex ">," # Excludes both plugins targeted by EXCLUDE_BY_TYPE and not included in # INCLUDE_BY_TYPE. - "$<STREQUAL:,$<GENEX_EVAL:$<TARGET_PROPERTY:QT_PLUGINS_${_plugin_type}>>>" + "$<STREQUAL:,$<GENEX_EVAL:$<TARGET_PROPERTY:${target_infix}QT_PLUGINS_${_plugin_type}>>>" ">" ) @@ -90,7 +100,7 @@ function(__qt_internal_get_static_plugin_condition_genex "$<IN_LIST:" "${plugin_target}," "$<GENEX_EVAL:" - "$<TARGET_PROPERTY:QT_PLUGINS_${_plugin_type}>" + "$<TARGET_PROPERTY:${target_infix}QT_PLUGINS_${_plugin_type}>" ">" ">" ) @@ -98,7 +108,7 @@ function(__qt_internal_get_static_plugin_condition_genex "$<IN_LIST:" "${plugin_target_versionless}," "$<GENEX_EVAL:" - "$<TARGET_PROPERTY:QT_PLUGINS_${_plugin_type}>" + "$<TARGET_PROPERTY:${target_infix}QT_PLUGINS_${_plugin_type}>" ">" ">" ) @@ -106,7 +116,7 @@ function(__qt_internal_get_static_plugin_condition_genex # No point in linking the plugin initialization source file into static libraries. The # initialization symbol will be discarded by the linker when the static lib is linked into an # executable or shared library, because nothing is referencing the global static symbol. - set(type_genex "$<TARGET_PROPERTY:TYPE>") + set(type_genex "$<TARGET_PROPERTY:${target_infix}TYPE>") set(no_static_genex "$<NOT:$<STREQUAL:${type_genex},STATIC_LIBRARY>>") # Complete condition that defines whether a static plugin is linked @@ -177,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) @@ -308,6 +318,25 @@ function(__qt_internal_collect_plugin_init_libraries plugin_targets out_var) set("${out_var}" "${plugin_inits_to_link}" PARENT_SCOPE) endfunction() +# Collect a list of genexes to deploy plugin libraries. +function(__qt_internal_collect_plugin_library_files target plugin_targets out_var) + set(library_files "") + + foreach(plugin_target ${plugin_targets}) + set(plugin_target_versioned "${QT_CMAKE_EXPORT_NAMESPACE}::${plugin_target}") + __qt_internal_get_static_plugin_condition_genex( + "${plugin_target}" + plugin_condition + TARGET ${target} + ) + + set(target_genex "$<${plugin_condition}:${plugin_target_versioned}>") + list(APPEND library_files "$<$<BOOL:${target_genex}>:$<TARGET_FILE:${target_genex}>>") + endforeach() + + set("${out_var}" "${library_files}" PARENT_SCOPE) +endfunction() + # Collects all plugin targets discovered by walking the dependencies of ${target}. # # Walks immediate dependencies and their transitive dependencies. @@ -387,6 +416,46 @@ function(__qt_internal_collect_plugin_targets_from_dependencies_of_plugins targe set("${out_var}" "${plugin_targets}" PARENT_SCOPE) endfunction() +# Generate plugin information files for deployment +# +# Arguments: +# OUT_PLUGIN_TARGETS - Variable name to store the plugin targets that were collected with +# __qt_internal_collect_plugin_targets_from_dependencies. +function(__qt_internal_generate_plugin_deployment_info target) + set(no_value_options "") + set(single_value_options "OUT_PLUGIN_TARGETS") + set(multi_value_options "") + cmake_parse_arguments(PARSE_ARGV 0 arg + "${no_value_options}" "${single_value_options}" "${multi_value_options}" + ) + + __qt_internal_collect_plugin_targets_from_dependencies("${target}" plugin_targets) + if(NOT "${arg_OUT_PLUGIN_TARGETS}" STREQUAL "") + set("${arg_OUT_PLUGIN_TARGETS}" "${plugin_targets}" PARENT_SCOPE) + endif() + + get_target_property(marked_for_deployment ${target} _qt_marked_for_deployment) + if(NOT marked_for_deployment) + return() + endif() + + __qt_internal_collect_plugin_library_files(${target} "${plugin_targets}" plugins_files) + set(plugins_files "$<FILTER:${plugins_files},EXCLUDE,^$>") + + _qt_internal_get_deploy_impl_dir(deploy_impl_dir) + set(file_path "${deploy_impl_dir}/${target}-plugins") + get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) + if(is_multi_config) + string(APPEND file_path "-$<CONFIG>") + endif() + string(APPEND file_path ".cmake") + + file(GENERATE + OUTPUT ${file_path} + CONTENT "set(__QT_DEPLOY_PLUGINS ${plugins_files})" + ) +endfunction() + # Main logic of finalizer mode. function(__qt_internal_apply_plugin_imports_finalizer_mode target) # Process a target only once. @@ -395,6 +464,9 @@ function(__qt_internal_apply_plugin_imports_finalizer_mode target) return() endif() + __qt_internal_generate_plugin_deployment_info(${target} + OUT_PLUGIN_TARGETS plugin_targets) + # By default if the project hasn't explicitly opted in or out, use finalizer mode. # The precondition for this is that qt_finalize_target was called (either explicitly by the user # or auto-deferred by CMake 3.19+). @@ -408,7 +480,6 @@ function(__qt_internal_apply_plugin_imports_finalizer_mode target) return() endif() - __qt_internal_collect_plugin_targets_from_dependencies("${target}" plugin_targets) __qt_internal_collect_plugin_init_libraries("${plugin_targets}" init_libraries) __qt_internal_collect_plugin_libraries("${plugin_targets}" plugin_libraries) diff --git a/cmake/QtPublicTargetHelpers.cmake b/cmake/QtPublicTargetHelpers.cmake index 554b02fcf5..02d5546560 100644 --- a/cmake/QtPublicTargetHelpers.cmake +++ b/cmake/QtPublicTargetHelpers.cmake @@ -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: BSD-3-Clause function(__qt_internal_strip_target_directory_scope_token target out_var) # In CMake versions earlier than CMake 3.18, a subdirectory scope id is appended to the @@ -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 70fa45a898..771911c5d5 100644 --- a/cmake/QtPublicTestHelpers.cmake +++ b/cmake/QtPublicTestHelpers.cmake @@ -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: BSD-3-Clause # This function wraps COMMAND with cmake script, that makes possible standalone run with external # arguments. @@ -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,8 +97,12 @@ 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()" ) endfunction() + +function(_qt_internal_test_batch_target_name out) + set(${out} "test_batch" PARENT_SCOPE) +endfunction() diff --git a/cmake/QtPublicToolHelpers.cmake b/cmake/QtPublicToolHelpers.cmake index ae5923a37a..031f0a3317 100644 --- a/cmake/QtPublicToolHelpers.cmake +++ b/cmake/QtPublicToolHelpers.cmake @@ -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: BSD-3-Clause # The function returns location of the imported 'tool', returns an empty string if tool is not # imported. @@ -44,3 +44,73 @@ function(__qt_internal_get_tool_imported_location out_var tool) set(${out_var} "${${out_var}}" PARENT_SCOPE) endfunction() + +function(_qt_internal_generate_tool_command_wrapper) + get_property(is_called GLOBAL PROPERTY _qt_internal_generate_tool_command_wrapper_called) + if(NOT CMAKE_HOST_WIN32 OR is_called) + return() + endif() + + set(prefixes "") + + # In a prefix build, the just-built tools should pick up libraries from the current repo build + # dir. + if(QT_BUILD_DIR) + list(APPEND prefixes "${QT_BUILD_DIR}") + endif() + + # Pick up libraries from the main location where Qt was installed during a Qt build. + if(QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX) + list(APPEND prefixes "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}") + endif() + + # Needed for ExternalProjects examples, where the Qt build dir is passed via this variable + # to the example project. + if(QT_ADDITIONAL_PACKAGES_PREFIX_PATH) + __qt_internal_prefix_paths_to_roots(additional_roots + "${QT_ADDITIONAL_PACKAGES_PREFIX_PATH}") + list(APPEND prefixes ${QT_ADDITIONAL_PACKAGES_PREFIX_PATH}) + endif() + + # Fallback to wherever Qt6 package is. + if(QT6_INSTALL_PREFIX) + list(APPEND prefixes "${QT6_INSTALL_PREFIX}") + endif() + + # When building qtbase, QT6_INSTALL_BINS is not set yet. + if(INSTALL_BINDIR) + set(bin_suffix "${INSTALL_BINDIR}") + else() + set(bin_suffix "${QT6_INSTALL_BINS}") + endif() + + set(path_dirs "") + foreach(prefix IN LISTS prefixes) + set(bin_dir "${prefix}/${bin_suffix}") + if(EXISTS "${bin_dir}") + file(TO_NATIVE_PATH "${bin_dir}" path_dir) + list(APPEND path_dirs "${path_dir}") + endif() + endforeach() + + set(tool_command_wrapper_dir "${CMAKE_BINARY_DIR}/.qt/bin") + file(MAKE_DIRECTORY "${tool_command_wrapper_dir}") + set(tool_command_wrapper_path "${tool_command_wrapper_dir}/qt_setup_tool_path.bat") + + file(WRITE "${tool_command_wrapper_path}" "@echo off +set PATH=${path_dirs};%PATH% +%*") + + set(QT_TOOL_COMMAND_WRAPPER_PATH "${tool_command_wrapper_path}" + CACHE INTERNAL "Path to the wrapper of the tool commands") + + set_property(GLOBAL PROPERTY _qt_internal_generate_tool_command_wrapper_called TRUE) +endfunction() + +# Gets the path to tool wrapper shell script. +function(_qt_internal_get_tool_wrapper_script_path out_variable) + # Ensure the script wrapper exists. + _qt_internal_generate_tool_command_wrapper() + + set(${out_variable} "${QT_TOOL_COMMAND_WRAPPER_PATH}" PARENT_SCOPE) +endfunction() diff --git a/cmake/QtPublicWalkLibsHelpers.cmake b/cmake/QtPublicWalkLibsHelpers.cmake index bf9262992b..f79b70c710 100644 --- a/cmake/QtPublicWalkLibsHelpers.cmake +++ b/cmake/QtPublicWalkLibsHelpers.cmake @@ -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: BSD-3-Clause # Add libraries to variable ${out_libs_var} in a way that duplicates # are added at the end. This ensures the library order needed for the @@ -189,7 +189,7 @@ function(__qt_internal_walk_libs # 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) + if(TARGET "${namespaceless_lib_target}") set(lib_target ${namespaceless_lib_target}) endif() endif() @@ -277,6 +277,18 @@ ${target}, but not declared.") set(${rcc_objects_out_var} ${rcc_objects} PARENT_SCOPE) endfunction() +function(__qt_internal_print_missing_dependency_target_warning target dep) + if(QT_SILENCE_MISSING_DEPENDENCY_TARGET_WARNING) + return() + endif() + message(WARNING + "When trying to collect dependencies of target '${target}', " + "the non-existent target '${dep}' was encountered. " + "This can likely be fixed by moving the find_package call that pulls in " + "'${dep}' to the scope of directory '${CMAKE_CURRENT_LIST_DIR}' or higher. " + "This warning can be silenced by setting QT_SILENCE_MISSING_DEPENDENCY_TARGET_WARNING to " + "ON.") +endfunction() # Given ${target}, collect all its private dependencies that are CMake targets. # @@ -303,9 +315,14 @@ function(__qt_internal_collect_all_target_dependencies target out_var) "qt_private_link_library_targets" "collect_targets") - if(lib_walked_targets) - list(APPEND dep_targets ${lib_walked_targets}) - endif() + foreach(lib_target IN LISTS lib_walked_targets) + if(NOT TARGET "${lib_target}") + __qt_internal_print_missing_dependency_target_warning(${target} + ${lib_target}) + continue() + endif() + list(APPEND dep_targets ${lib_target}) + endforeach() endif() endforeach() endif() diff --git a/cmake/QtPublicWasmToolchainHelpers.cmake b/cmake/QtPublicWasmToolchainHelpers.cmake index 2a4f459b49..31f6b5aca5 100644 --- a/cmake/QtPublicWasmToolchainHelpers.cmake +++ b/cmake/QtPublicWasmToolchainHelpers.cmake @@ -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: BSD-3-Clause # Assuming EMSDK == /path/emsdk # @@ -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.10") + set(QT_EMCC_RECOMMENDED_VERSION "3.1.50") set(${out_var} "${QT_EMCC_RECOMMENDED_VERSION}" PARENT_SCOPE) endfunction() @@ -72,3 +72,37 @@ function(__qt_internal_show_error_no_emscripten_toolchain_file_found_when_using_ "or provide a path to a valid emscripten installation via the EMSDK " "environment variable.") endfunction() + +function(__qt_internal_get_qt_build_emsdk_version out_var) + if(QT6_INSTALL_PREFIX) + set(WASM_BUILD_DIR "${QT6_INSTALL_PREFIX}") + elseif(QT_BUILD_DIR) + set(WASM_BUILD_DIR "${QT_BUILD_DIR}") + endif() + if(EXISTS "${WASM_BUILD_DIR}/src/corelib/global/qconfig.h") + file(READ "${WASM_BUILD_DIR}/src/corelib/global/qconfig.h" ver) + elseif(EXISTS "${WASM_BUILD_DIR}/include/QtCore/qconfig.h") + file(READ "${WASM_BUILD_DIR}/include/QtCore/qconfig.h" ver) + else() + message("qconfig.h not found, unable to determine Qt build Emscripten version") + endif() + 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) + __qt_internal_get_emcc_recommended_version(_recommended_emver) + __qt_internal_get_emroot_path_suffix_from_emsdk_env(emroot_path) + __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}") + 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}") + message("This may not work correctly") + endif() +endfunction() diff --git a/cmake/QtQmakeHelpers.cmake b/cmake/QtQmakeHelpers.cmake index a81ba8d4b3..c618fa0510 100644 --- a/cmake/QtQmakeHelpers.cmake +++ b/cmake/QtQmakeHelpers.cmake @@ -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: BSD-3-Clause # Create a QMake list (values space-separated) containing paths. # Entries that contain whitespace characters are quoted. @@ -37,8 +37,6 @@ function(qt_generate_qconfig_cpp in_file out_file) set(QT_SYS_CONF_DIR "${INSTALL_SYSCONFDIR}") # Compute and set relocation prefixes. - # TODO: Clean this up, there's a bunch of unrealistic assumptions here. - # See qtConfOutput_preparePaths in qtbase/configure.pri. if(WIN32) set(lib_location_absolute_path "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}/${INSTALL_BINDIR}") @@ -82,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() @@ -98,7 +96,8 @@ function(qt_generate_qmake_and_qtpaths_wrapper_for_target) "${host_prefix}") file(RELATIVE_PATH ext_prefix_relative_to_conf_file "${ext_prefix}/${INSTALL_BINDIR}" "${ext_prefix}") - file(RELATIVE_PATH ext_prefix_relative_to_host_prefix "${host_prefix}" "${ext_prefix}") + file(RELATIVE_PATH ext_datadir_relative_to_host_prefix "${host_prefix}" + "${ext_prefix}/${INSTALL_MKSPECSDIR}/..") set(content "") @@ -129,8 +128,24 @@ Prefix=${prefix} string(APPEND content "[Paths] Prefix=${ext_prefix_relative_to_conf_file} +Documentation=${INSTALL_DOCDIR} +Headers=${INSTALL_INCLUDEDIR} +Libraries=${INSTALL_LIBDIR} +LibraryExecutables=${INSTALL_LIBEXECDIR} +Binaries=${INSTALL_BINDIR} +Plugins=${INSTALL_PLUGINSDIR} +QmlImports=${INSTALL_QMLDIR} +ArchData=${INSTALL_ARCHDATADIR} +Data=${INSTALL_DATADIR} +Translations=${INSTALL_TRANSLATIONSDIR} +Examples=${INSTALL_EXAMPLESDIR} +Tests=${INSTALL_TESTSDIR} +Settings=${INSTALL_SYSCONFDIR} HostPrefix=${host_prefix_relative_to_conf_file} -HostData=${ext_prefix_relative_to_host_prefix} +HostBinaries=${QT${PROJECT_VERSION_MAJOR}_HOST_INFO_BINDIR} +HostLibraries=${QT${PROJECT_VERSION_MAJOR}_HOST_INFO_LIBDIR} +HostLibraryExecutables=${QT${PROJECT_VERSION_MAJOR}_HOST_INFO_LIBEXECDIR} +HostData=${ext_datadir_relative_to_host_prefix} Sysroot=${sysroot} SysrootifyPrefix=${sysrootify_prefix} TargetSpec=${QT_QMAKE_TARGET_MKSPEC} @@ -155,6 +170,11 @@ HostSpec=${QT_QMAKE_HOST_MKSPEC} endif() set(host_qt_bindir "${host_prefix}/${QT${PROJECT_VERSION_MAJOR}_HOST_INFO_BINDIR}") + file(TO_NATIVE_PATH "${host_qt_bindir}" host_qt_bindir) + + if(QT_CREATE_VERSIONED_HARD_LINK AND QT_WILL_INSTALL) + set(tool_version "${PROJECT_VERSION_MAJOR}") + endif() foreach(host_type ${hosts}) foreach(tool_name qmake qtpaths) @@ -173,6 +193,14 @@ HostSpec=${QT_QMAKE_HOST_MKSPEC} configure_file("${wrapper_in_file}" "${wrapper}" @ONLY NEWLINE_STYLE ${newline_style}) qt_copy_or_install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/${wrapper}" DESTINATION "${INSTALL_BINDIR}") + + # Configuring a new wrapper file, this type setting the tool_version + if(QT_CREATE_VERSIONED_HARD_LINK AND QT_WILL_INSTALL) + set(versioned_wrapper "preliminary/${wrapper_prefix}${tool_name}${tool_version}${wrapper_extension}") + configure_file("${wrapper_in_file}" "${versioned_wrapper}" @ONLY NEWLINE_STYLE ${newline_style}) + qt_copy_or_install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/${versioned_wrapper}" + DESTINATION "${INSTALL_BINDIR}") + endif() endforeach() endforeach() endfunction() @@ -183,6 +211,8 @@ function(qt_get_qmake_module_name result module) string(REGEX REPLACE "^Qt6" "" module "${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/QtResourceHelpers.cmake b/cmake/QtResourceHelpers.cmake index 76f423d8b4..2df1fed50f 100644 --- a/cmake/QtResourceHelpers.cmake +++ b/cmake/QtResourceHelpers.cmake @@ -1,7 +1,19 @@ # 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: BSD-3-Clause function(qt_internal_add_resource target resourceName) + if(NOT TARGET "${target}") + message(FATAL_ERROR "${target} is not a target.") + endif() + qt_internal_is_skipped_test(skipped ${target}) + if(skipped) + return() + endif() + qt_internal_is_in_test_batch(in_batch ${target}) + if(in_batch) + _qt_internal_test_batch_target_name(target) + endif() + # Don't try to add resources when cross compiling, and the target is actually a host target # (like a tool). qt_is_imported_target("${target}" is_imported) @@ -9,7 +21,11 @@ function(qt_internal_add_resource target resourceName) return() endif() - qt_parse_all_arguments(arg "qt_add_resource" "" "PREFIX;LANG;BASE;OUTPUT_TARGETS" "FILES" ${ARGN}) + cmake_parse_arguments(PARSE_ARGV 2 arg + "" + "PREFIX;LANG;BASE;OUTPUT_TARGETS" + "FILES") + _qt_internal_validate_all_args_are_parsed(arg) _qt_internal_process_resource(${target} ${resourceName} PREFIX "${arg_PREFIX}" diff --git a/cmake/QtRpathHelpers.cmake b/cmake/QtRpathHelpers.cmake index bdc3b89944..da6c8715a8 100644 --- a/cmake/QtRpathHelpers.cmake +++ b/cmake/QtRpathHelpers.cmake @@ -1,12 +1,12 @@ # 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: BSD-3-Clause # Returns the platform-specific relative rpath base token, if it's supported. # If it's not supported, returns the string NO_KNOWN_RPATH_REL_BASE. function(qt_internal_get_relative_rpath_base_token out_var) if(APPLE) set(rpath_rel_base "@loader_path") - elseif(LINUX OR SOLARIS OR FREEBSD OR HURD) + elseif(LINUX OR SOLARIS OR FREEBSD OR HURD OR OPENBSD) set(rpath_rel_base "$ORIGIN") else() set(rpath_rel_base "NO_KNOWN_RPATH_REL_BASE") @@ -88,7 +88,12 @@ function(qt_apply_rpaths) return() endif() - qt_parse_all_arguments(arg "qt_apply_rpaths" "RELATIVE_RPATH" "TARGET;INSTALL_PATH" "" ${ARGN}) + cmake_parse_arguments(PARSE_ARGV 0 arg + "RELATIVE_RPATH" + "TARGET;INSTALL_PATH" + "") + _qt_internal_validate_all_args_are_parsed(arg) + if(NOT arg_TARGET) message(FATAL_ERROR "No target given to qt_apply_rpaths.") else() @@ -187,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 c4695ac398..2a933b2222 100644 --- a/cmake/QtSanitizerHelpers.cmake +++ b/cmake/QtSanitizerHelpers.cmake @@ -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: BSD-3-Clause # Computes which sanitizer options should be set based on features evaluated in qtbase. # Sets ECM_ENABLE_SANITIZERS with those options in the function calling scope. @@ -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/QtScopeFinalizerHelpers.cmake b/cmake/QtScopeFinalizerHelpers.cmake index 5e1f58fa42..9e13bec26d 100644 --- a/cmake/QtScopeFinalizerHelpers.cmake +++ b/cmake/QtScopeFinalizerHelpers.cmake @@ -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: BSD-3-Clause # Add a finalizer function for the current CMake list file. # It will be processed just before leaving the current source directory scope. 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 8f670bc197..61f62207fa 100644 --- a/cmake/QtSeparateDebugInfo.cmake +++ b/cmake/QtSeparateDebugInfo.cmake @@ -1,7 +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 - -include(CMakeFindBinUtils) +# SPDX-License-Identifier: BSD-3-Clause if(CMAKE_VERSION VERSION_LESS 3.17.0) set(CMAKE_CURRENT_FUNCTION_LIST_DIR ${CMAKE_CURRENT_LIST_DIR}) @@ -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 95b5e64170..a420495756 100644 --- a/cmake/QtSetup.cmake +++ b/cmake/QtSetup.cmake @@ -1,328 +1,6 @@ # 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: 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) - -# 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() - -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 "") - -# 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) -option(QT_BUILD_MINIMAL_STATIC_TESTS "Build minimal subset of tests for static Qt builds" OFF) -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() - -# 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() diff --git a/cmake/QtSimdHelpers.cmake b/cmake/QtSimdHelpers.cmake index 233b99e8ea..1e77bf449f 100644 --- a/cmake/QtSimdHelpers.cmake +++ b/cmake/QtSimdHelpers.cmake @@ -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: BSD-3-Clause # Handle files that need special SIMD-related flags. # @@ -16,8 +16,12 @@ # SIMD compiler flags. This is mostly relevant for fat / universal builds # function(qt_internal_add_simd_part target) - qt_parse_all_arguments(arg "qt_add_simd_part" "" "NAME;SIMD" - "${__default_private_args};COMPILE_FLAGS;EXCLUDE_OSX_ARCHITECTURES" ${ARGN}) + cmake_parse_arguments(PARSE_ARGV 1 arg + "" + "NAME;SIMD" + "${__default_private_args};COMPILE_FLAGS;EXCLUDE_OSX_ARCHITECTURES") + _qt_internal_validate_all_args_are_parsed(arg) + if ("x${arg_SIMD}" STREQUAL x) message(FATAL_ERROR "qt_add_simd_part needs a SIMD type to be set.") endif() @@ -94,7 +98,9 @@ function(qt_internal_add_simd_part target) ${arg_COMPILE_FLAGS} ) endforeach() - set_source_files_properties(${arg_SOURCES} PROPERTIES SKIP_PRECOMPILE_HEADERS TRUE) + set_source_files_properties(${arg_SOURCES} PROPERTIES + SKIP_PRECOMPILE_HEADERS TRUE + SKIP_UNITY_BUILD_INCLUSION TRUE) target_sources(${target} PRIVATE ${arg_SOURCES}) else() if(QT_CMAKE_DEBUG_EXTEND_TARGET) diff --git a/cmake/QtSingleRepoTargetSetBuildHelpers.cmake b/cmake/QtSingleRepoTargetSetBuildHelpers.cmake index 245d695079..9a003c62c5 100644 --- a/cmake/QtSingleRepoTargetSetBuildHelpers.cmake +++ b/cmake/QtSingleRepoTargetSetBuildHelpers.cmake @@ -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: BSD-3-Clause # Macro that checks for a single repo target set build, and returns from the current file, directory # or function. Use this at the top of project files to whitelist the file for the given package. 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 c84dbab40d..0188b87c6a 100644 --- a/cmake/QtSyncQtHelpers.cmake +++ b/cmake/QtSyncQtHelpers.cmake @@ -1,206 +1,324 @@ # 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: BSD-3-Clause -function(qt_ensure_perl) - find_program(HOST_PERL "perl" DOC "Perl binary") - if (NOT HOST_PERL) - message(FATAL_ERROR "Perl needs to be available to build Qt.") +# The function generates the Qt module header structure in build directory and creates install +# rules. Apart the lists of header files the function takes into account +# QT_REPO_PUBLIC_NAMESPACE_REGEX cache variable, that can be set by repository in .cmake.conf file. +# The variable tells the syncqt program, what namespaces are treated as public. Symbols in public +# namespaces are considered when generating CaMeL case header files. +function(qt_internal_target_sync_headers target module_headers module_headers_generated) + if(NOT TARGET ${QT_CMAKE_EXPORT_NAMESPACE}::syncqt) + message(FATAL_ERROR "${QT_CMAKE_EXPORT_NAMESPACE}::syncqt is not a target.") endif() -endfunction() - -function(qt_ensure_sync_qt) - qt_ensure_perl() - if(DEFINED QT_SYNCQT) + get_target_property(has_headers ${target} _qt_module_has_headers) + if(NOT has_headers) return() endif() - get_property(QT_SYNCQT GLOBAL PROPERTY _qt_syncqt) - if(NOT "${QT_SYNCQT}" STREQUAL "") - set(QT_SYNCQT "${QT_SYNCQT}" PARENT_SCOPE) - return() - endif() + qt_internal_module_info(module "${target}") - # When building qtbase, use the source syncqt, otherwise use the installed one. - set(SYNCQT_FROM_SOURCE "${QtBase_SOURCE_DIR}/libexec/syncqt.pl") - if(NOT ("${QtBase_SOURCE_DIR}" STREQUAL "") AND EXISTS "${SYNCQT_FROM_SOURCE}") - set(syncqt_absolute_path "${SYNCQT_FROM_SOURCE}") - message(STATUS "Using source syncqt found at: ${syncqt_absolute_path}") - - qt_path_join(syncqt_install_dir ${QT_INSTALL_DIR} ${INSTALL_LIBEXECDIR}) - qt_copy_or_install(PROGRAMS "${SYNCQT_FROM_SOURCE}" - DESTINATION "${syncqt_install_dir}") - elseif(NOT "${QT_HOST_PATH}" STREQUAL "") - get_filename_component(syncqt_absolute_path - "${QT_HOST_PATH}/${QT${PROJECT_VERSION_MAJOR}_HOST_INFO_LIBEXECDIR}/syncqt.pl" - ABSOLUTE) - message(STATUS "Using host syncqt found at: ${syncqt_absolute_path}") - else() - get_filename_component(syncqt_absolute_path - "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}/${INSTALL_LIBEXECDIR}/syncqt.pl" - ABSOLUTE) - message(STATUS "Using installed syncqt found at: ${syncqt_absolute_path}") + get_target_property(sync_source_directory ${target} _qt_sync_source_directory) + set(syncqt_timestamp "${CMAKE_CURRENT_BINARY_DIR}/${target}_syncqt_timestamp") + set(syncqt_outputs "${syncqt_timestamp}") + + set(is_interface_lib FALSE) + get_target_property(type ${target} TYPE) + if(type STREQUAL "INTERFACE_LIBRARY") + set(is_interface_lib TRUE) endif() - set(QT_SYNCQT "${syncqt_absolute_path}" PARENT_SCOPE) - set_property(GLOBAL PROPERTY _qt_syncqt "${syncqt_absolute_path}") -endfunction() + set(version_script_private_content_file "") + if(NOT is_interface_lib) + list(APPEND syncqt_outputs + "${module_build_interface_include_dir}/${module}Version" + "${module_build_interface_include_dir}/qt${module_lower}version.h") + if(TEST_ld_version_script) + set(version_script_private_content_file + "${CMAKE_CURRENT_BINARY_DIR}/${target}.version.private_content") + set(version_script_args + "-versionScript" "${version_script_private_content_file}") + list(APPEND syncqt_outputs "${version_script_private_content_file}") + qt_internal_add_linker_version_script(${target} + PRIVATE_CONTENT_FILE "${version_script_private_content_file}") + endif() + endif() -function(qt_install_injections target build_dir install_dir) - set(injections ${ARGN}) - qt_internal_module_info(module ${target}) - get_target_property(target_type ${target} TYPE) - if (target_type STREQUAL "INTERFACE_LIBRARY") - set(is_framework FALSE) + # Check for _qt_module_is_3rdparty_header_library flag to detect non-Qt modules and + # indicate this to syncqt. + get_target_property(is_3rd_party_library ${target} _qt_module_is_3rdparty_header_library) + set(non_qt_module_argument "") + if(is_3rd_party_library) + set(non_qt_module_argument "-nonQt") else() + list(APPEND syncqt_outputs "${module_build_interface_include_dir}/${module}") + get_target_property(no_headersclean_check ${target} _qt_no_headersclean_check) + if(NOT no_headersclean_check) + list(APPEND syncqt_outputs + "${CMAKE_CURRENT_BINARY_DIR}/${module}_header_check_exceptions") + endif() + endif() + + set(is_framework FALSE) + if(NOT is_interface_lib) get_target_property(is_framework ${target} FRAMEWORK) endif() - # examples: - # SYNCQT.INJECTIONS = src/corelib/global/qconfig.h:qconfig.h:QtConfig src/corelib/global/qconfig_p.h:5.12.0/QtCore/private/qconfig_p.h - # SYNCQT.INJECTIONS = src/gui/vulkan/qvulkanfunctions.h:^qvulkanfunctions.h:QVulkanFunctions:QVulkanDeviceFunctions src/gui/vulkan/qvulkanfunctions_p.h:^5.12.0/QtGui/private/qvulkanfunctions_p.h - # The are 3 parts to the assignment, divded by colons ':'. - # The first part contains a path to a generated file in a build folder. - # The second part contains the file name that the forwarding header should have, which points - # to the file in the first part. - # The third part contains multiple UpperCaseFileNames that should be forwarding headers to the - # header specified in the second part. - separate_arguments(injections UNIX_COMMAND "${injections}") - foreach(injection ${injections}) - string(REPLACE ":" ";" injection ${injection}) - # Part 1. - list(GET injection 0 file) - # Part 2. - list(GET injection 1 destination) - string(REGEX REPLACE "^\\^" "" destination "${destination}") - list(REMOVE_AT injection 0 1) - # Part 3. - set(fwd_hdrs ${injection}) - get_filename_component(destinationdir ${destination} DIRECTORY) - get_filename_component(destinationname ${destination} NAME) - get_filename_component(original_file_name ${file} NAME) - - # This describes a concrete example for easier comprehension: - # A file 'qtqml-config.h' is generated by qt_internal_feature_write_file into - # ${qtdeclarative_build_dir}/src/{module_include_name}/qtqml-config.h (part 1). - # - # Generate a lower case forwarding header (part 2) 'qtqml-config.h' at the following - # location: - # ${some_prefix}/include/${module_include_name}/qtqml-config.h. - # - # Inside this file, we #include the originally generated file, - # ${qtdeclarative_build_dir}/src/{module_include_name}/qtqml-config.h. - # - # ${some_prefix}'s value depends on the build type. - # If doing a prefix build, it should point to - # ${current_repo_build_dir} which is ${qtdeclarative_build_dir}. - # If doing a non-prefix build, it should point to - # ${qtbase_build_dir}. - # - # In the code below, ${some_prefix} == ${build_dir}. - set(lower_case_forwarding_header_path "${build_dir}/include/${module_include_name}") - if(destinationdir) - string(APPEND lower_case_forwarding_header_path "/${destinationdir}") - endif() - set(current_repo_build_dir "${PROJECT_BINARY_DIR}") - file(RELATIVE_PATH relpath - "${lower_case_forwarding_header_path}" - "${current_repo_build_dir}/${file}") - set(main_contents "#include \"${relpath}\"") + qt_internal_get_qt_all_known_modules(known_modules) + + get_target_property(is_internal_module ${target} _qt_is_internal_module) + set(internal_module_argument "") + if(is_internal_module) + set(internal_module_argument "-internal") + endif() - qt_configure_file(OUTPUT "${lower_case_forwarding_header_path}/${original_file_name}" - CONTENT "${main_contents}") + 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) - if(is_framework) - if(file MATCHES "_p\\.h$") - set(header_type PRIVATE) - else() - set(header_type PUBLIC) - endif() - qt_copy_framework_headers(${target} ${header_type} - ${current_repo_build_dir}/${file}) + # We need to use the real paths since otherwise it may lead to the invalid work of the + # std::filesystem API + get_filename_component(source_dir_real "${sync_source_directory}" REALPATH) + get_filename_component(binary_dir_real "${CMAKE_CURRENT_BINARY_DIR}" REALPATH) + + if(QT_REPO_PUBLIC_NAMESPACE_REGEX) + set(public_namespaces_filter -publicNamespaceFilter "${QT_REPO_PUBLIC_NAMESPACE_REGEX}") + endif() + + if(qpa_filter_regex) + set(qpa_filter_argument + -qpaHeadersFilter "${qpa_filter_regex}" + ) + endif() + + if(rhi_filter_regex) + set(rhi_filter_argument + -rhiHeadersFilter "${rhi_filter_regex}" + ) + endif() + + if(ssg_filter_regex) + set(ssg_filter_argument + -ssgHeadersFilter "${ssg_filter_regex}" + ) + endif() + + set(common_syncqt_arguments + -module "${module}" + -sourceDir "${source_dir_real}" + -binaryDir "${binary_dir_real}" + -privateHeadersFilter "${private_filter_regex}" + -includeDir "${module_build_interface_include_dir}" + -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} + ) + + if(QT_INTERNAL_ENABLE_SYNCQT_DEBUG_OUTPUT) + list(APPEND common_syncqt_arguments -debug) + endif() + + set(build_time_syncqt_arguments "") + if(WARNINGS_ARE_ERRORS) + if(is_interface_lib) + set(warnings_are_errors_enabled_genex 1) else() - # Copy the actual injected (generated) header file (not the just created forwarding one) - # to its install location when doing a prefix build. In an non-prefix build, the qt_install - # will be a no-op. - qt_path_join(install_destination - ${install_dir} ${INSTALL_INCLUDEDIR} - ${module_include_name} ${destinationdir}) - qt_install(FILES ${current_repo_build_dir}/${file} - DESTINATION ${install_destination} - RENAME ${destinationname} OPTIONAL) + set(warnings_are_errors_enabled_genex + "$<NOT:$<BOOL:$<TARGET_PROPERTY:${target},QT_SKIP_WARNINGS_ARE_ERRORS>>>") endif() + list(APPEND build_time_syncqt_arguments + "$<${warnings_are_errors_enabled_genex}:-warningsAreErrors>") + endif() - # Generate UpperCaseNamed forwarding headers (part 3). - foreach(fwd_hdr ${fwd_hdrs}) - set(upper_case_forwarding_header_path "include/${module_include_name}") - if(destinationdir) - string(APPEND upper_case_forwarding_header_path "/${destinationdir}") - endif() + if(is_framework) + list(REMOVE_ITEM module_headers "${CMAKE_CURRENT_BINARY_DIR}/${target}_fake_header.h") + endif() + + # Filter the generated ui_ header files and header files located in the 'doc/' subdirectory. + list(FILTER module_headers EXCLUDE REGEX + "(.+/(ui_)[^/]+\\.h|${CMAKE_CURRENT_SOURCE_DIR}(/.+)?/doc/+\\.h)") - # Generate upper case forwarding header like QVulkanFunctions or QtConfig. - qt_configure_file(OUTPUT "${build_dir}/${upper_case_forwarding_header_path}/${fwd_hdr}" - CONTENT "#include \"${destinationname}\"\n") - - if(is_framework) - # Copy the forwarding header to the framework's Headers directory. - qt_copy_framework_headers(${target} PUBLIC - "${build_dir}/${upper_case_forwarding_header_path}/${fwd_hdr}") - else() - # Install the forwarding header. - qt_path_join(install_destination "${install_dir}" "${INSTALL_INCLUDEDIR}" - ${module_include_name}) - qt_install(FILES "${build_dir}/${upper_case_forwarding_header_path}/${fwd_hdr}" - DESTINATION ${install_destination} OPTIONAL) + set(syncqt_staging_dir "${module_build_interface_include_dir}/.syncqt_staging") + + set(syncqt_args "${common_syncqt_arguments}") + list(APPEND syncqt_args + -headers ${module_headers} + -stagingDir "${syncqt_staging_dir}" + -knownModules ${known_modules} + ${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} + COMMAND + ${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 + ${syncqt_args_rsp} + ${module_headers} + ${QT_CMAKE_EXPORT_NAMESPACE}::syncqt + "$<GENEX_EVAL:$<TARGET_PROPERTY:${target},_qt_internal_sync_headers_deps>>" + COMMENT + "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} + ) + add_dependencies(sync_headers ${target}_sync_headers) + 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 + # ${target}_sync_all_public_headers is running after ${target}_sync_headers, when building docs. + set(syncqt_all_args "${common_syncqt_arguments};-all") + list(JOIN syncqt_all_args "\n" syncqt_all_args_string) + set(syncqt_all_args_rsp "${binary_dir_real}/${target}_syncqt_all_args") + qt_configure_file(OUTPUT "${syncqt_all_args_rsp}" CONTENT "${syncqt_all_args_string}") + add_custom_target(${target}_sync_all_public_headers + COMMAND + ${QT_CMAKE_EXPORT_NAMESPACE}::syncqt + "@${syncqt_all_args_rsp}" + ${external_headers_dir_copy_cmd} + DEPENDS + ${module_headers} + ${syncqt_all_args_rsp} + ${QT_CMAKE_EXPORT_NAMESPACE}::syncqt + ${target}_sync_headers + VERBATIM + ) + + if(NOT TARGET sync_all_public_headers) + add_custom_target(sync_all_public_headers) + endif() + add_dependencies(sync_all_public_headers ${target}_sync_all_public_headers) + + if(NOT is_3rd_party_library AND NOT is_framework AND module_headers) + # Install all the CaMeL style aliases of header files from the staging directory in one rule + qt_install(DIRECTORY "${syncqt_staging_dir}/" + DESTINATION "${module_install_interface_include_dir}" + ) + endif() + + if(NOT is_interface_lib) + set_property(TARGET ${target} + APPEND PROPERTY AUTOGEN_TARGET_DEPENDS "${target}_sync_headers") + endif() + add_dependencies(${target} "${target}_sync_headers") + + + get_target_property(private_module_target ${target} _qt_private_module_target_name) + if(private_module_target) + add_dependencies(${private_module_target} "${target}_sync_headers") + endif() + + # 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 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( + COMMAND + ${syncqt_location} + "@${syncqt_args_rsp}" + RESULT_VARIABLE syncqt_result + OUTPUT_VARIABLE syncqt_output + ERROR_VARIABLE syncqt_output + ) + if(NOT syncqt_result EQUAL 0) + if(syncqt_output STREQUAL "") + string(JOIN "" syncqt_output "The syncqt process exited with code ${syncqt_result}" + " and without any useful output. This can happen if syncqt crashes due to the" + " incompatibilities with the standard C++ library located by either PATH or" + " LD_LIBRARY_PATH environment variables. Please make sure that PATH or" + " LD_LIBRARY_PATH don't point to the standard libraries different from the one you" + " use for building Qt.") endif() - endforeach() - endforeach() + message(FATAL_ERROR + "syncqt.cpp failed for module ${module}:\n${syncqt_output}") + endif() + if(syncqt_output) + message(WARNING "${syncqt_output}") + endif() + set_property(GLOBAL APPEND PROPERTY _qt_synced_modules ${module}) + endif() endfunction() -function(qt_read_headers_pri module_include_dir resultVarPrefix) - file(STRINGS "${module_include_dir}/headers.pri" headers_pri_contents) - foreach(line ${headers_pri_contents}) - if("${line}" MATCHES "SYNCQT.HEADER_FILES = (.*)") - set(public_module_headers "${CMAKE_MATCH_1}") - separate_arguments(public_module_headers UNIX_COMMAND "${public_module_headers}") - elseif("${line}" MATCHES "SYNCQT.PRIVATE_HEADER_FILES = (.*)") - set(private_module_headers "${CMAKE_MATCH_1}") - separate_arguments(private_module_headers UNIX_COMMAND "${private_module_headers}") - elseif("${line}" MATCHES "SYNCQT.GENERATED_HEADER_FILES = (.*)") - set(generated_module_headers "${CMAKE_MATCH_1}") - separate_arguments(generated_module_headers UNIX_COMMAND "${generated_module_headers}") - foreach(generated_header ${generated_module_headers}) - list(APPEND public_module_headers "${module_include_dir}/${generated_header}") - endforeach() - elseif("${line}" MATCHES "SYNCQT.INJECTIONS = (.*)") - set(injections "${CMAKE_MATCH_1}") - elseif("${line}" MATCHES "SYNCQT.([A-Z_]+)_HEADER_FILES = (.+)") - set(prefix "${CMAKE_MATCH_1}") - string(TOLOWER "${prefix}" prefix) - set(entries "${CMAKE_MATCH_2}") - separate_arguments(entries UNIX_COMMAND "${entries}") - set("${resultVarPrefix}_${prefix}" "${entries}" PARENT_SCOPE) +function(qt_internal_collect_sync_header_dependencies out_var skip_non_existing) + list(LENGTH ARGN sync_headers_target_count) + if(sync_headers_target_count EQUAL 0) + message(FATAL_ERROR "Invalid use of qt_internal_collect_sync_header_dependencies," + " dependencies are not specified") + endif() + + set(${out_var} "") + foreach(sync_headers_target IN LISTS ARGN) + set(sync_headers_target "${sync_headers_target}_sync_headers") + if(NOT skip_non_existing OR TARGET ${sync_headers_target}) + list(APPEND ${out_var} ${sync_headers_target}) endif() endforeach() - set(${resultVarPrefix}_public "${public_module_headers}" PARENT_SCOPE) - set(${resultVarPrefix}_private "${private_module_headers}" PARENT_SCOPE) - set(${resultVarPrefix}_injections "${injections}" PARENT_SCOPE) -endfunction() - -function(qt_compute_injection_forwarding_header target) - qt_parse_all_arguments(arg "qt_compute_injection_forwarding_header" - "PRIVATE" "SOURCE;OUT_VAR" "" ${ARGN}) - qt_internal_module_info(module "${target}") - get_filename_component(file_name "${arg_SOURCE}" NAME) + list(REMOVE_DUPLICATES ${out_var}) - set(source_absolute_path "${CMAKE_CURRENT_BINARY_DIR}/${arg_SOURCE}") - file(RELATIVE_PATH relpath "${PROJECT_BINARY_DIR}" "${source_absolute_path}") + set(${out_var} "${${out_var}}" PARENT_SCOPE) +endfunction() - if (arg_PRIVATE) - set(fwd "${PROJECT_VERSION}/${module_include_name}/private/${file_name}") - else() - set(fwd "${file_name}") +function(qt_internal_add_sync_header_dependencies target) + qt_internal_collect_sync_header_dependencies(sync_headers_targets FALSE ${ARGN}) + if(sync_headers_targets) + add_dependencies(${target} ${sync_headers_targets}) endif() +endfunction() - string(APPEND ${arg_OUT_VAR} " ${relpath}:${fwd}") - set(${arg_OUT_VAR} ${${arg_OUT_VAR}} PARENT_SCOPE) +function(qt_internal_add_autogen_sync_header_dependencies target) + qt_internal_collect_sync_header_dependencies(sync_headers_targets TRUE ${ARGN}) + foreach(sync_headers_target IN LISTS sync_headers_targets) + set_property(TARGET ${target} APPEND PROPERTY AUTOGEN_TARGET_DEPENDS + "${sync_headers_target}") + endforeach() endfunction() diff --git a/cmake/QtTargetHelpers.cmake b/cmake/QtTargetHelpers.cmake index 1c9e1247b6..e669047ff1 100644 --- a/cmake/QtTargetHelpers.cmake +++ b/cmake/QtTargetHelpers.cmake @@ -1,44 +1,105 @@ # 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: BSD-3-Clause # This function can be used to add sources/libraries/etc. to the specified CMake target # if the provided CONDITION evaluates to true. +# One-value Arguments: +# PRECOMPILED_HEADER +# Name of the precompiled header that is used for the target. +# Multi-value Arguments: +# CONDITION +# The condition under which the target will be extended. +# CONDITION_INDEPENDENT_SOURCES +# Source files that should be added to the target unconditionally. Note that if target is Qt +# module, these files will raise a warning at configure time if the condition is not met. +# COMPILE_FLAGS +# 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 +# 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.") + endif() + qt_internal_is_skipped_test(skipped ${target}) + if(skipped) + return() + endif() + qt_internal_is_in_test_batch(in_batch ${target}) + if(in_batch) + _qt_internal_test_batch_target_name(target) + endif() + # Don't try to extend_target when cross compiling an imported host target (like a tool). qt_is_imported_target("${target}" is_imported) if(is_imported) return() endif() - if (NOT TARGET "${target}") - message(FATAL_ERROR "Trying to extend non-existing target \"${target}\".") - endif() - qt_parse_all_arguments(arg "qt_extend_target" "" "PRECOMPILED_HEADER" - "CONDITION;${__default_public_args};${__default_private_args};${__default_private_module_args};COMPILE_FLAGS;NO_PCH_SOURCES" ${ARGN}) - if ("x${arg_CONDITION}" STREQUAL x) + set(option_args + NO_UNITY_BUILD + ) + set(single_args + PRECOMPILED_HEADER + EXTRA_LINKER_SCRIPT_CONTENT + ) + set(multi_args + ${__default_public_args} + ${__default_private_args} + ${__default_private_module_args} + CONDITION + CONDITION_INDEPENDENT_SOURCES + COMPILE_FLAGS + EXTRA_LINKER_SCRIPT_EXPORTS + ) + + cmake_parse_arguments(PARSE_ARGV 1 arg + "${option_args}" + "${single_args}" + "${multi_args}" + ) + _qt_internal_validate_all_args_are_parsed(arg) + + if("x${arg_CONDITION}" STREQUAL "x") set(arg_CONDITION ON) endif() qt_evaluate_config_expression(result ${arg_CONDITION}) - if (${result}) + if(${result}) if(QT_CMAKE_DEBUG_EXTEND_TARGET) message("qt_extend_target(${target} CONDITION ${arg_CONDITION} ...): Evaluated") endif() set(dbus_sources "") foreach(adaptor ${arg_DBUS_ADAPTOR_SOURCES}) - qt_create_qdbusxml2cpp_command("${target}" "${adaptor}" ADAPTOR BASENAME "${arg_DBUS_ADAPTOR_BASENAME}" FLAGS "${arg_DBUS_ADAPTOR_FLAGS}") + qt_create_qdbusxml2cpp_command("${target}" "${adaptor}" + ADAPTOR + BASENAME "${arg_DBUS_ADAPTOR_BASENAME}" + FLAGS ${arg_DBUS_ADAPTOR_FLAGS} + ) list(APPEND dbus_sources "${adaptor}") endforeach() foreach(interface ${arg_DBUS_INTERFACE_SOURCES}) - qt_create_qdbusxml2cpp_command("${target}" "${interface}" INTERFACE BASENAME "${arg_DBUS_INTERFACE_BASENAME}" FLAGS "${arg_DBUS_INTERFACE_FLAGS}") + qt_create_qdbusxml2cpp_command("${target}" "${interface}" + INTERFACE + BASENAME "${arg_DBUS_INTERFACE_BASENAME}" + FLAGS ${arg_DBUS_INTERFACE_FLAGS} + ) list(APPEND dbus_sources "${interface}") endforeach() + set(all_sources + ${arg_SOURCES} + ${dbus_sources} + ) + get_target_property(target_type ${target} TYPE) set(is_library FALSE) set(is_interface_lib FALSE) - if (${target_type} STREQUAL "STATIC_LIBRARY" OR ${target_type} STREQUAL "SHARED_LIBRARY") + 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) @@ -55,8 +116,27 @@ function(qt_internal_extend_target target) if(NOT base_lib STREQUAL lib) qt_create_nolink_target("${base_lib}" ${target}) endif() + + # Collect _sync_headers targets from libraries that the target depends on. This is + # heuristic way of building the dependency tree between the _sync_headers targets of + # different Qt modules. + if(TARGET "${lib}") + 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 @@ -67,14 +147,14 @@ function(qt_internal_extend_target target) # functions. The property itself is not exported and should only be used in the Qt internal # build tree. if(NOT is_interface_lib OR CMAKE_VERSION VERSION_GREATER_EQUAL "3.19") - target_sources("${target}" PRIVATE ${arg_SOURCES} ${dbus_sources}) - if (arg_COMPILE_FLAGS) - set_source_files_properties(${arg_SOURCES} PROPERTIES + target_sources("${target}" PRIVATE ${all_sources}) + if(arg_COMPILE_FLAGS) + set_source_files_properties(${all_sources} PROPERTIES COMPILE_FLAGS "${arg_COMPILE_FLAGS}") endif() else() set_property(TARGET ${target} APPEND PROPERTY - _qt_internal_target_sources ${arg_SOURCES} ${dbus_sources}) + _qt_internal_target_sources ${all_sources}) endif() set(public_visibility_option "PUBLIC") @@ -86,6 +166,8 @@ function(qt_internal_extend_target target) target_include_directories("${target}" ${public_visibility_option} ${arg_PUBLIC_INCLUDE_DIRECTORIES} ${private_visibility_option} ${arg_INCLUDE_DIRECTORIES}) + target_include_directories("${target}" SYSTEM + ${private_visibility_option} ${arg_SYSTEM_INCLUDE_DIRECTORIES}) target_compile_definitions("${target}" ${public_visibility_option} ${arg_PUBLIC_DEFINES} ${private_visibility_option} ${arg_DEFINES}) @@ -100,7 +182,7 @@ function(qt_internal_extend_target target) ${private_visibility_option} ${arg_LINK_OPTIONS}) if(NOT is_interface_lib) - set_property (TARGET "${target}" APPEND PROPERTY + set_property(TARGET "${target}" APPEND PROPERTY AUTOMOC_MOC_OPTIONS "${arg_MOC_OPTIONS}" ) # Plugin types associated to a module @@ -154,15 +236,252 @@ function(qt_internal_extend_target target) DISABLE_AUTOGEN_TOOLS ${arg_DISABLE_AUTOGEN_TOOLS}) qt_update_precompiled_header("${target}" "${arg_PRECOMPILED_HEADER}") + ## Also exclude them from unity build qt_update_ignore_pch_source("${target}" "${arg_NO_PCH_SOURCES}") ## Ignore objective-c files for PCH (not supported atm) qt_ignore_pch_obj_c_sources("${target}" "${arg_SOURCES}") + if(arg_NO_UNITY_BUILD) + set_target_properties("${target}" PROPERTIES UNITY_BUILD OFF) + qt_update_ignore_unity_build_sources("${target}" "${arg_SOURCES}") + endif() + if(arg_NO_UNITY_BUILD_SOURCES) + qt_update_ignore_unity_build_sources("${target}" "${arg_NO_UNITY_BUILD_SOURCES}") + endif() else() if(QT_CMAKE_DEBUG_EXTEND_TARGET) message("qt_extend_target(${target} CONDITION ${arg_CONDITION} ...): Skipped") endif() endif() + + if(arg_CONDITION_INDEPENDENT_SOURCES) + set_source_files_properties(${arg_CONDITION_INDEPENDENT_SOURCES} PROPERTIES + _qt_extend_target_condition "${arg_CONDITION}" + SKIP_AUTOGEN TRUE + ) + + qt_internal_get_target_sources_property(sources_property) + set_property(TARGET ${target} APPEND PROPERTY + ${sources_property} "${arg_CONDITION_INDEPENDENT_SOURCES}") + endif() + + if(arg_EXTRA_LINKER_SCRIPT_CONTENT) + 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() +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}}" 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) @@ -235,20 +554,20 @@ endfunction() # On Windows, these properties are used to generate the version information resource. function(qt_set_target_info_properties target) cmake_parse_arguments(arg "" "${__default_target_info_args}" "" ${ARGN}) - if("${arg_TARGET_VERSION}" STREQUAL "") + if(NOT arg_TARGET_VERSION) set(arg_TARGET_VERSION "${PROJECT_VERSION}.0") endif() - if("${arg_TARGET_PRODUCT}" STREQUAL "") + if(NOT arg_TARGET_PRODUCT) set(arg_TARGET_PRODUCT "Qt6") endif() - if("${arg_TARGET_DESCRIPTION}" STREQUAL "") + if(NOT arg_TARGET_DESCRIPTION) set(arg_TARGET_DESCRIPTION "C++ Application Development Framework") endif() - if("${arg_TARGET_COMPANY}" STREQUAL "") + if(NOT arg_TARGET_COMPANY) set(arg_TARGET_COMPANY "The Qt Company Ltd.") endif() - if("${arg_TARGET_COPYRIGHT}" STREQUAL "") - set(arg_TARGET_COPYRIGHT "Copyright (C) 2022 The Qt Company Ltd.") + if(NOT arg_TARGET_COPYRIGHT) + set(arg_TARGET_COPYRIGHT "${QT_COPYRIGHT}") endif() set_target_properties(${target} PROPERTIES QT_TARGET_VERSION "${arg_TARGET_VERSION}" @@ -495,56 +814,139 @@ 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() set(properties_retrieved TRUE) + get_target_property(is_configure_time_target ${target} _qt_internal_configure_time_target) + if(is_configure_time_target) + # 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) + + 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_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\") + endif() + add_executable(${full_target} IMPORTED) + set_property(TARGET ${full_target} APPEND PROPERTY IMPORTED_CONFIGURATIONS ${default_cfg}) + set_target_properties(${full_target} PROPERTIES IMPORTED_LOCATION_${uc_default_cfg} + \"$\{_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() + endif() + # Non-prefix debug-and-release builds: add check for the existence of the debug binary of # 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) -if(NOT EXISTS \"$\\{_qt_imported_location}\") +if(NOT EXISTS \"$\{_qt_imported_location}\") get_target_property(_qt_imported_configs ${full_target} IMPORTED_CONFIGURATIONS) list(REMOVE_ITEM _qt_imported_configs DEBUG) - set_property(TARGET ${full_target} PROPERTY IMPORTED_CONFIGURATIONS $\\{_qt_imported_configs}) + set_property(TARGET ${full_target} PROPERTY IMPORTED_CONFIGURATIONS $\{_qt_imported_configs}) set_property(TARGET ${full_target} PROPERTY IMPORTED_LOCATION_DEBUG) -endif()\n\n") +endif()\n") endif() endif() 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) + elseif(WASM) + # Keep write_soname at FALSE 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") + 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") + 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) @@ -560,20 +962,32 @@ endif()\n\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}}\") + 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}) - set_property(TARGET ${full_target} PROPERTY IMPORTED_IMPLIB${property_suffix} \"$\\{_qt_imported_implib${var_suffix}}\") + set_property(TARGET ${full_target} PROPERTY IMPORTED_IMPLIB${property_suffix} \"$\{_qt_imported_implib${var_suffix}}\") endif()") endif() if(write_soname) string(APPEND content " if(_qt_imported_soname${var_suffix}) - set_property(TARGET ${full_target} PROPERTY IMPORTED_SONAME${property_suffix} \"$\\{_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") @@ -586,6 +1000,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() @@ -599,25 +1017,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() + + if("${arg_CONFIG_BUILD_DIR}" STREQUAL "") + message(FATAL_ERROR "CONFIG_BUILD_DIR is not specified") + 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_INSTALL_DIR}" STREQUAL "") + message(FATAL_ERROR "CONFIG_INSTALL_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_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) @@ -626,20 +1070,22 @@ function(qt_internal_create_tracepoints name tracepoints_file) set(header_filename "${provider_name}_tracepoints_p.h") set(header_path "${CMAKE_CURRENT_BINARY_DIR}/${header_filename}") - if(QT_FEATURE_lttng OR QT_FEATURE_etw) + if(QT_FEATURE_lttng OR QT_FEATURE_etw OR QT_FEATURE_ctf) set(source_path "${CMAKE_CURRENT_BINARY_DIR}/${provider_name}_tracepoints.cpp") qt_configure_file(OUTPUT "${source_path}" CONTENT "#define TRACEPOINT_CREATE_PROBES #define TRACEPOINT_DEFINE #include \"${header_filename}\"") target_sources(${name} PRIVATE "${source_path}") - target_compile_definitions(${name} PRIVATE Q_TRACEPOINT) + target_compile_definitions(${name} PUBLIC Q_TRACEPOINT) if(QT_FEATURE_lttng) set(tracegen_arg "lttng") target_link_libraries(${name} PRIVATE LTTng::UST) elseif(QT_FEATURE_etw) set(tracegen_arg "etw") + elseif(QT_FEATURE_ctf) + set(tracegen_arg "ctf") endif() if(NOT "${QT_HOST_PATH}" STREQUAL "") @@ -662,6 +1108,77 @@ function(qt_internal_create_tracepoints name tracepoints_file) endif() endfunction() +function(qt_internal_generate_tracepoints name provider) + cmake_parse_arguments(arg "" "" "SOURCES" ${ARGN} ) + set(provider_name ${provider}) + string(PREPEND provider_name "qt") + set(tracepoint_filename "${provider_name}.tracepoints") + set(tracepoints_path "${CMAKE_CURRENT_BINARY_DIR}/${tracepoint_filename}") + set(header_filename "${provider_name}_tracepoints_p.h") + set(header_path "${CMAKE_CURRENT_BINARY_DIR}/${header_filename}") + + if(QT_FEATURE_lttng OR QT_FEATURE_etw OR QT_FEATURE_ctf) + + set(absolute_file_paths "") + foreach(file IN LISTS arg_SOURCES) + get_filename_component(absolute_file ${file} ABSOLUTE) + list(APPEND absolute_file_paths ${absolute_file}) + endforeach() + + if(NOT "${QT_HOST_PATH}" STREQUAL "") + qt_path_join(tracepointgen + "${QT_HOST_PATH}" + "${QT${PROJECT_VERSION_MAJOR}_HOST_INFO_LIBEXECDIR}" + "tracepointgen") + else() + set(tracepointgen "${QT_CMAKE_EXPORT_NAMESPACE}::tracepointgen") + endif() + + add_custom_command(OUTPUT "${tracepoints_path}" + COMMAND ${tracepointgen} ${provider_name} "${tracepoints_path}" "I$<JOIN:$<TARGET_PROPERTY:${name},INCLUDE_DIRECTORIES>,;>" ${absolute_file_paths} + DEPENDS ${absolute_file_paths} + VERBATIM) + add_custom_target(${name}_${provider_name}_tracepoints_file DEPENDS "${tracepoints_path}") + add_dependencies(${name} ${name}_${provider_name}_tracepoints_file) + + set(source_path "${CMAKE_CURRENT_BINARY_DIR}/${provider_name}_tracepoints.cpp") + qt_configure_file(OUTPUT "${source_path}" + CONTENT "#define TRACEPOINT_CREATE_PROBES + #define TRACEPOINT_DEFINE + #include \"${header_filename}\"") + target_sources(${name} PRIVATE "${source_path}") + target_compile_definitions(${name} PUBLIC Q_TRACEPOINT) + + if(QT_FEATURE_lttng) + set(tracegen_arg "lttng") + target_link_libraries(${name} PRIVATE LTTng::UST) + elseif(QT_FEATURE_etw) + set(tracegen_arg "etw") + elseif(QT_FEATURE_ctf) + set(tracegen_arg "ctf") + endif() + + if(NOT "${QT_HOST_PATH}" STREQUAL "") + qt_path_join(tracegen + "${QT_HOST_PATH}" + "${QT${PROJECT_VERSION_MAJOR}_HOST_INFO_LIBEXECDIR}" + "tracegen") + else() + set(tracegen "${QT_CMAKE_EXPORT_NAMESPACE}::tracegen") + endif() + + get_filename_component(tracepoints_filepath "${tracepoints_path}" ABSOLUTE) + add_custom_command(OUTPUT "${header_path}" + COMMAND ${tracegen} ${tracegen_arg} "${tracepoints_filepath}" "${header_path}" + DEPENDS "${tracepoints_path}" + VERBATIM) + add_custom_target(${name}_${provider_name}_tracepoints_header DEPENDS "${header_path}") + add_dependencies(${name} ${name}_${provider_name}_tracepoints_header) + else() + qt_configure_file(OUTPUT "${header_path}" CONTENT "#include <private/qtrace_p.h>\n") + endif() +endfunction() + function(qt_internal_set_compile_pdb_names target) if(MSVC) get_target_property(target_type ${target} TYPE) @@ -819,6 +1336,33 @@ 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 +# ExternalProjects. +# Needed to create a ${repo}_src global target that examples can depend on in multi-config builds +# due to a bug in AUTOUIC. +# +# See QTBUG-110369. +function(qt_internal_skip_dependency_for_examples target) + set_target_properties(${target} PROPERTIES _qt_skip_dependency_for_examples TRUE) +endfunction() + +function(qt_internal_is_target_skipped_for_examples target out_var) + get_property(is_skipped TARGET ${target} PROPERTY _qt_skip_dependency_for_examples) + if(NOT is_skipped) + set(is_skipped FALSE) + endif() + set(${out_var} "${is_skipped}" PARENT_SCOPE) endfunction() function(qt_internal_link_internal_platform_for_object_library target) @@ -883,9 +1427,17 @@ endfunction() # The function disables one or multiple internal global definitions that are defined by the # qt_internal_add_global_definition function for a specific 'target'. function(qt_internal_undefine_global_definition target) - if(NOT TARGET ${target}) + if(NOT TARGET "${target}") message(FATAL_ERROR "${target} is not a target.") endif() + qt_internal_is_skipped_test(skipped ${target}) + if(skipped) + return() + endif() + qt_internal_is_in_test_batch(in_batch ${target}) + if(in_batch) + _qt_internal_test_batch_target_name(target) + endif() if("${ARGN}" STREQUAL "") message(FATAL_ERROR "The function expects at least one definition as an argument.") @@ -934,3 +1486,186 @@ function(qt_internal_get_target_sources_property out_var) endif() set(${out_var} "${${out_var}}" PARENT_SCOPE) endfunction() + +# This function collects target properties that contain generator expressions and needs to be +# exported. This function is needed since the CMake EXPORT_PROPERTIES property doesn't support +# properties that contain generator expressions. +# Usage: qt_internal_add_genex_properties_export(target properties...) +function(qt_internal_add_genex_properties_export target) + get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) + + set(config_check_begin "") + set(config_check_end "") + if(is_multi_config) + list(GET CMAKE_CONFIGURATION_TYPES 0 first_config_type) + + # The genex snippet is evaluated to '$<NOT:$<BOOL:$<CONFIG>>>' in the generated cmake file. + # The check is only applicable to the 'main' configuration. If user project doesn't use + # multi-config generator, then the check supposed to return true and the value from the + # 'main' configuration supposed to be used. + string(JOIN "" check_if_config_empty + "$<1:$><NOT:" + "$<1:$><BOOL:" + "$<1:$><CONFIG$<ANGLE-R>" + "$<ANGLE-R>" + "$<ANGLE-R>" + ) + + # The genex snippet is evaluated to '$<CONFIG:'Qt config type'>' in the generated cmake + # file and checks if the config that user uses matches the generated cmake file config. + string(JOIN "" check_user_config + "$<1:$><CONFIG:$<CONFIG>$<ANGLE-R>" + ) + + # The genex snippet is evaluated to '$<$<OR:$<CONFIG:'Qt config type'>>:'Property content'> + # for non-main Qt configs and to + # $<$<OR:$<CONFIG:'Qt config type'>,$<NOT:$<BOOL:$<CONFIG>>>>:'Property content'> for the + # main Qt config. This guard is required to choose the correct value of the property for the + # user project according to the user config type. + # All genexes need to be escaped properly to protect them from evaluation by the + # file(GENERATE call in the qt_internal_export_genex_properties function. + string(JOIN "" config_check_begin + "$<1:$><" + "$<1:$><OR:" + "${check_user_config}" + "$<$<CONFIG:${first_config_type}>:$<COMMA>${check_if_config_empty}>" + "$<ANGLE-R>:" + ) + set(config_check_end "$<ANGLE-R>") + endif() + set(target_name "${QT_CMAKE_EXPORT_NAMESPACE}::${target}") + foreach(property IN LISTS ARGN) + set(target_property_genex "$<TARGET_PROPERTY:${target_name},${property}>") + # All properties that contain lists need to be protected of processing by JOIN genex calls. + # So this escapes the semicolons for these list. + set(target_property_list_escape + "$<JOIN:$<GENEX_EVAL:${target_property_genex}>,\;>") + set(property_value + "\"${config_check_begin}${target_property_list_escape}${config_check_end}\"") + set_property(TARGET ${target} APPEND PROPERTY _qt_export_genex_properties_content + "${property} ${property_value}") + endforeach() +endfunction() + +# This function executes generator expressions for the properties that are added by the +# qt_internal_add_genex_properties_export function and sets the calculated values to the +# corresponding properties in the generated ExtraProperties.cmake file. The file then needs to be +# included after the target creation routines in Config.cmake files. It also supports Multi-Config +# builds. +# Arguments: +# EXPORT_NAME_PREFIX: +# The portion of the file name before ExtraProperties.cmake +# CONFIG_INSTALL_DIR: +# Installation location for the file. +# TARGETS: +# The internal target names. +function(qt_internal_export_genex_properties) + set(option_args "") + set(single_args + EXPORT_NAME_PREFIX + CONFIG_INSTALL_DIR + ) + set(multi_args TARGETS) + cmake_parse_arguments(arg "${option_args}" "${single_args}" "${multi_args}" ${ARGN}) + + if(NOT arg_EXPORT_NAME_PREFIX) + message(FATAL_ERROR "qt_internal_export_genex_properties: " + "Missing EXPORT_NAME_PREFIX argument.") + endif() + + if(NOT arg_TARGETS) + message(FATAL_ERROR "qt_internal_export_genex_properties: " + "TARGETS argument must contain at least one target") + endif() + + foreach(target IN LISTS arg_TARGETS) + get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) + + set(output_file_base_name "${arg_EXPORT_NAME_PREFIX}ExtraProperties") + set(should_append "") + set(config_suffix "") + if(is_multi_config) + list(GET CMAKE_CONFIGURATION_TYPES 0 first_config_type) + set(config_suffix "$<$<NOT:$<CONFIG:${first_config_type}>>:-$<CONFIG>>") + # If the generated file belongs to the 'main' config type, we should set property + # but not append it. + string(JOIN "" should_append + "$<$<NOT:$<CONFIG:${first_config_type}>>: APPEND>") + endif() + set(file_name "${output_file_base_name}${config_suffix}.cmake") + + qt_path_join(output_file "${arg_CONFIG_INSTALL_DIR}" + "${file_name}") + + if(NOT IS_ABSOLUTE "${output_file}") + qt_path_join(output_file "${QT_BUILD_DIR}" "${output_file}") + endif() + + set(target_name "${QT_CMAKE_EXPORT_NAMESPACE}::${target}") + + string(JOIN "" set_property_begin "set_property(TARGET " + "${target_name}${should_append} PROPERTY " + ) + set(set_property_end ")") + set(set_property_glue "${set_property_end}\n${set_property_begin}") + set(property_list + "$<GENEX_EVAL:$<TARGET_PROPERTY:${target},_qt_export_genex_properties_content>>") + string(JOIN "" set_property_content "${set_property_begin}" + "$<JOIN:${property_list},${set_property_glue}>" + "${set_property_end}") + + if(is_multi_config) + set(config_includes "") + foreach(config IN LISTS CMAKE_CONFIGURATION_TYPES) + if(NOT first_config_type STREQUAL config) + set(include_file_name + "${output_file_base_name}-${config}.cmake") + list(APPEND config_includes + "include(\"\${CMAKE_CURRENT_LIST_DIR}/${include_file_name}\")") + endif() + endforeach() + list(JOIN config_includes "\n" config_includes_string) + set(config_includes_string + "\n$<$<CONFIG:${first_config_type}>:${config_includes_string}>") + endif() + + file(GENERATE OUTPUT "${output_file}" + CONTENT "$<$<BOOL:${property_list}>:${set_property_content}${config_includes_string}>" + CONDITION "$<BOOL:${property_list}>" + ) + endforeach() + + qt_install(FILES "$<$<BOOL:${property_list}>:${output_file}>" + DESTINATION "${arg_CONFIG_INSTALL_DIR}" + 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 78f50a0a06..705de2f739 100644 --- a/cmake/QtTestHelpers.cmake +++ b/cmake/QtTestHelpers.cmake @@ -1,17 +1,21 @@ # 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: BSD-3-Clause # Simple wrapper around qt_internal_add_executable for benchmarks which insure that # the binary is built under ${CMAKE_CURRENT_BINARY_DIR} and never installed. # See qt_internal_add_executable() for more details. function(qt_internal_add_benchmark target) + if(QT_BUILD_TESTS_BATCHED) + message(WARNING "Benchmarks won't be batched - unsupported (yet)") + endif() - qt_parse_all_arguments(arg "qt_add_benchmark" + cmake_parse_arguments(PARSE_ARGV 1 arg "${__qt_internal_add_executable_optional_args}" "${__qt_internal_add_executable_single_args}" "${__qt_internal_add_executable_multi_args}" - ${ARGN} ) + _qt_internal_validate_all_args_are_parsed(arg) + _qt_internal_validate_no_unity_build(arg) qt_remove_args(exec_args ARGS_TO_REMOVE @@ -27,14 +31,25 @@ 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 OUTPUT_DIRECTORY "${arg_OUTPUT_DIRECTORY}" # avoid polluting bin directory ${exec_args} ) + qt_internal_extend_target(${target} + DEFINES + ${deprecation_define} + ) # Benchmarks on iOS must be app bundles. if(IOS) @@ -72,50 +87,18 @@ function(qt_internal_add_benchmark target) qt_internal_add_test_finalizers("${target}") endfunction() +function(qt_internal_add_test_dependencies target) + if(QT_BUILD_TESTS_BATCHED) + _qt_internal_test_batch_target_name(target) + endif() + add_dependencies(${target} ${ARGN}) +endfunction() + # Simple wrapper around qt_internal_add_executable for manual tests which insure that # the binary is built under ${CMAKE_CURRENT_BINARY_DIR} and never installed. # See qt_internal_add_executable() for more details. function(qt_internal_add_manual_test target) - - qt_parse_all_arguments(arg "qt_add_manual_test" - "${__qt_internal_add_executable_optional_args}" - "${__qt_internal_add_executable_single_args}" - "${__qt_internal_add_executable_multi_args}" - ${ARGN} - ) - - qt_remove_args(exec_args - ARGS_TO_REMOVE - ${target} - OUTPUT_DIRECTORY - INSTALL_DIRECTORY - ALL_ARGS - "${__qt_internal_add_executable_optional_args}" - "${__qt_internal_add_executable_single_args}" - "${__qt_internal_add_executable_multi_args}" - ARGS - ${ARGV} - ) - - if(NOT arg_OUTPUT_DIRECTORY) - set(arg_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") - endif() - - qt_internal_add_executable(${target} - NO_INSTALL # we don't install benchmarks - OUTPUT_DIRECTORY "${arg_OUTPUT_DIRECTORY}" # avoid polluting bin directory - ${exec_args} - ) - - # Tests on iOS must be app bundles. - if(IOS) - set_target_properties(${target} PROPERTIES MACOSX_BUNDLE TRUE) - endif() - - # Disable the QT_NO_NARROWING_CONVERSIONS_IN_CONNECT define for manual tests - qt_internal_undefine_global_definition(${target} QT_NO_NARROWING_CONVERSIONS_IN_CONNECT) - - qt_internal_add_test_finalizers("${target}") + qt_internal_add_test(${ARGV} MANUAL) endfunction() # This function will configure the fixture for the network tests that require docker network services @@ -126,6 +109,11 @@ function(qt_internal_setup_docker_test_fixture name) return() endif() + option(QT_SKIP_DOCKER_COMPOSE "Skip setting up docker on Linux." OFF) + if(QT_SKIP_DOCKER_COMPOSE) + return() + endif() + set(QT_TEST_SERVER_LIST ${ARGN}) set(DNSDOMAIN test-net.qt.local) @@ -192,20 +180,32 @@ function(qt_internal_setup_docker_test_fixture name) endfunction() -# This function creates a CMake test target with the specified name for use with CTest. -# -# All tests are wrapped with cmake script that supports TESTARGS and TESTRUNNER environment -# variables handling. Endpoint wrapper may be used standalone as cmake script to run tests e.g.: -# TESTARGS="-o result.xml,junitxml" TESTRUNNER="testrunner --arg" ./tst_simpleTestWrapper.cmake -# On non-UNIX machine you may need to use 'cmake -P' explicitly to execute wrapper. -# You may avoid test wrapping by either passing NO_WRAPPER option or switching QT_NO_TEST_WRAPPERS -# to ON. This is helpful if you want to use internal CMake tools within tests, like memory or -# sanitizer checks. See https://cmake.org/cmake/help/v3.19/manual/ctest.1.html#ctest-memcheck-step -# Arguments: -# BUILTIN_TESTDATA the option forces adding the provided TESTDATA to resources. -function(qt_internal_add_test name) - # EXCEPTIONS is a noop as they are enabled by default. - set(optional_args +function(qt_internal_get_test_batch out) + get_property(batched_list GLOBAL PROPERTY _qt_batched_test_list_property) + set(${out} ${batched_list} PARENT_SCOPE) +endfunction() + +function(qt_internal_prepare_test_target_flags version_arg exceptions_text gui_text) + cmake_parse_arguments(arg "EXCEPTIONS;NO_EXCEPTIONS;GUI" "VERSION" "" ${ARGN}) + + if (arg_VERSION) + set(${version_arg} VERSION "${arg_VERSION}" PARENT_SCOPE) + endif() + + # Qt modules get compiled without exceptions enabled by default. + # However, testcases should be still built with exceptions. + set(${exceptions_text} "EXCEPTIONS" PARENT_SCOPE) + if (${arg_NO_EXCEPTIONS} OR WASM) + set(${exceptions_text} "" PARENT_SCOPE) + endif() + + if (${arg_GUI}) + set(${gui_text} "GUI" PARENT_SCOPE) + endif() +endfunction() + +function(qt_internal_get_test_arg_definitions optional_args single_value_args multi_value_args) + set(${optional_args} RUN_SERIAL EXCEPTIONS NO_EXCEPTIONS @@ -215,45 +215,287 @@ function(qt_internal_add_test name) LOWDPI NO_WRAPPER BUILTIN_TESTDATA + MANUAL + NO_BATCH + NO_INSTALL + BUNDLE_ANDROID_OPENSSL_LIBS + PARENT_SCOPE ) - set(single_value_args + set(${single_value_args} OUTPUT_DIRECTORY WORKING_DIRECTORY TIMEOUT VERSION + PARENT_SCOPE ) - set(multi_value_args + set(${multi_value_args} QML_IMPORTPATH TESTDATA QT_TEST_SERVER_LIST ${__default_private_args} ${__default_public_args} + PARENT_SCOPE ) - qt_parse_all_arguments(arg "qt_add_test" +endfunction() + +function(qt_internal_add_test_to_batch batch_name name) + qt_internal_get_test_arg_definitions(optional_args single_value_args multi_value_args) + + cmake_parse_arguments( + arg "${optional_args}" "${single_value_args}" "${multi_value_args}" ${ARGN}) + qt_internal_prepare_test_target_flags(version_arg exceptions_text gui_text ${ARGN}) + + _qt_internal_validate_no_unity_build(arg) + + _qt_internal_test_batch_target_name(target) + + # 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} + ${version_arg} + NO_INSTALL + OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/build_dir" + SOURCES "${QT_CMAKE_DIR}/qbatchedtestrunner.in.cpp" + DEFINES QTEST_BATCH_TESTS ${deprecation_define} + INCLUDE_DIRECTORIES ${private_includes} + LIBRARIES ${QT_CMAKE_EXPORT_NAMESPACE}::Core + ${QT_CMAKE_EXPORT_NAMESPACE}::Test + ${QT_CMAKE_EXPORT_NAMESPACE}::TestPrivate + # Add GUI by default so that the plugins link properly with non-standalone + # build of tests. Plugin handling is currently only done in + # qt_internal_add_executable if Gui is present. This should be reevaluated with + # multiple batches. + ${QT_CMAKE_EXPORT_NAMESPACE}::Gui + ) + + set_property(TARGET ${target} PROPERTY _qt_has_exceptions ${arg_EXCEPTIONS}) + set_property(TARGET ${target} PROPERTY _qt_has_gui ${arg_GUI}) + set_property(TARGET ${target} PROPERTY _qt_has_lowdpi ${arg_LOWDPI}) + set_property(TARGET ${target} PROPERTY _qt_version ${version_arg}) + set_property(TARGET ${target} PROPERTY _qt_is_test_executable TRUE) + set_property(TARGET ${target} PROPERTY _qt_is_manual_test ${arg_MANUAL}) + else() + # Check whether the args match with the batch. Some differences between + # flags cannot be reconciled - one should not combine these tests into + # a single binary. + qt_internal_get_target_property( + batch_has_exceptions ${target} _qt_has_exceptions) + if(NOT ${batch_has_exceptions} STREQUAL ${arg_EXCEPTIONS}) + qt_internal_get_test_batch(test_batch_contents) + message(FATAL_ERROR "Conflicting exceptions declaration between test \ + batch (${test_batch_contents}) and ${name}") + endif() + qt_internal_get_target_property(batch_has_gui ${target} _qt_has_gui) + if(NOT ${batch_has_gui} STREQUAL ${arg_GUI}) + qt_internal_get_test_batch(test_batch_contents) + message(FATAL_ERROR "Conflicting gui declaration between test batch \ + (${test_batch_contents}) and ${name}") + endif() + qt_internal_get_target_property( + batch_has_lowdpi ${target} _qt_has_lowdpi) + if(NOT ${batch_has_lowdpi} STREQUAL ${arg_LOWDPI}) + qt_internal_get_test_batch(test_batch_contents) + message(FATAL_ERROR "Conflicting lowdpi declaration between test batch \ + (${test_batch_contents}) and ${name}") + endif() + qt_internal_get_target_property(batch_version ${target} _qt_version) + if(NOT "${batch_version} " STREQUAL " " AND + NOT "${version_arg} " STREQUAL " " AND + NOT "${batch_version} " STREQUAL "${version_arg} ") + qt_internal_get_test_batch(test_batch_contents) + message(FATAL_ERROR "Conflicting version declaration between test \ + batch ${test_batch_contents} (${batch_version}) and ${name} (${version_arg})") + endif() + endif() + + get_property(batched_test_list GLOBAL PROPERTY _qt_batched_test_list_property) + if(NOT batched_test_list) + set_property(GLOBAL PROPERTY _qt_batched_test_list_property "") + set(batched_test_list "") + endif() + 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} + PUBLIC_LIBRARIES ${arg_PUBLIC_LIBRARIES} + LIBRARIES ${arg_LIBRARIES} + SOURCES ${arg_SOURCES} + COMPILE_OPTIONS ${arg_COMPILE_OPTIONS} + COMPILE_FLAGS ${arg_COMPILE_FLAGS} + LINK_OPTIONS ${arg_LINK_OPTIONS} + MOC_OPTIONS ${arg_MOC_OPTIONS} + ENABLE_AUTOGEN_TOOLS ${arg_ENABLE_AUTOGEN_TOOLS} + DISABLE_AUTOGEN_TOOLS ${arg_DISABLE_AUTOGEN_TOOLS} + NO_UNITY_BUILD # Tests should not be built using UNITY_BUILD + ) + + set(${batch_name} ${target} PARENT_SCOPE) + + # Add a dummy target so that new tests don't have problems with a nonexistent + # target when calling cmake functions. + # The batch tests that include this target will compile, but may fail to work. + # Manual action is required then. + add_custom_target(${name}) + + # Add the dependency to the dummy target so that it is indirectly added to the test batch + # dependencies. + add_dependencies(${target} ${name}) +endfunction() + +# Checks whether the test 'name' is present in the test batch. See QT_BUILD_TESTS_BATCHED. +# The result of the check is placed in the 'out' variable. +function(qt_internal_is_in_test_batch out name) + set(${out} FALSE PARENT_SCOPE) + if(QT_BUILD_TESTS_BATCHED) + get_property(batched_test_list GLOBAL PROPERTY _qt_batched_test_list_property) + if("${name}" IN_LIST batched_test_list) + set(${out} TRUE PARENT_SCOPE) + endif() + endif() +endfunction() + +function(qt_internal_is_skipped_test out name) + get_target_property(is_skipped_test ${name} _qt_is_skipped_test) + set(${out} ${is_skipped_test} PARENT_SCOPE) +endfunction() + +function(qt_internal_set_skipped_test name) + set_target_properties(${name} PROPERTIES _qt_is_skipped_test TRUE) +endfunction() + +function(qt_internal_is_qtbase_test out) + get_filename_component(dir "${CMAKE_CURRENT_BINARY_DIR}" ABSOLUTE) + set(${out} FALSE PARENT_SCOPE) + + while(TRUE) + get_filename_component(filename "${dir}" NAME) + if("${filename}" STREQUAL "qtbase") + set(${out} TRUE PARENT_SCOPE) + break() + endif() + + set(prev_dir "${dir}") + get_filename_component(dir "${dir}" DIRECTORY) + if("${dir}" STREQUAL "${prev_dir}") + break() + endif() + endwhile() +endfunction() + +function(qt_internal_get_batched_test_arguments out testname) + if(WASM) + # Add a query string to the runner document, so that the script therein + # knows which test to run in response to launching the testcase by ctest. + list(APPEND args "qbatchedtest") + if(CMAKE_BUILD_TYPE STREQUAL "Debug") + list(APPEND args "qvisualoutput") + endif() + else() + # Simply add the test name in case of standard executables. + list(APPEND args "${testname}") + endif() + set(${out} ${args} PARENT_SCOPE) +endfunction() + +# This function creates a CMake test target with the specified name for use with CTest. +# +# All tests are wrapped with cmake script that supports TESTARGS and TESTRUNNER environment +# variables handling. Endpoint wrapper may be used standalone as cmake script to run tests e.g.: +# TESTARGS="-o result.xml,junitxml" TESTRUNNER="testrunner --arg" ./tst_simpleTestWrapper.cmake +# On non-UNIX machine you may need to use 'cmake -P' explicitly to execute wrapper. +# You may avoid test wrapping by either passing NO_WRAPPER option or switching QT_NO_TEST_WRAPPERS +# to ON. This is helpful if you want to use internal CMake tools within tests, like memory or +# sanitizer checks. See https://cmake.org/cmake/help/v3.19/manual/ctest.1.html#ctest-memcheck-step +# Arguments: +# BUILTIN_TESTDATA +# The option forces adding the provided TESTDATA to resources. +# MANUAL +# The option indicates that the test is a manual test. +function(qt_internal_add_test name) + qt_internal_get_test_arg_definitions(optional_args single_value_args multi_value_args) + + cmake_parse_arguments(PARSE_ARGV 1 arg "${optional_args}" "${single_value_args}" "${multi_value_args}" - ${ARGN} ) - - if (NOT arg_OUTPUT_DIRECTORY) - set(arg_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") + _qt_internal_validate_all_args_are_parsed(arg) + _qt_internal_validate_no_unity_build(arg) + + set(batch_current_test FALSE) + if(QT_BUILD_TESTS_BATCHED AND NOT arg_NO_BATCH AND NOT arg_QMLTEST AND NOT arg_MANUAL + AND ("${QT_STANDALONE_TEST_PATH}" STREQUAL "" + OR DEFINED ENV{QT_BATCH_STANDALONE_TESTS})) + set(batch_current_test TRUE) endif() - # Qt modules get compiled without exceptions enabled by default. - # However, testcases should be still built with exceptions. - set(exceptions_text "EXCEPTIONS") - if (${arg_NO_EXCEPTIONS}) - set(exceptions_text "") + if(batch_current_test OR (QT_BUILD_TESTS_BATCHED AND arg_QMLTEST)) + if (QT_SUPERBUILD OR DEFINED ENV{TESTED_MODULE_COIN}) + set(is_qtbase_test FALSE) + if(QT_SUPERBUILD) + qt_internal_is_qtbase_test(is_qtbase_test) + elseif($ENV{TESTED_MODULE_COIN} STREQUAL "qtbase") + set(is_qtbase_test TRUE) + endif() + if(NOT is_qtbase_test) + file(GENERATE OUTPUT "dummy${name}.cpp" CONTENT "int main() { return 0; }") + # Add a dummy target to tackle some potential problems + qt_internal_add_executable(${name} SOURCES "dummy${name}.cpp") + # Batched tests outside of qtbase are unsupported and skipped + qt_internal_set_skipped_test(${name}) + return() + endif() + endif() endif() - if (${arg_GUI}) - set(gui_text "GUI") + 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() - if (arg_VERSION) - set(version_arg VERSION "${arg_VERSION}") - endif() + set(private_includes + "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_BINARY_DIR}" + "$<BUILD_INTERFACE:${QT_BUILD_DIR}/include>" + ) + + set(testname "${name}") if(arg_PUBLIC_LIBRARIES) message(WARNING @@ -261,14 +503,17 @@ function(qt_internal_add_test name) "removed in a future Qt version. Use the LIBRARIES option instead.") endif() - # Handle cases where we have a qml test without source files - if (arg_SOURCES) - set(private_includes - "${CMAKE_CURRENT_SOURCE_DIR}" - "${CMAKE_CURRENT_BINARY_DIR}" - "$<BUILD_INTERFACE:${QT_BUILD_DIR}/include>" - ${arg_INCLUDE_DIRECTORIES} - ) + if(batch_current_test) + qt_internal_add_test_to_batch(name ${name} ${ARGN}) + elseif(arg_SOURCES) + if(QT_BUILD_TESTS_BATCHED AND arg_QMLTEST) + message(WARNING "QML tests won't be batched - unsupported (yet)") + endif() + # Handle cases where we have a qml test without source files + 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} @@ -281,6 +526,7 @@ function(qt_internal_add_test name) ${private_includes} DEFINES ${arg_DEFINES} + ${deprecation_define} LIBRARIES ${arg_LIBRARIES} ${arg_PUBLIC_LIBRARIES} @@ -291,6 +537,7 @@ function(qt_internal_add_test name) MOC_OPTIONS ${arg_MOC_OPTIONS} ENABLE_AUTOGEN_TOOLS ${arg_ENABLE_AUTOGEN_TOOLS} DISABLE_AUTOGEN_TOOLS ${arg_DISABLE_AUTOGEN_TOOLS} + NO_UNITY_BUILD # Tests should not be built using UNITY_BUILD ) qt_internal_add_repo_local_defines(${name}) @@ -298,13 +545,16 @@ function(qt_internal_add_test name) # Disable the QT_NO_NARROWING_CONVERSIONS_IN_CONNECT define for tests qt_internal_undefine_global_definition(${name} QT_NO_NARROWING_CONVERSIONS_IN_CONNECT) - # Tests should not be bundles on macOS even if arg_GUI is true, because some tests make - # assumptions about the location of helper processes, and those paths would be different - # if a test is built as a bundle. - set_property(TARGET "${name}" PROPERTY MACOSX_BUNDLE FALSE) - # The same goes for WIN32_EXECUTABLE, but because it will detach from the console window - # and not print anything. - set_property(TARGET "${name}" PROPERTY WIN32_EXECUTABLE FALSE) + # Manual tests can be bundle apps + if(NOT arg_MANUAL) + # Tests should not be bundles on macOS even if arg_GUI is true, because some tests make + # assumptions about the location of helper processes, and those paths would be different + # if a test is built as a bundle. + set_property(TARGET "${name}" PROPERTY MACOSX_BUNDLE FALSE) + # The same goes for WIN32_EXECUTABLE, but because it will detach from the console window + # and not print anything. + set_property(TARGET "${name}" PROPERTY WIN32_EXECUTABLE FALSE) + endif() # Tests on iOS must be app bundles. if(IOS) @@ -330,6 +580,13 @@ function(qt_internal_add_test name) qt_internal_extend_target("${name}" CONDITION ANDROID LIBRARIES ${QT_CMAKE_EXPORT_NAMESPACE}::Gui ) + 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) @@ -355,23 +612,80 @@ 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 "") set(test_executable "${name}") elseif(WASM) - # Test script expects html file - set(test_executable "${name}.html") + # The test script expects an html file. In case of batched tests, the + # version specialized for running batches has to be supplied. + if(batch_current_test) + get_target_property(batch_output_dir ${name} RUNTIME_OUTPUT_DIRECTORY) + set(test_executable "${batch_output_dir}/${name}.html") + else() + set(test_executable "${name}.html") + endif() - if(QT6_INSTALL_PREFIX) - set(QT_WASM_TESTRUNNER "${QT6_INSTALL_PREFIX}/${INSTALL_LIBEXECDIR}/qt-wasmtestrunner.py") - elseif(QT_BUILD_DIR) - set(QT_WASM_TESTRUNNER "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/qt-wasmtestrunner.py") + list(APPEND extra_test_args "quseemrun") + list(APPEND extra_test_args "qtestname=${testname}") + list(APPEND extra_test_args "--silence_timeout=60") + # TODO: Add functionality to specify browser + list(APPEND extra_test_args "--browser=chrome") + list(APPEND extra_test_args "--browser_args=\"--password-store=basic\"") + list(APPEND extra_test_args "--kill_exit") + + # 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 + 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 "${QT_WASM_TESTRUNNER}") + 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}") @@ -388,58 +702,79 @@ function(qt_internal_add_test name) endif() endif() - qt_internal_collect_command_environment(test_env_path test_env_plugin_path) + if(NOT arg_MANUAL) + if(batch_current_test) + qt_internal_get_batched_test_arguments(batched_test_args ${testname}) + list(PREPEND extra_test_args ${batched_test_args}) + elseif(WASM AND CMAKE_BUILD_TYPE STREQUAL "Debug") + list(PREPEND extra_test_args "qvisualoutput") + endif() - if(arg_NO_WRAPPER OR QT_NO_TEST_WRAPPERS) - add_test(NAME "${name}" COMMAND ${test_executable} ${extra_test_args} - WORKING_DIRECTORY "${test_working_dir}") - set_property(TEST "${name}" APPEND PROPERTY - ENVIRONMENT "PATH=${test_env_path}" - "QT_TEST_RUNNING_IN_CTEST=1" - "QT_PLUGIN_PATH=${test_env_plugin_path}" - ) - else() - set(test_wrapper_file "${CMAKE_CURRENT_BINARY_DIR}/${name}Wrapper$<CONFIG>.cmake") - qt_internal_create_test_script(NAME "${name}" - COMMAND "${test_executable}" - ARGS "${extra_test_args}" - WORKING_DIRECTORY "${test_working_dir}" - OUTPUT_FILE "${test_wrapper_file}" - ENVIRONMENT "QT_TEST_RUNNING_IN_CTEST" 1 - "PATH" "${test_env_path}" - "QT_PLUGIN_PATH" "${test_env_plugin_path}" - ) - endif() + qt_internal_collect_command_environment(test_env_path test_env_plugin_path) - if(arg_QT_TEST_SERVER_LIST AND NOT ANDROID) - qt_internal_setup_docker_test_fixture(${name} ${arg_QT_TEST_SERVER_LIST}) - endif() + if(arg_NO_WRAPPER OR QT_NO_TEST_WRAPPERS) + if(QT_BUILD_TESTS_BATCHED) + message(FATAL_ERROR "Wrapperless tests are unspupported with test batching") + endif() - set_tests_properties("${name}" PROPERTIES RUN_SERIAL "${arg_RUN_SERIAL}" LABELS "${label}") - if (arg_TIMEOUT) - set_tests_properties(${name} PROPERTIES TIMEOUT ${arg_TIMEOUT}) - endif() + add_test(NAME "${testname}" COMMAND ${test_executable} ${extra_test_args} + WORKING_DIRECTORY "${test_working_dir}") + set_property(TEST "${testname}" APPEND PROPERTY + ENVIRONMENT "PATH=${test_env_path}" + "QT_TEST_RUNNING_IN_CTEST=1" + "QT_PLUGIN_PATH=${test_env_plugin_path}" + ) + else() + set(test_wrapper_file "${CMAKE_CURRENT_BINARY_DIR}/${testname}Wrapper$<CONFIG>.cmake") + qt_internal_create_test_script(NAME "${testname}" + COMMAND "${test_executable}" + ARGS "${extra_test_args}" + WORKING_DIRECTORY "${test_working_dir}" + OUTPUT_FILE "${test_wrapper_file}" + ENVIRONMENT "QT_TEST_RUNNING_IN_CTEST" 1 + "PATH" "${test_env_path}" + "QT_PLUGIN_PATH" "${test_env_plugin_path}" + ) + endif() - # Add a ${target}/check makefile target, to more easily test one test. + if(arg_QT_TEST_SERVER_LIST AND NOT ANDROID) + qt_internal_setup_docker_test_fixture(${testname} ${arg_QT_TEST_SERVER_LIST}) + endif() - set(test_config_options "") - get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) - if(is_multi_config) - set(test_config_options -C $<CONFIG>) - endif() - add_custom_target("${name}_check" - VERBATIM - COMMENT "Running ${CMAKE_CTEST_COMMAND} -V -R \"^${name}$\" ${test_config_options}" - COMMAND "${CMAKE_CTEST_COMMAND}" -V -R "^${name}$" ${test_config_options} - ) - if(TARGET "${name}") - add_dependencies("${name}_check" "${name}") - if(ANDROID) - add_dependencies("${name}_check" "${name}_make_apk") + set_tests_properties("${testname}" PROPERTIES RUN_SERIAL "${arg_RUN_SERIAL}" LABELS "${label}") + if(arg_TIMEOUT) + 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 "") + get_cmake_property(is_multi_config GENERATOR_IS_MULTI_CONFIG) + if(is_multi_config) + set(test_config_options -C $<CONFIG>) + endif() + add_custom_target("${testname}_check" + VERBATIM + COMMENT "Running ${CMAKE_CTEST_COMMAND} -V -R \"^${name}$\" ${test_config_options}" + COMMAND "${CMAKE_CTEST_COMMAND}" -V -R "^${name}$" ${test_config_options} + ) + if(TARGET "${name}") + add_dependencies("${testname}_check" "${name}") + if(ANDROID) + add_dependencies("${testname}_check" "${name}_make_apk") + endif() endif() endif() - if(ANDROID OR IOS OR INTEGRITY OR arg_BUILTIN_TESTDATA) + if(ANDROID OR IOS OR WASM OR INTEGRITY OR arg_BUILTIN_TESTDATA) set(builtin_testdata TRUE) endif() @@ -451,10 +786,29 @@ function(qt_internal_add_test name) foreach(testdata IN LISTS arg_TESTDATA) list(APPEND builtin_files ${testdata}) endforeach() + foreach(file IN LISTS builtin_files) + set_source_files_properties(${file} + PROPERTIES QT_SKIP_QUICKCOMPILER TRUE + ) + endforeach() - set(blacklist_path "BLACKLIST") - if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${blacklist_path}") - list(APPEND builtin_files ${blacklist_path}) + if(batch_current_test) + set(blacklist_path "BLACKLIST") + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${blacklist_path}") + get_target_property(blacklist_files ${name} _qt_blacklist_files) + if(NOT blacklist_files) + set_target_properties(${name} PROPERTIES _qt_blacklist_files "") + set(blacklist_files "") + cmake_language(EVAL CODE "cmake_language(DEFER DIRECTORY \"${CMAKE_SOURCE_DIR}\" CALL \"_qt_internal_finalize_batch\" \"${name}\") ") + endif() + list(PREPEND blacklist_files "${CMAKE_CURRENT_SOURCE_DIR}/${blacklist_path}") + set_target_properties(${name} PROPERTIES _qt_blacklist_files "${blacklist_files}") + endif() + else() + set(blacklist_path "BLACKLIST") + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${blacklist_path}") + list(APPEND builtin_files ${blacklist_path}) + endif() endif() list(REMOVE_DUPLICATES builtin_files) @@ -466,23 +820,35 @@ function(qt_internal_add_test name) ) endforeach() - if (builtin_files) - qt_internal_add_resource(${name} "${name}_testdata_builtin" + if(builtin_files) + qt_internal_add_resource(${name} "${testname}_testdata_builtin" PREFIX "/" FILES ${builtin_files} BASE ${CMAKE_CURRENT_SOURCE_DIR}) 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}" @@ -499,6 +865,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. # @@ -551,6 +946,11 @@ for this function. Will be ignored") set(executable_file "${arg_COMMAND}") endif() + set(executable_name ${arg_NAME}) + qt_internal_is_in_test_batch(is_in_batch ${executable_name}) + if(is_in_batch) + _qt_internal_test_batch_target_name(executable_name) + endif() add_test(NAME "${arg_NAME}" COMMAND "${CMAKE_COMMAND}" "-P" "${arg_OUTPUT_FILE}" WORKING_DIRECTORY "${arg_WORKING_DIRECTORY}") @@ -559,8 +959,8 @@ for this function. Will be ignored") # CROSSCOMPILING_EMULATOR don't check if actual cross compilation is configured, # emulator is prepended independently. set(crosscompiling_emulator "") - if(CMAKE_CROSSCOMPILING AND TARGET ${arg_NAME}) - get_target_property(crosscompiling_emulator ${arg_NAME} CROSSCOMPILING_EMULATOR) + if(CMAKE_CROSSCOMPILING AND TARGET ${executable_name}) + get_target_property(crosscompiling_emulator ${executable_name} CROSSCOMPILING_EMULATOR) if(NOT crosscompiling_emulator) set(crosscompiling_emulator "") else() @@ -592,11 +992,12 @@ function(qt_internal_add_test_helper name) "OVERRIDE_OUTPUT_DIRECTORY" ) - qt_parse_all_arguments(arg "qt_add_test_helper" + cmake_parse_arguments(PARSE_ARGV 1 arg "${qt_add_test_helper_optional_args};${__qt_internal_add_executable_optional_args}" "${__qt_internal_add_executable_single_args}" "${__qt_internal_add_executable_multi_args}" - ${ARGN}) + ) + _qt_internal_validate_all_args_are_parsed(arg) qt_remove_args(forward_args ARGS_TO_REMOVE @@ -613,10 +1014,19 @@ function(qt_internal_add_test_helper name) set(extra_args_to_pass) if(NOT arg_OVERRIDE_OUTPUT_DIRECTORY) - set(extra_args_to_pass OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/..") + _qt_internal_test_batch_target_name(test_batch_target_name) + if(QT_BUILD_TESTS_BATCHED AND TARGET ${test_batch_target_name}) + get_target_property( + test_batch_output_dir ${test_batch_target_name} RUNTIME_OUTPUT_DIRECTORY) + set(extra_args_to_pass OUTPUT_DIRECTORY "${test_batch_output_dir}") + else() + set(extra_args_to_pass OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/..") + endif() endif() - qt_internal_add_executable("${name}" NO_INSTALL ${extra_args_to_pass} ${forward_args}) + qt_internal_add_executable("${name}" NO_INSTALL + NO_UNITY_BUILD # excluded by default + ${extra_args_to_pass} ${forward_args}) # Disable the QT_NO_NARROWING_CONVERSIONS_IN_CONNECT define for test helpers qt_internal_undefine_global_definition(${name} QT_NO_NARROWING_CONVERSIONS_IN_CONNECT) diff --git a/cmake/QtToolHelpers.cmake b/cmake/QtToolHelpers.cmake index 421235789c..7dd507c0ee 100644 --- a/cmake/QtToolHelpers.cmake +++ b/cmake/QtToolHelpers.cmake @@ -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: BSD-3-Clause # This function is used to define a "Qt tool", such as moc, uic or rcc. # @@ -15,6 +15,13 @@ # INSTALL_VERSIONED_LINK # Prefix build only. On installation, create a versioned hard-link of the installed file. # E.g. create a link of "bin/qmake6" to "bin/qmake". +# 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. 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 @@ -37,159 +44,35 @@ # linked to the executable target by default. function(qt_internal_add_tool target_name) qt_tool_target_to_name(name ${target_name}) - set(option_keywords NO_INSTALL USER_FACING INSTALL_VERSIONED_LINK EXCEPTIONS) + set(option_keywords + NO_INSTALL + USER_FACING + INSTALL_VERSIONED_LINK + EXCEPTIONS + NO_UNITY_BUILD + TRY_RUN) set(one_value_keywords TOOLS_TARGET INSTALL_DIR CORE_LIBRARY + TRY_RUN_FLAGS ${__default_target_info_args}) set(multi_value_keywords EXTRA_CMAKE_FILES EXTRA_CMAKE_INCLUDES PUBLIC_LIBRARIES ${__default_private_args}) - qt_parse_all_arguments(arg "qt_internal_add_tool" "${option_keywords}" - "${one_value_keywords}" - "${multi_value_keywords}" ${ARGN}) - - # Handle case when a tool does not belong to a module and it can't be built either (like - # during a cross-compile). - if(NOT arg_TOOLS_TARGET AND NOT QT_WILL_BUILD_TOOLS) - message(FATAL_ERROR "The tool \"${name}\" has not been assigned to a module via" - " TOOLS_TARGET (so it can't be found) and it can't be built" - " (QT_WILL_BUILD_TOOLS is ${QT_WILL_BUILD_TOOLS}).") - 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.") - endif() - - set(full_name "${QT_CMAKE_EXPORT_NAMESPACE}::${name}") - set(imported_tool_target_already_found FALSE) - - # This condition can only be TRUE if a previous find_package(Qt6${arg_TOOLS_TARGET}Tools) - # was already done. That can happen if QT_FORCE_FIND_TOOLS was ON or we're cross-compiling. - # In such a case, we need to exit early if we're not going to also build the tools. - if(TARGET ${full_name}) - get_property(path TARGET ${full_name} PROPERTY LOCATION) - message(STATUS "Tool '${full_name}' was found at ${path}.") - set(imported_tool_target_already_found TRUE) - if(NOT QT_WILL_BUILD_TOOLS) - return() - endif() - endif() - - # We need to search for the host Tools package when doing a cross-build - # or when QT_FORCE_FIND_TOOLS is ON. - # As an optimiziation, we don't search for the package one more time if the target - # was already brought into scope from a previous find_package. - set(search_for_host_package FALSE) - if(NOT QT_WILL_BUILD_TOOLS OR QT_WILL_RENAME_TOOL_TARGETS) - set(search_for_host_package TRUE) - endif() - if(search_for_host_package AND NOT imported_tool_target_already_found) - set(tools_package_name "Qt6${arg_TOOLS_TARGET}Tools") - message(STATUS "Searching for tool '${full_name}' in package ${tools_package_name}.") - - # Create the tool targets, even if QT_NO_CREATE_TARGETS is set. - # Otherwise targets like Qt6::moc are not available in a top-level cross-build. - set(BACKUP_QT_NO_CREATE_TARGETS ${QT_NO_CREATE_TARGETS}) - set(QT_NO_CREATE_TARGETS OFF) - - # When cross-compiling, we want to search for Tools packages in QT_HOST_PATH. - # To do that, we override CMAKE_PREFIX_PATH and CMAKE_FIND_ROOT_PATH. - # - # We don't use find_package + PATHS option because any recursive find_dependency call - # inside a Tools package would not inherit the initial PATHS value given. - # TODO: Potentially we could set a global __qt_cmake_host_dir var like we currently - # do with _qt_cmake_dir in Qt6Config and change all our host tool find_package calls - # everywhere to specify that var in PATHS. - # - # Note though that due to path rerooting issue in - # https://gitlab.kitware.com/cmake/cmake/-/issues/21937 - # we have to append a lib/cmake suffix to CMAKE_PREFIX_PATH so the value does not get - # rerooted on top of CMAKE_FIND_ROOT_PATH. - # Use QT_HOST_PATH_CMAKE_DIR for the suffix when available (it would be set by - # the qt.toolchain.cmake file when building other repos or given by the user when - # configuring qtbase) or derive it from from the Qt6HostInfo package which is - # found in QtSetup. - set(${tools_package_name}_BACKUP_CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH}) - set(${tools_package_name}_BACKUP_CMAKE_FIND_ROOT_PATH "${CMAKE_FIND_ROOT_PATH}") - if(QT_HOST_PATH_CMAKE_DIR) - set(qt_host_path_cmake_dir_absolute "${QT_HOST_PATH_CMAKE_DIR}") - elseif(Qt${PROJECT_VERSION_MAJOR}HostInfo_DIR) - get_filename_component(qt_host_path_cmake_dir_absolute - "${Qt${PROJECT_VERSION_MAJOR}HostInfo_DIR}/.." ABSOLUTE) - else() - # This should never happen, serves as an assert. - message(FATAL_ERROR - "Neither QT_HOST_PATH_CMAKE_DIR nor " - "Qt${PROJECT_VERSION_MAJOR}HostInfo_DIR available.") - endif() - set(CMAKE_PREFIX_PATH "${qt_host_path_cmake_dir_absolute}") - - # Look for tools in additional host Qt installations. This is done for conan support where - # we have separate installation prefixes per package. For simplicity, we assume here that - # all host Qt installations use the same value of INSTALL_LIBDIR. - if(DEFINED QT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH) - file(RELATIVE_PATH rel_host_cmake_dir "${QT_HOST_PATH}" - "${qt_host_path_cmake_dir_absolute}") - foreach(host_path IN LISTS QT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH) - set(host_cmake_dir "${host_path}/${rel_host_cmake_dir}") - list(PREPEND CMAKE_PREFIX_PATH "${host_cmake_dir}") - endforeach() - - list(PREPEND CMAKE_FIND_ROOT_PATH "${QT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH}") - endif() - list(PREPEND CMAKE_FIND_ROOT_PATH "${QT_HOST_PATH}") - - find_package( - ${tools_package_name} - ${PROJECT_VERSION} - NO_PACKAGE_ROOT_PATH - NO_CMAKE_ENVIRONMENT_PATH - NO_SYSTEM_ENVIRONMENT_PATH - NO_CMAKE_PACKAGE_REGISTRY - NO_CMAKE_SYSTEM_PATH - NO_CMAKE_SYSTEM_PACKAGE_REGISTRY) - # Restore backups. - set(CMAKE_FIND_ROOT_PATH "${${tools_package_name}_BACKUP_CMAKE_FIND_ROOT_PATH}") - set(CMAKE_PREFIX_PATH "${${tools_package_name}_BACKUP_CMAKE_PREFIX_PATH}") - set(QT_NO_CREATE_TARGETS ${BACKUP_QT_NO_CREATE_TARGETS}) + cmake_parse_arguments(PARSE_ARGV 1 arg + "${option_keywords}" + "${one_value_keywords}" + "${multi_value_keywords}") + _qt_internal_validate_all_args_are_parsed(arg) - if(${${tools_package_name}_FOUND} AND TARGET ${full_name}) - # Even if the tool is already visible, make sure that our modules remain associated - # with the tools. - qt_internal_append_known_modules_with_tools("${arg_TOOLS_TARGET}") - get_property(path TARGET ${full_name} PROPERTY LOCATION) - message(STATUS "${full_name} was found at ${path} using package ${tools_package_name}.") - if (NOT QT_FORCE_BUILD_TOOLS) - return() - endif() - endif() - endif() + qt_internal_find_tool(will_build_tools ${target_name} "${arg_TOOLS_TARGET}") - if(NOT QT_WILL_BUILD_TOOLS) - if(${${tools_package_name}_FOUND}) - set(pkg_found_msg "") - string(APPEND pkg_found_msg - "the ${tools_package_name} package, but the package did not contain the tool. " - "Make sure that the host module ${arg_TOOLS_TARGET} was built with all features " - "enabled (no explicitly disabled tools).") - else() - set(pkg_found_msg "") - string(APPEND pkg_found_msg - "the ${tools_package_name} package, but the package could not be found. " - "Make sure you have built and installed the host ${arg_TOOLS_TARGET} module, " - "which will ensure the creation of the ${tools_package_name} package.") - endif() - message(FATAL_ERROR - "Failed to find the host tool \"${full_name}\". It is part of " - ${pkg_found_msg}) - else() - message(STATUS "Tool '${full_name}' will be built from source.") + if(NOT will_build_tools) + return() endif() set(disable_autogen_tools "${arg_DISABLE_AUTOGEN_TOOLS}") @@ -217,16 +100,27 @@ 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() + set(arg_NO_UNITY_BUILD "") + endif() + qt_internal_add_executable("${target_name}" OUTPUT_DIRECTORY "${output_dir}" ${exceptions} 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} LIBRARIES ${arg_LIBRARIES} @@ -236,16 +130,22 @@ function(qt_internal_add_tool target_name) LINK_OPTIONS ${arg_LINK_OPTIONS} MOC_OPTIONS ${arg_MOC_OPTIONS} DISABLE_AUTOGEN_TOOLS ${disable_autogen_tools} - TARGET_VERSION "${arg_TARGET_VERSION}" - TARGET_PRODUCT "${arg_TARGET_PRODUCT}" - TARGET_DESCRIPTION "${arg_TARGET_DESCRIPTION}" - TARGET_COMPANY "${arg_TARGET_COMPANY}" - TARGET_COPYRIGHT "${arg_TARGET_COPYRIGHT}" + TARGET_VERSION ${arg_TARGET_VERSION} + TARGET_PRODUCT ${arg_TARGET_PRODUCT} + TARGET_DESCRIPTION ${arg_TARGET_DESCRIPTION} + TARGET_COMPANY ${arg_TARGET_COMPANY} + TARGET_COPYRIGHT ${arg_TARGET_COPYRIGHT} + # If you are putting anything after these, make sure that + # qt_set_target_info_properties knows how to process them ) qt_internal_add_target_aliases("${target_name}") _qt_internal_apply_strict_cpp("${target_name}") qt_internal_adjust_main_config_runtime_output_dir("${target_name}" "${output_dir}") + if (WIN32) + _qt_internal_generate_longpath_win32_rc_file_and_manifest("${target_name}") + endif() + set_target_properties(${target_name} PROPERTIES _qt_package_version "${PROJECT_VERSION}" ) @@ -325,17 +225,70 @@ function(qt_internal_add_tool target_name) endforeach() if(arg_INSTALL_VERSIONED_LINK) - qt_internal_install_versioned_link("${install_dir}" "${target_name}") + qt_internal_install_versioned_link(WORKING_DIRECTORY "${install_dir}" + TARGETS "${target_name}") endif() qt_apply_rpaths(TARGET "${target_name}" INSTALL_PATH "${install_dir}" RELATIVE_RPATH) qt_internal_apply_staging_prefix_build_rpath_workaround() endif() + 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 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}) + get_target_property(target_bin_dir ${target} + BINARY_DIR) + + set(try_run_scripts_path "${target_bin_dir}/${target}_try_run.bat") + # The only reason -h is passed is because some of the tools, e.g., moc + # wait for an input without any arguments. + + qt_configure_file(OUTPUT "${try_run_scripts_path}" + CONTENT "@echo off + +${target_out_dir}/${target}.exe ${try_run_flags} > nul 2>&1 + +if \"%errorlevel%\" == \"-1073741515\" ( +echo +echo '${target}' is built successfully, but some of the libraries +echo necessary for running it are missing. If you are building Qt with +echo 3rdparty libraries, make sure that you add their directory to the +echo PATH environment variable. +echo +exit /b %errorlevel% +) +echo. > ${target_bin_dir}/${target}_try_run_passed" + ) + + add_custom_command( + OUTPUT + ${target_bin_dir}/${target}_try_run_passed + DEPENDS + ${target} + COMMAND + ${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 + ) + + add_custom_target(${target}_try_run ALL + DEPENDS ${target_bin_dir}/${target}_try_run_passed) +endfunction() + function(qt_export_tools module_name) # Bail out when not building tools. if(NOT QT_WILL_BUILD_TOOLS) @@ -395,7 +348,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() ") @@ -544,25 +497,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) @@ -589,3 +551,219 @@ function(qt_exclude_tool_directories_from_default_target) set(__qt_exclude_tool_directories "${absolute_path_directories}" PARENT_SCOPE) endif() endfunction() + +function(qt_internal_find_tool out_var target_name tools_target) + qt_tool_target_to_name(name ${target_name}) + + # Handle case when a tool does not belong to a module and it can't be built either (like + # during a cross-compile). + if(NOT tools_target AND NOT QT_WILL_BUILD_TOOLS) + message(FATAL_ERROR "The tool \"${name}\" has not been assigned to a module via" + " TOOLS_TARGET (so it can't be found) and it can't be built" + " (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.") + endif() + + set(full_name "${QT_CMAKE_EXPORT_NAMESPACE}::${name}") + set(imported_tool_target_already_found FALSE) + + # This condition can only be TRUE if a previous find_package(Qt6${tools_target}Tools) + # was already done. That can happen if QT_FORCE_FIND_TOOLS was ON or we're cross-compiling. + # In such a case, we need to exit early if we're not going to also build the tools. + if(TARGET ${full_name}) + get_property(path TARGET ${full_name} PROPERTY LOCATION) + message(STATUS "Tool '${full_name}' was found at ${path}.") + set(imported_tool_target_already_found TRUE) + if(NOT QT_WILL_BUILD_TOOLS) + set(${out_var} "FALSE" PARENT_SCOPE) + return() + endif() + endif() + + # We need to search for the host Tools package when doing a cross-build + # or when QT_FORCE_FIND_TOOLS is ON. + # As an optimiziation, we don't search for the package one more time if the target + # was already brought into scope from a previous find_package. + set(search_for_host_package FALSE) + if(NOT QT_WILL_BUILD_TOOLS OR QT_WILL_RENAME_TOOL_TARGETS) + set(search_for_host_package TRUE) + endif() + if(search_for_host_package AND NOT imported_tool_target_already_found) + set(tools_package_name "${INSTALL_CMAKE_NAMESPACE}${tools_target}Tools") + message(STATUS "Searching for tool '${full_name}' in package ${tools_package_name}.") + + # Create the tool targets, even if QT_NO_CREATE_TARGETS is set. + # Otherwise targets like Qt6::moc are not available in a top-level cross-build. + set(BACKUP_QT_NO_CREATE_TARGETS ${QT_NO_CREATE_TARGETS}) + set(QT_NO_CREATE_TARGETS OFF) + + # When cross-compiling, we want to search for Tools packages in QT_HOST_PATH. + # To do that, we override CMAKE_PREFIX_PATH and CMAKE_FIND_ROOT_PATH. + # + # We don't use find_package + PATHS option because any recursive find_dependency call + # inside a Tools package would not inherit the initial PATHS value given. + # TODO: Potentially we could set a global __qt_cmake_host_dir var like we currently + # do with _qt_cmake_dir in Qt6Config and change all our host tool find_package calls + # everywhere to specify that var in PATHS. + # + # Note though that due to path rerooting issue in + # https://gitlab.kitware.com/cmake/cmake/-/issues/21937 + # we have to append a lib/cmake suffix to CMAKE_PREFIX_PATH so the value does not get + # rerooted on top of CMAKE_FIND_ROOT_PATH. + # Use QT_HOST_PATH_CMAKE_DIR for the suffix when available (it would be set by + # the qt.toolchain.cmake file when building other repos or given by the user when + # configuring qtbase) or derive it from from the Qt6HostInfo package which is + # found in QtSetup. + set(${tools_package_name}_BACKUP_CMAKE_PREFIX_PATH ${CMAKE_PREFIX_PATH}) + set(${tools_package_name}_BACKUP_CMAKE_FIND_ROOT_PATH "${CMAKE_FIND_ROOT_PATH}") + if(QT_HOST_PATH_CMAKE_DIR) + set(qt_host_path_cmake_dir_absolute "${QT_HOST_PATH_CMAKE_DIR}") + elseif(Qt${PROJECT_VERSION_MAJOR}HostInfo_DIR) + get_filename_component(qt_host_path_cmake_dir_absolute + "${Qt${PROJECT_VERSION_MAJOR}HostInfo_DIR}/.." ABSOLUTE) + else() + # This should never happen, serves as an assert. + message(FATAL_ERROR + "Neither QT_HOST_PATH_CMAKE_DIR nor " + "Qt${PROJECT_VERSION_MAJOR}HostInfo_DIR available.") + endif() + set(CMAKE_PREFIX_PATH "${qt_host_path_cmake_dir_absolute}") + + # Look for tools in additional host Qt installations. This is done for conan support where + # we have separate installation prefixes per package. For simplicity, we assume here that + # all host Qt installations use the same value of INSTALL_LIBDIR. + if(DEFINED QT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH) + file(RELATIVE_PATH rel_host_cmake_dir "${QT_HOST_PATH}" + "${qt_host_path_cmake_dir_absolute}") + foreach(host_path IN LISTS QT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH) + set(host_cmake_dir "${host_path}/${rel_host_cmake_dir}") + list(PREPEND CMAKE_PREFIX_PATH "${host_cmake_dir}") + endforeach() + + list(PREPEND CMAKE_FIND_ROOT_PATH "${QT_ADDITIONAL_HOST_PACKAGES_PREFIX_PATH}") + endif() + list(PREPEND CMAKE_FIND_ROOT_PATH "${QT_HOST_PATH}") + + find_package( + ${tools_package_name} + ${PROJECT_VERSION} + NO_PACKAGE_ROOT_PATH + NO_CMAKE_ENVIRONMENT_PATH + NO_SYSTEM_ENVIRONMENT_PATH + NO_CMAKE_PACKAGE_REGISTRY + NO_CMAKE_SYSTEM_PATH + NO_CMAKE_SYSTEM_PACKAGE_REGISTRY) + + # Restore backups. + set(CMAKE_FIND_ROOT_PATH "${${tools_package_name}_BACKUP_CMAKE_FIND_ROOT_PATH}") + set(CMAKE_PREFIX_PATH "${${tools_package_name}_BACKUP_CMAKE_PREFIX_PATH}") + set(QT_NO_CREATE_TARGETS ${BACKUP_QT_NO_CREATE_TARGETS}) + + if(${${tools_package_name}_FOUND} AND TARGET ${full_name}) + # Even if the tool is already visible, make sure that our modules remain associated + # with the tools. + qt_internal_append_known_modules_with_tools("${tools_target}") + get_property(path TARGET ${full_name} PROPERTY LOCATION) + message(STATUS "${full_name} was found at ${path} using package ${tools_package_name}.") + if (NOT QT_FORCE_BUILD_TOOLS) + set(${out_var} "FALSE" PARENT_SCOPE) + return() + endif() + endif() + endif() + + 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 + "the ${tools_package_name} package, but the package did not contain the tool. " + "Make sure that the host module ${tools_target} was built with all features " + "enabled (no explicitly disabled tools).") + else() + set(pkg_found_msg "") + string(APPEND pkg_found_msg + "the ${tools_package_name} package, but the package could not be found. " + "Make sure you have built and installed the host ${tools_target} module, " + "which will ensure the creation of the ${tools_package_name} package.") + endif() + message(FATAL_ERROR + "Failed to find the host tool \"${full_name}\". It is part of " + ${pkg_found_msg}) + endif() + + if(QT_WILL_BUILD_TOOLS) + message(STATUS "Tool '${full_name}' will be built from source.") + endif() + set(${out_var} "TRUE" PARENT_SCOPE) +endfunction() + +# This function adds an internal tool that should be compiled at configure time. +# TOOLS_TARGET +# Specifies the module this tool belongs to. The Qt6${TOOLS_TARGET}Tools module +# will then expose targets for this tool. Ignored if NO_INSTALL is set. +function(qt_internal_add_configure_time_tool target_name) + set(one_value_args INSTALL_DIRECTORY TOOLS_TARGET CONFIG) + set(multi_value_args) + set(option_args NO_INSTALL) + cmake_parse_arguments(PARSE_ARGV 1 arg + "${option_args}" "${one_value_args}" "${multi_value_args}") + + qt_internal_find_tool(will_build_tools ${target_name} "${arg_TOOLS_TARGET}") + if(NOT will_build_tools) + return() + endif() + + qt_tool_target_to_name(name ${target_name}) + set(extra_args "") + if(arg_NO_INSTALL OR NOT arg_TOOLS_TARGET) + list(APPEND extra_args "NO_INSTALL") + else() + set(install_dir "${INSTALL_BINDIR}") + if(arg_INSTALL_DIRECTORY) + set(install_dir "${arg_INSTALL_DIRECTORY}") + endif() + set(extra_args "INSTALL_DIRECTORY" "${install_dir}") + endif() + + if(arg_CONFIG) + set(tool_config "${arg_CONFIG}") + elseif(QT_MULTI_CONFIG_FIRST_CONFIG) + set(tool_config "${arg_QT_MULTI_CONFIG_FIRST_CONFIG}") + else() + set(tool_config "${CMAKE_BUILD_TYPE}") + endif() + + string(REPLACE "\\\;" "\\\\\\\;" unparsed_arguments "${arg_UNPARSED_ARGUMENTS}") + qt_internal_add_configure_time_executable(${target_name} + OUTPUT_NAME ${name} + CONFIG ${tool_config} + ${extra_args} + ${unparsed_arguments} + ) + + if(TARGET host_tools) + add_dependencies(host_tools "${target_name}_build") + endif() + + if(NOT arg_NO_INSTALL AND arg_TOOLS_TARGET) + qt_internal_add_targets_to_additional_targets_export_file( + TARGETS ${target_name} + TARGET_EXPORT_NAMES ${QT_CMAKE_EXPORT_NAMESPACE}::${name} + EXPORT_NAME_PREFIX ${INSTALL_CMAKE_NAMESPACE}${arg_TOOLS_TARGET}Tools + ) + endif() +endfunction() diff --git a/cmake/QtToolchainHelpers.cmake b/cmake/QtToolchainHelpers.cmake index 5f3d2dc4d3..26b44bb10c 100644 --- a/cmake/QtToolchainHelpers.cmake +++ b/cmake/QtToolchainHelpers.cmake @@ -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: BSD-3-Clause # Create a CMake toolchain file for convenient configuration of both internal Qt builds # as well as CMake application projects. @@ -31,6 +31,14 @@ set(__qt_chainload_toolchain_file \"\${__qt_initially_configured_toolchain_file} list(APPEND init_platform "set(CMAKE_SYSTEM_PROCESSOR arm64 CACHE STRING \"\")") endif() + if(QT_QMAKE_TARGET_MKSPEC) + list(APPEND init_platform + "if(NOT QT_QMAKE_TARGET_MKSPEC)" + " set(QT_QMAKE_TARGET_MKSPEC ${QT_QMAKE_TARGET_MKSPEC} CACHE STRING \"\")" + "endif()" + ) + endif() + if("${QT_QMAKE_TARGET_MKSPEC}" STREQUAL "linux-g++-32" AND NOT QT_NO_AUTO_DETECT_LINUX_X86) set(__qt_toolchain_common_flags_init "-m32") @@ -121,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) @@ -208,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()") @@ -222,15 +232,11 @@ set(__qt_chainload_toolchain_file \"\${__qt_initially_configured_toolchain_file} elseif(ANDROID) list(APPEND init_platform "# Detect Android SDK/NDK from environment before loading the Android platform toolchain file." -"if(NOT DEFINED ANDROID_SDK_ROOT)" -" if(NOT \"\$ENV{ANDROID_SDK_ROOT}\" STREQUAL \"\")" -" set(ANDROID_SDK_ROOT \"\$ENV{ANDROID_SDK_ROOT}\" CACHE STRING \"Path to the Android SDK\")" -" endif()" +"if(\"$\{ANDROID_SDK_ROOT}\" STREQUAL \"\" AND NOT \"\$ENV{ANDROID_SDK_ROOT}\" STREQUAL \"\")" +" set(ANDROID_SDK_ROOT \"\$ENV{ANDROID_SDK_ROOT}\" CACHE STRING \"Path to the Android SDK\")" "endif()" -"if(NOT DEFINED ANDROID_NDK_ROOT)" -" if(NOT \"\$ENV{ANDROID_NDK_ROOT}\" STREQUAL \"\")" -" set(ANDROID_NDK_ROOT \"\$ENV{ANDROID_NDK_ROOT}\" CACHE STRING \"Path to the Android NDK\")" -" endif()" +"if(\"$\{ANDROID_NDK_ROOT}\" STREQUAL \"\" AND NOT \"\$ENV{ANDROID_NDK_ROOT}\" STREQUAL \"\")" +" set(ANDROID_NDK_ROOT \"\$ENV{ANDROID_NDK_ROOT}\" CACHE STRING \"Path to the Android NDK\")" "endif()" ) diff --git a/cmake/QtUnityBuildHelpers.cmake b/cmake/QtUnityBuildHelpers.cmake new file mode 100644 index 0000000000..e804396f59 --- /dev/null +++ b/cmake/QtUnityBuildHelpers.cmake @@ -0,0 +1,24 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +# Check whether no unity build is requested where it is disabled by default. +function(_qt_internal_validate_no_unity_build prefix) + if(${prefix}_NO_UNITY_BUILD OR ${prefix}_NO_UNITY_BUILD_SOURCES) + message(WARNING + "Unity build is disabled by default for this target, and its sources. " + "You may remove the NO_UNITY_BUILD and/or NO_UNITY_BUILD_SOURCES arguments.") + endif() +endfunction() + +function(qt_update_ignore_unity_build_sources target sources) + 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 c41de308af..41ef5cb0ba 100644 --- a/cmake/QtWasmHelpers.cmake +++ b/cmake/QtWasmHelpers.cmake @@ -1,45 +1,48 @@ # 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: BSD-3-Clause # WARNING must keep in sync with wasm-emscripten/qmake.conf! function (qt_internal_setup_wasm_target_properties wasmTarget) target_link_options("${wasmTarget}" INTERFACE - "SHELL:-s ERROR_ON_UNDEFINED_SYMBOLS=1" "SHELL:-s MAX_WEBGL_VERSION=2" "SHELL:-s FETCH=1" - "SHELL:-s WASM_BIGINT=1") + "SHELL:-s WASM_BIGINT=1" + "SHELL:-s STACK_SIZE=5MB") target_link_libraries("${wasmTarget}" INTERFACE embind) - # 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") + ## wasm64 + if (WASM64) + target_compile_options("${wasmTarget}" INTERFACE "SHELL:-s MEMORY64=1" ) + target_link_options("${wasmTarget}" INTERFACE "SHELL:-s MEMORY64=1" -mwasm64) + endif() + # 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) + target_compile_options("${wasmTarget}" INTERFACE -msimd128) + endif() if (QT_FEATURE_sse2) target_compile_options("${wasmTarget}" INTERFACE -O2 -msimd128 -msse -msse2) endif() - set(disable_exceptions_catching 1) - if (QT_FEATURE_exceptions) - set(disable_exceptions_catching 0) + # wasm exceptions + if (QT_FEATURE_wasm_exceptions) + target_compile_options("${wasmTarget}" INTERFACE -fwasm-exceptions) + target_link_options("${wasmTarget}" INTERFACE -fwasm-exceptions) endif() - target_link_options("${wasmTarget}" INTERFACE "SHELL:-s DISABLE_EXCEPTION_CATCHING=${disable_exceptions_catching}") if (QT_FEATURE_thread) target_compile_options("${wasmTarget}" INTERFACE "SHELL:-pthread") target_link_options("${wasmTarget}" INTERFACE "SHELL:-pthread") - else() - target_link_options("${wasmTarget}" INTERFACE "SHELL:-s ALLOW_MEMORY_GROWTH=1") endif() + target_link_options("${wasmTarget}" INTERFACE "SHELL:-s ALLOW_MEMORY_GROWTH") + # debug add_compile_options if ("QT_WASM_SOURCE_MAP=1" IN_LIST QT_QMAKE_DEVICE_OPTIONS) set(WASM_SOURCE_MAP_BASE "http://localhost:8000/") @@ -58,7 +61,6 @@ function (qt_internal_setup_wasm_target_properties wasmTarget) # a few good defaults to make console more verbose while debugging target_link_options("${wasmTarget}" INTERFACE $<$<CONFIG:Debug>: "SHELL:-s DEMANGLE_SUPPORT=1" - "SHELL:-s GL_DEBUG=1" --profiling-funcs>) # target_link_options("${wasmTarget}" INTERFACE "SHELL:-s LIBRARY_DEBUG=1") # print out library calls, verbose @@ -67,7 +69,6 @@ function (qt_internal_setup_wasm_target_properties wasmTarget) # target_link_options("${wasmTarget}" INTERFACE "SHELL:-s SOCKET_DEBUG") # print out socket,network data transfer if ("QT_EMSCRIPTEN_ASYNCIFY=1" IN_LIST QT_QMAKE_DEVICE_OPTIONS) - # Emscripten recommends building with optimizations when using asyncify # in order to reduce wasm file size, and may also generate broken wasm # (with "wasm validation error: too many locals" type errors) if optimizations @@ -82,10 +83,45 @@ function (qt_internal_setup_wasm_target_properties wasmTarget) # Set ASYNCIFY_IMPORTS unconditionally in order to support enabling asyncify at link time. target_link_options("${wasmTarget}" INTERFACE "SHELL:-sASYNCIFY_IMPORTS=qt_asyncify_suspend_js,qt_asyncify_resume_js") + if(QT_FEATURE_shared) + + set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS TRUE) + + set(side_modules + MODULE_LIBRARY SHARED_LIBRARY) + set(enable_side_module_if_needed + "$<$<IN_LIST:$<TARGET_PROPERTY:TYPE>,${side_modules}>:SHELL:-s SIDE_MODULE=1>") + set(enable_main_module_if_needed + "$<$<IN_LIST:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:SHELL:-s MAIN_MODULE=1>") + set(set_shared_module_type_if_needed + "${enable_side_module_if_needed}" + "${enable_main_module_if_needed}" + ) + + # Add Qt libdir to linker library paths + set(qt_lib_location + "${QT_BUILD_INTERNALS_RELOCATABLE_INSTALL_PREFIX}/${INSTALL_LIBDIR}") + target_link_options("${wasmTarget}" INTERFACE + "$<$<STREQUAL:$<TARGET_PROPERTY:TYPE>,EXECUTABLE>:SHELL:" -L${qt_lib_location}/>) + + target_compile_options("${wasmTarget}" INTERFACE "${set_shared_module_type_if_needed}") + target_link_options("${wasmTarget}" INTERFACE "${set_shared_module_type_if_needed}") + + else() + target_link_options("${wasmTarget}" INTERFACE "SHELL:-s ERROR_ON_UNDEFINED_SYMBOLS=1") + endif() + + # Suppress warnings for known issues for developer builds + if(FEATURE_developer_build) + target_link_options("${wasmTarget}" INTERFACE "SHELL:-Wno-pthreads-mem-growth") + endif() + 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 21ad62dd92..8eb4416e6d 100644 --- a/cmake/QtWrapperScriptHelpers.cmake +++ b/cmake/QtWrapperScriptHelpers.cmake @@ -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: BSD-3-Clause # Creates and installs the following wrapper CMake scripts: # qt-make @@ -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" @@ -39,21 +44,40 @@ function(qt_internal_create_wrapper_scripts) DESTINATION "${INSTALL_BINDIR}") endif() - # Provide a private convenience wrapper with options which should not be propagated via the + if(generate_unix) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bin/qt-cmake-create.in" + "${QT_BUILD_DIR}/${INSTALL_BINDIR}/qt-cmake-create" @ONLY + NEWLINE_STYLE LF) + qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_BINDIR}/qt-cmake-create" + DESTINATION "${INSTALL_BINDIR}") + endif() + if(generate_non_unix) + configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bin/qt-cmake-create.bat.in" + "${QT_BUILD_DIR}/${INSTALL_BINDIR}/qt-cmake-create.bat" @ONLY + NEWLINE_STYLE CRLF) + qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_BINDIR}/qt-cmake-create.bat" + DESTINATION "${INSTALL_BINDIR}") + endif() + + # 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_BINDIR}/qt-cmake-private" @ONLY + "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/qt-cmake-private" @ONLY NEWLINE_STYLE LF) - qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_BINDIR}/qt-cmake-private" - DESTINATION "${INSTALL_BINDIR}") + qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/qt-cmake-private" + DESTINATION "${INSTALL_LIBEXECDIR}") endif() if(generate_non_unix) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bin/qt-cmake.bat.in" @@ -95,9 +119,15 @@ function(qt_internal_create_wrapper_scripts) # find_package(Qt...) to get all dependencies like examples do. # Instead a template CMakeLists.txt project is used which sets up all the necessary private bits # and then calls add_subdirectory on the provided project path. - set(__qt_cmake_standalone_test_bin_name "qt-cmake-standalone-test") - set(__qt_cmake_standalone_test_bin_path - "${INSTALL_BINDIR}/${__qt_cmake_standalone_test_bin_name}") + set(__qt_cmake_standalone_test_name "qt-cmake-standalone-test") + if(generate_unix) + set(__qt_cmake_standalone_test_libexec_path + "${INSTALL_LIBEXECDIR}/${__qt_cmake_standalone_test_name}") + endif() + if(generate_non_unix) + set(__qt_cmake_standalone_test_bin_path + "${INSTALL_BINDIR}/${__qt_cmake_standalone_test_name}") + endif() # Configuring a standalone test on iOS should use the Xcode generator, but qt-cmake-private uses # the generator that was used to build Qt itself (e.g. Ninja). @@ -106,16 +136,19 @@ function(qt_internal_create_wrapper_scripts) set(__qt_cmake_private_path "${QT_STAGING_PREFIX}/${INSTALL_BINDIR}/qt-cmake") else() - set(__qt_cmake_private_path - "${QT_STAGING_PREFIX}/${INSTALL_BINDIR}/qt-cmake-private") + if(generate_unix) + set(__qt_cmake_private_path + "${QT_STAGING_PREFIX}/${INSTALL_LIBEXECDIR}/qt-cmake-private") + endif() + if(generate_non_unix) + set(__qt_cmake_private_path + "${QT_STAGING_PREFIX}/${INSTALL_BINDIR}/qt-cmake-private") + endif() endif() set(__qt_cmake_standalone_test_path "${__build_internals_install_dir}/${__build_internals_standalone_test_template_dir}") - get_filename_component(rel_base_path - "${QT_STAGING_PREFIX}/${__qt_cmake_standalone_test_bin_path}" - DIRECTORY) if(QT_WILL_INSTALL) # Need to prepend the staging prefix when doing prefix builds, because the build internals # install dir is relative in that case.. @@ -124,12 +157,16 @@ function(qt_internal_create_wrapper_scripts) "${__qt_cmake_standalone_test_path}") endif() - file(RELATIVE_PATH __qt_cmake_private_relpath "${rel_base_path}" - "${__qt_cmake_private_path}") - file(RELATIVE_PATH __qt_cmake_standalone_test_relpath "${rel_base_path}" - "${__qt_cmake_standalone_test_path}") - if(generate_unix) + get_filename_component(rel_base_path + "${QT_STAGING_PREFIX}/${__qt_cmake_standalone_test_libexec_path}" + DIRECTORY) + + file(RELATIVE_PATH __qt_cmake_private_relpath "${rel_base_path}" + "${__qt_cmake_private_path}") + file(RELATIVE_PATH __qt_cmake_standalone_test_relpath "${rel_base_path}" + "${__qt_cmake_standalone_test_path}") + set(__qt_cmake_standalone_test_os_prelude "#!/bin/sh") set(__qt_cmake_standalone_test_script_relpath "SCRIPT_DIR=`dirname $0`") string(PREPEND __qt_cmake_private_relpath "exec $SCRIPT_DIR/") @@ -137,12 +174,21 @@ function(qt_internal_create_wrapper_scripts) set(__qt_cmake_standalone_passed_args "\"$@\" -DPWD=\"$PWD\"") configure_file("${CMAKE_CURRENT_SOURCE_DIR}/bin/qt-cmake-standalone-test.in" - "${QT_BUILD_DIR}/${__qt_cmake_standalone_test_bin_path}" + "${QT_BUILD_DIR}/${__qt_cmake_standalone_test_libexec_path}" NEWLINE_STYLE LF) - qt_install(PROGRAMS "${QT_BUILD_DIR}/${__qt_cmake_standalone_test_bin_path}" - DESTINATION "${INSTALL_BINDIR}") + qt_install(PROGRAMS "${QT_BUILD_DIR}/${__qt_cmake_standalone_test_libexec_path}" + DESTINATION "${INSTALL_LIBEXECDIR}") endif() if(generate_non_unix) + get_filename_component(rel_base_path + "${QT_STAGING_PREFIX}/${__qt_cmake_standalone_test_bin_path}" + DIRECTORY) + + file(RELATIVE_PATH __qt_cmake_private_relpath "${rel_base_path}" + "${__qt_cmake_private_path}") + file(RELATIVE_PATH __qt_cmake_standalone_test_relpath "${rel_base_path}" + "${__qt_cmake_standalone_test_path}") + set(__qt_cmake_standalone_test_os_prelude "@echo off") set(__qt_cmake_standalone_test_script_relpath "set SCRIPT_DIR=%~dp0") string(APPEND __qt_cmake_standalone_test_bin_path ".bat") @@ -166,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_BINDIR}/${__qt_cmake_install_script_name}" @ONLY) - qt_install(FILES "${QT_BUILD_DIR}/${INSTALL_BINDIR}/${__qt_cmake_install_script_name}" - DESTINATION "${INSTALL_BINDIR}") + "${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) @@ -185,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. + + 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() - set(script_passed_args "-DQT_BUILD_STANDALONE_TESTS=ON") + 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} @@ -213,16 +286,48 @@ function(qt_internal_create_qt_configure_tests_wrapper_script) endif() if(generate_non_unix) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/libexec/${script_name}.bat.in" - "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/${script_name}.bat" @ONLY + "${QT_BUILD_DIR}/${INSTALL_BINDIR}/${script_name}.bat" @ONLY NEWLINE_STYLE CRLF) - qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_LIBEXECDIR}/${script_name}.bat" - DESTINATION "${INSTALL_LIBEXECDIR}") + qt_install(PROGRAMS "${QT_BUILD_DIR}/${INSTALL_BINDIR}/${script_name}.bat" + DESTINATION "${INSTALL_BINDIR}") 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/QtWriteArgsFile.cmake b/cmake/QtWriteArgsFile.cmake index c03834edc9..77a9eb2463 100644 --- a/cmake/QtWriteArgsFile.cmake +++ b/cmake/QtWriteArgsFile.cmake @@ -1,22 +1,79 @@ # 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: BSD-3-Clause # This script writes its arguments to the file determined by OUT_FILE. # Each argument appears on a separate line. # This is used for writing the config.opt file. # # This script takes the following arguments: -# IN_FILE: The input file. The whole command line as one string. +# IN_FILE: The input file. The whole command line as one string, or one argument per line. +# REDO_FILE: A file containing extra commands to be joined with IN_FILE. # OUT_FILE: The output file. One argument per line. # SKIP_ARGS: Number of arguments to skip from the front of the arguments list. # IGNORE_ARGS: List of arguments to be ignored, i.e. that are not written. +# +# If the REDO_FILE is given, its parameters will be merged with IN_FILE parameters +# and be written into the OUT_FILE. cmake_minimum_required(VERSION 3.16) # Read arguments from IN_FILE and separate them. file(READ "${IN_FILE}" raw_args) +# To catch cases where the path ends with an `\`, e.g., `-prefix "C:\Path\"` +string(REPLACE "\\\"" "\"" raw_args "${raw_args}") +string(REPLACE ";" "[[;]]" raw_args "${raw_args}") + separate_arguments(args NATIVE_COMMAND "${raw_args}") +string(REPLACE "\;" ";" args "${args}") +string(REPLACE "[[;]]" "\;" args "${args}") + +if(DEFINED REDO_FILE) + file(READ "${REDO_FILE}" raw_redo_args) + separate_arguments(redo_args NATIVE_COMMAND "${raw_redo_args}") + + if(args) + list(FIND args "--" args_ddash_loc) + list(FIND redo_args "--" redo_ddash_loc) + if("${redo_ddash_loc}" STREQUAL "-1") + if("${args_ddash_loc}" STREQUAL "-1") + list(LENGTH args args_ddash_loc) + endif() + # Avoid adding an empty line for an empty -redo + if(NOT "${redo_args}" STREQUAL "") + list(INSERT args ${args_ddash_loc} "${redo_args}") + endif() + else() + # Handling redo's configure options + list(SUBLIST redo_args 0 ${redo_ddash_loc} redo_config_args) + if(redo_config_args) + if("${args_ddash_loc}" STREQUAL "-1") + list(APPEND args "${redo_config_args}") + else() + list(INSERT args ${args_ddash_loc} "${redo_config_args}") + endif() + endif() + + # Handling redo's CMake options + list(LENGTH redo_args redo_args_len) + math(EXPR redo_ddash_loc "${redo_ddash_loc} + 1") + # Catch an unlikely case of -redo being called with an empty --, ie., `-redo --` + if(NOT ${redo_ddash_loc} STREQUAL ${redo_args_len}) + list(SUBLIST redo_args ${redo_ddash_loc} -1 redo_cmake_args) + endif() + + if(DEFINED redo_cmake_args) + if("${args_ddash_loc}" STREQUAL "-1") + list(APPEND args "--") + endif() + list(APPEND args "${redo_cmake_args}") + endif() + endif() + else() + list(APPEND args "${redo_args}") + endif() +endif() + # Skip arguments if requested if(DEFINED SKIP_ARGS) foreach(i RANGE 1 ${SKIP_ARGS}) diff --git a/cmake/README.md b/cmake/README.md index 64b4321189..9d0743566d 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). @@ -48,7 +53,9 @@ You may use brew to install dependencies needed to build QtBase. `/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"` * Build Qt dependencies: ``brew install pcre2 harfbuzz freetype`` * Install cmake: ``brew install cmake`` - * When running cmake in qtbase, pass ``-DCMAKE_PREFIX_PATH=/usr/local`` + * When running cmake in qtbase, pass ``-DFEATURE_pkg_config=ON`` together with + ``-DCMAKE_PREFIX_PATH=/usr/local``, or ``-DCMAKE_PREFIX_PATH=/opt/homebrew`` if you have a Mac + with Apple Silicon. # Building @@ -193,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`` @@ -311,3 +318,16 @@ $ cd some/empty/directory $ ~/Qt/6.0.0/bin/qt-cmake-standalone-test ~/source/of/qtbase/test/auto/corelib/io/qprocess $ cmake --build . ``` + +## qt-cmake-create + +Generates a simple CMakeLists.txt based on source files in specified project directory. + +Example: + +``` +$ cd some/source/directory/ +$ qt-cmake-create +$ qt-cmake -S . -B /build/directory +$ cmake --build /build/directory +``` diff --git a/cmake/configure-cmake-mapping.md b/cmake/configure-cmake-mapping.md index ade65ed5de..d504bd8d4b 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 | | | @@ -17,7 +18,6 @@ The following table describes the mapping of configure options to CMake argument | -no-feature-foo | -DFEATURE_foo=OFF | | | -list-features | | At the moment: configure with cmake once, | | | | then use ccmake or cmake-gui to inspect the features. | -| -list-libraries | | | | -opensource | n/a | | | -commercial | n/a | | | -confirm-license | n/a | | @@ -45,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 | @@ -60,6 +61,7 @@ The following table describes the mapping of configure options to CMake argument | -R <string> | -DQT_EXTRA_RPATHS=path1;path2 | | | -rpath | negative CMAKE_SKIP_BUILD_RPATH | | | | negative CMAKE_SKIP_INSTALL_RPATH | | +| | negative CMAKE_MACOSX_RPATH | | | -reduce-exports | -DFEATURE_reduce_exports=ON | | | -reduce-relocations | -DFEATURE_reduce_relocations=ON | | | -plugin-manifests | | | @@ -73,21 +75,16 @@ The following table describes the mapping of configure options to CMake argument | | | custom build steps for moc, uic, and rcc. | | | | This lacks support in CMake. | | -ccache | -DQT_USE_CCACHE=ON | | -| -make-tool <tool> | n/a | | -| -mp | n/a | | +| -unity-build | -DQT_UNITY_BUILD=ON | | +| -unity-build-batch-size <int> | -DQT_UNITY_BUILD_BATCH_SIZE=<int> | | | -warnings-are-errors | -DWARNINGS_ARE_ERRORS=ON | | -| -silent | n/a | | -| -sysroot <dir> | -DCMAKE_SYSROOT=<dir> | Should be provided by a toolchain file that's | -| | | passed via -DCMAKE_TOOLCHAIN_FILE=<filename> | -| -no-gcc-sysroot | n/a | The corresponding CMake variables are CMAKE_SYSROOT_LINK | -| | | and CMAKE_SYSROOT_COMPILE. | -| | | They are usually set in a toolchain file. | | -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> | | @@ -98,7 +95,9 @@ The following table describes the mapping of configure options to CMake argument | -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> | -QT_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 | @@ -108,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 | | @@ -118,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 | | @@ -165,8 +164,10 @@ 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> | | +| -mimetype-database-compression <type> | -DINPUT_mimetype_database_compression=<type> | Sets the compression type for mime type database. Supported | +| | | types: gzip, zstd, none. | diff --git a/cmake/ios/Info.plist.app.in b/cmake/ios/Info.plist.app.in index f2cbe593b2..2f13828213 100644 --- a/cmake/ios/Info.plist.app.in +++ b/cmake/ios/Info.plist.app.in @@ -2,54 +2,53 @@ <!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>CFBundleInfoDictionaryVersion</key> + <string>6.0</string> - <key>CFBundlePackageType</key> - <string>APPL</string> + <key>CFBundlePackageType</key> + <string>APPL</string> - <key>CFBundleName</key> - <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> + <key>CFBundleName</key> + <string>${MACOSX_BUNDLE_BUNDLE_NAME}</string> - <key>CFBundleDisplayName</key> - <string>${QT_INTERNAL_DOLLAR_VAR}{PRODUCT_NAME}</string> + <key>CFBundleDisplayName</key> + <string>${QT_INTERNAL_DOLLAR_VAR}{PRODUCT_NAME}</string> - <key>CFBundleIdentifier</key> - <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> + <key>CFBundleIdentifier</key> + <string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string> - <key>CFBundleExecutable</key> - <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> + <key>CFBundleExecutable</key> + <string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string> - <key>CFBundleVersion</key> - <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> + <key>CFBundleVersion</key> + <string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string> - <key>CFBundleShortVersionString</key> - <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> + <key>CFBundleShortVersionString</key> + <string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string> - <key>NSHumanReadableCopyright</key> - <string>${MACOSX_BUNDLE_COPYRIGHT}</string> + <key>CFBundleIconFile</key> + <string>${MACOSX_BUNDLE_ICON_FILE}</string> - <key>CFBundleIconFile</key> - <string>${MACOSX_BUNDLE_ICON_FILE}</string> + <key>CFBundleDevelopmentRegion</key> + <string>$(DEVELOPMENT_LANGUAGE)</string> + <key>CFBundleAllowMixedLocalizations</key> + <true/> - <key>CFBundleDevelopmentRegion</key> - <string>English</string> + <key>LSRequiresIPhoneOS</key> + <true/> - <key>LSRequiresIPhoneOS</key> - <true/> + <key>NOTE</key> + <string>This file was generated by Qt's default CMake support.</string> - <key>NOTE</key> - <string>This file was generated by Qt's default CMake support.</string> + <key>UILaunchStoryboardName</key> + <string>@qt_ios_launch_screen_plist_entry@</string> - <key>UILaunchStoryboardName</key> - <string>@qt_ios_launch_screen_plist_entry@</string> - - <key>UISupportedInterfaceOrientations</key> - <array> - <string>UIInterfaceOrientationPortrait</string> - <string>UIInterfaceOrientationPortraitUpsideDown</string> - <string>UIInterfaceOrientationLandscapeLeft</string> - <string>UIInterfaceOrientationLandscapeRight</string> - </array> + <key>UISupportedInterfaceOrientations</key> + <array> + <string>UIInterfaceOrientationPortrait</string> + <string>UIInterfaceOrientationPortraitUpsideDown</string> + <string>UIInterfaceOrientationLandscapeLeft</string> + <string>UIInterfaceOrientationLandscapeRight</string> + </array> </dict> </plist> 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/MacOSXBundleInfo.plist.in b/cmake/macos/Info.plist.app.in index 2ead02b7d5..c791521655 100644 --- a/cmake/macos/MacOSXBundleInfo.plist.in +++ b/cmake/macos/Info.plist.app.in @@ -29,7 +29,9 @@ <string>${MACOSX_BUNDLE_ICON_FILE}</string> <key>CFBundleDevelopmentRegion</key> - <string>English</string> + <string>en</string> + <key>CFBundleAllowMixedLocalizations</key> + <true/> <key>NSPrincipalClass</key> <string>NSApplication</string> 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 f4e976b540..4d41a3a2a2 100644 --- a/cmake/modulecppexports.h.in +++ b/cmake/modulecppexports.h.in @@ -4,7 +4,9 @@ #ifndef @header_base_name_upper@_H #define @header_base_name_upper@_H -#include <QtCore/qglobal.h> +#include <QtCore/qcompilerdetection.h> +#include <QtCore/qtconfigmacros.h> // Q_@module_define_infix@_EXPORT +#include <QtCore/qtdeprecationmarkers.h> // QT_IF_DEPRECATED_SINCE #if defined(QT_SHARED) || !defined(QT_STATIC) # if defined(QT_BUILD_@module_define_infix@_LIB) @@ -16,22 +18,23 @@ # define Q_@module_define_infix@_EXPORT #endif -#if !defined(QT_BUILD_@module_define_infix@_LIB) || defined(QT_STATIC) -/* outside library → inline decl + defi */ +#if !defined(QT_BUILD_@module_define_infix@_LIB) && !defined(QT_STATIC) +/* 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 5f873e3c70..0000000000 --- a/cmake/modulecppexports_p.h.in +++ /dev/null @@ -1,22 +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 - -// -// 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/platforms/FindIntegrityPlatformGraphics.cmake b/cmake/platforms/FindIntegrityPlatformGraphics.cmake index 3bc266649b..7b03d7ae89 100644 --- a/cmake/platforms/FindIntegrityPlatformGraphics.cmake +++ b/cmake/platforms/FindIntegrityPlatformGraphics.cmake @@ -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: BSD-3-Clause #.rst: # IntegrityPlatformGraphics diff --git a/cmake/platforms/Platform/Integrity.cmake b/cmake/platforms/Platform/Integrity.cmake index d742bf2544..0ad7fd1099 100644 --- a/cmake/platforms/Platform/Integrity.cmake +++ b/cmake/platforms/Platform/Integrity.cmake @@ -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: BSD-3-Clause # Custom platform module file for INTEGRITY. # diff --git a/cmake/qbatchedtestrunner.in.cpp b/cmake/qbatchedtestrunner.in.cpp new file mode 100644 index 0000000000..cc49b77e0a --- /dev/null +++ b/cmake/qbatchedtestrunner.in.cpp @@ -0,0 +1,18 @@ +// Copyright (C) 2022 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include <QList> +#include <QString> +#include <QtTest/private/qtestcase_p.h> + +int main(int argc, char **argv) +{ + if (argc == 1) { + printf("%s\n", QTest::qGetTestCaseNames().join( + QStringLiteral(" ")).toStdString().c_str()); + return 0; + } + + const auto entryFunction = QTest::qGetTestCaseEntryFunction(QString::fromUtf8(argv[1])); + return entryFunction ? entryFunction(argc - 1, argv + 1) : -1; +} diff --git a/cmake/tests/CMakeLists.txt b/cmake/tests/CMakeLists.txt index 5ee20fbf15..45523c2195 100644 --- a/cmake/tests/CMakeLists.txt +++ b/cmake/tests/CMakeLists.txt @@ -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: BSD-3-Clause # These macros are inspired by ECM: diff --git a/cmake/tests/features/CMakeLists.txt b/cmake/tests/features/CMakeLists.txt index 8ff854ad59..415accb04e 100644 --- a/cmake/tests/features/CMakeLists.txt +++ b/cmake/tests/features/CMakeLists.txt @@ -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: BSD-3-Clause cmake_minimum_required(VERSION 3.16) diff --git a/cmake/tests/features/configure.cmake b/cmake/tests/features/configure.cmake index 4dc8f8208e..fa68d8596e 100644 --- a/cmake/tests/features/configure.cmake +++ b/cmake/tests/features/configure.cmake @@ -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: BSD-3-Clause #### Features diff --git a/cmake/tests/features/src/CMakeLists.txt b/cmake/tests/features/src/CMakeLists.txt index 927b093d4b..a02fb1f581 100644 --- a/cmake/tests/features/src/CMakeLists.txt +++ b/cmake/tests/features/src/CMakeLists.txt @@ -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: BSD-3-Clause ## Features from parent scope were inherited: assert(QT_FEATURE_top_a STREQUAL "ON") diff --git a/cmake/tests/qt_make_output_file/CMakeLists.txt b/cmake/tests/qt_make_output_file/CMakeLists.txt index 83de59da2e..5beecc6ec3 100644 --- a/cmake/tests/qt_make_output_file/CMakeLists.txt +++ b/cmake/tests/qt_make_output_file/CMakeLists.txt @@ -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: BSD-3-Clause cmake_minimum_required(VERSION 3.16) diff --git a/cmake/tests/test.cmake b/cmake/tests/test.cmake index 9087c4a274..db55cc8842 100644 --- a/cmake/tests/test.cmake +++ b/cmake/tests/test.cmake @@ -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: BSD-3-Clause # FAKE moc-ing: set(QT_MOCSCANNER /usr/bin/true) diff --git a/cmake/visionos/Info.plist.app.in b/cmake/visionos/Info.plist.app.in new file mode 100644 index 0000000000..7aa4698649 --- /dev/null +++ b/cmake/visionos/Info.plist.app.in @@ -0,0 +1,42 @@ +<?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> +</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> |