qt5base-lts/mkspecs/features/toolchain.prf
Tor Arne Vestbø cdf64b0e47 qmake: Don't error out if QMAKE_DEFAULT_LIBDIRS is empty on macOS
The new linker in Xcode 15 doesn't provide any default linker or
framework paths when requested via -v, but still seems to use the
default paths documented in the ld man page.

We trust that linker will do the right thing, even if we don't
know of its default linker paths.

We also need to opt out of the default fallback logic to
set the libdirs to /lib and /usr/lib.

This may result in UnixMakefileGenerator::findLibraries finding
different libraries than expected, if additional paths are
passed with -L, which will then take precedence for qmake,
even if the linker itself will use the library from the
SDK's default paths. This should hopefully not be an issue
in practice, as we don't turn -lFoo into absolute paths in
qmake, so the only risk is that we're picking up the wrong
prl files and adding additional dependencies that the lib
in the SDK doesn't have.

Pick-to: 6.6 6.6.0
Change-Id: I2347b26e2df0828471373b0e15b8c9089274c65d
Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
2023-09-13 16:22:03 +00:00

446 lines
18 KiB
Plaintext

defineTest(qtToolchainError) {
msg = \
$$1 \
"===================" \
$$2 \
"===================" \
$$3
error($$join(msg, $$escape_expand(\\n)))
}
defineTest(qtCompilerError) {
!cross_compile: \
what =
else: host_build: \
what = " host"
else: \
what = " target"
qtToolchainError("Cannot run$$what compiler '$$1'. Output:", $$2, \
"Maybe you forgot to setup the environment?")
}
cross_compile:host_build: \
target_prefix = QMAKE_HOST_CXX
else: \
target_prefix = QMAKE_CXX
#
# Determine and cache the compiler version
#
defineReplace(qtVariablesFromMSVC) {
ret = $$system("$$1 -nologo -E $$2 $$system_quote($$PWD/data/macros.cpp) 2>NUL", lines, ec)
!equals(ec, 0): qtCompilerError($$1, $$ret)
return($$ret)
}
defineReplace(qtVariablesFromGCC) {
ret = $$system("$$1 -E $$system_quote($$PWD/data/macros.cpp) \
2>$$QMAKE_SYSTEM_NULL_DEVICE", lines, ec)
!equals(ec, 0): qtCompilerError($$1, $$ret)
return($$ret)
}
isEmpty($${target_prefix}.COMPILER_MACROS) {
msvc {
clang_cl {
# We need to obtain the cl.exe version first
vars = $$qtVariablesFromMSVC(cl)
for (v, vars) {
isEmpty(v)|contains(v, $${LITERAL_HASH}.*): next()
eval($$v)
}
isEmpty(QMAKE_MSC_FULL_VER): error("Could not determine the Visual Studio version")
QMAKE_CFLAGS_MSVC_COMPAT = $$replace(QMAKE_MSC_FULL_VER, "(..)(..)(.*)", \
"-fms-compatibility-version=\\1.\\2.\\3")
cache($${target_prefix}.QMAKE_CFLAGS_MSVC_COMPAT, set stash, QMAKE_CFLAGS_MSVC_COMPAT)
$${target_prefix}.COMPILER_MACROS += QMAKE_CFLAGS_MSVC_COMPAT
vars = $$qtVariablesFromMSVC($$QMAKE_CXX, $$QMAKE_CFLAGS_MSVC_COMPAT)
} else {
vars = $$qtVariablesFromMSVC($$QMAKE_CXX)
}
} else: gcc|ghs {
vars = $$qtVariablesFromGCC($$QMAKE_CXX)
}
for (v, vars) {
!contains(v, "[A-Z_]+ = .*"): next()
# Set both <varname> for the outer scope ...
eval($$v)
v ~= s/ .*//
isEmpty($$v): error("Compiler produced empty value for $${v}.")
# ... and save QMAKE_(HOST_)?CXX.<varname> in the cache.
cache($${target_prefix}.$$v, set stash, $$v)
$${target_prefix}.COMPILER_MACROS += $$v
}
cache($${target_prefix}.COMPILER_MACROS, set stash)
} else {
# load from the cache
for (i, $${target_prefix}.COMPILER_MACROS): \
$$i = $$eval($${target_prefix}.$$i)
}
# Populate QMAKE_COMPILER_DEFINES and some compatibility variables.
# The $$format_number() calls strip leading zeros to avoid misinterpretation as octal.
QMAKE_COMPILER_DEFINES += __cplusplus=$$QT_COMPILER_STDCXX
!isEmpty(QMAKE_MSC_VER): \
QMAKE_COMPILER_DEFINES += _MSC_VER=$$QMAKE_MSC_VER _MSC_FULL_VER=$$QMAKE_MSC_FULL_VER
!isEmpty(QMAKE_ICC_VER): \
QMAKE_COMPILER_DEFINES += __INTEL_COMPILER=$$QMAKE_ICC_VER __INTEL_COMPILER_UPDATE=$$QMAKE_ICC_UPDATE_VER
!isEmpty(QMAKE_APPLE_CC): \
QMAKE_COMPILER_DEFINES += __APPLE_CC__=$$QMAKE_APPLE_CC
!isEmpty(QMAKE_APPLE_CLANG_MAJOR_VERSION): \
QMAKE_COMPILER_DEFINES += __clang__ \
__clang_major__=$$QMAKE_APPLE_CLANG_MAJOR_VERSION \
__clang_minor__=$$QMAKE_APPLE_CLANG_MINOR_VERSION \
__clang_patchlevel__=$$QMAKE_APPLE_CLANG_PATCH_VERSION
!isEmpty(QMAKE_CLANG_MAJOR_VERSION): \
QMAKE_COMPILER_DEFINES += __clang__ \
__clang_major__=$$QMAKE_CLANG_MAJOR_VERSION \
__clang_minor__=$$QMAKE_CLANG_MINOR_VERSION \
__clang_patchlevel__=$$QMAKE_CLANG_PATCH_VERSION
!isEmpty(QMAKE_GCC_MAJOR_VERSION): \
QMAKE_COMPILER_DEFINES += \
__GNUC__=$$QMAKE_GCC_MAJOR_VERSION \
__GNUC_MINOR__=$$QMAKE_GCC_MINOR_VERSION \
__GNUC_PATCHLEVEL__=$$QMAKE_GCC_PATCH_VERSION
!isEmpty(QMAKE_GHS_VERSION): \
QMAKE_COMPILER_DEFINES += __ghs__ __GHS_VERSION_NUMBER=$$QMAKE_GHS_VERSION
QMAKE_CFLAGS += $$QMAKE_CFLAGS_MSVC_COMPAT
QMAKE_CXXFLAGS += $$QMAKE_CFLAGS_MSVC_COMPAT
clang_cl|intel_icl {
include(../common/msvc-based-version.conf)
} else: msvc {
include(../common/msvc-version.conf)
}
#
# Determine and cache the default search paths
#
defineReplace(qtMakeExpand) {
out = "$$1"
for(ever) {
m = $$replace(out, ".*\\$\\(EXPORT_([^)]+)\\).*", \\1)
equals(m, $$out): \
return($$out)
out = $$replace(out, "\\$\\(EXPORT_$$m\\)", $$eval($$m))
}
}
defineReplace(qtSplitPathList) {
paths = $$split(1, $$QMAKE_DIRLIST_SEP)
ret =
for (p, paths): \
ret += $$clean_path($$p)
return($$ret)
}
defineReplace(qtNmakePathList) {
paths =
for (p, 1): \
paths += $$shell_path($$p)
paths ~= s,$${LITERAL_HASH},^$${LITERAL_HASH},g
paths ~= s,\\$,\$\$,g
return($$join(paths, $$QMAKE_DIRLIST_SEP))
}
msvc {
arch = $$lower($$VCPROJ_ARCH)
equals(arch, x64): \ # may be "win32" or undefined
arch = amd64
else: !equals(arch, arm):!equals(arch, arm64): \ # may be "win32" or undefined
arch = x86
# Consider only ARM64 desktop builds to be cross-builds -
# the host is assumed to be Intel and capable of running the target
# executables (so building for x64 on x86 will break).
equals(arch, arm64): \
CONFIG += msvc_cross
}
isEmpty($${target_prefix}.INCDIRS) {
#
# Get default include and library paths from compiler
#
wasm {
# wasm compiler does not work here, just use defaults
} else: gcc {
cmd_suffix = "<$$QMAKE_SYSTEM_NULL_DEVICE >$$QMAKE_SYSTEM_NULL_DEVICE"
equals(QMAKE_HOST.os, Windows): \
cmd_prefix = "set LC_ALL=C&"
else: \
cmd_prefix = "LC_ALL=C"
cxx_flags = $$QMAKE_CXXFLAGS
# Manually inject the sysroot for Apple Platforms because its resolution
# normally does not happen until default_post.prf. This is especially
# important for moc to gain the correct default include directory list.
# While technically incorrect but without any likely practical effect,
# UIKit simulator platforms will see the device SDK's sysroot in
# QMAKE_DEFAULT_*DIRS, because they're handled in a single build pass.
darwin {
uikit {
# Clang doesn't automatically pick up the architecture, just because
# we're passing the iOS sysroot below, and we will end up building the
# test for the host architecture, resulting in linker errors when
# linking against the iOS libraries. We work around this by passing
# the architecture explicitly.
cxx_flags += -arch $$first(QMAKE_APPLE_DEVICE_ARCHS)
}
uikit:macx-xcode: \
cxx_flags += -isysroot $$sdk_path_device.value
else: \
cxx_flags += -isysroot $$QMAKE_MAC_SDK_PATH
}
rim_qcc: \
# Need the cc1plus and ld command lines to pick up the paths
cxx_flags += $$QMAKE_LFLAGS_SHLIB -o $$QMAKE_SYSTEM_NULL_DEVICE -v
else: darwin:clang: \
# Need to link to pick up library paths
cxx_flags += -g0 $$QMAKE_LFLAGS_SHLIB -o /dev/null -v -Wl,-v
else: \
# Just preprocess, might not pick up library paths
cxx_flags += -E -v
output = $$system("$$cmd_prefix $$QMAKE_CXX $$qtMakeExpand($$cxx_flags) -xc++ - 2>&1 $$cmd_suffix", lines, ec)
!equals(ec, 0): qtCompilerError($$QMAKE_CXX, $$output)
rim_qcc {
for (line, output) {
contains(line, "^[^ ]*cc1plus .*") {
take_next = false
for (parameter, $$list($$line)) {
$$take_next {
QMAKE_DEFAULT_INCDIRS += $$clean_path($$parameter)
take_next = false
} else: equals(parameter, "-isystem") {
take_next = true
}
}
} else: contains(line, "^[^ ]*-ld .*") {
for (parameter, $$list($$line)) {
contains(parameter, "^-L.*") {
parameter ~= s/^-L//
QMAKE_DEFAULT_LIBDIRS += $$clean_path($$parameter)
}
}
}
}
} else {
add_includes = false
add_libraries = false
for (line, output) {
line ~= s/^[ \\t]*// # remove leading spaces
contains(line, "LIBRARY_PATH=.*") {
line ~= s/^LIBRARY_PATH=// # remove leading LIBRARY_PATH=
equals(QMAKE_HOST.os, Windows): \
paths = $$split(line, ;)
else: \
paths = $$split(line, $$QMAKE_DIRLIST_SEP)
for (path, paths): \
QMAKE_DEFAULT_LIBDIRS += $$clean_path($$path)
} else: contains(line, "Library search paths:") {
add_libraries = true
} else: contains(line, "$${LITERAL_HASH}include <.*") { # #include <...> search starts here:
add_includes = true
} else: contains(line, "End of search.*") {
add_includes = false
} else: $$add_libraries {
# We assume all library search paths are absolute
!contains(line, "^/.*") {
add_libraries = false
next()
}
QMAKE_DEFAULT_LIBDIRS += $$clean_path($$line)
} else: $$add_includes {
!contains(line, ".* \\(framework directory\\)"): \
QMAKE_DEFAULT_INCDIRS += $$clean_path($$line)
}
}
}
if(!darwin:clang)|intel_icc {
# Clang on a non-Apple system (that is, a system without ld64 -- say, with GNU ld
# or gold under Linux) will not print any library search path. Need to use another
# invocation with different options (which in turn doesn't print include search
# paths, so it can't just be used in place of the above code).
# What's more, -print-search-dirs can't be used on clang on Apple because it
# won't print all the library paths (only the clang-internal ones).
output = $$system("$$cmd_prefix $$QMAKE_LINK $$QMAKE_LFLAGS -print-search-dirs", lines, ec)
!equals(ec, 0): qtCompilerError($$QMAKE_LINK, $$output)
for (line, output) {
contains(line, "^libraries: .*") {
line ~= s,^libraries: ,,
equals(QMAKE_HOST.os, Windows) {
# clang (7.x) on Windows uses the wrong path list separator ...
line ~= s,:(?![/\\\\]),;,
paths = $$split(line, ;)
} else {
paths = $$split(line, $$QMAKE_DIRLIST_SEP)
}
for (path, paths): \
QMAKE_DEFAULT_LIBDIRS += $$clean_path($$replace(path, ^=, $$[SYSROOT]))
}
}
}
isEmpty(QMAKE_DEFAULT_INCDIRS): \
!integrity: \
error("failed to parse default include paths from compiler output")
isEmpty(QMAKE_DEFAULT_LIBDIRS): \
!integrity:!darwin: \
error("failed to parse default library paths from compiler output")
QMAKE_DEFAULT_LIBDIRS = $$unique(QMAKE_DEFAULT_LIBDIRS)
} else: ghs {
cmd = $$QMAKE_CXX $$QMAKE_CXXFLAGS -$${LITERAL_HASH} -o /tmp/fake_output /tmp/fake_input.cpp
output = $$system("$$cmd", blob, ec)
!equals(ec, 0): qtCompilerError($$QMAKE_CXX, $$output)
output ~= s/\\\\\\n {8}//g
output = $$split(output, $$escape_expand(\\n))
for (line, output) {
contains(line, "^[^ ]+/ecom[^ ]+ .* /tmp/fake_input\\.cpp") {
for (parameter, $$list($$line)) {
contains(parameter, "^(-I|--include_no_mmd=|--sys_include=).*") {
parameter ~= s/^(-I|--include_no_mmd=|--sys_include=)//
QMAKE_DEFAULT_INCDIRS += $$clean_path($$parameter)
}
}
} else: contains(line, "^[^ ]+/elxr .*") {
for (parameter, $$list($$line)) {
contains(parameter, "^-L.*") {
parameter ~= s/^-L//
QMAKE_DEFAULT_LIBDIRS += $$clean_path($$parameter)
}
}
}
}
} else: msvc_cross {
# Use a batch file, because %VAR% in the system() call expands to
# the pre-script-call value, and !VAR! cannot be enabled outside
# a batch file without invoking another shell instance.
cmd = $$system_quote($$system_path($$PWD/data/dumpvcvars.bat))
hostArch = $$QMAKE_HOST.arch
equals(hostArch, x86_64): \
hostArch = amd64
!equals(arch, $$hostArch): \
arch = $${hostArch}_$$arch
isEmpty(MSVC_VER): \
error("Mkspec does not specify MSVC_VER. Cannot continue.")
versionAtLeast(MSVC_VER, 15.0) {
dir = $$(VSINSTALLDIR)
isEmpty(dir) {
version_parts = $$split(MSVC_VER, .)
MSVC_NEXT_MAJOR = $$num_add($$first(version_parts), 1)
vswhere = "$$getenv(ProgramFiles\(x86\))/Microsoft Visual Studio/Installer/vswhere.exe"
!exists($$vswhere): \
error("Could not find $$vswhere")
vswhere = $$system_quote($$system_path($$vswhere))
# -version parameter: A version range for instances to find. 15.0 will get all versions >= 15.0
# Example: [15.0,16.0) will find versions 15.*.
dir = $$system("$$vswhere -latest -version [$$MSVC_VER,$${MSVC_NEXT_MAJOR}.0] -property installationPath")
}
isEmpty(dir): \
error("Failed to find the Visual Studio installation directory.")
cmd += $$system_quote($$dir\\VC\\Auxiliary\\Build\\vcvarsall.bat) $$arch
} else {
dir = $$(VCINSTALLDIR)
isEmpty(dir): \
dir = $$read_registry(HKLM, \
"Software\\Microsoft\\VisualStudio\\$$MSVC_VER\\Setup\\VC\\ProductDir", 32)
isEmpty(dir): \
error("Failed to find the Visual C installation directory.")
cmd += $$system_quote($$dir\\vcvarsall.bat) $$arch
}
isEmpty(WINSDK_VER): \
error("Mkspec does not specify WINSDK_VER. Cannot continue.")
# We prefer the environment variable, because that may work around
# a broken registry entry after uninstalling a newer SDK.
# However, we do that only if the major+minor SDK version matches
# the one requested by the mkspec, as we might be building for a
# newer target than the host.
winsdk_ver = $$(WindowsSDKVersion)
!isEmpty(winsdk_ver) {
winsdk_ver ~= s,\\\\$,, # Work around SDK breakage.
!equals(WINSDK_VER, $$replace(winsdk_ver, ^(\\d+\\.\\d+).*$, \\1)): \
winsdk_ver =
}
!isEmpty(winsdk_ver) {
cmd += $$winsdk_ver
} else {
winsdk_ver = $$read_registry(HKLM, \
"Software\\Microsoft\\Microsoft SDKs\\Windows\\v$$WINSDK_VER\\ProductVersion", 32)
isEmpty(winsdk_ver): \
error("Windows SDK $$WINSDK_VER requested by mkspec is not installed. Cannot continue.")
cmd += $${winsdk_ver}.0
}
output = $$system("$$cmd 2>&1", lines, ec)
!equals(ec, 0): \
qtToolchainError("SDK setup script failed. Output:", $$output, \
"Command was: $$cmd")
lines = $$output
for(ever) {
isEmpty(lines): \
break()
line = $$take_first(lines)
equals(line, "=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+="): \
break()
}
!count(lines, 3): \
qtToolchainError("SDK setup script returned unexpected output:", $$output, \
"Command was: $$cmd")
# These contain only paths for the target.
QMAKE_DEFAULT_INCDIRS = $$qtSplitPathList($$member(lines, 0))
QMAKE_DEFAULT_LIBDIRS = $$qtSplitPathList($$member(lines, 1))
# PATH is inherently for the host, and paths that are not shadowed
# by vcvarsall.bat are assumed to contain only tools that work for
# both host and target builds.
QMAKE_DEFAULT_PATH = $$qtSplitPathList($$member(lines, 2))
# We de-duplicate, because the script just prepends to the paths for
# the host, some of which are identical to the ones for the target.
QMAKE_DEFAULT_PATH = $$unique(QMAKE_DEFAULT_PATH)
} else: msvc {
LIB = $$getenv("LIB")
QMAKE_DEFAULT_LIBDIRS = $$split(LIB, $$QMAKE_DIRLIST_SEP)
INCLUDE = $$getenv("INCLUDE")
QMAKE_DEFAULT_INCDIRS = $$split(INCLUDE, $$QMAKE_DIRLIST_SEP)
}
unix:!darwin:if(!cross_compile|host_build) {
isEmpty(QMAKE_DEFAULT_INCDIRS): QMAKE_DEFAULT_INCDIRS = /usr/include /usr/local/include
isEmpty(QMAKE_DEFAULT_LIBDIRS): QMAKE_DEFAULT_LIBDIRS = /lib /usr/lib
}
# cache() complains about undefined variables and doesn't persist empty ones.
!isEmpty(QMAKE_DEFAULT_INCDIRS): \
cache($${target_prefix}.INCDIRS, set stash, QMAKE_DEFAULT_INCDIRS)
!isEmpty(QMAKE_DEFAULT_LIBDIRS): \
cache($${target_prefix}.LIBDIRS, set stash, QMAKE_DEFAULT_LIBDIRS)
!isEmpty(QMAKE_DEFAULT_PATH): \
cache($${target_prefix}.PATH, set stash, QMAKE_DEFAULT_PATH)
} else {
QMAKE_DEFAULT_INCDIRS = $$eval($${target_prefix}.INCDIRS)
QMAKE_DEFAULT_LIBDIRS = $$eval($${target_prefix}.LIBDIRS)
QMAKE_DEFAULT_PATH = $$eval($${target_prefix}.PATH)
}
msvc_cross {
qmake_inc_exp.name = INCLUDE
qmake_inc_exp.value = $$qtNmakePathList($$QMAKE_DEFAULT_INCDIRS)
qmake_lib_exp.name = LIB
qmake_lib_exp.value = $$qtNmakePathList($$QMAKE_DEFAULT_LIBDIRS)
qmake_path_exp.name = PATH
qmake_path_exp.value = $$qtNmakePathList($$QMAKE_DEFAULT_PATH)
QMAKE_EXPORTED_VARIABLES += qmake_inc_exp qmake_lib_exp qmake_path_exp
}
unset(target_prefix)