diff --git a/src/plugins/projectexplorer/abstractmsvctoolchain.cpp b/src/plugins/projectexplorer/abstractmsvctoolchain.cpp index 64de2dd7074..70c35391862 100644 --- a/src/plugins/projectexplorer/abstractmsvctoolchain.cpp +++ b/src/plugins/projectexplorer/abstractmsvctoolchain.cpp @@ -53,10 +53,6 @@ AbstractMsvcToolChain::AbstractMsvcToolChain(Core::Id typeId, Core::Id l, Detect m_abi(abi), m_vcvarsBat(vcvarsBat) { - Q_ASSERT(abi.os() == Abi::WindowsOS); - Q_ASSERT(abi.binaryFormat() == Abi::PEFormat); - Q_ASSERT(abi.osFlavor() != Abi::WindowsMSysFlavor); - Q_ASSERT(!m_vcvarsBat.isEmpty()); setLanguage(l); } diff --git a/src/plugins/projectexplorer/abstractmsvctoolchain.h b/src/plugins/projectexplorer/abstractmsvctoolchain.h index 0851e09cc09..8dc0485a0e4 100644 --- a/src/plugins/projectexplorer/abstractmsvctoolchain.h +++ b/src/plugins/projectexplorer/abstractmsvctoolchain.h @@ -106,7 +106,7 @@ protected: mutable QList m_headerPaths; Abi m_abi; - QString m_vcvarsBat; + QString m_vcvarsBat; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 80973d751c4..d56d6a64136 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include @@ -84,6 +85,8 @@ const MsvcPlatform platforms[] = {MsvcToolChain::amd64_x86, "amd64_x86", "/bin/amd64_x86", "vcvarsamd64_x86.bat"} }; +static QList g_availableMsvcToolchains; + static const MsvcPlatform *platformEntry(MsvcToolChain::Platform t) { for (const MsvcPlatform &p : platforms) { @@ -776,20 +779,138 @@ MsvcToolChainConfigWidget::MsvcToolChainConfigWidget(ToolChain *tc) : ClangClToolChainConfigWidget::ClangClToolChainConfigWidget(ToolChain *tc) : MsvcBasedToolChainConfigWidget(tc) - , m_llvmDirLabel(new QLabel(this)) { - m_llvmDirLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); - m_mainLayout->addRow(tr("LLVM:"), m_llvmDirLabel); + if (tc->isAutoDetected()) { + m_llvmDirLabel = new QLabel(this); + m_llvmDirLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); + m_mainLayout->addRow(tr("&Compiler path:"), m_llvmDirLabel); + } else { + const QStringList gnuVersionArgs = QStringList("--version"); + m_compilerCommand = new Utils::PathChooser(this); + m_compilerCommand->setExpectedKind(Utils::PathChooser::ExistingCommand); + m_compilerCommand->setCommandVersionArguments(gnuVersionArgs); + m_compilerCommand->setHistoryCompleter("PE.Clang.Command.History"); + m_mainLayout->addRow(tr("&Compiler path:"), m_compilerCommand); + } addErrorLabel(); setFromClangClToolChain(); + + if (m_compilerCommand) { + connect(m_compilerCommand, &Utils::PathChooser::rawPathChanged, + this, &ClangClToolChainConfigWidget::dirty); + } } void ClangClToolChainConfigWidget::setFromClangClToolChain() { setFromMsvcToolChain(); - const ClangClToolChain *tc = static_cast(toolChain()); - QTC_ASSERT(tc, return); - m_llvmDirLabel->setText(QDir::toNativeSeparators(tc-> llvmDir())); + const auto *clangClToolChain = static_cast(toolChain()); + if (clangClToolChain->isAutoDetected()) + m_llvmDirLabel->setText(QDir::toNativeSeparators(clangClToolChain->clangPath())); + else + m_compilerCommand->setFileName(Utils::FileName::fromString(clangClToolChain->clangPath())); +} + +static const MsvcToolChain *findMsvcToolChain(unsigned char wordWidth, Abi::OSFlavor flavor) +{ + return Utils::findOrDefault(g_availableMsvcToolchains, + [wordWidth, flavor] (const MsvcToolChain *tc) + { const Abi abi = tc->targetAbi(); + return abi.osFlavor() == flavor + && wordWidth == abi.wordWidth();} ); +} + +static QVersionNumber clangClVersion(const QString& clangClPath) +{ + Utils::SynchronousProcess clangClProcess; + const Utils::SynchronousProcessResponse response = clangClProcess.runBlocking( + clangClPath, {QStringLiteral("--version")}); + if (response.result != Utils::SynchronousProcessResponse::Finished || response.exitCode != 0) + return {}; + const QRegularExpressionMatch match = QRegularExpression( + QStringLiteral("clang version (\\d+(\\.\\d+)+)")).match(response.stdOut()); + if (!match.hasMatch()) + return {}; + return QVersionNumber::fromString(match.captured(1)); +} + +static const MsvcToolChain *selectMsvcToolChain(const QString &clangClPath, + unsigned char wordWidth) +{ + const MsvcToolChain *toolChain = nullptr; + const QVersionNumber version = clangClVersion(clangClPath); + if (version.majorVersion() >= 6) + toolChain = findMsvcToolChain(wordWidth, Abi::WindowsMsvc2017Flavor); + if (!toolChain) { + toolChain = findMsvcToolChain(wordWidth, Abi::WindowsMsvc2015Flavor); + if (!toolChain) + toolChain = findMsvcToolChain(wordWidth, Abi::WindowsMsvc2013Flavor); + } + return toolChain; +} + +static void detectClangClToolChainInPath(const QString &clangClPath, + QList &list) +{ + const unsigned char wordWidth = Utils::is64BitWindowsBinary(clangClPath) ? 64 : 32; + const MsvcToolChain *toolChain = selectMsvcToolChain(clangClPath, wordWidth); + + if (!toolChain) { + qWarning("Unable to find a suitable MSVC version for \"%s\".", + qPrintable(QDir::toNativeSeparators(clangClPath))); + return; + } + + const Abi targetAbi = toolChain->targetAbi(); + const QString name = QStringLiteral("LLVM ") + QString::number(wordWidth) + + QStringLiteral("bit based on ") + + Abi::toString(targetAbi.osFlavor()).toUpper(); + for (auto language: {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}) { + auto *clangClToolChain = new ClangClToolChain(name, clangClPath, + language, ToolChain::AutoDetection); + clangClToolChain->resetMsvcToolChain(toolChain); + list.append(clangClToolChain); + } +} + +static QString compilerFromPath(const QString &path) +{ + return path + "/bin/clang-cl.exe"; +} + +void ClangClToolChainConfigWidget::applyImpl() +{ + Utils::FileName clangClPath = m_compilerCommand->fileName(); + auto clangClToolChain = static_cast(toolChain()); + clangClToolChain->setClangPath(clangClPath.toString()); + + if (clangClPath.fileName() != "clang-cl.exe") { + clangClToolChain->resetMsvcToolChain(); + setFromClangClToolChain(); + return; + } + + QList results; + detectClangClToolChainInPath(clangClPath.toString(), results); + + if (results.isEmpty()) { + clangClToolChain->resetMsvcToolChain(); + } else { + for (const ToolChain *toolchain : results) { + if (toolchain->language() == clangClToolChain->language()) { + clangClToolChain->resetMsvcToolChain(static_cast(toolchain)); + break; + } + } + + qDeleteAll(results); + } + setFromClangClToolChain(); +} + +void ClangClToolChainConfigWidget::discardImpl() +{ + setFromClangClToolChain(); } // -------------------------------------------------------------------------- @@ -797,36 +918,33 @@ void ClangClToolChainConfigWidget::setFromClangClToolChain() // clang-cl.exe as a [to some extent] compatible drop-in replacement for cl. // -------------------------------------------------------------------------- -static QString compilerFromPath(const QString &path) -{ - return path + "/bin/clang-cl.exe"; -} - -ClangClToolChain::ClangClToolChain(const QString &name, const QString &llvmDir, - const Abi &abi, - const QString &varsBat, const QString &varsBatArg, Core::Id language, +ClangClToolChain::ClangClToolChain(const QString &name, const QString &clangPath, + Core::Id language, Detection d) - : MsvcToolChain(Constants::CLANG_CL_TOOLCHAIN_TYPEID, name, abi, varsBat, varsBatArg, language, d) - , m_llvmDir(llvmDir) -{ } + : MsvcToolChain(Constants::CLANG_CL_TOOLCHAIN_TYPEID, name, Abi(), "", "", language, d) + , m_clangPath(clangPath) +{ +} ClangClToolChain::ClangClToolChain() : MsvcToolChain(Constants::CLANG_CL_TOOLCHAIN_TYPEID) { } bool ClangClToolChain::isValid() const { - return MsvcToolChain::isValid() && compilerCommand().exists(); + return MsvcToolChain::isValid() && compilerCommand().exists() + && compilerCommand().fileName() == "clang-cl.exe"; } void ClangClToolChain::addToEnvironment(Utils::Environment &env) const { MsvcToolChain::addToEnvironment(env); - env.prependOrSetPath(m_llvmDir + QStringLiteral("/bin")); + QDir path = QFileInfo(m_clangPath).absoluteDir(); // bin folder + env.prependOrSetPath(path.canonicalPath()); } Utils::FileName ClangClToolChain::compilerCommand() const { - return Utils::FileName::fromString(compilerFromPath(m_llvmDir)); + return Utils::FileName::fromString(m_clangPath); } QString ClangClToolChain::typeDisplayName() const @@ -856,7 +974,7 @@ static inline QString llvmDirKey() { return QStringLiteral("ProjectExplorer.Clan QVariantMap ClangClToolChain::toMap() const { QVariantMap result = MsvcToolChain::toMap(); - result.insert(llvmDirKey(), m_llvmDir); + result.insert(llvmDirKey(), m_clangPath); return result; } @@ -864,10 +982,11 @@ bool ClangClToolChain::fromMap(const QVariantMap &data) { if (!MsvcToolChain::fromMap(data)) return false; - const QString llvmDir = data.value(llvmDirKey()).toString(); - if (llvmDir.isEmpty()) + const QString clangPath = data.value(llvmDirKey()).toString(); + if (clangPath.isEmpty()) return false; - m_llvmDir = llvmDir; + m_clangPath = clangPath; + return true; } @@ -876,6 +995,19 @@ ToolChainConfigWidget *ClangClToolChain::configurationWidget() return new ClangClToolChainConfigWidget(this); } +void ClangClToolChain::resetMsvcToolChain(const MsvcToolChain *base) +{ + if (!base) { + m_abi = Abi(); + m_vcvarsBat.clear(); + setVarsBatArg(""); + return; + } + m_abi = base->targetAbi(); + m_vcvarsBat = base->varsBat(); + setVarsBatArg(base->varsBatArg()); +} + // -------------------------------------------------------------------------- // MsvcToolChainFactory // -------------------------------------------------------------------------- @@ -969,96 +1101,6 @@ static void detectCppBuildTools2015(QList *list) } } -static ToolChain *findMsvcToolChain(const QList &list, - unsigned char wordWidth, Abi::OSFlavor flavor) -{ - return Utils::findOrDefault(list, [wordWidth, flavor] (const ToolChain *tc) - { const Abi abi = tc->targetAbi(); - return abi.osFlavor() == flavor - && wordWidth == abi.wordWidth();} ); -} - -static QVersionNumber clangClVersion(const QString& clangClPath) -{ - Utils::SynchronousProcess clangClProcess; - const Utils::SynchronousProcessResponse response = clangClProcess.runBlocking( - clangClPath, {QStringLiteral("--version")}); - if (response.result != Utils::SynchronousProcessResponse::Finished || response.exitCode != 0) - return {}; - const QRegularExpressionMatch match = QRegularExpression( - QStringLiteral("clang version (\\d+(\\.\\d+)+)")).match(response.stdOut()); - if (!match.hasMatch()) - return {}; - return QVersionNumber::fromString(match.captured(1)); -} - -static const ToolChain *selectMsvcToolChain(const QString &clangClPath, - const QList &list, - unsigned char wordWidth) -{ - const ToolChain *toolChain = nullptr; - const QVersionNumber version = clangClVersion(clangClPath); - if (version.majorVersion() >= 6) - toolChain = findMsvcToolChain(list, wordWidth, Abi::WindowsMsvc2017Flavor); - if (!toolChain) { - toolChain = findMsvcToolChain(list, wordWidth, Abi::WindowsMsvc2015Flavor); - if (!toolChain) - toolChain = findMsvcToolChain(list, wordWidth, Abi::WindowsMsvc2013Flavor); - } - return toolChain; -} - -static void detectClangClToolChainInPath(const QString &clangClPath, QList *list) -{ - const unsigned char wordWidth = Utils::is64BitWindowsBinary(clangClPath) ? 64 : 32; - const ToolChain *toolChain = selectMsvcToolChain(clangClPath, *list, wordWidth); - - QDir path = QFileInfo(clangClPath).absoluteDir(); // bin folder - path.cdUp(); // cd to LLVM root - const QString rootPath = path.canonicalPath(); - - if (!toolChain) { - qWarning("Unable to find a suitable MSVC version for \"%s\".", - qPrintable(QDir::toNativeSeparators(rootPath))); - return; - } - const MsvcToolChain *msvcToolChain = static_cast(toolChain); - const Abi targetAbi = msvcToolChain->targetAbi(); - const QString name = QStringLiteral("LLVM ") + QString::number(wordWidth) - + QStringLiteral("bit based on ") - + Abi::toString(targetAbi.osFlavor()).toUpper(); - for (auto language: {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}) { - list->append(new ClangClToolChain(name, rootPath, targetAbi, - msvcToolChain->varsBat(), msvcToolChain->varsBatArg(), - language, ToolChain::AutoDetection)); - } -} - -// Detect Clang-cl on top of MSVC2017, MSVC2015 or MSVC2013. -static void detectClangClToolChain(QList *list) -{ -#ifdef Q_OS_WIN64 - const char registryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\LLVM\\LLVM"; -#else - const char registryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\LLVM\\LLVM"; -#endif - - const QSettings registry(QLatin1String(registryNode), QSettings::NativeFormat); - if (registry.status() == QSettings::NoError) { - const QString path = QDir::cleanPath(registry.value(QStringLiteral(".")).toString()); - const QString clangClPath = compilerFromPath(path); - if (!path.isEmpty()) { - detectClangClToolChainInPath(path, list); - return; - } - } - - const Utils::Environment systemEnvironment = Utils::Environment::systemEnvironment(); - const Utils::FileName clangClPath = systemEnvironment.searchInPath("clang-cl"); - if (!clangClPath.isEmpty()) - detectClangClToolChainInPath(clangClPath.toString(), list); -} - QList MsvcToolChainFactory::autoDetect(const QList &alreadyKnown) { QList results; @@ -1131,11 +1173,53 @@ QList MsvcToolChainFactory::autoDetect(const QList &al detectCppBuildTools2015(&results); - detectClangClToolChain(&results); + for (const ToolChain *toolchain : results) + g_availableMsvcToolchains.append(static_cast(toolchain)); return results; } +ClangClToolChainFactory::ClangClToolChainFactory() +{ + setDisplayName(tr("clang-cl")); +} + +bool ClangClToolChainFactory::canCreate() +{ + return !g_availableMsvcToolchains.isEmpty(); +} + +QList ClangClToolChainFactory::autoDetect(const QList &alreadyKnown) +{ +#ifdef Q_OS_WIN64 + const char registryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\WOW6432Node\\LLVM\\LLVM"; +#else + const char registryNode[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\LLVM\\LLVM"; +#endif + + QList results; + const QSettings registry(QLatin1String(registryNode), QSettings::NativeFormat); + if (registry.status() == QSettings::NoError) { + const QString path = QDir::cleanPath(registry.value(QStringLiteral(".")).toString()); + const QString clangClPath = compilerFromPath(path); + if (!path.isEmpty()) { + detectClangClToolChainInPath(clangClPath, results); + return results; + } + } + + const Utils::Environment systemEnvironment = Utils::Environment::systemEnvironment(); + const Utils::FileName clangClPath = systemEnvironment.searchInPath("clang-cl"); + if (!clangClPath.isEmpty()) + detectClangClToolChainInPath(clangClPath.toString(), results); + return results; +} + +ToolChain *ClangClToolChainFactory::create(Core::Id l) +{ + return new ClangClToolChain("clang-cl", "", l, ToolChain::ManualDetection); +} + bool MsvcToolChain::operator ==(const ToolChain &other) const { if (!AbstractMsvcToolChain::operator ==(other)) @@ -1147,7 +1231,7 @@ bool MsvcToolChain::operator ==(const ToolChain &other) const bool MsvcToolChainFactory::canRestore(const QVariantMap &data) { const Core::Id id = typeIdFromMap(data); - return id == Constants::MSVC_TOOLCHAIN_TYPEID || id == Constants::CLANG_CL_TOOLCHAIN_TYPEID; + return id == Constants::MSVC_TOOLCHAIN_TYPEID; } template @@ -1162,11 +1246,19 @@ ToolChainType *readFromMap(const QVariantMap &data) ToolChain *MsvcToolChainFactory::restore(const QVariantMap &data) { - const Core::Id id = typeIdFromMap(data); - if (id == Constants::CLANG_CL_TOOLCHAIN_TYPEID) - return readFromMap(data); return readFromMap(data); } +bool ClangClToolChainFactory::canRestore(const QVariantMap &data) +{ + const Core::Id id = typeIdFromMap(data); + return id == Constants::CLANG_CL_TOOLCHAIN_TYPEID; +} + +ToolChain *ClangClToolChainFactory::restore(const QVariantMap &data) +{ + return readFromMap(data); +} + } // namespace Internal } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/msvctoolchain.h b/src/plugins/projectexplorer/msvctoolchain.h index e71773800eb..fe1922c2f05 100644 --- a/src/plugins/projectexplorer/msvctoolchain.h +++ b/src/plugins/projectexplorer/msvctoolchain.h @@ -34,6 +34,8 @@ QT_FORWARD_DECLARE_CLASS(QLabel) QT_FORWARD_DECLARE_CLASS(QVersionNumber) +namespace Utils { class PathChooser; } + namespace ProjectExplorer { namespace Internal { @@ -74,6 +76,7 @@ public: ToolChain *clone() const override; QString varsBatArg() const { return m_varsBatArg; } + void setVarsBatArg(const QString &varsBA) { m_varsBatArg = varsBA; } bool operator == (const ToolChain &) const override; @@ -103,11 +106,9 @@ private: class ClangClToolChain : public MsvcToolChain { public: - explicit ClangClToolChain(const QString &name, const QString &llvmDir, - const Abi &abi, - const QString &varsBat, const QString &varsBatArg, - Core::Id language, - Detection d = ManualDetection); + ClangClToolChain(const QString &name, const QString &llvmDir, + Core::Id language, + Detection d); ClangClToolChain(); bool isValid() const override; @@ -121,10 +122,13 @@ public: bool fromMap(const QVariantMap &data) override; ToolChainConfigWidget *configurationWidget() override; - QString llvmDir() const { return m_llvmDir; } + const QList &msvcToolchains() const; + QString clangPath() const { return m_clangPath; } + void setClangPath(const QString &path) { m_clangPath = path; } + void resetMsvcToolChain(const MsvcToolChain *base = nullptr); private: - QString m_llvmDir; + QString m_clangPath; }; // -------------------------------------------------------------------------- @@ -144,11 +148,27 @@ public: bool canRestore(const QVariantMap &data) override; ToolChain *restore(const QVariantMap &data) override; - ToolChainConfigWidget *configurationWidget(ToolChain *); static QString vcVarsBatFor(const QString &basePath, MsvcToolChain::Platform platform, const QVersionNumber &v); }; +class ClangClToolChainFactory : public MsvcToolChainFactory +{ + Q_OBJECT + +public: + ClangClToolChainFactory(); + + QList autoDetect(const QList &alreadyKnown) override; + + bool canRestore(const QVariantMap &data) override; + ToolChain *restore(const QVariantMap &data) override; + + bool canCreate() override; + ToolChain *create(Core::Id l) override; +}; + + // -------------------------------------------------------------------------- // MsvcBasedToolChainConfigWidget // -------------------------------------------------------------------------- @@ -197,12 +217,14 @@ public: explicit ClangClToolChainConfigWidget(ToolChain *); protected: - void discardImpl() override { setFromClangClToolChain(); } + void applyImpl() override; + void discardImpl() override; private: void setFromClangClToolChain(); - QLabel *m_llvmDirLabel; + QLabel *m_llvmDirLabel = nullptr; + Utils::PathChooser *m_compilerCommand = nullptr; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 72f9eda2050..cccca0ea4a2 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -447,6 +447,7 @@ public: #ifdef Q_OS_WIN WinDebugInterface m_winDebugInterface; MsvcToolChainFactory m_mscvToolChainFactory; + ClangClToolChainFactory m_clangClToolChainFactory; #else LinuxIccToolChainFactory m_linuxToolChainFactory; #endif