diff options
27 files changed, 1088 insertions, 420 deletions
diff --git a/share/qbs/imports/qbs/Probes/ClangClProbe.qbs b/share/qbs/imports/qbs/Probes/ClangClProbe.qbs new file mode 100644 index 000000000..c7687f0e9 --- /dev/null +++ b/share/qbs/imports/qbs/Probes/ClangClProbe.qbs @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.Utilities +import "../../../modules/cpp/gcc.js" as Gcc + +PathProbe { + // Inputs + property string compilerFilePath + property string vcvarsallFilePath + property stringList enableDefinesByLanguage + property string architecture + property string _nullDevice: qbs.nullDevice + property string _pathListSeparator: qbs.pathListSeparator + + // Outputs + property int versionMajor + property int versionMinor + property int versionPatch + property stringList includePaths + property var buildEnv + property var compilerDefinesByLanguage + + configure: { + var languages = enableDefinesByLanguage; + if (!languages || languages.length === 0) + languages = ["c"]; + + var info = languages.contains("c") + ? Utilities.clangClCompilerInfo(compilerFilePath, architecture, vcvarsallFilePath, "c") : {}; + var infoCpp = languages.contains("cpp") + ? Utilities.clangClCompilerInfo(compilerFilePath, architecture, vcvarsallFilePath, "cpp") : {}; + found = (!languages.contains("c") || + (!!info && !!info.macros && !!info.buildEnvironment)) + && (!languages.contains("cpp") || + (!!infoCpp && !!infoCpp.macros && !!infoCpp.buildEnvironment)); + + compilerDefinesByLanguage = { + "c": info.macros, + "cpp": infoCpp.macros, + }; + + var macros = info.macros || infoCpp.macros; + + versionMajor = parseInt(macros["__clang_major__"], 10); + versionMinor = parseInt(macros["__clang_minor__"], 10); + versionPatch = parseInt(macros["__clang_patchlevel__"], 10); + + buildEnv = info.buildEnvironment || infoCpp.buildEnvironment; + // clang-cl is just a wrapper around clang.exe, so the includes *should be* the same + var clangPath = FileInfo.joinPaths(FileInfo.path(compilerFilePath), "clang.exe"); + + var defaultPaths = Gcc.dumpDefaultPaths(buildEnv, clangPath, + [], _nullDevice, + _pathListSeparator, "", ""); + includePaths = defaultPaths.includePaths; + } +} diff --git a/share/qbs/modules/cpp/CppModule.qbs b/share/qbs/modules/cpp/CppModule.qbs index 01b0f8af9..35a5de4f0 100644 --- a/share/qbs/modules/cpp/CppModule.qbs +++ b/share/qbs/modules/cpp/CppModule.qbs @@ -454,42 +454,48 @@ Module { fileTags: ["hpp"] } - validate: { - var validator = new ModUtils.PropertyValidator("cpp"); - validator.setRequiredProperty("architecture", architecture, - "you might want to re-run 'qbs-setup-toolchains'"); - validator.addCustomValidator("architecture", architecture, function (value) { - return !architecture || architecture === Utilities.canonicalArchitecture(architecture); - }, "'" + architecture + "' is invalid. You must use the canonical name '" + - Utilities.canonicalArchitecture(architecture) + "'"); - validator.setRequiredProperty("endianness", endianness); - validator.setRequiredProperty("compilerDefinesByLanguage", compilerDefinesByLanguage); - validator.setRequiredProperty("compilerVersion", compilerVersion); - validator.setRequiredProperty("compilerVersionMajor", compilerVersionMajor); - validator.setRequiredProperty("compilerVersionMinor", compilerVersionMinor); - validator.setRequiredProperty("compilerVersionPatch", compilerVersionPatch); - validator.addVersionValidator("compilerVersion", compilerVersion, 3, 3); - validator.addRangeValidator("compilerVersionMajor", compilerVersionMajor, 1); - validator.addRangeValidator("compilerVersionMinor", compilerVersionMinor, 0); - validator.addRangeValidator("compilerVersionPatch", compilerVersionPatch, 0); - if (minimumWindowsVersion) { - validator.addVersionValidator("minimumWindowsVersion", minimumWindowsVersion, 2, 2); - validator.addCustomValidator("minimumWindowsVersion", minimumWindowsVersion, function (v) { - return !v || v === WindowsUtils.canonicalizeVersion(v); - }, "'" + minimumWindowsVersion + "' is invalid. Did you mean '" + - WindowsUtils.canonicalizeVersion(minimumWindowsVersion) + "'?"); - } - validator.validate(); - - if (minimumWindowsVersion && !WindowsUtils.isValidWindowsVersion(minimumWindowsVersion)) { - console.warn("Unknown Windows version '" + minimumWindowsVersion - + "'; expected one of: " - + WindowsUtils.knownWindowsVersions().map(function (a) { - return '"' + a + '"'; }).join(", ") - + ". See https://docs.microsoft.com/en-us/windows/desktop/SysInfo/operating-system-version"); + property var validateFunc: { + return function() { + var validator = new ModUtils.PropertyValidator("cpp"); + validator.setRequiredProperty("architecture", architecture, + "you might want to re-run 'qbs-setup-toolchains'"); + validator.addCustomValidator("architecture", architecture, function (value) { + return !architecture || architecture === Utilities.canonicalArchitecture(architecture); + }, "'" + architecture + "' is invalid. You must use the canonical name '" + + Utilities.canonicalArchitecture(architecture) + "'"); + validator.setRequiredProperty("endianness", endianness); + validator.setRequiredProperty("compilerDefinesByLanguage", compilerDefinesByLanguage); + validator.setRequiredProperty("compilerVersion", compilerVersion); + validator.setRequiredProperty("compilerVersionMajor", compilerVersionMajor); + validator.setRequiredProperty("compilerVersionMinor", compilerVersionMinor); + validator.setRequiredProperty("compilerVersionPatch", compilerVersionPatch); + validator.addVersionValidator("compilerVersion", compilerVersion, 3, 3); + validator.addRangeValidator("compilerVersionMajor", compilerVersionMajor, 1); + validator.addRangeValidator("compilerVersionMinor", compilerVersionMinor, 0); + validator.addRangeValidator("compilerVersionPatch", compilerVersionPatch, 0); + if (minimumWindowsVersion) { + validator.addVersionValidator("minimumWindowsVersion", minimumWindowsVersion, 2, 2); + validator.addCustomValidator("minimumWindowsVersion", minimumWindowsVersion, function (v) { + return !v || v === WindowsUtils.canonicalizeVersion(v); + }, "'" + minimumWindowsVersion + "' is invalid. Did you mean '" + + WindowsUtils.canonicalizeVersion(minimumWindowsVersion) + "'?"); + } + validator.validate(); + + if (minimumWindowsVersion && !WindowsUtils.isValidWindowsVersion(minimumWindowsVersion)) { + console.warn("Unknown Windows version '" + minimumWindowsVersion + + "'; expected one of: " + + WindowsUtils.knownWindowsVersions().map(function (a) { + return '"' + a + '"'; }).join(", ") + + ". See https://docs.microsoft.com/en-us/windows/desktop/SysInfo/operating-system-version"); + } } } + validate: { + return validateFunc(); + } + setupRunEnvironment: { SetupRunEnv.setupRunEnvironment(product, config); } diff --git a/share/qbs/modules/cpp/msvc.js b/share/qbs/modules/cpp/msvc.js index 1864c40e1..5da2ad0cf 100644 --- a/share/qbs/modules/cpp/msvc.js +++ b/share/qbs/modules/cpp/msvc.js @@ -62,7 +62,8 @@ function hasCxx17Option(input) { // Probably this is not the earliest version to support the flag, but we have tested this one // and it's a pain to find out the exact minimum. - return Utilities.versionCompare(input.cpp.compilerVersion, "19.12.25831") >= 0; + return Utilities.versionCompare(input.cpp.compilerVersion, "19.12.25831") >= 0 + || (input.qbs.toolchain.contains("clang-cl") && input.cpp.compilerVersionMajor >= 7); } function addLanguageVersionFlag(input, args) { @@ -72,7 +73,9 @@ function addLanguageVersionFlag(input, args) { return; // Visual C++ 2013, Update 3 - var hasStdOption = Utilities.versionCompare(input.cpp.compilerVersion, "18.00.30723") >= 0; + var hasStdOption = Utilities.versionCompare(input.cpp.compilerVersion, "18.00.30723") >= 0 + // or clang-cl + || input.qbs.toolchain.contains("clang-cl"); if (!hasStdOption) return; @@ -210,10 +213,20 @@ function prepareCompiler(project, product, inputs, outputs, input, output, expli var pchInputs = explicitlyDependsOn[tag + "_pch"]; if (pchOutput) { // create PCH - args.push("/Yc"); - args.push("/Fp" + FileInfo.toWindowsSeparators(pchOutput.filePath)); - args.push("/Fo" + FileInfo.toWindowsSeparators(objOutput.filePath)); - args.push(FileInfo.toWindowsSeparators(input.filePath)); + if (input.qbs.toolchain.contains("clang-cl")) { + // clang-cl does not support /Yc flag without filename + args.push("/Yc" + FileInfo.toWindowsSeparators(input.filePath)); + // clang-cl complains when pch file is not included + args.push("/FI" + FileInfo.toWindowsSeparators(input.filePath)); + args.push("/Fp" + FileInfo.toWindowsSeparators(pchOutput.filePath)); + args.push("/Fo" + FileInfo.toWindowsSeparators(objOutput.filePath)); + } else { // real msvc + args.push("/Yc"); + args.push("/Fp" + FileInfo.toWindowsSeparators(pchOutput.filePath)); + args.push("/Fo" + FileInfo.toWindowsSeparators(objOutput.filePath)); + args.push(FileInfo.toWindowsSeparators(input.filePath)); + } + } else if (pchInputs && pchInputs.length === 1 && ModUtils.moduleProperty(input, "usePrecompiledHeader", tag)) { // use PCH @@ -399,7 +412,8 @@ function prepareLinker(project, product, inputs, outputs, input, output) { 'subsystem'); if (subsystemVersion) { subsystemSwitch += ',' + subsystemVersion; - args.push('/OSVERSION:' + subsystemVersion); + if (product.cpp.linkerName !== "lld-link.exe") // llvm linker does not support /OSVERSION + args.push('/OSVERSION:' + subsystemVersion); } } diff --git a/share/qbs/modules/cpp/windows-clang-cl.qbs b/share/qbs/modules/cpp/windows-clang-cl.qbs new file mode 100644 index 000000000..1b2833060 --- /dev/null +++ b/share/qbs/modules/cpp/windows-clang-cl.qbs @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs +import qbs.ModUtils +import qbs.Probes +import qbs.FileInfo +import 'windows-msvc-base.qbs' as MsvcBaseModule + +MsvcBaseModule { + condition: qbs.hostOS.contains('windows') && + qbs.targetOS.contains('windows') && + qbs.toolchain && qbs.toolchain.contains('clang-cl') + priority: 100 + + Probes.BinaryProbe { + id: clangPathProbe + condition: !toolchainInstallPath && !_skipAllChecks + names: ["clang-cl"] + } + + Probes.ClangClProbe { + id: clangClProbe + condition: !_skipAllChecks + compilerFilePath: compilerPath + vcvarsallFilePath: vcvarsallPath + enableDefinesByLanguage: enableCompilerDefinesByLanguage + architecture: qbs.architecture + } + + compilerVersionMajor: clangClProbe.versionMajor + compilerVersionMinor: clangClProbe.versionMinor + compilerVersionPatch: clangClProbe.versionPatch + compilerIncludePaths: clangClProbe.includePaths + compilerDefinesByLanguage: clangClProbe.compilerDefinesByLanguage + + toolchainInstallPath: clangPathProbe.found ? clangPathProbe.path + : undefined + buildEnv: clangClProbe.buildEnv + + property string vcvarsallPath + + compilerName: "clang-cl.exe" + linkerName: "lld-link.exe" + linkerPath: FileInfo.joinPaths(toolchainInstallPath, linkerName) + + validateFunc: { + return function() { + if (_skipAllChecks) + return; + var validator = new ModUtils.PropertyValidator("cpp"); + validator.setRequiredProperty("vcvarsallPath", vcvarsallPath); + validator.validate(); + base(); + } + } +} diff --git a/share/qbs/modules/cpp/windows-msvc-base.qbs b/share/qbs/modules/cpp/windows-msvc-base.qbs new file mode 100644 index 000000000..d36daa99f --- /dev/null +++ b/share/qbs/modules/cpp/windows-msvc-base.qbs @@ -0,0 +1,372 @@ +/**************************************************************************** +** +** Copyright (C) 2015 The Qt Company Ltd. +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms and +** conditions see http://www.qt.io/terms-conditions. For further information +** use the contact form at http://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, The Qt Company gives you certain additional +** rights. These rights are described in The Qt Company LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +import qbs.File +import qbs.FileInfo +import qbs.ModUtils +import qbs.PathTools +import qbs.Probes +import qbs.Utilities +import qbs.WindowsUtils +import 'msvc.js' as MSVC + +CppModule { + condition: false + + windowsApiCharacterSet: "unicode" + platformDefines: { + var defines = base.concat(WindowsUtils.characterSetDefines(windowsApiCharacterSet)) + .concat("WIN32"); + var def = WindowsUtils.winapiFamilyDefine(windowsApiFamily); + if (def) + defines.push("WINAPI_FAMILY=WINAPI_FAMILY_" + def); + (windowsApiAdditionalPartitions || []).map(function (name) { + defines.push("WINAPI_PARTITION_" + WindowsUtils.winapiPartitionDefine(name) + "=1"); + }); + return defines; + } + platformCommonCompilerFlags: { + var flags = base; + if (compilerVersionMajor >= 18) // 2013 + flags.push("/FS"); + return flags; + } + warningLevel: "default" + compilerName: "cl.exe" + compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName) + assemblerName: { + switch (qbs.architecture) { + case "armv7": + return "armasm.exe"; + case "arm64": + return "armasm64.exe"; + case "ia64": + return "ias.exe"; + case "x86": + return "ml.exe"; + case "x86_64": + return "ml64.exe"; + } + } + + linkerName: "link.exe" + runtimeLibrary: "dynamic" + separateDebugInformation: true + + property bool generateManifestFile: true + property string toolchainInstallPath + + architecture: qbs.architecture + endianness: "little" + staticLibrarySuffix: ".lib" + dynamicLibrarySuffix: ".dll" + executableSuffix: ".exe" + debugInfoSuffix: ".pdb" + imageFormat: "pe" + Properties { + condition: product.multiplexByQbsProperties.contains("buildVariants") + && qbs.buildVariants && qbs.buildVariants.length > 1 + && qbs.buildVariant !== "release" + && product.type.containsAny(["staticlibrary", "dynamiclibrary"]) + variantSuffix: "d" + } + + property var buildEnv + + setupBuildEnvironment: { + for (var key in product.cpp.buildEnv) { + var v = new ModUtils.EnvironmentVariable(key, ';'); + v.prepend(product.cpp.buildEnv[key]); + v.set(); + } + } + + Rule { + condition: useCPrecompiledHeader + inputs: ["c_pch_src"] + auxiliaryInputs: ["hpp"] + Artifact { + fileTags: ['obj'] + filePath: Utilities.getHash(input.completeBaseName) + '_c.obj' + } + Artifact { + fileTags: ['c_pch'] + filePath: product.name + '_c.pch' + } + prepare: { + return MSVC.prepareCompiler.apply(MSVC, arguments); + } + } + + Rule { + condition: useCxxPrecompiledHeader + inputs: ["cpp_pch_src"] + explicitlyDependsOn: ["c_pch"] // to prevent vc--0.pdb conflict + auxiliaryInputs: ["hpp"] + Artifact { + fileTags: ['obj'] + filePath: Utilities.getHash(input.completeBaseName) + '_cpp.obj' + } + Artifact { + fileTags: ['cpp_pch'] + filePath: product.name + '_cpp.pch' + } + prepare: { + return MSVC.prepareCompiler.apply(MSVC, arguments); + } + } + + Rule { + name: "compiler" + inputs: ["cpp", "c"] + auxiliaryInputs: ["hpp"] + explicitlyDependsOn: ["c_pch", "cpp_pch"] + + outputFileTags: ["obj", "intermediate_obj"] + outputArtifacts: { + var tags = input.fileTags.contains("cpp_intermediate_object") + ? ["intermediate_obj"] + : ["obj"]; + return [{ + fileTags: tags, + filePath: Utilities.getHash(input.baseDir) + "/" + input.fileName + ".obj" + }]; + } + + prepare: { + return MSVC.prepareCompiler.apply(MSVC, arguments); + } + } + + FileTagger { + patterns: ["*.manifest"] + fileTags: ["native.pe.manifest"] + } + + Rule { + name: "applicationLinker" + multiplex: true + inputs: ['obj', 'native.pe.manifest'] + inputsFromDependencies: ['staticlibrary', 'dynamiclibrary_import', "debuginfo_app"] + + outputFileTags: ["application", "debuginfo_app"] + outputArtifacts: { + var app = { + fileTags: ["application"], + filePath: FileInfo.joinPaths( + product.destinationDirectory, + PathTools.applicationFilePath(product)) + }; + var artifacts = [app]; + if (product.cpp.debugInformation && product.cpp.separateDebugInformation) { + artifacts.push({ + fileTags: ["debuginfo_app"], + filePath: app.filePath.substr(0, app.filePath.length - 4) + + product.cpp.debugInfoSuffix + }); + } + return artifacts; + } + + prepare: { + return MSVC.prepareLinker.apply(MSVC, arguments); + } + } + + Rule { + name: "dynamicLibraryLinker" + multiplex: true + inputs: ['obj', 'native.pe.manifest'] + inputsFromDependencies: ['staticlibrary', 'dynamiclibrary_import', "debuginfo_dll"] + + outputFileTags: ["dynamiclibrary", "dynamiclibrary_import", "debuginfo_dll"] + outputArtifacts: { + var artifacts = [ + { + fileTags: ["dynamiclibrary"], + filePath: product.destinationDirectory + "/" + PathTools.dynamicLibraryFilePath(product) + }, + { + fileTags: ["dynamiclibrary_import"], + filePath: product.destinationDirectory + "/" + PathTools.importLibraryFilePath(product), + alwaysUpdated: false + } + ]; + if (product.cpp.debugInformation && product.cpp.separateDebugInformation) { + var lib = artifacts[0]; + artifacts.push({ + fileTags: ["debuginfo_dll"], + filePath: lib.filePath.substr(0, lib.filePath.length - 4) + + product.cpp.debugInfoSuffix + }); + } + return artifacts; + } + + prepare: { + return MSVC.prepareLinker.apply(MSVC, arguments); + } + } + + Rule { + name: "libtool" + multiplex: true + inputs: ["obj"] + inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"] + outputFileTags: ["staticlibrary", "debuginfo_cl"] + outputArtifacts: { + var artifacts = [ + { + fileTags: ["staticlibrary"], + filePath: FileInfo.joinPaths(product.destinationDirectory, + PathTools.staticLibraryFilePath(product)) + } + ]; + if (product.cpp.debugInformation && product.cpp.separateDebugInformation) { + artifacts.push({ + fileTags: ["debuginfo_cl"], + filePath: product.targetName + ".cl" + product.cpp.debugInfoSuffix + }); + } + return artifacts; + } + prepare: { + var args = ['/nologo'] + var lib = outputs["staticlibrary"][0]; + var nativeOutputFileName = FileInfo.toWindowsSeparators(lib.filePath) + args.push('/OUT:' + nativeOutputFileName) + for (var i in inputs.obj) { + var fileName = FileInfo.toWindowsSeparators(inputs.obj[i].filePath) + args.push(fileName) + } + var cmd = new Command("lib.exe", args); + cmd.description = 'creating ' + lib.fileName; + cmd.highlight = 'linker'; + cmd.jobPool = "linker"; + cmd.workingDirectory = FileInfo.path(lib.filePath) + cmd.responseFileUsagePrefix = '@'; + return cmd; + } + } + + FileTagger { + patterns: ["*.rc"] + fileTags: ["rc"] + } + + Rule { + inputs: ["rc"] + auxiliaryInputs: ["hpp"] + + Artifact { + filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + ".res" + fileTags: ["obj"] + } + + prepare: { + var platformDefines = input.cpp.platformDefines; + var defines = input.cpp.defines; + var includePaths = input.cpp.includePaths; + var systemIncludePaths = input.cpp.systemIncludePaths; + var args = []; + var i; + var hasNoLogo = product.cpp.compilerVersionMajor >= 16; // 2010 + if (hasNoLogo) + args.push("/nologo"); + + for (i in platformDefines) { + args.push('/d'); + args.push(platformDefines[i]); + } + for (i in defines) { + args.push('/d'); + args.push(defines[i]); + } + for (i in includePaths) { + args.push('/i'); + args.push(includePaths[i]); + } + for (i in systemIncludePaths) { + args.push('/i'); + args.push(systemIncludePaths[i]); + } + + args = args.concat(['/fo', output.filePath, input.filePath]); + var cmd = new Command('rc', args); + cmd.description = 'compiling ' + input.fileName; + cmd.highlight = 'compiler'; + cmd.jobPool = "compiler"; + + if (!hasNoLogo) { + // Remove the first two lines of stdout. That's the logo. + cmd.stdoutFilterFunction = function(output) { + var idx = 0; + for (var i = 0; i < 3; ++i) + idx = output.indexOf('\n', idx + 1); + return output.substr(idx + 1); + } + } + + return cmd; + } + } + + FileTagger { + patterns: "*.asm" + fileTags: ["asm"] + } + + Rule { + inputs: ["asm"] + Artifact { + filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + ".obj" + fileTags: ["obj"] + } + prepare: { + var args = ["/nologo", "/c", + "/Fo" + FileInfo.toWindowsSeparators(output.filePath), + FileInfo.toWindowsSeparators(input.filePath)]; + if (product.cpp.debugInformation) + args.push("/Zi"); + args = args.concat(ModUtils.moduleProperty(input, 'platformFlags', 'asm'), + ModUtils.moduleProperty(input, 'flags', 'asm')); + var cmd = new Command(product.cpp.assemblerPath, args); + cmd.description = "assembling " + input.fileName; + cmd.jobPool = "assembler"; + cmd.inputFileName = input.fileName; + cmd.stdoutFilterFunction = function(output) { + var lines = output.split("\r\n").filter(function (s) { + return !s.endsWith(inputFileName); }); + return lines.join("\r\n"); + }; + return cmd; + } + } +} diff --git a/share/qbs/modules/cpp/windows-msvc.qbs b/share/qbs/modules/cpp/windows-msvc.qbs index 8dd6dd2c0..59032d28a 100644 --- a/share/qbs/modules/cpp/windows-msvc.qbs +++ b/share/qbs/modules/cpp/windows-msvc.qbs @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2019 Ivan Komissarov (abbapoh@gmail.com) ** Contact: http://www.qt.io/licensing ** ** This file is part of Qbs. @@ -28,19 +29,14 @@ ** ****************************************************************************/ -import qbs.File -import qbs.FileInfo -import qbs.ModUtils -import qbs.PathTools import qbs.Probes -import qbs.Utilities -import qbs.WindowsUtils -import 'msvc.js' as MSVC +import "windows-msvc-base.qbs" as MsvcBaseModule -CppModule { +MsvcBaseModule { condition: qbs.hostOS.contains('windows') && qbs.targetOS.contains('windows') && qbs.toolchain && qbs.toolchain.contains('msvc') + priority: 50 Probes.BinaryProbe { id: compilerPathProbe @@ -62,335 +58,9 @@ CppModule { compilerVersionMinor: msvcProbe.versionMinor compilerVersionPatch: msvcProbe.versionPatch compilerIncludePaths: msvcProbe.includePaths - - windowsApiCharacterSet: "unicode" - platformDefines: { - var defines = base.concat(WindowsUtils.characterSetDefines(windowsApiCharacterSet)) - .concat("WIN32"); - var def = WindowsUtils.winapiFamilyDefine(windowsApiFamily); - if (def) - defines.push("WINAPI_FAMILY=WINAPI_FAMILY_" + def); - (windowsApiAdditionalPartitions || []).map(function (name) { - defines.push("WINAPI_PARTITION_" + WindowsUtils.winapiPartitionDefine(name) + "=1"); - }); - return defines; - } - platformCommonCompilerFlags: { - var flags = base; - if (compilerVersionMajor >= 18) // 2013 - flags.push("/FS"); - return flags; - } compilerDefinesByLanguage: msvcProbe.compilerDefinesByLanguage - warningLevel: "default" - compilerName: "cl.exe" - compilerPath: FileInfo.joinPaths(toolchainInstallPath, compilerName) - assemblerName: { - switch (qbs.architecture) { - case "armv7": - return "armasm.exe"; - case "arm64": - return "armasm64.exe"; - case "ia64": - return "ias.exe"; - case "x86": - return "ml.exe"; - case "x86_64": - return "ml64.exe"; - } - } - - linkerName: "link.exe" - runtimeLibrary: "dynamic" - separateDebugInformation: true - - property bool generateManifestFile: true - property string toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path - : undefined - architecture: qbs.architecture - endianness: "little" - staticLibrarySuffix: ".lib" - dynamicLibrarySuffix: ".dll" - executableSuffix: ".exe" - debugInfoSuffix: ".pdb" - imageFormat: "pe" - Properties { - condition: product.multiplexByQbsProperties.contains("buildVariants") - && qbs.buildVariants && qbs.buildVariants.length > 1 - && qbs.buildVariant !== "release" - && product.type.containsAny(["staticlibrary", "dynamiclibrary"]) - variantSuffix: "d" - } - - property var buildEnv: msvcProbe.buildEnv - - setupBuildEnvironment: { - for (var key in product.cpp.buildEnv) { - var v = new ModUtils.EnvironmentVariable(key, ';'); - v.prepend(product.cpp.buildEnv[key]); - v.set(); - } - } - - Rule { - condition: useCPrecompiledHeader - inputs: ["c_pch_src"] - auxiliaryInputs: ["hpp"] - Artifact { - fileTags: ['obj'] - filePath: Utilities.getHash(input.completeBaseName) + '_c.obj' - } - Artifact { - fileTags: ['c_pch'] - filePath: product.name + '_c.pch' - } - prepare: { - return MSVC.prepareCompiler.apply(MSVC, arguments); - } - } - - Rule { - condition: useCxxPrecompiledHeader - inputs: ["cpp_pch_src"] - explicitlyDependsOn: ["c_pch"] // to prevent vc--0.pdb conflict - auxiliaryInputs: ["hpp"] - Artifact { - fileTags: ['obj'] - filePath: Utilities.getHash(input.completeBaseName) + '_cpp.obj' - } - Artifact { - fileTags: ['cpp_pch'] - filePath: product.name + '_cpp.pch' - } - prepare: { - return MSVC.prepareCompiler.apply(MSVC, arguments); - } - } - - Rule { - name: "compiler" - inputs: ["cpp", "c"] - auxiliaryInputs: ["hpp"] - explicitlyDependsOn: ["c_pch", "cpp_pch"] - - outputFileTags: ["obj", "intermediate_obj"] - outputArtifacts: { - var tags = input.fileTags.contains("cpp_intermediate_object") - ? ["intermediate_obj"] - : ["obj"]; - return [{ - fileTags: tags, - filePath: Utilities.getHash(input.baseDir) + "/" + input.fileName + ".obj" - }]; - } - - prepare: { - return MSVC.prepareCompiler.apply(MSVC, arguments); - } - } - - FileTagger { - patterns: ["*.manifest"] - fileTags: ["native.pe.manifest"] - } - - Rule { - name: "applicationLinker" - multiplex: true - inputs: ['obj', 'native.pe.manifest'] - inputsFromDependencies: ['staticlibrary', 'dynamiclibrary_import', "debuginfo_app"] - - outputFileTags: ["application", "debuginfo_app"] - outputArtifacts: { - var app = { - fileTags: ["application"], - filePath: FileInfo.joinPaths( - product.destinationDirectory, - PathTools.applicationFilePath(product)) - }; - var artifacts = [app]; - if (product.cpp.debugInformation && product.cpp.separateDebugInformation) { - artifacts.push({ - fileTags: ["debuginfo_app"], - filePath: app.filePath.substr(0, app.filePath.length - 4) - + product.cpp.debugInfoSuffix - }); - } - return artifacts; - } - prepare: { - return MSVC.prepareLinker.apply(MSVC, arguments); - } - } - - Rule { - name: "dynamicLibraryLinker" - multiplex: true - inputs: ['obj', 'native.pe.manifest'] - inputsFromDependencies: ['staticlibrary', 'dynamiclibrary_import', "debuginfo_dll"] - - outputFileTags: ["dynamiclibrary", "dynamiclibrary_import", "debuginfo_dll"] - outputArtifacts: { - var artifacts = [ - { - fileTags: ["dynamiclibrary"], - filePath: product.destinationDirectory + "/" + PathTools.dynamicLibraryFilePath(product) - }, - { - fileTags: ["dynamiclibrary_import"], - filePath: product.destinationDirectory + "/" + PathTools.importLibraryFilePath(product), - alwaysUpdated: false - } - ]; - if (product.cpp.debugInformation && product.cpp.separateDebugInformation) { - var lib = artifacts[0]; - artifacts.push({ - fileTags: ["debuginfo_dll"], - filePath: lib.filePath.substr(0, lib.filePath.length - 4) - + product.cpp.debugInfoSuffix - }); - } - return artifacts; - } - - prepare: { - return MSVC.prepareLinker.apply(MSVC, arguments); - } - } - - Rule { - name: "libtool" - multiplex: true - inputs: ["obj"] - inputsFromDependencies: ["staticlibrary", "dynamiclibrary_import"] - outputFileTags: ["staticlibrary", "debuginfo_cl"] - outputArtifacts: { - var artifacts = [ - { - fileTags: ["staticlibrary"], - filePath: FileInfo.joinPaths(product.destinationDirectory, - PathTools.staticLibraryFilePath(product)) - } - ]; - if (product.cpp.debugInformation && product.cpp.separateDebugInformation) { - artifacts.push({ - fileTags: ["debuginfo_cl"], - filePath: product.targetName + ".cl" + product.cpp.debugInfoSuffix - }); - } - return artifacts; - } - prepare: { - var args = ['/nologo'] - var lib = outputs["staticlibrary"][0]; - var nativeOutputFileName = FileInfo.toWindowsSeparators(lib.filePath) - args.push('/OUT:' + nativeOutputFileName) - for (var i in inputs.obj) { - var fileName = FileInfo.toWindowsSeparators(inputs.obj[i].filePath) - args.push(fileName) - } - var cmd = new Command("lib.exe", args); - cmd.description = 'creating ' + lib.fileName; - cmd.highlight = 'linker'; - cmd.jobPool = "linker"; - cmd.workingDirectory = FileInfo.path(lib.filePath) - cmd.responseFileUsagePrefix = '@'; - return cmd; - } - } - - FileTagger { - patterns: ["*.rc"] - fileTags: ["rc"] - } - - Rule { - inputs: ["rc"] - auxiliaryInputs: ["hpp"] - - Artifact { - filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + ".res" - fileTags: ["obj"] - } - - prepare: { - var platformDefines = input.cpp.platformDefines; - var defines = input.cpp.defines; - var includePaths = input.cpp.includePaths; - var systemIncludePaths = input.cpp.systemIncludePaths; - var args = []; - var i; - var hasNoLogo = product.cpp.compilerVersionMajor >= 16; // 2010 - if (hasNoLogo) - args.push("/nologo"); - - for (i in platformDefines) { - args.push('/d'); - args.push(platformDefines[i]); - } - for (i in defines) { - args.push('/d'); - args.push(defines[i]); - } - for (i in includePaths) { - args.push('/i'); - args.push(includePaths[i]); - } - for (i in systemIncludePaths) { - args.push('/i'); - args.push(systemIncludePaths[i]); - } - - args = args.concat(['/fo', output.filePath, input.filePath]); - var cmd = new Command('rc', args); - cmd.description = 'compiling ' + input.fileName; - cmd.highlight = 'compiler'; - cmd.jobPool = "compiler"; - - if (!hasNoLogo) { - // Remove the first two lines of stdout. That's the logo. - cmd.stdoutFilterFunction = function(output) { - var idx = 0; - for (var i = 0; i < 3; ++i) - idx = output.indexOf('\n', idx + 1); - return output.substr(idx + 1); - } - } - - return cmd; - } - } - - FileTagger { - patterns: "*.asm" - fileTags: ["asm"] - } - - Rule { - inputs: ["asm"] - Artifact { - filePath: Utilities.getHash(input.baseDir) + "/" + input.completeBaseName + ".obj" - fileTags: ["obj"] - } - prepare: { - var args = ["/nologo", "/c", - "/Fo" + FileInfo.toWindowsSeparators(output.filePath), - FileInfo.toWindowsSeparators(input.filePath)]; - if (product.cpp.debugInformation) - args.push("/Zi"); - args = args.concat(ModUtils.moduleProperty(input, 'platformFlags', 'asm'), - ModUtils.moduleProperty(input, 'flags', 'asm')); - var cmd = new Command(product.cpp.assemblerPath, args); - cmd.description = "assembling " + input.fileName; - cmd.jobPool = "assembler"; - cmd.inputFileName = input.fileName; - cmd.stdoutFilterFunction = function(output) { - var lines = output.split("\r\n").filter(function (s) { - return !s.endsWith(inputFileName); }); - return lines.join("\r\n"); - }; - return cmd; - } - } + toolchainInstallPath: compilerPathProbe.found ? compilerPathProbe.path + : undefined + buildEnv: msvcProbe.buildEnv } diff --git a/src/app/qbs-setup-toolchains/clangclprobe.cpp b/src/app/qbs-setup-toolchains/clangclprobe.cpp new file mode 100644 index 000000000..89075c5e8 --- /dev/null +++ b/src/app/qbs-setup-toolchains/clangclprobe.cpp @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "clangclprobe.h" +#include "msvcprobe.h" +#include "probe.h" + +#include "../shared/logging/consolelogger.h" + +#include <logging/translator.h> +#include <tools/hostosinfo.h> +#include <tools/profile.h> +#include <tools/qttools.h> +#include <tools/settings.h> + +#include <QtCore/qdir.h> +#include <QtCore/qfileinfo.h> + +using qbs::Settings; +using qbs::Profile; +using qbs::Internal::HostOsInfo; + +using qbs::Internal::Tr; + +namespace { + +QString getToolchainInstallPath(const QString &compilerFilePath) +{ + return QFileInfo(compilerFilePath).path(); // 1 level up +} + +Profile createProfileHelper( + Settings *settings, + const QString &profileName, + const QString &toolchainInstallPath, + const QString &vcvarsallPath, + const QString &architecture) +{ + Profile profile(profileName, settings); + profile.removeProfile(); + profile.setValue(QStringLiteral("qbs.architecture"), architecture); + profile.setValue( + QStringLiteral("qbs.toolchain"), + QStringList{QStringLiteral("clang-cl"), QStringLiteral("msvc")}); + profile.setValue(QStringLiteral("cpp.toolchainInstallPath"), toolchainInstallPath); + profile.setValue(QStringLiteral("cpp.vcvarsallPath"), vcvarsallPath); + qbsInfo() << Tr::tr("Profile '%1' created for '%2'.") + .arg(profile.name(), QDir::toNativeSeparators(toolchainInstallPath)); + return profile; +} + +std::vector<MSVCInstallInfo> compatibleMsvcs() +{ + auto msvcs = installedMSVCs(); + auto filter = [](const MSVCInstallInfo &info) + { + const auto versions = info.version.split(QLatin1Char('.')); + if (versions.empty()) + return true; + bool ok = false; + const int major = versions.at(0).toInt(&ok); + return !(ok && major >= 15); // support MSVC2017 and above + }; + const auto it = std::remove_if(msvcs.begin(), msvcs.end(), filter); + msvcs.erase(it, msvcs.end()); + for (const auto &msvc: msvcs) { + auto vcvarsallPath = msvc.findVcvarsallBat(); + if (vcvarsallPath.isEmpty()) + continue; + } + return msvcs; +} + +QString findCompatibleVcsarsallBat() +{ + for (const auto &msvc: compatibleMsvcs()) { + const auto vcvarsallPath = msvc.findVcvarsallBat(); + if (!vcvarsallPath.isEmpty()) + return vcvarsallPath; + } + return {}; +} + +} // namespace + +void createClangClProfile( + const QString &profileName, const QString &compilerFilePath, Settings *settings) +{ + const auto compilerName = QStringLiteral("clang-cl"); + const auto vcvarsallPath = findCompatibleVcsarsallBat(); + if (vcvarsallPath.isEmpty()) { + qbsWarning() + << Tr::tr("%1 requires installed Visual Studio 2017 or newer, but none was found.") + .arg(compilerName); + return; + } + + const auto toolchainInstallPath = getToolchainInstallPath(compilerFilePath); + const auto hostArch = QString::fromStdString(HostOsInfo::hostOSArchitecture()); + createProfileHelper(settings, profileName, toolchainInstallPath, vcvarsallPath, hostArch); +} + +/*! + \brief Creates a clang-cl profile based on auto-detected vsversion. + \internal +*/ +void clangClProbe(Settings *settings, QList<Profile> &profiles) +{ + const auto compilerName = QStringLiteral("clang-cl"); + qbsInfo() << Tr::tr("Trying to detect %1...").arg(compilerName); + const auto compilerFilePath = findExecutable(HostOsInfo::appendExecutableSuffix(compilerName)); + if (compilerFilePath.isEmpty()) { + qbsInfo() << Tr::tr("%1 was not found.").arg(compilerName); + return; + } + + const auto vcvarsallPath = findCompatibleVcsarsallBat(); + if (vcvarsallPath.isEmpty()) { + qbsWarning() + << Tr::tr("%1 requires installed Visual Studio 2017 or newer, but none was found.") + .arg(compilerName); + return; + } + + const QString architectures[] = { + QStringLiteral("x86_64"), + QStringLiteral("x86") + }; + const auto toolchainInstallPath = getToolchainInstallPath(compilerFilePath); + for (const auto &arch: architectures) { + const auto profileName = QStringLiteral("clang-cl-%1").arg(arch); + auto profile = createProfileHelper( + settings, profileName, toolchainInstallPath, vcvarsallPath, arch); + profiles.push_back(std::move(profile)); + } +} diff --git a/src/app/qbs-setup-toolchains/clangclprobe.h b/src/app/qbs-setup-toolchains/clangclprobe.h new file mode 100644 index 000000000..1e7724fbf --- /dev/null +++ b/src/app/qbs-setup-toolchains/clangclprobe.h @@ -0,0 +1,55 @@ +/**************************************************************************** +** +** Copyright (C) 2019 Ivan Komissarov (abbapoh@gmail.com) +** Contact: http://www.qt.io/licensing +** +** This file is part of Qbs. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CLANGCLPROBE_H +#define CLANGCLPROBE_H + +#include <QtCore/qlist.h> + +namespace qbs { +class Profile; +class Settings; +} + +void createClangClProfile( + const QString &profileName, const QString &compilerFilePath, qbs::Settings *settings); + +void clangClProbe(qbs::Settings *settings, QList<qbs::Profile> &profiles); + +#endif // CLANGCLPROBE_H diff --git a/src/app/qbs-setup-toolchains/msvcprobe.cpp b/src/app/qbs-setup-toolchains/msvcprobe.cpp index faacb4d09..d0b60a7fe 100644 --- a/src/app/qbs-setup-toolchains/msvcprobe.cpp +++ b/src/app/qbs-setup-toolchains/msvcprobe.cpp @@ -160,12 +160,6 @@ static QString wow6432Key() #endif } -struct MSVCInstallInfo -{ - QString version; - QString installDir; -}; - static QString vswhereFilePath() { static const std::vector<const char *> envVarCandidates{"ProgramFiles", "ProgramFiles(x86)"}; @@ -288,7 +282,28 @@ static std::vector<MSVCInstallInfo> installedMSVCsFromRegistry() return result; } -static std::vector<MSVC> installedMSVCs() +QString MSVCInstallInfo::findVcvarsallBat() const +{ + static const auto vcvarsall2017 = QStringLiteral("VC/Auxiliary/Build/vcvarsall.bat"); + // 2015, 2013 and 2012 + static const auto vcvarsallOld = QStringLiteral("VC/vcvarsall.bat"); + QDir dir(installDir); + if (dir.exists(vcvarsall2017)) + return dir.absoluteFilePath(vcvarsall2017); + if (dir.exists(vcvarsallOld)) + return dir.absoluteFilePath(vcvarsallOld); + return {}; +} + +std::vector<MSVCInstallInfo> installedMSVCs() +{ + const auto installInfos = installedMSVCsFromVsWhere(); + if (installInfos.empty()) + return installedMSVCsFromRegistry(); + return installInfos; +} + +static std::vector<MSVC> installedCompilers() { std::vector<MSVC> msvcs; std::vector<MSVCInstallInfo> installInfos = installedMSVCsFromVsWhere(); @@ -385,7 +400,7 @@ void msvcProbe(Settings *settings, QList<Profile> &profiles) // 2) Installed MSVCs std::vector<MSVC> msvcs; - const auto instMsvcs = installedMSVCs(); + const auto instMsvcs = installedCompilers(); for (const MSVC &msvc : instMsvcs) { if (msvc.internalVsVersion.majorVersion() < 15) { // Check existence of various install scripts @@ -452,7 +467,7 @@ void msvcProbe(Settings *settings, QList<Profile> &profiles) void createMsvcProfile(const QString &profileName, const QString &compilerFilePath, Settings *settings) { - MSVC msvc(compilerFilePath); + MSVC msvc(compilerFilePath, MSVC::architectureFromClPath(compilerFilePath)); msvc.init(); QList<Profile> dummy; addMSVCPlatform(settings, dummy, profileName, &msvc); diff --git a/src/app/qbs-setup-toolchains/msvcprobe.h b/src/app/qbs-setup-toolchains/msvcprobe.h index 0fa209548..4fa2cde48 100644 --- a/src/app/qbs-setup-toolchains/msvcprobe.h +++ b/src/app/qbs-setup-toolchains/msvcprobe.h @@ -42,11 +42,23 @@ #include <QtCore/qlist.h> +#include <vector> + namespace qbs { class Profile; class Settings; } +struct MSVCInstallInfo +{ + QString version; + QString installDir; + + QString findVcvarsallBat() const; +}; + +std::vector<MSVCInstallInfo> installedMSVCs(); + void createMsvcProfile(const QString &profileName, const QString &compilerFilePath, qbs::Settings *settings); diff --git a/src/app/qbs-setup-toolchains/probe.cpp b/src/app/qbs-setup-toolchains/probe.cpp index c4ccbc517..6deac36ee 100644 --- a/src/app/qbs-setup-toolchains/probe.cpp +++ b/src/app/qbs-setup-toolchains/probe.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "probe.h" +#include "clangclprobe.h" #include "msvcprobe.h" #include "xcodeprobe.h" @@ -66,7 +67,7 @@ using Internal::Tr; static QTextStream qStdout(stdout); static QTextStream qStderr(stderr); -static QString findExecutable(const QString &fileName) +QString findExecutable(const QString &fileName) { QString fullFileName = fileName; if (HostOsInfo::isWindowsHost() @@ -135,6 +136,8 @@ static QStringList toolchainTypeFromCompilerName(const QString &compilerName) { if (compilerName == QLatin1String("cl.exe")) return canonicalToolchain(QStringLiteral("msvc")); + if (compilerName == QLatin1String("clang-cl.exe")) + return canonicalToolchain(QLatin1String("clang-cl")); const auto types = { QStringLiteral("clang"), QStringLiteral("llvm"), QStringLiteral("mingw"), QStringLiteral("gcc") }; for (const auto &type : types) { @@ -409,6 +412,7 @@ void probe(Settings *settings) QList<Profile> profiles; if (HostOsInfo::isWindowsHost()) { msvcProbe(settings, profiles); + clangClProbe(settings, profiles); } else { gccProbe(settings, profiles, QStringLiteral("gcc")); gccProbe(settings, profiles, QStringLiteral("clang")); @@ -451,6 +455,8 @@ void createProfile(const QString &profileName, const QString &toolchainType, if (toolchainTypes.contains(QLatin1String("msvc"))) createMsvcProfile(profileName, compiler.absoluteFilePath(), settings); + else if (toolchainTypes.contains(QLatin1String("clang-cl"))) + createClangClProfile(profileName, compiler.absoluteFilePath(), settings); else if (toolchainTypes.contains(QLatin1String("gcc"))) createGccProfile(compiler.absoluteFilePath(), settings, toolchainTypes, profileName); else if (toolchainTypes.contains(QLatin1String("iar"))) diff --git a/src/app/qbs-setup-toolchains/probe.h b/src/app/qbs-setup-toolchains/probe.h index 5c8774ddb..510747ef7 100644 --- a/src/app/qbs-setup-toolchains/probe.h +++ b/src/app/qbs-setup-toolchains/probe.h @@ -48,6 +48,8 @@ QT_END_NAMESPACE namespace qbs { class Settings; } +QString findExecutable(const QString &fileName); + void createProfile(const QString &profileName, const QString &toolchainType, const QString &compilerFilePath, qbs::Settings *settings); diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro index f395c5458..79b9316ad 100644 --- a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro +++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.pro @@ -4,12 +4,14 @@ TARGET = qbs-setup-toolchains HEADERS += \ commandlineparser.h \ + clangclprobe.h \ msvcprobe.h \ probe.h \ xcodeprobe.h SOURCES += \ commandlineparser.cpp \ + clangclprobe.cpp \ main.cpp \ msvcprobe.cpp \ probe.cpp \ diff --git a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs index 1b7cb6526..f4a521b22 100644 --- a/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs +++ b/src/app/qbs-setup-toolchains/qbs-setup-toolchains.qbs @@ -4,6 +4,8 @@ QbsApp { name: "qbs-setup-toolchains" cpp.dynamicLibraries: qbs.targetOS.contains("windows") ? base.concat("shell32") : base files: [ + "clangclprobe.cpp", + "clangclprobe.h", "commandlineparser.cpp", "commandlineparser.h", "main.cpp", diff --git a/src/lib/corelib/jsextensions/utilitiesextension.cpp b/src/lib/corelib/jsextensions/utilitiesextension.cpp index 5abaccad9..62778665c 100644 --- a/src/lib/corelib/jsextensions/utilitiesextension.cpp +++ b/src/lib/corelib/jsextensions/utilitiesextension.cpp @@ -109,6 +109,7 @@ public: static QScriptValue js_certificateInfo(QScriptContext *context, QScriptEngine *engine); static QScriptValue js_signingIdentities(QScriptContext *context, QScriptEngine *engine); static QScriptValue js_msvcCompilerInfo(QScriptContext *context, QScriptEngine *engine); + static QScriptValue js_clangClCompilerInfo(QScriptContext *context, QScriptEngine *engine); static QScriptValue js_versionCompare(QScriptContext *context, QScriptEngine *engine); @@ -455,6 +456,37 @@ QScriptValue UtilitiesExtension::js_signingIdentities(QScriptContext *context, #endif } +#ifdef Q_OS_WIN +static std::pair<QVariantMap /*result*/, QString /*error*/> msvcCompilerInfoHelper( + const QString &compilerFilePath, + MSVC::CompilerLanguage language, + const QString &vcvarsallPath, + const QString &arch) +{ + MSVC msvc(compilerFilePath, arch); + VsEnvironmentDetector envdetector(vcvarsallPath); + if (!envdetector.start(&msvc)) + return { {}, QStringLiteral("Detecting the MSVC build environment failed: ") + + envdetector.errorString() }; + + try { + QVariantMap envMap; + for (const QString &key : msvc.environment.keys()) + envMap.insert(key, msvc.environment.value(key)); + + return { + QVariantMap { + {QStringLiteral("buildEnvironment"), envMap}, + {QStringLiteral("macros"), msvc.compilerDefines(compilerFilePath, language)}, + }, + {} + }; + } catch (const qbs::ErrorInfo &info) { + return { {}, info.toString() }; + } +} +#endif + QScriptValue UtilitiesExtension::js_msvcCompilerInfo(QScriptContext *context, QScriptEngine *engine) { #ifndef Q_OS_WIN @@ -462,14 +494,12 @@ QScriptValue UtilitiesExtension::js_msvcCompilerInfo(QScriptContext *context, QS return context->throwError(QScriptContext::UnknownError, QStringLiteral("msvcCompilerInfo is not available on this platform")); #else - if (Q_UNLIKELY(context->argumentCount() < 1)) + if (Q_UNLIKELY(context->argumentCount() < 2)) return context->throwError(QScriptContext::SyntaxError, - QStringLiteral("msvcCompilerInfo expects at least 1 argument")); + QStringLiteral("msvcCompilerInfo expects 2 arguments")); const QString compilerFilePath = context->argument(0).toString(); - const QString compilerLanguage = context->argumentCount() > 1 - ? context->argument(1).toString() - : QString(); + const QString compilerLanguage = context->argument(1).toString(); MSVC::CompilerLanguage language; if (compilerLanguage == QStringLiteral("c")) language = MSVC::CLanguage; @@ -479,26 +509,45 @@ QScriptValue UtilitiesExtension::js_msvcCompilerInfo(QScriptContext *context, QS return context->throwError(QScriptContext::TypeError, QStringLiteral("msvcCompilerInfo expects \"c\" or \"cpp\" as its second argument")); - MSVC msvc(compilerFilePath); - VsEnvironmentDetector envdetector; - if (!envdetector.start(&msvc)) - return context->throwError(QScriptContext::UnknownError, - QStringLiteral("Detecting the MSVC build environment failed: ") - + envdetector.errorString()); + const auto result = msvcCompilerInfoHelper( + compilerFilePath, language, {}, MSVC::architectureFromClPath(compilerFilePath)); + if (result.first.isEmpty()) + return context->throwError(QScriptContext::UnknownError, result.second); + return engine->toScriptValue(result.first); +#endif +} - try { - QVariantMap envMap; - for (const QString &key : msvc.environment.keys()) - envMap.insert(key, msvc.environment.value(key)); +QScriptValue UtilitiesExtension::js_clangClCompilerInfo(QScriptContext *context, QScriptEngine *engine) +{ +#ifndef Q_OS_WIN + Q_UNUSED(engine); + return context->throwError(QScriptContext::UnknownError, + QStringLiteral("clangClCompilerInfo is not available on this platform")); +#else + if (Q_UNLIKELY(context->argumentCount() < 4)) + return context->throwError(QScriptContext::SyntaxError, + QStringLiteral("clangClCompilerInfo expects 4 arguments")); - return engine->toScriptValue(QVariantMap { - {QStringLiteral("buildEnvironment"), envMap}, - {QStringLiteral("macros"), msvc.compilerDefines(compilerFilePath, language)}, - }); - } catch (const qbs::ErrorInfo &info) { - return context->throwError(QScriptContext::UnknownError, - info.toString()); - } + const QString compilerFilePath = context->argument(0).toString(); + QString arch = context->argument(1).toString(); + QString vcvarsallPath = context->argument(2).toString(); + const QString compilerLanguage = context->argumentCount() > 3 + ? context->argument(3).toString() + : QString(); + MSVC::CompilerLanguage language; + if (compilerLanguage == QStringLiteral("c")) + language = MSVC::CLanguage; + else if (compilerLanguage == StringConstants::cppLang()) + language = MSVC::CPlusPlusLanguage; + else + return context->throwError(QScriptContext::TypeError, + QStringLiteral("clangClCompilerInfo expects \"c\" or \"cpp\" as its fourth argument")); + + const auto result = msvcCompilerInfoHelper( + compilerFilePath, language, vcvarsallPath, arch); + if (result.first.isEmpty()) + return context->throwError(QScriptContext::UnknownError, result.second); + return engine->toScriptValue(result.first); #endif } @@ -800,6 +849,8 @@ void initializeJsExtensionUtilities(QScriptValue extensionObject) engine->newFunction(UtilitiesExtension::js_signingIdentities, 0)); environmentObj.setProperty(QStringLiteral("msvcCompilerInfo"), engine->newFunction(UtilitiesExtension::js_msvcCompilerInfo, 1)); + environmentObj.setProperty(QStringLiteral("clangClCompilerInfo"), + engine->newFunction(UtilitiesExtension::js_clangClCompilerInfo, 1)); environmentObj.setProperty(QStringLiteral("versionCompare"), engine->newFunction(UtilitiesExtension::js_versionCompare, 2)); environmentObj.setProperty(QStringLiteral("qmlTypeInfo"), diff --git a/src/lib/corelib/tools/hostosinfo.h b/src/lib/corelib/tools/hostosinfo.h index cae451638..d7f718c19 100644 --- a/src/lib/corelib/tools/hostosinfo.h +++ b/src/lib/corelib/tools/hostosinfo.h @@ -74,6 +74,7 @@ public: enum HostOs { HostOsWindows, HostOsLinux, HostOsMacos, HostOsOtherUnix, HostOsOther }; static inline std::string hostOSIdentifier(); + static inline std::string hostOSArchitecture(); static inline std::vector<std::string> hostOSIdentifiers(); static inline std::vector<std::string> canonicalOSIdentifiers(const std::string &os); static inline HostOs hostOs(); @@ -177,6 +178,14 @@ std::string HostOsInfo::hostOSIdentifier() #endif } +std::string HostOsInfo::hostOSArchitecture() +{ + const auto cpuArch = QSysInfo::currentCpuArchitecture(); + if (cpuArch == QLatin1String("i386")) + return "x86"; + return cpuArch.toStdString(); +} + std::vector<std::string> HostOsInfo::hostOSIdentifiers() { return canonicalOSIdentifiers(hostOSIdentifier()); diff --git a/src/lib/corelib/tools/msvcinfo.cpp b/src/lib/corelib/tools/msvcinfo.cpp index c1312c8c9..cffec85b2 100644 --- a/src/lib/corelib/tools/msvcinfo.cpp +++ b/src/lib/corelib/tools/msvcinfo.cpp @@ -198,11 +198,88 @@ static QVariantMap getMsvcDefines(const QString &compilerFilePath, #endif } +/*! + \internal + clang-cl does not support gcc and msvc ways to dump a macros, so we have to use original + clang.exe directly +*/ +static QVariantMap getClangClDefines( + const QString &compilerFilePath, + const QProcessEnvironment &compilerEnv, + MSVC::CompilerLanguage language) +{ +#ifdef Q_OS_WIN + QFileInfo clInfo(compilerFilePath); + QFileInfo clangInfo(clInfo.absolutePath() + QLatin1String("/clang.exe")); + if (!clangInfo.exists()) + throw ErrorInfo(QStringLiteral("%1 does not exist").arg(clangInfo.absoluteFilePath())); + + QString languageSwitch; + switch (language) { + case MSVC::CLanguage: + languageSwitch = QStringLiteral("c"); + break; + case MSVC::CPlusPlusLanguage: + languageSwitch = QStringLiteral("c++"); + break; + } + QStringList args = { + QStringLiteral("-dM"), + QStringLiteral("-E"), + QStringLiteral("-x"), + languageSwitch, + QStringLiteral("NUL"), + }; + const auto lines = QString::fromLocal8Bit( + runProcess( + clangInfo.absoluteFilePath(), + args, + compilerEnv, + true)).split(QLatin1Char('\n')); + QVariantMap result; + for (const auto &line: lines) { + static const auto defineString = QLatin1String("#define "); + if (!line.startsWith(defineString)) { + throw ErrorInfo(QStringLiteral("Unexpected compiler frontend output: ") + + lines.join(QLatin1Char('\n'))); + } + QStringView view(line.data() + defineString.size()); + const auto it = std::find(view.begin(), view.end(), QLatin1Char(' ')); + if (it == view.end()) { + throw ErrorInfo(QStringLiteral("Unexpected compiler frontend output: ") + + lines.join(QLatin1Char('\n'))); + } + QStringView key(view.begin(), it); + QStringView value(it + 1, view.end()); + result.insert(key.toString(), value.isEmpty() ? QVariant() : QVariant(value.toString())); + } + return result; +#else + Q_UNUSED(compilerFilePath); + Q_UNUSED(compilerEnv); + Q_UNUSED(language); + return {}; +#endif +} + void MSVC::init() { determineCompilerVersion(); } +/*! + \internal + Returns the architecture detected from the compiler path. +*/ +QString MSVC::architectureFromClPath(const QString &clPath) +{ + const auto parentDir = QFileInfo(clPath).absolutePath(); + const auto parentDirName = QFileInfo(parentDir).fileName().toLower(); + if (parentDirName == QLatin1String("bin")) + return QStringLiteral("x86"); + return parentDirName; +} + QString MSVC::binPathForArchitecture(const QString &arch) const { QString archSubDir; @@ -222,6 +299,9 @@ QString MSVC::clPathForArchitecture(const QString &arch) const QVariantMap MSVC::compilerDefines(const QString &compilerFilePath, MSVC::CompilerLanguage language) const { + const auto compilerName = QFileInfo(compilerFilePath).fileName().toLower(); + if (compilerName == QLatin1String("clang-cl.exe")) + return getClangClDefines(compilerFilePath, environment, language); return getMsvcDefines(compilerFilePath, environment, language); } diff --git a/src/lib/corelib/tools/msvcinfo.h b/src/lib/corelib/tools/msvcinfo.h index 61a19dc4f..5f542fc97 100644 --- a/src/lib/corelib/tools/msvcinfo.h +++ b/src/lib/corelib/tools/msvcinfo.h @@ -77,20 +77,19 @@ public: MSVC() { } - MSVC(const QString &clPath) + MSVC(const QString &clPath, QString arch): + architecture(std::move(arch)) { QDir parentDir = QFileInfo(clPath).dir(); binPath = parentDir.absolutePath(); QString parentDirName = parentDir.dirName().toLower(); - if (parentDirName == QLatin1String("bin")) - parentDirName = QStringLiteral("x86"); - else + if (parentDirName != QLatin1String("bin")) parentDir.cdUp(); - architecture = parentDirName; vcInstallPath = parentDir.path(); } QBS_EXPORT void init(); + QBS_EXPORT static QString architectureFromClPath(const QString &clPath); QBS_EXPORT QString binPathForArchitecture(const QString &arch) const; QBS_EXPORT QString clPathForArchitecture(const QString &arch) const; QBS_EXPORT QVariantMap compilerDefines(const QString &compilerFilePath, diff --git a/src/lib/corelib/tools/vsenvironmentdetector.cpp b/src/lib/corelib/tools/vsenvironmentdetector.cpp index 869423950..90f6b8921 100644 --- a/src/lib/corelib/tools/vsenvironmentdetector.cpp +++ b/src/lib/corelib/tools/vsenvironmentdetector.cpp @@ -69,8 +69,9 @@ static QString windowsSystem32Path() return {}; } -VsEnvironmentDetector::VsEnvironmentDetector() +VsEnvironmentDetector::VsEnvironmentDetector(QString vcvarsallPath) : m_windowsSystemDirPath(windowsSystem32Path()) + , m_vcvarsallPath(std::move(vcvarsallPath)) { } @@ -137,7 +138,15 @@ QString VsEnvironmentDetector::findVcVarsAllBat(const MSVC &msvc, bool VsEnvironmentDetector::startDetection(const std::vector<MSVC *> &compatibleMSVCs) { std::vector<QString> searchedPaths; - const QString vcvarsallbat = findVcVarsAllBat(**compatibleMSVCs.begin(), searchedPaths); + + if (!m_vcvarsallPath.isEmpty() && !QFileInfo::exists(m_vcvarsallPath)) { + m_errorString = Tr::tr("%1 does not exist.").arg(m_vcvarsallPath); + return false; + } + + const auto vcvarsallbat = !m_vcvarsallPath.isEmpty() + ? m_vcvarsallPath + : findVcVarsAllBat(**compatibleMSVCs.begin(), searchedPaths); if (vcvarsallbat.isEmpty()) { if (!searchedPaths.empty()) { m_errorString = Tr::tr( diff --git a/src/lib/corelib/tools/vsenvironmentdetector.h b/src/lib/corelib/tools/vsenvironmentdetector.h index 1970273ee..7fa152cb6 100644 --- a/src/lib/corelib/tools/vsenvironmentdetector.h +++ b/src/lib/corelib/tools/vsenvironmentdetector.h @@ -57,7 +57,7 @@ class MSVC; class QBS_EXPORT VsEnvironmentDetector { public: - VsEnvironmentDetector(); + explicit VsEnvironmentDetector(QString vcvarsallPath = QString()); bool start(MSVC *msvc); bool start(std::vector<MSVC *> msvcs); @@ -70,6 +70,7 @@ private: void parseBatOutput(const QByteArray &output, std::vector<MSVC *> msvcs); const QString m_windowsSystemDirPath; + const QString m_vcvarsallPath; QString m_errorString; }; diff --git a/tests/auto/api/testdata/app-without-sources/app-without-sources.qbs b/tests/auto/api/testdata/app-without-sources/app-without-sources.qbs index a366e0ce0..4cc25ad66 100644 --- a/tests/auto/api/testdata/app-without-sources/app-without-sources.qbs +++ b/tests/auto/api/testdata/app-without-sources/app-without-sources.qbs @@ -28,6 +28,7 @@ Project { Properties { condition: qbs.toolchain.contains("msvc") cpp.entryPoint: "main" + cpp.dynamicLibraries: ["ucrt", "kernel32"] } cpp.entryPoint: undefined diff --git a/tests/auto/api/testdata/precompiled-header-dynamic/autogen.h.in b/tests/auto/api/testdata/precompiled-header-dynamic/autogen.h.in index c5c182c8f..becfbba89 100644 --- a/tests/auto/api/testdata/precompiled-header-dynamic/autogen.h.in +++ b/tests/auto/api/testdata/precompiled-header-dynamic/autogen.h.in @@ -1 +1,6 @@ +#ifndef AUTOGEN_IN_H +#define AUTOGEN_IN_H + inline void f() { } + +#endif // AUTOGEN_IN_H diff --git a/tests/auto/blackbox/testdata/cxx-language-version/cxx-language-version.qbs b/tests/auto/blackbox/testdata/cxx-language-version/cxx-language-version.qbs index 6beaabfa3..322ded85c 100644 --- a/tests/auto/blackbox/testdata/cxx-language-version/cxx-language-version.qbs +++ b/tests/auto/blackbox/testdata/cxx-language-version/cxx-language-version.qbs @@ -13,7 +13,10 @@ CppApplication { var isEvenNewerMsvc; var isOlderMsvc; var isGcc; - if (toolchain.contains("msvc")) { + if (toolchain.contains("clang-cl")) { + isEvenNewerMsvc = true; + isNewerMsvc = true; + } else if (toolchain.contains("msvc")) { if (compilerVersion >= "19.12.25831") isEvenNewerMsvc = true; if (compilerVersion >= "18.00.30723") diff --git a/tests/auto/blackbox/testdata/enableRtti/main.cpp b/tests/auto/blackbox/testdata/enableRtti/main.cpp index 4dcd5d2e8..4011cf823 100644 --- a/tests/auto/blackbox/testdata/enableRtti/main.cpp +++ b/tests/auto/blackbox/testdata/enableRtti/main.cpp @@ -32,6 +32,10 @@ #error RTTI is disabled! #endif +#if defined(_MSC_VER) && !defined (_CPPRTTI) +#error RTTI is disabled! +#endif + class I { public: virtual ~I() { } diff --git a/tests/auto/blackbox/testdata/minimumSystemVersion/main.cpp b/tests/auto/blackbox/testdata/minimumSystemVersion/main.cpp index 4cc99756b..47c9ed007 100644 --- a/tests/auto/blackbox/testdata/minimumSystemVersion/main.cpp +++ b/tests/auto/blackbox/testdata/minimumSystemVersion/main.cpp @@ -42,6 +42,10 @@ int main(int argc, char *argv[]) return 1; #ifdef _WIN32 +#if defined(__clang__) + std::cout << "Unsupported compiler" << std::endl; + return 0; +#endif #if defined(WINVER) && defined(QBS_WINVER) std::cout << "WINVER=" << WINVER << std::endl; std::string command = TOOLCHAIN_INSTALL_PATH; diff --git a/tests/auto/blackbox/testdata/no-exported-symbols/no-exported-symbols.qbs b/tests/auto/blackbox/testdata/no-exported-symbols/no-exported-symbols.qbs index 4bda00caf..346a94e21 100644 --- a/tests/auto/blackbox/testdata/no-exported-symbols/no-exported-symbols.qbs +++ b/tests/auto/blackbox/testdata/no-exported-symbols/no-exported-symbols.qbs @@ -12,7 +12,7 @@ Project { id: toolchainProbe property stringList toolchain: qbs.toolchain configure: { - if (toolchain.contains("msvc")) + if (toolchain.contains("msvc") && !toolchain.contains("clang-cl")) console.info("compiler is MSVC") else console.info("compiler is not MSVC") diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp index ca11e4e3e..219a473e0 100644 --- a/tests/auto/blackbox/tst_blackbox.cpp +++ b/tests/auto/blackbox/tst_blackbox.cpp @@ -6414,6 +6414,8 @@ void TestBlackbox::minimumSystemVersion() QbsRunParameters params({ "-f", file + ".qbs" }); params.command = "run"; QCOMPARE(runQbs(params), 0); + if (m_qbsStdout.contains("Unsupported compiler")) + QSKIP("Unsupported compiler"); if (!m_qbsStdout.contains(output.toUtf8())) { qDebug() << "expected output:" << qPrintable(output); qDebug() << "actual output:" << m_qbsStdout.constData(); |