9864d2c6f3
This reverts commit521a85395d
. Having libraries resolved to absolute file paths by configure has annoying consequences: 1. The Qt installer needs to unabsolutify paths in all kinds of files. This is error-prone and fragile. 2. It hurts Qt's relocatabilty. The absolute paths are unlikely to be right on different systems. 3. Qt's configure must imitate linker behavior correctly to do the resolution right on every platform, which is hard to get right. Consequently, the disadvantages of521a8539
outweigh the advantages. Task-number: QTBUG-72903 Change-Id: I3e159c46a1348963615b95614e56f026ecb2aefd Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io> Reviewed-by: Edward Welbourne <edward.welbourne@qt.io>
2467 lines
81 KiB
Plaintext
2467 lines
81 KiB
Plaintext
#
|
|
# W A R N I N G
|
|
# -------------
|
|
#
|
|
# This file is not part of the Qt API. It exists purely as an
|
|
# implementation detail. It may change from version to version
|
|
# without notice, or even be removed.
|
|
#
|
|
# We mean it.
|
|
#
|
|
|
|
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):qt_conf_tests_allowed {
|
|
CONFIG += mention_config_log
|
|
export(CONFIG)
|
|
}
|
|
}
|
|
|
|
defineTest(qtConfFatalError) {
|
|
qtConfAddError($$1, $$2)
|
|
qtConfPrintReport()
|
|
error()
|
|
}
|
|
|
|
# Return a string list for the specified JSON path, which may be either a
|
|
# single string or an array of strings.
|
|
# Note that this returns a variable name, so it can be directly iterated over.
|
|
defineReplace(qtConfScalarOrList) {
|
|
defined($$1, var): return($$1)
|
|
vals = $$list()
|
|
for (i, $${1}._KEYS_): \
|
|
$$vals += $$eval($${1}.$$i)
|
|
export($$vals)
|
|
return($$vals)
|
|
}
|
|
|
|
defineTest(qtConfCommandlineSetInput) {
|
|
arg = $${1}
|
|
val = $${2}
|
|
!isEmpty($${currentConfig}.commandline.options.$${arg}.name): \
|
|
arg = $$eval($${currentConfig}.commandline.options.$${arg}.name)
|
|
!isEmpty(config.input.$$arg) {
|
|
oldval = $$eval(config.input.$$arg)
|
|
equals(oldval, $$val): \
|
|
qtConfAddNote("Option '$$arg' with value '$$val' was specified twice")
|
|
else: \
|
|
qtConfAddNote("Overriding option '$$arg' with '$$val' (was: '$$oldval')")
|
|
}
|
|
|
|
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}
|
|
nextok = $${3}
|
|
isEmpty(val):$$nextok: 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}
|
|
nextok = $${3}
|
|
isEmpty(val) {
|
|
$$nextok: val = $$qtConfPeekNextCommandlineArg()
|
|
contains(val, "^-.*|[A-Z0-9_]+=.*")|isEmpty(val): \
|
|
val = "yes"
|
|
else: \
|
|
val = $$qtConfGetNextCommandlineArg()
|
|
}
|
|
|
|
!qtConfValidateValue($$opt, $$val): \
|
|
return()
|
|
|
|
qtConfCommandlineSetInput($$opt, $$val)
|
|
}
|
|
|
|
|
|
defineTest(qtConfCommandline_addString) {
|
|
opt = $${1}
|
|
val = $${2}
|
|
nextok = $${3}
|
|
isEmpty(val):$$nextok: 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()
|
|
|
|
!isEmpty($${currentConfig}.commandline.options.$${opt}.name): \
|
|
opt = $$eval($${currentConfig}.commandline.options.$${opt}.name)
|
|
|
|
config.input.$$opt += $$val
|
|
export(config.input.$$opt)
|
|
}
|
|
|
|
defineTest(qtConfCommandline_redo) {
|
|
!exists($$OUT_PWD/config.opt) {
|
|
qtConfAddError("No config.opt present - cannot redo configuration.")
|
|
return()
|
|
}
|
|
QMAKE_EXTRA_REDO_ARGS = $$cat($$OUT_PWD/config.opt, lines)
|
|
export(QMAKE_EXTRA_REDO_ARGS) # just for config.log
|
|
QMAKE_EXTRA_ARGS = $$QMAKE_EXTRA_REDO_ARGS $$QMAKE_EXTRA_ARGS
|
|
export(QMAKE_EXTRA_ARGS)
|
|
QMAKE_REDO_CONFIG = true
|
|
export(QMAKE_REDO_CONFIG)
|
|
}
|
|
|
|
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()
|
|
|
|
didCustomCall = false
|
|
for (customCall, customCalls) {
|
|
$${customCall}($$c) {
|
|
didCustomCall = true
|
|
break()
|
|
}
|
|
}
|
|
$$didCustomCall: \
|
|
next()
|
|
|
|
contains(c, "([A-Z0-9_]+)=(.*)") {
|
|
opt = $$replace(c, "^([A-Z0-9_]+)=(.*)", "\\1")
|
|
val = $$replace(c, "^([A-Z0-9_]+)=(.*)", "\\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
|
|
nextok = false
|
|
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 =
|
|
} else: contains(c, "^-(.*)") {
|
|
opt = $$replace(c, "^-(.*)", "\\1")
|
|
val =
|
|
nextok = true
|
|
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")
|
|
nextok = true
|
|
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):contains(opt, "skip") {
|
|
isEmpty(skipOptionWarningAdded) {
|
|
qtConfAddWarning("Command line option -skip is only effective in top-level builds.")
|
|
skipOptionWarningAdded = 1
|
|
}
|
|
val = $$qtConfGetNextCommandlineArg()
|
|
next()
|
|
}
|
|
|
|
isEmpty(type) {
|
|
qtConfAddError("Unknown command line option '$$c'.")
|
|
equals(config.input.continue, yes): \
|
|
next()
|
|
else: \
|
|
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, $$nextok)
|
|
}
|
|
}
|
|
|
|
defineReplace(qtConfToolchainSupportsFlag) {
|
|
test_out_dir = $$OUT_PWD/$$basename(QMAKE_CONFIG_TESTS_DIR)
|
|
test_cmd_base = "$$QMAKE_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 $$QMAKE_CXXFLAGS $${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_bfd_linker: \
|
|
LFLAGS = -fuse-ld=bfd
|
|
use_gold_linker: \
|
|
LFLAGS = -fuse-ld=gold
|
|
use_lld_linker: \
|
|
LFLAGS = -fuse-ld=lld
|
|
|
|
return($$qtConfToolchainSupportsFlag($$LFLAGS "-Wl,$$flag"))
|
|
}
|
|
|
|
defineReplace(qtConfFindInPathList) {
|
|
# This nesting is consistent with Apple ld -search_paths_first,
|
|
# and presumably with GNU ld (no actual documentation found).
|
|
for (dir, 2) {
|
|
for (file, 1) {
|
|
exists("$$dir/$$file"): \
|
|
return("$$dir/$$file")
|
|
}
|
|
}
|
|
return()
|
|
}
|
|
|
|
defineReplace(qtConfFindInPath) {
|
|
ensurePathEnv()
|
|
equals(QMAKE_HOST.os, Windows):!contains(1, .*\\.exe): 1 = $${1}.exe
|
|
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(qtSystemQuote) {
|
|
args =
|
|
for (a, 1): \
|
|
args += $$system_quote($$a)
|
|
return($$args)
|
|
}
|
|
|
|
defineReplace(qtConfPrepareArgs) {
|
|
return($$qtSystemQuote($$split(1)))
|
|
}
|
|
|
|
defineTest(qtConfSetupLibraries) {
|
|
asspfx = $${currentConfig}.commandline.assignments
|
|
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 = $$replace(l, -, _)
|
|
export($${lpfx}.export)
|
|
}
|
|
# 'export' may also be empty, but we need a derived identifier
|
|
alias = $$eval($${lpfx}.export)
|
|
isEmpty(alias): alias = $$replace(l, -, _)
|
|
$${lpfx}.alias = $$alias
|
|
export($${lpfx}.alias)
|
|
# make it easy to refer to the library by its export name.
|
|
$${currentConfig}.exports._KEYS_ += $$alias
|
|
$${currentConfig}.exports.$$alias += $$l
|
|
export($${currentConfig}.exports.$$alias)
|
|
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)
|
|
}
|
|
}
|
|
}
|
|
$${currentConfig}.exports._KEYS_ = $$unique($${currentConfig}.exports._KEYS_)
|
|
export($${currentConfig}.exports._KEYS_)
|
|
|
|
for (alias, $${currentConfig}.exports._KEYS_) {
|
|
ua = $$upper($$alias)
|
|
$${asspfx}._KEYS_ += \
|
|
$${ua}_PREFIX $${ua}_INCDIR $${ua}_LIBDIR \
|
|
$${ua}_LIBS $${ua}_LIBS_DEBUG $${ua}_LIBS_RELEASE
|
|
uapfx = $${asspfx}.$${ua}
|
|
$${uapfx}_PREFIX = $${alias}.prefix
|
|
$${uapfx}_INCDIR = $${alias}.incdir
|
|
$${uapfx}_LIBDIR = $${alias}.libdir
|
|
$${uapfx}_LIBS = $${alias}.libs
|
|
$${uapfx}_LIBS_DEBUG = $${alias}.libs.debug
|
|
$${uapfx}_LIBS_RELEASE = $${alias}.libs.release
|
|
export($${uapfx}_PREFIX)
|
|
export($${uapfx}_INCDIR)
|
|
export($${uapfx}_LIBDIR)
|
|
export($${uapfx}_LIBS)
|
|
export($${uapfx}_LIBS_DEBUG)
|
|
export($${uapfx}_LIBS_RELEASE)
|
|
}
|
|
export($${asspfx}._KEYS_)
|
|
|
|
# reverse mapping for assignments on command line.
|
|
for (a, $${asspfx}._KEYS_) {
|
|
apfx = $${asspfx}.$${a}
|
|
ra = config.commandline.rev_assignments.$$eval($$apfx)
|
|
$$ra = $$a
|
|
export($$ra)
|
|
}
|
|
}
|
|
|
|
defineReplace(qtGccSysrootifiedPath) {
|
|
return($$replace(1, ^=, $$[QT_SYSROOT]))
|
|
}
|
|
|
|
defineReplace(qtGccSysrootifiedPaths) {
|
|
sysrootified =
|
|
for (path, 1): \
|
|
sysrootified += $$qtGccSysrootifiedPath($$path)
|
|
return($$sysrootified)
|
|
}
|
|
|
|
# libs-var, libs, in-paths
|
|
defineTest(qtConfResolveLibs) {
|
|
for (path, 3): \
|
|
pre_lflags += -L$$path
|
|
$$1 = $$pre_lflags $$2
|
|
export($$1)
|
|
return(true)
|
|
}
|
|
|
|
# libs-var, in-paths, libs
|
|
defineTest(qtConfResolvePathLibs) {
|
|
ret = true
|
|
gcc: \
|
|
local_paths = $$qtGccSysrootifiedPaths($$2)
|
|
else: \
|
|
local_paths = $$2
|
|
for (libdir, local_paths) {
|
|
!exists($$libdir/.) {
|
|
qtLog("Library path $$val_escape(libdir) is invalid.")
|
|
ret = false
|
|
}
|
|
}
|
|
!qtConfResolveLibs($$1, $$3, $$2): \
|
|
ret = false
|
|
return($$ret)
|
|
}
|
|
|
|
defineReplace(qtConfGetTestSourceList) {
|
|
result =
|
|
!isEmpty($${1}.test.inherit) {
|
|
base = $$section(1, ., 0, -2)
|
|
for (i, $${1}.test.inherit): \
|
|
result += $$qtConfGetTestSourceList($${base}.$$i)
|
|
}
|
|
return($$result $$1)
|
|
}
|
|
|
|
defineReplace(qtConfGetTestIncludes) {
|
|
defined($${1}._KEYS_, var) {
|
|
1st = $$first($${1}._KEYS_)
|
|
equals(1st, 0) {
|
|
# array; recurse for every element
|
|
ret =
|
|
for (k, $${1}._KEYS_): \
|
|
ret += $$qtConfGetTestIncludes($${1}.$$k)
|
|
return($$ret)
|
|
}
|
|
# object; try condition and recurse
|
|
!defined($${1}.headers, var):!defined($${1}.headers._KEYS_, var): \ # just plain broken without it
|
|
error("headers object '$$1' has no nested headers entry")
|
|
cond = $$eval($${1}.condition)
|
|
isEmpty(cond): \ # would be pointless otherwise
|
|
error("headers object '$$1' has no condition")
|
|
!$$qtConfEvaluate($$cond) {
|
|
qtLog("header entry '$$1' failed condition '$$cond'.")
|
|
return()
|
|
}
|
|
qtLog("header entry '$$1' passed condition.")
|
|
return($$qtConfGetTestIncludes($${1}.headers))
|
|
}
|
|
return($$eval($$1)) # plain string - or nothing (can happen for top-level call only)
|
|
}
|
|
|
|
# includes-var, in-paths, test-object-var
|
|
defineTest(qtConfResolvePathIncs) {
|
|
ret = true
|
|
gcc: \
|
|
local_paths = $$qtGccSysrootifiedPaths($$2)
|
|
else: \
|
|
local_paths = $$2
|
|
for (incdir, local_paths) {
|
|
!exists($$incdir/.) {
|
|
qtLog("Include path $$val_escape(incdir) is invalid.")
|
|
ret = false
|
|
}
|
|
}
|
|
2 -= $$QMAKE_DEFAULT_INCDIRS
|
|
$$1 = $$2
|
|
export($$1)
|
|
wasm {
|
|
# FIXME: emcc downloads pre-built libraries and adds their include
|
|
# path to the clang call dynamically. it would be possible to parse
|
|
# the emcc -s USE_xyz=1 --cflags output to populate xzy_INCDIR and
|
|
# thus make the code below work.
|
|
return($$ret)
|
|
}
|
|
tests = $$qtConfGetTestSourceList($$3)
|
|
hdrs =
|
|
for (test, tests): \
|
|
hdrs += $$qtConfGetTestIncludes($${test}.headers)
|
|
for (hdr, hdrs) {
|
|
h = $$qtConfFindInPathList($$hdr, $$2 $$EXTRA_INCLUDEPATH $$QMAKE_DEFAULT_INCDIRS)
|
|
isEmpty(h) {
|
|
qtLog("$$hdr not found in [$$val_escape(2)] and global paths.")
|
|
ret = false
|
|
}
|
|
}
|
|
return($$ret)
|
|
}
|
|
|
|
# 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):isEmpty($${1}.builds._KEYS_): \
|
|
error("'inline' source in library '$$lib' specifies neither 'libs' nor 'builds'.")
|
|
|
|
# library lists are specified as strings in the json sources for
|
|
# readability, but it's a pain to work with that, so expand it now.
|
|
eval($${1}.libs = $$eval($${1}.libs))
|
|
export($${1}.libs)
|
|
for (b, $${1}.builds._KEYS_) {
|
|
eval($${1}.builds.$${b} = $$eval($${1}.builds.$${b}))
|
|
export($${1}.builds.$${b})
|
|
}
|
|
|
|
# if multiple libraries provide the same export, it makes sense
|
|
# to make them recognize the same input variables.
|
|
input = $$eval($${2}.alias)
|
|
|
|
# build-specific direct libs. overwrites inline libs.
|
|
vars =
|
|
any = false
|
|
all = true
|
|
for (b, $$list(debug release)) {
|
|
iv = $${input}.libs.$${b}
|
|
vars += $$eval(config.commandline.rev_assignments.$${iv})
|
|
defined(config.input.$${iv}, var) {
|
|
eval($${1}.builds.$${b} = $$eval(config.input.$${iv}))
|
|
$${1}.builds._KEYS_ *= $${b}
|
|
any = true
|
|
} else {
|
|
all = false
|
|
}
|
|
}
|
|
$$any {
|
|
!$$all {
|
|
qtConfAddError("Either none or all of $$join(vars, ", ", [, ]) must be specified.")
|
|
return(false)
|
|
}
|
|
export($${1}.builds._KEYS_)
|
|
# we also reset the generic libs, to avoid surprises.
|
|
$${1}.libs =
|
|
}
|
|
|
|
# direct libs. overwrites inline libs.
|
|
defined(config.input.$${input}.libs, var): \
|
|
eval($${1}.libs = $$eval(config.input.$${input}.libs))
|
|
|
|
includes = $$eval(config.input.$${input}.incdir)
|
|
|
|
# prefix. prepends to (possibly overwritten) inline libs.
|
|
prefix = $$eval(config.input.$${input}.prefix)
|
|
!isEmpty(prefix) {
|
|
includes += $$prefix/include
|
|
$${1}.libs = -L$$prefix/lib $$eval($${1}.libs)
|
|
}
|
|
|
|
libdir = $$eval(config.input.$${input}.libdir)
|
|
!isEmpty(libdir) {
|
|
libs =
|
|
for (ld, libdir): \
|
|
libs += -L$$ld
|
|
$${1}.libs = $$libs $$eval($${1}.libs)
|
|
export($${1}.libs)
|
|
}
|
|
|
|
!qtConfResolvePathIncs($${1}.includedir, $$includes, $$2): \
|
|
return(false)
|
|
|
|
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'.")
|
|
|
|
!qtConfResolvePathLibs($${1}.libs, $$eval(QMAKE_LIBDIR_$$spec), $$eval(QMAKE_LIBS_$$spec)): \
|
|
return(false)
|
|
|
|
!qtConfResolvePathIncs($${1}.includedir, $$eval(QMAKE_INCDIR_$$spec), $$2): \
|
|
return(false)
|
|
|
|
!isEmpty(QMAKE_EXPORT_INCDIR_$$spec) {
|
|
$${1}.exportincludedir = $$eval(QMAKE_EXPORT_INCDIR_$$spec)
|
|
export($${1}.exportincludedir)
|
|
}
|
|
|
|
# note that the object is re-exported, because we resolve the libraries.
|
|
|
|
return(true)
|
|
}
|
|
|
|
# the library is found via pkg-config.
|
|
defineTest(qtConfLibrary_pkgConfig) {
|
|
pkg_config = $$qtConfPkgConfig($$eval($${1}.host))
|
|
isEmpty(pkg_config) {
|
|
qtLog("pkg-config use disabled globally.")
|
|
return(false)
|
|
}
|
|
args = $$qtConfPrepareArgs($$eval($${1}.args))
|
|
|
|
!qtConfPkgConfigPackageExists($$pkg_config, $$args) {
|
|
qtLog("pkg-config did not find package.")
|
|
return(false)
|
|
}
|
|
|
|
qtRunLoggedCommand("$$pkg_config --modversion $$args", version)|return(false)
|
|
version ~= s/[^0-9.].*$//
|
|
$${1}.version = $$first(version)
|
|
export($${1}.version)
|
|
|
|
qtRunLoggedCommand("$$pkg_config --libs-only-L $$args", libpaths)|return(false)
|
|
qtRunLoggedCommand("$$pkg_config --libs-only-l $$args", libs)|return(false)
|
|
eval(libs = $$libpaths $$libs)
|
|
!qtConfResolveLibs($${1}.libs, $$libs): \
|
|
return(false)
|
|
contains($${1}.libs, ".*\\.$${QMAKE_EXTENSION_STATICLIB}$") {
|
|
qtRunLoggedCommand("$$pkg_config --static --libs-only-L $$args", libpaths)|return(false)
|
|
qtRunLoggedCommand("$$pkg_config --static --libs-only-l $$args", libs)|return(false)
|
|
eval(libs = $$libpaths $$libs)
|
|
!qtConfResolveLibs($${1}.libs, $$libs): \
|
|
return(false)
|
|
}
|
|
|
|
qtRunLoggedCommand("$$pkg_config --cflags $$args", $${1}.cflags)|return(false)
|
|
# Split CFLAGS into stuff that goes into DEFINES, INCLUDEPATH, and other stuff.
|
|
# The compound variable is still set in case something wants to use it outside
|
|
# regular library exports.
|
|
defines =
|
|
includes =
|
|
ignored =
|
|
eval(cflags = $$eval($${1}.cflags))
|
|
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 include other flags
|
|
# we really don't need and shouldn't add.
|
|
ignored += $$i
|
|
}
|
|
}
|
|
!isEmpty(ignored): \
|
|
qtLog("Note: Dropped compiler flags '$$ignored'.")
|
|
!qtConfResolvePathIncs($${1}.includedir, $$includes, $$2): \
|
|
return(false)
|
|
$${1}.defines = $$defines
|
|
|
|
# now remove the content of the transitive deps we know about.
|
|
largs = $$qtConfAllLibraryArgs($$eval($${2}.dependencies))
|
|
for (la, largs): \
|
|
eval("$$la")
|
|
USES = $$eval($$list($$upper($$replace(QMAKE_USE, -, _))))
|
|
# _CC == _LD for configure's library sources, so pick first arbitrarily.
|
|
DEPS = $$resolve_depends(USES, QMAKE_DEPENDS_, _CC)
|
|
for (DEP, DEPS) {
|
|
$${1}.libs -= $$eval(QMAKE_LIBS_$${DEP})
|
|
$${1}.includedir -= $$eval(QMAKE_INCDIR_$${DEP})
|
|
$${1}.defines -= $$eval(QMAKE_DEFINES_$${DEP})
|
|
}
|
|
export($${1}.libs)
|
|
export($${1}.includedir)
|
|
export($${1}.defines)
|
|
|
|
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) {
|
|
NAME = $$upper($$replace($${1}.library, -, _))
|
|
qmake_args = "QMAKE_LIBS_$${NAME} = $$val_escape($${1}.libs)"
|
|
for (b, $${1}.builds._KEYS_): \
|
|
qmake_args += "QMAKE_LIBS_$${NAME}_$$upper($$b) = $$val_escape($${1}.builds.$${b})"
|
|
includedir = $$eval($${1}.includedir)
|
|
!isEmpty(includedir): \
|
|
qmake_args += "QMAKE_INCDIR_$${NAME} = $$val_escape(includedir)"
|
|
defines = $$eval($${1}.defines)
|
|
!isEmpty(defines): \
|
|
qmake_args += "QMAKE_DEFINES_$${NAME} = $$val_escape(defines)"
|
|
depends = $$eval($${2}.dependencies)
|
|
!isEmpty(depends) {
|
|
dep_uses =
|
|
for (use, depends): \
|
|
dep_uses += $$section(use, :, 1, 1)
|
|
qmake_args += \
|
|
"QMAKE_DEPENDS_$${NAME}_CC = $$upper($$replace(dep_uses, -, _))" \
|
|
"QMAKE_DEPENDS_$${NAME}_LD = $$upper($$replace(dep_uses, -, _))"
|
|
}
|
|
return($$qmake_args)
|
|
}
|
|
|
|
defineReplace(qtConfAllLibraryArgs) {
|
|
isEmpty(1): return()
|
|
dep_uses =
|
|
for (use, 1): \
|
|
dep_uses += $$section(use, :, 1, 1)
|
|
dep_args =
|
|
seen =
|
|
for(ever) {
|
|
isEmpty(1): break()
|
|
use = $$take_last(1)
|
|
contains(seen, $$use): next()
|
|
seen += $$use
|
|
use_cfg = $$section(use, :, 0, 0)
|
|
!isEmpty(use_cfg) {
|
|
use_lib = $$section(use, :, 1, 1)
|
|
lpfx = $${use_cfg}.libraries.$$use_lib
|
|
dep_args += $$qtConfLibraryArgs($${lpfx}.sources.$$eval($${lpfx}.source), $$lpfx)
|
|
1 += $$eval($${lpfx}.dependencies)
|
|
}
|
|
}
|
|
return("QMAKE_USE += $$dep_uses" $$dep_args)
|
|
}
|
|
|
|
defineTest(qtConfExportLibrary) {
|
|
lpfx = $${currentConfig}.libraries.$$1
|
|
alias = $$eval($${lpfx}.alias)
|
|
$${currentConfig}.found.$$alias = $$1
|
|
export($${currentConfig}.found.$$alias)
|
|
name = $$eval($${lpfx}.export)
|
|
isEmpty(name): return()
|
|
spfx = $${lpfx}.sources.$$eval($${lpfx}.source)
|
|
!$$qtConfEvaluate($$eval($${spfx}.export)): return()
|
|
|
|
output = privatePro
|
|
NAME = $$upper($$name)
|
|
# LIBS is emitted even if empty, as this allows the library to be "seen".
|
|
libs = $$eval($${spfx}.libs)
|
|
qtConfOutputVar(assign, $$output, QMAKE_LIBS_$$NAME, $$libs)
|
|
for (b, $${spfx}.builds._KEYS_) {
|
|
blibs = $$eval($${spfx}.builds.$${b})
|
|
qtConfOutputVar(assign, $$output, QMAKE_LIBS_$${NAME}_$$upper($$b), $$blibs)
|
|
}
|
|
defines = $$eval($${spfx}.defines)
|
|
!isEmpty(defines): qtConfOutputVar(assign, $$output, QMAKE_DEFINES_$$NAME, $$defines)
|
|
includes = $$eval($${spfx}.exportincludedir)
|
|
!equals(includes, -) {
|
|
isEmpty(includes): includes = $$eval($${spfx}.includedir)
|
|
!isEmpty(includes): qtConfOutputVar(assign, $$output, QMAKE_INCDIR_$$NAME, $$includes)
|
|
}
|
|
uses = $$eval($${lpfx}.dependencies)
|
|
!isEmpty(uses) {
|
|
# FIXME: ideally, we would export transitive deps only for static
|
|
# libs, to not extend the link interface unduly. however, the system
|
|
# does currently not differentiate between public and private deps.
|
|
depends =
|
|
for (use, uses) {
|
|
use_cfg = $$section(use, :, 0, 0)
|
|
use_lib = $$section(use, :, 1, 1)
|
|
!isEmpty(use_cfg): \
|
|
depends += $$upper($$eval($${use_cfg}.libraries.$${use_lib}.export))
|
|
else: \
|
|
depends += $$upper($$replace(use_lib, -, _))
|
|
}
|
|
# we use suffixes instead of infixes, because $$resolve_depends() demands it.
|
|
qtConfOutputVar(assign, $$output, QMAKE_DEPENDS_$${NAME}_CC, $$depends)
|
|
qtConfOutputVar(assign, $$output, QMAKE_DEPENDS_$${NAME}_LD, $$depends)
|
|
}
|
|
!isEmpty($${currentConfig}.module): \
|
|
qtConfExtendVar($$output, "QT.$${currentModule}_private.libraries", $$name)
|
|
}
|
|
|
|
defineTest(qtConfHandleLibrary) {
|
|
lpfx = $${currentConfig}.libraries.$$1
|
|
defined($${lpfx}.result, var): return()
|
|
|
|
alias = $$eval($${lpfx}.alias)
|
|
!isEmpty($${currentConfig}.found.$$alias) {
|
|
# this happening indicates a logic error in the conditions
|
|
# of the feature(s) referring to this library.
|
|
# note that this does not look across module boundaries, as
|
|
# multiple modules may know the same libraries; de-duplication
|
|
# happens via the cache (obviously, this assumes identical
|
|
# definitions and logic).
|
|
error("A library exporting '$$alias' was already found.")
|
|
}
|
|
|
|
qtConfEnsureTestTypeDeps("library")
|
|
!qtConfTestPrepare_compile($$lpfx) {
|
|
$${lpfx}.result = false
|
|
export($${lpfx}.result)
|
|
return()
|
|
}
|
|
$${lpfx}.dependencies = $$eval($${lpfx}.resolved_uses)
|
|
export($${lpfx}.dependencies)
|
|
|
|
qtConfLoadResult($${lpfx}, $$1, "library") {
|
|
$$eval($${lpfx}.result): \
|
|
qtConfExportLibrary($$1)
|
|
return()
|
|
}
|
|
|
|
qtLogTestIntro($${lpfx}, "looking for library $${1}")
|
|
qtPersistLog()
|
|
|
|
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} ...")
|
|
|
|
cond = $$eval($${spfx}.condition)
|
|
!$$qtConfEvaluate($$cond) {
|
|
qtLog(" => source failed condition '$$cond'.")
|
|
next()
|
|
}
|
|
|
|
!$${call}($$spfx, $$lpfx) {
|
|
qtLog(" => source produced no result.")
|
|
next()
|
|
}
|
|
|
|
$${lpfx}.source = $$s
|
|
export($${lpfx}.source)
|
|
|
|
# if the library defines a test, use it to verify the source.
|
|
defined($${lpfx}.test, var)|defined($${lpfx}.test._KEYS_, var) {
|
|
$${lpfx}.resolved_uses = $$currentConfig:$$1
|
|
$${lpfx}.host = $$eval($${spfx}.host)
|
|
!qtConfTest_compile($$lpfx) {
|
|
qtLog(" => source failed verification.")
|
|
next()
|
|
}
|
|
}
|
|
|
|
qtLog(" => source accepted.")
|
|
|
|
$${lpfx}.cache += source
|
|
for (v, $$list(libs includedir 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($$1)
|
|
|
|
result = true
|
|
break()
|
|
}
|
|
|
|
$${lpfx}.msgs = $$qtPersistedLog()
|
|
export($${lpfx}.msgs)
|
|
|
|
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) {
|
|
!isEmpty($${1}.use._KEYS_) {
|
|
uses =
|
|
for (k, $${1}.use._KEYS_) {
|
|
use = $$eval($${1}.use.$${k}.lib)
|
|
isEmpty(use): \
|
|
error("'use' entry $$k in test $$1 lacks 'lib' field.")
|
|
!$$qtConfEvaluate($$eval($${1}.use.$${k}.condition)): \
|
|
next()
|
|
uses += $$use
|
|
}
|
|
} else {
|
|
uses = $$split($${1}.use)
|
|
}
|
|
for (u, uses) {
|
|
libConfig =
|
|
exports = $$eval($${currentConfig}.exports.$$u)
|
|
!isEmpty(exports) {
|
|
# using a local library by exported name.
|
|
ru = $$eval($${currentConfig}.found.$$u)
|
|
!isEmpty(ru) {
|
|
# if it was already found, all is good.
|
|
u = $$ru
|
|
} else: count(exports, 1) {
|
|
# otherwise, if there is only one option, ensure it's resolved.
|
|
u = $$exports
|
|
qtConfHandleLibrary($$u)
|
|
} else {
|
|
# otherwise, verify that all options were resolved.
|
|
for (x, exports) {
|
|
isEmpty($${currentConfig}.libraries.$${x}.result) {
|
|
# the higher-level logic is in the features, which we cannot
|
|
# infer from here. so the only option is failing.
|
|
error("Test $$1 refers to yet unresolved library export '$$u'")
|
|
}
|
|
}
|
|
return(false)
|
|
}
|
|
libConfig = $$currentConfig
|
|
} else: contains($${currentConfig}.libraries._KEYS_, $$u) {
|
|
# using a local library by real name. this should be the exception.
|
|
qtConfHandleLibrary($$u)
|
|
libConfig = $$currentConfig
|
|
} else {
|
|
for (d, QMAKE_LIBRARY_DEPS) {
|
|
exports = $$eval($${d}.exports.$$u)
|
|
!isEmpty(exports) {
|
|
# using a foreign library by exported name.
|
|
# foreign libraries may be external (if they are from a different
|
|
# repository and the build is modular), and using these by real
|
|
# name is impossible. so for consistency, uses by real name are
|
|
# limited to local libraries.
|
|
ru = $$eval($${d}.found.$$u)
|
|
!isEmpty(ru) {
|
|
u = $$ru
|
|
libConfig = $$d
|
|
break()
|
|
}
|
|
for (x, exports) {
|
|
isEmpty($${d}.libraries.$${x}.result): \
|
|
error("Test $$1 refers to unresolved library export '$$u' in '$$d'")
|
|
}
|
|
return(false)
|
|
}
|
|
}
|
|
}
|
|
isEmpty(libConfig) {
|
|
nu = $$upper($$replace(u, -, _))
|
|
!defined(QMAKE_LIBS_$$nu, var): \
|
|
error("Test $$1 tries to use undeclared library '$$u'")
|
|
# using an external library by exported name.
|
|
$${1}.resolved_uses += :$$u
|
|
} else {
|
|
lpfx = $${libConfig}.libraries.$${u}
|
|
!equals($${lpfx}.result, true): \
|
|
return(false)
|
|
$${1}.resolved_uses += $$libConfig:$$u
|
|
}
|
|
}
|
|
export($${1}.resolved_uses)
|
|
return(true)
|
|
}
|
|
|
|
defineTest(qtConfPrepareCompileTestSource) {
|
|
test_dir = $$2
|
|
|
|
tests = $$qtConfGetTestSourceList($$1)
|
|
|
|
test_lang = "c++"
|
|
for (test, tests): \
|
|
test_lang += $$eval($${test}.test.lang)
|
|
test_lang = $$last(test_lang) # Last non-empty, that is.
|
|
|
|
equals(test_lang, "c++"): suffix = "cpp"
|
|
else: equals(test_lang, "c"): suffix = "c"
|
|
else: equals(test_lang, "objc"): suffix = "m"
|
|
else: equals(test_lang, "objc++"): suffix = "mm"
|
|
else: error("Unknown language '$$test_lang' in compile test $$1")
|
|
|
|
# Create source code
|
|
contents = "/* Generated by configure */"
|
|
# Custom code before includes
|
|
for (test, tests): \
|
|
for (ent, $$qtConfScalarOrList($${test}.test.head)): \
|
|
contents += $$ent
|
|
# Includes
|
|
for (test, tests) {
|
|
hdrs = $$qtConfGetTestIncludes($${test}.test.include)
|
|
isEmpty(hdrs): \
|
|
hdrs = $$qtConfGetTestIncludes($${test}.headers)
|
|
for (ent, hdrs): \
|
|
contents += "$${LITERAL_HASH}include <$$ent>"
|
|
}
|
|
# Custom code after includes
|
|
for (test, tests): \
|
|
for (ent, $$qtConfScalarOrList($${test}.test.tail)): \
|
|
contents += $$ent
|
|
# And finally the custom code inside main()
|
|
contents += \
|
|
"int main(int argc, char **argv)" \
|
|
"{" \
|
|
" (void)argc; (void)argv;" \
|
|
" /* BEGIN TEST: */"
|
|
for (test, tests): \
|
|
for (ent, $$qtConfScalarOrList($${test}.test.main)): \
|
|
contents += " $$ent"
|
|
contents += \
|
|
" /* END TEST */" \
|
|
" return 0;" \
|
|
"}"
|
|
write_file($$test_dir/main.$$suffix, contents)|error()
|
|
|
|
for (test, tests) {
|
|
for (file, $$qtConfScalarOrList($${test}.test.files._KEYS_)): \
|
|
write_file($$test_dir/$$file, $$qtConfScalarOrList($${test}.test.files.$${file}))|error()
|
|
}
|
|
|
|
# Create stub .pro file
|
|
contents = "SOURCES = main.$$suffix"
|
|
# Custom project code
|
|
pwd = $$val_escape($${currentConfig}.dir)
|
|
for (test, tests): \
|
|
for (ent, $$qtConfScalarOrList($${test}.test.qmake)): \
|
|
contents += $$replace(ent, "@PWD@", $$pwd)
|
|
write_file($$test_dir/$$basename(test_dir).pro, contents)|error()
|
|
}
|
|
|
|
defineTest(qtConfTest_compile) {
|
|
test = $$eval($${1}.test)
|
|
host = $$eval($${1}.host)
|
|
isEmpty(host): host = false
|
|
|
|
test_base_out_dir = $$OUT_PWD/$$basename(QMAKE_CONFIG_TESTS_DIR)
|
|
isEmpty(test) {
|
|
test_dir = $$test_base_out_dir/$$section(1, ".", -1)
|
|
test_out_dir = $$test_dir
|
|
qtConfPrepareCompileTestSource($$1, $$test_dir)
|
|
} else {
|
|
test_dir = $$QMAKE_CONFIG_TESTS_DIR/$$test
|
|
test_out_dir = $$test_base_out_dir/$$test
|
|
!isEmpty($${1}.pro): \
|
|
test_dir = $$test_dir/$$eval($${1}.pro)
|
|
}
|
|
test_cmd_base = "$$QMAKE_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_bfd_linker: \
|
|
qmake_configs += "use_bfd_linker"
|
|
use_gold_linker: \
|
|
qmake_configs += "use_gold_linker"
|
|
use_lld_linker: \
|
|
qmake_configs += "use_lld_linker"
|
|
|
|
# disable warnings from the builds, since they're just noise at this point.
|
|
qmake_configs += "warn_off"
|
|
|
|
# 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"
|
|
|
|
# for platforms with multiple architectures (macOS, iOS, tvOS, watchOS),
|
|
# make sure tests are only built for a single architecture
|
|
qmake_configs += "single_arch"
|
|
|
|
qmake_args += "\"CONFIG += $$qmake_configs\""
|
|
|
|
!$$host|!cross_compile {
|
|
# 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
|
|
}
|
|
|
|
# make sure to make this the last override (because of -early)
|
|
cross_compile {
|
|
# must be done before loading default_pre.prf.
|
|
qmake_args += -early "\"CONFIG += cross_compile\""
|
|
}
|
|
|
|
# Clean up after previous run
|
|
exists($$test_out_dir/Makefile): \
|
|
QMAKE_MAKE = "$$QMAKE_MAKE clean && $$QMAKE_MAKE"
|
|
|
|
mkpath($$test_out_dir)|error()
|
|
cont = "CONFIG += QTDIR_build"
|
|
write_file($$test_base_out_dir/.qmake.cache, cont)|error()
|
|
|
|
$${1}.literal_args += $$qtConfAllLibraryArgs($$eval($${1}.resolved_uses))
|
|
|
|
# add possible command line args
|
|
qmake_args += \
|
|
$$qtConfPrepareArgs($$eval($${1}.args)) \
|
|
$$qtSystemQuote($$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_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()
|
|
|
|
isEmpty(3): 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
|
|
isEmpty(3): logn("$$log_msg")
|
|
write_file($$QMAKE_CONFIG_LOG, msg, append)
|
|
}
|
|
|
|
defineTest(qtConfSaveResult) {
|
|
equals($${1}.cache, -): \
|
|
return()
|
|
keys = result msgs $$eval($${1}.cache)
|
|
cont = "cache.$${2}._KEYS_ = $$keys"
|
|
cache.$${2}._KEYS_ = $$keys
|
|
export(cache.$${2}._KEYS_)
|
|
for (k, keys) {
|
|
cont += "cache.$${2}.$${k} = $$val_escape($${1}.$${k})"
|
|
cache.$${2}.$${k} = $$eval($${1}.$${k})
|
|
export(cache.$${2}.$${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})
|
|
}
|
|
# we could print the cached result, but that's basically just noise -
|
|
# the explicitly generated summary is supposed to contain all relevant
|
|
# information.
|
|
qtLogTestIntro($$1, "loaded result for $$3 $$1", false)
|
|
qtLog($$eval($${1}.msgs))
|
|
qtLogTestResult($$1, $$eval($${1}.result), false)
|
|
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(qtConfEnsureTestTypeDepsOne) {
|
|
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): \
|
|
qtConfEnsureTestTypeDepsOne($$fwd)
|
|
$$fwdsn =
|
|
export($$fwdsn)
|
|
}
|
|
}
|
|
|
|
defineTest(qtConfEnsureTestTypeDeps) {
|
|
qtConfEnsureTestTypeDepsOne($$1)
|
|
currentConfig = config.builtins
|
|
qtConfEnsureTestTypeDepsOne($$1)
|
|
}
|
|
|
|
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, "config test"): \
|
|
return()
|
|
|
|
qtLogTestIntro($${tpfx}, "executing config test $${1}")
|
|
qtPersistLog()
|
|
|
|
result = false
|
|
$${call}($${tpfx}): result = true
|
|
|
|
$${tpfx}.msgs = $$qtPersistedLog()
|
|
export($${tpfx}.msgs)
|
|
|
|
qtLogTestResult($${tpfx}, $$result)
|
|
|
|
$${tpfx}.result = $$result
|
|
export($${tpfx}.result)
|
|
qtConfSaveResult($${tpfx}, $$1)
|
|
}
|
|
|
|
defineTest(qtConfHaveModule) {
|
|
module = $$replace(1, -, _)
|
|
!isEmpty(QT.$${module}.skip):$$eval(QT.$${module}.skip): \
|
|
return(false)
|
|
!isEmpty(QT.$${module}.name): \
|
|
return(true)
|
|
return(false)
|
|
}
|
|
|
|
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, "^module\\..*") {
|
|
var = $$replace(e, "^module\\.", "")
|
|
result = false
|
|
qtConfHaveModule($$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, "^subarch\\..*") {
|
|
var = $$replace(e, "^subarch\\.", "")
|
|
result = false
|
|
isEmpty(QT_ARCH): \
|
|
qtConfCheckFeature(architecture)
|
|
contains(QT_CPU_FEATURES.$$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 {
|
|
result = true
|
|
for (condition, $$qtConfScalarOrList($${fpfx}.condition)) {
|
|
result = $$qtConfCheckSingleCondition($$1, $$condition, $$enabled)
|
|
!$$result: break()
|
|
}
|
|
}
|
|
$${fpfx}.available = $$result
|
|
export($${fpfx}.available)
|
|
|
|
for (i, $${fpfx}.output._KEYS_): \
|
|
qtConfProcessOneOutput($${1}, $$i)
|
|
|
|
return(true)
|
|
}
|
|
|
|
defineTest(qtConfCheckModuleCondition) {
|
|
QT.$${currentModule}.skip = false
|
|
!$$qtConfEvaluate($$eval($${currentConfig}.condition)): \
|
|
QT.$${currentModule}.skip = true
|
|
export(QT.$${currentModule}.skip)
|
|
|
|
# ensure qtConfHaveModule() works
|
|
QT.$${currentModule}.name = -
|
|
export(QT.$${currentModule}.name)
|
|
}
|
|
|
|
|
|
defineTest(qtConfProcessFeatures) {
|
|
for (feature, $${currentConfig}.features._KEYS_): \
|
|
qtConfCheckFeature($$feature)
|
|
}
|
|
|
|
#
|
|
# reporting
|
|
#
|
|
|
|
defineReplace(qtConfPadCols) {
|
|
pad = $$num_add($$str_size($$2), -$$str_size($${1}))
|
|
lessThan(pad, 0): pad = 0
|
|
return("$$1 $$str_member($$2, 0, $$pad) $$3")
|
|
}
|
|
|
|
defineTest(qtConfReportPadded) {
|
|
qtConfAddReport($$qtConfPadCols($$1, "........................................", $$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(qtConfReport_fatal) {
|
|
qtConfFatalError($${1})
|
|
}
|
|
|
|
defineTest(qtConfCreateReportRecurse) {
|
|
equals(2, false) {
|
|
indent = ""
|
|
recurse = false
|
|
} else {
|
|
indent = $${2}
|
|
recurse = true
|
|
}
|
|
|
|
keys = $$eval($${1}._KEYS_)
|
|
for (n, keys) {
|
|
entry = $${1}.$$n
|
|
subKeys = $$eval($${entry}._KEYS_)
|
|
contains(subKeys, condition) {
|
|
r = true
|
|
for (condition, $$qtConfScalarOrList($${entry}.condition)) {
|
|
r = $$qtConfEvaluate($$condition)
|
|
!$$r: break()
|
|
}
|
|
!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) {
|
|
blocks = \
|
|
"$$join(QT_CONFIGURE_REPORT, $$escape_expand(\\n))" \
|
|
"$$join(QT_CONFIGURE_NOTES, $$escape_expand(\\n\\n))" \
|
|
"$$join(QT_CONFIGURE_WARNINGS, $$escape_expand(\\n\\n))"
|
|
|
|
!isEmpty(QT_CONFIGURE_ERRORS) {
|
|
blocks += "$$join(QT_CONFIGURE_ERRORS, $$escape_expand(\\n\\n))"
|
|
mention_config_log:!$$QMAKE_CONFIG_VERBOSE: \
|
|
blocks += "Check config.log for details."
|
|
}
|
|
blocks = "$$join(blocks, $$escape_expand(\\n\\n))"
|
|
logn($$blocks)
|
|
!isEmpty(QT_CONFIGURE_ERRORS):!equals(config.input.continue, yes): \
|
|
error()
|
|
write_file($$OUT_PWD/config.summary, blocks)|error()
|
|
}
|
|
|
|
defineTest(qtConfCheckErrors) {
|
|
!isEmpty(QT_CONFIGURE_ERRORS):!equals(config.input.continue, yes): \
|
|
qtConfPrintReport()
|
|
}
|
|
|
|
#
|
|
# output generation
|
|
#
|
|
|
|
defineTest(qtConfOutput_libraryPaths) {
|
|
qtLog("Global lib dirs: [$$val_escape(EXTRA_LIBDIR)] [$$val_escape(QMAKE_DEFAULT_LIBDIRS)]")
|
|
qtLog("Global inc dirs: [$$val_escape(EXTRA_INCLUDEPATH)] [$$val_escape(QMAKE_DEFAULT_INCDIRS)]")
|
|
}
|
|
|
|
# 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}
|
|
|
|
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'.")
|
|
|
|
!$$qtConfEvaluate($$eval($${opfx}.condition)): \
|
|
return()
|
|
|
|
$${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
|
|
!defined($${currentConfig}.files._KEYS_, var) {
|
|
# 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: contains(type, ".*Header") {
|
|
for (define, $${currentConfig}.output.$${type}._KEYS_) {
|
|
value = $$eval($${currentConfig}.output.$${type}.$${define})
|
|
$${currentConfig}.output.$$type += "$${LITERAL_HASH}define $$define $$value"
|
|
}
|
|
}
|
|
|
|
content = $$eval($${currentConfig}.output.$${type})
|
|
|
|
!isEmpty(module): \
|
|
call = qtConfOutputPostProcess_$${module}_$${type}
|
|
else: \
|
|
call = qtConfOutputPostProcess_$${type}
|
|
defined($$call, replace): \
|
|
eval(content = \$\$"$$call"(\$\$content))
|
|
|
|
file = $$eval($${currentConfig}.files.$${type})
|
|
fileCont.$$file += $$content
|
|
fileCont._KEYS_ *= $$file
|
|
}
|
|
|
|
for (file, fileCont._KEYS_): \
|
|
write_file($$basedir/$$file, fileCont.$$file)|error()
|
|
}
|
|
|
|
#
|
|
# tie it all together
|
|
#
|
|
|
|
!isEmpty(_QMAKE_SUPER_CACHE_):!equals(OUT_PWD, $$dirname(_QMAKE_SUPER_CACHE_)) {
|
|
# sub-repo within a top-level build; no need to configure anything.
|
|
!isEmpty(QMAKE_EXTRA_ARGS) {
|
|
# sub-projects don't get the extra args passed down automatically,
|
|
# so we can use their presence to detect misguided attempts to
|
|
# configure the repositories separately.
|
|
# caveat: a plain qmake call is indistinguishable from a recursion
|
|
# (by design), so we cannot detect this case.
|
|
error("You cannot configure $$TARGET separately within a top-level build.")
|
|
}
|
|
return()
|
|
}
|
|
|
|
config.$${TARGET}.dir = $$_PRO_FILE_PWD_
|
|
cfgs = $$TARGET
|
|
!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) {
|
|
!isEmpty(QMAKE_EXTRA_ARGS): \
|
|
error("This module does not accept configure command line arguments.")
|
|
return()
|
|
}
|
|
|
|
load(configure_base)
|
|
|
|
QMAKE_POST_CONFIGURE =
|
|
config.builtins.dir = $$PWD/data
|
|
configsToProcess = builtins $$configsToProcess
|
|
allConfigs =
|
|
for(ever) {
|
|
isEmpty(configsToProcess): \
|
|
break()
|
|
|
|
thisConfig = $$take_first(configsToProcess)
|
|
currentConfig = config.$$thisConfig
|
|
thisDir = $$eval($${currentConfig}.dir)
|
|
jsonFile = $$thisDir/configure.json
|
|
priFile = $$thisDir/configure.pri
|
|
|
|
# load configuration data
|
|
configure_data = $$cat($$jsonFile, blob)
|
|
!parseJson(configure_data, $$currentConfig): \
|
|
error("Invalid or non-existent file $${jsonFile}.")
|
|
exists($$priFile): \
|
|
!include($$priFile): error()
|
|
|
|
# only configs which contain more than just subconfigs are saved for later.
|
|
$${currentConfig}._KEYS_ -= subconfigs
|
|
!isEmpty($${currentConfig}._KEYS_) {
|
|
allConfigs += $$currentConfig
|
|
contains($${currentConfig}._KEYS_, libraries) {
|
|
qtConfSetupLibraries()
|
|
# this ensures that references in QMAKE_LIBRARY_DEPS are unique.
|
|
qtConfSetModuleName()
|
|
ex = $$eval(config.modules.$${currentModule})
|
|
!isEmpty(ex): \
|
|
error("Module $$currentModule is claimed by both $$currentConfig and $${ex}.")
|
|
config.modules.$${currentModule} = $$currentConfig
|
|
}
|
|
}
|
|
|
|
# prepend all subconfigs to files to keep a depth first search order
|
|
subconfigs =
|
|
for(n, $${currentConfig}.subconfigs._KEYS_) {
|
|
subconfig = $$eval($${currentConfig}.subconfigs.$${n})
|
|
name = $${thisConfig}_$$basename(subconfig)
|
|
ex = $$eval(config.$${name}.dir)
|
|
!isEmpty(ex): \
|
|
error("Basename clash between $$thisDir/$$subconfig and $${ex}.")
|
|
config.$${name}.dir = $$thisDir/$$subconfig
|
|
subconfigs += $$name
|
|
}
|
|
configsToProcess = $$subconfigs $$configsToProcess
|
|
}
|
|
# 'builtins' is used for command line parsing and test type dependency
|
|
# injection, but its features must not be processed regularly.
|
|
allModuleConfigs = $$member(allConfigs, 1, -1)
|
|
|
|
QMAKE_SAVED_ARGS = $$QMAKE_EXTRA_ARGS
|
|
QMAKE_REDO_CONFIG = false
|
|
qtConfParseCommandLine()
|
|
qtConfCheckErrors()
|
|
|
|
!isEmpty(config.input.list-features) {
|
|
all_ft =
|
|
for (currentConfig, allModuleConfigs) {
|
|
for (k, $${currentConfig}.features._KEYS_) {
|
|
pp = $$eval($${currentConfig}.features.$${k}.purpose)
|
|
!isEmpty(pp) {
|
|
pfx = $$eval($${currentConfig}.features.$${k}.section)
|
|
!isEmpty(pfx): pfx = "$$pfx: "
|
|
all_ft += $$qtConfPadCols($$k, ".......................", \
|
|
$$pfx$$section(pp, $$escape_expand(\\n), 0, 0))
|
|
}
|
|
}
|
|
}
|
|
all_ft = $$sorted(all_ft)
|
|
logn()
|
|
for (ft, all_ft): \
|
|
logn($$ft)
|
|
error()
|
|
}
|
|
|
|
!isEmpty(config.input.list-libraries) {
|
|
logn()
|
|
for (currentConfig, allModuleConfigs) {
|
|
!isEmpty($${currentConfig}.exports._KEYS_) {
|
|
!isEmpty($${currentConfig}.module): \
|
|
logn($$eval($${currentConfig}.module):)
|
|
else: \
|
|
logn($$section(currentConfig, ., -1):)
|
|
all_xp =
|
|
for (xport, $${currentConfig}.exports._KEYS_) {
|
|
libs = $$eval($${currentConfig}.exports.$$xport)
|
|
isEqual($${currentConfig}.libraries.$$first(libs).export, "") { # not isEmpty()!
|
|
!isEmpty(config.input.verbose): \
|
|
all_xp += "$$xport!"
|
|
} else {
|
|
out = "$$xport"
|
|
!isEmpty(config.input.verbose):!isEqual(xport, $$libs): \
|
|
out += "($$libs)"
|
|
all_xp += "$$out"
|
|
}
|
|
}
|
|
all_xp = $$sorted(all_xp)
|
|
all_xp ~= s,^([^!]*)!$,(\\1),g
|
|
for (xp, all_xp): \
|
|
logn(" $$xp")
|
|
}
|
|
}
|
|
error()
|
|
}
|
|
|
|
QMAKE_CONFIG_VERBOSE = $$eval(config.input.verbose)
|
|
isEmpty(QMAKE_CONFIG_VERBOSE): \
|
|
QMAKE_CONFIG_VERBOSE = false
|
|
QMAKE_CONFIG_LOG = $$OUT_PWD/config.log
|
|
write_file($$QMAKE_CONFIG_LOG, "")
|
|
qtLog("Command line: $$qtSystemQuote($$QMAKE_SAVED_ARGS)")
|
|
$$QMAKE_REDO_CONFIG: \
|
|
qtLog("config.opt: $$qtSystemQuote($$QMAKE_EXTRA_REDO_ARGS)")
|
|
|
|
for (currentConfig, allModuleConfigs) {
|
|
qtConfSetModuleName()
|
|
qtConfSetupModuleOutputs()
|
|
# do early checks, mainly to validate the command line
|
|
qtConfProcessEarlyChecks()
|
|
}
|
|
qtConfCheckErrors()
|
|
|
|
QMAKE_CONFIG_CACHE = $$OUT_PWD/config.cache
|
|
QMAKE_CONFIG_CACHE_USE = $$eval(config.input.cache_use)
|
|
cache_recheck = $$eval(config.input.cache_recheck)
|
|
equals(cache_recheck, yes) {
|
|
QMAKE_CONFIG_CACHE_USE = positive
|
|
cache_recheck =
|
|
}
|
|
isEmpty(QMAKE_CONFIG_CACHE_USE): \
|
|
QMAKE_CONFIG_CACHE_USE = all
|
|
!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
|
|
} else: !isEmpty(cache_recheck) {
|
|
for (cr, $$list($$split(cache_recheck, ","))) {
|
|
!isEmpty(cache.$${cr}._KEYS_) {
|
|
cache.$${cr}._KEYS_ =
|
|
} else {
|
|
qtConfAddWarning("Attempting to discard non-cached result '$$cr'.")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
equals(QMAKE_CONFIG_CACHE_USE, none) {
|
|
cont = \
|
|
"cache.platform = $$[QMAKE_SPEC]" \
|
|
"cache.xplatform = $$[QMAKE_XSPEC]"
|
|
write_file($$QMAKE_CONFIG_CACHE, cont)
|
|
}
|
|
|
|
CONFIG += qt_conf_tests_allowed
|
|
logn()
|
|
logn("Running configuration tests...")
|
|
|
|
for (currentConfig, allModuleConfigs) {
|
|
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
|
|
QMAKE_LIBRARY_DEPS = $$eval(config.modules.global)
|
|
!isEmpty($${currentConfig}.module) {
|
|
for (d, $${currentConfig}.depends._KEYS_) {
|
|
dep = $$replace($${currentConfig}.depends.$$d, -private$, _private)
|
|
gdep = $$replace(dep, _private$, )
|
|
dep *= $$gdep
|
|
QMAKE_CONFIG_DEPS += $$dep
|
|
!isEqual(gdep, $$dep): \ # libraries are in the private module.
|
|
QMAKE_LIBRARY_DEPS += $$eval(config.modules.$$gdep)
|
|
}
|
|
}
|
|
|
|
qtConfCheckModuleCondition()
|
|
|
|
qtConfHaveModule($$currentModule) {
|
|
# process all features
|
|
qtConfProcessFeatures()
|
|
} else {
|
|
qtConfOutputVar(assign, "privatePro", "QT.$${currentModule}.skip", "true")
|
|
}
|
|
|
|
# generate files and reports
|
|
qtConfProcessOutput()
|
|
qtConfHaveModule($$currentModule) {
|
|
qtConfCreateReport()
|
|
qtConfCreateSummary()
|
|
} else {
|
|
QT_CONFIGURE_SKIPPED_MODULES += " $$currentModule"
|
|
}
|
|
}
|
|
|
|
!isEmpty(QT_CONFIGURE_SKIPPED_MODULES): \
|
|
qtConfAddNote("The following modules are not being compiled in this configuration:" $$QT_CONFIGURE_SKIPPED_MODULES)
|
|
|
|
logn("Done running configuration tests.")
|
|
logn()
|
|
|
|
!$$QMAKE_REDO_CONFIG {
|
|
write_file($$OUT_PWD/config.opt, QMAKE_SAVED_ARGS)|error()
|
|
}
|
|
|
|
# these come from the pri files loaded above.
|
|
for (p, QMAKE_POST_CONFIGURE): \
|
|
eval($$p)
|
|
|
|
logn("Configure summary:")
|
|
logn()
|
|
qtConfPrintReport()
|
|
|
|
load(qt_prefix_build_check)
|
|
|
|
# final notes for the user
|
|
logn()
|
|
logn("Qt is now configured for building. Just run '$$QMAKE_MAKE_NAME'.")
|
|
pfx = $$[QT_INSTALL_PREFIX]
|
|
qtIsPrefixBuild($$pfx) {
|
|
logn("Once everything is built, you must run '$$QMAKE_MAKE_NAME install'.")
|
|
logn("Qt will be installed into '$$system_path($$pfx)'.")
|
|
} else {
|
|
logn("Once everything is built, Qt is installed.")
|
|
logn("You should NOT run '$$QMAKE_MAKE_NAME install'.")
|
|
logn("Note that this build cannot be deployed to other machines or devices.")
|
|
}
|
|
logn()
|
|
logn("Prior to reconfiguration, make sure you remove any leftovers from")
|
|
logn("the previous build.")
|
|
logn()
|