aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Komissarov <ABBAPOH@gmail.com>2020-02-12 16:53:00 +0100
committerIvan Komissarov <ABBAPOH@gmail.com>2020-02-17 10:26:13 +0000
commitff53ff1fda2689e71b9d0d8ec73e55f414a78f79 (patch)
tree767c915f21e88e10efe7e427ab01d6c3a600692a
parentab2dd5f1b20f07f2734267095af9b9deaede7079 (diff)
Autodetect clang-cl by using Probe
This allows to build projects without calling "qbs setup-toolchains" first by simply calling "qbs build qbs.toolchainType:clang-cl" Change-Id: I86d135ddf2cb588f322709e4e7a265cb6fd7772f Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
-rw-r--r--share/qbs/imports/qbs/Probes/ClangClBinaryProbe.qbs73
-rw-r--r--share/qbs/modules/cpp/windows-clang-cl.qbs4
-rw-r--r--src/app/qbs-setup-toolchains/clangclprobe.cpp120
-rw-r--r--src/lib/corelib/corelib.qbs2
-rw-r--r--src/lib/corelib/jsextensions/utilitiesextension.cpp44
-rw-r--r--src/lib/corelib/tools/clangclinfo.cpp176
-rw-r--r--src/lib/corelib/tools/clangclinfo.h61
-rw-r--r--src/lib/corelib/tools/tools.pri2
-rw-r--r--tests/auto/blackbox/tst_blackbox.cpp15
9 files changed, 384 insertions, 113 deletions
diff --git a/share/qbs/imports/qbs/Probes/ClangClBinaryProbe.qbs b/share/qbs/imports/qbs/Probes/ClangClBinaryProbe.qbs
new file mode 100644
index 000000000..f4916a37a
--- /dev/null
+++ b/share/qbs/imports/qbs/Probes/ClangClBinaryProbe.qbs
@@ -0,0 +1,73 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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.FileInfo
+import qbs.ModUtils
+import qbs.Utilities
+import "path-probe.js" as PathProbeConfigure
+
+BinaryProbe {
+ // output
+ property string vcvarsallPath;
+
+ configure: {
+ var _selectors;
+ var results = PathProbeConfigure.configure(_selectors, names, nameSuffixes, nameFilter,
+ candidateFilter, searchPaths, pathSuffixes,
+ platformSearchPaths, environmentPaths,
+ platformEnvironmentPaths, pathListSeparator);
+ var compilerPath;
+ if (results.found)
+ compilerPath = results.files[0].filePath;
+ var compilers = Utilities.installedClangCls(compilerPath);
+ if (compilers.length >= 1) {
+ var result = {};
+ result.fileName = "clang-cl.exe";
+ result.path = compilers[0].toolchainInstallPath;
+ result.filePath = FileInfo.joinPaths(result.path, result.fileName);
+ result.candidatePaths = result.filePath;
+ result.vcvarsallPath = compilers[0].vcvarsallPath;
+ results.found = true;
+ results.files = [result];
+ }
+
+ found = results.found;
+ allResults = results.files;
+
+ if (allResults.length === 1) {
+ var result = allResults[0];
+ candidatePaths = result.candidatePaths;
+ path = result.path;
+ filePath = result.filePath;
+ fileName = result.fileName;
+ vcvarsallPath = result.vcvarsallPath;
+ }
+ }
+}
diff --git a/share/qbs/modules/cpp/windows-clang-cl.qbs b/share/qbs/modules/cpp/windows-clang-cl.qbs
index d57f5a7ad..a34a67ad2 100644
--- a/share/qbs/modules/cpp/windows-clang-cl.qbs
+++ b/share/qbs/modules/cpp/windows-clang-cl.qbs
@@ -40,7 +40,7 @@ MsvcBaseModule {
qbs.toolchain && qbs.toolchain.contains('clang-cl')
priority: 100
- Probes.BinaryProbe {
+ Probes.ClangClBinaryProbe {
id: clangPathProbe
condition: !toolchainInstallPath && !_skipAllChecks
names: ["clang-cl"]
@@ -78,7 +78,7 @@ MsvcBaseModule {
driverLinkerFlags: "-fuse-ld=" + linkerVariant
}
- property string vcvarsallPath
+ property string vcvarsallPath : clangPathProbe.found ? clangPathProbe.vcvarsallPath : undefined
compilerName: "clang-cl.exe"
linkerName: "lld-link.exe"
diff --git a/src/app/qbs-setup-toolchains/clangclprobe.cpp b/src/app/qbs-setup-toolchains/clangclprobe.cpp
index ee445a2fb..6eff83217 100644
--- a/src/app/qbs-setup-toolchains/clangclprobe.cpp
+++ b/src/app/qbs-setup-toolchains/clangclprobe.cpp
@@ -44,6 +44,7 @@
#include "../shared/logging/consolelogger.h"
#include <logging/translator.h>
+#include <tools/clangclinfo.h>
#include <tools/hostosinfo.h>
#include <tools/msvcinfo.h>
#include <tools/profile.h>
@@ -55,6 +56,7 @@
using qbs::Settings;
using qbs::Profile;
+using qbs::Internal::ClangClInfo;
using qbs::Internal::HostOsInfo;
using qbs::Internal::MSVC;
using qbs::Internal::MSVCInstallInfo;
@@ -63,11 +65,6 @@ using qbs::Internal::Tr;
namespace {
-QString getToolchainInstallPath(const QFileInfo &compiler)
-{
- return compiler.path(); // 1 level up
-}
-
Profile createProfileHelper(
Settings *settings,
const QString &profileName,
@@ -88,90 +85,13 @@ Profile createProfileHelper(
return profile;
}
-std::vector<MSVCInstallInfo> compatibleMsvcs()
-{
- auto msvcs = MSVCInstallInfo::installedMSVCs(ConsoleLogger::instance());
- 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(const std::vector<MSVCInstallInfo> &msvcs)
+QString findClangCl()
{
- for (const auto &msvc: msvcs) {
- const auto vcvarsallPath = msvc.findVcvarsallBat();
- if (!vcvarsallPath.isEmpty())
- return vcvarsallPath;
- }
- return {};
-}
-
-QString wow6432Key()
-{
-#ifdef Q_OS_WIN64
- return QStringLiteral("\\Wow6432Node");
-#else
- return {};
-#endif
-}
-
-// Function can modify passed list in case it has found clang in one of the VS installation -
-// in that case, there's no point in looking for vcvarsall.bat among all installed VSs, we just
-// use .bat file corresponding to that particular VS installation
-QString findClangCl(std::vector<MSVCInstallInfo> *msvcs)
-{
- Q_ASSERT(msvcs);
const auto compilerName = HostOsInfo::appendExecutableSuffix(QStringLiteral("clang-cl"));
const auto compilerFromPath = findExecutable(compilerName);
if (!compilerFromPath.isEmpty())
return compilerFromPath;
- const QSettings registry(
- QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\LLVM\\LLVM").arg(wow6432Key()),
- QSettings::NativeFormat);
- const auto key = QStringLiteral(".");
- if (registry.contains(key)) {
- const auto compilerPath = QDir::fromNativeSeparators(registry.value(key).toString())
- + QStringLiteral("/bin/") + compilerName;
- if (QFileInfo(compilerPath).exists())
- return compilerPath;
- }
-
- // this branch can be useful in case user had two LLVM installations (e.g. 32bit & 64bit)
- // but uninstalled one - in that case, registry will be empty
- static const char * const envVarCandidates[] = {"ProgramFiles", "ProgramFiles(x86)"};
- for (const auto &envVar : envVarCandidates) {
- const auto value
- = QDir::fromNativeSeparators(QString::fromLocal8Bit(qgetenv(envVar)));
- const auto compilerPath = value + QStringLiteral("/LLVM/bin/") + compilerName;
- if (QFileInfo(compilerPath).exists())
- return compilerPath;
- }
-
- // If we didn't find custom LLVM installation, try to find if it's installed with Visual Studio
- for (const auto &msvc : *msvcs) {
- const auto compilerPath = QStringLiteral("%1/VC/Tools/Llvm/bin/%2")
- .arg(msvc.installDir, compilerName);
- if (QFileInfo(compilerPath).exists()) {
- *msvcs = {msvc}; // reduce list to one installation
- return compilerPath;
- }
- }
-
return {};
}
@@ -180,18 +100,13 @@ QString findClangCl(std::vector<MSVCInstallInfo> *msvcs)
void createClangClProfile(const QFileInfo &compiler, Settings *settings,
const QString &profileName)
{
- const auto compilerName = QStringLiteral("clang-cl");
- const auto vcvarsallPath = findCompatibleVcsarsallBat(compatibleMsvcs());
- if (vcvarsallPath.isEmpty()) {
- qbsWarning()
- << Tr::tr("%1 requires installed Visual Studio 2017 or newer, but none was found.")
- .arg(compilerName);
+ const auto clangCl = ClangClInfo::fromCompilerFilePath(
+ compiler.filePath(), ConsoleLogger::instance());
+ if (clangCl.isEmpty())
return;
- }
-
- const auto toolchainInstallPath = getToolchainInstallPath(compiler);
const auto hostArch = QString::fromStdString(HostOsInfo::hostOSArchitecture());
- createProfileHelper(settings, profileName, toolchainInstallPath, vcvarsallPath, hostArch);
+ createProfileHelper(
+ settings, profileName, clangCl.toolchainInstallPath, clangCl.vcvarsallPath, hostArch);
}
/*!
@@ -200,33 +115,24 @@ void createClangClProfile(const QFileInfo &compiler, Settings *settings,
*/
void clangClProbe(Settings *settings, std::vector<Profile> &profiles)
{
- auto msvcs = compatibleMsvcs();
-
const auto compilerName = QStringLiteral("clang-cl");
qbsInfo() << Tr::tr("Trying to detect %1...").arg(compilerName);
- const QString compilerFilePath = findClangCl(&msvcs);
- if (compilerFilePath.isEmpty()) {
+ const auto clangCls = ClangClInfo::installedCompilers(
+ {findClangCl()}, ConsoleLogger::instance());
+ if (clangCls.empty()) {
qbsInfo() << Tr::tr("%1 was not found.").arg(compilerName);
return;
}
- const QFileInfo compiler(compilerFilePath);
- const auto vcvarsallPath = findCompatibleVcsarsallBat(msvcs);
- if (vcvarsallPath.isEmpty()) {
- qbsWarning()
- << Tr::tr("%1 requires installed Visual Studio 2017 or newer, but none was found.")
- .arg(compilerName);
- return;
- }
+ const auto clangCl = clangCls.front();
const QString architectures[] = {
QStringLiteral("x86_64"),
QStringLiteral("x86")
};
- const auto toolchainInstallPath = getToolchainInstallPath(compiler);
for (const auto &arch: architectures) {
const auto profileName = QStringLiteral("clang-cl-%1").arg(arch);
auto profile = createProfileHelper(
- settings, profileName, toolchainInstallPath, vcvarsallPath, arch);
+ settings, profileName, clangCl.toolchainInstallPath, clangCl.vcvarsallPath, arch);
profiles.push_back(std::move(profile));
}
}
diff --git a/src/lib/corelib/corelib.qbs b/src/lib/corelib/corelib.qbs
index bd44ac3ed..619c0f0e3 100644
--- a/src/lib/corelib/corelib.qbs
+++ b/src/lib/corelib/corelib.qbs
@@ -395,6 +395,8 @@ QbsLibrary {
"buildgraphlocker.cpp",
"buildgraphlocker.h",
"buildoptions.cpp",
+ "clangclinfo.cpp",
+ "clangclinfo.h",
"cleanoptions.cpp",
"codelocation.cpp",
"commandechomode.cpp",
diff --git a/src/lib/corelib/jsextensions/utilitiesextension.cpp b/src/lib/corelib/jsextensions/utilitiesextension.cpp
index 2d29cb7c5..6c693cb61 100644
--- a/src/lib/corelib/jsextensions/utilitiesextension.cpp
+++ b/src/lib/corelib/jsextensions/utilitiesextension.cpp
@@ -72,6 +72,7 @@ struct fat_arch_64 {
#ifdef Q_OS_WIN
+#include <tools/clangclinfo.h>
#include <tools/msvcinfo.h>
#include <tools/vsenvironmentdetector.h>
#endif
@@ -88,6 +89,10 @@ struct fat_arch_64 {
namespace qbs {
namespace Internal {
+class DummyLogSink : public ILogSink {
+ void doPrintMessage(LoggerLevel, const QString &, const QString &) override { }
+};
+
class UtilitiesExtension : public QObject, QScriptable
{
Q_OBJECT
@@ -111,6 +116,7 @@ public:
static QScriptValue js_msvcCompilerInfo(QScriptContext *context, QScriptEngine *engine);
static QScriptValue js_clangClCompilerInfo(QScriptContext *context, QScriptEngine *engine);
static QScriptValue js_installedMSVCs(QScriptContext *context, QScriptEngine *engine);
+ static QScriptValue js_installedClangCls(QScriptContext *context, QScriptEngine *engine);
static QScriptValue js_versionCompare(QScriptContext *context, QScriptEngine *engine);
@@ -530,7 +536,11 @@ QScriptValue UtilitiesExtension::js_clangClCompilerInfo(QScriptContext *context,
QStringLiteral("clangClCompilerInfo expects 4 arguments"));
const QString compilerFilePath = context->argument(0).toString();
- QString arch = context->argument(1).toString();
+ // architecture cannot be empty as vcvarsall.bat requires at least 1 arg, so fallback
+ // to host architecture if none is present
+ QString arch = !context->argument(1).isNull() && !context->argument(1).isUndefined()
+ ? context->argument(1).toString()
+ : QString::fromStdString(HostOsInfo::hostOSArchitecture());
QString vcvarsallPath = context->argument(2).toString();
const QString compilerLanguage = context->argumentCount() > 3
? context->argument(3).toString()
@@ -570,9 +580,7 @@ QScriptValue UtilitiesExtension::js_installedMSVCs(QScriptContext *context, QScr
? value0.toString()
: hostArch;
- class LogSink : public ILogSink {
- void doPrintMessage(LoggerLevel, const QString &, const QString &) override { }
- } dummySink;
+ DummyLogSink dummySink;
Logger dummyLogger(&dummySink);
auto msvcs = MSVC::installedCompilers(dummyLogger);
@@ -589,6 +597,32 @@ QScriptValue UtilitiesExtension::js_installedMSVCs(QScriptContext *context, QScr
#endif
}
+QScriptValue UtilitiesExtension::js_installedClangCls(
+ QScriptContext *context, QScriptEngine *engine)
+{
+#ifndef Q_OS_WIN
+ Q_UNUSED(engine);
+ return context->throwError(QScriptContext::UnknownError,
+ QStringLiteral("installedClangCls is not available on this platform"));
+#else
+ if (Q_UNLIKELY(context->argumentCount() != 1)) {
+ return context->throwError(QScriptContext::SyntaxError,
+ QStringLiteral("installedClangCls expects 1 arguments"));
+ }
+
+ const auto value0 = context->argument(0);
+ const auto path = !value0.isNull() && !value0.isUndefined() ? value0.toString() : QString();
+
+ DummyLogSink dummySink;
+ Logger dummyLogger(&dummySink);
+ auto compilers = ClangClInfo::installedCompilers({path}, dummyLogger);
+ QVariantList result;
+ for (const auto &compiler: compilers)
+ result.append(compiler.toVariantMap());
+ return engine->toScriptValue(result);
+#endif
+}
+
QScriptValue UtilitiesExtension::js_versionCompare(QScriptContext *context, QScriptEngine *engine)
{
if (context->argumentCount() == 2) {
@@ -891,6 +925,8 @@ void initializeJsExtensionUtilities(QScriptValue extensionObject)
engine->newFunction(UtilitiesExtension::js_clangClCompilerInfo, 1));
environmentObj.setProperty(QStringLiteral("installedMSVCs"),
engine->newFunction(UtilitiesExtension::js_installedMSVCs, 1));
+ environmentObj.setProperty(QStringLiteral("installedClangCls"),
+ engine->newFunction(UtilitiesExtension::js_installedClangCls, 1));
environmentObj.setProperty(QStringLiteral("versionCompare"),
engine->newFunction(UtilitiesExtension::js_versionCompare, 2));
environmentObj.setProperty(QStringLiteral("qmlTypeInfo"),
diff --git a/src/lib/corelib/tools/clangclinfo.cpp b/src/lib/corelib/tools/clangclinfo.cpp
new file mode 100644
index 000000000..4e1022c28
--- /dev/null
+++ b/src/lib/corelib/tools/clangclinfo.cpp
@@ -0,0 +1,176 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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.
+**
+****************************************************************************/
+
+#include "clangclinfo.h"
+
+#include "hostosinfo.h"
+#include "msvcinfo.h"
+#include "stlutils.h"
+
+namespace qbs {
+namespace Internal {
+
+static std::vector<MSVCInstallInfo> compatibleMsvcs(Logger &logger)
+{
+ auto msvcs = MSVCInstallInfo::installedMSVCs(logger);
+ 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;
+}
+
+static QString findCompatibleVcsarsallBat(const std::vector<MSVCInstallInfo> &msvcs)
+{
+ for (const auto &msvc: msvcs) {
+ const auto vcvarsallPath = msvc.findVcvarsallBat();
+ if (!vcvarsallPath.isEmpty())
+ return vcvarsallPath;
+ }
+ return {};
+}
+
+static QString wow6432Key()
+{
+#ifdef Q_OS_WIN64
+ return QStringLiteral("\\Wow6432Node");
+#else
+ return {};
+#endif
+}
+
+static QString getToolchainInstallPath(const QFileInfo &compiler)
+{
+ return compiler.path(); // 1 level up
+}
+
+QVariantMap ClangClInfo::toVariantMap() const
+{
+ return {
+ {QStringLiteral("toolchainInstallPath"), toolchainInstallPath},
+ {QStringLiteral("vcvarsallPath"), vcvarsallPath},
+ };
+}
+
+ClangClInfo ClangClInfo::fromCompilerFilePath(const QString &path, Logger &logger)
+{
+ const auto compilerName = QStringLiteral("clang-cl");
+ const auto vcvarsallPath = findCompatibleVcsarsallBat(compatibleMsvcs(logger));
+ if (vcvarsallPath.isEmpty()) {
+ logger.qbsWarning()
+ << Tr::tr("%1 requires installed Visual Studio 2017 or newer, but none was found.")
+ .arg(compilerName);
+ return {};
+ }
+
+ const auto toolchainInstallPath = getToolchainInstallPath(path);
+ return {toolchainInstallPath, vcvarsallPath};
+}
+
+std::vector<ClangClInfo> ClangClInfo::installedCompilers(
+ const std::vector<QString> &extraPaths, Logger &logger)
+{
+ std::vector<QString> compilerPaths;
+ compilerPaths.reserve(extraPaths.size());
+ std::copy_if(extraPaths.begin(), extraPaths.end(),
+ std::back_inserter(compilerPaths),
+ [](const QString &path){ return !path.isEmpty(); });
+ const auto compilerName = HostOsInfo::appendExecutableSuffix(QStringLiteral("clang-cl"));
+
+ const QSettings registry(
+ QStringLiteral("HKEY_LOCAL_MACHINE\\SOFTWARE%1\\LLVM\\LLVM").arg(wow6432Key()),
+ QSettings::NativeFormat);
+ const auto key = QStringLiteral(".");
+ if (registry.contains(key)) {
+ const auto compilerPath = QDir::fromNativeSeparators(registry.value(key).toString())
+ + QStringLiteral("/bin/") + compilerName;
+ if (QFileInfo(compilerPath).exists())
+ compilerPaths.push_back(compilerPath);
+ }
+
+ // this branch can be useful in case user had two LLVM installations (e.g. 32bit & 64bit)
+ // but uninstalled one - in that case, registry will be empty
+ static const char * const envVarCandidates[] = {"ProgramFiles", "ProgramFiles(x86)"};
+ for (const auto &envVar : envVarCandidates) {
+ const auto value
+ = QDir::fromNativeSeparators(QString::fromLocal8Bit(qgetenv(envVar)));
+ const auto compilerPath = value + QStringLiteral("/LLVM/bin/") + compilerName;
+ if (QFileInfo(compilerPath).exists() && !contains(compilerPaths, compilerPath))
+ compilerPaths.push_back(compilerPath);
+ }
+
+ const auto msvcs = compatibleMsvcs(logger);
+ const auto vcvarsallPath = findCompatibleVcsarsallBat(msvcs);
+ if (vcvarsallPath.isEmpty()) {
+ logger.qbsWarning()
+ << Tr::tr("%1 requires installed Visual Studio 2017 or newer, but none was found.")
+ .arg(compilerName);
+ return {};
+ }
+
+ std::vector<ClangClInfo> result;
+ result.reserve(compilerPaths.size() + msvcs.size());
+
+ for (const auto &path: compilerPaths)
+ result.push_back({getToolchainInstallPath(path), vcvarsallPath});
+
+ // If we didn't find custom LLVM installation, try to find if it's installed with Visual Studio
+ for (const auto &msvc : msvcs) {
+ const auto compilerPath = QStringLiteral("%1/VC/Tools/Llvm/bin/%2")
+ .arg(msvc.installDir, compilerName);
+ if (QFileInfo(compilerPath).exists()) {
+ const auto vcvarsallPath = msvc.findVcvarsallBat();
+ if (vcvarsallPath.isEmpty()) {
+ logger.qbsWarning()
+ << Tr::tr("Found LLVM in %1, but vcvarsall.bat is missing.")
+ .arg(msvc.installDir);
+ }
+
+ result.push_back({getToolchainInstallPath(compilerPath), vcvarsallPath});
+ }
+ }
+
+ return result;
+}
+
+} // namespace Internal
+} // namespace qbs
diff --git a/src/lib/corelib/tools/clangclinfo.h b/src/lib/corelib/tools/clangclinfo.h
new file mode 100644
index 000000000..76ae169f2
--- /dev/null
+++ b/src/lib/corelib/tools/clangclinfo.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 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.
+**
+****************************************************************************/
+
+#ifndef QBS_CLANGCLINFO_H
+#define QBS_CLANGCLINFO_H
+
+#include "logging/logger.h"
+
+#include <QtCore/qstring.h>
+
+#include <vector>
+
+namespace qbs {
+namespace Internal {
+
+class ClangClInfo
+{
+public:
+ QString toolchainInstallPath;
+ QString vcvarsallPath;
+
+ bool isEmpty() const { return toolchainInstallPath.isEmpty() && vcvarsallPath.isEmpty(); }
+
+ QBS_EXPORT QVariantMap toVariantMap() const;
+
+ QBS_EXPORT static ClangClInfo fromCompilerFilePath(const QString &path, Logger &logger);
+ QBS_EXPORT static std::vector<ClangClInfo> installedCompilers(
+ const std::vector<QString> &extraPaths, qbs::Internal::Logger &logger);
+};
+
+} // namespace Internal
+} // namespace qbs
+
+#endif // QBS_CLANGCLINFO_H
diff --git a/src/lib/corelib/tools/tools.pri b/src/lib/corelib/tools/tools.pri
index 89d752671..00d87ecc7 100644
--- a/src/lib/corelib/tools/tools.pri
+++ b/src/lib/corelib/tools/tools.pri
@@ -10,6 +10,7 @@ QBS_SYSTEM_SETTINGS_DIR = $$(QBS_SYSTEM_SETTINGS_DIR)
HEADERS += \
$$PWD/architectures.h \
$$PWD/buildgraphlocker.h \
+ $$PWD/clangclinfo.h \
$$PWD/codelocation.h \
$$PWD/commandechomode.h \
$$PWD/dynamictypecheck.h \
@@ -68,6 +69,7 @@ HEADERS += \
SOURCES += \
$$PWD/architectures.cpp \
$$PWD/buildgraphlocker.cpp \
+ $$PWD/clangclinfo.cpp \
$$PWD/codelocation.cpp \
$$PWD/commandechomode.cpp \
$$PWD/error.cpp \
diff --git a/tests/auto/blackbox/tst_blackbox.cpp b/tests/auto/blackbox/tst_blackbox.cpp
index a30d57be9..a1672f4c1 100644
--- a/tests/auto/blackbox/tst_blackbox.cpp
+++ b/tests/auto/blackbox/tst_blackbox.cpp
@@ -3476,10 +3476,25 @@ void TestBlackbox::emptyProfile()
{
QDir::setCurrent(testDataDir + "/empty-profile");
+ const SettingsPtr s = settings();
+ const Profile buildProfile(profileName(), s.get());
+ const QStringList toolchain = buildProfile.value("qbs.toolchain").toStringList();
+
QTemporaryDir tempDir;
QbsRunParameters params;
params.profile.clear();
params.settingsDir = tempDir.path(); // should be no settings here
+ if (toolchain.contains(QLatin1String("clang-cl")))
+ params.arguments = QStringList{QStringLiteral("qbs.toolchainType:clang-cl")};
+ else if (toolchain.contains(QLatin1String("msvc")))
+ params.arguments = QStringList{QStringLiteral("qbs.toolchainType:msvc")};
+ else if (toolchain.contains(QLatin1String("xcode")))
+ params.arguments = QStringList{QStringLiteral("qbs.toolchainType:xcode")};
+ else if (toolchain.contains(QLatin1String("clang")))
+ params.arguments = QStringList{QStringLiteral("qbs.toolchainType:clang")};
+ else if (toolchain.contains(QLatin1String("gcc")))
+ params.arguments = QStringList{QStringLiteral("qbs.toolchainType:gcc")};
+
QCOMPARE(runQbs(params), 0);
}