summaryrefslogtreecommitdiffstats
path: root/mkspecs/features/qt_configure.prf
diff options
context:
space:
mode:
Diffstat (limited to 'mkspecs/features/qt_configure.prf')
-rw-r--r--mkspecs/features/qt_configure.prf1785
1 files changed, 1785 insertions, 0 deletions
diff --git a/mkspecs/features/qt_configure.prf b/mkspecs/features/qt_configure.prf
new file mode 100644
index 0000000000..6b500c0f3e
--- /dev/null
+++ b/mkspecs/features/qt_configure.prf
@@ -0,0 +1,1785 @@
+
+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($${currentConfig}.commandline.options.$${arg}.name): \
+ arg = $$eval($${currentConfig}.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($${currentConfig}.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($${currentConfig}.commandline.options.$${opt}.values.$${val})
+ isEmpty(mapped) {
+ # just a list of allowed values
+ for (i, $${currentConfig}.commandline.options.$${opt}.values._KEYS_) {
+ equals($${currentConfig}.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($${currentConfig}.commandline.options.$${opt}.values._KEYS_)
+ isEmpty(validValues): \
+ return(true)
+
+ for (i, validValues) {
+ equals($${currentConfig}.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($${currentConfig}.commandline.options.$${opt}.name): \
+ opt = $$eval($${currentConfig}.commandline.options.$${opt}.name)
+
+ config.input.$$opt += $$val
+ export(config.input.$$opt)
+}
+
+defineTest(qtConfParseCommandLine) {
+ customCalls =
+ for (cc, allConfigs) {
+ custom = $$eval($${cc}.commandline.custom)
+
+ !isEmpty(custom) {
+ customCall = qtConfCommandline_$$custom
+ !defined($$customCall, test): \
+ error("Custom command line callback '$$custom' is undefined.")
+ customCalls += $$customCall
+ }
+ }
+
+ 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()
+ }
+
+ didCustomCall = false
+ for (customCall, customCalls) {
+ $${customCall}($$c) {
+ didCustomCall = true
+ break()
+ }
+ }
+ $$didCustomCall: \
+ next()
+
+ contains(c, "([A-Z_]+)=(.*)") {
+ opt = $$replace(c, "^([A-Z_]+)=(.*)", "\\1")
+ val = $$replace(c, "^([A-Z_]+)=(.*)", "\\2")
+ for (cc, allConfigs) {
+ var = $$eval($${cc}.commandline.assignments.$${opt})
+ !isEmpty(var): \
+ break()
+ }
+ 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, "^--([^=]+)=(.*)") {
+ opt = $$replace(c, "^--?([^=]+)=(.*)", "\\1")
+ val = $$replace(c, "^--?([^=]+)=(.*)", "\\2")
+ } else: contains(c, "^--(.*)") {
+ opt = $$replace(c, "^--(.*)", "\\1")
+ val = yes
+ } else: contains(c, "^-(.*)") {
+ opt = $$replace(c, "^-(.*)", "\\1")
+ val =
+ for (cc, allConfigs) {
+ type = $$eval($${cc}.commandline.options.$${opt})
+ !isEmpty(type): break()
+ type = $$eval($${cc}.commandline.options.$${opt}.type)
+ !isEmpty(type): break()
+ }
+ isEmpty(type):contains(opt, "(qt|system)-.*") {
+ val = $$replace(opt, "(qt|system)-(.*)", "\\1")
+ opt = $$replace(opt, "(qt|system)-(.*)", "\\2")
+ }
+ } else {
+ qtConfAddError("Invalid command line parameter '$$c'.")
+ return()
+ }
+
+ for (cc, allConfigs) {
+ type = $$eval($${cc}.commandline.options.$${opt})
+ isEmpty(type): \
+ type = $$eval($${cc}.commandline.options.$${opt}.type)
+ isEmpty(type) {
+ # no match in the regular options, try matching the prefixes
+ for (p, $${cc}.commandline.prefix._KEYS_) {
+ e = "^-$${p}(.*)"
+ contains(c, $$e) {
+ opt = $$eval($${cc}.commandline.prefix.$${p})
+ val = $$replace(c, $$e, "\\1")
+ type = "addString"
+ break()
+ }
+ }
+ }
+ !isEmpty(type) {
+ currentConfig = $$cc
+ break()
+ }
+ }
+ # handle builtin [-no]-feature-xxx
+ isEmpty(type):contains(opt, "feature-(.*)") {
+ opt ~= s,^feature-,,
+ found = false
+ for (cc, allConfigs) {
+ contains($${cc}.features._KEYS_, $$opt) {
+ found = true
+ break()
+ }
+ }
+ !$$found {
+ qtConfAddError("Enabling/Disabling unknown feature '$$opt'.")
+ return()
+ }
+ # this is a boolean enabling/disabling the corresponding feature
+ type = boolean
+ }
+
+ 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_EXECUTABLE"
+ }
+
+ 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, $${currentConfig}.libraries._KEYS_) {
+ lpfx = $${currentConfig}.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, $${currentConfig}.commandline.assignments._KEYS_) {
+ apfx = $${currentConfig}.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}.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)
+ 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)
+ version ~= s/[^0-9.].*$//
+ $${1}.version = $$first(version)
+ 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)
+ cflags = $$eval($${1}.cflags)
+ !isEmpty(cflags): \
+ qmake_args += $$system_quote(QMAKE_CFLAGS += $$cflags) $$system_quote(QMAKE_CXXFLAGS += $$cflags)
+ return($$qmake_args)
+}
+
+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))
+
+ # Split $$cflags into stuff that goes into DEFINES, INCLUDEPATH, and other stuff.
+ defines =
+ ignored =
+ for (i, cflags) {
+ contains(i, "-I.*") {
+ i ~= s/^-I//
+ includes += $$i
+ } else: contains(i, "-D.*") {
+ i ~= s/^-D//
+ defines += $$i
+ } else {
+ # Sometimes, pkg-config files or *-config scripts include other flags
+ # we really don't need and shouldn't add (pg_config is really bad).
+ ignored += $$i
+ }
+ }
+ !isEmpty(ignored): \
+ qtConfAddNote("Dropped compiler flags '$$ignored' when detecting library '$$2'.")
+
+ 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(defines): qtConfOutputVar(assign, $$output, QMAKE_DEFINES_$$NAME, $$defines)
+ !isEmpty(includes): qtConfOutputVar(assign, $$output, QMAKE_INCDIR_$$NAME, $$includes)
+ !isEmpty($${currentConfig}.module): \
+ qtConfExtendVar($$output, "QT.$${currentModule}_private.libraries", $$2)
+}
+
+defineTest(qtConfHandleLibrary) {
+ lpfx = $${currentConfig}.libraries.$$1
+ defined($${lpfx}.result, var): return()
+
+ qtConfEnsureTestTypeDeps("library")
+ qtConfTestPrepare_compile($$lpfx)
+ use_args = $$eval($${lpfx}.literal_args)
+
+ 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_) {
+ spfx = $${lpfx}.sources.$${s}
+
+ t = $$eval($${spfx}.type)
+ call = qtConfLibrary_$$t
+ !defined($$call, test): \
+ error("Library $${1} source $${s} has unknown type '$$t'")
+
+ qtLog("Trying source $$s (type $$t) of library $${1} ...")
+
+ !$$qtConfEvaluate($$eval($${spfx}.condition)) {
+ qtLog(" => source failed condition.")
+ next()
+ }
+
+ !$${call}($$spfx) {
+ qtLog(" => source produced no result.")
+ next()
+ }
+
+ # if the library defines a test, use it to verify the source.
+ !isEmpty($${lpfx}.test) {
+ $${lpfx}.literal_args = $$use_args $$qtConfLibraryArgs($$spfx)
+ $${lpfx}.host = $$eval($${spfx}.host)
+ !qtConfTest_compile($$lpfx) {
+ qtLog(" => source failed verification.")
+ next()
+ }
+ }
+
+ qtLog(" => source accepted.")
+
+ $${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($${currentConfig}.libraries._KEYS_, $$u): \
+ error("Test $$1 tries to use undeclared library '$$u'")
+ qtConfHandleLibrary($$u)
+ lpfx = $${currentConfig}.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) {
+ label = $$eval($${1}.label)
+ isEmpty(label): return()
+
+ msg = "Checking for $${label}... "
+ log($$msg)
+ $$QMAKE_CONFIG_VERBOSE: log("$$escape_expand(\\n)")
+ write_file($$QMAKE_CONFIG_LOG, msg, append)
+}
+
+defineTest(qtLogTestResult) {
+ isEmpty($${1}.label): 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, $${currentConfig}.testTypeDependencies._KEYS_) {
+ !defined(qtConfTest_$${tt}, test): \
+ error("Declaring dependency for undefined test type '$$tt'.")
+ for (f, $${currentConfig}.testTypeDependencies.$${tt}._KEYS_) {
+ feature = $$eval($${currentConfig}.testTypeDependencies.$${tt}.$${f})
+ isEmpty($${currentConfig}.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, $${currentConfig}.testTypeAliases._KEYS_) {
+ !defined(qtConfTest_$${tt}, test): \
+ error("Aliasing undefined test type '$$tt'.")
+ for (tta, $${currentConfig}.testTypeAliases.$${tt}._KEYS_) {
+ type = $$eval($${currentConfig}.testTypeAliases.$${tt}.$${tta})
+ !defined(qtConfTest_$${type}, test): \
+ error("Aliasing '$$tt' to undefined test type '$$type'.")
+ $${currentConfig}.testTypeForwards.$${type} += $$tt
+ export($${currentConfig}.testTypeForwards.$${type})
+ }
+ }
+}
+
+defineTest(qtConfEnsureTestTypeDeps) {
+ depsn = $${currentConfig}.testTypeDependencies.$${1}._KEYS_
+ !isEmpty($$depsn) {
+ for (dep, $$depsn) {
+ feature = $$eval($${currentConfig}.testTypeDependencies.$${1}.$${dep})
+ !qtConfCheckFeature($$feature): \
+ error("Test type '$$1' depends on non-emitted feature $${feature}.")
+ }
+ $$depsn =
+ export($$depsn)
+ }
+ fwdsn = $${currentConfig}.testTypeForwards.$${1}
+ !isEmpty($$fwdsn) {
+ for (fwd, $$fwdsn): \
+ qtConfEnsureTestTypeDeps($$fwd)
+ $$fwdsn =
+ export($$fwdsn)
+ }
+}
+
+defineTest(qtRunSingleTest) {
+ tpfx = $${currentConfig}.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($${currentConfig}.tests._KEYS_, $$test): \
+ error("Unknown test object $${test} in expression '$${1}'.")
+ qtRunSingleTest($$test)
+ result = $$eval($${currentConfig}.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($${currentConfig}.libraries._KEYS_, $$lib): \
+ error("Unknown library object $${lib} in expression '$${1}'.")
+ qtConfHandleLibrary($$lib)
+ !defined($${currentConfig}.libraries.$${lib}.$${var}, var): \
+ var = sources.$$eval($${currentConfig}.libraries.$${lib}.source).$$var
+ result = $$eval($${currentConfig}.libraries.$${lib}.$${var})
+ } else: contains(e, "^features\..*") {
+ feature = $$section(e, ".", 1, 1)
+ var = $$section(e, ".", 2, -1)
+ isEmpty(var): \
+ var = available
+ !contains($${currentConfig}.features._KEYS_, $$feature) {
+ # this is basically a copy of what qtConfig() in qt_build_config.prf
+ # does, but we produce a nicer error message.
+ for (module, QMAKE_CONFIG_DEPS) {
+ contains(QT.$${module}.enabled_features, $$feature): \
+ result = true
+ else: contains(QT.$${module}.disabled_features, $$feature): \
+ result = false
+ else: \
+ next()
+ !equals(var, available): \
+ error("Expression '$$1' is accessing field '$$var' of non-local feature $${feature}.")
+ return($$result)
+ }
+ error("Unknown feature object $${feature} in expression '$${1}'.")
+ }
+ !qtConfCheckFeature($$feature): \
+ error("Expression '$$1' is accessing non-emitted feature $${feature}.")
+ result = $$eval($${currentConfig}.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($${currentConfig}.features.$${1}.enable)
+ !isEmpty(enable) {
+ $$qtConfEvaluate($$enable): \
+ return(true)
+ } else {
+ equals(config.input.$${1}, "yes"): \
+ return(true)
+ }
+
+ return(false)
+}
+
+defineReplace(qtIsFeatureDisabled) {
+ disable = $$eval($${currentConfig}.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 = $${currentConfig}.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, $${currentConfig}.features.$${feature}.output._KEYS_): \
+ qtConfProcessOneOutput($$feature, $$i)
+
+ return(true)
+}
+
+
+defineTest(qtConfProcessFeatures) {
+ for (feature, $${currentConfig}.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($${currentConfig}.features.$${feature}.available): \
+ l += $$eval($${currentConfig}.features.$${feature}.label)
+ }
+
+ isEmpty(l): return("<none>")
+ return($$join(l, ' '))
+}
+
+defineTest(qtConfReport_featureList) {
+ qtConfReportPadded($${1}, $$qtConfCollectFeatures($${2}))
+}
+
+defineReplace(qtConfFindFirstAvailableFeature) {
+ for (feature, $$list($${1})) {
+ isEmpty($${currentConfig}.features.$${feature}._KEYS_): \
+ error("Asking for a report on undefined feature $${2}.")
+ $$eval($${currentConfig}.features.$${feature}.available): \
+ return($$eval($${currentConfig}.features.$${feature}.label))
+ }
+
+ return("<none>")
+}
+
+defineTest(qtConfReport_firstAvailableFeature) {
+ qtConfReportPadded($${1}, $$qtConfFindFirstAvailableFeature($${2}))
+}
+
+defineTest(qtConfReport_feature) {
+ !contains($${currentConfig}.features._KEYS_, $$2): \
+ error("Asking for a report on undefined feature $${2}.")
+
+ # hide report for not emitted features
+ isEmpty($${currentConfig}.features.$${2}.available): \
+ return()
+
+ $$eval($${currentConfig}.features.$${2}.available) {
+ result = "yes"
+ !isEmpty(3): result = "$${3}"
+ } else {
+ result = "no"
+ !isEmpty(4): result = "$${4}"
+ }
+
+ text = $$eval($${currentConfig}.features.$${2}.label)
+
+ 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($${currentConfig}.earlyReport, false)
+}
+
+defineTest(qtConfCreateReport) {
+ qtConfCreateReportRecurse($${currentConfig}.report, false)
+}
+
+defineTest(qtConfCreateSummary) {
+ qtConfCreateReportRecurse($${currentConfig}.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
+#
+
+# qtConfOutputVar(modifier, output, name, value)
+defineTest(qtConfOutputVar) {
+ modifier = $$1
+ output = $$2
+ name = $$3
+ value = $$val_escape(4)
+
+ defined($${currentConfig}.output.$${output}.assign.$${name}, var): \
+ error("Trying to overwrite assigned variable '$$name' in '$$output' using modifier '$$modifier'.")
+
+ equals(modifier, assign) {
+ !isEmpty($${currentConfig}.output.$${output}.append.$${name})|!isEmpty($${currentConfig}.output.$${output}.remove.$${name}): \
+ error("Trying to assign variable '$$name' in '$$output', which has already appended or removed parts.")
+ $${currentConfig}.output.$${output}.assign.$${name} = $$value
+ } else: equals(modifier, append) {
+ contains($${currentConfig}.output.$${output}.remove.$${name}, $$value): \
+ error("Trying to append removed '$$value' to variable '$$name' in '$$output'.")
+ $${currentConfig}.output.$${output}.append.$${name} += $$value
+ } else: equals(modifier, remove) {
+ contains($${currentConfig}.output.$${output}.append.$${name}, $$value): \
+ error("Trying to remove appended '$$value' to variable '$$name' in '$$output'.")
+ $${currentConfig}.output.$${output}.remove.$${name} += $$value
+ } else {
+ error("Invalid modifier '$$modifier' passed to qtConfOutputVar.")
+ }
+ $${currentConfig}.output.$${output}.$${modifier}._KEYS_ *= $${name}
+ export($${currentConfig}.output.$${output}.$${modifier}.$${name})
+ export($${currentConfig}.output.$${output}.$${modifier}._KEYS_)
+}
+
+# qtConfExtendVar(output, name, value)
+defineTest(qtConfExtendVar) {
+ output = $$1
+ name = $$2
+ value = $$val_escape(3)
+
+ !defined($${currentConfig}.output.$${output}.assign.$${name}, var): \
+ error("Trying to extend undefined variable '$$name' in '$$output'.")
+
+ $${currentConfig}.output.$${output}.assign.$${name} += $$value
+ export($${currentConfig}.output.$${output}.assign.$${name})
+}
+
+defineTest(qtConfOutputVarHelper) {
+ !isEmpty($${2}.public):$$eval($${2}.public) {
+ output = "publicPro"
+ } else {
+ output = "privatePro"
+ }
+
+ negative = $$eval($${2}.negative)
+ isEmpty(negative): negative = false
+ equals(3, $$negative): return()
+
+ 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)
+ equals(output, "publicPro"):!isEmpty($${currentConfig}.module): \
+ qtConfExtendVar($$output, "QT.$${currentModule}.exports", $$name)
+}
+
+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
+ modular = $$5
+
+ 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
+ }
+
+ isEmpty($${currentConfig}.module)|!$$modular: \
+ qtConfOutputVar(append, $$pro, $$var, $$val)
+ else: \
+ qtConfExtendVar($$pro, "QT.$${currentModule}.$$var", $$val)
+}
+
+defineTest(qtConfOutput_publicQtConfig) {
+ qtConfOutputConfigVar($$1, $$2, "publicPro", "QT_CONFIG", true)
+}
+
+defineTest(qtConfOutput_publicConfig) {
+ !isEmpty($${currentConfig}.module): \
+ error("Cannot use output type 'publicConfig' in module-local feature '$$eval($${1}.feature)'.")
+ qtConfOutputConfigVar($$1, $$2, "publicPro", "CONFIG", false)
+}
+
+defineTest(qtConfOutput_privateConfig) {
+ qtConfOutputConfigVar($$1, $$2, "privatePro", "CONFIG", false)
+}
+
+defineTest(qtConfOutputSetDefine) {
+ $${currentConfig}.output.$${1}.$${2} = $${3}
+ $${currentConfig}.output.$${1}._KEYS_ *= $${2}
+ export($${currentConfig}.output.$${1}.$${2})
+ export($${currentConfig}.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} {
+ isEmpty($${currentConfig}.module): \
+ qtConfOutputVar(append, "publicPro", "QT_CONFIG", $$name)
+ else: \
+ qtConfExtendVar("publicPro", "QT.$${currentModule}.QT_CONFIG", $$name)
+ } else {
+ f = $$upper($$replace(name, -, _))
+ qtConfOutputSetDefine("publicHeader", "QT_NO_$$f")
+ }
+}
+
+defineTest(qtConfSetModuleName) {
+ currentModule = $$eval($${currentConfig}.module)
+ isEmpty(currentModule): \
+ currentModule = global
+ export(currentModule)
+}
+
+defineTest(qtConfSetupModuleOutputs) {
+ qtConfOutputVar(assign, "publicPro", "QT.$${currentModule}.enabled_features", )
+ qtConfOutputVar(assign, "publicPro", "QT.$${currentModule}.disabled_features", )
+ qtConfOutputVar(assign, "privatePro", "QT.$${currentModule}_private.enabled_features", )
+ qtConfOutputVar(assign, "privatePro", "QT.$${currentModule}_private.disabled_features", )
+ !isEmpty($${currentConfig}.module) {
+ qtConfOutputVar(assign, "publicPro", "QT.$${currentModule}.QT_CONFIG", )
+ qtConfOutputVar(assign, "publicPro", "QT.$${currentModule}.exports", )
+ qtConfOutputVar(assign, "privatePro", "QT.$${currentModule}_private.libraries", )
+ }
+}
+
+defineTest(qtConfOutput_publicFeature) {
+ name = "$$eval($${1}.name)"
+ isEmpty(name): \
+ name = $$eval($${1}.feature)
+ feature = $$replace(name, [-+.], _)
+
+ $${2} {
+ qtConfExtendVar("publicPro", "QT.$${currentModule}.enabled_features", $$name)
+ QT.$${currentModule}.enabled_features += $$name
+ export(QT.$${currentModule}.enabled_features)
+ qtConfOutputSetDefine("publicHeader", "QT_FEATURE_$$feature", 1)
+ } else {
+ qtConfExtendVar("publicPro", "QT.$${currentModule}.disabled_features", $$name)
+ QT.$${currentModule}.disabled_features += $$name
+ export(QT.$${currentModule}.disabled_features)
+ 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} {
+ qtConfExtendVar("privatePro", "QT.$${currentModule}_private.enabled_features", $$name)
+ QT.$${currentModule}_private.enabled_features += $$name
+ export(QT.$${currentModule}_private.enabled_features)
+ qtConfOutputSetDefine("privateHeader", "QT_FEATURE_$$feature", 1)
+ } else {
+ qtConfExtendVar("privatePro", "QT.$${currentModule}_private.disabled_features", $$name)
+ QT.$${currentModule}_private.disabled_features += $$name
+ export(QT.$${currentModule}_private.disabled_features)
+ qtConfOutputSetDefine("privateHeader", "QT_FEATURE_$$feature", -1)
+ }
+}
+
+defineTest(qtConfProcessOneOutput) {
+ feature = $${1}
+ fpfx = $${currentConfig}.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) {
+ !contains($${currentConfig}._KEYS_, "features"): \
+ return()
+
+ basedir = $$shadowed($$eval($${currentConfig}.dir))
+ module = $$eval($${currentConfig}.module)
+
+ # write it to the output files
+ isEmpty($${currentConfig}.files._KEYS_) {
+ # set defaults that should work for most Qt modules
+ isEmpty(module): \
+ error("Neither module nor files section specified in configuration file.")
+
+ $${currentConfig}.files._KEYS_ = publicPro privatePro publicHeader privateHeader
+ $${currentConfig}.files.publicPro = qt$${module}-config.pri
+ $${currentConfig}.files.privatePro = qt$${module}-config.pri # sic!
+ $${currentConfig}.files.publicHeader = qt$${module}-config.h
+ $${currentConfig}.files.privateHeader = qt$${module}-config_p.h
+ }
+
+ for (type, $${currentConfig}.files._KEYS_) {
+ contains(type, ".*Pro") {
+ for (k, $${currentConfig}.output.$${type}.assign._KEYS_): \
+ $${currentConfig}.output.$$type += "$$k = $$eval($${currentConfig}.output.$${type}.assign.$$k)"
+ for (k, $${currentConfig}.output.$${type}.remove._KEYS_): \
+ $${currentConfig}.output.$$type += "$$k -= $$eval($${currentConfig}.output.$${type}.remove.$$k)"
+ for (k, $${currentConfig}.output.$${type}.append._KEYS_): \
+ $${currentConfig}.output.$$type += "$$k += $$eval($${currentConfig}.output.$${type}.append.$$k)"
+ } else {
+ for (define, $${currentConfig}.output.$${type}._KEYS_) {
+ value = $$eval($${currentConfig}.output.$${type}.$${define})
+ $${currentConfig}.output.$$type += "$${LITERAL_HASH}define $$define $$value"
+ }
+ }
+
+ ppScope =
+ !isEmpty(module): ppScope = $${module}_
+ defined(qtConfOutputPostProcess_$${ppScope}$${type}, test): \
+ qtConfOutputPostProcess_$${ppScope}$${type}()
+
+ file = $$eval($${currentConfig}.files.$${type})
+ fileCont.$$file += $$eval($${currentConfig}.output.$${type})
+ fileCont._KEYS_ *= $$file
+ }
+
+ for (file, fileCont._KEYS_): \
+ write_file($$basedir/$$file, fileCont.$$file)|error()
+}
+
+#
+# tie it all together
+#
+
+cfgs =
+isEmpty(_QMAKE_SUPER_CACHE_)|equals(OUT_PWD, $$dirname(_QMAKE_SUPER_CACHE_)) {
+ c = $$basename(_PRO_FILE_PWD_)
+ config.$${c}.dir = $$_PRO_FILE_PWD_
+ cfgs += $$c
+ !isEmpty(_QMAKE_SUPER_CACHE_) {
+ for (s, SUBDIRS) {
+ config.$${s}.dir = $$_PRO_FILE_PWD_/$${s}
+ cfgs += $$s
+ }
+ }
+}
+configsToProcess =
+for (c, cfgs) {
+ s = $$eval(config.$${c}.dir)
+ exists($$s/configure.json): \
+ configsToProcess += $$c
+}
+isEmpty(configsToProcess): \
+ return()
+
+load(configure_base)
+
+QMAKE_POST_CONFIGURE =
+allConfigs =
+for(ever) {
+ isEmpty(configsToProcess): \
+ break()
+
+ currentConfig = config.$$take_first(configsToProcess)
+ thisDir = $$eval($${currentConfig}.dir)
+ jsonFile = $$thisDir/configure.json
+ priFile = $$thisDir/configure.pri
+
+ allConfigs += $$currentConfig
+
+ # load configuration data
+ configure_data = $$cat($$jsonFile, blob)
+ !parseJson(configure_data, $$currentConfig): \
+ error("Invalid or non-existent file $${jsonFile}.")
+ exists($$priFile): \
+ !include($$priFile): error()
+
+ # prepend all subconfigs to files to keep a depth first search order
+ subconfigs =
+ for(n, $${currentConfig}.subconfigs._KEYS_) {
+ subconfig = $$eval($${currentConfig}.subconfigs.$${n})
+ name = $$basename(subconfig)
+ config.$${name}.dir = $$thisDir/$$subconfig
+ subconfigs += $$name
+ }
+ configsToProcess = $$subconfigs $$configsToProcess
+}
+
+for (currentConfig, allConfigs): \
+ qtConfSetupLibraries()
+
+!isEmpty(_QMAKE_SUPER_CACHE_): \
+ QMAKE_CONFIG_CACHE = $$dirname(_QMAKE_SUPER_CACHE_)/config.cache
+else: \
+ QMAKE_CONFIG_CACHE = $$dirname(_QMAKE_CACHE_)/config.cache
+QMAKE_CONFIG_CACHE_USE = all
+
+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)
+}
+
+for (currentConfig, allConfigs) {
+ qtConfSetModuleName()
+ qtConfSetupModuleOutputs()
+ # do early checks, mainly to validate the command line
+ qtConfProcessEarlyChecks()
+}
+qtConfCheckErrors()
+
+CONFIG += qt_conf_tests_allowed
+logn()
+logn("Running configuration tests...")
+
+for (currentConfig, allConfigs) {
+ tdir = $$eval($${currentConfig}.testDir)
+ isEmpty(tdir): tdir = config.tests
+ QMAKE_CONFIG_TESTS_DIR = $$absolute_path($$tdir, $$eval($${currentConfig}.dir))
+
+ qtConfSetModuleName()
+
+ qtConfSetupTestTypeDeps()
+
+ # correctly setup dependencies
+ QMAKE_CONFIG_DEPS = global global_private
+ !isEmpty($${currentConfig}.module) {
+ for (d, $${currentConfig}.depends._KEYS_) {
+ dep = $$replace($${currentConfig}.depends.$$d, -private$, _private)
+ dep *= $$replace(dep, _private$, )
+ QMAKE_CONFIG_DEPS += $$dep
+ }
+ }
+
+ # process all features
+ qtConfProcessFeatures()
+
+ # generate files and reports
+ qtConfProcessOutput()
+ qtConfCreateReport()
+ qtConfCreateSummary()
+}
+
+# these come from the pri files loaded above.
+for (p, QMAKE_POST_CONFIGURE): \
+ eval($$p)
+
+logn("Done running configuration tests.")
+logn()
+
+logn("Configure summary:")
+logn()
+qtConfPrintReport()