From 020883c47f62151684f5d72cb7204b6fc7061934 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Thu, 11 Jul 2024 13:03:12 +0200 Subject: [PATCH] ProjectExplorer: Stop pretending that C and C++ compilers are unrelated Motivation: a) It was ridiculous that when users wanted to manually add a new toolchain, they had to do the entire setup twice. b) It was equally weird that users had to take care to choose matching toolchains when setting up a kit, or indeed that it was even possible to mix random toolchains in the first place. User-visible changes: - The "C" and "C++" categories in the toolchain settings page have been merged into a single "C/C++" category. - When adding a new toolchain, the "C" and "C++" sub-menus are gone. Instead, the toolchain config widget offers two path choosers if the respective toolchain type supports C and C++ compilers. - By default, the C++ compiler file path is derived from the C compiler file path automatically, so the user usually has to enter only the former. - In the kit settings page, the "C" and "C++" toolchain combo boxes have been replaced by a single "C/C++" combo box, relieving the user of the responsibility to choose two matching toolchains. Implementation: The notion that a Toolchain object corresponds to a single compiler is so deeply engrained in the code that it cannot realistically be changed in the short term. We therefore introduce the concept of a "toolchain bundle" as an additional layer that groups matching C and C++ toolchains together. This way, most code dealing with toolchains stays unchanged, and only the presentation layer (i.e. the toolchain and kit settings pages) needed to be rewritten. Once set up in a bundle, toolchains stay implicitly linked together so the matching only needs to be done once. In follow-up patches, we will make use of toolchain bundles in all the places where kits are auto-created, eliminating the risk of mixing incompatible toolchains in a kit. Change-Id: Ie6c5add9963e7c1096268dd77acd624671b2674f Reviewed-by: Christian Stenger Reviewed-by: hjk --- src/plugins/android/androidtoolchain.cpp | 13 + src/plugins/baremetal/iarewtoolchain.cpp | 102 +++--- src/plugins/baremetal/keiltoolchain.cpp | 81 ++--- src/plugins/baremetal/sdcctoolchain.cpp | 58 ++-- src/plugins/cppeditor/projectinfo_test.cpp | 5 +- src/plugins/ios/iosconfigurations.cpp | 7 + src/plugins/mcusupport/test/unittest.cpp | 2 - src/plugins/nim/project/nimtoolchain.cpp | 57 +-- src/plugins/nim/project/nimtoolchain.h | 3 +- .../projectexplorer/customtoolchain.cpp | 120 +++---- src/plugins/projectexplorer/gcctoolchain.cpp | 264 ++++++++------ src/plugins/projectexplorer/gcctoolchain.h | 17 +- src/plugins/projectexplorer/kitaspects.cpp | 89 +++-- src/plugins/projectexplorer/msvctoolchain.cpp | 145 ++++---- src/plugins/projectexplorer/msvctoolchain.h | 6 +- .../projectexplorer/projectexplorer.cpp | 2 + src/plugins/projectexplorer/toolchain.cpp | 260 +++++++++++++- src/plugins/projectexplorer/toolchain.h | 112 +++++- .../projectexplorer/toolchainconfigwidget.cpp | 124 ++++++- .../projectexplorer/toolchainconfigwidget.h | 29 +- .../projectexplorer/toolchainmanager.cpp | 36 ++ .../projectexplorer/toolchainmanager.h | 6 +- .../projectexplorer/toolchainoptionspage.cpp | 324 +++++++++++------- .../toolchainsettingsaccessor.cpp | 7 +- src/plugins/qnx/qnxtoolchain.cpp | 63 ++-- src/plugins/qnx/qnxtoolchain.h | 2 - .../webassembly/webassemblytoolchain.cpp | 9 +- 27 files changed, 1277 insertions(+), 666 deletions(-) diff --git a/src/plugins/android/androidtoolchain.cpp b/src/plugins/android/androidtoolchain.cpp index 5bb5aa4ddea..02da5692eff 100644 --- a/src/plugins/android/androidtoolchain.cpp +++ b/src/plugins/android/androidtoolchain.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #include @@ -246,6 +247,18 @@ public: ProjectExplorer::Constants::CXX_LANGUAGE_ID}); setToolchainConstructor([] { return new AndroidToolchain; }); } + +private: + std::unique_ptr createConfigurationWidget( + const ToolchainBundle &bundle) const final + { + return GccToolchain::createConfigurationWidget(bundle); + } + + FilePath correspondingCompilerCommand(const FilePath &srcPath, Id targetLang) const override + { + return GccToolchain::correspondingCompilerCommand(srcPath, targetLang, "clang", "clang++"); + } }; void setupAndroidToolchain() diff --git a/src/plugins/baremetal/iarewtoolchain.cpp b/src/plugins/baremetal/iarewtoolchain.cpp index d5029c78788..198af3a0203 100644 --- a/src/plugins/baremetal/iarewtoolchain.cpp +++ b/src/plugins/baremetal/iarewtoolchain.cpp @@ -253,12 +253,10 @@ static QString buildDisplayName(Abi::Architecture arch, Utils::Id language, // IarToolchainConfigWidget -class IarToolchain; - class IarToolchainConfigWidget final : public ToolchainConfigWidget { public: - explicit IarToolchainConfigWidget(IarToolchain *tc); + explicit IarToolchainConfigWidget(const ProjectExplorer::ToolchainBundle &bundle); private: void applyImpl() final; @@ -267,13 +265,13 @@ private: void makeReadOnlyImpl() final; void setFromToolchain(); - void handleCompilerCommandChange(); + void handleCompilerCommandChange(Id language); void handlePlatformCodeGenFlagsChange(); - PathChooser *m_compilerCommand = nullptr; AbiWidget *m_abiWidget = nullptr; QLineEdit *m_platformCodeGenFlagsLineEdit = nullptr; - Macros m_macros; + Macros m_cMacros; + Macros m_cxxMacros; }; // IarToolchain @@ -301,8 +299,6 @@ public: void addToEnvironment(Environment &env) const final; QList createOutputParsers() const final { return {new IarParser()}; } - std::unique_ptr createConfigurationWidget() final; - bool operator==(const Toolchain &other) const final; QStringList extraCodeModelFlags() const final { return m_extraCodeModelFlags(); } @@ -386,11 +382,6 @@ void IarToolchain::addToEnvironment(Environment &env) const env.prependOrSetPath(compilerCommand().parentDir()); } -std::unique_ptr IarToolchain::createConfigurationWidget() -{ - return std::make_unique(this); -} - bool IarToolchain::operator==(const Toolchain &other) const { if (!Toolchain::operator==(other)) @@ -401,7 +392,6 @@ bool IarToolchain::operator==(const Toolchain &other) const && m_extraCodeModelFlags() == customTc->m_extraCodeModelFlags(); } - // IarToolchainFactory class IarToolchainFactory final : public ToolchainFactory @@ -419,6 +409,8 @@ public: Toolchains autoDetect(const ToolchainDetector &detector) const final; Toolchains detectForImport(const ToolchainDescription &tcd) const final; + std::unique_ptr createConfigurationWidget( + const ProjectExplorer::ToolchainBundle &bundle) const final; private: Toolchains autoDetectToolchains(const Candidates &candidates, @@ -509,6 +501,12 @@ Toolchains IarToolchainFactory::detectForImport(const ToolchainDescription &tcd) return { autoDetectToolchain({tcd.compilerPath, {}}, tcd.language) }; } +std::unique_ptr IarToolchainFactory::createConfigurationWidget( + const ToolchainBundle &bundle) const +{ + return std::make_unique(bundle); +} + Toolchains IarToolchainFactory::autoDetectToolchains( const Candidates &candidates, const Toolchains &alreadyKnown) const { @@ -562,16 +560,12 @@ Toolchains IarToolchainFactory::autoDetectToolchain(const Candidate &candidate, // IarToolchainConfigWidget -IarToolchainConfigWidget::IarToolchainConfigWidget(IarToolchain *tc) : - ToolchainConfigWidget(tc), - m_compilerCommand(new PathChooser), +IarToolchainConfigWidget::IarToolchainConfigWidget(const ToolchainBundle &bundle) : + ToolchainConfigWidget(bundle), m_abiWidget(new AbiWidget) { - m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); - m_compilerCommand->setHistoryCompleter("PE.IAREW.Command.History"); - m_mainLayout->addRow(Tr::tr("&Compiler path:"), m_compilerCommand); m_platformCodeGenFlagsLineEdit = new QLineEdit(this); - m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->extraCodeModelFlags())); + m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(bundle.extraCodeModelFlags())); m_mainLayout->addRow(Tr::tr("Platform codegen flags:"), m_platformCodeGenFlagsLineEdit); m_mainLayout->addRow(Tr::tr("&ABI:"), m_abiWidget); @@ -580,7 +574,7 @@ IarToolchainConfigWidget::IarToolchainConfigWidget(IarToolchain *tc) : addErrorLabel(); setFromToolchain(); - connect(m_compilerCommand, &PathChooser::rawPathChanged, + connect(this, &ToolchainConfigWidget::compilerCommandChanged, this, &IarToolchainConfigWidget::handleCompilerCommandChange); connect(m_platformCodeGenFlagsLineEdit, &QLineEdit::editingFinished, this, &IarToolchainConfigWidget::handlePlatformCodeGenFlagsChange); @@ -590,39 +584,36 @@ IarToolchainConfigWidget::IarToolchainConfigWidget(IarToolchain *tc) : void IarToolchainConfigWidget::applyImpl() { - if (toolchain()->isAutoDetected()) + if (bundle().isAutoDetected()) return; - const auto tc = static_cast(toolchain()); - const QString displayName = tc->displayName(); - tc->setCompilerCommand(m_compilerCommand->filePath()); + bundle().forEach([this](IarToolchain &tc) { + tc.m_extraCodeModelFlags.setValue(splitString(m_platformCodeGenFlagsLineEdit->text())); + }); + bundle().setTargetAbi(m_abiWidget->currentAbi()); - tc->m_extraCodeModelFlags.setValue(splitString(m_platformCodeGenFlagsLineEdit->text())); - - tc->setTargetAbi(m_abiWidget->currentAbi()); - tc->setDisplayName(displayName); - - if (m_macros.isEmpty()) + if (m_cMacros.isEmpty() && m_cxxMacros.isEmpty()) return; - const auto languageVersion = Toolchain::languageVersion(tc->language(), m_macros); - tc->predefinedMacrosCache()->insert({}, {m_macros, languageVersion}); + bundle().forEach([this](IarToolchain &tc) { + const Macros ¯os = tc.language() == ProjectExplorer::Constants::C_LANGUAGE_ID + ? m_cMacros : m_cxxMacros; + const auto languageVersion = Toolchain::languageVersion(tc.language(), macros); + tc.predefinedMacrosCache()->insert({}, {macros, languageVersion}); + }); setFromToolchain(); } bool IarToolchainConfigWidget::isDirtyImpl() const { - const auto tc = static_cast(toolchain()); - return m_compilerCommand->filePath() != tc->compilerCommand() - || m_platformCodeGenFlagsLineEdit->text() != ProcessArgs::joinArgs(tc->extraCodeModelFlags()) - || m_abiWidget->currentAbi() != tc->targetAbi() - ; + return m_platformCodeGenFlagsLineEdit->text() + != ProcessArgs::joinArgs(bundle().extraCodeModelFlags()) + || m_abiWidget->currentAbi() != bundle().targetAbi(); } void IarToolchainConfigWidget::makeReadOnlyImpl() { - m_compilerCommand->setReadOnly(true); m_platformCodeGenFlagsLineEdit->setEnabled(false); m_abiWidget->setEnabled(false); } @@ -630,28 +621,25 @@ void IarToolchainConfigWidget::makeReadOnlyImpl() void IarToolchainConfigWidget::setFromToolchain() { const QSignalBlocker blocker(this); - const auto tc = static_cast(toolchain()); - m_compilerCommand->setFilePath(tc->compilerCommand()); - m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->extraCodeModelFlags())); - m_abiWidget->setAbis({}, tc->targetAbi()); - const bool haveCompiler = m_compilerCommand->filePath().isExecutableFile(); - m_abiWidget->setEnabled(haveCompiler && !tc->isAutoDetected()); + m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(bundle().extraCodeModelFlags())); + m_abiWidget->setAbis({}, bundle().targetAbi()); + m_abiWidget->setEnabled(hasAnyCompiler() && !bundle().isAutoDetected()); } -void IarToolchainConfigWidget::handleCompilerCommandChange() +void IarToolchainConfigWidget::handleCompilerCommandChange(Id language) { - const FilePath compilerPath = m_compilerCommand->filePath(); + const bool isC = language == ProjectExplorer::Constants::C_LANGUAGE_ID; + const FilePath compilerPath = compilerCommand(language); + Macros ¯os = isC ? m_cMacros : m_cxxMacros; const bool haveCompiler = compilerPath.isExecutableFile(); if (haveCompiler) { const auto env = Environment::systemEnvironment(); const QStringList extraArgs = splitString(m_platformCodeGenFlagsLineEdit->text()); - const Id languageId = toolchain()->language(); - m_macros = dumpPredefinedMacros(compilerPath, extraArgs, languageId, env); - const Abi guessed = guessAbi(m_macros); + macros = dumpPredefinedMacros(compilerPath, extraArgs, language, env); + const Abi guessed = guessAbi(macros); m_abiWidget->setAbis({}, guessed); } - - m_abiWidget->setEnabled(haveCompiler); + m_abiWidget->setEnabled(hasAnyCompiler() && !bundle().isAutoDetected()); emit dirty(); } @@ -659,10 +647,12 @@ void IarToolchainConfigWidget::handlePlatformCodeGenFlagsChange() { const QString str1 = m_platformCodeGenFlagsLineEdit->text(); const QString str2 = ProcessArgs::joinArgs(splitString(str1)); - if (str1 != str2) + if (str1 != str2) { m_platformCodeGenFlagsLineEdit->setText(str2); - else - handleCompilerCommandChange(); + } else { + handleCompilerCommandChange(ProjectExplorer::Constants::C_LANGUAGE_ID); + handleCompilerCommandChange(ProjectExplorer::Constants::CXX_LANGUAGE_ID); + } } } // BareMetal::Internal diff --git a/src/plugins/baremetal/keiltoolchain.cpp b/src/plugins/baremetal/keiltoolchain.cpp index bc260130b2b..0bc91e7b2fe 100644 --- a/src/plugins/baremetal/keiltoolchain.cpp +++ b/src/plugins/baremetal/keiltoolchain.cpp @@ -391,7 +391,7 @@ class KeilToolchain; class KeilToolchainConfigWidget final : public ToolchainConfigWidget { public: - explicit KeilToolchainConfigWidget(KeilToolchain *tc); + explicit KeilToolchainConfigWidget(const ToolchainBundle &bundle); private: void applyImpl() final; @@ -400,10 +400,9 @@ private: void makeReadOnlyImpl() final; void setFromToolchain(); - void handleCompilerCommandChange(); + void handleCompilerCommandChange(Id language); void handlePlatformCodeGenFlagsChange(); - PathChooser *m_compilerCommand = nullptr; AbiWidget *m_abiWidget = nullptr; QLineEdit *m_platformCodeGenFlagsLineEdit = nullptr; Macros m_macros; @@ -436,8 +435,6 @@ public: QList createOutputParsers() const final { return {new KeilParser}; } - std::unique_ptr createConfigurationWidget() final; - bool operator==(const Toolchain &other) const final; QStringList extraCodeModelFlags() const final; @@ -508,11 +505,6 @@ void KeilToolchain::addToEnvironment(Environment &env) const env.prependOrSetPath(compilerCommand().parentDir()); } -std::unique_ptr KeilToolchain::createConfigurationWidget() -{ - return std::make_unique(this); -} - bool KeilToolchain::operator ==(const Toolchain &other) const { if (!Toolchain::operator ==(other)) @@ -545,6 +537,11 @@ public: } Toolchains autoDetect(const ToolchainDetector &detector) const final; + std::unique_ptr createConfigurationWidget( + const ToolchainBundle &bundle) const final + { + return std::make_unique(bundle); + } private: Toolchains autoDetectToolchains(const Candidates &candidates, @@ -717,16 +714,11 @@ Toolchains KeilToolchainFactory::autoDetectToolchain(const Candidate &candidate, // KeilToolchainConfigWidget -KeilToolchainConfigWidget::KeilToolchainConfigWidget(KeilToolchain *tc) : - ToolchainConfigWidget(tc), - m_compilerCommand(new PathChooser), +KeilToolchainConfigWidget::KeilToolchainConfigWidget(const ToolchainBundle &bundle) : + ToolchainConfigWidget(bundle), m_abiWidget(new AbiWidget) { - m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); - m_compilerCommand->setHistoryCompleter("PE.KEIL.Command.History"); - m_mainLayout->addRow(Tr::tr("&Compiler path:"), m_compilerCommand); m_platformCodeGenFlagsLineEdit = new QLineEdit(this); - m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->extraCodeModelFlags())); m_mainLayout->addRow(Tr::tr("Platform codegen flags:"), m_platformCodeGenFlagsLineEdit); m_mainLayout->addRow(Tr::tr("&ABI:"), m_abiWidget); @@ -735,7 +727,7 @@ KeilToolchainConfigWidget::KeilToolchainConfigWidget(KeilToolchain *tc) : addErrorLabel(); setFromToolchain(); - connect(m_compilerCommand, &PathChooser::rawPathChanged, + connect(this, &ToolchainConfigWidget::compilerCommandChanged, this, &KeilToolchainConfigWidget::handleCompilerCommandChange); connect(m_platformCodeGenFlagsLineEdit, &QLineEdit::editingFinished, this, &KeilToolchainConfigWidget::handlePlatformCodeGenFlagsChange); @@ -745,37 +737,34 @@ KeilToolchainConfigWidget::KeilToolchainConfigWidget(KeilToolchain *tc) : void KeilToolchainConfigWidget::applyImpl() { - if (toolchain()->isAutoDetected()) + if (bundle().isAutoDetected()) return; - const auto tc = static_cast(toolchain()); - const QString displayName = tc->displayName(); - tc->setCompilerCommand(m_compilerCommand->filePath()); - tc->m_extraCodeModelFlags.setValue(splitString(m_platformCodeGenFlagsLineEdit->text())); - tc->setTargetAbi(m_abiWidget->currentAbi()); - tc->setDisplayName(displayName); + bundle().setTargetAbi(m_abiWidget->currentAbi()); + bundle().forEach([this](KeilToolchain &tc) { + tc.m_extraCodeModelFlags.setValue(splitString(m_platformCodeGenFlagsLineEdit->text())); + }); if (m_macros.isEmpty()) return; - const auto languageVersion = Toolchain::languageVersion(tc->language(), m_macros); - tc->predefinedMacrosCache()->insert({}, {m_macros, languageVersion}); + bundle().forEach([this](KeilToolchain &tc) { + const auto languageVersion = Toolchain::languageVersion(tc.language(), m_macros); + tc.predefinedMacrosCache()->insert({}, {m_macros, languageVersion}); + }); setFromToolchain(); } bool KeilToolchainConfigWidget::isDirtyImpl() const { - const auto tc = static_cast(toolchain()); - return m_compilerCommand->filePath() != tc->compilerCommand() - || m_platformCodeGenFlagsLineEdit->text() != ProcessArgs::joinArgs(tc->extraCodeModelFlags()) - || m_abiWidget->currentAbi() != tc->targetAbi() - ; + return m_platformCodeGenFlagsLineEdit->text() + != ProcessArgs::joinArgs(bundle().extraCodeModelFlags()) + || m_abiWidget->currentAbi() != bundle().targetAbi(); } void KeilToolchainConfigWidget::makeReadOnlyImpl() { - m_compilerCommand->setReadOnly(true); m_platformCodeGenFlagsLineEdit->setEnabled(false); m_abiWidget->setEnabled(false); } @@ -783,19 +772,15 @@ void KeilToolchainConfigWidget::makeReadOnlyImpl() void KeilToolchainConfigWidget::setFromToolchain() { const QSignalBlocker blocker(this); - const auto tc = static_cast(toolchain()); - m_compilerCommand->setFilePath(tc->compilerCommand()); - m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->extraCodeModelFlags())); - m_abiWidget->setAbis({}, tc->targetAbi()); - const bool haveCompiler = m_compilerCommand->filePath().isExecutableFile(); - m_abiWidget->setEnabled(haveCompiler && !tc->isAutoDetected()); + m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(bundle().extraCodeModelFlags())); + m_abiWidget->setAbis({}, bundle().targetAbi()); + m_abiWidget->setEnabled(hasAnyCompiler() && !bundle().isAutoDetected()); } -void KeilToolchainConfigWidget::handleCompilerCommandChange() +void KeilToolchainConfigWidget::handleCompilerCommandChange(Id language) { - const FilePath compilerPath = m_compilerCommand->filePath(); - const bool haveCompiler = compilerPath.isExecutableFile(); - if (haveCompiler) { + const FilePath compilerPath = compilerCommand(language); + if (compilerPath.isExecutableFile()) { const auto env = Environment::systemEnvironment(); const QStringList prevExtraArgs = splitString(m_platformCodeGenFlagsLineEdit->text()); QStringList newExtraArgs = prevExtraArgs; @@ -807,7 +792,7 @@ void KeilToolchainConfigWidget::handleCompilerCommandChange() m_abiWidget->setAbis({}, guessed); } - m_abiWidget->setEnabled(haveCompiler); + m_abiWidget->setEnabled(hasAnyCompiler()); emit dirty(); } @@ -815,10 +800,12 @@ void KeilToolchainConfigWidget::handlePlatformCodeGenFlagsChange() { const QString str1 = m_platformCodeGenFlagsLineEdit->text(); const QString str2 = ProcessArgs::joinArgs(splitString(str1)); - if (str1 != str2) + if (str1 != str2) { m_platformCodeGenFlagsLineEdit->setText(str2); - else - handleCompilerCommandChange(); + } else { + handleCompilerCommandChange(ProjectExplorer::Constants::C_LANGUAGE_ID); + handleCompilerCommandChange(ProjectExplorer::Constants::CXX_LANGUAGE_ID); + } } } // BareMetal::Internal diff --git a/src/plugins/baremetal/sdcctoolchain.cpp b/src/plugins/baremetal/sdcctoolchain.cpp index 971fd9f13a8..82a881b8880 100644 --- a/src/plugins/baremetal/sdcctoolchain.cpp +++ b/src/plugins/baremetal/sdcctoolchain.cpp @@ -179,7 +179,7 @@ class SdccToolchain; class SdccToolchainConfigWidget final : public ToolchainConfigWidget { public: - explicit SdccToolchainConfigWidget(SdccToolchain *tc); + explicit SdccToolchainConfigWidget(const ToolchainBundle &bundle); private: void applyImpl() final; @@ -190,7 +190,6 @@ private: void setFromToolchain(); void handleCompilerCommandChange(); - PathChooser *m_compilerCommand = nullptr; AbiWidget *m_abiWidget = nullptr; Macros m_macros; }; @@ -216,8 +215,6 @@ public: void addToEnvironment(Environment &env) const final; QList createOutputParsers() const final { return {new SdccParser}; } - std::unique_ptr createConfigurationWidget() final; - bool operator==(const Toolchain &other) const final; FilePath makeCommand(const Environment &) const final { return {}; } @@ -281,11 +278,6 @@ void SdccToolchain::addToEnvironment(Environment &env) const env.prependOrSetPath(compilerCommand().parentDir()); } -std::unique_ptr SdccToolchain::createConfigurationWidget() -{ - return std::make_unique(this); -} - bool SdccToolchain::operator==(const Toolchain &other) const { if (!Toolchain::operator==(other)) @@ -311,6 +303,11 @@ public: } Toolchains autoDetect(const ToolchainDetector &detector) const final; + std::unique_ptr createConfigurationWidget( + const ToolchainBundle &bundle) const final + { + return std::make_unique(bundle); + } private: Toolchains autoDetectToolchains(const Candidates &candidates, @@ -445,14 +442,10 @@ Toolchains SdccToolchainFactory::autoDetectToolchain(const Candidate &candidate, // SdccToolchainConfigWidget -SdccToolchainConfigWidget::SdccToolchainConfigWidget(SdccToolchain *tc) : - ToolchainConfigWidget(tc), - m_compilerCommand(new PathChooser), +SdccToolchainConfigWidget::SdccToolchainConfigWidget(const ToolchainBundle &bundle) : + ToolchainConfigWidget(bundle), m_abiWidget(new AbiWidget) { - m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); - m_compilerCommand->setHistoryCompleter("PE.SDCC.Command.History"); - m_mainLayout->addRow(Tr::tr("&Compiler path:"), m_compilerCommand); m_mainLayout->addRow(Tr::tr("&ABI:"), m_abiWidget); m_abiWidget->setEnabled(false); @@ -460,7 +453,7 @@ SdccToolchainConfigWidget::SdccToolchainConfigWidget(SdccToolchain *tc) : addErrorLabel(); setFromToolchain(); - connect(m_compilerCommand, &PathChooser::rawPathChanged, + connect(this, &ToolchainConfigWidget::compilerCommandChanged, this, &SdccToolchainConfigWidget::handleCompilerCommandChange); connect(m_abiWidget, &AbiWidget::abiChanged, this, &ToolchainConfigWidget::dirty); @@ -468,51 +461,42 @@ SdccToolchainConfigWidget::SdccToolchainConfigWidget(SdccToolchain *tc) : void SdccToolchainConfigWidget::applyImpl() { - if (toolchain()->isAutoDetected()) + if (bundle().isAutoDetected()) return; - const auto tc = static_cast(toolchain()); - const QString displayName = tc->displayName(); - tc->setCompilerCommand(m_compilerCommand->filePath()); - tc->setTargetAbi(m_abiWidget->currentAbi()); - tc->setDisplayName(displayName); - + bundle().setTargetAbi(m_abiWidget->currentAbi()); if (m_macros.isEmpty()) return; - const auto languageVersion = Toolchain::languageVersion(tc->language(), m_macros); - tc->predefinedMacrosCache()->insert({}, {m_macros, languageVersion}); - + bundle().forEach([this](SdccToolchain &tc) { + const auto languageVersion = Toolchain::languageVersion(tc.language(), m_macros); + tc.predefinedMacrosCache()->insert({}, {m_macros, languageVersion}); + }); setFromToolchain(); } bool SdccToolchainConfigWidget::isDirtyImpl() const { - const auto tc = static_cast(toolchain()); - return m_compilerCommand->filePath() != tc->compilerCommand() - || m_abiWidget->currentAbi() != tc->targetAbi() - ; + return m_abiWidget->currentAbi() != bundle().targetAbi(); } void SdccToolchainConfigWidget::makeReadOnlyImpl() { - m_compilerCommand->setReadOnly(true); m_abiWidget->setEnabled(false); } void SdccToolchainConfigWidget::setFromToolchain() { const QSignalBlocker blocker(this); - const auto tc = static_cast(toolchain()); - m_compilerCommand->setFilePath(tc->compilerCommand()); - m_abiWidget->setAbis({}, tc->targetAbi()); - const bool haveCompiler = m_compilerCommand->filePath().isExecutableFile(); - m_abiWidget->setEnabled(haveCompiler && !tc->isAutoDetected()); + m_abiWidget->setAbis({}, bundle().targetAbi()); + const bool haveCompiler + = compilerCommand(ProjectExplorer::Constants::C_LANGUAGE_ID).isExecutableFile(); + m_abiWidget->setEnabled(haveCompiler && !bundle().isAutoDetected()); } void SdccToolchainConfigWidget::handleCompilerCommandChange() { - const FilePath compilerPath = m_compilerCommand->filePath(); + const FilePath compilerPath = compilerCommand(ProjectExplorer::Constants::C_LANGUAGE_ID); const bool haveCompiler = compilerPath.isExecutableFile(); if (haveCompiler) { const auto env = Environment::systemEnvironment(); diff --git a/src/plugins/cppeditor/projectinfo_test.cpp b/src/plugins/cppeditor/projectinfo_test.cpp index 19c869c148b..3536c139106 100644 --- a/src/plugins/cppeditor/projectinfo_test.cpp +++ b/src/plugins/cppeditor/projectinfo_test.cpp @@ -344,10 +344,7 @@ private: void addToEnvironment(Utils::Environment &) const override {} Utils::FilePath makeCommand(const Utils::Environment &) const override { return {}; } QList createOutputParsers() const override { return {}; } - std::unique_ptr createConfigurationWidget() override - { - return {}; - }; + bool canShareBundleImpl(const Toolchain &) const override { return false; } }; class ProjectInfoGeneratorTestHelper diff --git a/src/plugins/ios/iosconfigurations.cpp b/src/plugins/ios/iosconfigurations.cpp index 6d7d559d488..ce66978151e 100644 --- a/src/plugins/ios/iosconfigurations.cpp +++ b/src/plugins/ios/iosconfigurations.cpp @@ -20,6 +20,7 @@ #include #include #include +#include #include #include @@ -555,6 +556,12 @@ public: } Toolchains autoDetect(const ToolchainDetector &detector) const final; + + std::unique_ptr createConfigurationWidget( + const ToolchainBundle &bundle) const override + { + return GccToolchain::createConfigurationWidget(bundle); + } }; Toolchains IosToolchainFactory::autoDetect(const ToolchainDetector &detector) const diff --git a/src/plugins/mcusupport/test/unittest.cpp b/src/plugins/mcusupport/test/unittest.cpp index c2bf0a9dd95..c64e196cbc3 100644 --- a/src/plugins/mcusupport/test/unittest.cpp +++ b/src/plugins/mcusupport/test/unittest.cpp @@ -213,7 +213,6 @@ auto expandTargetsAndPackages = [](Targets &targets, Packages &packages) { void verifyIarToolchain(const McuToolchainPackagePtr &iarToolchainPackage) { - ProjectExplorer::ToolchainFactory toolchainFactory; Id iarId{BareMetal::Constants::IAREW_TOOLCHAIN_TYPEID}; Toolchain *iarToolchain{ProjectExplorer::ToolchainFactory::createToolchain(iarId)}; iarToolchain->setLanguage(cxxLanguageId); @@ -236,7 +235,6 @@ void verifyIarToolchain(const McuToolchainPackagePtr &iarToolchainPackage) void verifyArmGccToolchain(const McuToolchainPackagePtr &armGccPackage, const QStringList &versions) { //Fake register and fake detect compiler. - ProjectExplorer::ToolchainFactory toolchainFactory; Id armGccId{ProjectExplorer::Constants::GCC_TOOLCHAIN_TYPEID}; Toolchain *armToolchain{ProjectExplorer::ToolchainFactory::createToolchain(armGccId)}; diff --git a/src/plugins/nim/project/nimtoolchain.cpp b/src/plugins/nim/project/nimtoolchain.cpp index 701883de0c0..d333e618537 100644 --- a/src/plugins/nim/project/nimtoolchain.cpp +++ b/src/plugins/nim/project/nimtoolchain.cpp @@ -123,16 +123,12 @@ bool NimToolchain::parseVersion(const FilePath &path, std::tuple class NimToolchainConfigWidget : public ToolchainConfigWidget { public: - explicit NimToolchainConfigWidget(NimToolchain *tc) - : ToolchainConfigWidget(tc) - , m_compilerCommand(new PathChooser) + explicit NimToolchainConfigWidget(const ToolchainBundle &bundle) + : ToolchainConfigWidget(bundle) , m_compilerVersion(new QLineEdit) { // Create ui - const auto gnuVersionArgs = QStringList("--version"); - m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); - m_compilerCommand->setCommandVersionArguments(gnuVersionArgs); - m_mainLayout->addRow(Tr::tr("&Compiler path:"), m_compilerCommand); + setCommandVersionArguments({"--version"}); m_compilerVersion->setReadOnly(true); m_mainLayout->addRow(Tr::tr("&Compiler version:"), m_compilerVersion); @@ -140,11 +136,9 @@ public: fillUI(); // Connect - connect(m_compilerCommand, &PathChooser::validChanged, this, [this] { - const FilePath path = m_compilerCommand->unexpandedFilePath(); - auto tc = static_cast(toolchain()); - QTC_ASSERT(tc, return); - tc->setCompilerCommand(path); + connect(this, &ToolchainConfigWidget::compilerCommandChanged, this, [this] { + const FilePath path = compilerCommand(Constants::C_NIMLANGUAGE_ID); + this->bundle().setCompilerCommand(Constants::C_NIMLANGUAGE_ID, path); fillUI(); }); } @@ -158,47 +152,22 @@ protected: private: void fillUI(); - Utils::PathChooser *m_compilerCommand; QLineEdit *m_compilerVersion; }; -void NimToolchainConfigWidget::applyImpl() -{ - auto tc = static_cast(toolchain()); - Q_ASSERT(tc); - if (tc->isAutoDetected()) - return; - tc->setCompilerCommand(m_compilerCommand->filePath()); -} +void NimToolchainConfigWidget::applyImpl() {} void NimToolchainConfigWidget::discardImpl() { fillUI(); } -bool NimToolchainConfigWidget::isDirtyImpl() const -{ - auto tc = static_cast(toolchain()); - Q_ASSERT(tc); - return tc->compilerCommand() != m_compilerCommand->filePath(); -} - -void NimToolchainConfigWidget::makeReadOnlyImpl() -{ - m_compilerCommand->setReadOnly(true); -} +bool NimToolchainConfigWidget::isDirtyImpl() const { return false; } +void NimToolchainConfigWidget::makeReadOnlyImpl() {} void NimToolchainConfigWidget::fillUI() { - auto tc = static_cast(toolchain()); - Q_ASSERT(tc); - m_compilerCommand->setFilePath(tc->compilerCommand()); - m_compilerVersion->setText(tc->compilerVersion()); -} - -std::unique_ptr NimToolchain::createConfigurationWidget() -{ - return std::make_unique(this); + m_compilerVersion->setText(bundle().get(&NimToolchain::compilerVersion)); } // NimToolchainFactory @@ -247,4 +216,10 @@ Toolchains NimToolchainFactory::detectForImport(const ToolchainDescription &tcd) return result; } +std::unique_ptr NimToolchainFactory::createConfigurationWidget( + const ProjectExplorer::ToolchainBundle &bundle) const +{ + return std::make_unique(bundle); +} + } // Nim diff --git a/src/plugins/nim/project/nimtoolchain.h b/src/plugins/nim/project/nimtoolchain.h index 6304143d8b8..a97225d702f 100644 --- a/src/plugins/nim/project/nimtoolchain.h +++ b/src/plugins/nim/project/nimtoolchain.h @@ -24,7 +24,6 @@ public: Utils::FilePath makeCommand(const Utils::Environment &env) const final; QString compilerVersion() const; QList createOutputParsers() const final; - std::unique_ptr createConfigurationWidget() final; void fromMap(const Utils::Store &data) final; @@ -41,6 +40,8 @@ public: ProjectExplorer::Toolchains autoDetect(const ProjectExplorer::ToolchainDetector &detector) const final; ProjectExplorer::Toolchains detectForImport(const ProjectExplorer::ToolchainDescription &tcd) const final; + std::unique_ptr createConfigurationWidget( + const ProjectExplorer::ToolchainBundle &bundle) const final; }; } // Nim diff --git a/src/plugins/projectexplorer/customtoolchain.cpp b/src/plugins/projectexplorer/customtoolchain.cpp index bb92eeb027d..5680e823227 100644 --- a/src/plugins/projectexplorer/customtoolchain.cpp +++ b/src/plugins/projectexplorer/customtoolchain.cpp @@ -6,6 +6,7 @@ #include "abiwidget.h" #include "gccparser.h" #include "clangparser.h" +#include "gcctoolchain.h" #include "linuxiccparser.h" #include "msvcparser.h" #include "customparser.h" @@ -80,8 +81,6 @@ public: void toMap(Store &data) const override; void fromMap(const Store &data) override; - std::unique_ptr createConfigurationWidget() override; - bool operator ==(const Toolchain &) const override; void setMakeCommand(const FilePath &); @@ -385,7 +384,7 @@ public: class CustomToolchainConfigWidget final : public ToolchainConfigWidget { public: - explicit CustomToolchainConfigWidget(CustomToolchain *); + explicit CustomToolchainConfigWidget(const ToolchainBundle &bundle); private: void updateSummaries(TextEditDetailsWidget *detailsWidget); @@ -398,7 +397,6 @@ private: void setFromToolchain(); - PathChooser *m_compilerCommand; PathChooser *m_makeCommand; AbiWidget *m_abiWidget; QPlainTextEdit *m_predefinedMacros; @@ -410,9 +408,8 @@ private: QComboBox *m_errorParserComboBox; }; -CustomToolchainConfigWidget::CustomToolchainConfigWidget(CustomToolchain *tc) : - ToolchainConfigWidget(tc), - m_compilerCommand(new PathChooser), +CustomToolchainConfigWidget::CustomToolchainConfigWidget(const ToolchainBundle &bundle) : + ToolchainConfigWidget(bundle), m_makeCommand(new PathChooser), m_abiWidget(new AbiWidget), m_predefinedMacros(new QPlainTextEdit), @@ -423,8 +420,6 @@ CustomToolchainConfigWidget::CustomToolchainConfigWidget(CustomToolchain *tc) : m_mkspecs(new QLineEdit), m_errorParserComboBox(new QComboBox) { - Q_ASSERT(tc); - const QList parsers = CustomToolchain::parsers(); for (const auto &parser : parsers) m_errorParserComboBox->addItem(parser.displayName, parser.parserId.toString()); @@ -441,11 +436,8 @@ CustomToolchainConfigWidget::CustomToolchainConfigWidget(CustomToolchain *tc) : m_headerPaths->setToolTip(Tr::tr("Each line adds a global header lookup path.")); m_cxx11Flags->setToolTip(Tr::tr("Comma-separated list of flags that turn on C++11 support.")); m_mkspecs->setToolTip(Tr::tr("Comma-separated list of mkspecs.")); - m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); - m_compilerCommand->setHistoryCompleter("PE.ToolChainCommand.History"); m_makeCommand->setExpectedKind(PathChooser::ExistingCommand); m_makeCommand->setHistoryCompleter("PE.MakeCommand.History"); - m_mainLayout->addRow(Tr::tr("&Compiler path:"), m_compilerCommand); m_mainLayout->addRow(Tr::tr("&Make path:"), m_makeCommand); m_mainLayout->addRow(Tr::tr("&ABI:"), m_abiWidget); m_mainLayout->addRow(Tr::tr("&Predefined macros:"), m_predefinedDetails); @@ -460,7 +452,6 @@ CustomToolchainConfigWidget::CustomToolchainConfigWidget(CustomToolchain *tc) : m_predefinedDetails->updateSummaryText(); m_headerDetails->updateSummaryText(); - connect(m_compilerCommand, &PathChooser::rawPathChanged, this, &ToolchainConfigWidget::dirty); connect(m_makeCommand, &PathChooser::rawPathChanged, this, &ToolchainConfigWidget::dirty); connect(m_abiWidget, &AbiWidget::abiChanged, this, &ToolchainConfigWidget::dirty); connect(m_predefinedMacros, &QPlainTextEdit::textChanged, @@ -487,62 +478,59 @@ void CustomToolchainConfigWidget::errorParserChanged(int ) void CustomToolchainConfigWidget::applyImpl() { - if (toolchain()->isAutoDetected()) + if (bundle().isAutoDetected()) return; - auto tc = static_cast(toolchain()); - Q_ASSERT(tc); - QString displayName = tc->displayName(); - tc->setCompilerCommand(m_compilerCommand->filePath()); - tc->setMakeCommand(m_makeCommand->filePath()); - tc->setTargetAbi(m_abiWidget->currentAbi()); - Macros macros = Utils::transform( - m_predefinedDetails->text().split('\n', Qt::SkipEmptyParts), - [](const QString &m) { - return Macro::fromKeyValue(m); + bundle().setTargetAbi(m_abiWidget->currentAbi()); + const Macros macros = Utils::transform( + m_predefinedDetails->text().split('\n', Qt::SkipEmptyParts), + [](const QString &m) { + return Macro::fromKeyValue(m); + }); + bundle().forEach([&](CustomToolchain &tc) { + tc.setMakeCommand(m_makeCommand->filePath()); + tc.setPredefinedMacros(macros); + tc.setHeaderPaths(m_headerDetails->entries()); + tc.setCxx11Flags(m_cxx11Flags->text().split(QLatin1Char(','))); + tc.setMkspecs(m_mkspecs->text()); + tc.setOutputParserId(Id::fromSetting(m_errorParserComboBox->currentData())); }); - tc->setPredefinedMacros(macros); - tc->setHeaderPaths(m_headerDetails->entries()); - tc->setCxx11Flags(m_cxx11Flags->text().split(QLatin1Char(','))); - tc->setMkspecs(m_mkspecs->text()); - tc->setDisplayName(displayName); // reset display name - tc->setOutputParserId(Id::fromSetting(m_errorParserComboBox->currentData())); - setFromToolchain(); // Refresh with actual data from the toolchain. This shows what e.g. the - // macro parser did with the input. + // Refresh with actual data from the toolchain. This shows what e.g. the + // macro parser did with the input. + setFromToolchain(); } void CustomToolchainConfigWidget::setFromToolchain() { // subwidgets are not yet connected! QSignalBlocker blocker(this); - auto tc = static_cast(toolchain()); - m_compilerCommand->setFilePath(tc->compilerCommand()); - m_makeCommand->setFilePath(tc->makeCommand(Environment())); - m_abiWidget->setAbis(Abis(), tc->targetAbi()); - const QStringList macroLines = Utils::transform(tc->rawPredefinedMacros(), [](const Macro &m) { - return QString::fromUtf8(m.toKeyValue(QByteArray())); - }); + m_makeCommand->setFilePath(bundle().makeCommand(Environment())); + m_abiWidget->setAbis(Abis(), bundle().targetAbi()); + const QStringList macroLines = Utils::transform( + bundle().get(&CustomToolchain::rawPredefinedMacros), + [](const Macro &m) { return QString::fromUtf8(m.toKeyValue(QByteArray())); }); m_predefinedMacros->setPlainText(macroLines.join('\n')); - m_headerPaths->setPlainText(tc->headerPathsList().join('\n')); - m_cxx11Flags->setText(tc->cxx11Flags().join(QLatin1Char(','))); - m_mkspecs->setText(tc->mkspecs()); - int index = m_errorParserComboBox->findData(tc->outputParserId().toSetting()); + m_headerPaths->setPlainText(bundle().get(&CustomToolchain::headerPathsList).join('\n')); + m_cxx11Flags->setText(bundle().get(&CustomToolchain::cxx11Flags).join(QLatin1Char(','))); + m_mkspecs->setText(bundle().get(&CustomToolchain::mkspecs)); + const int index = m_errorParserComboBox->findData( + bundle().get(&CustomToolchain::outputParserId).toSetting()); m_errorParserComboBox->setCurrentIndex(index); } bool CustomToolchainConfigWidget::isDirtyImpl() const { - auto tc = static_cast(toolchain()); - Q_ASSERT(tc); - return m_compilerCommand->filePath() != tc->compilerCommand() - || m_makeCommand->filePath().toString() != tc->makeCommand(Environment()).toString() - || m_abiWidget->currentAbi() != tc->targetAbi() - || Macro::toMacros(m_predefinedDetails->text().toUtf8()) != tc->rawPredefinedMacros() - || m_headerDetails->entries() != tc->headerPathsList() - || m_cxx11Flags->text().split(QLatin1Char(',')) != tc->cxx11Flags() - || m_mkspecs->text() != tc->mkspecs() - || Id::fromSetting(m_errorParserComboBox->currentData()) == tc->outputParserId(); + return m_makeCommand->filePath() != bundle().makeCommand({}) + || m_abiWidget->currentAbi() != bundle().targetAbi() + || Macro::toMacros(m_predefinedDetails->text().toUtf8()) + != bundle().get(&CustomToolchain::rawPredefinedMacros) + || m_headerDetails->entries() != bundle().get(&CustomToolchain::headerPathsList) + || m_cxx11Flags->text().split(QLatin1Char(',')) + != bundle().get(&CustomToolchain::cxx11Flags) + || m_mkspecs->text() != bundle().get(&CustomToolchain::mkspecs) + || Id::fromSetting(m_errorParserComboBox->currentData()) + == bundle().get(&CustomToolchain::outputParserId); } void CustomToolchainConfigWidget::makeReadOnlyImpl() @@ -550,11 +538,6 @@ void CustomToolchainConfigWidget::makeReadOnlyImpl() m_mainLayout->setEnabled(false); } -std::unique_ptr CustomToolchain::createConfigurationWidget() -{ - return std::make_unique(this); -} - // CustomToolchainFactory class CustomToolchainFactory final : public ToolchainFactory @@ -568,6 +551,27 @@ public: setToolchainConstructor([] { return new CustomToolchain; }); setUserCreatable(true); } + +private: + std::unique_ptr createConfigurationWidget( + const ToolchainBundle &bundle) const override + { + return std::make_unique(bundle); + } + + FilePath correspondingCompilerCommand(const FilePath &srcPath, Id targetLang) const override + { + static const std::pair patternPairs[] + = {{"gcc", "g++"}, {"clang", "clang++"}, {"icc", "icpc"}}; + for (const auto &[cPattern, cxxPattern] : patternPairs) { + if (const FilePath &targetPath = GccToolchain::correspondingCompilerCommand( + srcPath, targetLang, cPattern, cxxPattern); + targetPath != srcPath) { + return targetPath; + } + } + return srcPath; + } }; void setupCustomToolchain() diff --git a/src/plugins/projectexplorer/gcctoolchain.cpp b/src/plugins/projectexplorer/gcctoolchain.cpp index d3a74b5fa50..4a19b2cbf5c 100644 --- a/src/plugins/projectexplorer/gcctoolchain.cpp +++ b/src/plugins/projectexplorer/gcctoolchain.cpp @@ -107,10 +107,10 @@ class TargetTripleWidget; class GccToolchainConfigWidget : public ToolchainConfigWidget { public: - explicit GccToolchainConfigWidget(GccToolchain *tc); + explicit GccToolchainConfigWidget(const ToolchainBundle &bundle); private: - void handleCompilerCommandChange(); + void handleCompilerCommandChange(Id language); void handlePlatformCodeGenFlagsChange(); void handlePlatformLinkerFlagsChange(); @@ -122,12 +122,13 @@ private: void setFromToolchain(); void updateParentToolchainComboBox(); // Clang + Id bundleIdFromId(const QByteArray &parentId); + Toolchain *toolchainFromBundleId(Id bundleId, Id language); AbiWidget *m_abiWidget; GccToolchain::SubType m_subType = GccToolchain::RealGcc; - PathChooser *m_compilerCommand; QLineEdit *m_platformCodeGenFlagsLineEdit; QLineEdit *m_platformLinkerFlagsLineEdit; TargetTripleWidget * const m_targetTripleWidget; @@ -385,6 +386,12 @@ GccToolchain::~GccToolchain() } } +std::unique_ptr GccToolchain::createConfigurationWidget( + const ToolchainBundle &bundle) +{ + return std::make_unique(bundle); +} + void GccToolchain::setSupportedAbis(const Abis &abis) { if (m_supportedAbis == abis) @@ -403,6 +410,20 @@ void GccToolchain::setOriginalTargetTriple(const QString &targetTriple) toolChainUpdated(); } +FilePath GccToolchain::correspondingCompilerCommand( + const Utils::FilePath &srcPath, + Utils::Id targetLang, + const QString &cPattern, + const QString &cxxPattern) +{ + QString outFileName = srcPath.fileName(); + if (targetLang == Constants::CXX_LANGUAGE_ID) + outFileName.replace(cPattern, cxxPattern); + else + outFileName.replace(cxxPattern, cPattern); + return srcPath.parentDir().pathAppended(outFileName); +} + void GccToolchain::setInstallDir(const FilePath &installDir) { if (m_installDir == installDir) @@ -1067,11 +1088,6 @@ bool GccToolchain::operator ==(const Toolchain &other) const && m_platformLinkerFlags == gccTc->m_platformLinkerFlags; } -std::unique_ptr GccToolchain::createConfigurationWidget() -{ - return std::make_unique(this); -} - void GccToolchain::updateSupportedAbis() const { if (m_supportedAbis.isEmpty()) { @@ -1315,6 +1331,9 @@ public: Toolchains autoDetect(const ToolchainDetector &detector) const final; Toolchains detectForImport(const ToolchainDescription &tcd) const final; + std::unique_ptr createConfigurationWidget( + const ToolchainBundle &bundle) const final; + FilePath correspondingCompilerCommand(const FilePath &srcPath, Id targetLang) const final; private: static Toolchains autoDetectToolchains(const FilePaths &compilerPaths, @@ -1558,6 +1577,26 @@ Toolchains GccToolchainFactory::detectForImport(const ToolchainDescription &tcd) return result; } +std::unique_ptr GccToolchainFactory::createConfigurationWidget( + const ToolchainBundle &bundle) const +{ + return GccToolchain::createConfigurationWidget(bundle); +} + +FilePath GccToolchainFactory::correspondingCompilerCommand( + const FilePath &srcPath, Id targetLang) const +{ + if (supportedToolchainType() == Constants::GCC_TOOLCHAIN_TYPEID + || supportedToolchainType() == Constants::MINGW_TOOLCHAIN_TYPEID) { + return GccToolchain::correspondingCompilerCommand(srcPath, targetLang, "gcc", "g++"); + } + if (supportedToolchainType() == Constants::CLANG_TOOLCHAIN_TYPEID) + return GccToolchain::correspondingCompilerCommand(srcPath, targetLang, "clang", "clang++"); + if (supportedToolchainType() == Constants::LINUXICC_TOOLCHAIN_TYPEID) + return GccToolchain::correspondingCompilerCommand(srcPath, targetLang, "icc", "icpc"); + return {}; +} + Toolchains GccToolchainFactory::autoDetectSdkClangToolchain(const Toolchains &known) { const FilePath compilerPath = Core::ICore::clangExecutable(CLANG_BINDIR); @@ -1678,7 +1717,7 @@ class TargetTripleWidget : public QWidget Q_OBJECT public: - TargetTripleWidget(const Toolchain *toolchain) + TargetTripleWidget(const ToolchainBundle &bundle) { const auto layout = new QHBoxLayout(this); layout->setContentsMargins(0, 0, 0, 0); @@ -1694,8 +1733,8 @@ public: connect(&m_overrideCheckBox, &QCheckBox::toggled, &m_tripleLineEdit, &QLineEdit::setEnabled); - m_tripleLineEdit.setText(toolchain->effectiveCodeModelTargetTriple()); - m_overrideCheckBox.setChecked(!toolchain->explicitCodeModelTargetTriple().isEmpty()); + m_tripleLineEdit.setText(bundle.get(&Toolchain::effectiveCodeModelTargetTriple)); + m_overrideCheckBox.setChecked(!bundle.get(&Toolchain::explicitCodeModelTargetTriple).isEmpty()); } QString explicitCodeModelTargetTriple() const @@ -1714,24 +1753,18 @@ private: }; } -GccToolchainConfigWidget::GccToolchainConfigWidget(GccToolchain *tc) : - ToolchainConfigWidget(tc), +GccToolchainConfigWidget::GccToolchainConfigWidget(const ToolchainBundle &bundle) : + ToolchainConfigWidget(bundle), m_abiWidget(new AbiWidget), - m_subType(tc->m_subType), - m_compilerCommand(new PathChooser), - m_targetTripleWidget(new TargetTripleWidget(tc)) + m_subType(bundle.get(&GccToolchain::subType)), + m_targetTripleWidget(new TargetTripleWidget(bundle)) { - const QStringList gnuVersionArgs = QStringList("--version"); - m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); - m_compilerCommand->setCommandVersionArguments(gnuVersionArgs); - m_compilerCommand->setHistoryCompleter("PE.Gcc.Command.History"); - m_compilerCommand->setAllowPathFromDevice(true); - m_mainLayout->addRow(Tr::tr("&Compiler path:"), m_compilerCommand); + setCommandVersionArguments({"--version"}); m_platformCodeGenFlagsLineEdit = new QLineEdit(this); - m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformCodeGenFlags())); + m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(bundle.extraCodeModelFlags())); m_mainLayout->addRow(Tr::tr("Platform codegen flags:"), m_platformCodeGenFlagsLineEdit); m_platformLinkerFlagsLineEdit = new QLineEdit(this); - m_platformLinkerFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformLinkerFlags())); + m_platformLinkerFlagsLineEdit->setText(ProcessArgs::joinArgs(bundle.get(&GccToolchain::platformLinkerFlags))); m_mainLayout->addRow(Tr::tr("Platform linker flags:"), m_platformLinkerFlagsLineEdit); m_mainLayout->addRow(Tr::tr("&ABI:"), m_abiWidget); m_mainLayout->addRow(Tr::tr("Target triple:"), m_targetTripleWidget); @@ -1741,7 +1774,7 @@ GccToolchainConfigWidget::GccToolchainConfigWidget(GccToolchain *tc) : setFromToolchain(); - connect(m_compilerCommand, &PathChooser::rawPathChanged, + connect(this, &ToolchainConfigWidget::compilerCommandChanged, this, &GccToolchainConfigWidget::handleCompilerCommandChange); connect(m_platformCodeGenFlagsLineEdit, &QLineEdit::editingFinished, this, &GccToolchainConfigWidget::handlePlatformCodeGenFlagsChange); @@ -1752,7 +1785,7 @@ GccToolchainConfigWidget::GccToolchainConfigWidget(GccToolchain *tc) : this, &ToolchainConfigWidget::dirty); if (m_subType == GccToolchain::Clang) { - if (!HostOsInfo::isWindowsHost() || tc->typeId() != Constants::CLANG_TOOLCHAIN_TYPEID) + if (!HostOsInfo::isWindowsHost() || bundle.type() != Constants::CLANG_TOOLCHAIN_TYPEID) return; // Remove m_abiWidget row because the parent toolchain abi is going to be used. @@ -1760,6 +1793,8 @@ GccToolchainConfigWidget::GccToolchainConfigWidget(GccToolchain *tc) : m_abiWidget = nullptr; m_parentToolchainCombo = new QComboBox(this); + connect(m_parentToolchainCombo, &QComboBox::currentIndexChanged, + this, &ToolchainConfigWidget::dirty); m_mainLayout->insertRow(m_mainLayout->rowCount() - 1, Tr::tr("Parent toolchain:"), m_parentToolchainCombo); @@ -1779,12 +1814,13 @@ GccToolchainConfigWidget::GccToolchainConfigWidget(GccToolchain *tc) : updateParentToolchainComboBox(); } })); - m_parentToolchainConnections.append( - connect(tcManager, &ToolchainManager::toolchainsDeregistered, this, - [this](const Toolchains &toolchains) { + m_parentToolchainConnections.append(connect( + tcManager, &ToolchainManager::toolchainsDeregistered, this, [this, bundle](const Toolchains &toolchains) { bool updateParentComboBox = false; for (Toolchain * const tc : toolchains) { - if (tc->id() == toolchain()->id()) { + if (Utils::contains(bundle.toolchains(), [tc](const Toolchain *elem) { + return elem->id() == tc->id(); + })) { for (QMetaObject::Connection &connection : m_parentToolchainConnections) QObject::disconnect(connection); return; @@ -1795,71 +1831,60 @@ GccToolchainConfigWidget::GccToolchainConfigWidget(GccToolchain *tc) : if (updateParentComboBox) updateParentToolchainComboBox(); })); - updateParentToolchainComboBox(); } } void GccToolchainConfigWidget::applyImpl() { - if (toolchain()->isAutoDetected()) + if (bundle().isAutoDetected()) return; - auto tc = static_cast(toolchain()); - Q_ASSERT(tc); - QString displayName = tc->displayName(); - tc->setCompilerCommand(m_compilerCommand->filePath()); - if (m_abiWidget) { - tc->setSupportedAbis(m_abiWidget->supportedAbis()); - tc->setTargetAbi(m_abiWidget->currentAbi()); - } - tc->setInstallDir(tc->detectInstallDir()); - tc->setOriginalTargetTriple(tc->detectSupportedAbis().originalTargetTriple); - tc->setExplicitCodeModelTargetTriple(m_targetTripleWidget->explicitCodeModelTargetTriple()); - tc->setDisplayName(displayName); // reset display name - tc->setPlatformCodeGenFlags(splitString(m_platformCodeGenFlagsLineEdit->text())); - tc->setPlatformLinkerFlags(splitString(m_platformLinkerFlagsLineEdit->text())); + const Id parentBundleId = m_parentToolchainCombo + ? Id::fromSetting(m_parentToolchainCombo->currentData()) + : Id(); + bundle().forEach([&](GccToolchain &tc) { + tc.setCompilerCommand(compilerCommand(tc.language())); + if (m_abiWidget) { + tc.setSupportedAbis(m_abiWidget->supportedAbis()); + tc.setTargetAbi(m_abiWidget->currentAbi()); + } + tc.setInstallDir(tc.detectInstallDir()); + tc.setOriginalTargetTriple(tc.detectSupportedAbis().originalTargetTriple); + tc.setExplicitCodeModelTargetTriple(m_targetTripleWidget->explicitCodeModelTargetTriple()); + tc.setPlatformCodeGenFlags(splitString(m_platformCodeGenFlagsLineEdit->text())); + tc.setPlatformLinkerFlags(splitString(m_platformLinkerFlagsLineEdit->text())); - if (m_macros.isEmpty()) - return; - - tc->predefinedMacrosCache() - ->insert(tc->platformCodeGenFlags(), - Toolchain::MacroInspectionReport{m_macros, - Toolchain::languageVersion(tc->language(), - m_macros)}); - - if (m_subType == GccToolchain::Clang && m_parentToolchainCombo) { - - tc->m_parentToolchainId.clear(); - - const QByteArray parentId = m_parentToolchainCombo->currentData().toByteArray(); - if (!parentId.isEmpty()) { - for (const Toolchain *mingwTC : mingwToolchains()) { - if (parentId == mingwTC->id()) { - tc->m_parentToolchainId = mingwTC->id(); - tc->setTargetAbi(mingwTC->targetAbi()); - tc->setSupportedAbis(mingwTC->supportedAbis()); - break; - } + tc.m_parentToolchainId.clear(); + if (parentBundleId.isValid()) { + if (const Toolchain * const parentTc + = toolchainFromBundleId(parentBundleId, tc.language())) { + tc.m_parentToolchainId = parentTc->id(); + tc.setTargetAbi(parentTc->targetAbi()); + tc.setSupportedAbis(parentTc->supportedAbis()); } } - } + + if (!m_macros.isEmpty()) { + tc.predefinedMacrosCache()->insert( + tc.platformCodeGenFlags(), + Toolchain::MacroInspectionReport{m_macros, Toolchain::languageVersion( + tc.language(), m_macros)}); + } + }); } void GccToolchainConfigWidget::setFromToolchain() { // subwidgets are not yet connected! QSignalBlocker blocker(this); - auto tc = static_cast(toolchain()); - m_compilerCommand->setFilePath(tc->compilerCommand()); - m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformCodeGenFlags(), - HostOsInfo::hostOs())); - m_platformLinkerFlagsLineEdit->setText(ProcessArgs::joinArgs(tc->platformLinkerFlags(), - HostOsInfo::hostOs())); + m_platformCodeGenFlagsLineEdit->setText(ProcessArgs::joinArgs( + bundle().get(&GccToolchain::platformCodeGenFlags), HostOsInfo::hostOs())); + m_platformLinkerFlagsLineEdit->setText( + ProcessArgs::joinArgs(bundle().get(&GccToolchain::platformLinkerFlags), HostOsInfo::hostOs())); if (m_abiWidget) { - m_abiWidget->setAbis(tc->supportedAbis(), tc->targetAbi()); - if (!m_isReadOnly && !m_compilerCommand->filePath().toString().isEmpty()) + m_abiWidget->setAbis(bundle().supportedAbis(), bundle().targetAbi()); + if (!m_isReadOnly && hasAnyCompiler()) m_abiWidget->setEnabled(true); } @@ -1869,30 +1894,25 @@ void GccToolchainConfigWidget::setFromToolchain() bool GccToolchainConfigWidget::isDirtyImpl() const { - auto tc = static_cast(toolchain()); - - if (m_compilerCommand->filePath() != tc->compilerCommand() - || m_platformCodeGenFlagsLineEdit->text() - != ProcessArgs::joinArgs(tc->platformCodeGenFlags()) - || m_platformLinkerFlagsLineEdit->text() - != ProcessArgs::joinArgs(tc->platformLinkerFlags()) - || m_targetTripleWidget->explicitCodeModelTargetTriple() - != tc->explicitCodeModelTargetTriple() - || (m_abiWidget && m_abiWidget->currentAbi() != tc->targetAbi())) { + if (m_platformCodeGenFlagsLineEdit->text() != ProcessArgs::joinArgs(bundle().get(&GccToolchain::platformCodeGenFlags)) + || m_platformLinkerFlagsLineEdit->text() != ProcessArgs::joinArgs(bundle().get(&GccToolchain::platformLinkerFlags)) + || m_targetTripleWidget->explicitCodeModelTargetTriple() + != bundle().get(&GccToolchain::explicitCodeModelTargetTriple) + || (m_abiWidget && m_abiWidget->currentAbi() != bundle().targetAbi())) { return true; } if (!m_parentToolchainCombo) return false; - const GccToolchain *parentTC = mingwToolchainFromId(tc->m_parentToolchainId); - const QByteArray parentId = parentTC ? parentTC->id() : QByteArray(); - return parentId != m_parentToolchainCombo->currentData(); + const GccToolchain *parentTC = mingwToolchainFromId( + bundle().get(&GccToolchain::parentToolchainId)); + const Id parentBundleId = parentTC ? parentTC->bundleId() : Id(); + return parentBundleId.toSetting() != m_parentToolchainCombo->currentData(); } void GccToolchainConfigWidget::makeReadOnlyImpl() { - m_compilerCommand->setReadOnly(true); if (m_abiWidget) m_abiWidget->setEnabled(false); m_platformCodeGenFlagsLineEdit->setEnabled(false); @@ -1904,7 +1924,7 @@ void GccToolchainConfigWidget::makeReadOnlyImpl() m_parentToolchainCombo->setEnabled(false); } -void GccToolchainConfigWidget::handleCompilerCommandChange() +void GccToolchainConfigWidget::handleCompilerCommandChange(Id language) { if (!m_abiWidget) return; @@ -1912,7 +1932,7 @@ void GccToolchainConfigWidget::handleCompilerCommandChange() bool haveCompiler = false; Abi currentAbi = m_abiWidget->currentAbi(); bool customAbi = m_abiWidget->isCustomAbi() && m_abiWidget->isEnabled(); - FilePath path = m_compilerCommand->filePath(); + FilePath path = compilerCommand(language); Abis abiList; if (!path.isEmpty()) { @@ -1946,7 +1966,7 @@ void GccToolchainConfigWidget::handlePlatformCodeGenFlagsChange() if (str1 != str2) m_platformCodeGenFlagsLineEdit->setText(str2); else - handleCompilerCommandChange(); + handleCompilerCommandChange(Constants::C_LANGUAGE_ID); } void GccToolchainConfigWidget::handlePlatformLinkerFlagsChange() @@ -2011,8 +2031,10 @@ void GccToolchain::syncAutodetectedWithParentToolchains() if (tc == this) { QObject::disconnect(m_thisToolchainRemovedConnection); QObject::disconnect(m_mingwToolchainAddedConnection); + break; } else if (m_parentToolchainId == tc->id()) { updateParentId = true; + break; } } if (updateParentId) { @@ -2056,33 +2078,57 @@ QString GccToolchain::sysRoot() const return {}; } +bool GccToolchain::canShareBundleImpl(const Toolchain &other) const +{ + return platformLinkerFlags() == static_cast(other).platformLinkerFlags(); +} + void GccToolchainConfigWidget::updateParentToolchainComboBox() { QTC_ASSERT(m_parentToolchainCombo, return); - auto *tc = static_cast(toolchain()); - QByteArray parentId = m_parentToolchainCombo->currentData().toByteArray(); - if (tc->isAutoDetected() || m_parentToolchainCombo->count() == 0) - parentId = tc->m_parentToolchainId; - - const GccToolchain *parentTC = mingwToolchainFromId(parentId); + Id parentBundleId = Id::fromSetting(m_parentToolchainCombo->currentData()); + if (bundle().isAutoDetected() || m_parentToolchainCombo->count() == 0) + parentBundleId = bundleIdFromId(bundle().get(&GccToolchain::parentToolchainId)); + const QList mingwBundles + = Utils::filtered(ToolchainBundle::collectBundles(), [](const ToolchainBundle &b) { + return b.type() == Constants::MINGW_TOOLCHAIN_TYPEID; + }); + const auto parentBundle + = Utils::findOr(mingwBundles, std::nullopt, [parentBundleId](const ToolchainBundle &b) { + return b.bundleId() == parentBundleId; + }); m_parentToolchainCombo->clear(); - m_parentToolchainCombo->addItem(parentTC ? parentTC->displayName() : QString(), - parentTC ? parentId : QByteArray()); + m_parentToolchainCombo->addItem(parentBundle ? parentBundle->displayName() : QString(), + parentBundle ? parentBundleId.toSetting() : QVariant()); - if (tc->isAutoDetected()) + if (bundle().isAutoDetected()) return; - for (const Toolchain *mingwTC : mingwToolchains()) { - if (mingwTC->id() == parentId) - continue; - if (mingwTC->language() != tc->language()) - continue; - m_parentToolchainCombo->addItem(mingwTC->displayName(), mingwTC->id()); + for (const ToolchainBundle &mingwBundle : mingwBundles) { + if (mingwBundle.bundleId() != parentBundleId) { + m_parentToolchainCombo + ->addItem(mingwBundle.displayName(), mingwBundle.bundleId().toSetting()); + } } } +Id GccToolchainConfigWidget::bundleIdFromId(const QByteArray &id) +{ + const Toolchain * const tc = ToolchainManager::toolchain( + [id](const Toolchain *tc) { return tc->id() == id; }); + return tc ? tc->bundleId() : Id(); +} + +Toolchain *GccToolchainConfigWidget::toolchainFromBundleId(Id bundleId, Id language) +{ + return ToolchainManager::toolchain( + [bundleId, language](const Toolchain *tc) { + return tc->bundleId() == bundleId && tc->language() == language; + }); +} + } // namespace ProjectExplorer // Unit tests: diff --git a/src/plugins/projectexplorer/gcctoolchain.h b/src/plugins/projectexplorer/gcctoolchain.h index 7f870cc603f..7dc1ccfaf5e 100644 --- a/src/plugins/projectexplorer/gcctoolchain.h +++ b/src/plugins/projectexplorer/gcctoolchain.h @@ -10,7 +10,6 @@ #include "headerpath.h" #include -#include #include namespace ProjectExplorer { @@ -34,6 +33,9 @@ public: GccToolchain(Utils::Id typeId, SubType subType = RealGcc); ~GccToolchain() override; + static std::unique_ptr createConfigurationWidget( + const ToolchainBundle &bundle); + QString originalTargetTriple() const override; Utils::FilePath installDir() const override; QString version() const; @@ -55,8 +57,6 @@ public: void toMap(Utils::Store &data) const override; void fromMap(const Utils::Store &data) override; - std::unique_ptr createConfigurationWidget() override; - bool operator ==(const Toolchain &) const override; void resetToolchain(const Utils::FilePath &); @@ -86,12 +86,19 @@ public: void setPriority(int priority) { m_priority = priority; } void setOriginalTargetTriple(const QString &targetTriple); + static Utils::FilePath correspondingCompilerCommand( + const Utils::FilePath &srcPath, + Utils::Id targetLang, + const QString &cPattern, + const QString &cxxPattern); + protected: using CacheItem = QPair; using GccCache = QVector; void setSupportedAbis(const Abis &abis); void setInstallDir(const Utils::FilePath &installDir); + void setMacroCache(const QStringList &allCxxflags, const Macros ¯oCache) const; Macros macroCache(const QStringList &allCxxflags) const; @@ -113,8 +120,12 @@ protected: int priority() const override { return m_priority; } QString sysRoot() const override; + SubType subType() const { return m_subType; } + QByteArray parentToolchainId() const { return m_parentToolchainId; } private: + bool canShareBundleImpl(const Toolchain &other) const override; + void syncAutodetectedWithParentToolchains(); void updateSupportedAbis() const; diff --git a/src/plugins/projectexplorer/kitaspects.cpp b/src/plugins/projectexplorer/kitaspects.cpp index 450edb5084d..b0a99951b70 100644 --- a/src/plugins/projectexplorer/kitaspects.cpp +++ b/src/plugins/projectexplorer/kitaspects.cpp @@ -205,25 +205,28 @@ public: layout->setContentsMargins(0, 0, 0, 0); layout->setColumnStretch(1, 2); - const QList languageList = sorted(ToolchainManager::allLanguages(), [](Id l1, Id l2) { - return ToolchainManager::displayNameOfLanguageId(l1) - < ToolchainManager::displayNameOfLanguageId(l2); - }); - QTC_ASSERT(!languageList.isEmpty(), return); + const QList languageCategories = sorted( + ToolchainManager::languageCategories(), + [](const LanguageCategory &l1, const LanguageCategory &l2) { + return ToolchainManager::displayNameOfLanguageCategory(l1) + < ToolchainManager::displayNameOfLanguageCategory(l2); + }); + QTC_ASSERT(!languageCategories.isEmpty(), return); int row = 0; - for (Id l : std::as_const(languageList)) { - layout->addWidget(new QLabel(ToolchainManager::displayNameOfLanguageId(l) + ':'), row, 0); + for (const LanguageCategory &lc : std::as_const(languageCategories)) { + layout->addWidget( + new QLabel(ToolchainManager::displayNameOfLanguageCategory(lc) + ':'), row, 0); auto cb = new QComboBox; cb->setSizePolicy(QSizePolicy::Ignored, cb->sizePolicy().verticalPolicy()); cb->setToolTip(factory->description()); setWheelScrollingWithoutFocusBlocked(cb); - m_languageComboboxMap.insert(l, cb); + m_languageComboboxMap.insert(lc, cb); layout->addWidget(cb, row, 1); ++row; - connect(cb, &QComboBox::currentIndexChanged, this, [this, l](int idx) { - currentToolchainChanged(l, idx); + connect(cb, &QComboBox::currentIndexChanged, this, [this, lc](int idx) { + currentToolchainChanged(lc, idx); }); } @@ -250,8 +253,9 @@ private: const GuardLocker locker(m_ignoreChanges); for (auto it = m_languageComboboxMap.cbegin(); it != m_languageComboboxMap.cend(); ++it) { - const Id l = it.key(); - const Toolchains ltcList = ToolchainManager::toolchains(equal(&Toolchain::language, l)); + const LanguageCategory lc = it.key(); + const Toolchains ltcList = ToolchainManager::toolchains( + [lc](const Toolchain *tc) { return lc.contains(tc->language()); }); QComboBox *cb = *it; cb->clear(); @@ -264,18 +268,36 @@ private: return !tc->compilerCommand().isSameDevice(device->rootPath()); }); - for (Toolchain *item : same) - cb->addItem(item->displayName(), item->id()); + const QList sameBundles = ToolchainBundle::collectBundles(same); + const QList otherBundles = ToolchainBundle::collectBundles(other); + for (const ToolchainBundle &b : sameBundles) + cb->addItem(b.displayName(), b.bundleId().toSetting()); - if (!same.isEmpty() && !other.isEmpty()) + if (!sameBundles.isEmpty() && !otherBundles.isEmpty()) cb->insertSeparator(cb->count()); - for (Toolchain *item : other) - cb->addItem(item->displayName(), item->id()); + for (const ToolchainBundle &b : otherBundles) + cb->addItem(b.displayName(), b.bundleId().toSetting()); cb->setEnabled(cb->count() > 1 && !m_isReadOnly); - const int index = indexOf(cb, ToolchainKitAspect::toolchain(m_kit, l)); - cb->setCurrentIndex(index); + Id currentBundleId; + for (const Id lang : lc) { + Toolchain * const currentTc = ToolchainKitAspect::toolchain(m_kit, lang); + if (!currentTc) + continue; + for (const QList &bundles : {sameBundles, otherBundles}) + for (const ToolchainBundle &b : bundles) { + if (b.bundleId() == currentTc->bundleId()) { + currentBundleId = b.bundleId(); + break; + } + if (currentBundleId.isValid()) + break; + } + if (currentBundleId.isValid()) + break; + } + cb->setCurrentIndex(currentBundleId.isValid() ? indexOf(cb, currentBundleId) : -1); } } @@ -286,32 +308,37 @@ private: cb->setEnabled(false); } - void currentToolchainChanged(Id language, int idx) + void currentToolchainChanged(const LanguageCategory &languageCategory, int idx) { if (m_ignoreChanges.isLocked() || idx < 0) return; - const QByteArray id = m_languageComboboxMap.value(language)->itemData(idx).toByteArray(); - Toolchain *tc = ToolchainManager::findToolchain(id); - QTC_ASSERT(!tc || tc->language() == language, return); - if (tc) - ToolchainKitAspect::setToolchain(m_kit, tc); - else - ToolchainKitAspect::clearToolchain(m_kit, language); + const Id bundleId = Id::fromSetting( + m_languageComboboxMap.value(languageCategory)->itemData(idx)); + const Toolchains bundleTcs = ToolchainManager::toolchains( + [bundleId](const Toolchain *tc) { return tc->bundleId() == bundleId; }); + for (const Id lang : languageCategory) { + Toolchain *const tc = Utils::findOrDefault(bundleTcs, [lang](const Toolchain *tc) { + return tc->language() == lang; + }); + if (tc) + ToolchainKitAspect::setToolchain(m_kit, tc); + else + ToolchainKitAspect::clearToolchain(m_kit, lang); + } } - int indexOf(QComboBox *cb, const Toolchain *tc) + int indexOf(QComboBox *cb, Id bundleId) { - const QByteArray id = tc ? tc->id() : QByteArray(); for (int i = 0; i < cb->count(); ++i) { - if (id == cb->itemData(i).toByteArray()) + if (bundleId.toSetting() == cb->itemData(i)) return i; } return -1; } QWidget *m_mainWidget = nullptr; - QHash m_languageComboboxMap; + QHash m_languageComboboxMap; Guard m_ignoreChanges; bool m_isReadOnly = false; }; diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 2aa409b1425..dba9089f797 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -788,6 +788,12 @@ void MsvcToolchain::initEnvModWatcher(const QFuture &future) m_envModWatcher.setFuture(future); } +bool MsvcToolchain::canShareBundleImpl(const Toolchain &other) const +{ + const auto &otherMsvc = static_cast(other); + return m_vcvarsBat == otherMsvc.m_vcvarsBat && m_varsBatArg == otherMsvc.m_varsBatArg; +} + void MsvcToolchain::updateEnvironmentModifications(Utils::EnvironmentItems modifications) { Utils::EnvironmentItem::sort(&modifications); @@ -1183,6 +1189,9 @@ FilePath MsvcToolchain::makeCommand(const Environment &environment) const void MsvcToolchain::rescanForCompiler() { + if (typeId() == Constants::CLANG_CL_TOOLCHAIN_TYPEID) + return; + Utils::Environment env = Utils::Environment::systemEnvironment(); addToEnvironment(env); @@ -1248,11 +1257,16 @@ static QString msvcVarsToDisplay(const MsvcToolchain &tc) return varsBatDisplay; } +static QString msvcVarsToDisplay(const ToolchainBundle &bundle) +{ + return msvcVarsToDisplay(static_cast(*bundle.toolchains().first())); +} + class MsvcBasedToolchainConfigWidget : public ToolchainConfigWidget { public: - explicit MsvcBasedToolchainConfigWidget(Toolchain *tc) - : ToolchainConfigWidget(tc) + explicit MsvcBasedToolchainConfigWidget(const ToolchainBundle &bundle) + : ToolchainConfigWidget(bundle) , m_nameDisplayLabel(new QLabel(this)) , m_varsBatDisplayLabel(new QLabel(this)) { @@ -1270,10 +1284,7 @@ protected: void setFromMsvcToolChain() { - const auto *tc = static_cast(toolchain()); - QTC_ASSERT(tc, return ); - m_nameDisplayLabel->setText(tc->displayName()); - m_varsBatDisplayLabel->setText(msvcVarsToDisplay(*tc)); + m_varsBatDisplayLabel->setText(msvcVarsToDisplay(bundle())); } protected: @@ -1288,8 +1299,8 @@ protected: class MsvcToolchainConfigWidget final : public MsvcBasedToolchainConfigWidget { public: - explicit MsvcToolchainConfigWidget(Toolchain *tc) - : MsvcBasedToolchainConfigWidget(tc) + explicit MsvcToolchainConfigWidget(const ToolchainBundle &bundle) + : MsvcBasedToolchainConfigWidget(bundle) , m_varsBatPathCombo(new QComboBox(this)) , m_varsBatArchCombo(new QComboBox(this)) , m_varsBatArgumentsEdit(new QLineEdit(this)) @@ -1366,10 +1377,8 @@ private: void MsvcToolchainConfigWidget::applyImpl() { - auto *tc = static_cast(toolchain()); - QTC_ASSERT(tc, return ); const QString vcVars = QDir::fromNativeSeparators(m_varsBatPathCombo->currentText()); - tc->setupVarsBat(m_abiWidget->currentAbi(), vcVars, vcVarsArguments()); + bundle().set(&MsvcToolchain::setupVarsBat, m_abiWidget->currentAbi(), vcVars, vcVarsArguments()); setFromMsvcToolchain(); } @@ -1380,11 +1389,10 @@ void MsvcToolchainConfigWidget::discardImpl() bool MsvcToolchainConfigWidget::isDirtyImpl() const { - auto msvcToolchain = static_cast(toolchain()); - - return msvcToolchain->varsBat() != QDir::fromNativeSeparators(m_varsBatPathCombo->currentText()) - || msvcToolchain->varsBatArg() != vcVarsArguments() - || msvcToolchain->targetAbi() != m_abiWidget->currentAbi(); + return bundle().get(&MsvcToolchain::varsBat) + != QDir::fromNativeSeparators(m_varsBatPathCombo->currentText()) + || bundle().get(&MsvcToolchain::varsBatArg) != vcVarsArguments() + || bundle().get(&MsvcToolchain::targetAbi) != m_abiWidget->currentAbi(); } void MsvcToolchainConfigWidget::makeReadOnlyImpl() @@ -1397,10 +1405,7 @@ void MsvcToolchainConfigWidget::makeReadOnlyImpl() void MsvcToolchainConfigWidget::setFromMsvcToolchain() { - const auto *tc = static_cast(toolchain()); - QTC_ASSERT(tc, return ); - m_nameDisplayLabel->setText(tc->displayName()); - QString args = tc->varsBatArg(); + QString args = bundle().get(&MsvcToolchain::varsBatArg); QStringList argList = args.split(' '); for (int i = 0; i < argList.count(); ++i) { if (m_varsBatArchCombo->findText(argList.at(i).trimmed()) != -1) { @@ -1410,16 +1415,15 @@ void MsvcToolchainConfigWidget::setFromMsvcToolchain() break; } } - m_varsBatPathCombo->setCurrentText(QDir::toNativeSeparators(tc->varsBat())); + m_varsBatPathCombo->setCurrentText( + QDir::toNativeSeparators(bundle().get(&MsvcToolchain::varsBat))); m_varsBatArgumentsEdit->setText(args); - m_abiWidget->setAbis(tc->supportedAbis(), tc->targetAbi()); + m_abiWidget->setAbis(bundle().supportedAbis(), bundle().targetAbi()); } void MsvcToolchainConfigWidget::updateAbis() { const QString normalizedVcVars = QDir::fromNativeSeparators(m_varsBatPathCombo->currentText()); - const auto *currentTc = static_cast(toolchain()); - QTC_ASSERT(currentTc, return ); const MsvcToolchain::Platform platform = m_varsBatArchCombo->currentData().value(); const Abi::Architecture arch = archForPlatform(platform); const unsigned char wordWidth = wordWidthForPlatform(platform); @@ -1431,7 +1435,7 @@ void MsvcToolchainConfigWidget::updateAbis() Abi targetAbi; for (const MsvcToolchain *tc : std::as_const(g_availableMsvcToolchains)) { if (tc->varsBat() == normalizedVcVars && tc->targetAbi().wordWidth() == wordWidth - && tc->targetAbi().architecture() == arch && tc->language() == currentTc->language()) { + && tc->targetAbi().architecture() == arch) { // We need to filter out duplicates as there might be multiple toolchains with // same abi (like x86, amd64_x86 for example). for (const Abi &abi : tc->supportedAbis()) { @@ -1487,11 +1491,6 @@ QString MsvcToolchainConfigWidget::vcVarsArguments() const return varsBatArg; } -std::unique_ptr MsvcToolchain::createConfigurationWidget() -{ - return std::make_unique(this); -} - // -------------------------------------------------------------------------- // ClangClToolChainConfigWidget // -------------------------------------------------------------------------- @@ -1499,8 +1498,8 @@ std::unique_ptr MsvcToolchain::createConfigurationWidget( class ClangClToolchainConfigWidget final : public MsvcBasedToolchainConfigWidget { public: - explicit ClangClToolchainConfigWidget(Toolchain *tc) - : MsvcBasedToolchainConfigWidget(tc) + explicit ClangClToolchainConfigWidget(const ToolchainBundle &bundle) + : MsvcBasedToolchainConfigWidget(bundle) , m_varsBatDisplayCombo(new QComboBox(this)) { m_mainLayout->removeRow(m_mainLayout->rowCount() - 1); @@ -1508,28 +1507,9 @@ public: m_varsBatDisplayCombo->setObjectName("varsBatCombo"); m_varsBatDisplayCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); m_mainLayout->addRow(Tr::tr("Initialization:"), m_varsBatDisplayCombo); - - if (tc->isAutoDetected()) { - m_llvmDirLabel = new QLabel(this); - m_llvmDirLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); - m_mainLayout->addRow(Tr::tr("&Compiler path:"), m_llvmDirLabel); - } else { - const QStringList gnuVersionArgs = QStringList("--version"); - m_compilerCommand = new PathChooser(this); - m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); - m_compilerCommand->setCommandVersionArguments(gnuVersionArgs); - m_compilerCommand->setHistoryCompleter("PE.Clang.Command.History"); - m_mainLayout->addRow(Tr::tr("&Compiler path:"), m_compilerCommand); - } + setCommandVersionArguments(QStringList("--version")); addErrorLabel(); setFromClangClToolchain(); - - if (m_compilerCommand) { - connect(m_compilerCommand, - &Utils::PathChooser::rawPathChanged, - this, - &ClangClToolchainConfigWidget::dirty); - } } protected: @@ -1541,28 +1521,18 @@ protected: private: void setFromClangClToolchain(); - QLabel *m_llvmDirLabel = nullptr; QComboBox *m_varsBatDisplayCombo = nullptr; - PathChooser *m_compilerCommand = nullptr; }; void ClangClToolchainConfigWidget::setFromClangClToolchain() { - const auto *currentTC = static_cast(toolchain()); - m_nameDisplayLabel->setText(currentTC->displayName()); m_varsBatDisplayCombo->clear(); - m_varsBatDisplayCombo->addItem(msvcVarsToDisplay(*currentTC)); + m_varsBatDisplayCombo->addItem(msvcVarsToDisplay(bundle())); for (const MsvcToolchain *tc : std::as_const(g_availableMsvcToolchains)) { const QString varsToDisplay = msvcVarsToDisplay(*tc); if (m_varsBatDisplayCombo->findText(varsToDisplay) == -1) m_varsBatDisplayCombo->addItem(varsToDisplay); } - - const auto *clangClToolchain = static_cast(toolchain()); - if (clangClToolchain->isAutoDetected()) - m_llvmDirLabel->setText(clangClToolchain->clangPath().toUserOutput()); - else - m_compilerCommand->setFilePath(clangClToolchain->clangPath()); } class ClangClInfo @@ -1661,32 +1631,28 @@ static Toolchains detectClangClToolChainInPath(const FilePath &clangClPath, void ClangClToolchainConfigWidget::applyImpl() { - Utils::FilePath clangClPath = m_compilerCommand->filePath(); - auto clangClToolchain = static_cast(toolchain()); - clangClToolchain->setClangPath(clangClPath); - + const FilePath clangClPath = bundle().get(&ClangClToolchain::clangPath); if (clangClPath.fileName() != "clang-cl.exe") { - clangClToolchain->resetVarsBat(); + bundle().set(&ClangClToolchain::resetVarsBat); setFromClangClToolchain(); return; } const QString displayedVarsBat = m_varsBatDisplayCombo->currentText(); Toolchains results = detectClangClToolChainInPath(clangClPath, {}, displayedVarsBat); + const QList bundles = ToolchainBundle::collectBundles(results); - if (results.isEmpty()) { - clangClToolchain->resetVarsBat(); + if (bundles.isEmpty()) { + bundle().set(&ClangClToolchain::resetVarsBat); } else { - for (const Toolchain *toolchain : results) { - if (toolchain->language() == clangClToolchain->language()) { - auto mstc = static_cast(toolchain); - clangClToolchain->setupVarsBat(mstc->targetAbi(), mstc->varsBat(), mstc->varsBatArg()); - break; - } - } - - qDeleteAll(results); + const ToolchainBundle &b = bundles.first(); + bundle().set( + &MsvcToolchain::setupVarsBat, + b.targetAbi(), + b.get(&MsvcToolchain::varsBat), + b.get(&MsvcToolchain::varsBatArg)); } + qDeleteAll(results); setFromClangClToolchain(); } @@ -1766,11 +1732,6 @@ void ClangClToolchain::fromMap(const Store &data) m_clangPath = FilePath::fromString(clangPath); } -std::unique_ptr ClangClToolchain::createConfigurationWidget() -{ - return std::make_unique(this); -} - bool ClangClToolchain::operator==(const Toolchain &other) const { if (!MsvcToolchain::operator==(other)) @@ -1785,6 +1746,12 @@ int ClangClToolchain::priority() const return MsvcToolchain::priority() - 1; } +bool ClangClToolchain::canShareBundleImpl(const Toolchain &other) const +{ + return MsvcToolchain::canShareBundleImpl(other) + && m_clangPath == static_cast(other).m_clangPath; +} + Macros ClangClToolchain::msvcPredefinedMacros(const QStringList &cxxflags, const Utils::Environment &env) const { @@ -1842,6 +1809,11 @@ public: Toolchains autoDetect(const ToolchainDetector &detector) const final; bool canCreate() const final { return !g_availableMsvcToolchains.isEmpty(); } + std::unique_ptr createConfigurationWidget( + const ToolchainBundle &bundle) const override + { + return std::make_unique(bundle); + } static QString vcVarsBatFor(const QString &basePath, MsvcToolchain::Platform platform, @@ -2210,6 +2182,11 @@ public: } Toolchains autoDetect(const ToolchainDetector &detector) const final; + std::unique_ptr createConfigurationWidget( + const ToolchainBundle &bundle) const override + { + return std::make_unique(bundle); + } bool canCreate() const final { return !g_availableMsvcToolchains.isEmpty(); } }; diff --git a/src/plugins/projectexplorer/msvctoolchain.h b/src/plugins/projectexplorer/msvctoolchain.h index f22a7af38c2..1ac2594fd1a 100644 --- a/src/plugins/projectexplorer/msvctoolchain.h +++ b/src/plugins/projectexplorer/msvctoolchain.h @@ -39,7 +39,6 @@ public: void toMap(Utils::Store &data) const override; void fromMap(const Utils::Store &data) override; - std::unique_ptr createConfigurationWidget() override; bool hostPrefersToolchain() const override; MacroInspectionRunner createMacroInspectionRunner() const override; @@ -97,6 +96,7 @@ protected: virtual Utils::LanguageVersion msvcLanguageVersion(const QStringList &cxxflags, const Utils::Id &language, const Macros ¯os) const; + bool canShareBundleImpl(const Toolchain &other) const override; struct GenerateEnvResult { @@ -135,10 +135,10 @@ public: QStringList suggestedMkspecList() const override; void addToEnvironment(Utils::Environment &env) const override; Utils::FilePath compilerCommand() const override; // FIXME: Remove + void setCompilerCommand(const Utils::FilePath &cmd) override { setClangPath(cmd); } QList createOutputParsers() const override; void toMap(Utils::Store &data) const override; void fromMap(const Utils::Store &data) override; - std::unique_ptr createConfigurationWidget() override; BuiltInHeaderPathsRunner createBuiltInHeaderPathsRunner( const Utils::Environment &env) const override; @@ -157,6 +157,8 @@ public: int priority() const override; private: + bool canShareBundleImpl(const Toolchain &other) const override; + Utils::FilePath m_clangPath; }; diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index c91e221dd17..b802ebe1fb3 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -855,6 +855,8 @@ bool ProjectExplorerPlugin::initialize(const QStringList &arguments, QString *er // Register languages ToolchainManager::registerLanguage(Constants::C_LANGUAGE_ID, Tr::tr("C")); ToolchainManager::registerLanguage(Constants::CXX_LANGUAGE_ID, Tr::tr("C++")); + ToolchainManager::registerLanguageCategory( + {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}, Tr::tr("C/C++")); IWizardFactory::registerFeatureProvider(new KitFeatureProvider); IWizardFactory::registerFactoryCreator([] { return new SimpleProjectWizard; }); diff --git a/src/plugins/projectexplorer/toolchain.cpp b/src/plugins/projectexplorer/toolchain.cpp index 0fa9033fabb..e242f198753 100644 --- a/src/plugins/projectexplorer/toolchain.cpp +++ b/src/plugins/projectexplorer/toolchain.cpp @@ -5,13 +5,17 @@ #include "abi.h" #include "devicesupport/idevice.h" +#include "gcctoolchain.h" #include "projectexplorerconstants.h" +#include "projectexplorertr.h" #include "toolchainmanager.h" #include "task.h" #include +#include #include +#include #include #include @@ -19,9 +23,11 @@ using namespace Utils; namespace ProjectExplorer { + namespace Internal { const char ID_KEY[] = "ProjectExplorer.ToolChain.Id"; +const char BUNDLE_ID_KEY[] = "ProjectExplorer.ToolChain.BundleId"; const char DISPLAY_NAME_KEY[] = "ProjectExplorer.ToolChain.DisplayName"; const char AUTODETECT_KEY[] = "ProjectExplorer.ToolChain.Autodetect"; const char DETECTION_SOURCE_KEY[] = "ProjectExplorer.ToolChain.DetectionSource"; @@ -55,6 +61,7 @@ public: } QByteArray m_id; + Id m_bundleId; FilePath m_compilerCommand; Key m_compilerCommandKey; Abi m_targetAbi; @@ -125,9 +132,14 @@ Toolchain::Toolchain(Id typeId) : { } +bool Toolchain::canShareBundleImpl(const Toolchain &other) const +{ + Q_UNUSED(other) + return true; +} + void Toolchain::setLanguage(Id language) { - QTC_ASSERT(!d->m_language.isValid() || isAutoDetected(), return); QTC_ASSERT(language.isValid(), return); QTC_ASSERT(ToolchainManager::isLanguageSupported(language), return); @@ -177,6 +189,38 @@ QByteArray Toolchain::id() const return d->m_id; } +Id Toolchain::bundleId() const +{ + return d->m_bundleId; +} + +void Toolchain::setBundleId(Utils::Id id) +{ + d->m_bundleId = id; +} + +bool Toolchain::canShareBundle(const Toolchain &other) const +{ + QTC_ASSERT(typeId() == other.typeId(), return false); + QTC_ASSERT(language() != other.language(), return false); + + if (int(factory()->supportedLanguages().size()) == 1) + return false; + if (detection() != other.detection()) + return false; + + if (typeId() != Constants::MSVC_TOOLCHAIN_TYPEID + && typeId() != Constants::CLANG_CL_TOOLCHAIN_TYPEID + && (targetAbi() != other.targetAbi() || supportedAbis() != other.supportedAbis() + || extraCodeModelFlags() != other.extraCodeModelFlags() + || suggestedMkspecList() != other.suggestedMkspecList() || sysRoot() != other.sysRoot() + || correspondingCompilerCommand(other.language()) != other.compilerCommand())) { + return false; + } + + return canShareBundleImpl(other); +} + QStringList Toolchain::suggestedMkspecList() const { return {}; @@ -251,6 +295,7 @@ void Toolchain::toMap(Store &result) const QString idToSave = d->m_typeId.toString() + QLatin1Char(':') + QString::fromUtf8(id()); result.insert(ID_KEY, idToSave); + result.insert(BUNDLE_ID_KEY, d->m_bundleId.toSetting()); result.insert(DISPLAY_NAME_KEY, displayName()); result.insert(AUTODETECT_KEY, isAutoDetected()); result.insert(DETECTION_SOURCE_KEY, d->m_detectionSource); @@ -366,6 +411,7 @@ void Toolchain::fromMap(const Store &data) QTC_ASSERT(pos > 0, reportError(); return); d->m_typeId = Id::fromString(id.left(pos)); d->m_id = id.mid(pos + 1).toUtf8(); + d->m_bundleId = Id::fromSetting(data.value(BUNDLE_ID_KEY)); const bool autoDetect = data.value(AUTODETECT_KEY, false).toBool(); d->m_detection = autoDetect ? AutoDetection : ManualDetection; @@ -481,6 +527,12 @@ LanguageVersion Toolchain::languageVersion(const Id &language, const Macros &mac } } +FilePath Toolchain::correspondingCompilerCommand(Utils::Id otherLanguage) const +{ + QTC_ASSERT(language() != otherLanguage, return compilerCommand()); + return factory()->correspondingCompilerCommand(compilerCommand(), otherLanguage); +} + FilePaths Toolchain::includedFiles(const QString &option, const QStringList &flags, const FilePath &directoryPath, @@ -595,6 +647,13 @@ Toolchains ToolchainFactory::detectForImport(const ToolchainDescription &tcd) co return {}; } +FilePath ToolchainFactory::correspondingCompilerCommand( + const Utils::FilePath &srcPath, Utils::Id targetLang) const +{ + Q_UNUSED(targetLang) + return srcPath; +} + bool ToolchainFactory::canCreate() const { return m_userCreatable; @@ -662,6 +721,17 @@ QList ToolchainFactory::supportedLanguages() const return m_supportedLanguages; } +LanguageCategory ToolchainFactory::languageCategory() const +{ + const QList langs = supportedLanguages(); + if (langs.size() == 1 + && (langs.first() == Constants::C_LANGUAGE_ID + || langs.first() == Constants::CXX_LANGUAGE_ID)) { + return {Constants::C_LANGUAGE_ID, Constants::CXX_LANGUAGE_ID}; + } + return LanguageCategory(langs.cbegin(), langs.cend()); +} + Id ToolchainFactory::supportedToolchainType() const { return m_supportedToolchainType; @@ -797,4 +867,192 @@ void AsyncToolchainDetector::run() watcher->setFuture(Utils::asyncRun(m_func, m_detector)); } +/* + * PRE: + * - The list of toolchains is not empty. + * - All toolchains in the list have the same bundle id. + * - All toolchains in the list have the same type (and thus were created by the same factory) + * and are also otherwise "compatible" (target ABI etc). + * - No two toolchains in the list are for the same language. + * POST: + * - PRE. + * - There is exactly one toolchain in the list for every language supported by the factory. + * - If there is a C compiler, it comes first in the list. + */ +ToolchainBundle::ToolchainBundle(const Toolchains &toolchains) : m_toolchains(toolchains) +{ + // Check pre-conditions. + QTC_ASSERT(!m_toolchains.isEmpty(), return); + QTC_ASSERT(m_toolchains.size() <= factory()->supportedLanguages().size(), return); + for (const Toolchain * const tc : toolchains) + QTC_ASSERT(factory()->supportedLanguages().contains(tc->language()), return); + for (int i = 1; i < int(toolchains.size()); ++i) { + const Toolchain * const tc = toolchains.at(i); + QTC_ASSERT(tc->typeId() == toolchains.first()->typeId(), return); + QTC_ASSERT(tc->bundleId() == toolchains.first()->bundleId(), return); + } + + addMissingToolchains(); + + // Check post-conditions. + QTC_ASSERT(m_toolchains.size() == m_toolchains.first()->factory()->supportedLanguages().size(), + return); + for (auto i = toolchains.size(); i < m_toolchains.size(); ++i) + QTC_ASSERT(m_toolchains.at(i)->typeId() == m_toolchains.first()->typeId(), return); + + Utils::sort(m_toolchains, [](const Toolchain *tc1, const Toolchain *tc2) { + return tc1 != tc2 && tc1->language() == Constants::C_LANGUAGE_ID; + }); +} + +QList ToolchainBundle::collectBundles() +{ + return collectBundles(ToolchainManager::toolchains()); +} + +QList ToolchainBundle::collectBundles(const Toolchains &toolchains) +{ + QHash toolchainsPerBundleId; + for (Toolchain * const tc : toolchains) + toolchainsPerBundleId[tc->bundleId()] << tc; + + QList bundles; + if (const auto unbundled = toolchainsPerBundleId.constFind(Id()); + unbundled != toolchainsPerBundleId.constEnd()) { + bundles = bundleUnbundledToolchains(*unbundled); + toolchainsPerBundleId.erase(unbundled); + } + + for (const Toolchains &tcs : toolchainsPerBundleId) + bundles << tcs; + return bundles; +} + +ToolchainFactory *ToolchainBundle::factory() const +{ + QTC_ASSERT(!m_toolchains.isEmpty(), return nullptr); + return m_toolchains.first()->factory(); +} + +QString ToolchainBundle::displayName() const +{ + if (!isAutoDetected() || !dynamic_cast(m_toolchains.first())) + return get(&Toolchain::displayName); + + // Auto-detected GCC toolchains encode language and compiler command in their display names. + // We need to omit the language and we always want to use the C compiler command + // for consistency. + FilePath cmd; + for (const Toolchain * const tc : std::as_const(m_toolchains)) { + if (!tc->isValid()) + continue; + cmd = tc->compilerCommand(); + if (tc->language() == Constants::C_LANGUAGE_ID) + break; + } + + QString name = typeDisplayName(); + const Abi abi = targetAbi(); + if (abi.architecture() != Abi::UnknownArchitecture) + name.append(' ').append(Abi::toString(abi.architecture())); + if (abi.wordWidth() != 0) + name.append(' ').append(Abi::toString(abi.wordWidth())); + if (!cmd.exists()) + return name; + return Tr::tr("%1 at %2").arg(name, cmd.toUserOutput()); +} + +ToolchainBundle::Valid ToolchainBundle::validity() const +{ + if (Utils::allOf(m_toolchains, &Toolchain::isValid)) + return Valid::All; + if (Utils::contains(m_toolchains, &Toolchain::isValid)) + return Valid::Some; + return Valid::None; +} + +QList ToolchainBundle::bundleUnbundledToolchains(const Toolchains &unbundled) +{ + QList bundles; + QHash> unbundledByTypeAndLanguage; + for (Toolchain * const tc : unbundled) + unbundledByTypeAndLanguage[tc->typeId()][tc->language()] << tc; + for (const auto &tcsByLang : std::as_const(unbundledByTypeAndLanguage)) { + QList remainingUnbundled; + for (const auto &tcs : tcsByLang) + remainingUnbundled << tcs; + while (true) { + Toolchains nextBundle; + for (Toolchains &list : remainingUnbundled) { + for (auto it = list.begin(); it != list.end(); ++it) { + if (nextBundle.isEmpty() || nextBundle.first()->canShareBundle((**it))) { + nextBundle << *it; + list.erase(it); + break; + } + } + } + if (nextBundle.isEmpty()) + break; + const Id newBundleId = Id::generate(); + for (Toolchain * const tc : nextBundle) + tc->setBundleId(newBundleId); + bundles << nextBundle; + } + } + + return bundles; +} + +void ToolchainBundle::setCompilerCommand(Utils::Id language, const Utils::FilePath &cmd) +{ + for (Toolchain *const tc : std::as_const(m_toolchains)) { + if (tc->language() == language) { + tc->setCompilerCommand(cmd); + break; + } + } +} + +FilePath ToolchainBundle::compilerCommand(Utils::Id language) const +{ + for (Toolchain *const tc : std::as_const(m_toolchains)) { + if (tc->language() == language) + return tc->compilerCommand(); + } + return {}; +} + +void ToolchainBundle::deleteToolchains() +{ + qDeleteAll(m_toolchains); + m_toolchains.clear(); +} + +ToolchainBundle ToolchainBundle::clone() const +{ + const Toolchains clones = Utils::transform(m_toolchains, &Toolchain::clone); + const Id newBundleId = Id::generate(); + for (Toolchain * const tc : clones) + tc->setBundleId(newBundleId); + return clones; +} + +void ToolchainBundle::addMissingToolchains() +{ + const QList missingLanguages + = Utils::filtered(m_toolchains.first()->factory()->supportedLanguages(), [this](Id lang) { + return !Utils::contains(m_toolchains, [lang](const Toolchain *tc) { + return tc->language() == lang; + }); + }); + for (const Id lang : missingLanguages) { + Toolchain * const tc = m_toolchains.first()->clone(); + tc->setLanguage(lang); + tc->setCompilerCommand(m_toolchains.first()->correspondingCompilerCommand(lang)); + m_toolchains << tc; + m_createdToolchains << tc; + } +} + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/toolchain.h b/src/plugins/projectexplorer/toolchain.h index 5467d82abb9..dd3459905c2 100644 --- a/src/plugins/projectexplorer/toolchain.h +++ b/src/plugins/projectexplorer/toolchain.h @@ -18,9 +18,11 @@ #include #include +#include #include #include +#include namespace Utils { class OutputLineParser; } @@ -54,6 +56,8 @@ public: Utils::Id language; }; +using LanguageCategory = QSet; + // -------------------------------------------------------------------------- // Toolchain (documentation inside) // -------------------------------------------------------------------------- @@ -83,6 +87,10 @@ public: QByteArray id() const; + Utils::Id bundleId() const; + void setBundleId(Utils::Id id); + bool canShareBundle(const Toolchain &other) const; + virtual QStringList suggestedMkspecList() const; Utils::Id typeId() const; @@ -132,14 +140,13 @@ public: Utils::Id language() const; virtual Utils::FilePath compilerCommand() const; // FIXME: De-virtualize. - void setCompilerCommand(const Utils::FilePath &command); + virtual void setCompilerCommand(const Utils::FilePath &command); virtual bool matchesCompilerCommand(const Utils::FilePath &command) const; virtual QList createOutputParsers() const = 0; virtual bool operator ==(const Toolchain &) const; - virtual std::unique_ptr createConfigurationWidget() = 0; Toolchain *clone() const; // Used by the toolchainmanager to save user-generated tool chains. @@ -165,6 +172,8 @@ public: virtual int priority() const { return PriorityNormal; } virtual GccToolchain *asGccToolchain() { return nullptr; } + Utils::FilePath correspondingCompilerCommand(Utils::Id otherLanguage) const; + protected: explicit Toolchain(Utils::Id typeId); @@ -196,6 +205,8 @@ private: Toolchain(const Toolchain &) = delete; Toolchain &operator=(const Toolchain &) = delete; + virtual bool canShareBundleImpl(const Toolchain &other) const; + const std::unique_ptr d; friend class Internal::ToolchainSettingsAccessor; @@ -204,6 +215,98 @@ private: using Toolchains = QList; +class PROJECTEXPLORER_EXPORT ToolchainBundle +{ +public: + ToolchainBundle(const Toolchains &toolchains); + + static QList collectBundles(); + static QList collectBundles(const Toolchains &toolchains); + + template + R get(R (T:: *getter)(A...) const, A&&... args) const + { + return std::invoke(getter, static_cast(*m_toolchains.first()), std::forward(args)...); + } + + template R& get(R T::*member) const + { + return static_cast(*m_toolchains.first()).*member; + } + + template void set(void (T::*setter)(const A&...), const A& ...args) + { + for (Toolchain * const tc : std::as_const(m_toolchains)) + std::invoke(setter, static_cast(*tc), args...); + } + + template void set(void (T::*setter)(A...), const A ...args) + { + for (Toolchain * const tc : std::as_const(m_toolchains)) + std::invoke(setter, static_cast(*tc), args...); + } + + template void forEach(const std::function &modifier) + { + for (Toolchain * const tc : std::as_const(m_toolchains)) + modifier(static_cast(*tc)); + } + + template void forEach(const std::function &func) const + { + for (const Toolchain * const tc : m_toolchains) + func(static_cast(*tc)); + } + + int size() const { return m_toolchains.size(); } + + const QList toolchains() const { return m_toolchains; } + const QList createdToolchains() const { return m_createdToolchains; } + ToolchainFactory *factory() const; + Utils::Id bundleId() const { return get(&Toolchain::bundleId); } + QString displayName() const; + Utils::Id type() const { return get(&Toolchain::typeId); } + QString typeDisplayName() const { return get(&Toolchain::typeDisplayName); } + QStringList extraCodeModelFlags() const { return get(&Toolchain::extraCodeModelFlags); } + bool isAutoDetected() const { return get(&Toolchain::isAutoDetected); } + bool isSdkProvided() const { return get(&Toolchain::isSdkProvided); } + Utils::FilePath compilerCommand(Utils::Id language) const; + Abi targetAbi() const { return get(&Toolchain::targetAbi); } + QList supportedAbis() const { return get(&Toolchain::supportedAbis); } + Utils::FilePath makeCommand(const Utils::Environment &env) const + { + return get(&Toolchain::makeCommand, env); + } + + enum class Valid { All, Some, None }; + Valid validity() const; + + void setDetection(Toolchain::Detection d) { set(&Toolchain::setDetection, d); } + void setCompilerCommand(Utils::Id language, const Utils::FilePath &cmd); + + void setDisplayName(const QString &name) { set(&Toolchain::setDisplayName, name); } + void setTargetAbi(const Abi &abi) { set(&Toolchain::setTargetAbi, abi); } + + ToolchainBundle clone() const; + + // Rampdown operations. No regular access to the bundle is allowed after calling these. + bool removeToolchain(Toolchain *tc) { return m_toolchains.removeOne(tc); } + void clearToolchains() { m_toolchains.clear(); } + void deleteToolchains(); + + friend bool operator==(const ToolchainBundle &b1, const ToolchainBundle &b2) + { + return b1.m_toolchains == b2.m_toolchains; + } + +private: + void addMissingToolchains(); + static QList bundleUnbundledToolchains(const Toolchains &unbundled); + + Toolchains m_toolchains; + Toolchains m_createdToolchains; +}; + class PROJECTEXPLORER_EXPORT BadToolchain { public: @@ -276,6 +379,10 @@ public: const ToolchainDetector &detector) const; virtual Toolchains autoDetect(const ToolchainDetector &detector) const; virtual Toolchains detectForImport(const ToolchainDescription &tcd) const; + virtual std::unique_ptr createConfigurationWidget( + const ToolchainBundle &bundle) const = 0; + virtual Utils::FilePath correspondingCompilerCommand( + const Utils::FilePath &srcPath, Utils::Id targetLang) const; virtual bool canCreate() const; Toolchain *create() const; @@ -289,6 +396,7 @@ public: static Toolchain *createToolchain(Utils::Id toolchainType); QList supportedLanguages() const; + LanguageCategory languageCategory() const; void setUserCreatable(bool userCreatable); diff --git a/src/plugins/projectexplorer/toolchainconfigwidget.cpp b/src/plugins/projectexplorer/toolchainconfigwidget.cpp index 60b1479e7d8..2192c01c0c5 100644 --- a/src/plugins/projectexplorer/toolchainconfigwidget.cpp +++ b/src/plugins/projectexplorer/toolchainconfigwidget.cpp @@ -4,13 +4,17 @@ #include "toolchainconfigwidget.h" #include "toolchain.h" +#include "projectexplorerconstants.h" #include "projectexplorertr.h" +#include "toolchainmanager.h" #include #include #include +#include #include +#include #include #include #include @@ -20,11 +24,9 @@ using namespace Utils; namespace ProjectExplorer { -ToolchainConfigWidget::ToolchainConfigWidget(Toolchain *tc) : - m_toolChain(tc) +ToolchainConfigWidget::ToolchainConfigWidget(const ToolchainBundle &bundle) + : m_bundle(bundle) { - Q_ASSERT(tc); - auto centralWidget = new Utils::DetailsWidget; centralWidget->setState(Utils::DetailsWidget::NoSummary); @@ -42,38 +44,48 @@ ToolchainConfigWidget::ToolchainConfigWidget(Toolchain *tc) : m_mainLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow); // for the Macs... m_nameLineEdit = new QLineEdit; - m_nameLineEdit->setText(tc->displayName()); + m_nameLineEdit->setText(bundle.displayName()); m_mainLayout->addRow(Tr::tr("Name:"), m_nameLineEdit); + if (bundle.type() != Constants::MSVC_TOOLCHAIN_TYPEID) + setupCompilerPathChoosers(); + connect(m_nameLineEdit, &QLineEdit::textChanged, this, &ToolchainConfigWidget::dirty); } void ToolchainConfigWidget::apply() { - m_toolChain->setDisplayName(m_nameLineEdit->text()); + m_bundle.setDisplayName(m_nameLineEdit->text()); + if (!bundle().isAutoDetected()) { + for (const auto &[tc, pathChooser] : std::as_const(m_commands)) + bundle().setCompilerCommand(tc->language(), pathChooser->filePath()); + } applyImpl(); } void ToolchainConfigWidget::discard() { - m_nameLineEdit->setText(m_toolChain->displayName()); + m_nameLineEdit->setText(m_bundle.displayName()); + for (const auto &[tc, pathChooser] : std::as_const(m_commands)) + pathChooser->setFilePath(bundle().compilerCommand(tc->language())); discardImpl(); } bool ToolchainConfigWidget::isDirty() const { - return m_nameLineEdit->text() != m_toolChain->displayName() || isDirtyImpl(); -} - -Toolchain *ToolchainConfigWidget::toolchain() const -{ - return m_toolChain; + for (const auto &[tc, pathChooser] : std::as_const(m_commands)) { + if (pathChooser->filePath() != bundle().compilerCommand(tc->language())) + return true; + } + return m_nameLineEdit->text() != m_bundle.displayName() || isDirtyImpl(); } void ToolchainConfigWidget::makeReadOnly() { m_nameLineEdit->setEnabled(false); + for (const auto &commands : std::as_const(m_commands)) + commands.second->setReadOnly(true); makeReadOnlyImpl(); } @@ -122,4 +134,90 @@ QStringList ToolchainConfigWidget::splitString(const QString &s) return res; } +ToolchainConfigWidget::ToolchainChooser ToolchainConfigWidget::compilerPathChooser(Utils::Id language) +{ + for (const ToolchainChooser &chooser : std::as_const(m_commands)) { + if (chooser.first->language() == language) + return chooser; + } + return {}; +} + +void ToolchainConfigWidget::setupCompilerPathChoosers() +{ + const QString nameLabelString = int(bundle().toolchains().size()) == 1 + ? Tr::tr("&Compiler path") + : QString(); + bundle().forEach([&](const Toolchain &tc) { + const QString name = !nameLabelString.isEmpty() + ? nameLabelString + : Tr::tr("%1 compiler path").arg( + ToolchainManager::displayNameOfLanguageId(tc.language())); + const auto commandChooser = new PathChooser(this); + commandChooser->setExpectedKind(PathChooser::ExistingCommand); + commandChooser->setHistoryCompleter("PE.ToolChainCommand.History"); + commandChooser->setAllowPathFromDevice(true); + commandChooser->setFilePath(tc.compilerCommand()); + m_commands << std::make_pair(&tc, commandChooser); + if (tc.language() == Constants::CXX_LANGUAGE_ID + && bundle().factory()->supportedLanguages().contains(Constants::C_LANGUAGE_ID)) { + m_deriveCxxCompilerCheckBox = new QCheckBox(Tr::tr("Derive from C compiler")); + m_deriveCxxCompilerCheckBox->setChecked(true); + const auto commandLayout = new QHBoxLayout; + commandLayout->addWidget(commandChooser); + commandLayout->addWidget(m_deriveCxxCompilerCheckBox); + m_mainLayout->addRow(name, commandLayout); + if (!tc.compilerCommand().isExecutableFile()) + deriveCxxCompilerCommand(); + } else { + m_mainLayout->addRow(name, commandChooser); + } + connect(commandChooser, &PathChooser::rawPathChanged, this, [this, &tc] { + emit compilerCommandChanged(tc.language()); + if (tc.language() == Constants::C_LANGUAGE_ID) + deriveCxxCompilerCommand(); + }); + connect(commandChooser, &PathChooser::rawPathChanged, this, &ToolchainConfigWidget::dirty); + }); +} + +FilePath ToolchainConfigWidget::compilerCommand(Utils::Id language) +{ + if (const PathChooser * const chooser = compilerPathChooser(language).second) + return chooser->filePath(); + return {}; +} + +bool ToolchainConfigWidget::hasAnyCompiler() const +{ + for (const auto &cmd : std::as_const(m_commands)) { + if (cmd.second->filePath().isExecutableFile()) + return true; + } + return false; +} + +void ToolchainConfigWidget::setCommandVersionArguments(const QStringList &args) +{ + for (const auto &[_,pathChooser] : std::as_const(m_commands)) + pathChooser->setCommandVersionArguments(args); +} + +void ToolchainConfigWidget::deriveCxxCompilerCommand() +{ + if (!m_deriveCxxCompilerCheckBox || !m_deriveCxxCompilerCheckBox->isChecked()) + return; + + using namespace Constants; + const ToolchainChooser cChooser = compilerPathChooser(C_LANGUAGE_ID); + const ToolchainChooser cxxChooser = compilerPathChooser(CXX_LANGUAGE_ID); + QTC_ASSERT(cChooser.first && cChooser.second && cxxChooser.second, return); + if (cChooser.second->filePath().isExecutableFile()) { + if (const FilePath cxxCmd = bundle().factory()->correspondingCompilerCommand( + cChooser.second->filePath(), CXX_LANGUAGE_ID); + cxxCmd.isExecutableFile()) + cxxChooser.second->setFilePath(cxxCmd); + } +} + } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/toolchainconfigwidget.h b/src/plugins/projectexplorer/toolchainconfigwidget.h index 02257ef571e..1f8cee1273a 100644 --- a/src/plugins/projectexplorer/toolchainconfigwidget.h +++ b/src/plugins/projectexplorer/toolchainconfigwidget.h @@ -5,9 +5,16 @@ #include "projectexplorer_export.h" +#include "toolchain.h" + #include +#include + +namespace Utils { class PathChooser; } + QT_BEGIN_NAMESPACE +class QCheckBox; class QFormLayout; class QLineEdit; class QLabel; @@ -15,8 +22,6 @@ QT_END_NAMESPACE namespace ProjectExplorer { -class Toolchain; - // -------------------------------------------------------------------------- // ToolChainConfigWidget // -------------------------------------------------------------------------- @@ -26,9 +31,9 @@ class PROJECTEXPLORER_EXPORT ToolchainConfigWidget : public QScrollArea Q_OBJECT public: - explicit ToolchainConfigWidget(Toolchain *tc); + explicit ToolchainConfigWidget(const ToolchainBundle &bundle); - Toolchain *toolchain() const; + ToolchainBundle bundle() const { return m_bundle; } void apply(); void discard(); @@ -36,6 +41,7 @@ public: void makeReadOnly(); signals: + void compilerCommandChanged(Utils::Id language); void dirty(); protected: @@ -49,12 +55,23 @@ protected: void addErrorLabel(); static QStringList splitString(const QString &s); + Utils::FilePath compilerCommand(Utils::Id language); + bool hasAnyCompiler() const; + void setCommandVersionArguments(const QStringList &args); + void deriveCxxCompilerCommand(); + QFormLayout *m_mainLayout; - QLineEdit *m_nameLineEdit; private: - Toolchain *m_toolChain; + using ToolchainChooser = std::pair; + ToolchainChooser compilerPathChooser(Utils::Id language); + void setupCompilerPathChoosers(); + + ToolchainBundle m_bundle; + QLineEdit *m_nameLineEdit = nullptr; QLabel *m_errorLabel = nullptr; + QCheckBox *m_deriveCxxCompilerCheckBox = nullptr; + QList m_commands; }; } // namespace ProjectExplorer diff --git a/src/plugins/projectexplorer/toolchainmanager.cpp b/src/plugins/projectexplorer/toolchainmanager.cpp index e463a317a3b..f3fa73bb9c3 100644 --- a/src/plugins/projectexplorer/toolchainmanager.cpp +++ b/src/plugins/projectexplorer/toolchainmanager.cpp @@ -43,6 +43,7 @@ public: Toolchains m_toolChains; // prioritized List BadToolchains m_badToolchains; // to be skipped when auto-detecting QVector m_languages; + QList> m_languageCategories; ToolchainDetectionSettings m_detectionSettings; bool m_loaded = false; }; @@ -255,6 +256,11 @@ bool ToolchainManager::registerLanguage(const Utils::Id &language, const QString return true; } +void ToolchainManager::registerLanguageCategory(const LanguageCategory &languages, const QString &displayName) +{ + d->m_languageCategories.push_back(std::make_pair(languages, displayName)); +} + QString ToolchainManager::displayNameOfLanguageId(const Utils::Id &id) { QTC_ASSERT(id.isValid(), return Tr::tr("None")); @@ -263,6 +269,36 @@ QString ToolchainManager::displayNameOfLanguageId(const Utils::Id &id) return entry.displayName; } +QString ToolchainManager::displayNameOfLanguageCategory(const LanguageCategory &category) +{ + if (int(category.size()) == 1) + return displayNameOfLanguageId(*category.begin()); + QString name = Utils::findOrDefault(d->m_languageCategories, [&category](const auto &e) { + return e.first == category; + }).second; + QTC_ASSERT(!name.isEmpty(), return Tr::tr("None")); + return name; +} + +const QList ToolchainManager::languageCategories() +{ + QList categories + = Utils::transform>(d->m_languageCategories, [](const auto &e) { + return e.first; + }); + const QList languages = allLanguages(); + for (const Utils::Id &l : languages) { + if (Utils::contains(categories, [l](const LanguageCategory &lc) { + return lc.contains(l); + })) { + continue; + } + categories.push_back({l}); + } + + return categories; +} + bool ToolchainManager::isLanguageSupported(const Utils::Id &id) { return Utils::contains(d->m_languages, Utils::equal(&LanguageDisplayPair::id, id)); diff --git a/src/plugins/projectexplorer/toolchainmanager.h b/src/plugins/projectexplorer/toolchainmanager.h index 3a8dde495ea..5eff268c288 100644 --- a/src/plugins/projectexplorer/toolchainmanager.h +++ b/src/plugins/projectexplorer/toolchainmanager.h @@ -12,8 +12,6 @@ #include #include -#include - namespace Utils { class FilePath; } namespace ProjectExplorer { @@ -53,7 +51,11 @@ public: static QList allLanguages(); static bool registerLanguage(const Utils::Id &language, const QString &displayName); + static void registerLanguageCategory( + const LanguageCategory &languages, const QString &displayName); static QString displayNameOfLanguageId(const Utils::Id &id); + static QString displayNameOfLanguageCategory(const LanguageCategory &category); + static const QList languageCategories(); static bool isLanguageSupported(const Utils::Id &id); static void aboutToShutdown(); diff --git a/src/plugins/projectexplorer/toolchainoptionspage.cpp b/src/plugins/projectexplorer/toolchainoptionspage.cpp index c691a847356..c773a6d6957 100644 --- a/src/plugins/projectexplorer/toolchainoptionspage.cpp +++ b/src/plugins/projectexplorer/toolchainoptionspage.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -48,8 +49,8 @@ namespace Internal { class ToolChainTreeItem : public TreeItem { public: - ToolChainTreeItem(QStackedWidget *parentWidget, Toolchain *tc, bool c) : - toolChain(tc), changed(c), m_parentWidget(parentWidget) + ToolChainTreeItem(QStackedWidget *parentWidget, const ToolchainBundle &bundle, bool c) : + bundle(bundle), changed(c), m_parentWidget(parentWidget) {} QVariant data(int column, int role) const override @@ -57,8 +58,8 @@ public: switch (role) { case Qt::DisplayRole: if (column == 0) - return toolChain->displayName(); - return toolChain->typeDisplayName(); + return bundle.displayName(); + return bundle.typeDisplayName(); case Qt::FontRole: { QFont font; font.setBold(changed); @@ -66,18 +67,31 @@ public: } case Qt::ToolTipRole: { QString toolTip; - if (toolChain->isValid()) { + const ToolchainBundle::Valid validity = bundle.validity(); + if (validity != ToolchainBundle::Valid::None) { toolTip = Tr::tr("ABI: %1").arg( changed ? Tr::tr("not up-to-date") - : toolChain->targetAbi().toString()); + : bundle.targetAbi().toString()); + if (validity == ToolchainBundle::Valid::Some) + toolTip.append("
").append( + Tr::tr("Not all compilers are set up correctly.")); } else { toolTip = Tr::tr("This toolchain is invalid."); } return QVariant("
" + toolTip + "
"); } case Qt::DecorationRole: - return column == 0 && !toolChain->isValid() - ? Utils::Icons::CRITICAL.icon() : QVariant(); + if (column == 0) { + switch (bundle.validity()) { + case ToolchainBundle::Valid::All: + break; + case ToolchainBundle::Valid::Some: + return Utils::Icons::WARNING.icon(); + case ToolchainBundle::Valid::None: + return Utils::Icons::CRITICAL.icon(); + } + } + return QVariant(); } return {}; } @@ -85,10 +99,10 @@ public: ToolchainConfigWidget *widget() { if (!m_widget) { - m_widget = toolChain->createConfigurationWidget().release(); + m_widget = bundle.factory()->createConfigurationWidget(bundle).release(); if (m_widget) { m_parentWidget->addWidget(m_widget); - if (toolChain->isAutoDetected()) + if (bundle.isAutoDetected()) m_widget->makeReadOnly(); QObject::connect(m_widget, &ToolchainConfigWidget::dirty, [this] { @@ -100,7 +114,7 @@ public: return m_widget; } - Toolchain *toolChain; + ToolchainBundle bundle; bool changed; private: @@ -163,16 +177,15 @@ public: {ProjectExplorer::Constants::msgAutoDetectedToolTip()}); auto manualRoot = new StaticTreeItem(ProjectExplorer::Constants::msgManual()); - const QList languages = ToolchainManager::allLanguages(); - for (const Utils::Id &l : languages) { - const QString dn = ToolchainManager::displayNameOfLanguageId(l); + for (const LanguageCategory &category : ToolchainManager::languageCategories()) { + const QString dn = ToolchainManager::displayNameOfLanguageCategory(category); auto autoNode = new StaticTreeItem(dn); auto manualNode = new StaticTreeItem(dn); autoRoot->appendChild(autoNode); manualRoot->appendChild(manualNode); - m_languageMap.insert(l, {autoNode, manualNode}); + m_languageMap.insert(category, {autoNode, manualNode}); } m_model.rootItem()->appendChild(autoRoot); @@ -199,23 +212,14 @@ public: if (languages.isEmpty()) continue; - if (languages.count() == 1) { - addMenu->addAction(createAction(factory->displayName(), factory, languages.at(0))); - } else { - Utils::sort(languages, [](const Utils::Id &l1, const Utils::Id &l2) { - return ToolchainManager::displayNameOfLanguageId(l1) < ToolchainManager::displayNameOfLanguageId(l2); - }); - auto subMenu = addMenu->addMenu(factory->displayName()); - for (const Utils::Id &l : std::as_const(languages)) - subMenu->addAction(createAction(ToolchainManager::displayNameOfLanguageId(l), factory, l)); - } + addMenu->addAction(createAction(factory->displayName(), factory, languages)); } m_addButton->setMenu(addMenu); if (HostOsInfo::isMacHost()) m_addButton->setStyleSheet("text-align:center;"); m_cloneButton = new QPushButton(Tr::tr("Clone"), this); - connect(m_cloneButton, &QAbstractButton::clicked, this, [this] { cloneToolChain(); }); + connect(m_cloneButton, &QAbstractButton::clicked, this, [this] { cloneToolchains(); }); m_delButton = new QPushButton(Tr::tr("Remove"), this); @@ -227,7 +231,7 @@ public: if (item->level() != 3) return; const auto tcItem = static_cast(item); - if (!tcItem->toolChain->isSdkProvided()) + if (!tcItem->bundle.isSdkProvided()) itemsToRemove << tcItem; }); for (ToolChainTreeItem * const tcItem : std::as_const(itemsToRemove)) @@ -253,8 +257,11 @@ public: m_widgetStack = new QStackedWidget; m_container->setWidget(m_widgetStack); - for (Toolchain *tc : ToolchainManager::toolchains()) - insertToolChain(tc); + const QList bundles = ToolchainBundle::collectBundles(); + for (const ToolchainBundle &b : bundles) { + ToolchainManager::registerToolchains(b.createdToolchains()); + insertBundle(b); + } auto buttonLayout = new QVBoxLayout; buttonLayout->setSpacing(6); @@ -295,21 +302,23 @@ public: void toolChainSelectionChanged(); void updateState(); - void createToolChain(ToolchainFactory *factory, const Utils::Id &language); - void cloneToolChain(); + void createToolchains(ToolchainFactory *factory, const QList &languages); + void cloneToolchains(); ToolChainTreeItem *currentTreeItem(); void markForRemoval(ToolChainTreeItem *item); - ToolChainTreeItem *insertToolChain(ProjectExplorer::Toolchain *tc, bool changed = false); // Insert directly into model + ToolChainTreeItem *insertBundle(const ToolchainBundle &bundle, bool changed = false); // Insert directly into model void handleToolchainsRegistered(const Toolchains &toolchains); void handleToolchainsDeregistered(const Toolchains &toolchains); - StaticTreeItem *parentForToolChain(Toolchain *tc); - QAction *createAction(const QString &name, ToolchainFactory *factory, Utils::Id language) + StaticTreeItem *rootItem(const LanguageCategory &languageCategory, bool autoDetected); + StaticTreeItem *parentForBundle(const ToolchainBundle &bundle); + StaticTreeItem *parentForToolchain(const Toolchain &tc); + QAction *createAction(const QString &name, ToolchainFactory *factory, const QList &languages) { auto action = new QAction(name, this); connect(action, &QAction::triggered, this, - [this, factory, language] { createToolChain(factory, language); }); + [this, factory, languages] { createToolchains(factory, languages); }); return action; } @@ -331,10 +340,13 @@ public: QPushButton *m_redetectButton; QPushButton *m_detectionSettingsButton; - QHash> m_languageMap; + QHash> m_languageMap; - QList m_toAddList; - QList m_toRemoveList; + using AddRemoveList = QList; + AddRemoveList m_toAddList; + AddRemoveList m_toRemoveList; + Guard m_registerGuard; + Guard m_deregisterGuard; ToolchainDetectionSettings m_detectionSettings; }; @@ -342,20 +354,21 @@ public: void ToolChainOptionsWidget::markForRemoval(ToolChainTreeItem *item) { m_model.takeItem(item); - if (m_toAddList.contains(item)) { - delete item->toolChain; - item->toolChain = nullptr; - m_toAddList.removeOne(item); + if (const auto it = std::find(m_toAddList.begin(), m_toAddList.end(), item); + it != m_toAddList.end()) { + item->bundle.deleteToolchains(); + m_toAddList.erase(it); delete item; } else { m_toRemoveList.append(item); } } -ToolChainTreeItem *ToolChainOptionsWidget::insertToolChain(Toolchain *tc, bool changed) +ToolChainTreeItem *ToolChainOptionsWidget::insertBundle( + const ToolchainBundle &bundle, bool changed) { - StaticTreeItem *parent = parentForToolChain(tc); - auto item = new ToolChainTreeItem(m_widgetStack, tc, changed); + StaticTreeItem *parent = parentForBundle(bundle); + auto item = new ToolChainTreeItem(m_widgetStack, bundle, changed); parent->appendChild(item); return item; @@ -363,86 +376,148 @@ ToolChainTreeItem *ToolChainOptionsWidget::insertToolChain(Toolchain *tc, bool c void ToolChainOptionsWidget::handleToolchainsRegistered(const Toolchains &toolchains) { - for (Toolchain * const tc : toolchains) { - if (Utils::eraseOne(m_toAddList, [tc](const ToolChainTreeItem *item) { - return item->toolChain == tc; })) { - // do not delete here! - continue; - } + if (m_registerGuard.isLocked()) + return; + GuardLocker locker(m_registerGuard); - insertToolChain(tc); + if (const auto it = std::find_if( + m_toAddList.begin(), + m_toAddList.end(), + [&toolchains](ToolChainTreeItem * const item) { + return item->bundle.bundleId() == toolchains.first()->bundleId(); + }); + it != m_toAddList.end()) { + if ((*it)->bundle.toolchains().size() == toolchains.size()) + m_toAddList.erase(it); + return; + } + + const QList bundles = ToolchainBundle::collectBundles(toolchains); + for (const ToolchainBundle &bundle : bundles) { + ToolchainManager::registerToolchains(bundle.createdToolchains()); + insertBundle(bundle); } updateState(); } void ToolChainOptionsWidget::handleToolchainsDeregistered(const Toolchains &toolchains) { - for (Toolchain * const tc : toolchains) { - if (auto it = std::find_if( - m_toRemoveList.begin(), - m_toRemoveList.end(), - [tc](const ToolChainTreeItem *item) { return item->toolChain == tc; }); - it != m_toRemoveList.end()) { - m_toRemoveList.erase(it); - delete *it; - continue; - } + if (m_deregisterGuard.isLocked()) + return; + GuardLocker locker(m_deregisterGuard); - StaticTreeItem *parent = parentForToolChain(tc); - auto item = parent->findChildAtLevel(1, [tc](TreeItem *item) { - return static_cast(item)->toolChain == tc; - }); + if (const auto it = std::find_if( + m_toRemoveList.begin(), + m_toRemoveList.end(), + [&toolchains](const ToolChainTreeItem *item) { + return item->bundle.toolchains() == toolchains; + }); + it != m_toRemoveList.end()) { + ToolChainTreeItem * const item = *it; + m_toRemoveList.erase(it); + delete item; + return; + } + + QList affectedItems; + for (Toolchain * const tc : toolchains) { + StaticTreeItem *parent = parentForToolchain(*tc); + auto item = static_cast( + parent->findChildAtLevel(1, [tc](TreeItem *item) { + return static_cast(item)->bundle.bundleId() == tc->bundleId(); + })); + const bool removed = item->bundle.removeToolchain(tc); + QTC_CHECK(removed); + affectedItems << item; + } + + for (ToolChainTreeItem *item : std::as_const(affectedItems)) { + ToolchainManager::deregisterToolchains(item->bundle.toolchains()); + item->bundle.clearToolchains(); m_model.destroyItem(item); } updateState(); } -StaticTreeItem *ToolChainOptionsWidget::parentForToolChain(Toolchain *tc) +StaticTreeItem *ToolChainOptionsWidget::rootItem( + const LanguageCategory &languageCategory, bool autoDetected) { - QPair nodes = m_languageMap.value(tc->language()); - return tc->isAutoDetected() ? nodes.first : nodes.second; + QPair nodes = m_languageMap.value(languageCategory); + return autoDetected ? nodes.first : nodes.second; +} + +StaticTreeItem *ToolChainOptionsWidget::parentForBundle(const ToolchainBundle &bundle) +{ + return rootItem(bundle.factory()->languageCategory(), bundle.isAutoDetected()); +} + +StaticTreeItem *ToolChainOptionsWidget::parentForToolchain(const Toolchain &tc) +{ + return rootItem(tc.factory()->languageCategory(), tc.isAutoDetected()); } void ToolChainOptionsWidget::redetectToolchains() { - QList itemsToRemove; + // The second element is the set of toolchains for the respective bundle that were re-discovered. + using ItemToCheck = std::pair; + QList itemsToRemove; + Toolchains knownTcs; + + // Step 1: All previously auto-detected items are candidates for removal. m_model.forAllItems([&itemsToRemove, &knownTcs](TreeItem *item) { if (item->level() != 3) return; const auto tcItem = static_cast(item); - if (tcItem->toolChain->isAutoDetected() && !tcItem->toolChain->isSdkProvided()) - itemsToRemove << tcItem; + if (tcItem->bundle.isAutoDetected() && !tcItem->bundle.isSdkProvided()) + itemsToRemove << std::make_pair(tcItem, Toolchains()); else - knownTcs << tcItem->toolChain; + knownTcs << tcItem->bundle.toolchains(); }); + Toolchains toAdd; - QSet toDelete; ToolchainManager::resetBadToolchains(); + + // Step 2: Re-detect toolchains. for (ToolchainFactory *f : ToolchainFactory::allToolchainFactories()) { const ToolchainDetector detector(knownTcs, DeviceManager::defaultDesktopDevice(), {}); // FIXME: Pass search paths for (Toolchain * const tc : f->autoDetect(detector)) { - if (knownTcs.contains(tc) || toDelete.contains(tc)) + if (knownTcs.contains(tc)) continue; - const auto matchItem = [tc](const ToolChainTreeItem *item) { - return *item->toolChain == *tc; + knownTcs << tc; + const auto matchItem = [&](const ItemToCheck &item) { + return Utils::contains(item.first->bundle.toolchains(), [&](Toolchain *btc) { + return *btc == *tc; + }); }; - ToolChainTreeItem * const item = findOrDefault(itemsToRemove, matchItem); - if (item) { - itemsToRemove.removeOne(item); - toDelete << tc; + if (const auto item + = std::find_if(itemsToRemove.begin(), itemsToRemove.end(), matchItem); + item != itemsToRemove.end()) { + item->second << tc; continue; } - knownTcs << tc; toAdd << tc; } } - for (ToolChainTreeItem * const tcItem : std::as_const(itemsToRemove)) - markForRemoval(tcItem); - for (Toolchain * const newTc : std::as_const(toAdd)) - m_toAddList.append(insertToolChain(newTc, true)); - qDeleteAll(toDelete); + + // Step 3: Items whose toolchains were all re-discovered are no longer candidates for removal. + // Instead, delete the re-discovered toolchains. + // Conversely, if not all toolchains of the bundle were re-discovered, we remove the existing + // item and the newly discovered toolchains are marked for re-bundling. + for (const auto &[item, newToolchains] : itemsToRemove) { + if (item->bundle.toolchains().size() == newToolchains.size()) { + qDeleteAll(newToolchains); + } else { + toAdd << newToolchains; + markForRemoval(item); + } + } + + // Step 4: Create new bundles and add items for them. + const QList newBundles = ToolchainBundle::collectBundles(toAdd); + for (const ToolchainBundle &bundle : newBundles) + m_toAddList << insertBundle(bundle, true); } void ToolChainOptionsWidget::toolChainSelectionChanged() @@ -459,8 +534,9 @@ void ToolChainOptionsWidget::toolChainSelectionChanged() void ToolChainOptionsWidget::apply() { // Remove unused tool chains: - ToolchainManager::deregisterToolchains( - Utils::transform(m_toRemoveList, &ToolChainTreeItem::toolChain)); + const AddRemoveList toRemove = m_toRemoveList; + for (const ToolChainTreeItem * const item : toRemove) + ToolchainManager::deregisterToolchains(item->bundle.toolchains()); Q_ASSERT(m_toRemoveList.isEmpty()); @@ -469,8 +545,7 @@ void ToolChainOptionsWidget::apply() for (StaticTreeItem *parent : {autoAndManual.first, autoAndManual.second}) { for (TreeItem *item : *parent) { auto tcItem = static_cast(item); - Q_ASSERT(tcItem->toolChain); - if (!tcItem->toolChain->isAutoDetected() && tcItem->widget() && tcItem->changed) + if (!tcItem->bundle.isAutoDetected() && tcItem->widget() && tcItem->changed) tcItem->widget()->apply(); tcItem->changed = false; tcItem->update(); @@ -478,16 +553,18 @@ void ToolChainOptionsWidget::apply() } } - // Add new (and already updated) tool chains - const Toolchains notRegistered = ToolchainManager::registerToolchains( - Utils::transform(m_toAddList, &ToolChainTreeItem::toolChain)); - const QStringList removedTcs = Utils::transform(notRegistered, &Toolchain::displayName); - - const QList toAddList = m_toAddList; - for (ToolChainTreeItem *n : toAddList) - markForRemoval(n); - - qDeleteAll(m_toAddList); + // Add new (and already updated) toolchains + QStringList removedTcs; + const AddRemoveList toAdd = m_toAddList; + for (ToolChainTreeItem * const item : toAdd) { + const Toolchains notRegistered = ToolchainManager::registerToolchains(item->bundle.toolchains()); + removedTcs << Utils::transform(notRegistered, &Toolchain::displayName); + } + for (ToolChainTreeItem * const item : std::as_const(m_toAddList)) { + item->bundle.deleteToolchains(); + delete item; + } + m_toAddList.clear(); if (removedTcs.count() == 1) { QMessageBox::warning(Core::ICore::dialogParent(), @@ -508,41 +585,41 @@ void ToolChainOptionsWidget::apply() ToolchainManager::setDetectionSettings(m_detectionSettings); } -void ToolChainOptionsWidget::createToolChain(ToolchainFactory *factory, const Utils::Id &language) +void ToolChainOptionsWidget::createToolchains(ToolchainFactory *factory, const QList &languages) { QTC_ASSERT(factory, return); QTC_ASSERT(factory->canCreate(), return); - QTC_ASSERT(language.isValid(), return); - Toolchain *tc = factory->create(); - if (!tc) - return; + const Id bundleId = Id::generate(); + Toolchains toolchains; + for (const Id lang : languages) { + Toolchain *tc = factory->create(); + QTC_ASSERT(tc, return); - tc->setDetection(Toolchain::ManualDetection); - tc->setLanguage(language); - - auto item = insertToolChain(tc, true); - m_toAddList.append(item); + tc->setDetection(Toolchain::ManualDetection); + tc->setLanguage(lang); + tc->setBundleId(bundleId); + toolchains << tc; + } + const ToolchainBundle bundle(toolchains); + ToolChainTreeItem * const item = insertBundle(bundle, true); + m_toAddList << item; m_toolChainView->setCurrentIndex(m_sortModel.mapFromSource(m_model.indexForItem(item))); } -void ToolChainOptionsWidget::cloneToolChain() +void ToolChainOptionsWidget::cloneToolchains() { ToolChainTreeItem *current = currentTreeItem(); if (!current) return; - Toolchain *tc = current->toolChain->clone(); - if (!tc) - return; - - tc->setDetection(Toolchain::ManualDetection); - tc->setDisplayName(Tr::tr("Clone of %1").arg(current->toolChain->displayName())); - - auto item = insertToolChain(tc, true); - m_toAddList.append(item); + ToolchainBundle bundle = current->bundle.clone(); + bundle.setDetection(Toolchain::ManualDetection); + bundle.setDisplayName(Tr::tr("Clone of %1").arg(current->bundle.displayName())); + ToolChainTreeItem * const item = insertBundle(bundle, true); + m_toAddList << item; m_toolChainView->setCurrentIndex(m_sortModel.mapFromSource(m_model.indexForItem(item))); } @@ -551,9 +628,8 @@ void ToolChainOptionsWidget::updateState() bool canCopy = false; bool canDelete = false; if (ToolChainTreeItem *item = currentTreeItem()) { - Toolchain *tc = item->toolChain; - canCopy = tc->isValid(); - canDelete = !tc->isSdkProvided(); + canCopy = item->bundle.validity() != ToolchainBundle::Valid::None; + canDelete = !item->bundle.isSdkProvided(); } m_cloneButton->setEnabled(canCopy); diff --git a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp index d40a3b019fb..a12d747627c 100644 --- a/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp +++ b/src/plugins/projectexplorer/toolchainsettingsaccessor.cpp @@ -326,12 +326,12 @@ public: void addToEnvironment(Environment &env) const override { Q_UNUSED(env) } FilePath makeCommand(const Environment &) const override { return "make"; } QList createOutputParsers() const override { return {}; } - std::unique_ptr createConfigurationWidget() override { return nullptr; } bool operator ==(const Toolchain &other) const override { if (!Toolchain::operator==(other)) return false; return static_cast(&other)->token == token; } + bool canShareBundleImpl(const Toolchain &) const override { return false; } void fromMap(const Store &data) final { @@ -370,6 +370,11 @@ void ProjectExplorerTest::testToolChainMerging_data() setSupportedToolchainType(TestToolChainType); setToolchainConstructor([] { return new TTC; }); } + std::unique_ptr createConfigurationWidget( + const ToolchainBundle &) const override + { + return nullptr; + } }; TestToolchainFactory factory; diff --git a/src/plugins/qnx/qnxtoolchain.cpp b/src/plugins/qnx/qnxtoolchain.cpp index 6978a16611c..30feae7a2c7 100644 --- a/src/plugins/qnx/qnxtoolchain.cpp +++ b/src/plugins/qnx/qnxtoolchain.cpp @@ -27,7 +27,7 @@ namespace Qnx::Internal { class QnxToolchainConfigWidget : public ToolchainConfigWidget { public: - QnxToolchainConfigWidget(QnxToolchain *tc); + QnxToolchainConfigWidget(const ToolchainBundle &bundle); private: void applyImpl() override; @@ -37,7 +37,6 @@ private: void handleSdpPathChange(); - PathChooser *m_compilerCommand; PathChooser *m_sdpPath; ProjectExplorer::AbiWidget *m_abiWidget; }; @@ -116,11 +115,6 @@ QnxToolchain::QnxToolchain() }); } -std::unique_ptr QnxToolchain::createConfigurationWidget() -{ - return std::make_unique(this); -} - void QnxToolchain::addToEnvironment(Environment &env) const { if (env.expandedValueForKey("QNX_HOST").isEmpty() || @@ -166,32 +160,24 @@ bool QnxToolchain::operator ==(const Toolchain &other) const // QnxToolChainConfigWidget //--------------------------------------------------------------------------------- -QnxToolchainConfigWidget::QnxToolchainConfigWidget(QnxToolchain *tc) - : ToolchainConfigWidget(tc) - , m_compilerCommand(new PathChooser) +QnxToolchainConfigWidget::QnxToolchainConfigWidget(const ToolchainBundle &bundle) + : ToolchainConfigWidget(bundle) , m_sdpPath(new PathChooser) , m_abiWidget(new AbiWidget) { - m_compilerCommand->setExpectedKind(PathChooser::ExistingCommand); - m_compilerCommand->setHistoryCompleter("Qnx.ToolChain.History"); - m_compilerCommand->setFilePath(tc->compilerCommand()); - m_compilerCommand->setEnabled(!tc->isAutoDetected()); - m_sdpPath->setExpectedKind(PathChooser::ExistingDirectory); m_sdpPath->setHistoryCompleter("Qnx.Sdp.History"); - m_sdpPath->setFilePath(tc->sdpPath()); - m_sdpPath->setEnabled(!tc->isAutoDetected()); + m_sdpPath->setFilePath(bundle.get(&QnxToolchain::sdpPath)()); + m_sdpPath->setEnabled(!bundle.isAutoDetected()); const Abis abiList = detectTargetAbis(m_sdpPath->filePath()); - m_abiWidget->setAbis(abiList, tc->targetAbi()); - m_abiWidget->setEnabled(!tc->isAutoDetected() && !abiList.isEmpty()); + m_abiWidget->setAbis(abiList, bundle.targetAbi()); + m_abiWidget->setEnabled(!bundle.isAutoDetected() && !abiList.isEmpty()); - m_mainLayout->addRow(Tr::tr("&Compiler path:"), m_compilerCommand); //: SDP refers to 'Software Development Platform'. m_mainLayout->addRow(Tr::tr("SDP path:"), m_sdpPath); m_mainLayout->addRow(Tr::tr("&ABI:"), m_abiWidget); - connect(m_compilerCommand, &PathChooser::rawPathChanged, this, &ToolchainConfigWidget::dirty); connect(m_sdpPath, &PathChooser::rawPathChanged, this, &QnxToolchainConfigWidget::handleSdpPathChange); connect(m_abiWidget, &AbiWidget::abiChanged, this, &ToolchainConfigWidget::dirty); @@ -199,37 +185,30 @@ QnxToolchainConfigWidget::QnxToolchainConfigWidget(QnxToolchain *tc) void QnxToolchainConfigWidget::applyImpl() { - if (toolchain()->isAutoDetected()) + if (bundle().isAutoDetected()) return; - auto tc = static_cast(toolchain()); - Q_ASSERT(tc); - QString displayName = tc->displayName(); - tc->setDisplayName(displayName); // reset display name - tc->sdpPath.setValue(m_sdpPath->filePath()); - tc->setTargetAbi(m_abiWidget->currentAbi()); - tc->resetToolchain(m_compilerCommand->filePath()); + bundle().setTargetAbi(m_abiWidget->currentAbi()); + bundle().forEach([this](QnxToolchain &tc) { + tc.sdpPath.setValue(m_sdpPath->filePath()); + tc.resetToolchain(compilerCommand(tc.language())); + }); } void QnxToolchainConfigWidget::discardImpl() { // subwidgets are not yet connected! QSignalBlocker blocker(this); - auto tc = static_cast(toolchain()); - m_compilerCommand->setFilePath(tc->compilerCommand()); - m_sdpPath->setFilePath(tc->sdpPath()); - m_abiWidget->setAbis(tc->supportedAbis(), tc->targetAbi()); - if (!m_compilerCommand->filePath().toString().isEmpty()) + m_sdpPath->setFilePath(bundle().get(&QnxToolchain::sdpPath)()); + m_abiWidget->setAbis(bundle().supportedAbis(), bundle().targetAbi()); + if (hasAnyCompiler()) m_abiWidget->setEnabled(true); } bool QnxToolchainConfigWidget::isDirtyImpl() const { - auto tc = static_cast(toolchain()); - Q_ASSERT(tc); - return m_compilerCommand->filePath() != tc->compilerCommand() - || m_sdpPath->filePath() != tc->sdpPath() - || m_abiWidget->currentAbi() != tc->targetAbi(); + return m_sdpPath->filePath() != bundle().get(&QnxToolchain::sdpPath)() + || m_abiWidget->currentAbi() != bundle().targetAbi(); } void QnxToolchainConfigWidget::handleSdpPathChange() @@ -275,6 +254,12 @@ public: Toolchains tcs = autoDetectHelper(detector.alreadyKnown); return tcs; } + + std::unique_ptr createConfigurationWidget( + const ToolchainBundle &bundle) const override + { + return std::make_unique(bundle); + } }; void setupQnxToolchain() diff --git a/src/plugins/qnx/qnxtoolchain.h b/src/plugins/qnx/qnxtoolchain.h index 47a9a706439..da50e108da0 100644 --- a/src/plugins/qnx/qnxtoolchain.h +++ b/src/plugins/qnx/qnxtoolchain.h @@ -12,8 +12,6 @@ class QnxToolchain : public ProjectExplorer::GccToolchain public: QnxToolchain(); - std::unique_ptr createConfigurationWidget() override; - void addToEnvironment(Utils::Environment &env) const override; QStringList suggestedMkspecList() const override; diff --git a/src/plugins/webassembly/webassemblytoolchain.cpp b/src/plugins/webassembly/webassemblytoolchain.cpp index 99e8309d4a4..90c43c60d0a 100644 --- a/src/plugins/webassembly/webassemblytoolchain.cpp +++ b/src/plugins/webassembly/webassemblytoolchain.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -183,10 +184,16 @@ public: setUserCreatable(true); } - Toolchains autoDetect(const ToolchainDetector &detector) const + Toolchains autoDetect(const ToolchainDetector &detector) const override { return doAutoDetect(detector); } + + std::unique_ptr createConfigurationWidget( + const ToolchainBundle &bundle) const override + { + return GccToolchain::createConfigurationWidget(bundle); + } }; void setupWebAssemblyToolchain()