diff options
Diffstat (limited to 'mkspecs/features')
57 files changed, 2472 insertions, 196 deletions
diff --git a/mkspecs/features/android/android_deployment_settings.prf b/mkspecs/features/android/android_deployment_settings.prf index ce5696a33c..848de485f8 100644 --- a/mkspecs/features/android/android_deployment_settings.prf +++ b/mkspecs/features/android/android_deployment_settings.prf @@ -65,6 +65,6 @@ contains(TEMPLATE, ".*app"):!build_pass: { isEmpty(ANDROID_DEPLOYMENT_SETTINGS_FILE): ANDROID_DEPLOYMENT_SETTINGS_FILE = $$OUT_PWD/android-$$TARGET-deployment-settings.json - write_file($$ANDROID_DEPLOYMENT_SETTINGS_FILE, FILE_CONTENT) | error("Aborting.") + write_file($$ANDROID_DEPLOYMENT_SETTINGS_FILE, FILE_CONTENT)|error() } diff --git a/mkspecs/features/configure.prf b/mkspecs/features/configure.prf index a890c7f6b9..bd53e31a04 100644 --- a/mkspecs/features/configure.prf +++ b/mkspecs/features/configure.prf @@ -1,40 +1,4 @@ -equals(QMAKE_HOST.os, Windows) { - SETENV_PFX = "set " - SETENV_SFX = "&" -} else { - SETENV_PFX = - SETENV_SFX = -} - -QMAKE_MAKE = $$(MAKE) -!isEmpty(QMAKE_MAKE) { - # We were called recursively. Use the same make. -} else:if(equals(MAKEFILE_GENERATOR, UNIX)|equals(MAKEFILE_GENERATOR, MINGW)) { - !equals(QMAKE_HOST.os, Windows): \ - QMAKE_MAKE = make - else: \ - QMAKE_MAKE = mingw32-make -} else:if(equals(MAKEFILE_GENERATOR, MSVC.NET)|equals(MAKEFILE_GENERATOR, MSBUILD)) { - QMAKE_MAKE = nmake -} else { - error("Configure tests are not supported with the $$MAKEFILE_GENERATOR Makefile generator.") -} -# Make sure we don't inherit MAKEFLAGS - -i in particular is fatal. -QMAKE_MAKE = "$${SETENV_PFX}MAKEFLAGS=$$SETENV_SFX $$QMAKE_MAKE" - -# Ensure that a cache is present. If none was found on startup, this will create -# one in the build directory of the project which loads this feature. -cache() - -QMAKE_CONFIG_LOG = $$dirname(_QMAKE_CACHE_)/config.log -QMAKE_CONFIG_TESTS_DIR = $$_PRO_FILE_PWD_/config.tests - -defineTest(qtRunLoggedCommand) { - msg = "+ $$1" - write_file($$QMAKE_CONFIG_LOG, msg, append) - system("$$1 >> \"$$QMAKE_CONFIG_LOG\" 2>&1")|return(false) - return(true) -} +load(configure_base) # Try to build the test project in $$QMAKE_CONFIG_TESTS_DIR/$$1 # ($$_PRO_FILE_PWD_/config.tests/$$1 by default). @@ -73,7 +37,7 @@ defineTest(qtCompileTest) { # Clean up after previous run exists($$test_out_dir/Makefile):qtRunLoggedCommand("$$test_cmd_base $$QMAKE_MAKE distclean") - mkpath($$test_out_dir)|error("Aborting.") + mkpath($$test_out_dir)|error() !isEmpty (QMAKE_QTCONF): qtconfarg = -qtconf $$QMAKE_QTCONF diff --git a/mkspecs/features/configure_base.prf b/mkspecs/features/configure_base.prf new file mode 100644 index 0000000000..08edba4b1c --- /dev/null +++ b/mkspecs/features/configure_base.prf @@ -0,0 +1,53 @@ +equals(QMAKE_HOST.os, Windows) { + SETENV_PFX = "set " + SETENV_SFX = "&" +} else { + SETENV_PFX = + SETENV_SFX = +} + +QMAKE_MAKE = $$(MAKE) +!isEmpty(QMAKE_MAKE) { + # We were called recursively. Use the same make. +} else: if(equals(MAKEFILE_GENERATOR, UNIX)|equals(MAKEFILE_GENERATOR, MINGW)) { + !equals(QMAKE_HOST.os, Windows): \ + QMAKE_MAKE = make + else: \ + QMAKE_MAKE = mingw32-make +} else: if(equals(MAKEFILE_GENERATOR, MSVC.NET)|equals(MAKEFILE_GENERATOR, MSBUILD)) { + QMAKE_MAKE = nmake +} else { + error("Configure tests are not supported with the $$MAKEFILE_GENERATOR Makefile generator.") +} +# Make sure we don't inherit MAKEFLAGS - -i in particular is fatal. +QMAKE_MAKE = "$${SETENV_PFX}MAKEFLAGS=$$SETENV_SFX $$QMAKE_MAKE" + +isEmpty(QMAKE_CONFIG_VERBOSE): QMAKE_CONFIG_VERBOSE = false + +defineTest(qtLog) { + write_file($$QMAKE_CONFIG_LOG, 1, append) + $$QMAKE_CONFIG_VERBOSE: for (l, 1): log("$$l$$escape_expand(\\n)") +} + +defineTest(qtRunLoggedCommand) { + qtLog("+ $$1") + + output = $$system("( $$1 ) 2>&1", lines, result) + qtLog($$output) + !isEmpty(2) { + $$2 = $$output + export($$2) + } + + !equals(result, 0): return(false) + return(true) +} + +isEmpty(QMAKE_CONFIG_TESTS_DIR): QMAKE_CONFIG_TESTS_DIR = $$_PRO_FILE_PWD_/config.tests + +# Ensure that a cache is present. If none was found on startup, this will create +# one in the build directory of the project which loads this feature. +cache() + +QMAKE_CONFIG_LOG = $$dirname(_QMAKE_CACHE_)/config.log +write_file($$QMAKE_CONFIG_LOG, "") diff --git a/mkspecs/features/create_cmake.prf b/mkspecs/features/create_cmake.prf index 11fb52a0b1..1099e14b17 100644 --- a/mkspecs/features/create_cmake.prf +++ b/mkspecs/features/create_cmake.prf @@ -78,7 +78,7 @@ contains(CMAKE_PLUGIN_DIR, "^\\.\\./.*") { CMAKE_PLUGIN_DIR_IS_ABSOLUTE = True } -win32:!wince:!static:!staticlib { +win32:!static:!staticlib { CMAKE_DLL_DIR = $$cmakeRelativePath($$[QT_INSTALL_BINS], $$[QT_INSTALL_PREFIX]) contains(CMAKE_DLL_DIR, "^\\.\\./.*") { CMAKE_DLL_DIR = $$[QT_INSTALL_BINS]/ @@ -208,7 +208,7 @@ mac { CMAKE_PRL_FILE_LOCATION_DEBUG = lib$${CMAKE_QT_STEM}_debug.prl CMAKE_PRL_FILE_LOCATION_RELEASE = lib$${CMAKE_QT_STEM}.prl } else { - CONFIG(qt_framework, qt_framework|qt_no_framework) { + qt_framework { CMAKE_LIB_FILE_LOCATION_DEBUG = Qt$${CMAKE_MODULE_NAME}$${QT_LIBINFIX}.framework/Qt$${CMAKE_MODULE_NAME}$${QT_LIBINFIX} CMAKE_LIB_FILE_LOCATION_RELEASE = Qt$${CMAKE_MODULE_NAME}$${QT_LIBINFIX}.framework/Qt$${CMAKE_MODULE_NAME}$${QT_LIBINFIX} CMAKE_BUILD_IS_FRAMEWORK = "true" diff --git a/mkspecs/features/ctest_testcase_common.prf b/mkspecs/features/ctest_testcase_common.prf index 7183ade4df..40e41900e8 100644 --- a/mkspecs/features/ctest_testcase_common.prf +++ b/mkspecs/features/ctest_testcase_common.prf @@ -69,8 +69,8 @@ for (dep, dependentmodules): \ mod_deps += $$cmakeModuleName($$dep) dependentmodules = $$join(mod_deps, ";") -contains(QT_CONFIG, angle): CMAKE_GL_DEFINES = -DQT_WITH_ANGLE=True -!contains(QT_CONFIG, egl): CMAKE_GL_DEFINES += -DNO_EGL=True +qtConfig(angle): CMAKE_GL_DEFINES = -DQT_WITH_ANGLE=True +!qtConfig(egl): CMAKE_GL_DEFINES += -DNO_EGL=True CMAKE_MODULE_VERSIONS = CMAKE_MODULES_UNDER_TEST = diff --git a/mkspecs/features/default_post.prf b/mkspecs/features/default_post.prf index e7e9a5bd87..edc3cac7ba 100644 --- a/mkspecs/features/default_post.prf +++ b/mkspecs/features/default_post.prf @@ -116,6 +116,29 @@ c++11|c++14|c++1z { unset(cxxstd) } +suffix = +for(ever) { + QMAKE_USE$${suffix} = $$unique(QMAKE_USE$${suffix}) + for(name, QMAKE_USE$${suffix}) { + nu = $$upper($$name) + CONFIG(debug, debug|release): \ + libs = $$eval(QMAKE_LIBS_$${nu}_DEBUG) + else: \ + libs = $$eval(QMAKE_LIBS_$${nu}_RELEASE) + libs += $$eval(QMAKE_LIBS_$$nu) + cflags = $$eval(QMAKE_CFLAGS_$${nu}) + + isEmpty(libs):isEmpty(cflags): \ + error("Library '$$name' is not defined.") + + LIBS$${suffix} += $$libs + QMAKE_CFLAGS += $$cflags + QMAKE_CXXFLAGS += $$cflags + } + !isEmpty(suffix): break() + suffix = "_PRIVATE" +} + !precompile_header: SOURCES += $$NO_PCH_SOURCES QMAKE_INCDIR += $$QMAKE_INCDIR_POST diff --git a/mkspecs/features/default_pre.prf b/mkspecs/features/default_pre.prf index 62cd90912e..b655f2e0ca 100644 --- a/mkspecs/features/default_pre.prf +++ b/mkspecs/features/default_pre.prf @@ -8,8 +8,6 @@ CONFIG = \ testcase_targets import_plugins import_qpa_plugin \ $$CONFIG -contains(QT_CONFIG, c++11):lessThan(QT_COMPILER_STDCXX, 201103): CONFIG += c++11 - !build_pass:defined(QT_EDITION, var):!equals(QT_EDITION, "OpenSource"):!equals(QT_EDITION, "Preview") { # # call license checker (but cache result for one day) @@ -56,6 +54,11 @@ isEmpty(QMAKE_DEFAULT_INCDIRS):!host_build { } } QMAKE_DEFAULT_LIBDIRS = $$unique(QMAKE_DEFAULT_LIBDIRS) + } else: msvc { + LIB = $$getenv("LIB") + QMAKE_DEFAULT_LIBDIRS = $$split(LIB, $$QMAKE_DIRLIST_SEP) + INCLUDE = $$getenv("INCLUDE") + QMAKE_DEFAULT_INCDIRS = $$split(INCLUDE, $$QMAKE_DIRLIST_SEP) } unix { diff --git a/mkspecs/features/java.prf b/mkspecs/features/java.prf index 0f0b991f0a..852954e5ca 100644 --- a/mkspecs/features/java.prf +++ b/mkspecs/features/java.prf @@ -28,7 +28,7 @@ javac.commands = javac -source 6 -target 6 -Xlint:unchecked -bootclasspath $$AND javac.depends = FORCE QMAKE_EXTRA_COMPILERS += javac -mkpath($$absolute_path($$CLASS_DIR, $$OUT_PWD)) | error("Aborting.") +mkpath($$absolute_path($$CLASS_DIR, $$OUT_PWD))|error() # Disable all linker flags since we are overriding the regular linker QMAKE_LFLAGS = diff --git a/mkspecs/features/mac/default_post.prf b/mkspecs/features/mac/default_post.prf index 30960e66dd..7bf6b4a716 100644 --- a/mkspecs/features/mac/default_post.prf +++ b/mkspecs/features/mac/default_post.prf @@ -2,11 +2,11 @@ load(default_post) !no_objective_c:CONFIG += objective_c qt { - contains(QT_CONFIG, static) { + qtConfig(static) { # C++11 support means using libc++ instead of libstd++. As the # two libraries are incompatible we need to ensure the end user # project is built using the same C++11 support/no support as Qt. - contains(QT_CONFIG, c++11) { + qtConfig(c++11) { CONFIG += c++11 } else: c++11 { warning("Qt was not built with C++11 enabled, disabling feature") diff --git a/mkspecs/features/mac/default_pre.prf b/mkspecs/features/mac/default_pre.prf index 5df99d1acd..4c8c80d101 100644 --- a/mkspecs/features/mac/default_pre.prf +++ b/mkspecs/features/mac/default_pre.prf @@ -40,7 +40,7 @@ isEmpty(QMAKE_TARGET_BUNDLE_PREFIX) { # and put it into a dSYM file. We don't need that dSYM file in # the first place, since the information is available in the # object files inside the archives (static libraries). -contains(QT_CONFIG, static): \ +macx-xcode:qtConfig(static): \ QMAKE_XCODE_DEBUG_INFORMATION_FORMAT = dwarf # This variable is used by the xcode_dynamic_library_suffix diff --git a/mkspecs/features/mac/sdk.prf b/mkspecs/features/mac/sdk.prf index ab37b1740f..1db1db7b26 100644 --- a/mkspecs/features/mac/sdk.prf +++ b/mkspecs/features/mac/sdk.prf @@ -24,12 +24,6 @@ QMAKE_MAC_SDK_PATH = $$xcodeSDKInfo(Path) QMAKE_MAC_SDK_PLATFORM_PATH = $$xcodeSDKInfo(PlatformPath) QMAKE_MAC_SDK_VERSION = $$xcodeSDKInfo(SDKVersion) -!equals(MAKEFILE_GENERATOR, XCODE) { - QMAKE_CFLAGS += -isysroot $$QMAKE_MAC_SDK_PATH - QMAKE_CXXFLAGS += -isysroot $$QMAKE_MAC_SDK_PATH - QMAKE_LFLAGS += -Wl,-syslibroot,$$QMAKE_MAC_SDK_PATH -} - sysrootified = for(val, QMAKE_INCDIR_OPENGL): sysrootified += $${QMAKE_MAC_SDK_PATH}$$val QMAKE_INCDIR_OPENGL = $$sysrootified @@ -55,19 +49,74 @@ for(tool, $$list(QMAKE_CC QMAKE_CXX QMAKE_FIX_RPATH QMAKE_AR QMAKE_RANLIB QMAKE_ } !equals(MAKEFILE_GENERATOR, XCODE) { - ios:!host_buildĀ { - simulator: \ - version_identifier = $$simulator.deployment_identifier - else: \ - version_identifier = $$device.deployment_identifier - deployment_target = $$QMAKE_IOS_DEPLOYMENT_TARGET + uikit:!host_buildĀ { + ios: os_var = IOS + tvos: os_var = TVOS + watchos: os_var = WATCHOS + + deployment_target = $$eval(QMAKE_$${os_var}_DEPLOYMENT_TARGET) + !simulator|simulator_and_device: device_archs = $$eval(QMAKE_$${os_var}_DEVICE_ARCHS) + simulator: simulator_archs = $$eval(QMAKE_$${os_var}_SIMULATOR_ARCHS) + archs = $$device_archs $$simulator_archs + + QMAKE_XARCH_CFLAGS = + QMAKE_XARCH_LFLAGS = + QMAKE_EXTRA_VARIABLES += QMAKE_XARCH_CFLAGS QMAKE_XARCH_LFLAGS + + single_arch { + device_archs = $$first(device_archs) + simulator_archs = $$first(simulator_archs) + archs = $$first(archs) + } + + for(arch, archs) { + contains(simulator_archs, $$arch) { + sdk = $$simulator.sdk + version_identifier = $$simulator.deployment_identifier + } else { + sdk = $$device.sdk + version_identifier = $$device.deployment_identifier + } + + version_min_flags = \ + -Xarch_$${arch} \ + -m$${version_identifier}-version-min=$$deployment_target + QMAKE_XARCH_CFLAGS_$${arch} = $$version_min_flags \ + -Xarch_$${arch} \ + -isysroot$$xcodeSDKInfo(Path, $$sdk) + QMAKE_XARCH_LFLAGS_$${arch} = $$version_min_flags \ + -Xarch_$${arch} \ + -Wl,-syslibroot,$$xcodeSDKInfo(Path, $$sdk) + + QMAKE_XARCH_CFLAGS += $(EXPORT_QMAKE_XARCH_CFLAGS_$${arch}) + QMAKE_XARCH_LFLAGS += $(EXPORT_QMAKE_XARCH_CFLAGS_$${arch}) + + QMAKE_EXTRA_VARIABLES += \ + QMAKE_XARCH_CFLAGS_$${arch} \ + QMAKE_XARCH_LFLAGS_$${arch} + } + + QMAKE_CFLAGS_USE_PRECOMPILE = + for(arch, archs) { + QMAKE_CFLAGS_USE_PRECOMPILE += \ + -Xarch_$${arch} \ + -include${QMAKE_PCH_OUTPUT_$${arch}} + } + QMAKE_CXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE + QMAKE_OBJCFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE + QMAKE_OBJCXXFLAGS_USE_PRECOMPILE = $$QMAKE_CFLAGS_USE_PRECOMPILE + + QMAKE_PCH_OUTPUT_EXT = _${QMAKE_PCH_ARCH}$${QMAKE_PCH_OUTPUT_EXT} } else: osx { version_identifier = macosx deployment_target = $$QMAKE_MACOSX_DEPLOYMENT_TARGET + version_min_flag = -m$${version_identifier}-version-min=$$deployment_target + QMAKE_CFLAGS += -isysroot $$QMAKE_MAC_SDK_PATH $$version_min_flag + QMAKE_CXXFLAGS += -isysroot $$QMAKE_MAC_SDK_PATH $$version_min_flag + QMAKE_LFLAGS += -Wl,-syslibroot,$$QMAKE_MAC_SDK_PATH $$version_min_flag } - version_min_flag = -m$${version_identifier}-version-min=$$deployment_target - QMAKE_CFLAGS += $$version_min_flag - QMAKE_CXXFLAGS += $$version_min_flag - QMAKE_LFLAGS += $$version_min_flag + QMAKE_CFLAGS += $(EXPORT_QMAKE_XARCH_CFLAGS) + QMAKE_CXXFLAGS += $(EXPORT_QMAKE_XARCH_CFLAGS) + QMAKE_LFLAGS += $(EXPORT_QMAKE_XARCH_LFLAGS) } diff --git a/mkspecs/features/moc.prf b/mkspecs/features/moc.prf index 8ddfc38c63..4c81ee5c74 100644 --- a/mkspecs/features/moc.prf +++ b/mkspecs/features/moc.prf @@ -21,11 +21,27 @@ win32:count(MOC_INCLUDEPATH, 40, >) { WIN_INCLUDETEMP_CONT = for (inc, MOC_INCLUDEPATH): \ WIN_INCLUDETEMP_CONT += -I$$inc - write_file($$absolute_path($$WIN_INCLUDETEMP, $$OUT_PWD), WIN_INCLUDETEMP_CONT)|error("Aborting.") + write_file($$absolute_path($$WIN_INCLUDETEMP, $$OUT_PWD), WIN_INCLUDETEMP_CONT)|error() +} + +# QNX's compiler sets "gcc" config, but does not support the -dM option; +# UIKit builds are always multi-arch due to simulator_and_device (unless +# -sdk is used) so this feature cannot possibly work. +if(gcc|intel_icl|msvc):!rim_qcc:!uikit { + moc_predefs.CONFIG = no_link + gcc: moc_predefs.commands = $$QMAKE_CXX $$QMAKE_CXXFLAGS -dM -E -o ${QMAKE_FILE_OUT} ${QMAKE_FILE_IN} + else:intel_icl: moc_predefs.commands = $$QMAKE_CXX $$QMAKE_CXXFLAGS -QdM -P -Fi${QMAKE_FILE_OUT} ${QMAKE_FILE_IN} + else:msvc { + moc_predefs.commands += $$QMAKE_CXX -Bx$$shell_path($$[QT_INSTALL_BINS/get]/qmake) $$QMAKE_CXXFLAGS -E ${QMAKE_FILE_IN} 2>NUL >${QMAKE_FILE_OUT} + } else: error("Oops, I messed up") + moc_predefs.output = $$MOC_DIR/moc_predefs.h + moc_predefs.input = MOC_PREDEF_FILE + silent: moc_predefs.commands = @echo generating $$moc_predefs.output$$escape_expand(\n\t)@$$moc_predefs.commands + QMAKE_EXTRA_COMPILERS += moc_predefs + MOC_PREDEF_FILE = $$[QT_HOST_DATA/src]/mkspecs/features/data/dummy.cpp } defineReplace(mocCmdBase) { - RET = !isEmpty(WIN_INCLUDETEMP) { incvar = @$$WIN_INCLUDETEMP } else { @@ -34,7 +50,14 @@ defineReplace(mocCmdBase) { incvar += -I$$shell_quote($$inc) incvar += $$QMAKE_FRAMEWORKPATH_FLAGS } - RET += $$QMAKE_MOC $(DEFINES) $$join(QMAKE_COMPILER_DEFINES, " -D", -D) $$incvar $$QMAKE_MOC_OPTIONS + + RET = $$QMAKE_MOC $(DEFINES) + msvc: RET += --compiler-flavor=msvc + + isEmpty(MOC_PREDEF_FILE): RET += $$join(QMAKE_COMPILER_DEFINES, " -D", -D) + else: RET += --include $$moc_predefs.output + + RET += $$incvar $$QMAKE_MOC_OPTIONS return($$RET) } @@ -46,7 +69,7 @@ moc_header.output = $$MOC_DIR/$${QMAKE_H_MOD_MOC}${QMAKE_FILE_BASE}$${first(QMAK moc_header.input = HEADERS moc_header.variable_out = SOURCES moc_header.name = MOC ${QMAKE_FILE_IN} -moc_header.depends += $$WIN_INCLUDETEMP +moc_header.depends += $$WIN_INCLUDETEMP $$moc_predefs.output silent:moc_header.commands = @echo moc ${QMAKE_FILE_IN} && $$moc_header.commands QMAKE_EXTRA_COMPILERS += moc_header INCREDIBUILD_XGE += moc_header @@ -58,7 +81,7 @@ moc_source.commands = ${QMAKE_FUNC_mocCmdBase} ${QMAKE_FILE_IN} -o ${QMAKE_FILE_ moc_source.output = $$MOC_DIR/$${QMAKE_CPP_MOD_MOC}${QMAKE_FILE_BASE}$${QMAKE_EXT_CPP_MOC} moc_source.input = SOURCES OBJECTIVE_SOURCES moc_source.name = MOC ${QMAKE_FILE_IN} -moc_source.depends += $$WIN_INCLUDETEMP +moc_source.depends += $$WIN_INCLUDETEMP $$moc_predefs.output silent:moc_source.commands = @echo moc ${QMAKE_FILE_IN} && $$moc_source.commands QMAKE_EXTRA_COMPILERS += moc_source INCREDIBUILD_XGE += moc_source diff --git a/mkspecs/features/no_debug_info.prf b/mkspecs/features/no_debug_info.prf index 1307679a39..da14b7bb27 100644 --- a/mkspecs/features/no_debug_info.prf +++ b/mkspecs/features/no_debug_info.prf @@ -1,5 +1,4 @@ - -win32-msvc2*|wince*msvc* { +msvc { QMAKE_CFLAGS -= -Zi QMAKE_CFLAGS_DEBUG -= -Zi QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO -= -Zi diff --git a/mkspecs/features/qml_plugin.prf b/mkspecs/features/qml_plugin.prf index a367dc3337..149b3cff56 100644 --- a/mkspecs/features/qml_plugin.prf +++ b/mkspecs/features/qml_plugin.prf @@ -15,9 +15,9 @@ TEMPLATE = lib CONFIG += plugin if(win32|mac):!macx-xcode { - contains(QT_CONFIG, simulator_and_device): CONFIG += simulator_and_device - contains(QT_CONFIG, debug_and_release): CONFIG += debug_and_release - contains(QT_CONFIG, build_all): CONFIG += build_all + qtConfig(simulator_and_device): CONFIG += simulator_and_device + qtConfig(debug_and_release): CONFIG += debug_and_release + qtConfig(build_all): CONFIG += build_all } CONFIG += relative_qt_rpath # Qt's QML plugins should be relocatable diff --git a/mkspecs/features/qpa/basicunixfontdatabase.prf b/mkspecs/features/qpa/basicunixfontdatabase.prf index f2e4016c99..80382da214 100644 --- a/mkspecs/features/qpa/basicunixfontdatabase.prf +++ b/mkspecs/features/qpa/basicunixfontdatabase.prf @@ -1,3 +1,3 @@ -contains(QT_CONFIG, system-freetype) { +qtConfig(system-freetype) { LIBS_PRIVATE += -lfreetype } diff --git a/mkspecs/features/qpa/genericunixfontdatabase.prf b/mkspecs/features/qpa/genericunixfontdatabase.prf index b0d81e5571..3680d5ad79 100644 --- a/mkspecs/features/qpa/genericunixfontdatabase.prf +++ b/mkspecs/features/qpa/genericunixfontdatabase.prf @@ -1,5 +1,5 @@ CONFIG += qpa/basicunixfontdatabase -contains(QT_CONFIG, fontconfig) { +qtConfig(fontconfig) { DEFINES += Q_FONTCONFIGDATABASE LIBS_PRIVATE += -lfontconfig } else:!android { diff --git a/mkspecs/features/qt.prf b/mkspecs/features/qt.prf index 71e96b4651..0c5f080ec1 100644 --- a/mkspecs/features/qt.prf +++ b/mkspecs/features/qt.prf @@ -2,14 +2,14 @@ CONFIG *= thread #handle defines win32 { - contains(QT_CONFIG, shared) { + qtConfig(shared) { # this variable is read by qmake in qmake/generators/win32/msvc_vcproj.cpp # function VcprojGenerator::initDeploymentTool() QMAKE_DLL_PATHS += $$[QT_INSTALL_BINS/get] } } CONFIG(release, debug|release):DEFINES += QT_NO_DEBUG -contains(QT_CONFIG, force_asserts):DEFINES += QT_FORCE_ASSERTS +qtConfig(force_asserts): DEFINES += QT_FORCE_ASSERTS no_keywords:DEFINES += QT_NO_KEYWORDS plugin { #Qt plugins static:DEFINES += QT_STATICPLUGIN @@ -165,7 +165,7 @@ for(ever) { qt_module_deps = $$CLEAN_QT $$CLEAN_QT_PRIVATE qt_module_deps = $$resolve_depends(qt_module_deps, "QT.") -!no_qt_rpath:!static:contains(QT_CONFIG, rpath):!contains(QT_CONFIG, static):\ +!no_qt_rpath:!static:qtConfig(rpath):!qtConfig(static):\ contains(qt_module_deps, core) { relative_qt_rpath:!isEmpty(QMAKE_REL_RPATH_BASE):contains(INSTALLS, target):\ isEmpty(target.files):isEmpty(target.commands):isEmpty(target.extra) { @@ -176,7 +176,7 @@ qt_module_deps = $$resolve_depends(qt_module_deps, "QT.") } } -!isEmpty(QMAKE_LFLAGS_RPATHLINK):!contains(QT_CONFIG, static) { +!isEmpty(QMAKE_LFLAGS_RPATHLINK):!qtConfig(static) { # -rpath-link is used by the linker to find dependencies of dynamic # libraries which were NOT specified on the command line. # This means that paths of direct dependencies (QT & QT_PRIVATE) @@ -191,7 +191,7 @@ qt_module_deps = $$resolve_depends(qt_module_deps, "QT.") # static builds: link qml import plugins into the app. contains(qt_module_deps, qml): \ - contains(QT_CONFIG, static):contains(TEMPLATE, .*app):!host_build:!no_import_scan { + qtConfig(static):contains(TEMPLATE, .*app):!host_build:!no_import_scan { !isEmpty(QTREPOS) { for (qrep, QTREPOS): \ exists($$qrep/qml): \ @@ -238,7 +238,7 @@ contains(qt_module_deps, qml): \ } } QML_IMPORT_CPP = $$OUT_PWD/$$lower($$basename(TARGET))_qml_plugin_import.cpp - write_file($$QML_IMPORT_CPP, IMPORT_FILE_CONT)|error("Aborting.") + write_file($$QML_IMPORT_CPP, IMPORT_FILE_CONT)|error() GENERATED_SOURCES += $$QML_IMPORT_CPP QMAKE_DISTCLEAN += $$QML_IMPORT_CPP @@ -261,7 +261,7 @@ contains(qt_module_deps, qml): \ "[Paths]" \ "Imports = $$QMAKE_QML_BUNDLE_PATH" \ "Qml2Imports = $$QMAKE_QML_BUNDLE_PATH" - write_file("$$OUT_PWD/qt.conf", QT_CONF_CONTENTS)|error("Aborting.") + write_file("$$OUT_PWD/qt.conf", QT_CONF_CONTENTS)|error() # write qt.conf and copy each qml import dir into the bundle. # But strip away archives and other files that are not needed: @@ -318,7 +318,7 @@ contains(TEMPLATE, .*app) { } QT_PLUGIN_VERIFY = DEPLOYMENT_PLUGIN -contains(QT_CONFIG, static) { +qtConfig(static) { QT_PLUGIN_VERIFY += QTPLUGIN force_import_plugins|contains(TEMPLATE, .*app) { import_plugins:!isEmpty(QTPLUGIN) { @@ -334,7 +334,7 @@ contains(QT_CONFIG, static) { warning("Plugin class name could not be determined for $$IMPORT_PLUG plugin.") } IMPORT_CPP = $$OUT_PWD/$$lower($$basename(TARGET))_plugin_import.cpp - write_file($$IMPORT_CPP, IMPORT_FILE_CONT)|error("Aborting.") + write_file($$IMPORT_CPP, IMPORT_FILE_CONT)|error() GENERATED_SOURCES += $$IMPORT_CPP QMAKE_DISTCLEAN += $$IMPORT_CPP } @@ -365,7 +365,7 @@ for(QT_CURRENT_VERIFY, $$list($$QT_PLUGIN_VERIFY)) { # The following block is currently broken, because qt_plugin_XXX.prf files # are not generated for dynamic builds. - false:isEqual(QT_CURRENT_VERIFY, DEPLOYMENT_PLUGIN):shared:if(wince*|winrt) { + false:isEqual(QT_CURRENT_VERIFY, DEPLOYMENT_PLUGIN):shared:winrt { QT_ITEM = debug: QT_ITEM = $${QTPLUG}d4.dll else: QT_ITEM = $${QTPLUG}4.dll diff --git a/mkspecs/features/qt_android_deps.prf b/mkspecs/features/qt_android_deps.prf index 7ec7bad047..c172ca8c4e 100644 --- a/mkspecs/features/qt_android_deps.prf +++ b/mkspecs/features/qt_android_deps.prf @@ -79,7 +79,7 @@ DEPENDENCY_FILE = $$ANDROID_DEPENDS_DIR$$TARGET-android-dependencies.xml !isEmpty(FILE_CONTENT) { FILE_CONTENT = "<rules><dependencies><lib name=\"$$TARGET\"><depends>" $$FILE_CONTENT "</depends></lib></dependencies></rules>" - write_file($$DEPENDENCY_FILE, FILE_CONTENT) | error("Aborting.") + write_file($$DEPENDENCY_FILE, FILE_CONTENT)|error() } } diff --git a/mkspecs/features/qt_app.prf b/mkspecs/features/qt_app.prf index 46aca50cc2..90135b00a3 100644 --- a/mkspecs/features/qt_app.prf +++ b/mkspecs/features/qt_app.prf @@ -21,14 +21,14 @@ QMAKE_LFLAGS += $$QMAKE_LFLAGS_GCSECTIONS host_build: QT -= gui # no host tool will ever use gui host_build:force_bootstrap { - !build_pass:contains(QT_CONFIG, release_tools): CONFIG += release + !build_pass:qtConfig(release_tools): CONFIG += release contains(QT, core(-private)?|xml) { QT -= core core-private xml QT += bootstrap-private } target.path = $$[QT_HOST_BINS] } else { - !build_pass:contains(QT_CONFIG, debug_and_release): CONFIG += release + !build_pass:qtConfig(debug_and_release): CONFIG += release target.path = $$[QT_INSTALL_BINS] CONFIG += relative_qt_rpath # Qt's tools and apps should be relocatable } diff --git a/mkspecs/features/qt_build_config.prf b/mkspecs/features/qt_build_config.prf index 1e2d5c2423..e0e8ef9f51 100644 --- a/mkspecs/features/qt_build_config.prf +++ b/mkspecs/features/qt_build_config.prf @@ -12,7 +12,7 @@ !contains(QMAKE_INTERNAL_INCLUDED_FILES, .*qmodule\\.pri) { QMAKE_QT_MODULE = $$[QT_HOST_DATA/get]/mkspecs/qmodule.pri !exists($$QMAKE_QT_MODULE)|!include($$QMAKE_QT_MODULE, "", true) { - error("Cannot load qmodule.pri!") + debug(1, "Cannot load qmodule.pri!") } else { debug(1, "Loaded qmodule.pri from ($$QMAKE_QT_MODULE)") } @@ -64,7 +64,7 @@ CONFIG = qt_build_extra $$CONFIG cross_compile: \ CONFIG += force_bootstrap -android|ios|winrt: \ +android|uikit|winrt: \ CONFIG += builtin_testdata CONFIG += \ diff --git a/mkspecs/features/qt_build_extra.prf b/mkspecs/features/qt_build_extra.prf index abdd7bc2b2..a1512c3a19 100644 --- a/mkspecs/features/qt_build_extra.prf +++ b/mkspecs/features/qt_build_extra.prf @@ -23,7 +23,8 @@ INCLUDEPATH += $$EXTRA_INCLUDEPATH # The other flags are relevant only for actual libraries. equals(TEMPLATE, aux): return() -LIBS += $$EXTRA_LIBS +QMAKE_LIBDIR += $$EXTRA_LIBDIR +QMAKE_FRAMEWORKPATH += $$EXTRA_FRAMEWORKPATH # Static libs need no rpaths static: return() diff --git a/mkspecs/features/qt_common.prf b/mkspecs/features/qt_common.prf index 59e08fc124..d30983f62b 100644 --- a/mkspecs/features/qt_common.prf +++ b/mkspecs/features/qt_common.prf @@ -12,15 +12,14 @@ QMAKE_DIR_REPLACE_SANE += DESTDIR CONFIG -= debug_and_release_target -contains(QT_CONFIG, c++11): CONFIG += c++11 strict_c++ -contains(QT_CONFIG, c++14): CONFIG += c++14 -contains(QT_CONFIG, c++1z): CONFIG += c++1z +qtConfig(c++11): CONFIG += c++11 strict_c++ +qtConfig(c++14): CONFIG += c++14 +qtConfig(c++1z): CONFIG += c++1z contains(TEMPLATE, .*lib) { # module and plugins - !host_build:contains(QT_CONFIG, reduce_exports): CONFIG += hide_symbols - unix:contains(QT_CONFIG, reduce_relocations): CONFIG += bsymbolic_functions - contains(QT_CONFIG, largefile): CONFIG += largefile - contains(QT_CONFIG, separate_debug_info): CONFIG += separate_debug_info + !host_build:qtConfig(reduce_exports): CONFIG += hide_symbols + unix:qtConfig(reduce_relocations): CONFIG += bsymbolic_functions + qtConfig(separate_debug_info): CONFIG += separate_debug_info !isEmpty(_QMAKE_SUPER_CACHE_): \ rplbase = $$dirname(_QMAKE_SUPER_CACHE_)/[^/][^/]* @@ -61,6 +60,8 @@ clang { QMAKE_CXXFLAGS_WARN_ON += -Wvla # GCC 5 introduced -Wdate-time greaterThan(QT_GCC_MAJOR_VERSION, 4): QMAKE_CXXFLAGS_WARN_ON += -Wdate-time + # GCC 6 introduced these + greaterThan(QT_GCC_MAJOR_VERSION, 5): QMAKE_CXXFLAGS_WARN_ON += -Wshift-overflow=2 -Wduplicated-cond } warnings_are_errors:warning_clean { diff --git a/mkspecs/features/qt_configure.prf b/mkspecs/features/qt_configure.prf new file mode 100644 index 0000000000..bb93e3cffd --- /dev/null +++ b/mkspecs/features/qt_configure.prf @@ -0,0 +1,1590 @@ +CONFIG -= qt debug_and_release +load(configure_base) + +QMAKE_CONFIG_CACHE = $$dirname(_QMAKE_CACHE_)/config.cache +QMAKE_CONFIG_CACHE_USE = all + +QT_CONFIGURE_REPORT = +QT_CONFIGURE_NOTES = +QT_CONFIGURE_WARNINGS = +QT_CONFIGURE_ERRORS = + +defineTest(qtConfAddReport) { + QT_CONFIGURE_REPORT += "$$join(1, $$escape_expand(\\n))" + export(QT_CONFIGURE_REPORT) +} + +defineTest(qtConfAddNote) { + QT_CONFIGURE_NOTES += "Note: $$join(1, $$escape_expand(\\n))" + export(QT_CONFIGURE_NOTES) +} + +defineTest(qtConfAddWarning) { + QT_CONFIGURE_WARNINGS += "WARNING: $$join(1, $$escape_expand(\\n))" + export(QT_CONFIGURE_WARNINGS) +} + +defineTest(qtConfAddError) { + QT_CONFIGURE_ERRORS += "ERROR: $$join(1, $$escape_expand(\\n))" + export(QT_CONFIGURE_ERRORS) + equals(2, log) { + CONFIG += mention_config_log + export(CONFIG) + } +} + +defineTest(qtConfCommandlineSetInput) { + arg = $${1} + val = $${2} + !isEmpty(config.commandline.options.$${arg}.name): \ + arg = $$eval(config.commandline.options.$${arg}.name) + + config.input.$$arg = $$val + export(config.input.$$arg) +} + +defineReplace(qtConfGetNextCommandlineArg) { + c = $$take_first(QMAKE_EXTRA_ARGS) + export(QMAKE_EXTRA_ARGS) + return($$c) +} + +defineReplace(qtConfPeekNextCommandlineArg) { + return($$first(QMAKE_EXTRA_ARGS)) +} + +defineTest(qtConfCommandline_boolean) { + opt = $${1} + val = $${2} + isEmpty(val): val = yes + + !equals(val, yes):!equals(val, no) { + qtConfAddError("Invalid value given for boolean command line option '$$opt'.") + return() + } + + qtConfCommandlineSetInput($$opt, $$val) +} + +defineTest(qtConfCommandline_void) { + opt = $${1} + val = $${2} + !isEmpty(val) { + qtConfAddError("Command line option '$$opt' expects no argument ('$$val' given).") + return() + } + + val = $$eval(config.commandline.options.$${opt}.value) + isEmpty(val): val = yes + + qtConfCommandlineSetInput($$opt, $$val) +} + +defineTest(qtConfCommandline_enum) { + opt = $${1} + val = $${2} + isEmpty(val): val = yes + + # validate and map value + mapped = $$eval(config.commandline.options.$${opt}.values.$${val}) + isEmpty(mapped) { + # just a list of allowed values + for (i, config.commandline.options.$${opt}.values._KEYS_) { + equals(config.commandline.options.$${opt}.values.$${i}, $$val) { + mapped = $$val + break() + } + } + } + isEmpty(mapped) { + qtConfAddError("Invalid value '$$val' supplied to command line option '$$opt'.") + return() + } + + qtConfCommandlineSetInput($$opt, $$mapped) +} + +defineTest(qtConfValidateValue) { + opt = $${1} + val = $${2} + + validValues = $$eval(config.commandline.options.$${opt}.values._KEYS_) + isEmpty(validValues): \ + return(true) + + for (i, validValues) { + equals(config.commandline.options.$${opt}.values.$${i}, $$val): \ + return(true) + } + + qtConfAddError("Invalid value '$$val' supplied to command line option '$$opt'.") + return(false) +} + +defineTest(qtConfCommandline_string) { + opt = $${1} + val = $${2} + isEmpty(val): val = $$qtConfGetNextCommandlineArg() + + # Note: Arguments which are variable assignments are legit here. + contains(val, "^-.*")|isEmpty(val) { + qtConfAddError("No value supplied to command line option '$$opt'.") + return() + } + + !qtConfValidateValue($$opt, $$val): \ + return() + + qtConfCommandlineSetInput($$opt, $$val) +} + +defineTest(qtConfCommandline_optionalString) { + opt = $${1} + val = $${2} + isEmpty(val) { + v = $$qtConfPeekNextCommandlineArg() + contains(v, "^-.*|[A-Z_]+=.*")|isEmpty(v): \ + val = "yes" + else: \ + val = $$qtConfGetNextCommandlineArg() + } + + !qtConfValidateValue($$opt, $$val): \ + return() + + qtConfCommandlineSetInput($$opt, $$val) +} + + +defineTest(qtConfCommandline_addString) { + opt = $${1} + val = $${2} + isEmpty(val): val = $$qtConfGetNextCommandlineArg() + + contains(val, "^-.*|[A-Z_]+=.*")|isEmpty(val) { + qtConfAddError("No value supplied to command line option '$$opt'.") + return() + } + + !qtConfValidateValue($$opt, $$val): \ + return() + + !isEmpty(config.commandline.options.$${opt}.name): \ + opt = $$eval(config.commandline.options.$${opt}.name) + + config.input.$$opt += $$val + export(config.input.$$opt) +} + +defineTest(qtConfParseCommandLine) { + custom = $$config.commandline.custom + customCall = + !isEmpty(custom) { + customCall = qtConfCommandline_$$custom + !defined($$customCall, test): \ + error("Custom command line callback '$$custom' is undefined.") + } + + for (ever) { + c = $$qtConfGetNextCommandlineArg() + isEmpty(c): break() + + # handle options to turn on verbose logging + contains(c, "^-v")|contains(c, "^--?verbose") { + QMAKE_CONFIG_VERBOSE = true + export(QMAKE_CONFIG_VERBOSE) + next() + } + contains(c, "^-no-v")|contains(c, "^--?no-verbose") { + QMAKE_CONFIG_VERBOSE = false + export(QMAKE_CONFIG_VERBOSE) + next() + } + + contains(c, "^--?recheck") { + QMAKE_CONFIG_CACHE_USE = positive + export(QMAKE_CONFIG_CACHE_USE) + next() + } + contains(c, "^--?recheck-all") { + QMAKE_CONFIG_CACHE_USE = none + export(QMAKE_CONFIG_CACHE_USE) + next() + } + + !isEmpty(customCall) { + $${customCall}($$c): \ + next() + } + + contains(c, "([A-Z_]+)=(.*)") { + opt = $$replace(c, "^([A-Z_]+)=(.*)", "\\1") + val = $$replace(c, "^([A-Z_]+)=(.*)", "\\2") + var = $$eval(config.commandline.assignments.$${opt}) + isEmpty(var) { + qtConfAddError("Assigning unknown variable '$$opt' on command line.") + return() + } + config.input.$$var = $$val + export(config.input.$$var) + next() + } + + # parse out opt and val + contains(c, "^--?enable-(.*)") { + opt = $$replace(c, "^--?enable-(.*)", "\\1") + val = yes + } else: contains(c, "^--?(disable|no)-(.*)") { + opt = $$replace(c, "^--?(disable|no)-(.*)", "\\2") + val = no + } else: contains(c, "^--?qt-(.*)") { + opt = $$replace(c, "^--?qt-(.*)", "\\1") + val = qt + } else: contains(c, "^--?system-(.*)") { + opt = $$replace(c, "^--?system-(.*)", "\\1") + val = system + } else: contains(c, "^--?([^-].*)=(.*)") { + opt = $$replace(c, "^--?([^-].*)=(.*)", "\\1") + val = $$replace(c, "^--?([^-].*)=(.*)", "\\2") + } else: contains(c, "^--?([^-].*)") { + opt = $$replace(c, "^--?([^-].*)", "\\1") + val = + } else { + qtConfAddError("Invalid command line parameter '$$c'.") + return() + } + + type = $$eval(config.commandline.options.$${opt}) + isEmpty(type): \ + type = $$eval(config.commandline.options.$${opt}.type) + isEmpty(type) { + # no match in the regular options, try matching the prefixes + for (p, config.commandline.prefix._KEYS_) { + e = "^-$${p}(.*)" + contains(c, $$e) { + opt = $$eval(config.commandline.prefix.$${p}) + val = $$replace(c, $$e, "\\1") + type = "addString" + break() + } + } + } + # handle builtin [-no]-feature-xxx + isEmpty(type):contains(opt, "feature-(.*)") { + # simply skip for now + next() + } + + isEmpty(type) { + qtConfAddError("Unknown command line option '$$c'.") + return() + } + + call = "qtConfCommandline_$${type}" + !defined($$call, test): \ + error("Command line option '$$c' has unknown type '$$type'.") + + # now that we have opt and value, process it + $${call}($$opt, $$val) + } +} + +defineReplace(qtConfToolchainSupportsFlag) { + test_out_dir = $$shadowed($$QMAKE_CONFIG_TESTS_DIR) + test_cmd_base = "cd $$system_quote($$system_path($$test_out_dir)) &&" + + conftest = "int main() { return 0; }" + write_file("$$test_out_dir/conftest.cpp", conftest)|error() + + qtRunLoggedCommand("$$test_cmd_base $$QMAKE_CXX $${1} -o conftest-out conftest.cpp"): \ + return(true) + return(false) +} + +defineTest(qtConfTest_compilerSupportsFlag) { + flag = $$eval($${1}.flag) + + return($$qtConfToolchainSupportsFlag($$flag)) +} + +defineTest(qtConfTest_linkerSupportsFlag) { + flag = $$eval($${1}.flag) + + use_gold_linker: \ + LFLAGS = -fuse-ld=gold + + return($$qtConfToolchainSupportsFlag($$LFLAGS "-Wl,$$flag")) +} + +defineReplace(qtConfFindInPathList) { + for (dir, 2) { + exists("$$dir/$${1}"): \ + return("$$dir/$${1}") + } + return() +} + +defineReplace(qtConfFindInPath) { + ensurePathEnv() + return($$qtConfFindInPathList($$1, $$2 $$QMAKE_PATH_ENV)) +} + +defineReplace(qtConfPkgConfigEnv) { + env = + !isEmpty(PKG_CONFIG_SYSROOT_DIR): env = "$${SETENV_PFX}PKG_CONFIG_SYSROOT_DIR=$${PKG_CONFIG_SYSROOT_DIR}$${SETENV_SFX} " + !isEmpty(PKG_CONFIG_LIBDIR): env = "$$env$${SETENV_PFX}PKG_CONFIG_LIBDIR=$${PKG_CONFIG_LIBDIR}$${SETENV_SFX} " + return($$env) +} + +defineReplace(qtConfPkgConfig) { + host = $$1 + isEmpty(host): host = false + + $$host { + pkg_config = $$qtConfFindInPath("pkg-config") + } else { + pkg_config = "$$qtConfPkgConfigEnv()$$PKG_CONFIG" + } + + return($$pkg_config) +} + +defineTest(qtConfPkgConfigPackageExists) { + isEmpty(1)|isEmpty(2): \ + return(false) + + !qtRunLoggedCommand("$${1} --exists --silence-errors $${2}"): \ + return(false) + + return(true) +} + +defineReplace(qtConfPrepareArgs) { + arglist = $$split(1) + args = + for (a, arglist): \ + args += $$system_quote($$a) + return($$args) +} + +defineTest(qtConfSetupLibraries) { + for (l, config.libraries._KEYS_) { + lpfx = config.libraries.$${l} + # 'export' may be omitted, in which case it falls back to the library's name + !defined($${lpfx}.export, var) { + $${lpfx}.export = $$l + export($${lpfx}.export) + } + isEmpty($${lpfx}.sources._KEYS_): \ + error("Library $$l defines no sources") + for (s, $${lpfx}.sources._KEYS_) { + spfx = $${lpfx}.sources.$${s} + # link back to parent object + $${spfx}.library = $$l + export($${spfx}.library) + # a plain string is transformed into a structure + isEmpty($${spfx}._KEYS_) { + $${spfx}.libs = $$eval($${spfx}) + export($${spfx}.libs) + } + # if the type is missing (implicitly in the case of plain strings), assume 'inline' + isEmpty($${spfx}.type) { + $${spfx}.type = inline + export($${spfx}.type) + } + } + } + + # reverse mapping for assignments on command line. + for (a, config.commandline.assignments._KEYS_) { + apfx = config.commandline.assignments.$${a} + ra = config.commandline.rev_assignments.$$eval($$apfx) + $$ra = $$a + export($$ra) + } +} + +# the library is specified inline in a 'libs' field. +# overrides from the command line are accepted. +defineTest(qtConfLibrary_inline) { + lib = $$eval($${1}.library) + !defined($${1}.libs, var): \ + error("'inline' source in library '$$lib' does not specify 'libs'.") + + # direct libs. overwrites inline libs. + defined(config.input.$${lib}.libs, var) { + $${1}.libs = $$eval(config.input.$${lib}.libs) + export($${1}.libs) + } + + # build-specific direct libs. overwrites inline libs. + vars = + any = false + all = true + for (b, $${1}.builds._KEYS_) { + iv = $${lib}.libs.$${b} + vars += $$eval(config.commandline.rev_assignments.$${iv}) + defined(config.input.$${iv}, var) { + $${1}.builds.$${b}.libs = $$eval(config.input.$${iv}) + export($${1}.builds.$${b}.libs) + any = true + } else { + all = false + } + } + $$any:!$$all { + qtConfAddError("Either none or all of $$join(vars, ", ", [, ]) must be specified.") + return(false) + } + + # prefix. prepends to (possibly overwritten) inline libs. + prefix = $$val_escape(config.input.$${lib}.prefix) + !isEmpty(prefix) { + $${1}.includedir = $$prefix/include + export($${1}.includedir) + $${1}.cflags = -I$$prefix/include + export($${1}.cflags) + $${1}.libs = "-L$$prefix/lib $$eval($${1}.libs)" + export($${1}.libs) + } + + return(true) +} + +# the library is provided by the qmake spec. +# this source type cannot fail. +defineTest(qtConfLibrary_makeSpec) { + spec = $$eval($${1}.spec) + isEmpty(spec): \ + error("makeSpec source in library '$$eval($${1}.library)' does not specify 'spec'.") + + $${1}.includedir = "$$val_escape(QMAKE_INCDIR_$$spec)" + export($${1}.includedir) + $${1}.cflags = "$$val_escape(QMAKE_CFLAGS_$$spec)" + export($${1}.cflags) + libs = + for (l, QMAKE_LIBDIR_$$spec): \ + libs += -L$$l + libs += $$eval(QMAKE_LIBS_$$spec) + $${1}.libs = "$$val_escape(libs)" + export($${1}.libs) + + # the library definition is always in scope, so no point in exporting it. + $${1}.export = false + export($${1}.export) + + return(true) +} + +# the library is found via pkg-config. +defineTest(qtConfLibrary_pkgConfig) { + pkg_config = $$qtConfPkgConfig($$eval($${1}.host)) + isEmpty(pkg_config): \ + return(false) + args = $$qtConfPrepareArgs($$eval($${1}.args)) + + !qtConfPkgConfigPackageExists($$pkg_config, $$args): \ + return(false) + + qtRunLoggedCommand("$$pkg_config --modversion $$args", version)|return(false) + qtRunLoggedCommand("$$pkg_config --libs-only-L --libs-only-l $$args", $${1}.libs)|return(false) + qtRunLoggedCommand("$$pkg_config --cflags $$args", $${1}.cflags)|return(false) + qtRunLoggedCommand("$$pkg_config --cflags-only-I $$args", includes)|return(false) + eval(includes = $$includes) + includes ~= s/^-I//g + $${1}.includedir = "$$val_escape(includes)" + $${1}.version = $$first(version) + export($${1}.libs) + export($${1}.cflags) + export($${1}.includedir) + export($${1}.version) + return(true) +} + +defineTest(qtConfTest_getPkgConfigVariable) { + pkg_config = $$qtConfPkgConfig($$eval($${1}.host)) + isEmpty(pkg_config): \ + return(false) + args = $$qtConfPrepareArgs($$eval($${1}.pkg-config-args)) + + !qtConfPkgConfigPackageExists($$pkg_config, $$args): \ + return(false) + + variable = $$eval($${1}.pkg-config-variable) + qtRunLoggedCommand("$$pkg_config --variable=$$variable $$args", $${1}.value)|return(false) + export($${1}.value) + $${1}.cache += value + export($${1}.cache) + return(true) +} + +defineReplace(qtConfLibraryArgs) { + qmake_args = + libs = $$eval($${1}.libs) + !isEmpty(libs): \ + qmake_args += $$system_quote(LIBS += $$libs) + for (b, $${1}.builds._KEYS_): \ + qmake_args += $$system_quote(LIBS_$$upper($$b) += $$eval($${1}.builds.$${b})) + includedir = $$eval($${1}.includedir) + !isEmpty(includedir): \ + qmake_args += $$system_quote(INCLUDEPATH *= $$includedir) + return($$qmake_args) +} + +defineReplace(qtConfGetHexVersion) { + version = $$split(1, '.') + number = + for(i, 0..2) { + n = $$member(version, $$i) + isEmpty(n): n = 0 + number += $$format_number($$n, obase=16 zeropad width=2) + } + number = "0x$$join(number)" + return($$number) +} + +defineTest(qtConfExportLibrary) { + isEmpty(2): return() + !$$qtConfEvaluate($$eval($${1}.export)): return() + + output = privatePro + + eval(libs = $$eval($${1}.libs)) + eval(cflags = $$eval($${1}.cflags)) + eval(includes = $$eval($${1}.includedir)) + version = $$eval($${1}.version) + + NAME = $$upper($$2) + !isEmpty(libs): qtConfOutputVar(assign, $$output, QMAKE_LIBS_$$NAME, $$libs) + for (b, $${1}.builds._KEYS_): \ + qtConfOutputVar(assign, $$output, QMAKE_LIBS_$${NAME}_$$upper($$b), \ + $$eval($${1}.builds.$${b})) + !isEmpty(cflags): qtConfOutputVar(assign, $$output, QMAKE_CFLAGS_$$NAME, $$cflags) + !isEmpty(includes): qtConfOutputVar(assign, $$output, QMAKE_INCDIR_$$NAME, $$includes) + !isEmpty(version) { + qtConfOutputVar(assign, $$output, QMAKE_$${NAME}_VERSION, $$version) + qtConfOutputSetDefine("privateHeader", "QT_LIBRARY_VERSION_$${2}", $$qtConfGetHexVersion($$version)) + version = $$split(version, '.') + qtConfOutputVar(assign, $$output, QMAKE_$${NAME}_VERSION_MAJOR, $$member(version, 0)) + qtConfOutputVar(assign, $$output, QMAKE_$${NAME}_VERSION_MINOR, $$member(version, 1)) + qtConfOutputVar(assign, $$output, QMAKE_$${NAME}_VERSION_PATCH, $$member(version, 2)) + } +} + +defineTest(qtConfHandleLibrary) { + lpfx = config.libraries.$$1 + defined($${lpfx}.result, var): return() + + qtConfEnsureTestTypeDeps("library") + + qtConfLoadResult($${lpfx}, $$1) { + $$eval($${lpfx}.result): \ + qtConfExportLibrary($${lpfx}.sources.$$eval($${lpfx}.source), $$eval($${lpfx}.export)) + return() + } + + qtLogTestIntro($${lpfx}) + msg = "looking for library $${1}" + write_file($$QMAKE_CONFIG_LOG, msg, append) + + result = false + for (s, $${lpfx}.sources._KEYS_) { + qtLog("Trying source $$s of library $${1}.") + spfx = $${lpfx}.sources.$${s} + + t = $$eval($${spfx}.type) + call = qtConfLibrary_$$t + !defined($$call, test): \ + error("Library $${1} source $${s} has unknown type '$$t'") + + !$$qtConfEvaluate($$eval($${spfx}.condition)) { + qtLog("Source $$s of library $$1 failed condition.") + next() + } + + !$${call}($$spfx) { + qtLog("Source $$s of library $$1 produced no result.") + next() + } + + # if the library defines a test, use it to verify the source. + !isEmpty($${lpfx}.test) { + $${lpfx}.literal_args = $$qtConfLibraryArgs($$spfx) + $${lpfx}.host = $$eval($${spfx}.host) + !qtConfTest_compile($$lpfx) { + qtLog("Source $$s of library $$1 failed verification.") + next() + } + } + + $${lpfx}.cache += source + for (v, $$list(libs includes cflags version export)): \ + $${lpfx}.cache += sources.$${s}.$${v} + for (b, $${spfx}.builds._KEYS_): \ + $${lpfx}.cache += sources.$${s}.builds.$${b} + + # immediately output the library as well. + qtConfExportLibrary($${spfx}, $$eval($${lpfx}.export)) + + $${lpfx}.source = $$s + export($${lpfx}.source) + result = true + break() + } + + qtLogTestResult($${lpfx}, $$result) + + $${lpfx}.result = $$result + export($${lpfx}.result) + qtConfSaveResult($${lpfx}, $$1) +} + +# This is a fake test type for the test dependency system. +defineTest(qtConfTest_library) { + error("The test type 'library' may not be instantiated.") +} + +defineTest(qtConfTestPrepare_compile) { + for (u, $$list($$eval($${1}.use))) { + !contains(config.libraries._KEYS_, $$u): \ + error("Test $$1 tries to use undeclared library '$$u'") + qtConfHandleLibrary($$u) + lpfx = config.libraries.$${u} + isEmpty($${lpfx}.source): \ + return(false) + $${1}.literal_args += $$qtConfLibraryArgs($${lpfx}.sources.$$eval($${lpfx}.source)) + } + export($${1}.literal_args) + return(true) +} + +defineTest(qtConfTest_compile) { + test = $$eval($${1}.test) + host = $$eval($${1}.host) + isEmpty(host): host = false + + test_dir = $$QMAKE_CONFIG_TESTS_DIR/$$test + test_out_dir = $$shadowed($$test_dir) + !isEmpty($${1}.pro): \ + test_dir = $$test_dir/$$eval($${1}.pro) + test_cmd_base = "cd $$system_quote($$system_path($$test_out_dir)) &&" + + qmake_args = $$qtConfPkgConfigEnv()$$system_quote($$system_path($$QMAKE_QMAKE)) + !isEmpty(QMAKE_QTCONF): \ + qmake_args += -qtconf $$system_quote($$QMAKE_QTCONF) + + # Disable qmake features which are typically counterproductive for tests + qmake_args += "\"CONFIG -= qt debug_and_release app_bundle lib_bundle\"" + + # allow tests to behave differently depending on the type of library + # being built (shared/static). e.g. see config.tests/unix/icu + shared: \ + qmake_configs = "shared" + else: \ + qmake_configs = "static" + + use_gold_linker: \ + qmake_configs += "use_gold_linker" + + # add console to the CONFIG variable when running the tests, so that they + # can work with a regular main() entry point on Windows. + qmake_configs += "console" + + qmake_args += "\"CONFIG += $$qmake_configs\"" + + !$$host { + # On WinRT we need to change the entry point as we cannot create windows + # applications + winrt: \ + qmake_args += " \"QMAKE_LFLAGS += /ENTRY:main\"" + + # add compiler flags, these are set for the target and should not be applied to host tests + !isEmpty(EXTRA_DEFINES): \ + qmake_args += $$system_quote(DEFINES += $$val_escape(EXTRA_DEFINES)) + !isEmpty(EXTRA_LIBDIR) \ + qmake_args += $$system_quote(QMAKE_LIBDIR += $$val_escape(EXTRA_LIBDIR)) + !isEmpty(EXTRA_FRAMEWORKPATH) \ + qmake_args += $$system_quote(QMAKE_FRAMEWORKPATH += $$val_escape(EXTRA_FRAMEWORKPATH)) + !isEmpty(EXTRA_INCLUDEPATH): \ + qmake_args += $$system_quote(INCLUDEPATH += $$val_escape(EXTRA_INCLUDEPATH)) + qmake_args += $$EXTRA_QMAKE_ARGS + } + + # Clean up after previous run + exists($$test_out_dir/Makefile): \ + QMAKE_MAKE = "$$QMAKE_MAKE clean && $$QMAKE_MAKE" + + mkpath($$test_out_dir)|error() + + # add possible command line args + qmake_args += $$qtConfPrepareArgs($$eval($${1}.args)) $$eval($${1}.literal_args) + + qtRunLoggedCommand("$$test_cmd_base $$qmake_args $$system_quote($$test_dir)") { + qtRunLoggedCommand("$$test_cmd_base $$QMAKE_MAKE"): \ + return(true) + } + + return(false) +} + +defineTest(qtConfTest_verifySpec) { + qtConfTest_compile($$1): return(true) + qtConfAddError("Cannot compile a minimal program. The toolchain or QMakeSpec is broken.", log) + qtConfPrintReport() + error() +} + +defineTest(qtConfTest_files) { + for(i, $${1}.files._KEYS_) { + f = $$eval($${1}.files.$${i}) + qtLog("Searching for file $${f}.") + contains(f, ".*\.h") { + file = $$qtConfFindInPathList($$f, $$EXTRA_INCLUDEPATH $$QMAKE_DEFAULT_INCDIRS) + } else: contains(f, ".*\.(lib|so|a)") { + file = $$qtConfFindInPathList($$f, $$EXTRA_LIBDIR $$QMAKE_DEFAULT_LIBDIRS) + } else { + # assume we're looking for an executable + file = $$qtConfFindInPath($$f, $$EXTRA_PATH) + } + isEmpty(file) { + qtLog(" Not found."); + return(false) + } + qtLog(" Found at $${file}.") + } + return(true) +} + +defineTest(logn) { + log("$${1}$$escape_expand(\\n)") +} + +defineTest(qtLogTestIntro) { + description = $$eval($${1}.description) + isEmpty(description): return() + + msg = "Checking for $${description}... " + log($$msg) + $$QMAKE_CONFIG_VERBOSE: log("$$escape_expand(\\n)") + write_file($$QMAKE_CONFIG_LOG, msg, append) +} + +defineTest(qtLogTestResult) { + isEmpty($${1}.description): return() + + !isEmpty($${1}.log) { + field = $$eval($${1}.log) + log_msg = $$eval($${1}.$$field) + msg = "test $$1 gave result $$log_msg" + } else: $${2} { + log_msg = yes + msg = "test $$1 succeeded" + } else { + log_msg = no + msg = "test $$1 FAILED" + } + $$QMAKE_CONFIG_VERBOSE: log_msg = $$msg + logn("$$log_msg") + write_file($$QMAKE_CONFIG_LOG, msg, append) +} + +defineTest(qtConfSaveResult) { + equals($${1}.cache, -): \ + return() + keys = result $$eval($${1}.cache) + cont = "cache.$${2}._KEYS_ = $$keys" + for (k, keys): \ + cont += "cache.$${2}.$${k} = $$val_escape($${1}.$${k})" + write_file($$QMAKE_CONFIG_CACHE, cont, append)|error() +} + +defineTest(qtConfLoadResult) { + equals(QMAKE_CONFIG_CACHE_USE, none): \ + return(false) + isEmpty(cache.$${2}._KEYS_): \ + return(false) + equals(QMAKE_CONFIG_CACHE_USE, positive):!$$eval(cache.$${2}.result): \ + return(false) + for (k, cache.$${2}._KEYS_) { + $${1}.$${k} = $$eval(cache.$${2}.$${k}) + export($${1}.$${k}) + } + return(true) +} + +defineTest(qtConfIsBoolean) { + equals(1, "true")|equals(1, "false"): \ + return(true) + return(false) +} + +defineTest(qtConfSetupTestTypeDeps) { + for (tt, config.testTypeDependencies._KEYS_) { + !defined(qtConfTest_$${tt}, test): \ + error("Declaring dependency for undefined test type '$$tt'.") + for (f, config.testTypeDependencies.$${tt}._KEYS_) { + feature = $$eval(config.testTypeDependencies.$${tt}.$${f}) + isEmpty(config.features.$${feature}._KEYS_): \ + error("Test type '$$tt' depends on undefined feature '$$feature'.") + } + } + # Test type aliasing means that one test type's callback is called by + # another test type's callback. Put differently, one callback forwards + # the call to another one. The former representation is more natural + # (and concise) to write, while the latter is more efficient to process. + # Hence, this function inverts the mapping. + for (tt, config.testTypeAliases._KEYS_) { + !defined(qtConfTest_$${tt}, test): \ + error("Aliasing undefined test type '$$tt'.") + for (tta, config.testTypeAliases.$${tt}._KEYS_) { + type = $$eval(config.testTypeAliases.$${tt}.$${tta}) + !defined(qtConfTest_$${type}, test): \ + error("Aliasing '$$tt' to undefined test type '$$type'.") + config.testTypeForwards.$${type} += $$tt + export(config.testTypeForwards.$${type}) + } + } +} + +defineTest(qtConfEnsureTestTypeDeps) { + depsn = config.testTypeDependencies.$${1}._KEYS_ + !isEmpty($$depsn) { + for (dep, $$depsn) { + feature = $$eval(config.testTypeDependencies.$${1}.$${dep}) + !qtConfCheckFeature($$feature): \ + error("Test type '$$1' depends on non-emitted feature $${feature}.") + } + $$depsn = + export($$depsn) + } + fwdsn = config.testTypeForwards.$${1} + !isEmpty($$fwdsn) { + for (fwd, $$fwdsn): \ + qtConfEnsureTestTypeDeps($$fwd) + $$fwdsn = + export($$fwdsn) + } +} + +defineTest(qtRunSingleTest) { + tpfx = config.tests.$${1} + defined($${tpfx}.result, var): \ + return() + + type = $$eval($${tpfx}.type) + call = "qtConfTest_$$type" + !defined($$call, test): \ + error("Configure test $${1} refers to nonexistent type $$type") + + qtConfEnsureTestTypeDeps($$type) + + preCall = "qtConfTestPrepare_$$type" + defined($$preCall, test):!$${preCall}($${tpfx}) { + $${tpfx}.result = false + export($${tpfx}.result) + # don't cache the result; the pre-deps have their own caches. + return() + } + + # note: we do this only after resolving the dependencies and the + # preparation (which may resolve libraries), so that caching does + # not alter the execution order (and thus the output). + qtConfLoadResult($${tpfx}, $$1): \ + return() + + qtLogTestIntro($${tpfx}) + msg = "executing config test $${1}" + write_file($$QMAKE_CONFIG_LOG, msg, append) + + result = false + $${call}($${tpfx}): result = true + + qtLogTestResult($${tpfx}, $$result) + + $${tpfx}.result = $$result + export($${tpfx}.result) + qtConfSaveResult($${tpfx}, $$1) +} + +defineReplace(qtConfEvaluate) { + isEmpty(1): return(true) + + 1 ~= s/$$escape_expand(\\t)/ /g + 1 ~= s/$$escape_expand(\\r)//g + 1 ~= s/$$escape_expand(\\n) */ /g + expr = $${1} + expr ~= s/&&/ && /g + expr ~= s/\|\|/ || /g + expr ~= s/!/ ! /g + expr ~= s/\\(/ ( /g + expr ~= s/\\)/ ) /g + expr ~= s/ *== */==/g + expr ~= s/ *! = */!=/g + expr_list = $$eval($$list($$expr)) + return($$qtConfEvaluateSubExpression($${1}, $$expr_list, 0)) +} + +defineReplace(qtConfEvaluateSingleExpression) { + e = $${2} + + equals(e, true) { + result = true + } else: equals(e, false) { + result = false + } else: contains(e, "^[0-9]+$") { + # numbers + result = $$e + } else: contains(e, "^'.*'$") { + # quoted literals + result = $$replace(e, "^'(.*)'$", "\\1") + } else: contains(e, "^tests\..*") { + !qt_conf_tests_allowed: \ + error("Expression '$${1}' refers to a test, which is not allowed at this stage of configuring.") + test = $$section(e, ".", 1, 1) + var = $$section(e, ".", 2, -1) + isEmpty(var): \ + var = result + !contains(config.tests._KEYS_, $$test): \ + error("Unknown test object $${test} in expression '$${1}'.") + qtRunSingleTest($$test) + result = $$eval(config.tests.$${test}.$${var}) + } else: contains(e, "^libs\..*") { + !qt_conf_tests_allowed: \ + error("Expression '$${1}' refers to a library, which is not allowed at this stage of configuring.") + lib = $$section(e, ".", 1, 1) + var = $$section(e, ".", 2, -1) + isEmpty(var): \ + var = result + !contains(config.libraries._KEYS_, $$lib): \ + error("Unknown library object $${lib} in expression '$${1}'.") + qtConfHandleLibrary($$lib) + !defined(config.libraries.$${lib}.$${var}, var): \ + var = sources.$$eval(config.libraries.$${lib}.$${source}).$$var + result = $$eval(config.libraries.$${lib}.$${var}) + } else: contains(e, "^features\..*") { + feature = $$section(e, ".", 1, 1) + var = $$section(e, ".", 2, -1) + isEmpty(var): \ + var = available + !contains(config.features._KEYS_, $$feature): \ + error("Unknown feature object $${feature} in expression '$${1}'.") + !qtConfCheckFeature($$feature): \ + error("Expression '$$1' is accessing non-emitted feature $${feature}.") + result = $$eval(config.features.$${feature}.$${var}) + } else: contains(e, "^config\..*") { + var = $$replace(e, "^config\.", "") + result = false + contains(CONFIG, $$var): result = true + } else: contains(e, "^arch\..*") { + var = $$replace(e, "^arch\.", "") + result = false + isEmpty(QT_ARCH): \ + qtConfCheckFeature(architecture) + contains(QT_ARCH, $$var): result = true + } else: contains(e, "^input\..*") { + result = $$eval(config.$$e) + } else: contains(e, "^var\..*") { + var = $$replace(e, "^var\.", "") + result = $$eval($$var) + } else: contains(e, "^call\..*") { + call = $$replace(e, "^call\.", "qtConfFunc_") + !defined($$call, replace): \ + error("Call $$call referenced in expression '$${1}' does not exist") + eval(result = \$\$"$$call"()) + } else { + error("Unrecognized token $$e in expression '$${1}'") + } + return($$result) +} + +defineReplace(qtConfEvaluateSubExpression) { + expr_list = $${2} + result = true + negate = false + runSubExpression = false + nesting_level = 0 + for (n, $${3}..$$num_add($$size(expr_list), -1)) { + e = $$member(expr_list, $$n) + $$runSubExpression { + runSubExpression = false + result = $$qtConfEvaluateSubExpression($${1}, $$expr_list, $$n) + } else: isEqual(e, "(") { + isEqual(nesting_level, 0): runSubExpression = true + nesting_level = $$num_add($$nesting_level, 1) + next() + } else: isEqual(e, ")") { + nesting_level = $$num_add($$nesting_level, -1) + lessThan(nesting_level, 0): break() + next() + } else: greaterThan(nesting_level, 0) { + next() + } else: isEqual(e, "!") { + negate = true + next() + } else: isEqual(e, "&&") { + !qtConfIsBoolean($$result): \ + error("Left hand side of && is non-boolean value '$$result' in expression '$${1}'") + !$$result: return(false) + } else: isEqual(e, "||") { + !qtConfIsBoolean($$result): \ + error("Left hand side of || is non-boolean value '$$result' in expression '$${1}'") + $$result: return(true) + } else { + contains(e, ".*==.*") { + lhs = $$qtConfEvaluateSingleExpression($${1}, $$replace(e, "==.*", "")) + rhs = $$qtConfEvaluateSingleExpression($${1}, $$replace(e, ".*==", "")) + result = false + equals(lhs, $$rhs): result = true + } else: contains(e, ".*!=.*") { + lhs = $$qtConfEvaluateSingleExpression($${1}, $$replace(e, "!=.*", "")) + rhs = $$qtConfEvaluateSingleExpression($${1}, $$replace(e, ".*!=", "")) + result = false + !equals(lhs, $$rhs): result = true + } else { + result = $$qtConfEvaluateSingleExpression($${1}, $$e) + } + } + $$negate { + !qtConfIsBoolean($$result): \ + error("Attempting to negate a non-boolean value '$$result' in expression '$${1}'") + $$result: \ + result = false + else: \ + result = true + negate = false + } + } + return($$result) +} + +defineReplace(qtIsFeatureEnabled) { + enable = $$eval(config.features.$${1}.enable) + !isEmpty(enable) { + $$qtConfEvaluate($$enable): \ + return(true) + } else { + equals(config.input.$${1}, "yes"): \ + return(true) + } + + return(false) +} + +defineReplace(qtIsFeatureDisabled) { + disable = $$eval(config.features.$${1}.disable) + !isEmpty(disable) { + $$qtConfEvaluate($$disable): \ + return(true) + } else { + equals(config.input.$${1}, "no"): \ + return(true) + } + + return(false) +} + +defineReplace(qtConfCheckSingleCondition) { + result = $$qtConfEvaluate($$2) + + !qtConfIsBoolean($$result): \ + error("Evaluation of condition '$$2' yielded non-boolean value '$$result' in feature '$${1}'.") + + !$$result { + $${3} { + qtConfAddError("Feature '$${1}' was enabled, but the pre-condition '$$2' failed.", log) + $$result = true + } + } + return($$result) +} + +defineTest(qtConfCheckFeature) { + fpfx = config.features.$${1} + + available = $$eval($${fpfx}.available) + !isEmpty(available): return(true) + + # skip features that will not get emitted anyway + emitIf = $$qtConfEvaluate($$eval($${fpfx}.emitIf)) + enabled = $$qtIsFeatureEnabled($$1) + disabled = $$qtIsFeatureDisabled($$1) + + !$$emitIf { + $$enabled|$$disabled: \ + qtConfAddWarning("Feature $${1} is insignificant in this configuration, ignoring related command line option(s).") + return(false) + } + + $$disabled { + result = false + } else: !$$enabled:!$$qtConfEvaluate($$eval($${fpfx}.autoDetect)) { + # feature not auto-detected and not explicitly enabled + result = false + } else { + condition = $$eval($${fpfx}.condition) + !isEmpty(condition) { + result = $$qtConfCheckSingleCondition($$1, $$condition, $$enabled) + } else { + result = true + # check whether we have an array of conditions + for (i, $${fpfx}.condition._KEYS_) { + condition = $$eval($${fpfx}.condition.$$i) + result = $$qtConfCheckSingleCondition($$1, $$condition, $$enabled) + !$$result: break() + } + } + } + $${fpfx}.available = $$result + export($${fpfx}.available) + + for (i, config.features.$${feature}.output._KEYS_): \ + qtConfProcessOneOutput($$feature, $$i) + + return(true) +} + + +defineTest(qtConfProcessFeatures) { + for (feature, config.features._KEYS_): \ + qtConfCheckFeature($$feature) +} + +# +# reporting +# + +QT_CONF_REPORT_PADDING = "........................................" + +defineTest(qtConfReportPadded) { + pad = $$num_add($$str_size($$QT_CONF_REPORT_PADDING), -$$str_size($${1})) + lessThan(pad, 0): pad = 0 + str = "$$1 $$str_member($$QT_CONF_REPORT_PADDING, 0, $$pad)" + + qtConfAddReport("$$str $${2}") +} + +defineReplace(qtConfCollectFeatures) { + l = + for (feature, $$list($${1})) { + $$eval(config.features.$${feature}.available): \ + l += $$eval(config.features.$${feature}.description) + } + + isEmpty(l): return("<none>") + return($$join(l, ' ')) +} + +defineTest(qtConfReport_featureList) { + qtConfReportPadded($${1}, $$qtConfCollectFeatures($${2})) +} + +defineReplace(qtConfFindFirstAvailableFeature) { + for (feature, $$list($${1})) { + isEmpty(config.features.$${feature}._KEYS_): \ + error("Asking for a report on undefined feature $${2}.") + $$eval(config.features.$${feature}.available): \ + return($$eval(config.features.$${feature}.description)) + } + + return("<none>") +} + +defineTest(qtConfReport_firstAvailableFeature) { + qtConfReportPadded($${1}, $$qtConfFindFirstAvailableFeature($${2})) +} + +defineTest(qtConfReport_feature) { + !contains(config.features._KEYS_, $$2): \ + error("Asking for a report on undefined feature $${2}.") + + # hide report for not emitted features + isEmpty(config.features.$${2}.available): \ + return() + + $$eval(config.features.$${2}.available) { + result = "yes" + !isEmpty(3): result = "$${3}" + } else { + result = "no" + !isEmpty(4): result = "$${4}" + } + + text = $$eval(config.features.$${2}.description) + + qtConfReportPadded($${1}$$text, $$result) +} + +defineTest(qtConfReport_note) { + qtConfAddNote($${1}) +} + +defineTest(qtConfReport_warning) { + qtConfAddWarning($${1}) +} + +defineTest(qtConfReport_error) { + qtConfAddError($${1}, log) +} + +defineTest(qtConfCreateReportRecurse) { + equals(2, false) { + indent = "" + recurse = false + } else { + indent = $${2} + recurse = true + } + + keys = $$eval($${1}._KEYS_) + for (n, keys) { + entry = $${1}.$$n + subKeys = $$eval($${entry}._KEYS_) + contains(subKeys, condition) { + condition = $$eval($${entry}.condition) + r = $$qtConfEvaluate($$condition) + !qtConfIsBoolean($$r): \ + error("Evaluation of condition '$$condition' in report entry $${entry} yielded non-boolean value '$$r'.") + !$$r: next() + } + contains(subKeys, "section") { + !$$recurse: \ + error("Report type 'section' is not allowed in '$$1'.") + section = $$eval($${entry}.section) + qtConfAddReport("$$indent$$section:") + qtConfCreateReportRecurse("$${entry}.entries", "$$indent ") + } else: !isEmpty($${entry}) { + feature = $$eval($${entry}) + qtConfReport_feature($$indent, $$feature) + } else { + text = $$eval($${entry}.message) + isEmpty($${entry}.type): \ + error("Report entry $${entry} doesn't define a type.") + r = "qtConfReport_$$eval($${entry}.type)" + !defined($$r, test): \ + error("Undefined report type $$eval($${entry}.type) used in report entry $${entry}.") + args = $$eval($${entry}.args) + $${r}($$indent$${text}, $$args) + } + } +} + +defineTest(qtConfProcessEarlyChecks) { + qtConfCreateReportRecurse(config.earlyReport, false) + qtConfCheckErrors() +} + + +defineTest(qtConfCreateReport) { + qtConfCreateReportRecurse(config.report, false) +} + +defineTest(qtConfCreateSummary) { + qtConfCreateReportRecurse(config.summary, "") +} + +defineTest(qtConfPrintReport) { + for (n, QT_CONFIGURE_REPORT): \ + logn($$n) + logn() + + for (n, QT_CONFIGURE_NOTES) { + logn($$n) + logn() + } + + for (w, QT_CONFIGURE_WARNINGS) { + logn($$w) + logn() + } + + !isEmpty(QT_CONFIGURE_ERRORS) { + for (e, QT_CONFIGURE_ERRORS) { + logn($$e) + logn() + } + mention_config_log:!$$QMAKE_CONFIG_VERBOSE { + logn("Check config.log for details.") + logn() + } + + !equals(config.input.continue, yes): \ + error() + } +} + +defineTest(qtConfCheckErrors) { + !isEmpty(QT_CONFIGURE_ERRORS):!equals(config.input.continue, yes): \ + qtConfPrintReport() +} + +# +# output generation +# + +defineReplace(qtConfOutputSelectProFile) { + !isEmpty($${1}.public) { + $$eval($${1}.public): \ + return(publicPro) + } + return(privatePro) +} + + +# qtConfOutputVar(modifier, output, name, value) +defineTest(qtConfOutputVar) { + modifier = $$1 + output = $$2 + name = $$3 + value = $$val_escape(4) + + !isEmpty(config.output.$${output}.assign.$${name}): \ + error("Trying to overwrite assigned variable '$$name' in '$$output' using modifier '$$modifier'.") + + equals(modifier, assign) { + !isEmpty(config.output.$${output}.append.$${name})|!isEmpty(config.output.$${output}.remove.$${name}): \ + error("Trying to assign variable '$$name' in '$$output', which has already appended or removed parts.") + config.output.$${output}.assign.$${name} = $$value + } else: equals(modifier, append) { + contains(config.output.$${output}.remove.$${name}, $$value): \ + error("Trying to append removed '$$value' to variable '$$name' in '$$output'.") + config.output.$${output}.append.$${name} += $$value + } else: equals(modifier, remove) { + contains(config.output.$${output}.append.$${name}, $$value): \ + error("Trying to remove appended '$$value' to variable '$$name' in '$$output'.") + config.output.$${output}.remove.$${name} += $$value + } else { + error("Invalid modifier '$$modifier' passed to qtConfOutputVar.") + } + config.output.$${output}.$${modifier}._KEYS_ *= $${name} + export(config.output.$${output}.$${modifier}.$${name}) + export(config.output.$${output}.$${modifier}._KEYS_) +} + +defineTest(qtConfOutputVarHelper) { + negative = $$eval($${2}.negative) + isEmpty(negative): negative = false + equals(3, $$negative): return() + + output = $$qtConfOutputSelectProFile($${2}) + name = $$eval($${2}.name) + isEmpty(name): \ + error("Output type 'var$$title($$1)' used in feature '$$eval($${2}.feature)' without a 'name' entry.") + + value = $$qtConfEvaluate($$eval($${2}.value)) + !isEmpty($${2}.eval):$$qtConfEvaluate($$eval($${2}.eval)): \ + eval(value = $$value) + qtConfOutputVar($$1, $$output, $$name, $$value) +} + +defineTest(qtConfOutput_varAssign) { + qtConfOutputVarHelper(assign, $$1, $$2) +} + +defineTest(qtConfOutput_varAppend) { + qtConfOutputVarHelper(append, $$1, $$2) +} + +defineTest(qtConfOutput_varRemove) { + qtConfOutputVarHelper(remove, $$1, $$2) +} + +defineTest(qtConfOutputConfigVar) { + pro = $$3 + var = $$4 + negative = $$eval($${1}.negative) + isEmpty(negative): negative = false + equals(2, $$negative): return() + + val = $$eval($${1}.name) + isEmpty(val) { + val = $$eval($${1}.feature) + $$negative: val = no-$$val + } + + qtConfOutputVar(append, $$pro, $$var, $$val) +} + +defineTest(qtConfOutput_publicQtConfig) { + qtConfOutputConfigVar($$1, $$2, "publicPro", "QT_CONFIG") +} + +defineTest(qtConfOutput_publicConfig) { + qtConfOutputConfigVar($$1, $$2, "publicPro", "CONFIG") +} + +defineTest(qtConfOutput_privateConfig) { + qtConfOutputConfigVar($$1, $$2, "privatePro", "CONFIG") +} + +defineTest(qtConfOutputSetDefine) { + config.output.$${1}.$${2} = $${3} + config.output.$${1}._KEYS_ *= $${2} + export(config.output.$${1}.$${2}) + export(config.output.$${1}._KEYS_) +} + +defineTest(qtConfOutput_define) { + output = publicHeader + define = $$eval($${1}.name) + value = $$qtConfEvaluate($$eval($${1}.value)) + isEmpty(define): \ + error("Output type 'define' used in feature '$$eval($${1}.feature)' without a 'name' entry.") + + negative = $$eval($${1}.negative) + isEmpty(negative): negative = false + equals(2, $$negative): return() + + qtConfOutputSetDefine($$output, $$define, $$value) +} + +defineTest(qtConfOutput_feature) { + name = "$$eval($${1}.name)" + isEmpty(name): \ + name = $$eval($${1}.feature) + + $${2} { + qtConfOutputVar(append, "publicPro", "QT_CONFIG", $$name) + } else { + f = $$upper($$replace(name, -, _)) + qtConfOutputSetDefine("publicHeader", "QT_NO_$$f") + } +} + +defineTest(qtConfOutput_publicFeature) { + name = "$$eval($${1}.name)" + isEmpty(name): \ + name = $$eval($${1}.feature) + feature = $$replace(name, [-+.], _) + + $${2} { + qtConfOutputVar(append, "publicPro", "QT.global.enabled_features", $$name) + qtConfOutputSetDefine("publicHeader", "QT_FEATURE_$$feature", 1) + } else { + qtConfOutputVar(append, "publicPro", "QT.global.disabled_features", $$name) + qtConfOutputSetDefine("publicHeader", "QT_FEATURE_$$feature", -1) + } +} + +# currently this is somewhat inconsistent, as the feature is output to the public pro file, +# whereas the define is being added to the private pro file. +# This should get cleaned up to add to the private pro and header instead. +defineTest(qtConfOutput_privateFeature) { + name = "$$eval($${1}.name)" + isEmpty(name): \ + name = $$eval($${1}.feature) + feature = $$replace(name, [-+.], _) + + $${2} { + qtConfOutputVar(append, "privatePro", "QT.global.enabled_features", $$name) + qtConfOutputSetDefine("privateHeader", "QT_FEATURE_$$feature", 1) + } else { + qtConfOutputVar(append, "privatePro", "QT.global.disabled_features", $$name) + qtConfOutputSetDefine("privateHeader", "QT_FEATURE_$$feature", -1) + } +} + +defineTest(qtConfProcessOneOutput) { + feature = $${1} + fpfx = config.features.$${feature} + opfx = $${fpfx}.output.$${2} + + condition = $$eval($${opfx}.condition) + !isEmpty(condition):!$$qtConfEvaluate($$condition): \ + return() + + call = $$eval($${opfx}.type) + isEmpty(call) { + # output is just a string, not an object + call = $$eval($$opfx) + } + !defined("qtConfOutput_$$call", test): \ + error("Undefined type '$$call' in output '$$2' of feature '$$feature'.") + + condition = $$eval($${opfx}.condition) + !isEmpty(condition) { + !$$qtConfEvaluate($$condition): \ + return(false) + } + + $${opfx}.feature = $$feature + qtConfOutput_$${call}($$opfx, $$eval($${fpfx}.available)) +} + +defineTest(qtConfProcessOutput) { + # write it to the output files + for (type, config.files._KEYS_) { + file = $$OUT_PWD/$$eval(config.files.$${type}) + contains(type, ".*Pro") { + for (k, config.output.$${type}.assign._KEYS_): \ + config.output.$$type += "$$k = $$eval(config.output.$${type}.assign.$$k)" + for (k, config.output.$${type}.remove._KEYS_): \ + config.output.$$type += "$$k -= $$eval(config.output.$${type}.remove.$$k)" + for (k, config.output.$${type}.append._KEYS_): \ + config.output.$$type += "$$k += $$eval(config.output.$${type}.append.$$k)" + } else { + for (define, config.output.$${type}._KEYS_) { + value = $$eval(config.output.$${type}.$${define}) + config.output.$$type += "$${LITERAL_HASH}define $$define $$value" + } + } + defined(qtConfOutputPostProcess_$${type}, test): \ + qtConfOutputPostProcess_$${type}() + + write_file($$file, config.output.$${type})|error() + } +} + +# +# tie it all together +# + +defineTest(qtConfigure) { + # load configuration data + configure_data = $$cat($${1}, blob) + !parseJson(configure_data, config): \ + error("Invalid or non-existent file $${1}.") + + qtConfSetupLibraries() + qtConfSetupTestTypeDeps() + + qtConfParseCommandLine() + + !equals(QMAKE_CONFIG_CACHE_USE, none) { + include($$QMAKE_CONFIG_CACHE, , true) + # this crudely determines when to discard the cache. this also catches the case + # of no cache being there in the first place. + !equals(cache.platform, $$[QMAKE_SPEC])|!equals(cache.xplatform, $$[QMAKE_XSPEC]): \ + QMAKE_CONFIG_CACHE_USE = none + } + equals(QMAKE_CONFIG_CACHE_USE, none) { + cont = \ + "cache.platform = $$[QMAKE_SPEC]" \ + "cache.xplatform = $$[QMAKE_XSPEC]" + write_file($$QMAKE_CONFIG_CACHE, cont) + } + + # do early checks, mainly to validate the command line + qtConfProcessEarlyChecks() + + CONFIG += qt_conf_tests_allowed + logn() + logn("Running configuration tests...") + + # process all features + qtConfProcessFeatures() + + # generate files and reports + qtConfProcessOutput() + qtConfCreateReport() + qtConfCreateSummary() + + logn("Done running configuration tests.") + logn() +} + +qtConfigure($$_PRO_FILE_PWD_/configure.json) + +logn("Configure summary:") +logn() + +qtConfPrintReport() diff --git a/mkspecs/features/qt_docs.prf b/mkspecs/features/qt_docs.prf index 183d0c9502..72dde61a40 100644 --- a/mkspecs/features/qt_docs.prf +++ b/mkspecs/features/qt_docs.prf @@ -21,12 +21,16 @@ qtvertag.name = QT_VERSION_TAG qtvertag.value = $$replace(qtver.value, \.,) qtdocs.name = QT_INSTALL_DOCS qtdocs.value = $$[QT_INSTALL_DOCS/src] -QT_TOOL_ENV = qtver qtmver qtvertag qtdocs +builddir.name = BUILDDIR +builddir.value = $$OUT_PWD +QT_TOOL_ENV = qtver qtmver qtvertag qtdocs builddir qtPrepareTool(QDOC, qdoc) QT_TOOL_ENV = !build_online_docs: qtPrepareTool(QHELPGENERATOR, qhelpgenerator) +qtPrepareTool(QTATTRIBUTIONSSCANNER, qtattributionsscanner) + # qtPrepareTool() must be called outside a build pass, as it protects # against concurrent wrapper creation by omitting it during build passes. # However, creating the actual targets is reserved to the build passes. @@ -63,12 +67,21 @@ DOC_INDEXES = PREP_DOC_INDEXES += -indexdir $$shell_quote($$[QT_INSTALL_DOCS/get]) DOC_INDEXES += -indexdir $$shell_quote($$[QT_INSTALL_DOCS/get]) } + +qtattributionsscanner.target = qtattributionsscanner +qtattributionsscanner.commands = $$QTATTRIBUTIONSSCANNER $$shell_quote($$MODULE_BASE_INDIR) \ + --filter "QDocModule=$$QMAKE_DOCS_TARGET" -o $$shell_quote($$OUT_PWD/codeattributions.qdoc) +qtattributionsscanner.CONFIG += phony +QMAKE_EXTRA_TARGETS += qtattributionsscanner + doc_command = $$QDOC $$QMAKE_DOCS prepare_docs { prepare_docs.commands += $$doc_command -prepare $$PREP_DOC_INDEXES -no-link-errors generate_docs.commands += $$doc_command -generate $$DOC_INDEXES + prepare_docs.depends += qtattributionsscanner } else { html_docs.commands += $$doc_command $$DOC_INDEXES + html_docs.depends += qtattributionsscanner } !build_online_docs { diff --git a/mkspecs/features/qt_functions.prf b/mkspecs/features/qt_functions.prf index 54641ce5a1..735ece45f2 100644 --- a/mkspecs/features/qt_functions.prf +++ b/mkspecs/features/qt_functions.prf @@ -1,10 +1,6 @@ defineReplace(qtPlatformTargetSuffix) { - ios:CONFIG(simulator, simulator|device): \ - suffix = _$${simulator.sdk} - else: \ - suffix = - + suffix = CONFIG(debug, debug|release) { !debug_and_release|build_pass { mac: return($${suffix}_debug) @@ -16,7 +12,7 @@ defineReplace(qtPlatformTargetSuffix) { defineReplace(qtLibraryTarget) { LIBRARY_NAME = $$1 - CONFIG(shared, static|shared):contains(QT_CONFIG, qt_framework) { + CONFIG(shared, static|shared):qtConfig(framework) { QMAKE_FRAMEWORK_BUNDLE_NAME = $$LIBRARY_NAME export(QMAKE_FRAMEWORK_BUNDLE_NAME) } @@ -171,7 +167,7 @@ defineTest(qtAddToolEnv) { "EndLocal" $$1 = call } - !build_pass:!write_file($$OUT_PWD/$$batch_name, batch_cont, exe): error("Aborting.") + !build_pass:!write_file($$OUT_PWD/$$batch_name, batch_cont, exe): error() isEmpty(3): \ $$1 += $$shell_quote($$shell_path($$OUT_PWD/$$batch_name)) else: \ @@ -196,7 +192,7 @@ defineTest(qtAddTargetEnv) { } else:contains(QMAKE_HOST.os, Haiku) { deppath.name = LIBRARY_PATH } else:equals(QMAKE_HOST.os, Darwin) { - contains(QT_CONFIG, qt_framework): \ + qtConfig(framework): \ deppath.name = DYLD_FRAMEWORK_PATH else: \ deppath.name = DYLD_LIBRARY_PATH @@ -259,7 +255,7 @@ defineReplace(pkgConfigExecutable) { } defineTest(packagesExist) { - contains(QT_CONFIG, no-pkg-config) { + !qtConfig(pkg-config) { warning("pkg-config disabled, can't check package existence") return(false) } @@ -302,3 +298,12 @@ defineTest(prepareRecursiveTarget) { export($${target}.CONFIG) export($${target}.recurse_target) } + +defineTest(qtConfig) { + contains(QT.global.enabled_features, $$1): \ + return(true) + contains(QT.global.disabled_features, $$1): \ + return(false) + + error("Could not find feature $${1}.") +} diff --git a/mkspecs/features/qt_helper_lib.prf b/mkspecs/features/qt_helper_lib.prf index dc7a5da30e..8890866c64 100644 --- a/mkspecs/features/qt_helper_lib.prf +++ b/mkspecs/features/qt_helper_lib.prf @@ -18,15 +18,15 @@ QT = # In case qt is re-added. CONFIG -= warning_clean # Don't presume 3rd party code to be clean load(qt_common) -contains(QT_CONFIG, simulator_and_device): CONFIG += simulator_and_device -contains(QT_CONFIG, debug_and_release): CONFIG += debug_and_release -contains(QT_CONFIG, build_all): CONFIG += build_all +qtConfig(simulator_and_device): CONFIG += simulator_and_device +qtConfig(debug_and_release): CONFIG += debug_and_release +qtConfig(build_all): CONFIG += build_all DESTDIR = $$MODULE_BASE_OUTDIR/lib DLLDESTDIR = $$MODULE_BASE_OUTDIR/bin # In static builds of Qt, convenience libraries must be installed, # as in this case they are not linked to the final library/plugin. -installed|if(!not_installed:contains(QT_CONFIG, static)): load(qt_installs) +installed|if(!not_installed:qtConfig(static)): load(qt_installs) TARGET = $$qt5LibraryTarget($$TARGET) diff --git a/mkspecs/features/qt_module.prf b/mkspecs/features/qt_module.prf index d9011f5482..ed02c597da 100644 --- a/mkspecs/features/qt_module.prf +++ b/mkspecs/features/qt_module.prf @@ -32,17 +32,17 @@ host_build { QMAKE_CFLAGS += $$QMAKE_CFLAGS_SPLIT_SECTIONS QMAKE_CXXFLAGS += $$QMAKE_CXXFLAGS_SPLIT_SECTIONS force_bootstrap { - !build_pass:contains(QT_CONFIG, release_tools): CONFIG += release + !build_pass:qtConfig(release_tools): CONFIG += release contains(QT, core(-private)?|xml) { QT -= core core-private xml QT += bootstrap-private } } else { - !build_pass:contains(QT_CONFIG, debug_and_release): CONFIG += release + !build_pass:qtConfig(debug_and_release): CONFIG += release } } -CONFIG(shared, static|shared):contains(QT_CONFIG, qt_framework): \ +CONFIG(shared, static|shared):qtConfig(framework): \ CONFIG += lib_bundle CONFIG += relative_qt_rpath # Qt libraries should be relocatable @@ -74,7 +74,7 @@ header_module { TEMPLATE = lib } DESTDIR = $$MODULE_BASE_OUTDIR/lib -win32:!wince:!prefix_build: DLLDESTDIR = $$MODULE_BASE_OUTDIR/bin +win32:!prefix_build: DLLDESTDIR = $$MODULE_BASE_OUTDIR/bin CONFIG += qmake_cache target_qt @@ -89,9 +89,9 @@ INCLUDEPATH *= $$eval(QT.$${MODULE}.includes) $$eval(QT.$${MODULE}_private.inclu # If Qt was configured with -debug-and-release then build the module the same way # - unless this is a host library !host_build:if(win32|mac):!macx-xcode { - contains(QT_CONFIG, simulator_and_device): CONFIG += simulator_and_device - contains(QT_CONFIG, debug_and_release): CONFIG += debug_and_release - contains(QT_CONFIG, build_all): CONFIG += build_all + qtConfig(simulator_and_device): CONFIG += simulator_and_device + qtConfig(debug_and_release): CONFIG += debug_and_release + qtConfig(build_all): CONFIG += build_all } QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF @@ -114,10 +114,12 @@ lib_bundle { CONFIG += sliced_bundle header_module { CONFIG += bundle + QMAKE_BUNDLE_NAME = $$TARGET QMAKE_BUNDLE_EXTENSION = .framework QMAKE_INFO_PLIST = $$QMAKESPEC/Info.plist.lib } - !debug_and_release|!build_all|CONFIG(release, debug|release) { + !build_all| \ + if(if(!debug_and_release|CONFIG(release, debug|release))) { FRAMEWORK_HEADERS.version = Versions FRAMEWORK_HEADERS.files = $$SYNCQT.HEADER_FILES $$SYNCQT.HEADER_CLASSES FRAMEWORK_HEADERS.path = Headers @@ -137,7 +139,7 @@ mac { QMAKE_CXXFLAGS += -fconstant-cfstrings } - contains(QT_CONFIG, rpath): \ + qtConfig(rpath): \ QMAKE_SONAME_PREFIX = @rpath else: \ CONFIG += absolute_library_soname @@ -164,7 +166,7 @@ aix-g++* { QMAKE_CXXFLAGS += -mminimal-toc } -sse2:!contains(QT_CPU_FEATURES.$$QT_ARCH, sse2):!host_build:!if(static:contains(QT_CONFIG, shared)) { +sse2:!contains(QT_CPU_FEATURES.$$QT_ARCH, sse2):!host_build:!if(static:qtConfig(shared)) { # If the compiler supports SSE2, enable it unconditionally in all of Qt shared libraries # (and only the libraries). This is not expected to be a problem because: # - on Windows, sharing of libraries is uncommon @@ -232,7 +234,7 @@ android: CONFIG += qt_android_deps no_linker_version_script verscript = $$verscript_in } - write_file($$OUT_PWD/$$verscript, verscript_content)|error("Aborting.") + write_file($$OUT_PWD/$$verscript, verscript_content)|error() unset(current) unset(previous) unset(verscript) diff --git a/mkspecs/features/qt_module_headers.prf b/mkspecs/features/qt_module_headers.prf index 05d0706a33..9e0b21903f 100644 --- a/mkspecs/features/qt_module_headers.prf +++ b/mkspecs/features/qt_module_headers.prf @@ -16,7 +16,7 @@ load(qt_build_paths) minimal_syncqt { QMAKE_SYNCQT += -minimal $$QMAKE_SYNCQT_OPTIONS } else { - contains(QT_CONFIG, private_tests): \ # -developer-build + qtConfig(private_tests): \ # -developer-build QMAKE_SYNCQT += -check-includes } for(mod, MODULE_INCNAME): \ @@ -104,18 +104,23 @@ include($$INC_PATH/include/$$MODULE_INCNAME/headers.pri, "", true) alien_syncqt: return() +MODULE_INC_OUTDIR = $$MODULE_BASE_OUTDIR/include/$$MODULE_INCNAME + for (injection, SYNCQT.INJECTIONS) { injects = $$split(injection, :) + dst_hdr = $$absolute_path($$member(injects, 0), $$MODULE_INC_OUTDIR) fwd_hdr = $$member(injects, 1) - MAIN_FWD = $$INC_PATH/include/$$MODULE_INCNAME/$$fwd_hdr - MAIN_FWD_CONT = '$${LITERAL_HASH}include "$$member(injects, 0)"' - write_file($$MAIN_FWD, MAIN_FWD_CONT)|error("Aborting.") + MAIN_FWD = $$MODULE_INC_OUTDIR/$$fwd_hdr + MAIN_FWD_CONT = '$${LITERAL_HASH}include "$$relative_path($$dst_hdr, $$dirname(MAIN_FWD))"' + write_file($$MAIN_FWD, MAIN_FWD_CONT)|error() + touch($$MAIN_FWD, $$dst_hdr) !git_build: QMAKE_DISTCLEAN += $$MAIN_FWD injects = $$member(injects, 2, -1) for (inject, injects) { - CLASS_FWD = $$INC_PATH/include/$$MODULE_INCNAME/$$inject + CLASS_FWD = $$MODULE_INC_OUTDIR/$$inject CLASS_FWD_CONT = '$${LITERAL_HASH}include "$$fwd_hdr"' - write_file($$CLASS_FWD, CLASS_FWD_CONT)|error("Aborting.") + write_file($$CLASS_FWD, CLASS_FWD_CONT)|error() + touch($$CLASS_FWD, $$dst_hdr) !git_build: QMAKE_DISTCLEAN += $$CLASS_FWD } } @@ -135,7 +140,7 @@ MODULE_MASTER_DEPS_HEADER = $$MODULE_BASE_OUTDIR/include/$$MODULE_INCNAME/$${MOD MODULE_MASTER_DEPS_HEADER_CONT += "$${LITERAL_HASH}include <$$depname/$$depname>" } MODULE_MASTER_DEPS_HEADER_CONT += "$${LITERAL_HASH}endif" - write_file($$MODULE_MASTER_DEPS_HEADER, MODULE_MASTER_DEPS_HEADER_CONT)|error("Aborting.") + write_file($$MODULE_MASTER_DEPS_HEADER, MODULE_MASTER_DEPS_HEADER_CONT)|error() !git_build: QMAKE_DISTCLEAN += $$MODULE_MASTER_DEPS_HEADER } SYNCQT.HEADER_FILES += $$MODULE_MASTER_DEPS_HEADER @@ -227,7 +232,7 @@ headersclean:!internal_module { $$[QT_INSTALL_DATA/src]/mkspecs/features/data/dummy.cpp } - !isEmpty(hcleanCOMMAND):if(!contains(QT_CONFIG, debug_and_release)|CONFIG(release, debug|release)) { + !isEmpty(hcleanCOMMAND):if(!qtConfig(debug_and_release)|CONFIG(release, debug|release)) { header_check.dependency_type = TYPE_C header_check.CONFIG += no_link header_check.output = ${QMAKE_VAR_OBJECTS_DIR}header_${QMAKE_FILE_BASE}$${first(QMAKE_EXT_OBJ)} diff --git a/mkspecs/features/qt_module_pris.prf b/mkspecs/features/qt_module_pris.prf index 4dd9e25f9f..b67a56c7d5 100644 --- a/mkspecs/features/qt_module_pris.prf +++ b/mkspecs/features/qt_module_pris.prf @@ -107,7 +107,7 @@ MODULE_FWD_PRI = $$mod_work_pfx/qt_lib_$${MODULE_ID}.pri winrt: MODULE_PRI_CONT += \ "QT.$${MODULE_ID}.winrt_capabilities =$$join(MODULE_WINRT_CAPABILITIES, " ", " ")" \ "QT.$${MODULE_ID}.winrt_capabilities_device =$$join(MODULE_WINRT_CAPABILITIES_DEVICE, " ", " ")" - write_file($$MODULE_PRI, MODULE_PRI_CONT)|error("Aborting.") + write_file($$MODULE_PRI, MODULE_PRI_CONT)|error() !internal_module:!no_private_module { module_build_type += internal_module private_deps = $$QT @@ -127,7 +127,7 @@ MODULE_FWD_PRI = $$mod_work_pfx/qt_lib_$${MODULE_ID}.pri "QT.$${MODULE}_private.frameworks =" \ "QT.$${MODULE}_private.depends = $$private_deps" \ "QT.$${MODULE}_private.module_config =$$join(module_build_type, " ", " ")" - write_file($$MODULE_PRIVATE_PRI, MODULE_PRIVATE_PRI_CONT)|error("Aborting.") + write_file($$MODULE_PRIVATE_PRI, MODULE_PRIVATE_PRI_CONT)|error() } MODULE_PRI_FILES = $$MODULE_PRI $$MODULE_PRIVATE_PRI @@ -162,7 +162,7 @@ MODULE_FWD_PRI = $$mod_work_pfx/qt_lib_$${MODULE_ID}.pri MODULE_FWD_PRI_CONT += \ "QT.$${MODULE}_private.includes $$pls= $$MODULE_FWD_PRIVATE_INCLUDES" } - write_file($$MODULE_FWD_PRI, MODULE_FWD_PRI_CONT)|error("Aborting.") + write_file($$MODULE_FWD_PRI, MODULE_FWD_PRI_CONT)|error() touch($$MODULE_FWD_PRI, $$MODULE_PRI) MODULE_PRI_FILES += $$MODULE_FWD_PRI diff --git a/mkspecs/features/qt_parts.prf b/mkspecs/features/qt_parts.prf index 877bd60321..fa62f40e35 100644 --- a/mkspecs/features/qt_parts.prf +++ b/mkspecs/features/qt_parts.prf @@ -60,7 +60,7 @@ exists($$_PRO_FILE_PWD_/tests/tests.pro) { sub_tests.CONFIG = no_default_install !contains(QT_BUILD_PARTS, tests) { sub_tests.CONFIG += no_default_target - } else: !ios { + } else: !uikit { # Make sure these are there in case we need them sub_tools.CONFIG -= no_default_target sub_examples.CONFIG -= no_default_target diff --git a/mkspecs/features/qt_plugin.prf b/mkspecs/features/qt_plugin.prf index 8e47d91133..2179c7ca22 100644 --- a/mkspecs/features/qt_plugin.prf +++ b/mkspecs/features/qt_plugin.prf @@ -24,11 +24,11 @@ win32:CONFIG(shared, static|shared) { } tool_plugin { - !build_pass:contains(QT_CONFIG, debug_and_release): CONFIG += release + !build_pass:qtConfig(debug_and_release): CONFIG += release } else:if(win32|mac):!macx-xcode { - contains(QT_CONFIG, simulator_and_device): CONFIG += simulator_and_device - contains(QT_CONFIG, debug_and_release): CONFIG += debug_and_release - contains(QT_CONFIG, build_all): CONFIG += build_all + qtConfig(simulator_and_device): CONFIG += simulator_and_device + qtConfig(debug_and_release): CONFIG += debug_and_release + qtConfig(build_all): CONFIG += build_all } CONFIG += relative_qt_rpath # Qt's plugins should be relocatable @@ -50,7 +50,7 @@ CONFIG(static, static|shared)|prefix_build { "QT_PLUGIN.$${MODULE}.EXTENDS =$$join(PLUGIN_EXTENDS, " ", " ")" \ "QT_PLUGIN.$${MODULE}.CLASS_NAME = $$PLUGIN_CLASS_NAME" \ "QT_PLUGINS += $$MODULE" - write_file($$MODULE_PRI, MODULE_PRI_CONT)|error("Aborting.") + write_file($$MODULE_PRI, MODULE_PRI_CONT)|error() MODULE_PRI_FILES = $$MODULE_PRI force_independent { @@ -59,7 +59,7 @@ CONFIG(static, static|shared)|prefix_build { MODULE_FWD_PRI_CONT = \ "QT_PLUGIN.$${MODULE}.PATH = $$MODULE_BASE_OUTDIR/plugins" \ "include($$MODULE_PRI)" - write_file($$MODULE_FWD_PRI, MODULE_FWD_PRI_CONT)|error("Aborting.") + write_file($$MODULE_FWD_PRI, MODULE_FWD_PRI_CONT)|error() touch($$MODULE_FWD_PRI, $$MODULE_PRI) MODULE_PRI_FILES += $$MODULE_FWD_PRI @@ -92,5 +92,4 @@ CONFIG += create_cmake load(qt_targets) load(qt_common) -wince: LIBS += $$QMAKE_LIBS_GUI QMAKE_LFLAGS += $$QMAKE_LFLAGS_NOUNDEF diff --git a/mkspecs/features/qt_tool.prf b/mkspecs/features/qt_tool.prf index bdeb59c83d..93c952617d 100644 --- a/mkspecs/features/qt_tool.prf +++ b/mkspecs/features/qt_tool.prf @@ -60,7 +60,7 @@ DEFINES *= QT_USE_QSTRINGBUILDER "QT_TOOL.$${MODULE}.binary = $$val_escape(bin)" \ "QT_TOOL.$${MODULE}.depends =$$join(MODULE_DEPENDS, " ", " ")" \ $$module_envvars - write_file($$TOOL_PRI, TOOL_PRI_CONT)|error("Aborting.") + write_file($$TOOL_PRI, TOOL_PRI_CONT)|error() # Then, inject the new tool into the current cache state !contains(QMAKE_INTERNAL_INCLUDED_FILES, $$TOOL_PRI) { # before the actual include()! diff --git a/mkspecs/features/resolve_config.prf b/mkspecs/features/resolve_config.prf index b712a2b58e..816c3b680d 100644 --- a/mkspecs/features/resolve_config.prf +++ b/mkspecs/features/resolve_config.prf @@ -41,7 +41,7 @@ CONFIG(debug, debug|release) { # or not debug_and_release is active. for(build, $$list(debug release)) { suffix = - contains(QT_CONFIG, debug_and_release) { + qtConfig(debug_and_release) { equals(build, debug): \ suffix = _debug } else { diff --git a/mkspecs/features/resources.prf b/mkspecs/features/resources.prf index 21147e4a42..adc8e9a8ac 100644 --- a/mkspecs/features/resources.prf +++ b/mkspecs/features/resources.prf @@ -65,7 +65,7 @@ for(resource, RESOURCES) { "</RCC>" !write_file($$OUT_PWD/$$resource_file, resource_file_content): \ - error("Aborting.") + error() } RESOURCES -= $$resource diff --git a/mkspecs/features/uikit/bitcode.prf b/mkspecs/features/uikit/bitcode.prf new file mode 100644 index 0000000000..a1dff19eb3 --- /dev/null +++ b/mkspecs/features/uikit/bitcode.prf @@ -0,0 +1,15 @@ +lessThan(QMAKE_XCODE_VERSION, "7.0") { + warning("You need to update Xcode to version 7 or newer to support bitcode") +} else { + release:device { + QMAKE_CFLAGS += -fembed-bitcode + QMAKE_CXXFLAGS += -fembed-bitcode + QMAKE_OBJECTIVE_CFLAGS += -fembed-bitcode + QMAKE_LFLAGS += -fembed-bitcode + } else { + QMAKE_CFLAGS += -fembed-bitcode-marker + QMAKE_CXXFLAGS += -fembed-bitcode-marker + QMAKE_OBJECTIVE_CFLAGS += -fembed-bitcode-marker + QMAKE_LFLAGS += -fembed-bitcode-marker + } +} diff --git a/mkspecs/features/uikit/default_post.prf b/mkspecs/features/uikit/default_post.prf new file mode 100644 index 0000000000..b4de83d6b5 --- /dev/null +++ b/mkspecs/features/uikit/default_post.prf @@ -0,0 +1,120 @@ +equals(TEMPLATE, app):qt { + # If the application uses Qt, it needs to be an application bundle + # to be able to deploy and run on iOS. The only exception to this + # is if you're working with a jailbroken device and can run the + # resulting binary from the console/over SSH, but that's not a + # use-case we care about, so no need to complicate the logic. + CONFIG *= app_bundle + + # For Qt applications we want Xcode project files as the generated output, + # but since qmake doesn't handle the transition between makefiles and Xcode + # project files (which happens when using subdirs), we can't just override + # MAKEFILE_GENERATOR. Instead, we generate the Xcode project by spawning a + # child qmake process with -spec macx-xcode and let the top level qmake + # process generate a wrapper makefile that forwards everything to xcodebuild. + equals(MAKEFILE_GENERATOR, UNIX): \ + CONFIG = xcodebuild $$CONFIG +} + +load(default_post) + +macx-xcode { + device_family.name = TARGETED_DEVICE_FAMILY + ios: device_family.value = $$QMAKE_IOS_TARGETED_DEVICE_FAMILY + tvos: device_family.value = $$QMAKE_TVOS_TARGETED_DEVICE_FAMILY + watchos: device_family.value = $$QMAKE_WATCHOS_TARGETED_DEVICE_FAMILY + QMAKE_MAC_XCODE_SETTINGS += device_family + + ios { + # If QMAKE_BUNDLE_DATA contains an asset catalog that includes an + # AppIcon.appiconset, we configure Xcode to use it for app icons. + for(bundle_data, QMAKE_BUNDLE_DATA) { + for(bundle_file, $${bundle_data}.files) { + !contains(bundle_file, .*\.xcassets$): next() + !exists($$absolute_path($$bundle_file/AppIcon.appiconset, $$_PRO_FILE_PWD_)): next() + + asset_catalog_appicon.name = "ASSETCATALOG_COMPILER_APPICON_NAME" + asset_catalog_appicon.value = "AppIcon" + QMAKE_MAC_XCODE_SETTINGS += asset_catalog_appicon + break() + } + !isEmpty(asset_catalog_appicon.name): break() + } + + # Set up default 4-inch iPhone/iPod launch image so that our apps + # support the full screen resolution of those devices. + qmake_launch_image = Default-568h@2x.png + qmake_copy_image.input = $$QMAKESPEC/$$qmake_launch_image + qmake_copy_image.output = $$OUT_PWD/$${TARGET}.xcodeproj/$$qmake_launch_image + qmake_copy_image.CONFIG = verbatim + QMAKE_SUBSTITUTES += qmake_copy_image + qmake_launch_images.files = $$qmake_copy_image.output + QMAKE_BUNDLE_DATA += qmake_launch_images + + lessThan(QMAKE_XCODE_VERSION, "6.0") { + warning("You need to update Xcode to version 6 or newer to fully support iPhone6/6+") + } else { + # Set up default LaunchScreen to support iPhone6/6+ + qmake_launch_screen = LaunchScreen.xib + qmake_copy_launch_screen.input = $$QMAKESPEC/$$qmake_launch_screen + qmake_copy_launch_screen.output = $$OUT_PWD/$${TARGET}.xcodeproj/$$qmake_launch_screen + QMAKE_SUBSTITUTES += qmake_copy_launch_screen + qmake_launch_screens.files = $$qmake_copy_launch_screen.output + QMAKE_BUNDLE_DATA += qmake_launch_screens + } + } +} + +macx-xcode { + arch_device.name = "ARCHS[sdk=$${device.sdk}*]" + arch_simulator.name = "ARCHS[sdk=$${simulator.sdk}*]" + ios { + arch_device.value = $$QMAKE_IOS_DEVICE_ARCHS + arch_simulator.value = $$QMAKE_IOS_SIMULATOR_ARCHS + QMAKE_XCODE_ARCHS = $$QMAKE_IOS_DEVICE_ARCHS $$QMAKE_IOS_SIMULATOR_ARCHS + } + tvos { + arch_device.value = $$QMAKE_TVOS_DEVICE_ARCHS + arch_simulator.value = $$QMAKE_TVOS_SIMULATOR_ARCHS + QMAKE_XCODE_ARCHS = $$QMAKE_TVOS_DEVICE_ARCHS $$QMAKE_TVOS_SIMULATOR_ARCHS + } + watchos { + arch_device.value = $$QMAKE_WATCHOS_DEVICE_ARCHS + arch_simulator.value = $$QMAKE_WATCHOS_SIMULATOR_ARCHS + QMAKE_XCODE_ARCHS = $$QMAKE_WATCHOS_DEVICE_ARCHS $$QMAKE_WATCHOS_SIMULATOR_ARCHS + } + + QMAKE_MAC_XCODE_SETTINGS += arch_device arch_simulator + + only_active_arch.name = ONLY_ACTIVE_ARCH + only_active_arch.value = YES + only_active_arch.build = debug + QMAKE_MAC_XCODE_SETTINGS += only_active_arch +} else { + VALID_ARCHS = + !simulator|simulator_and_device { + ios: VALID_ARCHS += $$QMAKE_IOS_DEVICE_ARCHS + tvos: VALID_ARCHS += $$QMAKE_TVOS_DEVICE_ARCHS + watchos: VALID_ARCHS += $$QMAKE_WATCHOS_DEVICE_ARCHS + } + simulator { + ios: VALID_ARCHS += $$QMAKE_IOS_SIMULATOR_ARCHS + tvos: VALID_ARCHS += $$QMAKE_TVOS_SIMULATOR_ARCHS + watchos: VALID_ARCHS += $$QMAKE_WATCHOS_SIMULATOR_ARCHS + } + + single_arch: VALID_ARCHS = $$first(VALID_ARCHS) + + ACTIVE_ARCHS = $(filter $(EXPORT_VALID_ARCHS), $(ARCHS)) + ARCH_ARGS = $(foreach arch, $(if $(EXPORT_ACTIVE_ARCHS), $(EXPORT_ACTIVE_ARCHS), $(EXPORT_VALID_ARCHS)), -arch $(arch)) + + QMAKE_EXTRA_VARIABLES += VALID_ARCHS ACTIVE_ARCHS ARCH_ARGS + + arch_flags = $(EXPORT_ARCH_ARGS) + + QMAKE_CFLAGS += $$arch_flags + QMAKE_CXXFLAGS += $$arch_flags + QMAKE_LFLAGS += $$arch_flags + + QMAKE_PCH_ARCHS = $$VALID_ARCHS +} diff --git a/mkspecs/features/uikit/default_pre.prf b/mkspecs/features/uikit/default_pre.prf new file mode 100644 index 0000000000..8b5b3ccfe9 --- /dev/null +++ b/mkspecs/features/uikit/default_pre.prf @@ -0,0 +1,22 @@ + +load(default_pre) + +!isEmpty(QT_VERSION) { + qtConfig(simulator_and_device)|contains(QMAKE_MAC_SDK, ^$${device.sdk}.*): \ + CONFIG += device $${device.sdk} + qtConfig(simulator_and_device)|contains(QMAKE_MAC_SDK, ^$${simulator.sdk}.*): \ + CONFIG += simulator $${simulator.sdk} + + qtConfig(simulator_and_device) { + # For a simulator_and_device build all the config tests + # are based on the device's ARM SDK, but we know that the simulator + # is Intel and that we support SSE/SSE2. + QT_CPU_FEATURES.$$QT_ARCH += sse sse2 + CONFIG += sse sse2 + DEFINES += QT_COMPILER_SUPPORTS_SSE2 + } +} + +# Check for supported Xcode versions +lessThan(QMAKE_XCODE_VERSION, "4.3"): \ + error("This mkspec requires Xcode 4.3 or later") diff --git a/mkspecs/features/uikit/device_destinations.sh b/mkspecs/features/uikit/device_destinations.sh new file mode 100755 index 0000000000..162ad01aaf --- /dev/null +++ b/mkspecs/features/uikit/device_destinations.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +############################################################################# +## +## Copyright (C) 2016 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is the build configuration utility of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +booted_simulator=$($DIR/devices.pl "$1" "Booted" "NOT unavailable" | tail -n 1) +echo "SIMULATOR_DEVICES = $booted_simulator" + +xcodebuild test -scheme $2 -destination 'id=0' -destination-timeout 1 2>&1| sed -n 's/{ \(platform:.*\) }/\1/p' | while read destination; do + id=$(echo $destination | sed -n -E 's/.*id:([^ ,]+).*/\1/p') + [[ $id == *"placeholder"* ]] && continue + + echo $destination | tr ',' '\n' | while read keyval; do + key=$(echo $keyval | cut -d ':' -f 1 | tr '[:lower:]' '[:upper:]') + val=$(echo $keyval | cut -d ':' -f 2) + echo "%_$id: DESTINATION_${key} = $val" + + if [ $key = 'PLATFORM' ]; then + if [ "$val" = "iOS" ]; then + echo "HARDWARE_DEVICES += $id" + elif [ "$val" = "iOS Simulator" -a "$id" != "$booted_simulator" ]; then + echo "SIMULATOR_DEVICES += $id" + elif [ "$val" = "tvOS" ]; then + echo "HARDWARE_DEVICES += $id" + elif [ "$val" = "tvOS Simulator" -a "$id" != "$booted_simulator" ]; then + echo "SIMULATOR_DEVICES += $id" + elif [ "$val" = "watchOS" ]; then + echo "HARDWARE_DEVICES += $id" + elif [ "$val" = "watchOS Simulator" -a "$id" != "$booted_simulator" ]; then + echo "SIMULATOR_DEVICES += $id" + fi + fi + done + echo +done diff --git a/mkspecs/features/uikit/devices.pl b/mkspecs/features/uikit/devices.pl new file mode 100755 index 0000000000..eb45d1dab9 --- /dev/null +++ b/mkspecs/features/uikit/devices.pl @@ -0,0 +1,50 @@ +#!/usr/bin/perl + +############################################################################# +## +## Copyright (C) 2016 The Qt Company Ltd. +## Contact: http://www.qt.io/licensing/ +## +## This file is the build configuration utility of the Qt Toolkit. +## +## $QT_BEGIN_LICENSE:LGPL21$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see http://www.qt.io/terms-conditions. For further +## information use the contact form at http://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 2.1 or version 3 as published by the Free +## Software Foundation and appearing in the file LICENSE.LGPLv21 and +## LICENSE.LGPLv3 included in the packaging of this file. Please review the +## following information to ensure the GNU Lesser General Public License +## requirements will be met: https://www.gnu.org/licenses/lgpl.html and +## http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +## +## As a special exception, The Qt Company gives you certain additional +## rights. These rights are described in The Qt Company LGPL Exception +## version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +## +## $QT_END_LICENSE$ +## +############################################################################# + +$output = `xcrun simctl list devices --json 2>&1`; +$output =~ s/\n//g; + +BLOCK: +foreach $block ($output =~ /{.*?}/g) { + foreach $filter (@ARGV) { + if ($filter =~ /^NOT\s(.*)/) { + $block =~ /$1/ && next BLOCK; + } else { + $block =~ /$filter/ || next BLOCK; + } + } + $block =~ /udid[:|\s|\"]+(.*)\"/; + print "$1\n"; +} diff --git a/mkspecs/features/uikit/exclusive_builds_post.prf b/mkspecs/features/uikit/exclusive_builds_post.prf new file mode 100644 index 0000000000..1fb0a55846 --- /dev/null +++ b/mkspecs/features/uikit/exclusive_builds_post.prf @@ -0,0 +1,8 @@ + +xcodebuild { + # Prevent qmake from generating empty output dirs for each exclusive build, + # as Xcode will do this by itself, and with a different name. + QMAKE_DIR_REPLACE = +} + +load(exclusive_builds_post) diff --git a/mkspecs/features/uikit/qt.prf b/mkspecs/features/uikit/qt.prf new file mode 100644 index 0000000000..4475c137dc --- /dev/null +++ b/mkspecs/features/uikit/qt.prf @@ -0,0 +1,33 @@ + +qt_depends = $$resolve_depends(QT, "QT.") +!watchos:equals(TEMPLATE, app):contains(qt_depends, gui(-private)?) { + LIBS *= -L$$[QT_INSTALL_PLUGINS/get]/platforms + + lib_name = qios + lib_path_and_base = $$[QT_INSTALL_PLUGINS/get]/platforms/lib$${lib_name}$$qtPlatformTargetSuffix() + LIBS += -l$${lib_name}$$qtPlatformTargetSuffix() $$fromfile($${lib_path_and_base}.prl, QMAKE_PRL_LIBS) + + !bitcode { + # By marking qt_registerPlatformPlugin as undefined, we ensure that + # the plugin.o translation unit is considered for inclusion in + # the final binary, which in turn ensures that the plugin's + # static initializer is included and run. + QMAKE_LFLAGS += -u _qt_registerPlatformPlugin + + # We do link and dependency resolution for the platform plugin + # manually, since we know we always need the plugin, so we don't + # need to generate an import for it. + QTPLUGIN.platforms = - + } + + !no_main_wrapper { + # The LC_MAIN load command available in iOS 6.0 and above allows dyld to + # directly call the entrypoint instead of going through _start in crt.o. + # Passing -e to the linker changes the entrypoint from _main to our custom + # wrapper that calls UIApplicationMain and dispatches back to main() once + # the application has started up and is ready to initialize QApplication. + QMAKE_LFLAGS += -Wl,-e,_qt_main_wrapper + } +} + +load(qt) diff --git a/mkspecs/features/uikit/qt_config.prf b/mkspecs/features/uikit/qt_config.prf new file mode 100644 index 0000000000..5fa5a536f8 --- /dev/null +++ b/mkspecs/features/uikit/qt_config.prf @@ -0,0 +1,19 @@ +load(qt_config) + +isEmpty(QT_ARCH) { + # The configure tests are run without QT_ARCH being resolved yet, which + # means we fail to pass -arch to the compiler, resulting in broke tests. + # As the Xcode toolchain doesn't seem to have a way to auto-detect the + # arch based on the SDK, we have to hard-code the arch for configure. + contains(QMAKE_MAC_SDK, $${device.sdk}.*) { + QT_ARCH = arm + } else { # Simulator + ios: QT_ARCH = i386 + tvos: QT_ARCH = x64 + watchos: QT_ARCH = i386 + } + + # Prevent the arch/config tests from building as multi-arch binaries, + # as we only want the lowest common denominator features. + CONFIG += single_arch +} diff --git a/mkspecs/features/uikit/qt_parts.prf b/mkspecs/features/uikit/qt_parts.prf new file mode 100644 index 0000000000..81814a62b0 --- /dev/null +++ b/mkspecs/features/uikit/qt_parts.prf @@ -0,0 +1,5 @@ + +# Disable tests for anything but qtbase for now +!equals(TARGET, qtbase): QT_BUILD_PARTS -= tests + +load(qt_parts) diff --git a/mkspecs/features/uikit/resolve_config.prf b/mkspecs/features/uikit/resolve_config.prf new file mode 100644 index 0000000000..70ddd8be52 --- /dev/null +++ b/mkspecs/features/uikit/resolve_config.prf @@ -0,0 +1,11 @@ + +xcodebuild { + # Xcode project files always support both Debug and Release configurations + # and device and simulator targets, so we make sure the wrapper-makefile + # also does. + CONFIG += debug_and_release simulator_and_device +} + +load(resolve_config) + +!macx-xcode:xcodebuild: addExclusiveBuilds(simulator, device) diff --git a/mkspecs/features/uikit/sdk.prf b/mkspecs/features/uikit/sdk.prf new file mode 100644 index 0000000000..54674eb396 --- /dev/null +++ b/mkspecs/features/uikit/sdk.prf @@ -0,0 +1,29 @@ + +# In case the user sets the SDK manually +contains(QMAKE_MAC_SDK, ^$${simulator.sdk}.*) { + !isEmpty(QT_VERSION):qtConfig(simulator_and_device): \ + error("Simulator is handled automatically for simulator_and_device") + + CONFIG += simulator $${simulator.sdk} +} + +build_pass:simulator: \ + QMAKE_MAC_SDK ~= s,^$${device.sdk},$${simulator.sdk}, + +load(sdk) + +macx-xcode { + sdk_path_device.name = "QMAKE_MAC_SDK_PATH[sdk=$${device.sdk}*]" + sdk_path_device.value = $$xcodeSDKInfo(Path, $${device.sdk}) + sdk_path_simulator.name = "QMAKE_MAC_SDK_PATH[sdk=$${simulator.sdk}*]" + sdk_path_simulator.value = $$xcodeSDKInfo(Path, $${simulator.sdk}) + QMAKE_MAC_XCODE_SETTINGS += sdk_path_device sdk_path_simulator + QMAKE_MAC_SDK_PATH = "$(QMAKE_MAC_SDK_PATH)" + + sdk_platform_path_device.name = "QMAKE_MAC_SDK_PLATFORM_PATH[sdk=$${device.sdk}*]" + sdk_platform_path_device.value = $$xcodeSDKInfo(PlatformPath, $${device.sdk}) + sdk_platform_path_simulator.name = "QMAKE_MAC_SDK_PLATFORM_PATH[sdk=$${simulator.sdk}*]" + sdk_platform_path_simulator.value = $$xcodeSDKInfo(PlatformPath, $${simulator.sdk}) + QMAKE_MAC_XCODE_SETTINGS += sdk_platform_path_device sdk_platform_path_simulator + QMAKE_MAC_SDK_PLATFORM_PATH = "$(QMAKE_MAC_SDK_PLATFORM_PATH)" +} diff --git a/mkspecs/features/uikit/testcase.prf b/mkspecs/features/uikit/testcase.prf new file mode 100644 index 0000000000..e16c163ffa --- /dev/null +++ b/mkspecs/features/uikit/testcase.prf @@ -0,0 +1,12 @@ +# Pretend we have a target, even though our template is aux +xcodebuild: \ + CONFIG += have_target + +load(testcase) + +# We provide our own check logic +xcodebuild { + check.depends = + check.commands = + QMAKE_EXTRA_TARGETS *= check +} diff --git a/mkspecs/features/uikit/testcase_targets.prf b/mkspecs/features/uikit/testcase_targets.prf new file mode 100644 index 0000000000..e0a2922c3f --- /dev/null +++ b/mkspecs/features/uikit/testcase_targets.prf @@ -0,0 +1,3 @@ +# For the xcodebuild wrapper makefile we deal with test targets manually +!xcodebuild: \ + load(testcase_targets) diff --git a/mkspecs/features/uikit/xcodebuild.mk b/mkspecs/features/uikit/xcodebuild.mk new file mode 100644 index 0000000000..57011eaf01 --- /dev/null +++ b/mkspecs/features/uikit/xcodebuild.mk @@ -0,0 +1,102 @@ + +# We don't want xcodebuild to run in parallel +.NOTPARALLEL: + +# Functions +targets = $(foreach target, $(EXPORT_SUBTARGETS), $(target)-$(strip $(1))) +toupper = $(shell echo $1 | tr '[:lower:]' '[:upper:]') +tolower = $(shell echo $1 | tr '[:upper:]' '[:lower:]') +basesdk = $(shell echo $1 | sed 's/[0-9.]*$$//') + +# Explicit comma variable +, := , + +# Default targets +first: build +all: build_all + +.DEFAULT_GOAL = first + +# Top level targets +build: build_first +clean: clean_first +install: install_first +check: check_first +distclean: clean_all + +$(EXPORT_SUBTARGETS): % : %-build + +# Generic targets +%_first: $(firstword $(call targets, %)) ; +%_all: $(call targets, %) ; + +# Actions +%-build: ACTION = build +%-build: xcodebuild-% ; + +%-clean: ACTION = clean +%-clean: xcodebuild-% ; + +%-install: ACTION = install +%-install: xcodebuild-% ; + +# Simulator doesn't support archiving +%-simulator-install: ACTION = build +simulator-install: ACTION = build + +# Limit check to a single configuration +%-device-check: check-device ; +%-simulator-check: check-simulator ; + +# SDK +%-device: SDK = $(DEVICE_SDK) +%-simulator: SDK = $(SIMULATOR_SDK) + +# Configuration +release-%: CONFIGURATION = Release +debug-%: CONFIGURATION = Debug + +MAKEFILE_DIR := $(dir $(lastword $(MAKEFILE_LIST))) + +# Test and build (device) destinations +ifneq ($(filter check%,$(MAKECMDGOALS)),) + ifeq ($(DEVICES),) + $(info Enumerating test destinations (you may override this by setting DEVICES explicitly), please wait...) + DESTINATIONS_INCLUDE = /tmp/device_destinations.mk + $(shell $(MAKEFILE_DIR)device_destinations.sh '$(EXPORT_DEVICE_FILTER)' $(TARGET) > $(DESTINATIONS_INCLUDE)) + include $(DESTINATIONS_INCLUDE) + endif +endif + +%-simulator: DEVICES = $(firstword $(SIMULATOR_DEVICES)) +%-device: DEVICES = $(HARDWARE_DEVICES) + +GENERIC_DEVICE_DESTINATION := $(EXPORT_GENERIC_DEVICE_DESTINATION) +GENERIC_SIMULATOR_DESTINATION := "id=$(shell $(MAKEFILE_DIR)devices.pl '$(EXPORT_DEVICE_FILTER)' "NOT unavailable" | tail -n 1)" + +%-simulator: DESTINATION = $(if $(DESTINATION_ID),"id=$(DESTINATION_ID)",$(GENERIC_SIMULATOR_DESTINATION)) +%-device: DESTINATION = $(if $(DESTINATION_ID),"id=$(DESTINATION_ID)",$(GENERIC_DEVICE_DESTINATION)) + +# Xcodebuild + +DESTINATION_MESSAGE = "Running $(call tolower,$(CONFIGURATION)) $(ACTION) \ + on '$(DESTINATION_NAME)' ($(DESTINATION_ID))$(if $(DESTINATION_OS),$(,) $(DESTINATION_PLATFORM) $(DESTINATION_OS),)" + +xcodebuild-%: + @$(if $(DESTINATION_NAME), echo $(DESTINATION_MESSAGE),) + xcodebuild $(ACTION) -project $(TARGET).xcodeproj -scheme $(TARGET) $(if $(SDK), -sdk $(SDK),) $(if $(CONFIGURATION), -configuration $(CONFIGURATION),) $(if $(DESTINATION), -destination $(DESTINATION) -destination-timeout 1,) $(if $(INSTALL_ROOT), DSTROOT=$(INSTALL_ROOT),) + +xcodebuild-check-device_%: DESTINATION_ID=$(lastword $(subst _, ,$@)) + +# Special check target (requires SECONDEXPANSION due to devices) +.SECONDEXPANSION: +check-%: ACTION = test +check-%: $$(foreach device, $$(DEVICES), xcodebuild-check-device_$$(device)) ; + @echo $(if $^, Ran $(call tolower,$(CONFIGURATION)) tests on $(words $^) $(SDK) destination\(s\): $(DEVICES), No compatible test devices found for \'$(SDK)\' SDK && false) + +# Determined by device +check-%: SDK = + +# Default to debug for testing +check-%: CONFIGURATION = Debug + diff --git a/mkspecs/features/uikit/xcodebuild.prf b/mkspecs/features/uikit/xcodebuild.prf new file mode 100644 index 0000000000..82bcd9a315 --- /dev/null +++ b/mkspecs/features/uikit/xcodebuild.prf @@ -0,0 +1,65 @@ + +# For Qt applications we want Xcode project files as the generated output, +# but since qmake doesn't handle the transition between makefiles and Xcode +# project files (which happens when using subdirs), we can't just override +# MAKEFILE_GENERATOR. Instead, we generate the Xcode project by spawing a +# child qmake process with -spec macx-xcode and let the top level qmake +# process generate a wrapper makefile that forwards everything to xcodebuild. + +TEMPLATE = aux + +SOURCES = +OBJECTIVE_SOURCES = +RESOURCES = +INSTALLS = +QMAKE_EXTRA_COMPILERS = + +!mkpath($$OUT_PWD): \ + error("Failed to create $$OUT_PWD") + +args = +for(arg, QMAKE_ARGS): \ + args += $$system_quote($$arg) + +cmd = "$$QMAKE_QMAKE $$args $$system_quote($$_PRO_FILE_) -spec macx-xcode" +debug(1, "Generating Xcode project in $$OUT_PWD using '$$cmd'") +system("cd $$system_quote($$OUT_PWD) && $$cmd") + +# Subtargets + +for(build, BUILDS): \ + SUBTARGETS += $$eval($${build}.target) +QMAKE_EXTRA_VARIABLES += SUBTARGETS + +CONFIG += no_default_goal_deps + +DEVICE_SDK = $${device.sdk} +SIMULATOR_SDK = $${simulator.sdk} +ios { + DEVICE_FILTER = "iPhone|iPad" + GENERIC_DEVICE_DESTINATION = "generic/platform=iOS" +} +tvos { + DEVICE_FILTER = "Apple TV" + GENERIC_DEVICE_DESTINATION = "generic/platform=tvOS" +} +watchos { + DEVICE_FILTER = "Apple Watch" + GENERIC_DEVICE_DESTINATION = "generic/platform=watchOS" +} +QMAKE_EXTRA_VARIABLES += DEVICE_SDK SIMULATOR_SDK DEVICE_FILTER GENERIC_DEVICE_DESTINATION + +QMAKE_EXTRA_INCLUDES += $$shell_quote($$PWD/xcodebuild.mk) + +# Distclean + +distfiles = $${TARGET}.xcodeproj +for(build, BUILDS): \ + distfiles += $$title($$eval($${build}.target)) +distclean_xcodebuild.commands = -$(DEL_FILE) -R $$distfiles + +distclean.depends += clean_all distclean_xcodebuild +QMAKE_EXTRA_TARGETS += distclean distclean_xcodebuild + +# Empty exclusive builds, we've set them up manually +BUILDS = diff --git a/mkspecs/features/unix/opengl.prf b/mkspecs/features/unix/opengl.prf index 123c647be2..72b5ac453d 100644 --- a/mkspecs/features/unix/opengl.prf +++ b/mkspecs/features/unix/opengl.prf @@ -1,4 +1,4 @@ -contains(QT_CONFIG, opengles2) { +qtConfig(opengles2) { INCLUDEPATH += $$QMAKE_INCDIR_OPENGL_ES2 !isEmpty(QMAKE_LIBDIR_OPENGL_ES2):QMAKE_LIBDIR += $$QMAKE_LIBDIR_OPENGL_ES2 target_qt:LIBS_PRIVATE += $$QMAKE_LIBS_OPENGL_ES2 diff --git a/mkspecs/features/unix/openvg.prf b/mkspecs/features/unix/openvg.prf index 29acec18ff..500dfc50c6 100644 --- a/mkspecs/features/unix/openvg.prf +++ b/mkspecs/features/unix/openvg.prf @@ -2,13 +2,13 @@ !isEmpty(QMAKE_LIBDIR_OPENVG): QMAKE_LIBDIR += -L$$QMAKE_LIBDIR_OPENVG !isEmpty(QMAKE_LIBS_OPENVG): LIBS += $$QMAKE_LIBS_OPENVG -contains(QT_CONFIG, egl) { +qtConfig(egl) { !isEmpty(QMAKE_INCDIR_EGL): INCLUDEPATH += $$QMAKE_INCDIR_EGL !isEmpty(QMAKE_LIBDIR_EGL): LIBS += -L$$QMAKE_LIBDIR_EGL !isEmpty(QMAKE_LIBS_EGL): LIBS += $$QMAKE_LIBS_EGL } -contains(QT_CONFIG, openvg_on_opengl) { +qtConfig(openvg_on_opengl) { !isEmpty(QMAKE_INCDIR_OPENGL): INCLUDEPATH += $$QMAKE_INCDIR_OPENGL !isEmpty(QMAKE_LIBDIR_OPENGL): QMAKE_LIBDIR += -L$$QMAKE_LIBDIR_OPENGL !isEmpty(QMAKE_LIBS_OPENGL): LIBS += $$QMAKE_LIBS_OPENGL diff --git a/mkspecs/features/win32/console.prf b/mkspecs/features/win32/console.prf index 629f3af59b..739bb0e01b 100644 --- a/mkspecs/features/win32/console.prf +++ b/mkspecs/features/win32/console.prf @@ -1,6 +1,5 @@ CONFIG -= windows -QMAKE_LFLAGS += $$replace(QMAKE_LFLAGS_CONSOLE, @QMAKE_SUBSYSTEM_SUFFIX@, $$QMAKE_SUBSYSTEM_SUFFIX) +QMAKE_LFLAGS += $$QMAKE_LFLAGS_CONSOLE contains(TEMPLATE, ".*app") { - wince: QMAKE_LFLAGS += /ENTRY:mainACRTStartup QMAKE_LFLAGS += $$QMAKE_LFLAGS_EXE } diff --git a/mkspecs/features/win32/opengl.prf b/mkspecs/features/win32/opengl.prf index 8173348bc4..ebc29d0923 100644 --- a/mkspecs/features/win32/opengl.prf +++ b/mkspecs/features/win32/opengl.prf @@ -1,32 +1,21 @@ -# WinCE does not have a platform directory for .prf files, and the -# win32 directory is searched for .prfs by qmake on WinCE. Ideally -# there should be a features/wince/opengl.prf which contains the wince -# block below. - -wince* { - include(../unix/opengl.prf) -} else { - contains(QT_CONFIG, opengles2) { -# For Desktop, use the ANGLE library location passed on from configure. - INCLUDEPATH += $$QMAKE_INCDIR_OPENGL_ES2 - CONFIG(debug, debug|release) { - QMAKE_LIBDIR += $$QMAKE_LIBDIR_OPENGL_ES2_DEBUG - contains(QT_CONFIG, angle) { - LIBS += $$QMAKE_LIBS_OPENGL_ES2_DEBUG - } else { - LIBS += $$QMAKE_LIBS_OPENGL_ES2 - } +qtConfig(opengles2) { +# For Desktop, use the ANGLE library location passed on from configure. + INCLUDEPATH += $$QMAKE_INCDIR_OPENGL_ES2 + CONFIG(debug, debug|release) { + QMAKE_LIBDIR += $$QMAKE_LIBDIR_OPENGL_ES2_DEBUG + qtConfig(angle) { + LIBS += $$QMAKE_LIBS_OPENGL_ES2_DEBUG } else { LIBS += $$QMAKE_LIBS_OPENGL_ES2 - QMAKE_LIBDIR += $$QMAKE_LIBDIR_OPENGL_ES2_RELEASE } - DEFINES += QT_OPENGL_ES_2 QT_OPENGL_ES_2_ANGLE - contains(QT_CONFIG, static): DEFINES += QT_OPENGL_ES_2_ANGLE_STATIC GL_APICALL= EGLAPI= - QT_CONFIG -= opengl } else { - !contains(QT_CONFIG, dynamicgl) { - QMAKE_LIBS += $$QMAKE_LIBS_OPENGL - QMAKE_LFLAGS += $$QMAKE_LFLAGS_OPENGL - } + LIBS += $$QMAKE_LIBS_OPENGL_ES2 + QMAKE_LIBDIR += $$QMAKE_LIBDIR_OPENGL_ES2_RELEASE + } + qtConfig(static): DEFINES += GL_APICALL= EGLAPI= +} else { + !qtConfig(dynamicgl) { + QMAKE_LIBS += $$QMAKE_LIBS_OPENGL + QMAKE_LFLAGS += $$QMAKE_LFLAGS_OPENGL } } diff --git a/mkspecs/features/win32/openvg.prf b/mkspecs/features/win32/openvg.prf index 0df5b6621a..9d9a49d856 100644 --- a/mkspecs/features/win32/openvg.prf +++ b/mkspecs/features/win32/openvg.prf @@ -1,7 +1,7 @@ QMAKE_LIBS += $$QMAKE_LIBS_OPENVG QMAKE_LFLAGS += $$QMAKE_LFLAGS_OPENVG -contains(QT_CONFIG, openvg_on_opengl) { +qtConfig(openvg_on_opengl) { QMAKE_LIBS += $$QMAKE_LIBS_OPENGL QMAKE_LFLAGS += $$QMAKE_LFLAGS_OPENGL } diff --git a/mkspecs/features/win32/qt_config.prf b/mkspecs/features/win32/qt_config.prf deleted file mode 100644 index 49b4c79431..0000000000 --- a/mkspecs/features/win32/qt_config.prf +++ /dev/null @@ -1,10 +0,0 @@ -load(qt_config) - -equals(QMAKE_TARGET_OS, xp) { - # http://blogs.msdn.com/b/vcblog/archive/2012/10/08/10357555.aspx?PageIndex=3 - equals(QT_ARCH, x86_64) { - QMAKE_SUBSYSTEM_SUFFIX = ,5.02 - } else { - QMAKE_SUBSYSTEM_SUFFIX = ,5.01 - } -} diff --git a/mkspecs/features/win32/windows.prf b/mkspecs/features/win32/windows.prf index 986067fc8c..ecb167bf18 100644 --- a/mkspecs/features/win32/windows.prf +++ b/mkspecs/features/win32/windows.prf @@ -1,5 +1,5 @@ CONFIG -= console -QMAKE_LFLAGS += $$replace(QMAKE_LFLAGS_WINDOWS, @QMAKE_SUBSYSTEM_SUFFIX@, $$QMAKE_SUBSYSTEM_SUFFIX) +QMAKE_LFLAGS += $$QMAKE_LFLAGS_WINDOWS contains(TEMPLATE, ".*app") { QMAKE_LFLAGS += $$QMAKE_LFLAGS_EXE mingw:DEFINES += QT_NEEDS_QMAIN diff --git a/mkspecs/features/winrt/default_pre.prf b/mkspecs/features/winrt/default_pre.prf index 8299950d8b..f397ef3d61 100644 --- a/mkspecs/features/winrt/default_pre.prf +++ b/mkspecs/features/winrt/default_pre.prf @@ -1,12 +1,14 @@ *msvc2015 { - # Note that the order is important, ucrt(d) has to be first - # Otherwise the linker might use malloc from a different library - # but free_dbg() from the runtime, causing assert when deleting - # items from different heaps + # Note that the order is important - ucrt(d) has to be first. + # Otherwise, the linker might use malloc from a different library, + # but free_dbg() from the runtime, causing an assertion failure + # due to deleting an item from a different heap. + # vcruntime(d) is necessary when we don't link to any libraries + # which would pull it in transitively. CONFIG(debug, debug|release): \ - QMAKE_LIBS = ucrtd.lib $$QMAKE_LIBS + QMAKE_LIBS = ucrtd.lib vcruntimed.lib $$QMAKE_LIBS else: \ - QMAKE_LIBS = ucrt.lib $$QMAKE_LIBS + QMAKE_LIBS = ucrt.lib vcruntime.lib $$QMAKE_LIBS } equals(TEMPLATE, "vcapp"): CONFIG += windeployqt |