diff options
author | Ivan Komissarov <abbapoh@gmail.com> | 2019-12-12 20:47:27 +0100 |
---|---|---|
committer | Ivan Komissarov <ABBAPOH@gmail.com> | 2020-01-24 09:11:04 +0000 |
commit | 9c282ff7bd13b25118da3e6d7a46e75fe78caf4c (patch) | |
tree | 84bbea00869bd2902e91ffdd464b4ff27db339ce /share | |
parent | 1e7875f00e406b762065f0ea2fe30836829fdd42 (diff) |
MSVC: Use compiler driver for linking
To be able to use cpp.driverFlags and cpp.driverLinkerFlags with clang-
cl. This patchset makes possible to use clang-cl with "-
fsanitize=address" flag without passing the sanitizer libraries manually
to the linker
There's also a behavior change in which linker is used - clang-cl uses
native linker by default. Old behavior can be restored by setting
cpp.linkerVariant to "lld"
Fixes: QBS-1522
Change-Id: I9528ce40aa5fdfab987672b15fffd830fa2d6376
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Diffstat (limited to 'share')
-rw-r--r-- | share/qbs/modules/cpp/CppModule.qbs | 15 | ||||
-rw-r--r-- | share/qbs/modules/cpp/GenericGCC.qbs | 15 | ||||
-rw-r--r-- | share/qbs/modules/cpp/msvc.js | 152 | ||||
-rw-r--r-- | share/qbs/modules/cpp/windows-clang-cl.qbs | 11 |
4 files changed, 133 insertions, 60 deletions
diff --git a/share/qbs/modules/cpp/CppModule.qbs b/share/qbs/modules/cpp/CppModule.qbs index bdd6d2750..173c3f3e5 100644 --- a/share/qbs/modules/cpp/CppModule.qbs +++ b/share/qbs/modules/cpp/CppModule.qbs @@ -193,6 +193,21 @@ Module { property bool discardUnusedData property bool removeDuplicateLibraries: true + property string linkerMode: "automatic" + PropertyOptions { + name: "linkerMode" + allowedValues: ["automatic", "manual"] + description: "Controls whether to automatically use an appropriate compiler frontend " + + "in place of the system linker when linking binaries. The default is \"automatic\", " + + "which chooses either the C++ compiler, C compiler, or system linker specified by " + + "the linkerName/linkerPath properties, depending on the type of object files " + + "present on the linker command line. \"manual\" allows you to explicitly specify " + + "the linker using the linkerName/linkerPath properties, and allows linker flags " + + "passed to the linkerFlags and platformLinkerFlags properties to be escaped " + + "manually (using -Wl or -Xlinker) instead of automatically based on the selected " + + "linker." + } + property stringList assemblerFlags PropertyOptions { name: "assemblerFlags" diff --git a/share/qbs/modules/cpp/GenericGCC.qbs b/share/qbs/modules/cpp/GenericGCC.qbs index 63d5db7b8..80de576a9 100644 --- a/share/qbs/modules/cpp/GenericGCC.qbs +++ b/share/qbs/modules/cpp/GenericGCC.qbs @@ -148,21 +148,6 @@ CppModule { property string syslibroot: sysroot property stringList sysrootFlags: sysroot ? ["--sysroot=" + sysroot] : [] - property string linkerMode: "automatic" - PropertyOptions { - name: "linkerMode" - allowedValues: ["automatic", "manual"] - description: "Controls whether to automatically use an appropriate compiler frontend " - + "in place of the system linker when linking binaries. The default is \"automatic\", " - + "which chooses either the C++ compiler, C compiler, or system linker specified by " - + "the linkerName/linkerPath properties, depending on the type of object files " - + "present on the linker command line. \"manual\" allows you to explicitly specify " - + "the linker using the linkerName/linkerPath properties, and allows linker flags " - + "passed to the linkerFlags and platformLinkerFlags properties to be escaped " - + "manually (using -Wl or -Xlinker) instead of automatically based on the selected " - + "linker." - } - property string exportedSymbolsCheckMode: "ignore-undefined" PropertyOptions { name: "exportedSymbolsCheckMode" diff --git a/share/qbs/modules/cpp/msvc.js b/share/qbs/modules/cpp/msvc.js index dead67b43..479f7f416 100644 --- a/share/qbs/modules/cpp/msvc.js +++ b/share/qbs/modules/cpp/msvc.js @@ -35,6 +35,22 @@ var ModUtils = require("qbs.ModUtils"); var Utilities = require("qbs.Utilities"); var WindowsUtils = require("qbs.WindowsUtils"); +function effectiveLinkerPath(product, inputs) { + if (product.cpp.linkerMode === "automatic") { + var compiler = product.cpp.compilerPath; + if (compiler) { + if (inputs.obj || inputs.staticlibrary) { + console.log("Found C/C++ objects, using compiler as a linker for " + product.name); + return compiler; + } + } + + console.log("Found no C-language objects, choosing system linker for " + product.name); + } + + return product.cpp.linkerPath; +} + function handleCpuFeatures(input, flags) { if (!input.qbs.architecture) return; @@ -90,6 +106,15 @@ function addLanguageVersionFlag(input, args) { args.push(flag); } +function handleClangClArchitectureFlags(product, architecture, flags) { + if (product.qbs.toolchain.contains("clang-cl")) { + if (architecture === "x86") + flags.push("-m32"); + else if (architecture === "x86_64") + flags.push("-m64"); + } +} + function prepareCompiler(project, product, inputs, outputs, input, output, explicitlyDependsOn) { var i; var debugInformation = input.cpp.debugInformation; @@ -135,12 +160,7 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli break; } - if (input.qbs.toolchain.contains("clang-cl")) { - if (input.cpp.architecture === "x86") - args.push("-m32"); - else if (input.cpp.architecture === "x86_64") - args.push("-m64"); - } + handleClangClArchitectureFlags(product, input.cpp.architecture, args); if (debugInformation) { if (product.cpp.separateDebugInformation) @@ -157,6 +177,10 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli args.push(rtl); } + var driverFlags = product.cpp.driverFlags; + if (driverFlags) + args = args.concat(driverFlags); + // warnings: var warningLevel = input.cpp.warningLevel; if (warningLevel === 'none') @@ -368,44 +392,71 @@ function prepareLinker(project, product, inputs, outputs, input, output) { return a.filePath; }); var generateManifestFiles = !linkDLL && product.cpp.generateManifestFile; - var canEmbedManifest = (product.cpp.compilerVersionMajor >= 17); // VS 2012 + var useClangCl = product.qbs.toolchain.contains("clang-cl"); + var canEmbedManifest = useClangCl || product.cpp.compilerVersionMajor >= 17 // VS 2012 + + var linkerPath = effectiveLinkerPath(product, inputs); + var useCompilerDriver = linkerPath === product.cpp.compilerPath; + // args variable is built as follows: + // [linkerWrapper] linkerPath /nologo [driverFlags driverLinkerFlags] + // allInputs libDeps [/link] linkerArgs + var args = [] + + if (useCompilerDriver) { + args.push('/nologo'); + var driverFlags = product.cpp.driverFlags; + if (driverFlags) + args = args.concat(driverFlags); + var driverLinkerFlags = product.cpp.driverLinkerFlags; + if (driverLinkerFlags) + args = args.concat(driverLinkerFlags); + } + + var allInputs = inputs.obj || []; + for (i in allInputs) { + var fileName = FileInfo.toWindowsSeparators(allInputs[i].filePath) + args.push(fileName) + } - var args = ['/nologo'] + var linkerArgs = ['/nologo'] if (linkDLL) { - args.push('/DLL'); - args.push('/IMPLIB:' + FileInfo.toWindowsSeparators(outputs.dynamiclibrary_import[0].filePath)); + linkerArgs.push('/DLL'); + linkerArgs.push('/IMPLIB:' + FileInfo.toWindowsSeparators(outputs.dynamiclibrary_import[0].filePath)); } if (debugInformation) { - args.push("/DEBUG"); + linkerArgs.push("/DEBUG"); var debugInfo = outputs.debuginfo_app || outputs.debuginfo_dll; if (debugInfo) - args.push("/PDB:" + debugInfo[0].fileName); + linkerArgs.push("/PDB:" + debugInfo[0].fileName); } else { - args.push('/INCREMENTAL:NO') + linkerArgs.push('/INCREMENTAL:NO') } switch (product.qbs.architecture) { case "x86": - args.push("/MACHINE:X86"); + linkerArgs.push("/MACHINE:X86"); break; case "x86_64": - args.push("/MACHINE:X64"); + linkerArgs.push("/MACHINE:X64"); break; case "ia64": - args.push("/MACHINE:IA64"); + linkerArgs.push("/MACHINE:IA64"); break; case "armv7": - args.push("/MACHINE:ARM"); + linkerArgs.push("/MACHINE:ARM"); break; case "arm64": - args.push("/MACHINE:ARM64"); + linkerArgs.push("/MACHINE:ARM64"); break; } + if (useCompilerDriver) + handleClangClArchitectureFlags(product, product.qbs.architecture, args); + var requireAppContainer = product.cpp.requireAppContainer; if (requireAppContainer !== undefined) - args.push("/APPCONTAINER" + (requireAppContainer ? "" : ":NO")); + linkerArgs.push("/APPCONTAINER" + (requireAppContainer ? "" : ":NO")); var minimumWindowsVersion = product.cpp.minimumWindowsVersion; var subsystemSwitch = undefined; @@ -415,27 +466,29 @@ function prepareLinker(project, product, inputs, outputs, input, output) { subsystemSwitch = product.consoleApplication === false ? '/SUBSYSTEM:WINDOWS' : '/SUBSYSTEM:CONSOLE'; } - var useLldLink = product.cpp.linkerName === "lld-link.exe"; + var useLldLink = useCompilerDriver && product.cpp.linkerVariant === "lld" + || !useCompilerDriver && product.cpp.linkerName === "lld-link.exe"; if (minimumWindowsVersion) { var subsystemVersion = WindowsUtils.getWindowsVersionInFormat(minimumWindowsVersion, 'subsystem'); if (subsystemVersion) { subsystemSwitch += ',' + subsystemVersion; - if (!useLldLink) // llvm linker does not support /OSVERSION - args.push('/OSVERSION:' + subsystemVersion); + // llvm linker does not support /OSVERSION + if (!useLldLink) + linkerArgs.push('/OSVERSION:' + subsystemVersion); } } if (subsystemSwitch) - args.push(subsystemSwitch); + linkerArgs.push(subsystemSwitch); var linkerOutputNativeFilePath = FileInfo.toWindowsSeparators(primaryOutput.filePath); var manifestFileNames = []; if (generateManifestFiles) { if (canEmbedManifest) { - args.push("/MANIFEST:embed"); + linkerArgs.push("/MANIFEST:embed"); additionalManifestInputs.forEach(function (manifestFileName) { - args.push("/MANIFESTINPUT:" + manifestFileName); + linkerArgs.push("/MANIFESTINPUT:" + manifestFileName); }); } else { linkerOutputNativeFilePath @@ -444,17 +497,11 @@ function prepareLinker(project, product, inputs, outputs, input, output) { + primaryOutput.fileName); var manifestFileName = linkerOutputNativeFilePath + ".manifest"; - args.push('/MANIFEST', '/MANIFESTFILE:' + manifestFileName); + linkerArgs.push('/MANIFEST', '/MANIFESTFILE:' + manifestFileName); manifestFileNames = [manifestFileName].concat(additionalManifestInputs); } } - var allInputs = inputs.obj || []; - for (i in allInputs) { - var fileName = FileInfo.toWindowsSeparators(allInputs[i].filePath) - args.push(fileName) - } - var wholeArchiveSupported = linkerSupportsWholeArchive(product); var wholeArchiveRequested = false; var libDeps = collectLibraryDependencies(product); @@ -465,8 +512,16 @@ function prepareLinker(project, product, inputs, outputs, input, output) { if (lib === prevLib) continue; prevLib = lib; - args.push((wholeArchiveSupported && dep.wholeArchive ? "/WHOLEARCHIVE:" : "") - + FileInfo.toWindowsSeparators(lib)); + + if (wholeArchiveSupported && dep.wholeArchive) { + // need to pass libraries to the driver to avoid "no input files" error if no object + // files are specified; thus libraries are duplicated when using "WHOLEARCHIVE" + if (useCompilerDriver && allInputs.length === 0) + args.push(FileInfo.toWindowsSeparators(lib)); + linkerArgs.push("/WHOLEARCHIVE:" + FileInfo.toWindowsSeparators(lib)); + } else { + args.push(FileInfo.toWindowsSeparators(lib)); + } if (dep.wholeArchive) wholeArchiveRequested = true; } @@ -477,34 +532,36 @@ function prepareLinker(project, product, inputs, outputs, input, output) { } if (product.cpp.entryPoint) - args.push("/ENTRY:" + product.cpp.entryPoint); + linkerArgs.push("/ENTRY:" + product.cpp.entryPoint); if (outputs.application && product.cpp.generateLinkerMapFile) { if (useLldLink) - args.push("/lldmap:" + outputs.mem_map[0].filePath); + linkerArgs.push("/lldmap:" + outputs.mem_map[0].filePath); else - args.push("/MAP:" + outputs.mem_map[0].filePath); + linkerArgs.push("/MAP:" + outputs.mem_map[0].filePath); } - args.push('/OUT:' + linkerOutputNativeFilePath) + if (useCompilerDriver) + args.push('/Fe' + linkerOutputNativeFilePath); + else + linkerArgs.push('/OUT:' + linkerOutputNativeFilePath); var libraryPaths = product.cpp.libraryPaths; if (libraryPaths) libraryPaths = [].uniqueConcat(libraryPaths); for (i in libraryPaths) { - args.push('/LIBPATH:' + FileInfo.toWindowsSeparators(libraryPaths[i])) + linkerArgs.push('/LIBPATH:' + FileInfo.toWindowsSeparators(libraryPaths[i])) } - handleDiscardProperty(product, args); + handleDiscardProperty(product, linkerArgs); var linkerFlags = product.cpp.platformLinkerFlags.concat(product.cpp.linkerFlags); - args = args.concat(linkerFlags); + linkerArgs = linkerArgs.concat(linkerFlags); if (product.cpp.allowUnresolvedSymbols) - args.push("/FORCE:UNRESOLVED"); + linkerArgs.push("/FORCE:UNRESOLVED"); - var linkerPath = product.cpp.linkerPath; var wrapperArgs = product.cpp.linkerWrapper; if (wrapperArgs && wrapperArgs.length > 0) { - args.unshift(linkerPath); + linkerArgs.unshift(linkerPath); linkerPath = wrapperArgs.shift(); - args = wrapperArgs.concat(args); + linkerArgs = wrapperArgs.concat(linkerArgs); } var commands = []; var warningCmd = new JavaScriptCommand(); @@ -522,6 +579,10 @@ function prepareLinker(project, product, inputs, outputs, input, output) { } }; commands.push(warningCmd); + + if (linkerArgs.length !== 0) + args = args.concat(useCompilerDriver ? ['/link'] : []).concat(linkerArgs); + var cmd = new Command(linkerPath, args) cmd.description = 'linking ' + primaryOutput.fileName; cmd.highlight = 'linker'; @@ -529,6 +590,7 @@ function prepareLinker(project, product, inputs, outputs, input, output) { cmd.relevantEnvironmentVariables = ["LINK", "_LINK_", "LIB", "TMP"]; cmd.workingDirectory = FileInfo.path(primaryOutput.filePath) cmd.responseFileUsagePrefix = '@'; + cmd.responseFileSeparator = useCompilerDriver ? ' ' : '\n'; cmd.stdoutFilterFunction = function(output) { res = output.replace(/^.*performing full link.*\s*/, ""); return res.replace(/^ *Creating library.*\r\n$/, ""); diff --git a/share/qbs/modules/cpp/windows-clang-cl.qbs b/share/qbs/modules/cpp/windows-clang-cl.qbs index 1b2833060..2ac3f356a 100644 --- a/share/qbs/modules/cpp/windows-clang-cl.qbs +++ b/share/qbs/modules/cpp/windows-clang-cl.qbs @@ -65,6 +65,17 @@ MsvcBaseModule { : undefined buildEnv: clangClProbe.buildEnv + property string linkerVariant + PropertyOptions { + name: "linkerVariant" + allowedValues: ["lld", "link"] + description: "Allows to specify the linker variant. Maps to clang-cl's -fuse-ld option." + } + Properties { + condition: linkerVariant + driverLinkerFlags: "-fuse-ld=" + linkerVariant + } + property string vcvarsallPath compilerName: "clang-cl.exe" |