diff options
Diffstat (limited to 'mkspecs/features')
33 files changed, 1842 insertions, 92 deletions
diff --git a/mkspecs/features/configure.prf b/mkspecs/features/configure.prf index a890c7f6b9..e1040d250e 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). diff --git a/mkspecs/features/configure_base.prf b/mkspecs/features/configure_base.prf new file mode 100644 index 0000000000..fd1730741a --- /dev/null +++ b/mkspecs/features/configure_base.prf @@ -0,0 +1,54 @@ +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) { + msg = "+ $$1" + write_file($$QMAKE_CONFIG_LOG, msg, append) + $$QMAKE_CONFIG_VERBOSE: log("$$msg$$escape_expand(\\n)") +} + +defineTest(qtRunLoggedCommand) { + qtLog($$1) + write_file($${QMAKE_CONFIG_LOG}.part, "") + result = false + system("$$1 > \"$${QMAKE_CONFIG_LOG}.part\" 2>&1"): result = true + + output = $$cat($${QMAKE_CONFIG_LOG}.part, blob) + + write_file($${QMAKE_CONFIG_LOG}, output, append) + $$QMAKE_CONFIG_VERBOSE: log($$output) + + return($$result) +} + +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/default_pre.prf b/mkspecs/features/default_pre.prf index 62cd90912e..8841dc19f2 100644 --- a/mkspecs/features/default_pre.prf +++ b/mkspecs/features/default_pre.prf @@ -8,7 +8,7 @@ CONFIG = \ testcase_targets import_plugins import_qpa_plugin \ $$CONFIG -contains(QT_CONFIG, c++11):lessThan(QT_COMPILER_STDCXX, 201103): CONFIG += c++11 +CONFIG += c++11 !build_pass:defined(QT_EDITION, var):!equals(QT_EDITION, "OpenSource"):!equals(QT_EDITION, "Preview") { # @@ -56,6 +56,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/mac/sdk.prf b/mkspecs/features/mac/sdk.prf index ab37b1740f..c4d4c90f09 100644 --- a/mkspecs/features/mac/sdk.prf +++ b/mkspecs/features/mac/sdk.prf @@ -55,12 +55,13 @@ for(tool, $$list(QMAKE_CC QMAKE_CXX QMAKE_FIX_RPATH QMAKE_AR QMAKE_RANLIB QMAKE_ } !equals(MAKEFILE_GENERATOR, XCODE) { - ios:!host_buildĀ { + uikit:!host_buildĀ { simulator: \ version_identifier = $$simulator.deployment_identifier else: \ version_identifier = $$device.deployment_identifier - deployment_target = $$QMAKE_IOS_DEPLOYMENT_TARGET + ios: deployment_target = $$QMAKE_IOS_DEPLOYMENT_TARGET + tvos: deployment_target = $$QMAKE_TVOS_DEPLOYMENT_TARGET } else: osx { version_identifier = macosx deployment_target = $$QMAKE_MACOSX_DEPLOYMENT_TARGET 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/qt.prf b/mkspecs/features/qt.prf index 71e96b4651..8d96d18d06 100644 --- a/mkspecs/features/qt.prf +++ b/mkspecs/features/qt.prf @@ -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_build_config.prf b/mkspecs/features/qt_build_config.prf index 1e2d5c2423..f12cbb88fc 100644 --- a/mkspecs/features/qt_build_config.prf +++ b/mkspecs/features/qt_build_config.prf @@ -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 378f5bbd7c..448da9b797 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 b367bc12af..e50fdb767b 100644 --- a/mkspecs/features/qt_common.prf +++ b/mkspecs/features/qt_common.prf @@ -61,13 +61,15 @@ 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 { # If the module declares that it has does its clean-up of warnings, enable -Werror. # This setting is compiler-dependent anyway because it depends on the version of the # compiler. - clang:!ios { + clang:!uikit { # Apple clang 4.0-4.2,5.0-5.1,6.0-6.4 # Regular clang 3.3-3.8 apple_ver = $${QT_APPLE_CLANG_MAJOR_VERSION}.$${QT_APPLE_CLANG_MINOR_VERSION} diff --git a/mkspecs/features/qt_configure.prf b/mkspecs/features/qt_configure.prf new file mode 100644 index 0000000000..1a663fe1dc --- /dev/null +++ b/mkspecs/features/qt_configure.prf @@ -0,0 +1,1183 @@ +CONFIG -= qt debug_and_release +load(configure_base) + +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) +} + +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() + + 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, "^-.*")|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, "^-.*")|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) { + 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() + } + + # 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 { + error("Invalid command line parameter $$c") + } + + 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): \ + error("Unknown command line option $$c") + + 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) + } +} + +defineTest(qtConfTest_shell) { + test = $$eval($${1}.test) + dir = $$replace(test, [^/]*$, "") + test = $$replace(test, ([^/]*/)*, "") + args = $$eval($${1}.args) + # replace any things like $$QMAKE_CXX by their values + eval(args = $$args) + + test_dir = $$QMAKE_CONFIG_TESTS_DIR/$$dir + test_out_dir = $$shadowed($$test_dir) + test_cmd_base = "cd $$system_quote($$system_path($$test_out_dir)) &&" + + mkpath($$test_out_dir)|error("Aborting.") + + qtRunLoggedCommand("$$test_cmd_base $$test_dir/$${test} $${args}"): \ + return(false) + return(true) +} + +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("Aborting.") + + 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(qtConfFindInPath) { + ensurePathEnv() + for (dir, QMAKE_PATH_ENV) { + exists("$$dir/$${1}"): \ + return("$$dir/$${1}") + } + return() +} + +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") + isEmpty(pkg_config): \ + return(false) + } 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(qtConfTest_pkgConfig) { + pkg_config = $$qtConfPkgConfig($$eval($${1}.host)) + args = $$qtConfPrepareArgs($$eval($${1}.pkg-config-args)) + + !qtConfPkgConfigPackageExists($$pkg_config, $$args): \ + return(false) + + $${1}.libs = $$system("$$pkg_config --libs $$args") + $${1}.cflags = $$system("$$pkg_config --cflags $$args") + includes = $$system("$$pkg_config --cflags-only-I $$args") + includes ~= s/^-I//g + $${1}.includedir = $$includes + version = $$system("$$pkg_config --modversion $$args") + $${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)) + args = $$qtConfPrepareArgs($$eval($${1}.pkg-config-args)) + + !qtConfPkgConfigPackageExists($$pkg_config, $$args): \ + return(false) + + variable = $$eval($${1}.pkg-config-variable) + $${1}.value = $$system("$$pkg_config --variable=$$variable $$args") + export($${1}.value) + return(true) +} + +defineTest(qtConfTest_compile) { + test = $$eval($${1}.test) + host = $$eval($${1}.host) + isEmpty(host): host = false + + # get package config information + qtConfTest_pkgConfig($${1}) + + 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)) &&" + + # 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" + + # 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\"" + + # On WinRT we need to change the entry point as we cannot create windows + # applications + winrt: \ + qmake_args += " \"QMAKE_LFLAGS += /ENTRY:main\"" + + !$$host { + # add compiler flags, these are set for the target and should not be applied to host tests + !isEmpty(EXTRA_DEFINES): \ + qmake_args += "\"DEFINES += $$EXTRA_DEFINES\"" + !isEmpty(EXTRA_LIBDIR) \ + qmake_args += "\"QMAKE_LIBDIR += $$EXTRA_LIBDIR\"" + !isEmpty(EXTRA_FRAMEWORKPATH) \ + qmake_args += "\"QMAKE_FRAMEWORKPATH += $$EXTRA_FRAMEWORKPATH\"" + !isEmpty(EXTRA_INCLUDEPATH): \ + qmake_args += "\"INCLUDEPATH += $$EXTRA_INCLUDEPATH\"" + qmake_args += $$EXTRA_QMAKE_ARGS + } + + libs = $$eval($${1}.libs) + !isEmpty(libs): \ + qmake_args += "\"LIBS += $$libs\"" + + includedir = $$eval($${1}.includedir) + !isEmpty(includedir): \ + qmake_args += "\"INCLUDEPATH *= $$includedir\"" + + # Clean up after previous run + exists($$test_out_dir/Makefile): qtRunLoggedCommand("$$test_cmd_base $$QMAKE_MAKE distclean") + + mkpath($$test_out_dir)|error("Aborting.") + + !isEmpty(QMAKE_QTCONF): qtconfarg = -qtconf $$QMAKE_QTCONF + + # add possible command line args + qmake_args += $$qtConfPrepareArgs($$eval($${1}.args)) + + qtRunLoggedCommand("$$test_cmd_base $$qtConfPkgConfigEnv()$$system_quote($$system_path($$QMAKE_QMAKE)) $$qtconfarg $$qmake_args $$shell_quote($$test_dir)") { + qtRunLoggedCommand("$$test_cmd_base $$QMAKE_MAKE"): \ + return(true) + } + + return(false) +} + +defineTest(logn) { + log("$${1}$$escape_expand(\\n)") +} + +defineTest(qtLogTestResult) { + !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(qtConfIsBoolean) { + equals(1, "true")|equals(1, "false"): \ + return(true) + return(false) +} + +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") + + description = $$eval($${tpfx}.description) + !isEmpty(description) { + msg = "checking for $${description}... " + log($$msg) + $$QMAKE_CONFIG_VERBOSE: log("$$escape_expand(\\n)") + write_file($$QMAKE_CONFIG_LOG, msg, append) + } + + msg = "executing config test $${1}" + write_file($$QMAKE_CONFIG_LOG, msg, append) + + result = false + $${call}($${tpfx}): result = true + + !isEmpty(description): qtLogTestResult($${tpfx}, $$result) + $${tpfx}.result = $$result + export($${tpfx}.result) +} + +defineReplace(qtConfEvaluate) { + isEmpty(1): return(true) + + 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, "^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 + 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.") + $$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) { + priorities = 0 + for (feature, config.features._KEYS_): \ + priorities += $$eval(config.features.$${feature}.priority) + priorities = $$unique(priorities) + + for (p, priorities): \ + opriorities += $$format_number($$num_add($$p, 10000), width=5 zeropad) + opriorities = $$sorted(opriorities) + + priorities = + for (op, opriorities): \ + priorities += $$num_add($$op, -10000) + + for (priority, priorities) { + for (feature, config.features._KEYS_) { + p = $$eval(config.features.$${feature}.priority) + isEmpty(p): p = 0 + equals(p, $$priority): \ + 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 += $$feature + } + + 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($$feature) + } + + 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}" + } + + qtConfReportPadded($${1}, $$result) +} + +defineTest(qtConfReport_note) { + qtConfAddNote($${1}) +} + +defineTest(qtConfReport_warning) { + qtConfAddWarning($${1}) +} + +defineTest(qtConfReport_error) { + qtConfAddError($${1}) +} + +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}) + text = $$eval(config.features.$${feature}.description) + qtConfReport_feature($$indent$$text, $$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) { + qtConfAddReport(" ") + qtConfCreateReportRecurse(config.report, false) +} + +defineTest(qtConfCreateSummary) { + qtConfCreateReportRecurse(config.summary, "") +} + +defineTest(qtConfPrintReport) { + for (n, QT_CONFIGURE_REPORT): \ + logn($$n) + logn(" ") + + !isEmpty(QT_CONFIGURE_NOTES) { + for (n, QT_CONFIGURE_NOTES): \ + logn($$n) + logn(" ") + } + + !isEmpty(QT_CONFIGURE_WARNINGS) { + for (w, QT_CONFIGURE_WARNINGS): \ + logn($$w) + logn(" ") + } + + !isEmpty(QT_CONFIGURE_ERRORS) { + for (e, QT_CONFIGURE_ERRORS): \ + logn($$e) + !$$QMAKE_CONFIG_VERBOSE: logn("Check config.log for details.") + logn(" ") + + !equals(config.input.continue, yes): \ + error("Aborting.") + } +} + +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}.$${modifier}.$${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}.$${modifier}.$${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}.$${modifier}.$${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 + !$${3}:!$$negative: return() + $${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)) + 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 + + val = $$eval($${1}.name) + isEmpty(val) { + val = $$eval($${1}.feature) + $$negative: val = no-$$val + } + + $${2} { + !$$negative: qtConfOutputVar(append, $$pro, $$var, $$val) + } else { + $$negative: 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 + + $${2} { + !$$negative: qtConfOutputSetDefine($$output, $$define, $$value) + } else { + $$negative: 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") + } +} + +# 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) + + $${2} { + qtConfOutputVar(append, "publicPro", "QT_CONFIG", $$name) + } else { + f = $$upper($$replace(name, -, _)) + qtConfOutputVar(append, "privatePro", "DEFINES", "QT_NO_$$f") + } +} + + +defineTest(qtConfOutput_library) { + !$${2}: return() + + output = privatePro + name = "$$eval($${1}.name)" + isEmpty(name): \ + name = $$eval($${1}.feature) + NAME = $$upper($$replace(name, [-.], _)) + + lookup = "config.tests.$$eval($${1}.test)" + isEmpty(lookup): \ + error("Output type 'library' used in feature '$$eval($${1}.feature)' without a 'test' entry.") + + libs = $$eval($${lookup}.libs) + cflags = $$eval($${lookup}.cflags) + includes = $$eval($${lookup}.includedir) + version = $$split($${lookup}.version, '.') + + !isEmpty(libs): qtConfOutputVar(assign, $$output, QMAKE_LIBS_$$NAME, $$libs) + !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_MAJOR, $$first(version)) + qtConfOutputVar(assign, $$output, QMAKE_$${NAME}_VERSION_MINOR, $$member(version, 1)) + qtConfOutputVar(assign, $$output, QMAKE_$${NAME}_VERSION_PATCH, $$member(version, 2)) + } + export(config.output.$${output}) +} + +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'.") + + isEmpty($${opfx}.feature): \ + $${opfx}.feature = $$feature + + condition = $$eval($${opfx}.condition) + !isEmpty(condition) { + !$$qtConfEvaluate($$condition): \ + return(false) + } + + 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}ifndef $$define" + config.output.$$type += "$${LITERAL_HASH}define $$define $$value" + config.output.$$type += "$${LITERAL_HASH}endif" + } + } + defined(qtConfOutputPostProcess_$${type}, test): \ + qtConfOutputPostProcess_$${type}() + + write_file($$file, config.output.$${type})|error("Aborting.") + } +} + +# +# 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}.") + + qtConfParseCommandLine() + + # 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_functions.prf b/mkspecs/features/qt_functions.prf index 54641ce5a1..6dd4a36b5e 100644 --- a/mkspecs/features/qt_functions.prf +++ b/mkspecs/features/qt_functions.prf @@ -1,6 +1,6 @@ defineReplace(qtPlatformTargetSuffix) { - ios:CONFIG(simulator, simulator|device): \ + uikit:CONFIG(simulator, simulator|device): \ suffix = _$${simulator.sdk} else: \ suffix = diff --git a/mkspecs/features/qt_module.prf b/mkspecs/features/qt_module.prf index d9011f5482..923ac73200 100644 --- a/mkspecs/features/qt_module.prf +++ b/mkspecs/features/qt_module.prf @@ -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 @@ -114,10 +114,13 @@ 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)): \ + if(!simulator_and_device|CONFIG(device, simulator|device))) { FRAMEWORK_HEADERS.version = Versions FRAMEWORK_HEADERS.files = $$SYNCQT.HEADER_FILES $$SYNCQT.HEADER_CLASSES FRAMEWORK_HEADERS.path = Headers 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..e11b543cb4 100644 --- a/mkspecs/features/qt_plugin.prf +++ b/mkspecs/features/qt_plugin.prf @@ -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/uikit/bitcode.prf b/mkspecs/features/uikit/bitcode.prf new file mode 100644 index 0000000000..ecc6542b3c --- /dev/null +++ b/mkspecs/features/uikit/bitcode.prf @@ -0,0 +1,13 @@ +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 + } else { + QMAKE_CFLAGS += -fembed-bitcode-marker + QMAKE_CXXFLAGS += -fembed-bitcode-marker + QMAKE_OBJECTIVE_CFLAGS += -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..7c92a24556 --- /dev/null +++ b/mkspecs/features/uikit/default_post.prf @@ -0,0 +1,109 @@ +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 + 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 + } + + 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 { + # Be more specific about which architecture we're targeting + contains(QT_ARCH, arm.*) { + ios: VALID_ARCHS = $$QMAKE_IOS_DEVICE_ARCHS + tvos: VALID_ARCHS = $$QMAKE_TVOS_DEVICE_ARCHS + } else { + ios: VALID_ARCHS = $$QMAKE_IOS_SIMULATOR_ARCHS + tvos: VALID_ARCHS = $$QMAKE_TVOS_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 +} diff --git a/mkspecs/features/uikit/default_pre.prf b/mkspecs/features/uikit/default_pre.prf new file mode 100644 index 0000000000..ff45c1de0d --- /dev/null +++ b/mkspecs/features/uikit/default_pre.prf @@ -0,0 +1,31 @@ + +load(default_pre) + +# In case Qt was built for a specific SDK +!contains(QT_CONFIG, simulator_and_device):contains(QMAKE_MAC_SDK, ^$${simulator.sdk}.*): \ + CONFIG += simulator $${simulator.sdk} + +# Check for supported Xcode versions +lessThan(QMAKE_XCODE_VERSION, "4.3"): \ + error("This mkspec requires Xcode 4.3 or later") + +build_pass:simulator { + # For a simulator_and_device build all the config tests + # are based on the iPhoneOS ARM SDK, but we know that the simulator + # is i386 and that we support SSE/SSE2. + QT_ARCH = i386 + QT_CPU_FEATURES.i386 = sse sse2 + DEFINES += QT_COMPILER_SUPPORTS_SSE2 + CONFIG -= neon + CONFIG += sse sse2 +} +build_pass:appletvsimulator { + # For a simulator_and_device build all the config tests + # are based on the AppleTVOS ARM SDK, but we know that the simulator + # is x64 and that we support SSE/SSE2. + QT_ARCH = x64 + QT_CPU_FEATURES.x64 = sse sse2 + DEFINES += QT_COMPILER_SUPPORTS_SSE2 + CONFIG -= neon + CONFIG += sse sse2 +} diff --git a/mkspecs/features/uikit/device_destinations.sh b/mkspecs/features/uikit/device_destinations.sh new file mode 100755 index 0000000000..af7cb91e85 --- /dev/null +++ b/mkspecs/features/uikit/device_destinations.sh @@ -0,0 +1,65 @@ +#!/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$ +## +############################################################################# + +booted_simulator=$(xcrun simctl list devices | grep -E '$1' | grep -v unavailable | grep Booted | perl -lne 'print $2 if /\((.*?)\)/') +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') + 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" + fi + fi + done + echo +done 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..af047be466 --- /dev/null +++ b/mkspecs/features/uikit/qt.prf @@ -0,0 +1,33 @@ + +qt_depends = $$resolve_depends(QT, "QT.") +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..71e0982f7e --- /dev/null +++ b/mkspecs/features/uikit/qt_config.prf @@ -0,0 +1,18 @@ +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 + } + + # 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..c3ab90f45e --- /dev/null +++ b/mkspecs/features/uikit/resolve_config.prf @@ -0,0 +1,68 @@ + +xcodebuild { + # Xcode project files always support both Debug and Release configurations + # and iOS device and simulator targets, so we make sure the wrapper-makefile + # also does. + CONFIG += debug_and_release simulator_and_device +} + +load(resolve_config) + +# Legacy exclusive build configurations for backwards compatibility +CONFIG($${device.CONFIG}, $${device.CONFIG}|$${simulator.CONFIG}): \ + CONFIG += device +else: CONFIG($${simulator.CONFIG}, $${device.CONFIG}|$${simulator.CONFIG}): \ + CONFIG += simulator + +CONFIG(simulator, simulator|device): \ + CONFIG -= device $${device.CONFIG} +else: \ + CONFIG -= simulator $${simulator.CONFIG} + +macx-xcode { + # There is no way to genereate Xcode projects that are limited to either + # simulator or device builds, so simulator_and_device is always + # effectivly active, even if the user disabled it explicitly. + # The Xcode generator doesn't support multiple BUILDS though (exclusive + # builds), so we have to manually set up the simulator suffix. + library_suffix_simulator.name = "$${QMAKE_XCODE_LIBRARY_SUFFIX_SETTING}[sdk=$${simulator.sdk}*]" + library_suffix_simulator.value = "_$${simulator.sdk}$($${QMAKE_XCODE_LIBRARY_SUFFIX_SETTING})" + QMAKE_MAC_XCODE_SETTINGS += library_suffix_simulator + CONFIG *= xcode_dynamic_library_suffix +} else { + addExclusiveBuilds(simulator, device) +} + +equals(TEMPLATE, subdirs) { + # Prevent recursion into host_builds + for(subdir, SUBDIRS) { + contains($${subdir}.CONFIG, host_build) { + $${subdir}.CONFIG += no_$${simulator.target}_target no_$${device.target}_target + + # Other targets which we do want to recurse into may depend on this target, + # for example corelib depends on moc, rcc, bootstrap, etc, and other libs + # may depend on host-tools that are needed to build the lib, so we resolve + # the final target name and redirect it to the base target, so that the + # dependency chain is not broken for the other targets. + + !isEmpty($${subdir}.target) { + target = $$eval($${subdir}.target) + } else { + !isEmpty($${subdir}.file): \ + file = $$eval($${subdir}.file) + else: !isEmpty($${subdir}.subdir): \ + file = $$eval($${subdir}.subdir) + else: \ + file = $$subdir + + target = sub-$$file + } + + target ~= s,[^a-zA-Z0-9_],-, + + $${target}-$${simulator.target}.depends = $$target + $${target}-$${device.target}.depends = $$target + QMAKE_EXTRA_TARGETS += $${target}-$${simulator.target} $${target}-$${device.target} + } + } +} diff --git a/mkspecs/features/uikit/sdk.prf b/mkspecs/features/uikit/sdk.prf new file mode 100644 index 0000000000..94451ea196 --- /dev/null +++ b/mkspecs/features/uikit/sdk.prf @@ -0,0 +1,32 @@ + +# In case the user sets the SDK manually +contains(QMAKE_MAC_SDK, ^$${simulator.sdk}.*) { + contains(QT_CONFIG, 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) + +lessThan(QMAKE_MAC_SDK_VERSION, "8.0"): \ + error("Current $$QMAKE_MAC_SDK SDK version ($$QMAKE_MAC_SDK_VERSION) is too old. Please upgrade Xcode.") + +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..5cbad60804 --- /dev/null +++ b/mkspecs/features/uikit/xcodebuild.mk @@ -0,0 +1,101 @@ + +# 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 + +# 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...) + SPECDIR := $(dir $(lastword $(MAKEFILE_LIST))) + DESTINATIONS_INCLUDE = /tmp/device_destinations.mk + $(shell $(SPECDIR)/../features/uikit/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 xcrun simctl list devices | grep -E '$(EXPORT_DEVICE_FILTER)' | grep -v unavailable | perl -lne 'print $$1 if /\((.*?)\)/' | 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) -scheme $(TARGET) $(if $(SDK), -sdk $(SDK),) $(if $(CONFIGURATION), -configuration $(CONFIGURATION),) $(if $(DESTINATION), -destination $(DESTINATION) -destination-timeout 1,) + +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..6e50bbcf50 --- /dev/null +++ b/mkspecs/features/uikit/xcodebuild.prf @@ -0,0 +1,61 @@ + +# 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" +} +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/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..c26ab62f50 100644 --- a/mkspecs/features/win32/opengl.prf +++ b/mkspecs/features/win32/opengl.prf @@ -1,32 +1,23 @@ -# 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 - } +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 - 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 + } + 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 } } 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 |