IncrediBuild: Rework CommandBuilder

Simplify interfaces. Also persist all command line settings,
make overriding default arguments explicit.

Change-Id: Ifb7e791dfc07ae9a88cfd769b9d21c5ee242e31d
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
hjk
2020-08-06 18:11:52 +02:00
parent f00b088a1d
commit e04b9a5348
10 changed files with 121 additions and 257 deletions

View File

@@ -25,7 +25,6 @@
#include "buildconsolebuildstep.h"
#include "commandbuilder.h"
#include "commandbuilderaspect.h"
#include "incredibuildconstants.h"
@@ -317,10 +316,8 @@ bool BuildConsoleBuildStep::init()
{
QStringList args;
CommandBuilder *activeCommandBuilder = m_commandBuilder->commandBuilder();
activeCommandBuilder->keepJobNum(m_keepJobNum->value());
QString cmd("/Command= %0");
cmd = cmd.arg(activeCommandBuilder->fullCommandFlag());
cmd = cmd.arg(m_commandBuilder->fullCommandFlag(m_keepJobNum->value()));
args.append(cmd);
if (!m_profileXml->value().isEmpty())

View File

@@ -25,7 +25,6 @@
#include "cmakecommandbuilder.h"
#include <coreplugin/icore.h>
#include <projectexplorer/buildconfiguration.h>
#include <projectexplorer/buildstep.h>
#include <projectexplorer/buildsteplist.h>
@@ -34,8 +33,8 @@
#include <projectexplorer/toolchain.h>
#include <projectexplorer/project.h>
#include <QFileInfo>
#include <QMessageBox>
#include <utils/qtcprocess.h>
#include <QRegularExpression>
#include <QStandardPaths>
@@ -59,24 +58,15 @@ bool CMakeCommandBuilder::canMigrate(BuildStepList *buildStepList)
return false;
}
QString CMakeCommandBuilder::defaultCommand()
QString CMakeCommandBuilder::defaultCommand() const
{
if (!m_defaultMake.isEmpty())
return m_defaultMake;
m_defaultMake = "cmake";
QString cmake = QStandardPaths::findExecutable(m_defaultMake);
if (!cmake.isEmpty())
m_defaultMake = cmake;
return m_defaultMake;
const QString defaultCMake = "cmake";
const QString cmake = QStandardPaths::findExecutable(defaultCMake);
return cmake.isEmpty() ? defaultCMake : cmake;
}
QStringList CMakeCommandBuilder::defaultArguments()
QString CMakeCommandBuilder::defaultArguments() const
{
if (!m_defaultArgs.isEmpty())
return m_defaultArgs;
// Build folder or "."
QString buildDir;
BuildConfiguration *buildConfig = buildStep()->buildConfiguration();
@@ -86,12 +76,7 @@ QStringList CMakeCommandBuilder::defaultArguments()
if (buildDir.isEmpty())
buildDir = ".";
m_defaultArgs.append("--build");
m_defaultArgs.append(buildDir);
m_defaultArgs.append("--target");
m_defaultArgs.append("all");
return m_defaultArgs;
return Utils::QtcProcess::joinArgs({"--build", buildDir, "--target", "all"});
}
QString CMakeCommandBuilder::setMultiProcessArg(QString args)

View File

@@ -27,27 +27,21 @@
#include "commandbuilder.h"
#include <QCoreApplication>
namespace IncrediBuild {
namespace Internal {
class CMakeCommandBuilder : public CommandBuilder
{
Q_DECLARE_TR_FUNCTIONS(CommandBuilder)
public:
CMakeCommandBuilder(ProjectExplorer::BuildStep *buildStep) : CommandBuilder(buildStep) {}
bool canMigrate(ProjectExplorer::BuildStepList *buildStepList) override;
QString displayName() const override { return tr("CMake"); }
QString defaultCommand() override;
QStringList defaultArguments() override;
QString setMultiProcessArg(QString args) override;
private:
QString m_defaultMake{};
QStringList m_defaultArgs{};
bool canMigrate(ProjectExplorer::BuildStepList *buildStepList) final;
QString id() const final { return "CMakeCommandBuilder"; }
QString displayName() const final { return tr("CMake"); }
QString defaultCommand() const final;
QString defaultArguments() const final;
QString setMultiProcessArg(QString args) final;
};
} // namespace Internal

View File

@@ -29,114 +29,37 @@ namespace IncrediBuild {
namespace Internal {
namespace Constants {
const QLatin1String CUSTOMCOMMANDBUILDER_COMMAND("IncrediBuild.BuildConsole.CustomCommandBuilder.Command");
const QLatin1String CUSTOMCOMMANDBUILDER_ARGS("IncrediBuild.BuildConsole.CustomCommandBuilder.Arguments");
const QLatin1String CUSTOMCOMMANDBUILDER_ARGSSET("IncrediBuild.BuildConsole.CustomCommandBuilder.ArgumentsSet");
const QLatin1String CUSTOMCOMMANDBUILDER_COMMAND("IncrediBuild.BuildConsole.%1.Command");
const QLatin1String CUSTOMCOMMANDBUILDER_ARGS("IncrediBuild.BuildConsole.%1.Arguments");
const QLatin1String CUSTOMCOMMANDBUILDER_ARGSSET("IncrediBuild.BuildConsole.%1.ArgumentsSet");
} // namespace Constants
QString CommandBuilder::fullCommandFlag()
void CommandBuilder::fromMap(const QVariantMap &map)
{
QString argsLine;
for (const QString &a : arguments())
argsLine += "\"" + a + "\" ";
if (!keepJobNum())
argsLine = setMultiProcessArg(argsLine);
QString fullCommand("\"%0\" %1");
fullCommand = fullCommand.arg(command(), argsLine);
return fullCommand;
}
void CommandBuilder::reset()
{
m_command.clear();
m_args.clear();
m_argsSet = false;
}
bool CommandBuilder::fromMap(const QVariantMap &map)
{
m_command = map.value(Constants::CUSTOMCOMMANDBUILDER_COMMAND, QVariant(QString())).toString();
m_argsSet = map.value(Constants::CUSTOMCOMMANDBUILDER_ARGSSET, QVariant(false)).toBool();
if (m_argsSet)
arguments(map.value(Constants::CUSTOMCOMMANDBUILDER_ARGS, QVariant(QString())).toString());
// Not loading m_keepJobNum as it is managed by the build step.
return true;
m_command = map.value(QString(Constants::CUSTOMCOMMANDBUILDER_COMMAND).arg(id())).toString();
m_args = map.value(QString(Constants::CUSTOMCOMMANDBUILDER_ARGS).arg(id())).toString();
}
void CommandBuilder::toMap(QVariantMap *map) const
{
(*map)[Constants::CUSTOMCOMMANDBUILDER_COMMAND] = QVariant(m_command);
(*map)[Constants::CUSTOMCOMMANDBUILDER_ARGSSET] = QVariant(m_argsSet);
if (m_argsSet)
(*map)[Constants::CUSTOMCOMMANDBUILDER_ARGS] = QVariant(m_args);
// Not saving m_keepJobNum as it is managed by the build step.
(*map)[QString(Constants::CUSTOMCOMMANDBUILDER_COMMAND).arg(id())] = QVariant(m_command);
(*map)[QString(Constants::CUSTOMCOMMANDBUILDER_ARGS).arg(id())] = QVariant(m_args);
}
// Split args to quoted or spaced parts
void CommandBuilder::arguments(const QString &argsLine)
void CommandBuilder::setCommand(const QString &command)
{
QStringList args;
QString currArg;
bool inQuote = false;
bool inArg = false;
for (int i = 0; i < argsLine.length(); ++i) {
QChar c = argsLine[i];
if (c.isSpace()) {
if (!inArg) // Spaces between arguments
continue;
if (command == defaultCommand())
m_command.clear();
else
m_command = command;
}
if (!inQuote) { // Arg termination
if (currArg.length() > 0) {
args.append(currArg);
currArg.clear();
}
inArg = false;
continue;
} else { // Space within argument
currArg += c;
continue;
}
}
inArg = true;
if (c == '"') {
if ((i < (argsLine.length() - 1))
&& (argsLine[i + 1] == '"')) { // Convert '""' to double-quote within arg
currArg += '"';
++i;
continue;
}
if (inQuote) { // Arg termination
if (currArg.length() > 0) {
args.append(currArg);
currArg.clear();
}
inArg = false;
inQuote = false;
continue;
}
// Arg start
inArg = true;
inQuote = true;
continue;
}
// Simple character
inArg = true;
currArg += c;
}
if (currArg.length() > 0)
args.append(currArg);
arguments(args);
void CommandBuilder::setArguments(const QString &arguments)
{
if (arguments == defaultArguments())
m_args.clear();
else
m_args = arguments;
}
} // namespace Internal

View File

@@ -27,50 +27,43 @@
#include <projectexplorer/buildsteplist.h>
#include <QObject>
#include <QCoreApplication>
namespace IncrediBuild {
namespace Internal {
class CommandBuilder
{
Q_DECLARE_TR_FUNCTIONS(IncrediBuild::Internal::CommandBuilder)
public:
CommandBuilder(ProjectExplorer::BuildStep *buildStep) : m_buildStep(buildStep) {}
virtual ~CommandBuilder() = default;
virtual bool canMigrate(ProjectExplorer::BuildStepList*) { return false; }
ProjectExplorer::BuildStep* buildStep() { return m_buildStep; }
ProjectExplorer::BuildStep *buildStep() const { return m_buildStep; }
virtual QString displayName() const { return QString("Custom Command"); }
virtual QString id() const { return "CustomCommandBuilder"; }
virtual QString displayName() const { return tr("Custom Command"); }
virtual bool fromMap(const QVariantMap &map);
virtual void fromMap(const QVariantMap &map);
virtual void toMap(QVariantMap *map) const;
virtual QString defaultCommand() { return QString(); }
virtual QStringList defaultArguments() { return QStringList(); }
virtual QString defaultCommand() const { return QString(); }
virtual QString defaultArguments() const { return QString(); }
virtual QString setMultiProcessArg(QString args) { return args; }
void reset();
QString command() { return m_command.isEmpty() ? defaultCommand() : m_command; }
void command(const QString &command) { m_command = command; }
void setCommand(const QString &command);
QStringList arguments() { return m_argsSet ? m_args : defaultArguments(); }
void arguments(const QStringList &arguments) { m_args = arguments; m_argsSet = !arguments.isEmpty(); }
void arguments(const QString &arguments);
bool keepJobNum() const { return m_keepJobNum; }
void keepJobNum(bool keepJobNum) { m_keepJobNum = keepJobNum; }
QString fullCommandFlag();
QString arguments() { return m_args.isEmpty() ? defaultArguments() : m_args; }
void setArguments(const QString &arguments);
private:
ProjectExplorer::BuildStep *m_buildStep{};
QString m_command{};
QStringList m_args{};
bool m_argsSet{false}; // Args may be overridden to empty
bool m_keepJobNum{false};
QString m_command;
QString m_args;
};
} // namespace Internal

View File

@@ -55,10 +55,8 @@ public:
m_cmakeCommandBuilder{step}
{}
void commandBuilderChanged();
const QStringList &supportedCommandBuilders();
void setCommandBuilder(const QString &commandBuilder);
void tryToMigrate();
void setActiveCommandBuilder(const QString &commandBuilderId);
BuildStep *m_buildStep;
CommandBuilder m_customCommandBuilder;
@@ -71,14 +69,15 @@ public:
&m_cmakeCommandBuilder
};
// Default to the first in list, which should be the "Custom Command"
CommandBuilder *m_activeCommandBuilder = m_commandBuilders[0];
bool m_loadedFromMap = false;
QPointer<QLabel> label;
QPointer<QLineEdit> makeArgumentsLineEdit;
QPointer<QComboBox> commandBuilder;
QPointer<PathChooser> makePathChooser;
QPointer<QLineEdit> makeArgumentsLineEdit;
};
CommandBuilderAspect::CommandBuilderAspect(BuildStep *step)
@@ -91,56 +90,29 @@ CommandBuilderAspect::~CommandBuilderAspect()
delete d;
}
CommandBuilder *CommandBuilderAspect::commandBuilder() const
QString CommandBuilderAspect::fullCommandFlag(bool keepJobNum) const
{
return d->m_activeCommandBuilder;
QString argsLine = d->m_activeCommandBuilder->arguments();
if (!keepJobNum)
argsLine = d->m_activeCommandBuilder->setMultiProcessArg(argsLine);
QString fullCommand("\"%0\" %1");
fullCommand = fullCommand.arg(d->m_activeCommandBuilder->command(), argsLine);
return fullCommand;
}
const QStringList& CommandBuilderAspectPrivate::supportedCommandBuilders()
{
static QStringList list;
if (list.empty()) {
for (CommandBuilder *p : m_commandBuilders)
list.push_back(p->displayName());
}
return list;
}
void CommandBuilderAspectPrivate::setCommandBuilder(const QString &commandBuilder)
void CommandBuilderAspectPrivate::setActiveCommandBuilder(const QString &commandBuilderId)
{
for (CommandBuilder *p : m_commandBuilders) {
if (p->displayName().compare(commandBuilder) == 0) {
if (p->id() == commandBuilderId) {
m_activeCommandBuilder = p;
break;
}
}
}
void CommandBuilderAspectPrivate::commandBuilderChanged()
{
setCommandBuilder(commandBuilder->currentText());
QString defaultArgs;
for (const QString &a : m_activeCommandBuilder->defaultArguments())
defaultArgs += "\"" + a + "\" ";
QString args;
for (const QString &a : m_activeCommandBuilder->arguments())
args += "\"" + a + "\" ";
if (args == defaultArgs)
makeArgumentsLineEdit->clear();
makeArgumentsLineEdit->setText(args);
const QString defaultCommand = m_activeCommandBuilder->defaultCommand();
makePathChooser->lineEdit()->setPlaceholderText(defaultCommand);
QString command = m_activeCommandBuilder->command();
if (command == defaultCommand)
command.clear();
makePathChooser->setPath(command);
}
void CommandBuilderAspectPrivate::tryToMigrate()
{
// This constructor is called when creating a fresh build step.
@@ -157,45 +129,33 @@ void CommandBuilderAspect::addToLayout(LayoutBuilder &builder)
{
if (!d->commandBuilder) {
d->commandBuilder = new QComboBox;
d->commandBuilder->addItems(d->supportedCommandBuilders());
d->commandBuilder->setCurrentText(d->m_activeCommandBuilder->displayName());
connect(d->commandBuilder, &QComboBox::currentTextChanged, this,
[this] { d->commandBuilderChanged(); });
for (CommandBuilder *p : d->m_commandBuilders)
d->commandBuilder->addItem(p->displayName());
connect(d->commandBuilder, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, [this](int idx) {
if (idx >= 0 && idx < int(sizeof(d->m_commandBuilders) / sizeof(d->m_commandBuilders[0])))
d->m_activeCommandBuilder = d->m_commandBuilders[idx];
updateGui();
});
}
if (!d->makePathChooser) {
d->makePathChooser = new PathChooser;
d->makeArgumentsLineEdit = new QLineEdit;
const QString defaultCommand = d->m_activeCommandBuilder->defaultCommand();
d->makePathChooser->lineEdit()->setPlaceholderText(defaultCommand);
const QString command = d->m_activeCommandBuilder->command();
if (command != defaultCommand)
d->makePathChooser->setPath(command);
d->makePathChooser->setExpectedKind(PathChooser::Kind::ExistingCommand);
d->makePathChooser->setBaseDirectory(FilePath::fromString(PathChooser::homePath()));
d->makePathChooser->setHistoryCompleter("IncrediBuild.BuildConsole.MakeCommand.History");
connect(d->makePathChooser, &PathChooser::rawPathChanged, this, [this] {
d->m_activeCommandBuilder->command(d->makePathChooser->rawPath());
d->m_activeCommandBuilder->setCommand(d->makePathChooser->rawPath());
updateGui();
});
}
QString defaultArgs;
for (const QString &a : d->m_activeCommandBuilder->defaultArguments())
defaultArgs += "\"" + a + "\" ";
QString args;
for (const QString &a : d->m_activeCommandBuilder->arguments())
args += "\"" + a + "\" ";
d->makeArgumentsLineEdit->setPlaceholderText(defaultArgs);
if (args != defaultArgs)
d->makeArgumentsLineEdit->setText(args);
connect(d->makeArgumentsLineEdit, &QLineEdit::textEdited, this, [this] {
d->m_activeCommandBuilder->arguments(d->makeArgumentsLineEdit->text());
if (!d->makeArgumentsLineEdit) {
d->makeArgumentsLineEdit = new QLineEdit;
connect(d->makeArgumentsLineEdit, &QLineEdit::textEdited, this, [this](const QString &arg) {
d->m_activeCommandBuilder->setArguments(arg);
updateGui();
});
}
if (!d->label) {
@@ -210,24 +170,47 @@ void CommandBuilderAspect::addToLayout(LayoutBuilder &builder)
builder.startNewRow().addItems(d->label.data(), d->commandBuilder.data());
builder.startNewRow().addItems(tr("Make command:"), d->makePathChooser.data());
builder.startNewRow().addItems(tr("Make arguments:"), d->makeArgumentsLineEdit.data());
updateGui();
}
void CommandBuilderAspect::fromMap(const QVariantMap &map)
{
d->m_loadedFromMap = true;
// Command builder. Default to the first in list, which should be the "Custom Command"
d->setCommandBuilder(map.value(settingsKey(), d->m_commandBuilders[0]->displayName()).toString());
d->m_activeCommandBuilder->fromMap(map);
d->setActiveCommandBuilder(map.value(settingsKey()).toString());
d->m_customCommandBuilder.fromMap(map);
d->m_makeCommandBuilder.fromMap(map);
d->m_cmakeCommandBuilder.fromMap(map);
updateGui();
}
void CommandBuilderAspect::toMap(QVariantMap &map) const
{
map[IncrediBuild::Constants::INCREDIBUILD_BUILDSTEP_TYPE]
= QVariant(IncrediBuild::Constants::BUILDCONSOLE_BUILDSTEP_ID);
map[settingsKey()] = QVariant(d->m_activeCommandBuilder->displayName());
map[settingsKey()] = QVariant(d->m_activeCommandBuilder->id());
d->m_activeCommandBuilder->toMap(&map);
d->m_customCommandBuilder.toMap(&map);
d->m_makeCommandBuilder.toMap(&map);
d->m_cmakeCommandBuilder.toMap(&map);
}
void CommandBuilderAspect::updateGui()
{
if (!d->commandBuilder)
return;
d->commandBuilder->setCurrentText(d->m_activeCommandBuilder->displayName());
const QString defaultCommand = d->m_activeCommandBuilder->defaultCommand();
d->makePathChooser->lineEdit()->setPlaceholderText(defaultCommand);
d->makePathChooser->setPath(d->m_activeCommandBuilder->command());
const QString defaultArgs = d->m_activeCommandBuilder->defaultArguments();
d->makeArgumentsLineEdit->setPlaceholderText(defaultArgs);
d->makeArgumentsLineEdit->setText(d->m_activeCommandBuilder->arguments());
}
// TextDisplay

View File

@@ -34,21 +34,21 @@
namespace IncrediBuild {
namespace Internal {
class CommandBuilder;
class CommandBuilderAspect final : public ProjectExplorer::ProjectConfigurationAspect
{
public:
explicit CommandBuilderAspect(ProjectExplorer::BuildStep *step);
~CommandBuilderAspect() final;
CommandBuilder *commandBuilder() const;
QString fullCommandFlag(bool keepJobNum) const;
private:
void addToLayout(ProjectExplorer::LayoutBuilder &builder) final;
void fromMap(const QVariantMap &map) final;
void toMap(QVariantMap &map) const final;
void updateGui();
class CommandBuilderAspectPrivate *d = nullptr;
};

View File

@@ -25,7 +25,6 @@
#include "ibconsolebuildstep.h"
#include "commandbuilder.h"
#include "commandbuilderaspect.h"
#include "incredibuildconstants.h"
@@ -132,9 +131,7 @@ bool IBConsoleBuildStep::init()
if (m_forceRemote->value())
args.append("--force-remote");
CommandBuilder *commandBuilder = m_commandBuilder->commandBuilder();
commandBuilder->keepJobNum(m_keepJobNum->value());
args.append(commandBuilder->fullCommandFlag());
args.append(m_commandBuilder->fullCommandFlag(m_keepJobNum->value()));
CommandLine cmdLine("ib_console", args);
ProcessParameters *procParams = processParameters();

View File

@@ -58,25 +58,21 @@ bool MakeCommandBuilder::canMigrate(BuildStepList *buildStepList)
return false;
}
QString MakeCommandBuilder::defaultCommand()
QString MakeCommandBuilder::defaultCommand() const
{
if (!m_defaultMake.isEmpty())
return m_defaultMake;
BuildConfiguration *buildConfig = buildStep()->buildConfiguration();
if (buildConfig) {
Target *target = buildStep()->target();
if (target) {
ToolChain *toolChain = ToolChainKitAspect::toolChain(target->kit(), ProjectExplorer::Constants::CXX_LANGUAGE_ID);
if (toolChain)
m_defaultMake = toolChain->makeCommand(buildConfig->environment()).toString();
return toolChain->makeCommand(buildConfig->environment()).toString();
}
}
return m_defaultMake;
return QString();
}
QString MakeCommandBuilder::setMultiProcessArg(QString args)
{
QString cmd = command();

View File

@@ -27,24 +27,20 @@
#include "commandbuilder.h"
#include <QCoreApplication>
namespace IncrediBuild {
namespace Internal {
class MakeCommandBuilder : public CommandBuilder
class MakeCommandBuilder final : public CommandBuilder
{
Q_DECLARE_TR_FUNCTIONS(MakeCommandBuilder)
public:
MakeCommandBuilder(ProjectExplorer::BuildStep *buildStep) : CommandBuilder(buildStep) {}
bool canMigrate(ProjectExplorer::BuildStepList *buildStepList) override;
QString displayName() const override { return tr("Make"); }
QString defaultCommand() override;
QString setMultiProcessArg(QString args) override;
private:
QString m_defaultMake{};
bool canMigrate(ProjectExplorer::BuildStepList *buildStepList) final;
QString id() const final { return "MakeCommandBuilder"; }
QString displayName() const final { return tr("Make"); }
QString defaultCommand() const final;
QString setMultiProcessArg(QString args) final;
};
} // namespace Internal