diff options
Diffstat (limited to 'mkspecs/features/qt_configure.prf')
-rw-r--r-- | mkspecs/features/qt_configure.prf | 1779 |
1 files changed, 1779 insertions, 0 deletions
diff --git a/mkspecs/features/qt_configure.prf b/mkspecs/features/qt_configure.prf new file mode 100644 index 0000000000..d5b9c6dc11 --- /dev/null +++ b/mkspecs/features/qt_configure.prf @@ -0,0 +1,1779 @@ + +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}, "looking for library $${1}") + + 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() + + log("Checking for $${label}... ") + $$QMAKE_CONFIG_VERBOSE: log("$$escape_expand(\\n)") + write_file($$QMAKE_CONFIG_LOG, 2, 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}, "executing config test $${1}") + + 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, $${fpfx}.output._KEYS_): \ + qtConfProcessOneOutput($${1}, $$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) + } +} + +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) +} +!equals(QMAKE_CONFIG_CACHE_USE, all): \ + write_file($$QMAKE_CONFIG_LOG, "") + +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() |