e2446afaa1
The qtPlatformTargetSuffix() function is used in various places to
determine the suffix of targets based on the config, which for macOS
will result in a _debug suffix in debug mode.
This becomes tricky when one project built in debug mode tries to depend
on the libraries/plugins of another project (Qt) built in release, as
the qtPlatformTargetSuffix() function uses the current CONFIG as input,
which may be different than the QT_CONFIG (or CONFIG of whatever project
is being depended on).
For libraries this was fixed in 50e664835b
by iterating all known library paths, and trying the CONFIG suffix before
falling back to release version.
For plugins this was never solved, which becomes an issue when linking
to static plugins, either in a fully static build of Qt, or when some
of the plugins are static (permission plugins e.g.).
In this situation, the user project has to have the same configuration
as Qt was built with, to avoid errors like:
error: no such file or directory: '~/6.x-static/qtbase/plugins/platforms/libqcocoa_debug.a'
To work around this, we assume that a plugin installed into the Qt
tree has the same build configuration as Qt itself, then then use
QT_CONFIG as the determining factor when linking to the plugin.
This still ties the build config of the plugin to the config of Qt,
but relaxes the relationship to the application, allowing it to be
built in either debug or release, which is an improvement to the
current state.
Pick-to: 6.5 6.5.0
Task-number: QTBUG-110356
Change-Id: Icee67fc01313a6c6f34178a6345ccae1b57429d7
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
396 lines
14 KiB
Plaintext
396 lines
14 KiB
Plaintext
|
|
defineReplace(qtPlatformTargetSuffix) {
|
|
config_variable = $$1
|
|
isEmpty(config_variable): \
|
|
config_variable = CONFIG
|
|
|
|
suffix =
|
|
android: return($${suffix}_$${QT_ARCH})
|
|
win32 {
|
|
contains($$config_variable, debug, debug|release) {
|
|
mingw {
|
|
qtConfig(debug_and_release):build_pass: \
|
|
return($${suffix}d)
|
|
} else {
|
|
!debug_and_release|build_pass: \
|
|
return($${suffix}d)
|
|
}
|
|
}
|
|
}
|
|
darwin {
|
|
contains($$config_variable, debug, debug|release) {
|
|
!debug_and_release|build_pass: \
|
|
return($${suffix}_debug)
|
|
}
|
|
}
|
|
return($$suffix)
|
|
}
|
|
|
|
defineReplace(qtLibraryTarget) {
|
|
LIBRARY_NAME = $$1
|
|
CONFIG(shared, static|shared):qtConfig(framework) {
|
|
QMAKE_FRAMEWORK_BUNDLE_NAME = $$LIBRARY_NAME
|
|
export(QMAKE_FRAMEWORK_BUNDLE_NAME)
|
|
}
|
|
return($$LIBRARY_NAME$$qtPlatformTargetSuffix())
|
|
}
|
|
|
|
defineReplace(qt5LibraryTarget) {
|
|
android {
|
|
LIBRARY_NAME_PREFIX = $$2
|
|
LIBRARY_NAME_PREFIX = $$replace(LIBRARY_NAME_PREFIX, "//", "/")
|
|
LIBRARY_NAME_PREFIX = $$replace(LIBRARY_NAME_PREFIX, "/", "_")
|
|
LIBRARY_NAME = $$LIBRARY_NAME_PREFIX$$qtLibraryTarget($$1)
|
|
unset(LIBRARY_NAME_PREFIX)
|
|
} else: LIBRARY_NAME = $$qtLibraryTarget($$1)
|
|
isEmpty(QMAKE_FRAMEWORK_BUNDLE_NAME) {
|
|
# Insert the major version of Qt in the library name
|
|
# unless it's a framework build.
|
|
LIBRARY_NAME ~= s,^Qt,Qt$$QT_MAJOR_VERSION,
|
|
}
|
|
return($$LIBRARY_NAME)
|
|
}
|
|
|
|
defineReplace(qtRelativeRPathBase) {
|
|
darwin {
|
|
if(equals(TEMPLATE, app):app_bundle)|\
|
|
if(equals(TEMPLATE, lib):plugin:plugin_bundle) {
|
|
shallow_bundle: return($$target.path/$${TARGET}.app)
|
|
return($$target.path/$${TARGET}.app/Contents/MacOS)
|
|
}
|
|
equals(TEMPLATE, lib):!plugin:lib_bundle {
|
|
shallow_bundle: return($$target.path/$${TARGET}.framework)
|
|
return($$target.path/$${TARGET}.framework/Versions/Current)
|
|
}
|
|
}
|
|
return($$target.path)
|
|
}
|
|
|
|
defineTest(qtAddLibrary) {
|
|
warning("qtAddLibrary() is deprecated. Use QT+= instead.")
|
|
|
|
# Reverse-engineer the module name from the library name.
|
|
for(var, QT_MODULES) {
|
|
isEqual(QT.$${var}.name, $$1) {
|
|
QT += $$var
|
|
export(QT)
|
|
return(true)
|
|
}
|
|
}
|
|
error("No module matching library '$$1' found.")
|
|
}
|
|
|
|
# qt module
|
|
defineTest(qtHaveModule) {
|
|
!isEmpty(QT.$$replace(1, -, _).name): \
|
|
return(true)
|
|
return(false)
|
|
}
|
|
|
|
# Arguments:
|
|
# variable, default, [suffix for variable for system() use],
|
|
# [prepare primary variable for system() use],
|
|
# [installation location; default: $$[QT_HOST_BINS]]
|
|
defineTest(qtPrepareTool) {
|
|
cmd = $$eval(QT_TOOL.$${2}.binary)
|
|
isEmpty(cmd) {
|
|
isEmpty(5) {
|
|
instloc = $$[QT_HOST_BINS]
|
|
} else {
|
|
instloc = $$5
|
|
}
|
|
cmd = $$instloc/$$2
|
|
exists($${cmd}.pl) {
|
|
$${1}_EXE = $${cmd}.pl
|
|
cmd = perl -w $$system_path($${cmd}.pl)
|
|
} else: contains(QMAKE_HOST.os, Windows) {
|
|
$${1}_EXE = $${cmd}.exe
|
|
cmd = $$system_path($${cmd}.exe)
|
|
} else:contains(QMAKE_HOST.os, Darwin) {
|
|
BUNDLENAME = $${cmd}.app/Contents/MacOS/$$2
|
|
exists($$BUNDLENAME) {
|
|
cmd = $$BUNDLENAME
|
|
}
|
|
$${1}_EXE = $$cmd
|
|
} else {
|
|
$${1}_EXE = $$cmd
|
|
}
|
|
} else {
|
|
$${1}_EXE = $$last(cmd)
|
|
}
|
|
export($${1}_EXE)
|
|
QT_TOOL_ENV += $$eval(QT_TOOL.$${2}.envvars)
|
|
QT_TOOL_NAME = $$2
|
|
!isEmpty(3)|!isEmpty(4) {
|
|
$$1$$3 =
|
|
for (arg, cmd): \
|
|
$$1$$3 += $$system_quote($$arg)
|
|
qtAddTargetEnv($$1$$3, QT_TOOL.$${2}.depends, system)
|
|
}
|
|
isEmpty(4) {
|
|
$$1 =
|
|
for (arg, cmd): \
|
|
$$1 += $$shell_quote($$arg)
|
|
qtAddTargetEnv($$1, QT_TOOL.$${2}.depends, )
|
|
}
|
|
}
|
|
|
|
# Prepare a tool that's not supposed to be called manually by users but by the build system.
|
|
#
|
|
# Forwards its arguments to qtPrepareTool but defaults the installation location to
|
|
# $$[QT_HOST_LIBEXECS]
|
|
defineTest(qtPrepareLibExecTool) {
|
|
isEmpty(instloc): instloc = "$$[QT_HOST_LIBEXECS]"
|
|
qtPrepareTool($$1, $$2, $$3, $$4, $$instloc)
|
|
}
|
|
|
|
# target variable, list of env var names, [non-empty: prepare for system(), not make]
|
|
defineTest(qtAddToolEnv) {
|
|
isEmpty(3): \
|
|
ds = $$QMAKE_DIR_SEP
|
|
else: \
|
|
ds = $$DIR_SEPARATOR
|
|
batch_sets =
|
|
for(env, 2) {
|
|
value = $$eval($${env}.value)
|
|
!isEmpty(value) {
|
|
name = $$eval($${env}.name)
|
|
config = $$eval($${env}.CONFIG)
|
|
equals(ds, /) {
|
|
contains(config, prepend): infix = \${$$name:+:\$$$name}
|
|
else: contains(config, always_prepend): infix = :\$$$name
|
|
else: infix =
|
|
# Under msys, this path is taken only in the non-system()
|
|
# case, so using shell_quote() always works.
|
|
batch_sets += \
|
|
"$$name=$$shell_quote($$join(value, :))$$infix" \
|
|
"export $$name"
|
|
} else {
|
|
value ~= s,\\^,^^^^,g
|
|
value ~= s,!,^^!,g
|
|
value ~= s,\\),^),g
|
|
contains(config, prepend) {
|
|
batch_sets += \
|
|
"if defined $$name (" \
|
|
" set $$name=$$join(value, ;);!$$name!" \
|
|
") else (" \
|
|
" set $$name=$$join(value, ;)" \
|
|
")"
|
|
} else: contains(config, always_prepend) {
|
|
batch_sets += "(set $$name=$$join(value, ;);!$$name!)"
|
|
} else {
|
|
batch_sets += "(set $$name=$$join(value, ;))"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
!isEmpty(batch_sets) {
|
|
batch_name = wrapper
|
|
!isEmpty(QT_TOOL_NAME): batch_name = $${QT_TOOL_NAME}_wrapper
|
|
cmd = $$eval($$1)
|
|
!isEmpty(cmd): cmd = "$$cmd "
|
|
equals(ds, /) {
|
|
batch_name = $${batch_name}.sh
|
|
equals(QMAKE_HOST.os, Darwin):exists(/bin/bash): \
|
|
shell = /bin/bash
|
|
else: \
|
|
shell = /bin/sh
|
|
batch_cont = \
|
|
"$$LITERAL_HASH!$$shell" \
|
|
$$batch_sets \
|
|
"exec $$cmd\"$@\""
|
|
# It would be nicer to use the '.' command (without 'exec' above),
|
|
# but that doesn't set the positional arguments under (d)ash.
|
|
$$1 =
|
|
} else {
|
|
batch_name = $${batch_name}.bat
|
|
batch_cont = \
|
|
"@echo off" \
|
|
"SetLocal EnableDelayedExpansion" \
|
|
$$batch_sets \
|
|
"$$cmd%*" \
|
|
"EndLocal"
|
|
$$1 = call
|
|
}
|
|
!build_pass:!write_file($$OUT_PWD/$$batch_name, batch_cont, exe): error()
|
|
isEmpty(3): \
|
|
$$1 += $$shell_quote($$shell_path($$OUT_PWD/$$batch_name))
|
|
else: \
|
|
$$1 += $$system_quote($$system_path($$OUT_PWD/$$batch_name))
|
|
QMAKE_DISTCLEAN += $$OUT_PWD/$$batch_name
|
|
}
|
|
export($$1)
|
|
export(QMAKE_DISTCLEAN)
|
|
}
|
|
|
|
# target variable, dependency var name, [non-empty: prepare for system(), not make]
|
|
defineTest(qtAddTargetEnv) {
|
|
deps = $$replace($$2, -private$, _private)
|
|
deps = $$resolve_depends(deps, "QT.", ".depends" ".run_depends")
|
|
!isEmpty(deps) {
|
|
libs = libs
|
|
deppath.CONFIG = prepend
|
|
equals(QMAKE_HOST.os, Windows) {
|
|
libs = bins
|
|
deppath.CONFIG = always_prepend
|
|
deppath.name = PATH
|
|
} else:contains(QMAKE_HOST.os, Linux|FreeBSD|OpenBSD|NetBSD|DragonFly|SunOS|HP-UX|QNX|GNU) {
|
|
deppath.name = LD_LIBRARY_PATH
|
|
} else:contains(QMAKE_HOST.os, Haiku) {
|
|
deppath.name = LIBRARY_PATH
|
|
} else:equals(QMAKE_HOST.os, Darwin) {
|
|
qtConfig(framework): \
|
|
deppath.name = DYLD_FRAMEWORK_PATH
|
|
else: \
|
|
deppath.name = DYLD_LIBRARY_PATH
|
|
} else:equals(QMAKE_HOST.os, AIX) {
|
|
deppath.name = LIBPATH
|
|
} else {
|
|
error("Operating system not supported.")
|
|
}
|
|
ptypes =
|
|
for(dep, deps) {
|
|
isEmpty(3): \
|
|
deppath += $$shell_path($$eval(QT.$${dep}.$$libs))
|
|
else: \
|
|
deppath += $$system_path($$eval(QT.$${dep}.$$libs))
|
|
ptypes += $$eval(QT.$${dep}.plugin_types)
|
|
}
|
|
deppath.value = $$unique(deppath)
|
|
|
|
pluginpath.value =
|
|
ppaths = $$[QT_INSTALL_PLUGINS/get]
|
|
for(qplug, QT_PLUGINS): \
|
|
contains(ptypes, $$eval(QT_PLUGIN.$${qplug}.TYPE)): \
|
|
ppaths += $$eval(QT_PLUGIN.$${qplug}.PATH)
|
|
ppaths = $$unique(ppaths)
|
|
for(qplug, ppaths) {
|
|
isEmpty(3): \
|
|
pluginpath.value += $$shell_path($$qplug)
|
|
else: \
|
|
pluginpath.value += $$system_path($$qplug)
|
|
}
|
|
pluginpath.name = QT_PLUGIN_PATH
|
|
pluginpath.CONFIG = prepend
|
|
|
|
QT_TOOL_ENV += deppath pluginpath
|
|
}
|
|
qtAddToolEnv($$1, $$QT_TOOL_ENV, $$3)
|
|
}
|
|
|
|
defineReplace(pkgConfigExecutable) {
|
|
isEmpty(PKG_CONFIG) {
|
|
!isEmpty(QMAKE_PKG_CONFIG) {
|
|
# Assumed to be properly sysrootified.
|
|
PKG_CONFIG = $$QMAKE_PKG_CONFIG
|
|
export(PKG_CONFIG)
|
|
} else {
|
|
PKG_CONFIG = pkg-config
|
|
!cross_compile {
|
|
export(PKG_CONFIG)
|
|
} else {
|
|
# Cross compiling, ensure that pkg-config is set up sanely.
|
|
sysroot = $$[QT_SYSROOT]
|
|
|
|
pkgConfigLibdir = $$(PKG_CONFIG_LIBDIR)
|
|
isEmpty(pkgConfigLibdir) {
|
|
isEmpty(sysroot) {
|
|
warning("Cross compiling without sysroot. Disabling pkg-config.")
|
|
return()
|
|
}
|
|
!exists("$$sysroot/usr/lib/pkgconfig") {
|
|
warning("Disabling pkg-config since PKG_CONFIG_LIBDIR is not set and the")
|
|
warning("host's .pc files would be used (even if you set PKG_CONFIG_PATH).")
|
|
warning("Set this variable to the directory that contains target .pc files")
|
|
warning("for pkg-config to function correctly when cross-compiling.")
|
|
return()
|
|
}
|
|
|
|
pkgConfigLibdir = $$system_path($$sysroot/usr/lib/pkgconfig)$$QMAKE_DIRLIST_SEP$$system_path($$sysroot/usr/share/pkgconfig)
|
|
!isEmpty(GCC_MACHINE_DUMP): \
|
|
pkgConfigLibdir = "$$pkgConfigLibdir$$QMAKE_DIRLIST_SEP$$system_path($$sysroot/usr/lib/$$GCC_MACHINE_DUMP/pkgconfig)"
|
|
message("PKG_CONFIG_LIBDIR automatically set to $$pkgConfigLibdir")
|
|
}
|
|
pkgConfigSysrootDir = $$(PKG_CONFIG_SYSROOT_DIR)
|
|
isEmpty(pkgConfigSysrootDir) {
|
|
isEmpty(sysroot) {
|
|
warning("Disabling pkg-config since PKG_CONFIG_SYSROOT_DIR is not set.")
|
|
warning("Set this variable to your sysroot for pkg-config to function")
|
|
warning("correctly when cross-compiling.")
|
|
return()
|
|
}
|
|
|
|
pkgConfigSysrootDir = $$sysroot
|
|
message("PKG_CONFIG_SYSROOT_DIR automatically set to $$pkgConfigSysrootDir")
|
|
}
|
|
|
|
sysroot.name = PKG_CONFIG_SYSROOT_DIR
|
|
sysroot.value = $$pkgConfigSysrootDir
|
|
libdir.name = PKG_CONFIG_LIBDIR
|
|
libdir.value = $$pkgConfigLibdir
|
|
QT_TOOL_NAME = pkg-config
|
|
qtAddToolEnv(PKG_CONFIG, sysroot libdir, SYS)
|
|
}
|
|
}
|
|
}
|
|
|
|
PKG_CONFIG += 2> $$QMAKE_SYSTEM_NULL_DEVICE
|
|
|
|
return($$PKG_CONFIG)
|
|
}
|
|
|
|
defineTest(packagesExist) {
|
|
# this can't be done in global scope here because qt_functions is loaded
|
|
# before the .pro is parsed, so if the .pro set PKG_CONFIG, we wouldn't know it
|
|
# yet. oops.
|
|
pkg_config = $$pkgConfigExecutable()
|
|
|
|
for(package, ARGS) {
|
|
!system($$pkg_config --exists $$package):return(false)
|
|
}
|
|
|
|
return(true)
|
|
}
|
|
|
|
# Prepares target that will iterate through all subdirs (except those
|
|
# with no_default_target or no_{name_of_target}_target. The prepared
|
|
# target must still be manually added to QMAKE_EXTRA_TARGETS.
|
|
defineTest(prepareRecursiveTarget) {
|
|
target = $$1
|
|
no_$${target}_target: return()
|
|
|
|
for(subdir, SUBDIRS) {
|
|
subdir_config = $$eval($${subdir}.CONFIG)
|
|
contains(subdir_config, no_default_target): next()
|
|
contains(subdir_config, no_$${target}_target): next()
|
|
|
|
$${target}.recurse += $$subdir
|
|
}
|
|
|
|
# Set up the recurse target only when there
|
|
# is something to recurse into.
|
|
isEmpty($${target}.recurse): return()
|
|
|
|
$${target}.CONFIG = recursive
|
|
$${target}.recurse_target = $${target}
|
|
|
|
export($${target}.recurse)
|
|
export($${target}.CONFIG)
|
|
export($${target}.recurse_target)
|
|
}
|
|
|
|
defineTest(qtConfig) {
|
|
modules = $$QT $$QT_PRIVATE $$QT_FOR_CONFIG
|
|
modules ~= s,-private$,_private,g
|
|
modules = $$resolve_depends(modules, "QT.", ".depends")
|
|
modules += global global_private
|
|
modules = $$reverse(modules)
|
|
for (module, modules) {
|
|
contains(QT.$${module}.enabled_features, $$1): \
|
|
return(true)
|
|
contains(QT.$${module}.disabled_features, $$1): \
|
|
return(false)
|
|
}
|
|
error("Could not find feature $${1}.")
|
|
}
|