45e4dfb449
rather than reproducing vcvarsall.bat's functionality as hard-wired code in the nmake generator, just invoke the actual script from toolchain.prf. this is much easier, more future proof, and - critically - makes the detected variables available to configure's new library & header search facilities. [ChangeLog][Important Behavior Changes][qmake][WinRT] Cross-builds will now ignore pre-set values of %INCLUDE% and %LIB% when building target executables. If necessary, use configure's -I and -L switches when building Qt, and pass QMAKE_INCDIR and QMAKE_LIBDIR on qmake's command line when building own projects. Change-Id: I36f53e8880d6523f3f6f7a44d40d87d04bd06854 Reviewed-by: Thomas Miller <thomaslmiller91@gmail.com> Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
420 lines
17 KiB
Plaintext
420 lines
17 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 WinRT and 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).
|
|
winrt|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: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 += $$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=
|
|
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: ,,
|
|
# clang (7.x) on Windows uses the wrong path list separator ...
|
|
equals(QMAKE_HOST.os, Windows): line ~= s,:(?![/\\\\]),;,
|
|
paths = $$split(line, $$QMAKE_DIRLIST_SEP)
|
|
for (path, paths): \
|
|
QMAKE_DEFAULT_LIBDIRS += $$clean_path($$replace(path, ^=, $$[SYSROOT]))
|
|
}
|
|
}
|
|
}
|
|
isEmpty(QMAKE_DEFAULT_LIBDIRS)|isEmpty(QMAKE_DEFAULT_INCDIRS): \
|
|
!integrity: \
|
|
error("failed to parse default search 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): \
|
|
dir = $$read_registry(HKLM, \
|
|
"Software\\Microsoft\\VisualStudio\\SxS\\VS7\\$$MSVC_VER", 32)
|
|
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
|
|
}
|
|
winrt: cmd += store
|
|
|
|
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: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)
|