CMake: Support building several targets with cmake --build

CMake supports this for a while now.

As a side effect this fixes QTCREATORBUG-23738, which was caused by the
radio button hack used to show that only one item could be selected at
a time.

Change-Id: I18cbe6c5ee3872edaf74b9d828bde1ac5bf63563
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
This commit is contained in:
Tobias Hunger
2020-06-02 19:37:25 +02:00
parent 10b63683fc
commit 2fea24fe6a
4 changed files with 87 additions and 97 deletions

View File

@@ -227,16 +227,16 @@ void CMakeBuildConfiguration::buildTarget(const QString &buildTarget)
return bs->id() == Constants::CMAKE_BUILD_STEP_ID; return bs->id() == Constants::CMAKE_BUILD_STEP_ID;
})); }));
QString originalBuildTarget; QStringList originalBuildTargets;
if (cmBs) { if (cmBs) {
originalBuildTarget = cmBs->buildTarget(); originalBuildTargets = cmBs->buildTargets();
cmBs->setBuildTarget(buildTarget); cmBs->setBuildTargets({buildTarget});
} }
BuildManager::buildList(buildSteps()); BuildManager::buildList(buildSteps());
if (cmBs) if (cmBs)
cmBs->setBuildTarget(originalBuildTarget); cmBs->setBuildTargets(originalBuildTargets);
} }
CMakeConfig CMakeBuildConfiguration::configurationFromCMake() const CMakeConfig CMakeBuildConfiguration::configurationFromCMake() const

View File

@@ -41,11 +41,12 @@
#include <projectexplorer/runconfiguration.h> #include <projectexplorer/runconfiguration.h>
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
#include <utils/algorithm.h>
#include <QBoxLayout> #include <QBoxLayout>
#include <QFormLayout> #include <QFormLayout>
#include <QLineEdit> #include <QLineEdit>
#include <QListWidget> #include <QListWidget>
#include <QRadioButton>
using namespace ProjectExplorer; using namespace ProjectExplorer;
@@ -66,14 +67,12 @@ public:
explicit CMakeBuildStepConfigWidget(CMakeBuildStep *buildStep); explicit CMakeBuildStepConfigWidget(CMakeBuildStep *buildStep);
private: private:
void itemChanged(QListWidgetItem *); void itemsChanged();
void cmakeArgumentsEdited(); void cmakeArgumentsEdited();
void toolArgumentsEdited(); void toolArgumentsEdited();
void updateDetails(); void updateDetails();
void buildTargetsChanged(); void buildTargetsChanged();
void updateBuildTarget(); void updateBuildTargets();
QRadioButton *itemWidget(QListWidgetItem *item);
CMakeBuildStep *m_buildStep; CMakeBuildStep *m_buildStep;
QLineEdit *m_cmakeArguments; QLineEdit *m_cmakeArguments;
@@ -96,13 +95,13 @@ CMakeBuildStep::CMakeBuildStep(BuildStepList *bsl, Core::Id id) :
setDefaultDisplayName(tr("CMake Build")); setDefaultDisplayName(tr("CMake Build"));
// Set a good default build target: // Set a good default build target:
if (m_buildTarget.isEmpty()) if (m_buildTargets.isEmpty())
setBuildTarget(defaultBuildTarget()); setBuildTargets({defaultBuildTarget()});
setLowPriority(); setLowPriority();
connect(target(), &Target::parsingFinished, connect(target(), &Target::parsingFinished,
this, &CMakeBuildStep::handleBuildTargetChanges); this, &CMakeBuildStep::handleBuildTargetsChanges);
} }
CMakeBuildConfiguration *CMakeBuildStep::cmakeBuildConfiguration() const CMakeBuildConfiguration *CMakeBuildStep::cmakeBuildConfiguration() const
@@ -110,12 +109,17 @@ CMakeBuildConfiguration *CMakeBuildStep::cmakeBuildConfiguration() const
return static_cast<CMakeBuildConfiguration *>(buildConfiguration()); return static_cast<CMakeBuildConfiguration *>(buildConfiguration());
} }
void CMakeBuildStep::handleBuildTargetChanges(bool success) void CMakeBuildStep::handleBuildTargetsChanges(bool success)
{ {
if (!success) if (!success)
return; // Do not change when parsing failed. return; // Do not change when parsing failed.
if (!isCurrentExecutableTarget(m_buildTarget) && !knownBuildTargets().contains(m_buildTarget)) { const QStringList results = Utils::filtered(m_buildTargets, [this](const QString &s) {
setBuildTarget(defaultBuildTarget()); return knownBuildTargets().contains(s);
});
if (results.isEmpty())
setBuildTargets({defaultBuildTarget()});
else {
setBuildTargets(results);
} }
emit buildTargetsChanged(); emit buildTargetsChanged();
} }
@@ -124,7 +128,7 @@ QVariantMap CMakeBuildStep::toMap() const
{ {
QVariantMap map(AbstractProcessStep::toMap()); QVariantMap map(AbstractProcessStep::toMap());
// Use QStringList for compatibility with old files // Use QStringList for compatibility with old files
map.insert(BUILD_TARGETS_KEY, QStringList(m_buildTarget)); map.insert(BUILD_TARGETS_KEY, QStringList(m_buildTargets));
map.insert(CMAKE_ARGUMENTS_KEY, m_cmakeArguments); map.insert(CMAKE_ARGUMENTS_KEY, m_cmakeArguments);
map.insert(TOOL_ARGUMENTS_KEY, m_toolArguments); map.insert(TOOL_ARGUMENTS_KEY, m_toolArguments);
return map; return map;
@@ -132,13 +136,11 @@ QVariantMap CMakeBuildStep::toMap() const
bool CMakeBuildStep::fromMap(const QVariantMap &map) bool CMakeBuildStep::fromMap(const QVariantMap &map)
{ {
const QStringList targetList = map.value(BUILD_TARGETS_KEY).toStringList(); m_buildTargets = map.value(BUILD_TARGETS_KEY).toStringList();
if (!targetList.isEmpty())
m_buildTarget = targetList.last();
m_cmakeArguments = map.value(CMAKE_ARGUMENTS_KEY).toString(); m_cmakeArguments = map.value(CMAKE_ARGUMENTS_KEY).toString();
m_toolArguments = map.value(TOOL_ARGUMENTS_KEY).toString(); m_toolArguments = map.value(TOOL_ARGUMENTS_KEY).toString();
if (map.value(ADD_RUNCONFIGURATION_ARGUMENT_KEY, false).toBool()) if (map.value(ADD_RUNCONFIGURATION_ARGUMENT_KEY, false).toBool())
m_buildTarget = ADD_RUNCONFIGURATION_TEXT; m_buildTargets = QStringList(ADD_RUNCONFIGURATION_TEXT);
return BuildStep::fromMap(map); return BuildStep::fromMap(map);
} }
@@ -163,7 +165,8 @@ bool CMakeBuildStep::init()
} }
RunConfiguration *rc = target()->activeRunConfiguration(); RunConfiguration *rc = target()->activeRunConfiguration();
if (isCurrentExecutableTarget(m_buildTarget) && (!rc || rc->buildKey().isEmpty())) { const bool buildCurrent = Utils::contains(m_buildTargets, [](const QString &s) { return isCurrentExecutableTarget(s); });
if (buildCurrent && (!rc || rc->buildKey().isEmpty())) {
emit addTask(BuildSystemTask(Task::Error, emit addTask(BuildSystemTask(Task::Error,
QCoreApplication::translate("ProjectExplorer::Task", QCoreApplication::translate("ProjectExplorer::Task",
"You asked to build the current Run Configuration's build target only, " "You asked to build the current Run Configuration's build target only, "
@@ -189,7 +192,7 @@ bool CMakeBuildStep::init()
} }
} }
setIgnoreReturnValue(m_buildTarget == CMakeBuildStep::cleanTarget()); setIgnoreReturnValue(m_buildTargets == QStringList(CMakeBuildStep::cleanTarget()));
ProcessParameters *pp = processParameters(); ProcessParameters *pp = processParameters();
pp->setMacroExpander(bc->macroExpander()); pp->setMacroExpander(bc->macroExpander());
@@ -314,22 +317,22 @@ void CMakeBuildStep::stdOutput(const QString &output)
} }
} }
QString CMakeBuildStep::buildTarget() const QStringList CMakeBuildStep::buildTargets() const
{ {
return m_buildTarget; return m_buildTargets;
} }
bool CMakeBuildStep::buildsBuildTarget(const QString &target) const bool CMakeBuildStep::buildsBuildTarget(const QString &target) const
{ {
return target == m_buildTarget; return m_buildTargets.contains(target);
} }
void CMakeBuildStep::setBuildTarget(const QString &buildTarget) void CMakeBuildStep::setBuildTargets(const QStringList &buildTargets)
{ {
if (m_buildTarget == buildTarget) if (m_buildTargets == buildTargets)
return; return;
m_buildTarget = buildTarget; m_buildTargets = buildTargets;
emit targetToBuildChanged(); emit targetsToBuildChanged();
} }
QString CMakeBuildStep::cmakeArguments() const QString CMakeBuildStep::cmakeArguments() const
@@ -359,9 +362,10 @@ Utils::CommandLine CMakeBuildStep::cmakeCommand(RunConfiguration *rc) const
Utils::CommandLine cmd(tool ? tool->cmakeExecutable() : Utils::FilePath(), {}); Utils::CommandLine cmd(tool ? tool->cmakeExecutable() : Utils::FilePath(), {});
cmd.addArgs({"--build", "."}); cmd.addArgs({"--build", "."});
QString target; cmd.addArg("--target");
cmd.addArgs(Utils::transform(m_buildTargets, [rc](const QString &s) {
if (isCurrentExecutableTarget(m_buildTarget)) { QString target = s;
if (isCurrentExecutableTarget(s)) {
if (rc) { if (rc) {
target = rc->buildKey(); target = rc->buildKey();
const int pos = target.indexOf("///::///"); const int pos = target.indexOf("///::///");
@@ -371,11 +375,11 @@ Utils::CommandLine CMakeBuildStep::cmakeCommand(RunConfiguration *rc) const
} else { } else {
target = "<i>&lt;" + tr(ADD_RUNCONFIGURATION_TEXT) + "&gt;</i>"; target = "<i>&lt;" + tr(ADD_RUNCONFIGURATION_TEXT) + "&gt;</i>";
} }
} else {
target = m_buildTarget;
} }
return target;
}));
cmd.addArgs({"--target", target}); if (!m_cmakeArguments.isEmpty())
cmd.addArgs(m_cmakeArguments, Utils::CommandLine::Raw); cmd.addArgs(m_cmakeArguments, Utils::CommandLine::Raw);
if (!m_toolArguments.isEmpty()) { if (!m_toolArguments.isEmpty()) {
@@ -457,7 +461,7 @@ CMakeBuildStepConfigWidget::CMakeBuildStepConfigWidget(CMakeBuildStep *buildStep
connect(m_cmakeArguments, &QLineEdit::textEdited, this, &CMakeBuildStepConfigWidget::cmakeArgumentsEdited); connect(m_cmakeArguments, &QLineEdit::textEdited, this, &CMakeBuildStepConfigWidget::cmakeArgumentsEdited);
connect(m_toolArguments, &QLineEdit::textEdited, this, &CMakeBuildStepConfigWidget::toolArgumentsEdited); connect(m_toolArguments, &QLineEdit::textEdited, this, &CMakeBuildStepConfigWidget::toolArgumentsEdited);
connect(m_buildTargetsList, &QListWidget::itemChanged, this, &CMakeBuildStepConfigWidget::itemChanged); connect(m_buildTargetsList, &QListWidget::itemChanged, this, &CMakeBuildStepConfigWidget::itemsChanged);
connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged, connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::settingsChanged,
this, &CMakeBuildStepConfigWidget::updateDetails); this, &CMakeBuildStepConfigWidget::updateDetails);
@@ -467,9 +471,9 @@ CMakeBuildStepConfigWidget::CMakeBuildStepConfigWidget(CMakeBuildStep *buildStep
&CMakeBuildStepConfigWidget::buildTargetsChanged); &CMakeBuildStepConfigWidget::buildTargetsChanged);
connect(m_buildStep, connect(m_buildStep,
&CMakeBuildStep::targetToBuildChanged, &CMakeBuildStep::targetsToBuildChanged,
this, this,
&CMakeBuildStepConfigWidget::updateBuildTarget); &CMakeBuildStepConfigWidget::updateBuildTargets);
connect(m_buildStep->buildConfiguration(), connect(m_buildStep->buildConfiguration(),
&BuildConfiguration::environmentChanged, &BuildConfiguration::environmentChanged,
@@ -488,28 +492,32 @@ void CMakeBuildStepConfigWidget::toolArgumentsEdited()
updateDetails(); updateDetails();
} }
void CMakeBuildStepConfigWidget::itemChanged(QListWidgetItem *item) void CMakeBuildStepConfigWidget::itemsChanged()
{ {
const QString target = const QList<QListWidgetItem *> items = [this]() {
(item->checkState() == Qt::Checked) ? item->data(Qt::UserRole).toString() : CMakeBuildStep::allTarget(); QList<QListWidgetItem *> items;
m_buildStep->setBuildTarget(target); for (int row = 0; row < m_buildTargetsList->count(); ++row)
items.append(m_buildTargetsList->item(row));
return items;
}();
const QStringList targetsToBuild = Utils::transform(Utils::filtered(items, Utils::equal(&QListWidgetItem::checkState, Qt::Checked)),
[](const QListWidgetItem *i) { return i->data(Qt::UserRole).toString(); });
m_buildStep->setBuildTargets(targetsToBuild);
updateDetails(); updateDetails();
} }
void CMakeBuildStepConfigWidget::buildTargetsChanged() void CMakeBuildStepConfigWidget::buildTargetsChanged()
{ {
{ {
auto addItem = [this](const QString &buildTarget, QFont italics;
const QString &displayName) { italics.setItalic(true);
auto item = new QListWidgetItem(m_buildTargetsList);
auto button = new QRadioButton(displayName); auto addItem = [italics, this](const QString &buildTarget, const QString &displayName, bool special = false) {
connect(button, &QRadioButton::toggled, this, [this, buildTarget](bool toggled) { auto item = new QListWidgetItem(displayName, m_buildTargetsList);
if (toggled) { item->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled);
m_buildStep->setBuildTarget(buildTarget);
}
});
m_buildTargetsList->setItemWidget(item, button);
item->setData(Qt::UserRole, buildTarget); item->setData(Qt::UserRole, buildTarget);
if (special)
item->setFont(italics);
}; };
QSignalBlocker blocker(m_buildTargetsList); QSignalBlocker blocker(m_buildTargetsList);
@@ -518,52 +526,34 @@ void CMakeBuildStepConfigWidget::buildTargetsChanged()
QStringList targetList = m_buildStep->knownBuildTargets(); QStringList targetList = m_buildStep->knownBuildTargets();
targetList.sort(); targetList.sort();
QFont italics; addItem(ADD_RUNCONFIGURATION_TEXT, tr(ADD_RUNCONFIGURATION_TEXT), true);
italics.setItalic(true);
addItem(ADD_RUNCONFIGURATION_TEXT, tr(ADD_RUNCONFIGURATION_TEXT)); foreach (const QString &buildTarget, CMakeBuildStep::specialTargets())
addItem(buildTarget, buildTarget, true);
foreach (const QString &buildTarget, targetList) foreach (const QString &buildTarget, targetList)
addItem(buildTarget, buildTarget); addItem(buildTarget, buildTarget);
for (int i = 0; i < m_buildTargetsList->count(); ++i) { updateBuildTargets();
QListWidgetItem *item = m_buildTargetsList->item(i);
const QString title = item->data(Qt::UserRole).toString();
QRadioButton *radio = itemWidget(item);
radio->setChecked(m_buildStep->buildsBuildTarget(title));
// Print utility targets in italics:
if (CMakeBuildStep::specialTargets().contains(title) || title == ADD_RUNCONFIGURATION_TEXT)
radio->setFont(italics);
}
} }
updateDetails(); updateDetails();
} }
void CMakeBuildStepConfigWidget::updateBuildTarget() void CMakeBuildStepConfigWidget::updateBuildTargets()
{ {
const QString buildTarget = m_buildStep->buildTarget(); const QStringList buildTargets = m_buildStep->buildTargets();
{ {
QSignalBlocker blocker(m_buildTargetsList); QSignalBlocker blocker(m_buildTargetsList);
for (int y = 0; y < m_buildTargetsList->count(); ++y) { for (int row = 0; row < m_buildTargetsList->count(); ++row) {
QListWidgetItem *item = m_buildTargetsList->item(y); QListWidgetItem *item = m_buildTargetsList->item(row);
const QString itemTarget = item->data(Qt::UserRole).toString(); const QString title = item->data(Qt::UserRole).toString();
if (itemTarget == buildTarget) { item->setCheckState(m_buildStep->buildsBuildTarget(title) ? Qt::Checked : Qt::Unchecked);
QRadioButton *radio = itemWidget(item);
radio->setChecked(true);
}
} }
} }
updateDetails(); updateDetails();
} }
QRadioButton *CMakeBuildStepConfigWidget::itemWidget(QListWidgetItem *item)
{
return static_cast<QRadioButton *>(m_buildTargetsList->itemWidget(item));
}
void CMakeBuildStepConfigWidget::updateDetails() void CMakeBuildStepConfigWidget::updateDetails()
{ {
ProcessParameters param; ProcessParameters param;

View File

@@ -47,9 +47,9 @@ public:
CMakeBuildConfiguration *cmakeBuildConfiguration() const; CMakeBuildConfiguration *cmakeBuildConfiguration() const;
QString buildTarget() const; QStringList buildTargets() const;
bool buildsBuildTarget(const QString &target) const; bool buildsBuildTarget(const QString &target) const;
void setBuildTarget(const QString &target); void setBuildTargets(const QStringList &target);
QString cmakeArguments() const; QString cmakeArguments() const;
void setCMakeArguments(const QString &list); void setCMakeArguments(const QString &list);
@@ -69,7 +69,7 @@ public:
static QStringList specialTargets(); static QStringList specialTargets();
signals: signals:
void targetToBuildChanged(); void targetsToBuildChanged();
void buildTargetsChanged(); void buildTargetsChanged();
protected: protected:
@@ -94,14 +94,14 @@ private:
void runImpl(); void runImpl();
void handleProjectWasParsed(bool success); void handleProjectWasParsed(bool success);
void handleBuildTargetChanges(bool success); void handleBuildTargetsChanges(bool success);
QMetaObject::Connection m_runTrigger; QMetaObject::Connection m_runTrigger;
QRegExp m_percentProgress; QRegExp m_percentProgress;
QRegExp m_ninjaProgress; QRegExp m_ninjaProgress;
QString m_ninjaProgressString; QString m_ninjaProgressString;
QString m_buildTarget; QStringList m_buildTargets;
QString m_cmakeArguments; QString m_cmakeArguments;
QString m_toolArguments; QString m_toolArguments;
bool m_useNinja = false; bool m_useNinja = false;

View File

@@ -152,12 +152,12 @@ void BuildCMakeTargetLocatorFilter::accept(Core::LocatorFilterEntry selection,
return; return;
// Change the make step to build only the given target // Change the make step to build only the given target
QString oldTarget = buildStep->buildTarget(); QStringList oldTargets = buildStep->buildTargets();
buildStep->setBuildTarget(selection.displayName); buildStep->setBuildTargets({selection.displayName});
// Build // Build
BuildManager::buildProjectWithDependencies(cmakeProject); BuildManager::buildProjectWithDependencies(cmakeProject);
buildStep->setBuildTarget(oldTarget); buildStep->setBuildTargets(oldTargets);
} }
// -------------------------------------------------------------------- // --------------------------------------------------------------------