CMake: Improve generator selection in kits

Allow to select generator and extragenerator in a nicer way.
Enable support for platforms and toolsets.

Change-Id: I0c9aae635cdca0ec1db7f285e9cb2785172ed338
Reviewed-by: Tim Jenssen <tim.jenssen@qt.io>
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
Tobias Hunger
2016-09-28 12:24:06 +02:00
parent 4bce0d7c36
commit b3b6cfb5ef
6 changed files with 250 additions and 89 deletions

View File

@@ -43,7 +43,9 @@
#include <QDialog> #include <QDialog>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QLabel> #include <QLabel>
#include <QLineEdit>
#include <QPlainTextEdit> #include <QPlainTextEdit>
#include <QPointer>
#include <QPushButton> #include <QPushButton>
using namespace ProjectExplorer; using namespace ProjectExplorer;
@@ -208,22 +210,21 @@ void CMakeKitConfigWidget::manageCMakeTools()
CMakeGeneratorKitConfigWidget::CMakeGeneratorKitConfigWidget(Kit *kit, CMakeGeneratorKitConfigWidget::CMakeGeneratorKitConfigWidget(Kit *kit,
const KitInformation *ki) : const KitInformation *ki) :
KitConfigWidget(kit, ki), KitConfigWidget(kit, ki),
m_comboBox(new QComboBox) m_label(new QLabel),
m_changeButton(new QPushButton)
{ {
m_comboBox->setToolTip(toolTip()); m_label->setToolTip(toolTip());
m_changeButton->setText(tr("Change..."));
refresh(); refresh();
connect(m_comboBox, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), connect(m_changeButton, &QPushButton::clicked,
this, [this](int idx) { this, &CMakeGeneratorKitConfigWidget::changeGenerator);
m_ignoreChange = true;
CMakeGeneratorKitInformation::setGenerator(m_kit, m_comboBox->itemData(idx).toString());
m_ignoreChange = false;
});
} }
CMakeGeneratorKitConfigWidget::~CMakeGeneratorKitConfigWidget() CMakeGeneratorKitConfigWidget::~CMakeGeneratorKitConfigWidget()
{ {
delete m_comboBox; delete m_label;
delete m_changeButton;
} }
QString CMakeGeneratorKitConfigWidget::displayName() const QString CMakeGeneratorKitConfigWidget::displayName() const
@@ -233,7 +234,7 @@ QString CMakeGeneratorKitConfigWidget::displayName() const
void CMakeGeneratorKitConfigWidget::makeReadOnly() void CMakeGeneratorKitConfigWidget::makeReadOnly()
{ {
m_comboBox->setEnabled(false); m_changeButton->setEnabled(false);
} }
void CMakeGeneratorKitConfigWidget::refresh() void CMakeGeneratorKitConfigWidget::refresh()
@@ -242,33 +243,31 @@ void CMakeGeneratorKitConfigWidget::refresh()
return; return;
CMakeTool *const tool = CMakeKitInformation::cmakeTool(m_kit); CMakeTool *const tool = CMakeKitInformation::cmakeTool(m_kit);
if (tool != m_currentTool) { if (tool != m_currentTool)
m_currentTool = tool; m_currentTool = tool;
m_comboBox->clear();
m_comboBox->addItem(tr("<Use Default Generator>"), QString());
if (tool && tool->isValid()) {
foreach (const QString &g, tool->supportedGenerators())
m_comboBox->addItem(g, g);
}
}
const QString generator = CMakeGeneratorKitInformation::generator(m_kit); m_changeButton->setEnabled(m_currentTool);
const QString extraGenerator = CMakeGeneratorKitInformation::extraGenerator(m_kit); const QString generator = CMakeGeneratorKitInformation::generator(kit());
QString fullName = extraGenerator; const QString extraGenerator = CMakeGeneratorKitInformation::extraGenerator(kit());
if (!fullName.isEmpty()) const QString platform = CMakeGeneratorKitInformation::platform(kit());
fullName += " - "; const QString toolset = CMakeGeneratorKitInformation::toolset(kit());
fullName += generator;
m_comboBox->setCurrentIndex(m_comboBox->findData(fullName)); const QString message = tr("%1 - %2, Platform: %3, Toolset: %4")
.arg(extraGenerator.isEmpty() ? tr("<none>") : extraGenerator)
.arg(generator.isEmpty() ? tr("<none>") : generator)
.arg(platform.isEmpty() ? tr("<none>") : platform)
.arg(toolset.isEmpty() ? tr("<none>") : toolset);
m_label->setText(message);
} }
QWidget *CMakeGeneratorKitConfigWidget::mainWidget() const QWidget *CMakeGeneratorKitConfigWidget::mainWidget() const
{ {
return m_comboBox; return m_label;
} }
QWidget *CMakeGeneratorKitConfigWidget::buttonWidget() const QWidget *CMakeGeneratorKitConfigWidget::buttonWidget() const
{ {
return nullptr; return m_changeButton;
} }
QString CMakeGeneratorKitConfigWidget::toolTip() const QString CMakeGeneratorKitConfigWidget::toolTip() const
@@ -277,6 +276,91 @@ QString CMakeGeneratorKitConfigWidget::toolTip() const
"This setting is ignored when using other build systems."); "This setting is ignored when using other build systems.");
} }
void CMakeGeneratorKitConfigWidget::changeGenerator()
{
QPointer<QDialog> changeDialog = new QDialog(m_changeButton);
changeDialog->setWindowTitle(tr("CMake Generator"));
auto *layout = new QGridLayout(changeDialog);
auto *cmakeLabel = new QLabel;
auto *generatorCombo = new QComboBox;
auto *extraGeneratorCombo = new QComboBox;
auto *platformEdit = new QLineEdit;
auto *toolsetEdit = new QLineEdit;
int row = 0;
layout->addWidget(new QLabel(QLatin1String("Executable:")));
layout->addWidget(cmakeLabel, row, 1);
++row;
layout->addWidget(new QLabel(tr("Generator:")), row, 0);
layout->addWidget(generatorCombo, row, 1);
++row;
layout->addWidget(new QLabel(tr("Extra Generator:")), row, 0);
layout->addWidget(extraGeneratorCombo, row, 1);
++row;
layout->addWidget(new QLabel(tr("Platform:")), row, 0);
layout->addWidget(platformEdit, row, 1);
++row;
layout->addWidget(new QLabel(tr("Toolset:")), row, 0);
layout->addWidget(toolsetEdit, row, 1);
++row;
auto *bb = new QDialogButtonBox(QDialogButtonBox::Ok|QDialogButtonBox::Cancel);
layout->addWidget(bb, row, 0, 1, 2);
connect(bb, &QDialogButtonBox::accepted, changeDialog, &QDialog::accept);
connect(bb, &QDialogButtonBox::rejected, changeDialog, &QDialog::reject);
cmakeLabel->setText(m_currentTool->cmakeExecutable().toUserOutput());
const QList<CMakeTool::Generator> generatorList = m_currentTool->supportedGenerators();
for (auto it = generatorList.constBegin(); it != generatorList.constEnd(); ++it)
generatorCombo->addItem(it->name);
auto updateDialog = [this, &generatorList, generatorCombo, extraGeneratorCombo,
platformEdit, toolsetEdit](const QString &name) {
auto it = std::find_if(generatorList.constBegin(), generatorList.constEnd(),
[name](const CMakeTool::Generator &g) { return g.name == name; });
QTC_ASSERT(it != generatorList.constEnd(), return);
generatorCombo->setCurrentText(name);
extraGeneratorCombo->clear();
extraGeneratorCombo->addItem(tr("<none>"), QString());
foreach (const QString &eg, it->extraGenerators)
extraGeneratorCombo->addItem(eg, eg);
extraGeneratorCombo->setEnabled(extraGeneratorCombo->count() > 1);
platformEdit->setEnabled(it->supportsPlatform);
toolsetEdit->setEnabled(it->supportsToolset);
};
updateDialog(CMakeGeneratorKitInformation::generator(kit()));
generatorCombo->setCurrentText(CMakeGeneratorKitInformation::generator(kit()));
extraGeneratorCombo->setCurrentText(CMakeGeneratorKitInformation::extraGenerator(kit()));
platformEdit->setText(platformEdit->isEnabled() ? CMakeGeneratorKitInformation::platform(kit()) : QLatin1String("<unsupported>"));
toolsetEdit->setText(toolsetEdit->isEnabled() ? CMakeGeneratorKitInformation::toolset(kit()) : QLatin1String("<unsupported>"));
connect(generatorCombo, &QComboBox::currentTextChanged, updateDialog);
if (changeDialog->exec() == QDialog::Accepted) {
if (!changeDialog)
return;
CMakeGeneratorKitInformation::set(kit(), generatorCombo->currentText(),
extraGeneratorCombo->currentData().toString(),
platformEdit->isEnabled() ? platformEdit->text() : QString(),
toolsetEdit->isEnabled() ? toolsetEdit->text() : QString());
}
}
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// CMakeConfigurationKitConfigWidget: // CMakeConfigurationKitConfigWidget:
// -------------------------------------------------------------------- // --------------------------------------------------------------------

View File

@@ -98,8 +98,11 @@ public:
QString toolTip() const override; QString toolTip() const override;
private: private:
void changeGenerator();
bool m_ignoreChange = false; bool m_ignoreChange = false;
QComboBox *m_comboBox; QLabel *m_label;
QPushButton *m_changeButton;
CMakeTool *m_currentTool = nullptr; CMakeTool *m_currentTool = nullptr;
}; };

View File

@@ -166,10 +166,6 @@ struct GeneratorInfo {
toolset = value.value(TOOLSET_KEY).toString(); toolset = value.value(TOOLSET_KEY).toString();
} }
QString fullName() const {
return extraGenerator.isEmpty() ? generator : extraGenerator + " - " + generator;
}
QString generator; QString generator;
QString extraGenerator; QString extraGenerator;
QString platform; QString platform;
@@ -250,6 +246,14 @@ void CMakeGeneratorKitInformation::setToolset(Kit *k, const QString &toolset)
setGeneratorInfo(k, info); setGeneratorInfo(k, info);
} }
void CMakeGeneratorKitInformation::set(Kit *k,
const QString &generator, const QString &extraGenerator,
const QString &platform, const QString &toolset)
{
GeneratorInfo info = { generator, extraGenerator, platform, toolset };
setGeneratorInfo(k, info);
}
QStringList CMakeGeneratorKitInformation::generatorArguments(const Kit *k) QStringList CMakeGeneratorKitInformation::generatorArguments(const Kit *k)
{ {
QStringList result; QStringList result;
@@ -277,50 +281,49 @@ QVariant CMakeGeneratorKitInformation::defaultValue(const Kit *k) const
CMakeTool *tool = CMakeKitInformation::cmakeTool(k); CMakeTool *tool = CMakeKitInformation::cmakeTool(k);
if (!tool) if (!tool)
return QVariant(); return QVariant();
QString generator;
const QString extraGenerator = "CodeBlocks"; const QString extraGenerator = "CodeBlocks";
QStringList known = tool->supportedGenerators(); QList<CMakeTool::Generator> known = tool->supportedGenerators();
auto it = std::find_if(known.constBegin(), known.constEnd(), auto it = std::find_if(known.constBegin(), known.constEnd(),
[](const QString &s) { return s == QLatin1String("CodeBlocks - Ninja"); }); [extraGenerator](const CMakeTool::Generator &g) {
return g.matches("Ninja", extraGenerator);
});
if (it != known.constEnd()) { if (it != known.constEnd()) {
generator = "Ninja";
Utils::Environment env = Utils::Environment::systemEnvironment(); Utils::Environment env = Utils::Environment::systemEnvironment();
k->addToEnvironment(env); k->addToEnvironment(env);
const Utils::FileName ninjaExec = env.searchInPath(QLatin1String("ninja")); const Utils::FileName ninjaExec = env.searchInPath(QLatin1String("ninja"));
if (ninjaExec.isEmpty()) if (!ninjaExec.isEmpty())
it = known.constEnd(); // Ignore ninja generator without ninja exectuable return GeneratorInfo({ "Ninja", extraGenerator, QString(), QString() }).toVariant();
} }
if (Utils::HostOsInfo::isWindowsHost()) { if (Utils::HostOsInfo::isWindowsHost()) {
// *sigh* Windows with its zoo of incompatible stuff again... // *sigh* Windows with its zoo of incompatible stuff again...
ToolChain *tc = ToolChainKitInformation::toolChain(k, ToolChain::Language::Cxx); ToolChain *tc = ToolChainKitInformation::toolChain(k, ToolChain::Language::Cxx);
if (tc && tc->typeId() == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID) { if (tc && tc->typeId() == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID) {
if (it == known.constEnd())
it = std::find_if(known.constBegin(), known.constEnd(), it = std::find_if(known.constBegin(), known.constEnd(),
[](const QString &s) { return s == QLatin1String("CodeBlocks - MinGW Makefiles"); }); [extraGenerator](const CMakeTool::Generator &g) {
return g.matches("MinGW Makefiles", extraGenerator);
});
} else { } else {
if (it == known.constEnd())
it = std::find_if(known.constBegin(), known.constEnd(), it = std::find_if(known.constBegin(), known.constEnd(),
[](const QString &s) { return s == QLatin1String("CodeBlocks - NMake Makefiles"); }); [extraGenerator](const CMakeTool::Generator &g) {
return g.matches("NMake Makefiles", extraGenerator);
});
} }
} else { } else {
// Unix-oid OSes: // Unix-oid OSes:
if (it == known.constEnd())
it = std::find_if(known.constBegin(), known.constEnd(), it = std::find_if(known.constBegin(), known.constEnd(),
[](const QString &s) { return s == QLatin1String("CodeBlocks - Unix Makefiles"); }); [extraGenerator](const CMakeTool::Generator &g) {
return g.matches("Unix Makefiles", extraGenerator);
});
} }
if (it == known.constEnd()) if (it == known.constEnd())
it = known.constBegin(); // Fallback to the first generator... it = known.constBegin(); // Fallback to the first generator...
if (it == known.constEnd()) if (it == known.constEnd())
return QVariant(); return QVariant();
GeneratorInfo info; return GeneratorInfo({ it->name, extraGenerator, QString(), QString() }).toVariant();
info.extraGenerator = "CodeBlocks";
const int pos = it->indexOf(" - ");
info.generator = (pos < 0) ? *it : it->mid(pos + 3);
return info.toVariant();
} }
QList<Task> CMakeGeneratorKitInformation::validate(const Kit *k) const QList<Task> CMakeGeneratorKitInformation::validate(const Kit *k) const
@@ -334,10 +337,22 @@ QList<Task> CMakeGeneratorKitInformation::validate(const Kit *k) const
result << Task(Task::Warning, tr("CMake Tool is unconfigured, CMake generator will be ignored."), result << Task(Task::Warning, tr("CMake Tool is unconfigured, CMake generator will be ignored."),
Utils::FileName(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM)); Utils::FileName(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM));
} else { } else {
QStringList known = tool->supportedGenerators(); QList<CMakeTool::Generator> known = tool->supportedGenerators();
if (!known.contains(info.fullName())) { auto it = std::find_if(known.constBegin(), known.constEnd(), [info](const CMakeTool::Generator &g) {
return g.matches(info.generator, info.extraGenerator);
});
if (it == known.constEnd()) {
result << Task(Task::Warning, tr("CMake Tool does not support the configured generator."), result << Task(Task::Warning, tr("CMake Tool does not support the configured generator."),
Utils::FileName(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM)); Utils::FileName(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM));
} else {
if (!it->supportsPlatform && !info.platform.isEmpty()) {
result << Task(Task::Warning, tr("Platform is not supported by the selected CMake generator."),
Utils::FileName(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM));
}
if (!it->supportsToolset && !info.toolset.isEmpty()) {
result << Task(Task::Warning, tr("Toolset is not supported by the selected CMake generator."),
Utils::FileName(), -1, Core::Id(Constants::TASK_CATEGORY_BUILDSYSTEM));
}
} }
if (info.extraGenerator != "CodeBlocks") { if (info.extraGenerator != "CodeBlocks") {
result << Task(Task::Warning, tr("CMake generator does not generate a CodeBlocks file. " result << Task(Task::Warning, tr("CMake generator does not generate a CodeBlocks file. "
@@ -363,11 +378,20 @@ void CMakeGeneratorKitInformation::fix(Kit *k)
if (!tool) if (!tool)
return; return;
QStringList known = tool->supportedGenerators(); QList<CMakeTool::Generator> known = tool->supportedGenerators();
if (info.generator.isEmpty() || !known.contains(info.fullName())) { auto it = std::find_if(known.constBegin(), known.constEnd(),
[info](const CMakeTool::Generator &g) {
return g.matches(info.generator, info.extraGenerator);
});
if (it == known.constEnd()) {
GeneratorInfo dv; GeneratorInfo dv;
dv.fromVariant(defaultValue(k)); dv.fromVariant(defaultValue(k));
setGeneratorInfo(k, dv); setGeneratorInfo(k, dv);
} else {
const GeneratorInfo dv = { info.generator, info.extraGenerator,
it->supportsPlatform ? info.platform : QString(),
it->supportsToolset ? info.toolset : QString() };
setGeneratorInfo(k, dv);
} }
} }

View File

@@ -69,6 +69,8 @@ public:
static void setExtraGenerator(ProjectExplorer::Kit *k, const QString &extraGenerator); static void setExtraGenerator(ProjectExplorer::Kit *k, const QString &extraGenerator);
static void setPlatform(ProjectExplorer::Kit *k, const QString &platform); static void setPlatform(ProjectExplorer::Kit *k, const QString &platform);
static void setToolset(ProjectExplorer::Kit *k, const QString &toolset); static void setToolset(ProjectExplorer::Kit *k, const QString &toolset);
static void set(ProjectExplorer::Kit *k, const QString &generator,
const QString &extraGenerator, const QString &platform, const QString &toolset);
static QStringList generatorArguments(const ProjectExplorer::Kit *k); static QStringList generatorArguments(const ProjectExplorer::Kit *k);
// KitInformation interface // KitInformation interface

View File

@@ -45,6 +45,12 @@ const char CMAKE_INFORMATION_DISPLAYNAME[] = "DisplayName";
const char CMAKE_INFORMATION_AUTORUN[] = "AutoRun"; const char CMAKE_INFORMATION_AUTORUN[] = "AutoRun";
const char CMAKE_INFORMATION_AUTODETECTED[] = "AutoDetected"; const char CMAKE_INFORMATION_AUTODETECTED[] = "AutoDetected";
bool CMakeTool::Generator::matches(const QString &n, const QString &ex) const
{
return n == name && (ex.isEmpty() || extraGenerators.contains(ex));
}
/////////////////////////// ///////////////////////////
// CMakeTool // CMakeTool
/////////////////////////// ///////////////////////////
@@ -153,37 +159,10 @@ bool CMakeTool::isAutoRun() const
return m_isAutoRun; return m_isAutoRun;
} }
QStringList CMakeTool::supportedGenerators() const QList<CMakeTool::Generator> CMakeTool::supportedGenerators() const
{ {
if (m_generators.isEmpty()) { if (m_generators.isEmpty()) {
Utils::SynchronousProcessResponse response = run("--help"); fetchGeneratorsFromHelp();
if (response.result == Utils::SynchronousProcessResponse::Finished) {
bool inGeneratorSection = false;
const QStringList lines = response.stdOut().split('\n');
foreach (const QString &line, lines) {
if (line.isEmpty())
continue;
if (line == "Generators") {
inGeneratorSection = true;
continue;
}
if (!inGeneratorSection)
continue;
if (line.startsWith(" ") && line.at(3) != ' ') {
int pos = line.indexOf('=');
if (pos < 0)
pos = line.length();
if (pos >= 0) {
--pos;
while (pos > 2 && line.at(pos).isSpace())
--pos;
}
if (pos > 2)
m_generators.append(line.mid(2, pos - 1));
}
}
}
} }
return m_generators; return m_generators;
} }
@@ -334,4 +313,60 @@ QStringList CMakeTool::parseVariableOutput(const QString &output)
return result; return result;
} }
void CMakeTool::fetchGeneratorsFromHelp() const
{
Utils::SynchronousProcessResponse response = run("--help");
if (response.result != Utils::SynchronousProcessResponse::Finished)
return;
bool inGeneratorSection = false;
QHash<QString, QStringList> generatorInfo;
const QStringList lines = response.stdOut().split('\n');
foreach (const QString &line, lines) {
if (line.isEmpty())
continue;
if (line == "Generators") {
inGeneratorSection = true;
continue;
}
if (!inGeneratorSection)
continue;
if (line.startsWith(" ") && line.at(3) != ' ') {
int pos = line.indexOf('=');
if (pos < 0)
pos = line.length();
if (pos >= 0) {
--pos;
while (pos > 2 && line.at(pos).isSpace())
--pos;
}
if (pos > 2) {
const QString fullName = line.mid(2, pos - 1);
const int dashPos = fullName.indexOf(" - ");
QString generator;
QString extra;
if (dashPos < 0) {
generator = fullName;
} else {
extra = fullName.mid(0, dashPos);
generator = fullName.mid(dashPos + 3);
}
QStringList value = generatorInfo.value(generator);
if (!extra.isEmpty())
value.append(extra);
generatorInfo.insert(generator, value);
}
}
}
// Populate genertor list:
for (auto it = generatorInfo.constBegin(); it != generatorInfo.constEnd(); ++it) {
Generator info;
info.name = it.key();
info.extraGenerators = it.value();
m_generators.append(info);
}
}
} // namespace CMakeProjectManager } // namespace CMakeProjectManager

View File

@@ -52,6 +52,17 @@ public:
AutoDetection AutoDetection
}; };
class Generator
{
public:
QString name;
QStringList extraGenerators;
bool supportsPlatform = true;
bool supportsToolset = true;
bool matches(const QString &n, const QString &ex) const;
};
typedef std::function<QString (const ProjectExplorer::Kit *, const QString &)> PathMapper; typedef std::function<QString (const ProjectExplorer::Kit *, const QString &)> PathMapper;
explicit CMakeTool(Detection d, const Core::Id &id); explicit CMakeTool(Detection d, const Core::Id &id);
@@ -70,7 +81,7 @@ public:
Utils::FileName cmakeExecutable() const; Utils::FileName cmakeExecutable() const;
bool isAutoRun() const; bool isAutoRun() const;
QStringList supportedGenerators() const; QList<Generator> supportedGenerators() const;
TextEditor::Keywords keywords(); TextEditor::Keywords keywords();
bool isAutoDetected() const; bool isAutoDetected() const;
@@ -85,6 +96,8 @@ private:
void parseFunctionDetailsOutput(const QString &output); void parseFunctionDetailsOutput(const QString &output);
QStringList parseVariableOutput(const QString &output); QStringList parseVariableOutput(const QString &output);
void fetchGeneratorsFromHelp() const;
Core::Id m_id; Core::Id m_id;
QString m_displayName; QString m_displayName;
Utils::FileName m_executable; Utils::FileName m_executable;
@@ -95,7 +108,7 @@ private:
mutable bool m_didAttemptToRun = false; mutable bool m_didAttemptToRun = false;
mutable bool m_didRun = false; mutable bool m_didRun = false;
mutable QStringList m_generators; mutable QList<Generator> m_generators;
mutable QMap<QString, QStringList> m_functionArgs; mutable QMap<QString, QStringList> m_functionArgs;
mutable QStringList m_variables; mutable QStringList m_variables;
mutable QStringList m_functions; mutable QStringList m_functions;