From 62e07306481373d9d9b6b656d855b204aa6964f3 Mon Sep 17 00:00:00 2001 From: Ivan Komissarov Date: Tue, 19 Mar 2019 22:24:33 +0100 Subject: Add support for the clang-cl compiler Task-number: QBS-1316 Change-Id: Ibf9da364610c260ead088a8990a70c7739d53c39 Reviewed-by: Christian Kandeler --- src/app/qbs-setup-toolchains/clangclprobe.cpp | 172 +++++++++++++++++++++ src/app/qbs-setup-toolchains/clangclprobe.h | 55 +++++++ src/app/qbs-setup-toolchains/msvcprobe.cpp | 33 ++-- src/app/qbs-setup-toolchains/msvcprobe.h | 12 ++ src/app/qbs-setup-toolchains/probe.cpp | 8 +- src/app/qbs-setup-toolchains/probe.h | 2 + .../qbs-setup-toolchains/qbs-setup-toolchains.pro | 2 + .../qbs-setup-toolchains/qbs-setup-toolchains.qbs | 2 + .../corelib/jsextensions/utilitiesextension.cpp | 97 +++++++++--- src/lib/corelib/tools/hostosinfo.h | 9 ++ src/lib/corelib/tools/msvcinfo.cpp | 80 ++++++++++ src/lib/corelib/tools/msvcinfo.h | 9 +- src/lib/corelib/tools/vsenvironmentdetector.cpp | 13 +- src/lib/corelib/tools/vsenvironmentdetector.h | 3 +- 14 files changed, 456 insertions(+), 41 deletions(-) create mode 100644 src/app/qbs-setup-toolchains/clangclprobe.cpp create mode 100644 src/app/qbs-setup-toolchains/clangclprobe.h (limited to 'src') 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 +#include +#include +#include +#include + +#include +#include + +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 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 &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 + +namespace qbs { +class Profile; +class Settings; +} + +void createClangClProfile( + const QString &profileName, const QString &compilerFilePath, qbs::Settings *settings); + +void clangClProbe(qbs::Settings *settings, QList &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 envVarCandidates{"ProgramFiles", "ProgramFiles(x86)"}; @@ -288,7 +282,28 @@ static std::vector installedMSVCsFromRegistry() return result; } -static std::vector 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 installedMSVCs() +{ + const auto installInfos = installedMSVCsFromVsWhere(); + if (installInfos.empty()) + return installedMSVCsFromRegistry(); + return installInfos; +} + +static std::vector installedCompilers() { std::vector msvcs; std::vector installInfos = installedMSVCsFromVsWhere(); @@ -385,7 +400,7 @@ void msvcProbe(Settings *settings, QList &profiles) // 2) Installed MSVCs std::vector 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 &profiles) void createMsvcProfile(const QString &profileName, const QString &compilerFilePath, Settings *settings) { - MSVC msvc(compilerFilePath); + MSVC msvc(compilerFilePath, MSVC::architectureFromClPath(compilerFilePath)); msvc.init(); QList 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 +#include + namespace qbs { class Profile; class Settings; } +struct MSVCInstallInfo +{ + QString version; + QString installDir; + + QString findVcvarsallBat() const; +}; + +std::vector 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 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 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 hostOSIdentifiers(); static inline std::vector 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 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 &compatibleMSVCs) { std::vector 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 msvcs); @@ -70,6 +70,7 @@ private: void parseBatOutput(const QByteArray &output, std::vector msvcs); const QString m_windowsSystemDirPath; + const QString m_vcvarsallPath; QString m_errorString; }; -- cgit v1.2.3