qt5base-lts/mkspecs/features/qt_configure.prf
Lars Knoll 56ee007b3f Rework privateFeature
privateFeature would add QT_NO_FEATURE to the DEFINES in the private
.pri file, which was somewhat inelegant.
Additionally, it would add the feature to the _public_ QT_CONFIG
variable, which was plain wrong.

Replace the implementation with the one just introduced for
publicFeature, with the difference that the features are written to the
private files instead.

As this entirely disposes of the old system, all usages in the project
files need to be replaced atomically as well.

Change-Id: I506b5d41054410659ea503bc6901736cd5edec6e
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
2016-08-19 04:27:29 +00:00

1526 lines
46 KiB
Plaintext

CONFIG -= qt debug_and_release
load(configure_base)
QT_CONFIGURE_REPORT =
QT_CONFIGURE_NOTES =
QT_CONFIGURE_WARNINGS =
QT_CONFIGURE_ERRORS =
defineTest(qtConfAddReport) {
QT_CONFIGURE_REPORT += "$$join(1, $$escape_expand(\\n))"
export(QT_CONFIGURE_REPORT)
}
defineTest(qtConfAddNote) {
QT_CONFIGURE_NOTES += "Note: $$join(1, $$escape_expand(\\n))"
export(QT_CONFIGURE_NOTES)
}
defineTest(qtConfAddWarning) {
QT_CONFIGURE_WARNINGS += "WARNING: $$join(1, $$escape_expand(\\n))"
export(QT_CONFIGURE_WARNINGS)
}
defineTest(qtConfAddError) {
QT_CONFIGURE_ERRORS += "ERROR: $$join(1, $$escape_expand(\\n))"
export(QT_CONFIGURE_ERRORS)
equals(2, log) {
CONFIG += mention_config_log
export(CONFIG)
}
}
defineTest(qtConfCommandlineSetInput) {
arg = $${1}
val = $${2}
!isEmpty(config.commandline.options.$${arg}.name): \
arg = $$eval(config.commandline.options.$${arg}.name)
config.input.$$arg = $$val
export(config.input.$$arg)
}
defineReplace(qtConfGetNextCommandlineArg) {
c = $$take_first(QMAKE_EXTRA_ARGS)
export(QMAKE_EXTRA_ARGS)
return($$c)
}
defineReplace(qtConfPeekNextCommandlineArg) {
return($$first(QMAKE_EXTRA_ARGS))
}
defineTest(qtConfCommandline_boolean) {
opt = $${1}
val = $${2}
isEmpty(val): val = yes
!equals(val, yes):!equals(val, no) {
qtConfAddError("Invalid value given for boolean command line option '$$opt'.")
return()
}
qtConfCommandlineSetInput($$opt, $$val)
}
defineTest(qtConfCommandline_void) {
opt = $${1}
val = $${2}
!isEmpty(val) {
qtConfAddError("Command line option '$$opt' expects no argument ('$$val' given).")
return()
}
val = $$eval(config.commandline.options.$${opt}.value)
isEmpty(val): val = yes
qtConfCommandlineSetInput($$opt, $$val)
}
defineTest(qtConfCommandline_enum) {
opt = $${1}
val = $${2}
isEmpty(val): val = yes
# validate and map value
mapped = $$eval(config.commandline.options.$${opt}.values.$${val})
isEmpty(mapped) {
# just a list of allowed values
for (i, config.commandline.options.$${opt}.values._KEYS_) {
equals(config.commandline.options.$${opt}.values.$${i}, $$val) {
mapped = $$val
break()
}
}
}
isEmpty(mapped) {
qtConfAddError("Invalid value '$$val' supplied to command line option '$$opt'.")
return()
}
qtConfCommandlineSetInput($$opt, $$mapped)
}
defineTest(qtConfValidateValue) {
opt = $${1}
val = $${2}
validValues = $$eval(config.commandline.options.$${opt}.values._KEYS_)
isEmpty(validValues): \
return(true)
for (i, validValues) {
equals(config.commandline.options.$${opt}.values.$${i}, $$val): \
return(true)
}
qtConfAddError("Invalid value '$$val' supplied to command line option '$$opt'.")
return(false)
}
defineTest(qtConfCommandline_string) {
opt = $${1}
val = $${2}
isEmpty(val): val = $$qtConfGetNextCommandlineArg()
# Note: Arguments which are variable assignments are legit here.
contains(val, "^-.*")|isEmpty(val) {
qtConfAddError("No value supplied to command line option '$$opt'.")
return()
}
!qtConfValidateValue($$opt, $$val): \
return()
qtConfCommandlineSetInput($$opt, $$val)
}
defineTest(qtConfCommandline_optionalString) {
opt = $${1}
val = $${2}
isEmpty(val) {
v = $$qtConfPeekNextCommandlineArg()
contains(v, "^-.*|[A-Z_]+=.*")|isEmpty(v): \
val = "yes"
else: \
val = $$qtConfGetNextCommandlineArg()
}
!qtConfValidateValue($$opt, $$val): \
return()
qtConfCommandlineSetInput($$opt, $$val)
}
defineTest(qtConfCommandline_addString) {
opt = $${1}
val = $${2}
isEmpty(val): val = $$qtConfGetNextCommandlineArg()
contains(val, "^-.*|[A-Z_]+=.*")|isEmpty(val) {
qtConfAddError("No value supplied to command line option '$$opt'.")
return()
}
!qtConfValidateValue($$opt, $$val): \
return()
!isEmpty(config.commandline.options.$${opt}.name): \
opt = $$eval(config.commandline.options.$${opt}.name)
config.input.$$opt += $$val
export(config.input.$$opt)
}
defineTest(qtConfParseCommandLine) {
custom = $$config.commandline.custom
customCall =
!isEmpty(custom) {
customCall = qtConfCommandline_$$custom
!defined($$customCall, test): \
error("Custom command line callback '$$custom' is undefined.")
}
for (ever) {
c = $$qtConfGetNextCommandlineArg()
isEmpty(c): break()
# handle options to turn on verbose logging
contains(c, "^-v")|contains(c, "^--?verbose") {
QMAKE_CONFIG_VERBOSE = true
export(QMAKE_CONFIG_VERBOSE)
next()
}
contains(c, "^-no-v")|contains(c, "^--?no-verbose") {
QMAKE_CONFIG_VERBOSE = false
export(QMAKE_CONFIG_VERBOSE)
next()
}
!isEmpty(customCall) {
$${customCall}($$c): \
next()
}
contains(c, "([A-Z_]+)=(.*)") {
opt = $$replace(c, "^([A-Z_]+)=(.*)", "\\1")
val = $$replace(c, "^([A-Z_]+)=(.*)", "\\2")
var = $$eval(config.commandline.assignments.$${opt})
isEmpty(var) {
qtConfAddError("Assigning unknown variable '$$opt' on command line.")
return()
}
config.input.$$var = $$val
export(config.input.$$var)
next()
}
# parse out opt and val
contains(c, "^--?enable-(.*)") {
opt = $$replace(c, "^--?enable-(.*)", "\\1")
val = yes
} else: contains(c, "^--?(disable|no)-(.*)") {
opt = $$replace(c, "^--?(disable|no)-(.*)", "\\2")
val = no
} else: contains(c, "^--?qt-(.*)") {
opt = $$replace(c, "^--?qt-(.*)", "\\1")
val = qt
} else: contains(c, "^--?system-(.*)") {
opt = $$replace(c, "^--?system-(.*)", "\\1")
val = system
} else: contains(c, "^--?([^-].*)=(.*)") {
opt = $$replace(c, "^--?([^-].*)=(.*)", "\\1")
val = $$replace(c, "^--?([^-].*)=(.*)", "\\2")
} else: contains(c, "^--?([^-].*)") {
opt = $$replace(c, "^--?([^-].*)", "\\1")
val =
} else {
qtConfAddError("Invalid command line parameter '$$c'.")
return()
}
type = $$eval(config.commandline.options.$${opt})
isEmpty(type): \
type = $$eval(config.commandline.options.$${opt}.type)
isEmpty(type) {
# no match in the regular options, try matching the prefixes
for (p, config.commandline.prefix._KEYS_) {
e = "^-$${p}(.*)"
contains(c, $$e) {
opt = $$eval(config.commandline.prefix.$${p})
val = $$replace(c, $$e, "\\1")
type = "addString"
break()
}
}
}
# handle builtin [-no]-feature-xxx
isEmpty(type):contains(opt, "feature-(.*)") {
# simply skip for now
next()
}
isEmpty(type) {
qtConfAddError("Unknown command line option '$$c'.")
return()
}
call = "qtConfCommandline_$${type}"
!defined($$call, test): \
error("Command line option '$$c' has unknown type '$$type'.")
# now that we have opt and value, process it
$${call}($$opt, $$val)
}
}
defineReplace(qtConfToolchainSupportsFlag) {
test_out_dir = $$shadowed($$QMAKE_CONFIG_TESTS_DIR)
test_cmd_base = "cd $$system_quote($$system_path($$test_out_dir)) &&"
conftest = "int main() { return 0; }"
write_file("$$test_out_dir/conftest.cpp", conftest)|error()
qtRunLoggedCommand("$$test_cmd_base $$QMAKE_CXX $${1} -o conftest-out conftest.cpp"): \
return(true)
return(false)
}
defineTest(qtConfTest_compilerSupportsFlag) {
flag = $$eval($${1}.flag)
return($$qtConfToolchainSupportsFlag($$flag))
}
defineTest(qtConfTest_linkerSupportsFlag) {
flag = $$eval($${1}.flag)
use_gold_linker: \
LFLAGS = -fuse-ld=gold
return($$qtConfToolchainSupportsFlag($$LFLAGS "-Wl,$$flag"))
}
defineReplace(qtConfFindInPathList) {
for (dir, 2) {
exists("$$dir/$${1}"): \
return("$$dir/$${1}")
}
return()
}
defineReplace(qtConfFindInPath) {
ensurePathEnv()
return($$qtConfFindInPathList($$1, $$2 $$QMAKE_PATH_ENV))
}
defineReplace(qtConfPkgConfigEnv) {
env =
!isEmpty(PKG_CONFIG_SYSROOT_DIR): env = "$${SETENV_PFX}PKG_CONFIG_SYSROOT_DIR=$${PKG_CONFIG_SYSROOT_DIR}$${SETENV_SFX} "
!isEmpty(PKG_CONFIG_LIBDIR): env = "$$env$${SETENV_PFX}PKG_CONFIG_LIBDIR=$${PKG_CONFIG_LIBDIR}$${SETENV_SFX} "
return($$env)
}
defineReplace(qtConfPkgConfig) {
host = $$1
isEmpty(host): host = false
$$host {
pkg_config = $$qtConfFindInPath("pkg-config")
} else {
pkg_config = "$$qtConfPkgConfigEnv()$$PKG_CONFIG"
}
return($$pkg_config)
}
defineTest(qtConfPkgConfigPackageExists) {
isEmpty(1)|isEmpty(2): \
return(false)
!qtRunLoggedCommand("$${1} --exists --silence-errors $${2}"): \
return(false)
return(true)
}
defineReplace(qtConfPrepareArgs) {
arglist = $$split(1)
args =
for (a, arglist): \
args += $$system_quote($$a)
return($$args)
}
defineTest(qtConfSetupLibraries) {
for (l, config.libraries._KEYS_) {
lpfx = config.libraries.$${l}
# 'export' may be omitted, in which case it falls back to the library's name
!defined($${lpfx}.export, var) {
$${lpfx}.export = $$l
export($${lpfx}.export)
}
isEmpty($${lpfx}.sources._KEYS_): \
error("Library $$l defines no sources")
for (s, $${lpfx}.sources._KEYS_) {
spfx = $${lpfx}.sources.$${s}
# link back to parent object
$${spfx}.library = $$l
export($${spfx}.library)
# a plain string is transformed into a structure
isEmpty($${spfx}._KEYS_) {
$${spfx}.libs = $$eval($${spfx})
export($${spfx}.libs)
}
# if the type is missing (implicitly in the case of plain strings), assume 'inline'
isEmpty($${spfx}.type) {
$${spfx}.type = inline
export($${spfx}.type)
}
}
}
# reverse mapping for assignments on command line.
for (a, config.commandline.assignments._KEYS_) {
apfx = config.commandline.assignments.$${a}
ra = config.commandline.rev_assignments.$$eval($$apfx)
$$ra = $$a
export($$ra)
}
}
# the library is specified inline in a 'libs' field.
# overrides from the command line are accepted.
defineTest(qtConfLibrary_inline) {
lib = $$eval($${1}.library)
!defined($${1}.libs, var): \
error("'inline' source in library '$$lib' does not specify 'libs'.")
# direct libs. overwrites inline libs.
defined(config.input.$${lib}.libs, var) {
$${1}.libs = $$eval(config.input.$${lib}.libs)
export($${1}.libs)
}
# build-specific direct libs. overwrites inline libs.
vars =
any = false
all = true
for (b, $${1}.builds._KEYS_) {
iv = $${lib}.libs.$${b}
vars += $$eval(config.commandline.rev_assignments.$${iv})
defined(config.input.$${iv}, var) {
$${1}.builds.$${b}.libs = $$eval(config.input.$${iv})
export($${1}.builds.$${b}.libs)
any = true
} else {
all = false
}
}
$$any:!$$all {
qtConfAddError("Either none or all of $$join(vars, ", ", [, ]) must be specified.")
return(false)
}
# prefix. prepends to (possibly overwritten) inline libs.
prefix = $$val_escape(config.input.$${lib}.prefix)
!isEmpty(prefix) {
$${1}.includedir = $$prefix/include
export($${1}.includedir)
$${1}.cflags = -I$$prefix/include
export($${1}.cflags)
$${1}.libs = "-L$$prefix/lib $$eval($${1}.libs)"
export($${1}.libs)
}
return(true)
}
# the library is provided by the qmake spec.
# this source type cannot fail.
defineTest(qtConfLibrary_makeSpec) {
spec = $$eval($${1}.spec)
isEmpty(spec): \
error("makeSpec source in library '$$eval($${1}.library)' does not specify 'spec'.")
$${1}.includedir = "$$val_escape(QMAKE_INCDIR_$$spec)"
export($${1}.includedir)
$${1}.cflags = "$$val_escape(QMAKE_CFLAGS_$$spec)"
export($${1}.cflags)
libs =
for (l, QMAKE_LIBDIR_$$spec): \
libs += -L$$l
libs += $$eval(QMAKE_LIBS_$$spec)
$${1}.libs = "$$val_escape(libs)"
export($${1}.libs)
# the library definition is always in scope, so no point in exporting it.
$${1}.export = false
export($${1}.export)
return(true)
}
# the library is found via pkg-config.
defineTest(qtConfLibrary_pkgConfig) {
pkg_config = $$qtConfPkgConfig($$eval($${1}.host))
isEmpty(pkg_config): \
return(false)
args = $$qtConfPrepareArgs($$eval($${1}.args))
!qtConfPkgConfigPackageExists($$pkg_config, $$args): \
return(false)
qtRunLoggedCommand("$$pkg_config --modversion $$args", version)|return(false)
qtRunLoggedCommand("$$pkg_config --libs $$args", $${1}.libs)|return(false)
qtRunLoggedCommand("$$pkg_config --cflags $$args", $${1}.cflags)|return(false)
qtRunLoggedCommand("$$pkg_config --cflags-only-I $$args", includes)|return(false)
eval(includes = $$includes)
includes ~= s/^-I//g
$${1}.includedir = "$$val_escape(includes)"
$${1}.version = $$first(version)
export($${1}.libs)
export($${1}.cflags)
export($${1}.includedir)
export($${1}.version)
return(true)
}
defineTest(qtConfTest_getPkgConfigVariable) {
pkg_config = $$qtConfPkgConfig($$eval($${1}.host))
isEmpty(pkg_config): \
return(false)
args = $$qtConfPrepareArgs($$eval($${1}.pkg-config-args))
!qtConfPkgConfigPackageExists($$pkg_config, $$args): \
return(false)
variable = $$eval($${1}.pkg-config-variable)
qtRunLoggedCommand("$$pkg_config --variable=$$variable $$args", $${1}.value)|return(false)
export($${1}.value)
return(true)
}
defineReplace(qtConfLibraryArgs) {
qmake_args =
libs = $$eval($${1}.libs)
!isEmpty(libs): \
qmake_args += $$system_quote(LIBS += $$libs)
for (b, $${1}.builds._KEYS_): \
qmake_args += $$system_quote(LIBS_$$upper($$b) += $$eval($${1}.builds.$${b}))
includedir = $$eval($${1}.includedir)
!isEmpty(includedir): \
qmake_args += $$system_quote(INCLUDEPATH *= $$includedir)
return($$qmake_args)
}
defineReplace(qtConfGetHexVersion) {
version = $$split(1, '.')
number =
for(i, 0..2) {
n = $$member(version, $$i)
isEmpty(n): n = 0
number += $$format_number($$n, obase=16 zeropad width=2)
}
number = "0x$$join(number)"
return($$number)
}
defineTest(qtConfExportLibrary) {
isEmpty(2): return()
!$$qtConfEvaluate($$eval($${1}.export)): return()
output = privatePro
eval(libs = $$eval($${1}.libs))
eval(cflags = $$eval($${1}.cflags))
eval(includes = $$eval($${1}.includedir))
version = $$eval($${1}.version)
NAME = $$upper($$2)
!isEmpty(libs): qtConfOutputVar(assign, $$output, QMAKE_LIBS_$$NAME, $$libs)
for (b, $${1}.builds._KEYS_): \
qtConfOutputVar(assign, $$output, QMAKE_LIBS_$${NAME}_$$upper($$b), \
$$eval($${1}.builds.$${b}))
!isEmpty(cflags): qtConfOutputVar(assign, $$output, QMAKE_CFLAGS_$$NAME, $$cflags)
!isEmpty(includes): qtConfOutputVar(assign, $$output, QMAKE_INCDIR_$$NAME, $$includes)
!isEmpty(version) {
qtConfOutputVar(assign, $$output, QMAKE_$${NAME}_VERSION, $$version)
qtConfOutputSetDefine("privateHeader", "QT_LIBRARY_VERSION_$${2}", $$qtConfGetHexVersion($$version))
version = $$split(version, '.')
qtConfOutputVar(assign, $$output, QMAKE_$${NAME}_VERSION_MAJOR, $$member(version, 0))
qtConfOutputVar(assign, $$output, QMAKE_$${NAME}_VERSION_MINOR, $$member(version, 1))
qtConfOutputVar(assign, $$output, QMAKE_$${NAME}_VERSION_PATCH, $$member(version, 2))
}
export(config.output.$${output})
}
defineTest(qtConfHandleLibrary) {
lpfx = config.libraries.$$1
defined($${lpfx}.result, var): return()
qtConfEnsureTestTypeDeps("library")
qtLogTestIntro($${lpfx})
msg = "looking for library $${1}"
write_file($$QMAKE_CONFIG_LOG, msg, append)
result = false
for (s, $${lpfx}.sources._KEYS_) {
qtLog("Trying source $$s of library $${1}.")
spfx = $${lpfx}.sources.$${s}
t = $$eval($${spfx}.type)
call = qtConfLibrary_$$t
!defined($$call, test): \
error("Library $${1} source $${s} has unknown type '$$t'")
!$$qtConfEvaluate($$eval($${spfx}.condition)) {
qtLog("Source $$s of library $$1 failed condition.")
next()
}
!$${call}($$spfx) {
qtLog("Source $$s of library $$1 produced no result.")
next()
}
# if the library defines a test, use it to verify the source.
!isEmpty($${lpfx}.test) {
$${lpfx}.literal_args = $$qtConfLibraryArgs($$spfx)
$${lpfx}.host = $$eval($${spfx}.host)
!qtConfTest_compile($$lpfx) {
qtLog("Source $$s of library $$1 failed verification.")
next()
}
}
# 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)
}
# This is a fake test type for the test dependency system.
defineTest(qtConfTest_library) {
error("The test type 'library' may not be instantiated.")
}
defineTest(qtConfTestPrepare_compile) {
for (u, $$list($$eval($${1}.use))) {
!contains(config.libraries._KEYS_, $$u): \
error("Test $$1 tries to use undeclared library '$$u'")
qtConfHandleLibrary($$u)
lpfx = config.libraries.$${u}
isEmpty($${lpfx}.source): \
return(false)
$${1}.literal_args += $$qtConfLibraryArgs($${lpfx}.sources.$$eval($${lpfx}.source))
}
export($${1}.literal_args)
return(true)
}
defineTest(qtConfTest_compile) {
test = $$eval($${1}.test)
host = $$eval($${1}.host)
isEmpty(host): host = false
test_dir = $$QMAKE_CONFIG_TESTS_DIR/$$test
test_out_dir = $$shadowed($$test_dir)
!isEmpty($${1}.pro): \
test_dir = $$test_dir/$$eval($${1}.pro)
test_cmd_base = "cd $$system_quote($$system_path($$test_out_dir)) &&"
qmake_args = $$qtConfPkgConfigEnv()$$system_quote($$system_path($$QMAKE_QMAKE))
!isEmpty(QMAKE_QTCONF): \
qmake_args += -qtconf $$system_quote($$QMAKE_QTCONF)
# Disable qmake features which are typically counterproductive for tests
qmake_args += "\"CONFIG -= qt debug_and_release app_bundle lib_bundle\""
# allow tests to behave differently depending on the type of library
# being built (shared/static). e.g. see config.tests/unix/icu
shared: \
qmake_configs = "shared"
else: \
qmake_configs = "static"
use_gold_linker: \
qmake_configs += "use_gold_linker"
# add console to the CONFIG variable when running the tests, so that they
# can work with a regular main() entry point on Windows.
qmake_configs += "console"
qmake_args += "\"CONFIG += $$qmake_configs\""
!$$host {
# On WinRT we need to change the entry point as we cannot create windows
# applications
winrt: \
qmake_args += " \"QMAKE_LFLAGS += /ENTRY:main\""
# add compiler flags, these are set for the target and should not be applied to host tests
!isEmpty(EXTRA_DEFINES): \
qmake_args += $$system_quote(DEFINES += $$val_escape(EXTRA_DEFINES))
!isEmpty(EXTRA_LIBDIR) \
qmake_args += $$system_quote(QMAKE_LIBDIR += $$val_escape(EXTRA_LIBDIR))
!isEmpty(EXTRA_FRAMEWORKPATH) \
qmake_args += $$system_quote(QMAKE_FRAMEWORKPATH += $$val_escape(EXTRA_FRAMEWORKPATH))
!isEmpty(EXTRA_INCLUDEPATH): \
qmake_args += $$system_quote(INCLUDEPATH += $$val_escape(EXTRA_INCLUDEPATH))
qmake_args += $$EXTRA_QMAKE_ARGS
}
# Clean up after previous run
exists($$test_out_dir/Makefile): \
QMAKE_MAKE = "$$QMAKE_MAKE clean && $$QMAKE_MAKE"
mkpath($$test_out_dir)|error()
# add possible command line args
qmake_args += $$qtConfPrepareArgs($$eval($${1}.args)) $$eval($${1}.literal_args)
qtRunLoggedCommand("$$test_cmd_base $$qmake_args $$system_quote($$test_dir)") {
qtRunLoggedCommand("$$test_cmd_base $$QMAKE_MAKE"): \
return(true)
}
return(false)
}
defineTest(qtConfTest_verifySpec) {
qtConfTest_compile($$1): return(true)
qtConfAddError("Cannot compile a minimal program. The toolchain or QMakeSpec is broken.", log)
qtConfPrintReport()
error()
}
defineTest(qtConfTest_files) {
for(i, $${1}.files._KEYS_) {
f = $$eval($${1}.files.$${i})
qtLog("Searching for file $${f}.")
contains(f, ".*\.h") {
file = $$qtConfFindInPathList($$f, $$EXTRA_INCLUDEPATH $$QMAKE_DEFAULT_INCDIRS)
} else: contains(f, ".*\.(lib|so|a)") {
file = $$qtConfFindInPathList($$f, $$EXTRA_LIBDIR $$QMAKE_DEFAULT_LIBDIRS)
} else {
# assume we're looking for an executable
file = $$qtConfFindInPath($$f, $$EXTRA_PATH)
}
isEmpty(file) {
qtLog(" Not found.");
return(false)
}
qtLog(" Found at $${file}.")
}
return(true)
}
defineTest(logn) {
log("$${1}$$escape_expand(\\n)")
}
defineTest(qtLogTestIntro) {
description = $$eval($${1}.description)
isEmpty(description): return()
msg = "Checking for $${description}... "
log($$msg)
$$QMAKE_CONFIG_VERBOSE: log("$$escape_expand(\\n)")
write_file($$QMAKE_CONFIG_LOG, msg, append)
}
defineTest(qtLogTestResult) {
isEmpty($${1}.description): return()
!isEmpty($${1}.log) {
field = $$eval($${1}.log)
log_msg = $$eval($${1}.$$field)
msg = "test $$1 gave result $$log_msg"
} else: $${2} {
log_msg = yes
msg = "test $$1 succeeded"
} else {
log_msg = no
msg = "test $$1 FAILED"
}
$$QMAKE_CONFIG_VERBOSE: log_msg = $$msg
logn("$$log_msg")
write_file($$QMAKE_CONFIG_LOG, msg, append)
}
defineTest(qtConfIsBoolean) {
equals(1, "true")|equals(1, "false"): \
return(true)
return(false)
}
defineTest(qtConfSetupTestTypeDeps) {
for (tt, config.testTypeDependencies._KEYS_) {
!defined(qtConfTest_$${tt}, test): \
error("Declaring dependency for undefined test type '$$tt'.")
for (f, config.testTypeDependencies.$${tt}._KEYS_) {
feature = $$eval(config.testTypeDependencies.$${tt}.$${f})
isEmpty(config.features.$${feature}._KEYS_): \
error("Test type '$$tt' depends on undefined feature '$$feature'.")
}
}
# Test type aliasing means that one test type's callback is called by
# another test type's callback. Put differently, one callback forwards
# the call to another one. The former representation is more natural
# (and concise) to write, while the latter is more efficient to process.
# Hence, this function inverts the mapping.
for (tt, config.testTypeAliases._KEYS_) {
!defined(qtConfTest_$${tt}, test): \
error("Aliasing undefined test type '$$tt'.")
for (tta, config.testTypeAliases.$${tt}._KEYS_) {
type = $$eval(config.testTypeAliases.$${tt}.$${tta})
!defined(qtConfTest_$${type}, test): \
error("Aliasing '$$tt' to undefined test type '$$type'.")
config.testTypeForwards.$${type} += $$tt
export(config.testTypeForwards.$${type})
}
}
}
defineTest(qtConfEnsureTestTypeDeps) {
depsn = config.testTypeDependencies.$${1}._KEYS_
!isEmpty($$depsn) {
for (dep, $$depsn) {
feature = $$eval(config.testTypeDependencies.$${1}.$${dep})
!qtConfCheckFeature($$feature): \
error("Test type '$$1' depends on non-emitted feature $${feature}.")
}
$$depsn =
export($$depsn)
}
fwdsn = config.testTypeForwards.$${1}
!isEmpty($$fwdsn) {
for (fwd, $$fwdsn): \
qtConfEnsureTestTypeDeps($$fwd)
$$fwdsn =
export($$fwdsn)
}
}
defineTest(qtRunSingleTest) {
tpfx = config.tests.$${1}
defined($${tpfx}.result, var): \
return()
result = true
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}): result = false
$$result {
qtLogTestIntro($${tpfx})
msg = "executing config test $${1}"
write_file($$QMAKE_CONFIG_LOG, msg, append)
!$${call}($${tpfx}): result = false
qtLogTestResult($${tpfx}, $$result)
}
$${tpfx}.result = $$result
export($${tpfx}.result)
}
defineReplace(qtConfEvaluate) {
isEmpty(1): return(true)
1 ~= s/$$escape_expand(\\t)/ /g
1 ~= s/$$escape_expand(\\r)//g
1 ~= s/$$escape_expand(\\n) */ /g
expr = $${1}
expr ~= s/&&/ && /g
expr ~= s/\|\|/ || /g
expr ~= s/!/ ! /g
expr ~= s/\\(/ ( /g
expr ~= s/\\)/ ) /g
expr ~= s/ *== */==/g
expr ~= s/ *! = */!=/g
expr_list = $$eval($$list($$expr))
return($$qtConfEvaluateSubExpression($${1}, $$expr_list, 0))
}
defineReplace(qtConfEvaluateSingleExpression) {
e = $${2}
equals(e, true) {
result = true
} else: equals(e, false) {
result = false
} else: contains(e, "^[0-9]+$") {
# numbers
result = $$e
} else: contains(e, "^'.*'$") {
# quoted literals
result = $$replace(e, "^'(.*)'$", "\\1")
} else: contains(e, "^tests\..*") {
!qt_conf_tests_allowed: \
error("Expression '$${1}' refers to a test, which is not allowed at this stage of configuring.")
test = $$section(e, ".", 1, 1)
var = $$section(e, ".", 2, -1)
isEmpty(var): \
var = result
!contains(config.tests._KEYS_, $$test): \
error("Unknown test object $${test} in expression '$${1}'.")
qtRunSingleTest($$test)
result = $$eval(config.tests.$${test}.$${var})
} else: contains(e, "^libs\..*") {
!qt_conf_tests_allowed: \
error("Expression '$${1}' refers to a library, which is not allowed at this stage of configuring.")
lib = $$section(e, ".", 1, 1)
var = $$section(e, ".", 2, -1)
isEmpty(var): \
var = result
!contains(config.libraries._KEYS_, $$lib): \
error("Unknown library object $${lib} in expression '$${1}'.")
qtConfHandleLibrary($$lib)
!defined(config.libraries.$${lib}.$${var}, var): \
var = sources.$$eval(config.libraries.$${lib}.$${source}).$$var
result = $$eval(config.libraries.$${lib}.$${var})
} else: contains(e, "^features\..*") {
feature = $$section(e, ".", 1, 1)
var = $$section(e, ".", 2, -1)
isEmpty(var): \
var = available
!contains(config.features._KEYS_, $$feature): \
error("Unknown feature object $${feature} in expression '$${1}'.")
!qtConfCheckFeature($$feature): \
error("Expression '$$1' is accessing non-emitted feature $${feature}.")
result = $$eval(config.features.$${feature}.$${var})
} else: contains(e, "^config\..*") {
var = $$replace(e, "^config\.", "")
result = false
contains(CONFIG, $$var): result = true
} else: contains(e, "^arch\..*") {
var = $$replace(e, "^arch\.", "")
result = false
isEmpty(QT_ARCH): \
qtConfCheckFeature(architecture)
contains(QT_ARCH, $$var): result = true
} else: contains(e, "^input\..*") {
result = $$eval(config.$$e)
} else: contains(e, "^var\..*") {
var = $$replace(e, "^var\.", "")
result = $$eval($$var)
} else: contains(e, "^call\..*") {
call = $$replace(e, "^call\.", "qtConfFunc_")
!defined($$call, replace): \
error("Call $$call referenced in expression '$${1}' does not exist")
eval(result = \$\$"$$call"())
} else {
error("Unrecognized token $$e in expression '$${1}'")
}
return($$result)
}
defineReplace(qtConfEvaluateSubExpression) {
expr_list = $${2}
result = true
negate = false
runSubExpression = false
nesting_level = 0
for (n, $${3}..$$num_add($$size(expr_list), -1)) {
e = $$member(expr_list, $$n)
$$runSubExpression {
runSubExpression = false
result = $$qtConfEvaluateSubExpression($${1}, $$expr_list, $$n)
} else: isEqual(e, "(") {
isEqual(nesting_level, 0): runSubExpression = true
nesting_level = $$num_add($$nesting_level, 1)
next()
} else: isEqual(e, ")") {
nesting_level = $$num_add($$nesting_level, -1)
lessThan(nesting_level, 0): break()
next()
} else: greaterThan(nesting_level, 0) {
next()
} else: isEqual(e, "!") {
negate = true
next()
} else: isEqual(e, "&&") {
!qtConfIsBoolean($$result): \
error("Left hand side of && is non-boolean value '$$result' in expression '$${1}'")
!$$result: return(false)
} else: isEqual(e, "||") {
!qtConfIsBoolean($$result): \
error("Left hand side of || is non-boolean value '$$result' in expression '$${1}'")
$$result: return(true)
} else {
contains(e, ".*==.*") {
lhs = $$qtConfEvaluateSingleExpression($${1}, $$replace(e, "==.*", ""))
rhs = $$qtConfEvaluateSingleExpression($${1}, $$replace(e, ".*==", ""))
result = false
equals(lhs, $$rhs): result = true
} else: contains(e, ".*!=.*") {
lhs = $$qtConfEvaluateSingleExpression($${1}, $$replace(e, "!=.*", ""))
rhs = $$qtConfEvaluateSingleExpression($${1}, $$replace(e, ".*!=", ""))
result = false
!equals(lhs, $$rhs): result = true
} else {
result = $$qtConfEvaluateSingleExpression($${1}, $$e)
}
}
$$negate {
!qtConfIsBoolean($$result): \
error("Attempting to negate a non-boolean value '$$result' in expression '$${1}'")
$$result: \
result = false
else: \
result = true
negate = false
}
}
return($$result)
}
defineReplace(qtIsFeatureEnabled) {
enable = $$eval(config.features.$${1}.enable)
!isEmpty(enable) {
$$qtConfEvaluate($$enable): \
return(true)
} else {
equals(config.input.$${1}, "yes"): \
return(true)
}
return(false)
}
defineReplace(qtIsFeatureDisabled) {
disable = $$eval(config.features.$${1}.disable)
!isEmpty(disable) {
$$qtConfEvaluate($$disable): \
return(true)
} else {
equals(config.input.$${1}, "no"): \
return(true)
}
return(false)
}
defineReplace(qtConfCheckSingleCondition) {
result = $$qtConfEvaluate($$2)
!qtConfIsBoolean($$result): \
error("Evaluation of condition '$$2' yielded non-boolean value '$$result' in feature '$${1}'.")
!$$result {
$${3} {
qtConfAddError("Feature '$${1}' was enabled, but the pre-condition '$$2' failed.", log)
$$result = true
}
}
return($$result)
}
defineTest(qtConfCheckFeature) {
fpfx = config.features.$${1}
available = $$eval($${fpfx}.available)
!isEmpty(available): return(true)
# skip features that will not get emitted anyway
emitIf = $$qtConfEvaluate($$eval($${fpfx}.emitIf))
enabled = $$qtIsFeatureEnabled($$1)
disabled = $$qtIsFeatureDisabled($$1)
!$$emitIf {
$$enabled|$$disabled: \
qtConfAddWarning("Feature $${1} is insignificant in this configuration, ignoring related command line option(s).")
return(false)
}
$$disabled {
result = false
} else: !$$enabled:!$$qtConfEvaluate($$eval($${fpfx}.autoDetect)) {
# feature not auto-detected and not explicitly enabled
result = false
} else {
condition = $$eval($${fpfx}.condition)
!isEmpty(condition) {
result = $$qtConfCheckSingleCondition($$1, $$condition, $$enabled)
} else {
result = true
# check whether we have an array of conditions
for (i, $${fpfx}.condition._KEYS_) {
condition = $$eval($${fpfx}.condition.$$i)
result = $$qtConfCheckSingleCondition($$1, $$condition, $$enabled)
!$$result: break()
}
}
}
$${fpfx}.available = $$result
export($${fpfx}.available)
for (i, config.features.$${feature}.output._KEYS_): \
qtConfProcessOneOutput($$feature, $$i)
return(true)
}
defineTest(qtConfProcessFeatures) {
for (feature, config.features._KEYS_): \
qtConfCheckFeature($$feature)
}
#
# reporting
#
QT_CONF_REPORT_PADDING = "........................................"
defineTest(qtConfReportPadded) {
pad = $$num_add($$str_size($$QT_CONF_REPORT_PADDING), -$$str_size($${1}))
lessThan(pad, 0): pad = 0
str = "$$1 $$str_member($$QT_CONF_REPORT_PADDING, 0, $$pad)"
qtConfAddReport("$$str $${2}")
}
defineReplace(qtConfCollectFeatures) {
l =
for (feature, $$list($${1})) {
$$eval(config.features.$${feature}.available): \
l += $$eval(config.features.$${feature}.description)
}
isEmpty(l): return("<none>")
return($$join(l, ' '))
}
defineTest(qtConfReport_featureList) {
qtConfReportPadded($${1}, $$qtConfCollectFeatures($${2}))
}
defineReplace(qtConfFindFirstAvailableFeature) {
for (feature, $$list($${1})) {
isEmpty(config.features.$${feature}._KEYS_): \
error("Asking for a report on undefined feature $${2}.")
$$eval(config.features.$${feature}.available): \
return($$eval(config.features.$${feature}.description))
}
return("<none>")
}
defineTest(qtConfReport_firstAvailableFeature) {
qtConfReportPadded($${1}, $$qtConfFindFirstAvailableFeature($${2}))
}
defineTest(qtConfReport_feature) {
!contains(config.features._KEYS_, $$2): \
error("Asking for a report on undefined feature $${2}.")
# hide report for not emitted features
isEmpty(config.features.$${2}.available): \
return()
$$eval(config.features.$${2}.available) {
result = "yes"
!isEmpty(3): result = "$${3}"
} else {
result = "no"
!isEmpty(4): result = "$${4}"
}
text = $$eval(config.features.$${2}.description)
qtConfReportPadded($${1}$$text, $$result)
}
defineTest(qtConfReport_note) {
qtConfAddNote($${1})
}
defineTest(qtConfReport_warning) {
qtConfAddWarning($${1})
}
defineTest(qtConfReport_error) {
qtConfAddError($${1}, log)
}
defineTest(qtConfCreateReportRecurse) {
equals(2, false) {
indent = ""
recurse = false
} else {
indent = $${2}
recurse = true
}
keys = $$eval($${1}._KEYS_)
for (n, keys) {
entry = $${1}.$$n
subKeys = $$eval($${entry}._KEYS_)
contains(subKeys, condition) {
condition = $$eval($${entry}.condition)
r = $$qtConfEvaluate($$condition)
!qtConfIsBoolean($$r): \
error("Evaluation of condition '$$condition' in report entry $${entry} yielded non-boolean value '$$r'.")
!$$r: next()
}
contains(subKeys, "section") {
!$$recurse: \
error("Report type 'section' is not allowed in '$$1'.")
section = $$eval($${entry}.section)
qtConfAddReport("$$indent$$section:")
qtConfCreateReportRecurse("$${entry}.entries", "$$indent ")
} else: !isEmpty($${entry}) {
feature = $$eval($${entry})
qtConfReport_feature($$indent, $$feature)
} else {
text = $$eval($${entry}.message)
isEmpty($${entry}.type): \
error("Report entry $${entry} doesn't define a type.")
r = "qtConfReport_$$eval($${entry}.type)"
!defined($$r, test): \
error("Undefined report type $$eval($${entry}.type) used in report entry $${entry}.")
args = $$eval($${entry}.args)
$${r}($$indent$${text}, $$args)
}
}
}
defineTest(qtConfProcessEarlyChecks) {
qtConfCreateReportRecurse(config.earlyReport, false)
qtConfCheckErrors()
}
defineTest(qtConfCreateReport) {
qtConfAddReport(" ")
qtConfCreateReportRecurse(config.report, false)
}
defineTest(qtConfCreateSummary) {
qtConfCreateReportRecurse(config.summary, "")
}
defineTest(qtConfPrintReport) {
for (n, QT_CONFIGURE_REPORT): \
logn($$n)
logn()
for (n, QT_CONFIGURE_NOTES) {
logn($$n)
logn()
}
for (w, QT_CONFIGURE_WARNINGS) {
logn($$w)
logn()
}
!isEmpty(QT_CONFIGURE_ERRORS) {
for (e, QT_CONFIGURE_ERRORS) {
logn($$e)
logn()
}
mention_config_log:!$$QMAKE_CONFIG_VERBOSE {
logn("Check config.log for details.")
logn()
}
!equals(config.input.continue, yes): \
error()
}
}
defineTest(qtConfCheckErrors) {
!isEmpty(QT_CONFIGURE_ERRORS):!equals(config.input.continue, yes): \
qtConfPrintReport()
}
#
# output generation
#
defineReplace(qtConfOutputSelectProFile) {
!isEmpty($${1}.public) {
$$eval($${1}.public): \
return(publicPro)
}
return(privatePro)
}
# qtConfOutputVar(modifier, output, name, value)
defineTest(qtConfOutputVar) {
modifier = $$1
output = $$2
name = $$3
value = $$val_escape(4)
!isEmpty(config.output.$${output}.assign.$${name}): \
error("Trying to overwrite assigned variable '$$name' in '$$output' using modifier '$$modifier'.")
equals(modifier, assign) {
!isEmpty(config.output.$${output}.append.$${name})|!isEmpty(config.output.$${output}.remove.$${name}): \
error("Trying to assign variable '$$name' in '$$output', which has already appended or removed parts.")
config.output.$${output}.$${modifier}.$${name} = $$value
} else: equals(modifier, append) {
contains(config.output.$${output}.remove.$${name}, $$value): \
error("Trying to append removed '$$value' to variable '$$name' in '$$output'.")
config.output.$${output}.$${modifier}.$${name} += $$value
} else: equals(modifier, remove) {
contains(config.output.$${output}.append.$${name}, $$value): \
error("Trying to remove appended '$$value' to variable '$$name' in '$$output'.")
config.output.$${output}.$${modifier}.$${name} += $$value
} else {
error("Invalid modifier '$$modifier' passed to qtConfOutputVar.")
}
config.output.$${output}.$${modifier}._KEYS_ *= $${name}
export(config.output.$${output}.$${modifier}.$${name})
export(config.output.$${output}.$${modifier}._KEYS_)
}
defineTest(qtConfOutputVarHelper) {
negative = $$eval($${2}.negative)
isEmpty(negative): negative = false
!$${3}:!$$negative: return()
$${3}:$$negative: return()
output = $$qtConfOutputSelectProFile($${2})
name = $$eval($${2}.name)
isEmpty(name): \
error("Output type 'var$$title($$1)' used in feature '$$eval($${2}.feature)' without a 'name' entry.")
value = $$qtConfEvaluate($$eval($${2}.value))
!isEmpty($${2}.eval):$$qtConfEvaluate($$eval($${2}.eval)): \
eval(value = $$value)
qtConfOutputVar($$1, $$output, $$name, $$value)
}
defineTest(qtConfOutput_varAssign) {
qtConfOutputVarHelper(assign, $$1, $$2)
}
defineTest(qtConfOutput_varAppend) {
qtConfOutputVarHelper(append, $$1, $$2)
}
defineTest(qtConfOutput_varRemove) {
qtConfOutputVarHelper(remove, $$1, $$2)
}
defineTest(qtConfOutputConfigVar) {
pro = $$3
var = $$4
negative = $$eval($${1}.negative)
isEmpty(negative): negative = false
val = $$eval($${1}.name)
isEmpty(val) {
val = $$eval($${1}.feature)
$$negative: val = no-$$val
}
$${2} {
!$$negative: qtConfOutputVar(append, $$pro, $$var, $$val)
} else {
$$negative: qtConfOutputVar(append, $$pro, $$var, $$val)
}
}
defineTest(qtConfOutput_publicQtConfig) {
qtConfOutputConfigVar($$1, $$2, "publicPro", "QT_CONFIG")
}
defineTest(qtConfOutput_publicConfig) {
qtConfOutputConfigVar($$1, $$2, "publicPro", "CONFIG")
}
defineTest(qtConfOutput_privateConfig) {
qtConfOutputConfigVar($$1, $$2, "privatePro", "CONFIG")
}
defineTest(qtConfOutputSetDefine) {
config.output.$${1}.$${2} = $${3}
config.output.$${1}._KEYS_ *= $${2}
export(config.output.$${1}.$${2})
export(config.output.$${1}._KEYS_)
}
defineTest(qtConfOutput_define) {
output = publicHeader
define = $$eval($${1}.name)
value = $$qtConfEvaluate($$eval($${1}.value))
isEmpty(define): \
error("Output type 'define' used in feature '$$eval($${1}.feature)' without a 'name' entry.")
negative = $$eval($${1}.negative)
isEmpty(negative): negative = false
$${2} {
!$$negative: qtConfOutputSetDefine($$output, $$define, $$value)
} else {
$$negative: qtConfOutputSetDefine($$output, $$define, $$value)
}
}
defineTest(qtConfOutput_feature) {
name = "$$eval($${1}.name)"
isEmpty(name): \
name = $$eval($${1}.feature)
$${2} {
qtConfOutputVar(append, "publicPro", "QT_CONFIG", $$name)
} else {
f = $$upper($$replace(name, -, _))
qtConfOutputSetDefine("publicHeader", "QT_NO_$$f")
}
}
defineTest(qtConfOutput_publicFeature) {
name = "$$eval($${1}.name)"
isEmpty(name): \
name = $$eval($${1}.feature)
feature = $$replace(name, [-+.], _)
$${2} {
qtConfOutputVar(append, "publicPro", "QT.global.enabled_features", $$name)
qtConfOutputSetDefine("publicHeader", "QT_FEATURE_$$feature", 1)
} else {
qtConfOutputVar(append, "publicPro", "QT.global.disabled_features", $$name)
qtConfOutputSetDefine("publicHeader", "QT_FEATURE_$$feature", -1)
}
}
# currently this is somewhat inconsistent, as the feature is output to the public pro file,
# whereas the define is being added to the private pro file.
# This should get cleaned up to add to the private pro and header instead.
defineTest(qtConfOutput_privateFeature) {
name = "$$eval($${1}.name)"
isEmpty(name): \
name = $$eval($${1}.feature)
feature = $$replace(name, [-+.], _)
$${2} {
qtConfOutputVar(append, "privatePro", "QT.global.enabled_features", $$name)
qtConfOutputSetDefine("privateHeader", "QT_FEATURE_$$feature", 1)
} else {
qtConfOutputVar(append, "privatePro", "QT.global.disabled_features", $$name)
qtConfOutputSetDefine("privateHeader", "QT_FEATURE_$$feature", -1)
}
}
defineTest(qtConfProcessOneOutput) {
feature = $${1}
fpfx = config.features.$${feature}
opfx = $${fpfx}.output.$${2}
condition = $$eval($${opfx}.condition)
!isEmpty(condition):!$$qtConfEvaluate($$condition): \
return()
call = $$eval($${opfx}.type)
isEmpty(call) {
# output is just a string, not an object
call = $$eval($$opfx)
}
!defined("qtConfOutput_$$call", test): \
error("Undefined type '$$call' in output '$$2' of feature '$$feature'.")
condition = $$eval($${opfx}.condition)
!isEmpty(condition) {
!$$qtConfEvaluate($$condition): \
return(false)
}
$${opfx}.feature = $$feature
qtConfOutput_$${call}($$opfx, $$eval($${fpfx}.available))
}
defineTest(qtConfProcessOutput) {
# write it to the output files
for (type, config.files._KEYS_) {
file = $$OUT_PWD/$$eval(config.files.$${type})
contains(type, ".*Pro") {
for (k, config.output.$${type}.assign._KEYS_): \
config.output.$$type += "$$k = $$eval(config.output.$${type}.assign.$$k)"
for (k, config.output.$${type}.remove._KEYS_): \
config.output.$$type += "$$k -= $$eval(config.output.$${type}.remove.$$k)"
for (k, config.output.$${type}.append._KEYS_): \
config.output.$$type += "$$k += $$eval(config.output.$${type}.append.$$k)"
} else {
for (define, config.output.$${type}._KEYS_) {
value = $$eval(config.output.$${type}.$${define})
config.output.$$type += "$${LITERAL_HASH}ifndef $$define"
config.output.$$type += "$${LITERAL_HASH}define $$define $$value"
config.output.$$type += "$${LITERAL_HASH}endif"
}
}
defined(qtConfOutputPostProcess_$${type}, test): \
qtConfOutputPostProcess_$${type}()
write_file($$file, config.output.$${type})|error()
}
}
#
# tie it all together
#
defineTest(qtConfigure) {
# load configuration data
configure_data = $$cat($${1}, blob)
!parseJson(configure_data, config): \
error("Invalid or non-existent file $${1}.")
qtConfSetupLibraries()
qtConfSetupTestTypeDeps()
qtConfParseCommandLine()
# do early checks, mainly to validate the command line
qtConfProcessEarlyChecks()
CONFIG += qt_conf_tests_allowed
logn()
logn("Running configuration tests...")
# process all features
qtConfProcessFeatures()
# generate files and reports
qtConfProcessOutput()
qtConfCreateReport()
qtConfCreateSummary()
logn("Done running configuration tests.")
logn()
}
qtConfigure($$_PRO_FILE_PWD_/configure.json)
logn("Configure summary:")
logn()
qtConfPrintReport()