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 <christian.stenger@qt.io>
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Christian Kandeler
2024-07-11 13:03:12 +02:00
parent 08d6b86162
commit 020883c47f
27 changed files with 1277 additions and 666 deletions

View File

@@ -10,6 +10,7 @@
#include <projectexplorer/toolchainmanager.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/toolchainconfigwidget.h>
#include <utils/environment.h>
@@ -246,6 +247,18 @@ public:
ProjectExplorer::Constants::CXX_LANGUAGE_ID});
setToolchainConstructor([] { return new AndroidToolchain; });
}
private:
std::unique_ptr<ToolchainConfigWidget> 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()

View File

@@ -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<OutputLineParser *> createOutputParsers() const final { return {new IarParser()}; }
std::unique_ptr<ToolchainConfigWidget> 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<ToolchainConfigWidget> IarToolchain::createConfigurationWidget()
{
return std::make_unique<IarToolchainConfigWidget>(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<ToolchainConfigWidget> 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<ToolchainConfigWidget> IarToolchainFactory::createConfigurationWidget(
const ToolchainBundle &bundle) const
{
return std::make_unique<IarToolchainConfigWidget>(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<IarToolchain *>(toolchain());
const QString displayName = tc->displayName();
tc->setCompilerCommand(m_compilerCommand->filePath());
bundle().forEach<IarToolchain>([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<IarToolchain>([this](IarToolchain &tc) {
const Macros &macros = 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<IarToolchain *>(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<IarToolchain *>(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 &macros = 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

View File

@@ -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<OutputLineParser *> createOutputParsers() const final { return {new KeilParser}; }
std::unique_ptr<ToolchainConfigWidget> 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<ToolchainConfigWidget> KeilToolchain::createConfigurationWidget()
{
return std::make_unique<KeilToolchainConfigWidget>(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<ToolchainConfigWidget> createConfigurationWidget(
const ToolchainBundle &bundle) const final
{
return std::make_unique<KeilToolchainConfigWidget>(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<KeilToolchain *>(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<KeilToolchain>([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<KeilToolchain>([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<KeilToolchain *>(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<KeilToolchain *>(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

View File

@@ -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<OutputLineParser *> createOutputParsers() const final { return {new SdccParser}; }
std::unique_ptr<ToolchainConfigWidget> 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<ToolchainConfigWidget> SdccToolchain::createConfigurationWidget()
{
return std::make_unique<SdccToolchainConfigWidget>(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<ToolchainConfigWidget> createConfigurationWidget(
const ToolchainBundle &bundle) const final
{
return std::make_unique<SdccToolchainConfigWidget>(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<SdccToolchain *>(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<SdccToolchain>([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<SdccToolchain *>(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<SdccToolchain *>(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();

View File

@@ -344,10 +344,7 @@ private:
void addToEnvironment(Utils::Environment &) const override {}
Utils::FilePath makeCommand(const Utils::Environment &) const override { return {}; }
QList<Utils::OutputLineParser *> createOutputParsers() const override { return {}; }
std::unique_ptr<ToolchainConfigWidget> createConfigurationWidget() override
{
return {};
};
bool canShareBundleImpl(const Toolchain &) const override { return false; }
};
class ProjectInfoGeneratorTestHelper

View File

@@ -20,6 +20,7 @@
#include <projectexplorer/toolchain.h>
#include <projectexplorer/gcctoolchain.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/toolchainconfigwidget.h>
#include <debugger/debuggeritemmanager.h>
#include <debugger/debuggeritem.h>
@@ -555,6 +556,12 @@ public:
}
Toolchains autoDetect(const ToolchainDetector &detector) const final;
std::unique_ptr<ToolchainConfigWidget> createConfigurationWidget(
const ToolchainBundle &bundle) const override
{
return GccToolchain::createConfigurationWidget(bundle);
}
};
Toolchains IosToolchainFactory::autoDetect(const ToolchainDetector &detector) const

View File

@@ -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)};

View File

@@ -123,16 +123,12 @@ bool NimToolchain::parseVersion(const FilePath &path, std::tuple<int, int, int>
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<NimToolchain *>(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<NimToolchain *>(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<NimToolchain *>(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<NimToolchain *>(toolchain());
Q_ASSERT(tc);
m_compilerCommand->setFilePath(tc->compilerCommand());
m_compilerVersion->setText(tc->compilerVersion());
}
std::unique_ptr<ToolchainConfigWidget> NimToolchain::createConfigurationWidget()
{
return std::make_unique<NimToolchainConfigWidget>(this);
m_compilerVersion->setText(bundle().get(&NimToolchain::compilerVersion));
}
// NimToolchainFactory
@@ -247,4 +216,10 @@ Toolchains NimToolchainFactory::detectForImport(const ToolchainDescription &tcd)
return result;
}
std::unique_ptr<ToolchainConfigWidget> NimToolchainFactory::createConfigurationWidget(
const ProjectExplorer::ToolchainBundle &bundle) const
{
return std::make_unique<NimToolchainConfigWidget>(bundle);
}
} // Nim

View File

@@ -24,7 +24,6 @@ public:
Utils::FilePath makeCommand(const Utils::Environment &env) const final;
QString compilerVersion() const;
QList<Utils::OutputLineParser *> createOutputParsers() const final;
std::unique_ptr<ProjectExplorer::ToolchainConfigWidget> 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<ProjectExplorer::ToolchainConfigWidget> createConfigurationWidget(
const ProjectExplorer::ToolchainBundle &bundle) const final;
};
} // Nim

View File

@@ -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<ToolchainConfigWidget> 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<CustomToolchain::Parser> 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<CustomToolchain *>(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<QVector>(
bundle().setTargetAbi(m_abiWidget->currentAbi());
const Macros macros = Utils::transform<QVector>(
m_predefinedDetails->text().split('\n', Qt::SkipEmptyParts),
[](const QString &m) {
return Macro::fromKeyValue(m);
});
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()));
bundle().forEach<CustomToolchain>([&](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()));
});
setFromToolchain(); // Refresh with actual data from the toolchain. This shows what e.g. the
// 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<CustomToolchain *>(toolchain());
m_compilerCommand->setFilePath(tc->compilerCommand());
m_makeCommand->setFilePath(tc->makeCommand(Environment()));
m_abiWidget->setAbis(Abis(), tc->targetAbi());
const QStringList macroLines = Utils::transform<QList>(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<QList>(
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<CustomToolchain *>(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<ToolchainConfigWidget> CustomToolchain::createConfigurationWidget()
{
return std::make_unique<CustomToolchainConfigWidget>(this);
}
// CustomToolchainFactory
class CustomToolchainFactory final : public ToolchainFactory
@@ -568,6 +551,27 @@ public:
setToolchainConstructor([] { return new CustomToolchain; });
setUserCreatable(true);
}
private:
std::unique_ptr<ToolchainConfigWidget> createConfigurationWidget(
const ToolchainBundle &bundle) const override
{
return std::make_unique<CustomToolchainConfigWidget>(bundle);
}
FilePath correspondingCompilerCommand(const FilePath &srcPath, Id targetLang) const override
{
static const std::pair<QString, QString> 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()

View File

@@ -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<ToolchainConfigWidget> GccToolchain::createConfigurationWidget(
const ToolchainBundle &bundle)
{
return std::make_unique<GccToolchainConfigWidget>(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<ToolchainConfigWidget> GccToolchain::createConfigurationWidget()
{
return std::make_unique<GccToolchainConfigWidget>(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<ToolchainConfigWidget> 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<ToolchainConfigWidget> 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<GccToolchain *>(toolchain());
Q_ASSERT(tc);
QString displayName = tc->displayName();
tc->setCompilerCommand(m_compilerCommand->filePath());
const Id parentBundleId = m_parentToolchainCombo
? Id::fromSetting(m_parentToolchainCombo->currentData())
: Id();
bundle().forEach<GccToolchain>([&](GccToolchain &tc) {
tc.setCompilerCommand(compilerCommand(tc.language()));
if (m_abiWidget) {
tc->setSupportedAbis(m_abiWidget->supportedAbis());
tc->setTargetAbi(m_abiWidget->currentAbi());
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()));
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<GccToolchain *>(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<GccToolchain *>(toolchain());
if (m_compilerCommand->filePath() != tc->compilerCommand()
|| m_platformCodeGenFlagsLineEdit->text()
!= ProcessArgs::joinArgs(tc->platformCodeGenFlags())
|| m_platformLinkerFlagsLineEdit->text()
!= ProcessArgs::joinArgs(tc->platformLinkerFlags())
if (m_platformCodeGenFlagsLineEdit->text() != ProcessArgs::joinArgs(bundle().get(&GccToolchain::platformCodeGenFlags))
|| m_platformLinkerFlagsLineEdit->text() != ProcessArgs::joinArgs(bundle().get(&GccToolchain::platformLinkerFlags))
|| m_targetTripleWidget->explicitCodeModelTargetTriple()
!= tc->explicitCodeModelTargetTriple()
|| (m_abiWidget && m_abiWidget->currentAbi() != tc->targetAbi())) {
!= 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,32 +2078,56 @@ QString GccToolchain::sysRoot() const
return {};
}
bool GccToolchain::canShareBundleImpl(const Toolchain &other) const
{
return platformLinkerFlags() == static_cast<const GccToolchain &>(other).platformLinkerFlags();
}
void GccToolchainConfigWidget::updateParentToolchainComboBox()
{
QTC_ASSERT(m_parentToolchainCombo, return);
auto *tc = static_cast<GccToolchain *>(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<ToolchainBundle> 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

View File

@@ -10,7 +10,6 @@
#include "headerpath.h"
#include <functional>
#include <memory>
#include <optional>
namespace ProjectExplorer {
@@ -34,6 +33,9 @@ public:
GccToolchain(Utils::Id typeId, SubType subType = RealGcc);
~GccToolchain() override;
static std::unique_ptr<ToolchainConfigWidget> 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<ToolchainConfigWidget> 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<QStringList, Macros>;
using GccCache = QVector<CacheItem>;
void setSupportedAbis(const Abis &abis);
void setInstallDir(const Utils::FilePath &installDir);
void setMacroCache(const QStringList &allCxxflags, const Macros &macroCache) 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;

View File

@@ -205,25 +205,28 @@ public:
layout->setContentsMargins(0, 0, 0, 0);
layout->setColumnStretch(1, 2);
const QList<Id> languageList = sorted(ToolchainManager::allLanguages(), [](Id l1, Id l2) {
return ToolchainManager::displayNameOfLanguageId(l1)
< ToolchainManager::displayNameOfLanguageId(l2);
const QList<LanguageCategory> languageCategories = sorted(
ToolchainManager::languageCategories(),
[](const LanguageCategory &l1, const LanguageCategory &l2) {
return ToolchainManager::displayNameOfLanguageCategory(l1)
< ToolchainManager::displayNameOfLanguageCategory(l2);
});
QTC_ASSERT(!languageList.isEmpty(), return);
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<ToolchainBundle> sameBundles = ToolchainBundle::collectBundles(same);
const QList<ToolchainBundle> 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<ToolchainBundle> &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);
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, language);
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<Id, QComboBox *> m_languageComboboxMap;
QHash<LanguageCategory, QComboBox *> m_languageComboboxMap;
Guard m_ignoreChanges;
bool m_isReadOnly = false;
};

View File

@@ -788,6 +788,12 @@ void MsvcToolchain::initEnvModWatcher(const QFuture<GenerateEnvResult> &future)
m_envModWatcher.setFuture(future);
}
bool MsvcToolchain::canShareBundleImpl(const Toolchain &other) const
{
const auto &otherMsvc = static_cast<const MsvcToolchain &>(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<const MsvcToolchain &>(*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<const MsvcToolchain *>(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<MsvcToolchain *>(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<MsvcToolchain *>(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<const MsvcToolchain *>(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<const MsvcToolchain *>(toolchain());
QTC_ASSERT(currentTc, return );
const MsvcToolchain::Platform platform = m_varsBatArchCombo->currentData().value<MsvcToolchain::Platform>();
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<ToolchainConfigWidget> MsvcToolchain::createConfigurationWidget()
{
return std::make_unique<MsvcToolchainConfigWidget>(this);
}
// --------------------------------------------------------------------------
// ClangClToolChainConfigWidget
// --------------------------------------------------------------------------
@@ -1499,8 +1498,8 @@ std::unique_ptr<ToolchainConfigWidget> 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<const MsvcToolchain *>(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<const ClangClToolchain *>(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<ClangClToolchain *>(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<ToolchainBundle> 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<const MsvcToolchain *>(toolchain);
clangClToolchain->setupVarsBat(mstc->targetAbi(), mstc->varsBat(), mstc->varsBatArg());
break;
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<ToolchainConfigWidget> ClangClToolchain::createConfigurationWidget()
{
return std::make_unique<ClangClToolchainConfigWidget>(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<const ClangClToolchain &>(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<ToolchainConfigWidget> createConfigurationWidget(
const ToolchainBundle &bundle) const override
{
return std::make_unique<MsvcToolchainConfigWidget>(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<ToolchainConfigWidget> createConfigurationWidget(
const ToolchainBundle &bundle) const override
{
return std::make_unique<ClangClToolchainConfigWidget>(bundle);
}
bool canCreate() const final { return !g_availableMsvcToolchains.isEmpty(); }
};

View File

@@ -39,7 +39,6 @@ public:
void toMap(Utils::Store &data) const override;
void fromMap(const Utils::Store &data) override;
std::unique_ptr<ToolchainConfigWidget> 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 &macros) 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<Utils::OutputLineParser *> createOutputParsers() const override;
void toMap(Utils::Store &data) const override;
void fromMap(const Utils::Store &data) override;
std::unique_ptr<ToolchainConfigWidget> 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;
};

View File

@@ -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; });

View File

@@ -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 <utils/async.h>
#include <utils/pathchooser.h>
#include <utils/qtcassert.h>
#include <QHash>
#include <QUuid>
#include <utility>
@@ -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<Id> ToolchainFactory::supportedLanguages() const
return m_supportedLanguages;
}
LanguageCategory ToolchainFactory::languageCategory() const
{
const QList<Id> 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> ToolchainBundle::collectBundles()
{
return collectBundles(ToolchainManager::toolchains());
}
QList<ToolchainBundle> ToolchainBundle::collectBundles(const Toolchains &toolchains)
{
QHash<Id, Toolchains> toolchainsPerBundleId;
for (Toolchain * const tc : toolchains)
toolchainsPerBundleId[tc->bundleId()] << tc;
QList<ToolchainBundle> 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<GccToolchain *>(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> ToolchainBundle::bundleUnbundledToolchains(const Toolchains &unbundled)
{
QList<ToolchainBundle> bundles;
QHash<Id, QHash<Id, Toolchains>> unbundledByTypeAndLanguage;
for (Toolchain * const tc : unbundled)
unbundledByTypeAndLanguage[tc->typeId()][tc->language()] << tc;
for (const auto &tcsByLang : std::as_const(unbundledByTypeAndLanguage)) {
QList<Toolchains> 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<Id> 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

View File

@@ -18,9 +18,11 @@
#include <utils/store.h>
#include <QDateTime>
#include <QSet>
#include <functional>
#include <memory>
#include <optional>
namespace Utils { class OutputLineParser; }
@@ -54,6 +56,8 @@ public:
Utils::Id language;
};
using LanguageCategory = QSet<Utils::Id>;
// --------------------------------------------------------------------------
// 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<Utils::OutputLineParser *> createOutputParsers() const = 0;
virtual bool operator ==(const Toolchain &) const;
virtual std::unique_ptr<ToolchainConfigWidget> 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<Internal::ToolchainPrivate> d;
friend class Internal::ToolchainSettingsAccessor;
@@ -204,6 +215,98 @@ private:
using Toolchains = QList<Toolchain *>;
class PROJECTEXPLORER_EXPORT ToolchainBundle
{
public:
ToolchainBundle(const Toolchains &toolchains);
static QList<ToolchainBundle> collectBundles();
static QList<ToolchainBundle> collectBundles(const Toolchains &toolchains);
template<typename R, class T = Toolchain, typename... A>
R get(R (T:: *getter)(A...) const, A&&... args) const
{
return std::invoke(getter, static_cast<T &>(*m_toolchains.first()), std::forward<A>(args)...);
}
template<class T = Toolchain, typename R> R& get(R T::*member) const
{
return static_cast<T &>(*m_toolchains.first()).*member;
}
template<class T = Toolchain, typename ...A> void set(void (T::*setter)(const A&...), const A& ...args)
{
for (Toolchain * const tc : std::as_const(m_toolchains))
std::invoke(setter, static_cast<T &>(*tc), args...);
}
template<class T = Toolchain, typename ...A> void set(void (T::*setter)(A...), const A ...args)
{
for (Toolchain * const tc : std::as_const(m_toolchains))
std::invoke(setter, static_cast<T &>(*tc), args...);
}
template<typename T> void forEach(const std::function<void(T &toolchain)> &modifier)
{
for (Toolchain * const tc : std::as_const(m_toolchains))
modifier(static_cast<T &>(*tc));
}
template<typename T> void forEach(const std::function<void(const T &toolchain)> &func) const
{
for (const Toolchain * const tc : m_toolchains)
func(static_cast<const T &>(*tc));
}
int size() const { return m_toolchains.size(); }
const QList<Toolchain *> toolchains() const { return m_toolchains; }
const QList<Toolchain *> 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<Abi> 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<ToolchainBundle> 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<ToolchainConfigWidget> 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<Utils::Id> supportedLanguages() const;
LanguageCategory languageCategory() const;
void setUserCreatable(bool userCreatable);

View File

@@ -4,13 +4,17 @@
#include "toolchainconfigwidget.h"
#include "toolchain.h"
#include "projectexplorerconstants.h"
#include "projectexplorertr.h"
#include "toolchainmanager.h"
#include <utils/detailswidget.h>
#include <utils/qtcprocess.h>
#include <utils/qtcassert.h>
#include <QCheckBox>
#include <QFormLayout>
#include <QHBoxLayout>
#include <QLineEdit>
#include <QLabel>
#include <QScrollArea>
@@ -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();
for (const auto &[tc, pathChooser] : std::as_const(m_commands)) {
if (pathChooser->filePath() != bundle().compilerCommand(tc->language()))
return true;
}
Toolchain *ToolchainConfigWidget::toolchain() const
{
return m_toolChain;
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<Toolchain>([&](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

View File

@@ -5,9 +5,16 @@
#include "projectexplorer_export.h"
#include "toolchain.h"
#include <QScrollArea>
#include <utility>
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<const Toolchain *, Utils::PathChooser *>;
ToolchainChooser compilerPathChooser(Utils::Id language);
void setupCompilerPathChoosers();
ToolchainBundle m_bundle;
QLineEdit *m_nameLineEdit = nullptr;
QLabel *m_errorLabel = nullptr;
QCheckBox *m_deriveCxxCompilerCheckBox = nullptr;
QList<ToolchainChooser> m_commands;
};
} // namespace ProjectExplorer

View File

@@ -43,6 +43,7 @@ public:
Toolchains m_toolChains; // prioritized List
BadToolchains m_badToolchains; // to be skipped when auto-detecting
QVector<LanguageDisplayPair> m_languages;
QList<std::pair<LanguageCategory, QString>> 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<LanguageCategory> ToolchainManager::languageCategories()
{
QList<LanguageCategory> categories
= Utils::transform<QList<LanguageCategory>>(d->m_languageCategories, [](const auto &e) {
return e.first;
});
const QList<Utils::Id> 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));

View File

@@ -12,8 +12,6 @@
#include <QSet>
#include <QString>
#include <functional>
namespace Utils { class FilePath; }
namespace ProjectExplorer {
@@ -53,7 +51,11 @@ public:
static QList<Utils::Id> 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<LanguageCategory> languageCategories();
static bool isLanguageSupported(const Utils::Id &id);
static void aboutToShutdown();

View File

@@ -17,6 +17,7 @@
#include <utils/algorithm.h>
#include <utils/detailswidget.h>
#include <utils/guard.h>
#include <utils/qtcassert.h>
#include <utils/treemodel.h>
#include <utils/utilsicons.h>
@@ -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("<nobr><b>ABI:</b> %1").arg(
changed ? Tr::tr("not up-to-date")
: toolChain->targetAbi().toString());
: bundle.targetAbi().toString());
if (validity == ToolchainBundle::Valid::Some)
toolTip.append("<br/>").append(
Tr::tr("Not all compilers are set up correctly."));
} else {
toolTip = Tr::tr("This toolchain is invalid.");
}
return QVariant("<div style=\"white-space:pre\">" + toolTip + "</div>");
}
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<Utils::Id> 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<ToolChainTreeItem *>(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<ToolchainBundle> 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<Id> &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<Id> &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<Utils::Id, QPair<StaticTreeItem *, StaticTreeItem *>> m_languageMap;
QHash<LanguageCategory, QPair<StaticTreeItem *, StaticTreeItem *>> m_languageMap;
QList<ToolChainTreeItem *> m_toAddList;
QList<ToolChainTreeItem *> m_toRemoveList;
using AddRemoveList = QList<ToolChainTreeItem *>;
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);
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;
}
insertToolChain(tc);
const QList<ToolchainBundle> 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(
if (m_deregisterGuard.isLocked())
return;
GuardLocker locker(m_deregisterGuard);
if (const auto it = std::find_if(
m_toRemoveList.begin(),
m_toRemoveList.end(),
[tc](const ToolChainTreeItem *item) { return item->toolChain == tc; });
[&toolchains](const ToolChainTreeItem *item) {
return item->bundle.toolchains() == toolchains;
});
it != m_toRemoveList.end()) {
ToolChainTreeItem * const item = *it;
m_toRemoveList.erase(it);
delete *it;
continue;
delete item;
return;
}
StaticTreeItem *parent = parentForToolChain(tc);
auto item = parent->findChildAtLevel(1, [tc](TreeItem *item) {
return static_cast<ToolChainTreeItem *>(item)->toolChain == tc;
});
QList<ToolChainTreeItem *> affectedItems;
for (Toolchain * const tc : toolchains) {
StaticTreeItem *parent = parentForToolchain(*tc);
auto item = static_cast<ToolChainTreeItem *>(
parent->findChildAtLevel(1, [tc](TreeItem *item) {
return static_cast<ToolChainTreeItem *>(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<StaticTreeItem *, StaticTreeItem *> nodes = m_languageMap.value(tc->language());
return tc->isAutoDetected() ? nodes.first : nodes.second;
QPair<StaticTreeItem *, StaticTreeItem *> 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<ToolChainTreeItem *> itemsToRemove;
// The second element is the set of toolchains for the respective bundle that were re-discovered.
using ItemToCheck = std::pair<ToolChainTreeItem *, Toolchains>;
QList<ItemToCheck> 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<ToolChainTreeItem *>(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<Toolchain *> 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<ToolchainBundle> 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<ToolChainTreeItem *>(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();
@@ -479,15 +554,17 @@ void ToolChainOptionsWidget::apply()
}
// Add new (and already updated) toolchains
const Toolchains notRegistered = ToolchainManager::registerToolchains(
Utils::transform(m_toAddList, &ToolChainTreeItem::toolChain));
const QStringList removedTcs = Utils::transform(notRegistered, &Toolchain::displayName);
const QList<ToolChainTreeItem *> toAddList = m_toAddList;
for (ToolChainTreeItem *n : toAddList)
markForRemoval(n);
qDeleteAll(m_toAddList);
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<Id> &languages)
{
QTC_ASSERT(factory, return);
QTC_ASSERT(factory->canCreate(), return);
QTC_ASSERT(language.isValid(), return);
const Id bundleId = Id::generate();
Toolchains toolchains;
for (const Id lang : languages) {
Toolchain *tc = factory->create();
if (!tc)
return;
QTC_ASSERT(tc, return);
tc->setDetection(Toolchain::ManualDetection);
tc->setLanguage(language);
auto item = insertToolChain(tc, true);
m_toAddList.append(item);
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);

View File

@@ -326,12 +326,12 @@ public:
void addToEnvironment(Environment &env) const override { Q_UNUSED(env) }
FilePath makeCommand(const Environment &) const override { return "make"; }
QList<OutputLineParser *> createOutputParsers() const override { return {}; }
std::unique_ptr<ToolchainConfigWidget> createConfigurationWidget() override { return nullptr; }
bool operator ==(const Toolchain &other) const override {
if (!Toolchain::operator==(other))
return false;
return static_cast<const TTC *>(&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<ToolchainConfigWidget> createConfigurationWidget(
const ToolchainBundle &) const override
{
return nullptr;
}
};
TestToolchainFactory factory;

View File

@@ -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<ToolchainConfigWidget> QnxToolchain::createConfigurationWidget()
{
return std::make_unique<QnxToolchainConfigWidget>(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>(&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<QnxToolchain *>(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<QnxToolchain>([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<const QnxToolchain *>(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<const QnxToolchain *>(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<ProjectExplorer::ToolchainConfigWidget> createConfigurationWidget(
const ToolchainBundle &bundle) const override
{
return std::make_unique<QnxToolchainConfigWidget>(bundle);
}
};
void setupQnxToolchain()

View File

@@ -12,8 +12,6 @@ class QnxToolchain : public ProjectExplorer::GccToolchain
public:
QnxToolchain();
std::unique_ptr<ProjectExplorer::ToolchainConfigWidget> createConfigurationWidget() override;
void addToEnvironment(Utils::Environment &env) const override;
QStringList suggestedMkspecList() const override;

View File

@@ -13,6 +13,7 @@
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectmacro.h>
#include <projectexplorer/toolchainconfigwidget.h>
#include <projectexplorer/toolchainmanager.h>
#include <qtsupport/qtkitaspect.h>
@@ -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<ToolchainConfigWidget> createConfigurationWidget(
const ToolchainBundle &bundle) const override
{
return GccToolchain::createConfigurationWidget(bundle);
}
};
void setupWebAssemblyToolchain()