QbsBuildStep: Employ task tree for running

Task-number: QTCREATORBUG-29168
Change-Id: I508b7951f53f25f7cfd3e7f7e80086281cc7168e
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Jarek Kobus
2023-07-15 08:28:14 +02:00
parent 8423d2b5e5
commit e87a131c37
4 changed files with 107 additions and 230 deletions

View File

@@ -138,10 +138,6 @@ QbsBuildConfiguration::QbsBuildConfiguration(Target *target, Utils::Id id)
QbsBuildConfiguration::~QbsBuildConfiguration() QbsBuildConfiguration::~QbsBuildConfiguration()
{ {
for (BuildStep * const bs : buildSteps()->steps()) {
if (const auto qbs = qobject_cast<QbsBuildStep *>(bs))
qbs->dropSession();
}
delete m_buildSystem; delete m_buildSystem;
} }

View File

@@ -7,35 +7,29 @@
#include "qbsproject.h" #include "qbsproject.h"
#include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagerconstants.h"
#include "qbsprojectmanagertr.h" #include "qbsprojectmanagertr.h"
#include "qbsrequest.h"
#include "qbssession.h" #include "qbssession.h"
#include "qbssettings.h" #include "qbssettings.h"
#include <coreplugin/icore.h>
#include <projectexplorer/buildsteplist.h> #include <projectexplorer/buildsteplist.h>
#include <projectexplorer/kit.h>
#include <projectexplorer/projectexplorerconstants.h> #include <projectexplorer/projectexplorerconstants.h>
#include <projectexplorer/projectexplorertr.h> #include <projectexplorer/projectexplorertr.h>
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
#include <qtsupport/qtkitinformation.h> #include <qtsupport/qtkitinformation.h>
#include <qtsupport/qtversionmanager.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
#include <utils/aspects.h>
#include <utils/guard.h> #include <utils/guard.h>
#include <utils/layoutbuilder.h> #include <utils/layoutbuilder.h>
#include <utils/macroexpander.h> #include <utils/macroexpander.h>
#include <utils/outputformatter.h> #include <utils/outputformatter.h>
#include <utils/pathchooser.h> #include <utils/pathchooser.h>
#include <utils/process.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/variablechooser.h> #include <utils/variablechooser.h>
#include <QCheckBox> #include <QCheckBox>
#include <QJsonArray> #include <QJsonArray>
#include <QJsonObject> #include <QJsonObject>
#include <QLabel>
#include <QThread> #include <QThread>
// -------------------------------------------------------------------- // --------------------------------------------------------------------
@@ -51,10 +45,43 @@ const char QBS_CLEAN_INSTALL_ROOT[] = "Qbs.CleanInstallRoot";
using namespace ProjectExplorer; using namespace ProjectExplorer;
using namespace QtSupport; using namespace QtSupport;
using namespace Tasking;
using namespace Utils; using namespace Utils;
namespace QbsProjectManager { namespace QbsProjectManager::Internal {
namespace Internal {
class QbsParserTaskAdapter : public TaskAdapter<QPointer<QbsBuildSystem>>
{
public:
~QbsParserTaskAdapter()
{
QbsBuildSystem *buildSystem = *task();
if (buildSystem && m_isRunning)
buildSystem->cancelParsing();
}
void start() final
{
QbsBuildSystem *buildSystem = *task();
if (!buildSystem) {
emit done(false);
return;
}
m_isRunning = true;
connect(buildSystem->target(), &Target::parsingFinished, this, [this](bool success) {
m_isRunning = false;
emit done(success);
});
buildSystem->parseCurrentBuildConfiguration();
}
private:
bool m_isRunning = false;
};
} // namespace QbsProjectManager::Internal
TASKING_DECLARE_TASK(QbsParserTask, QbsProjectManager::Internal::QbsParserTaskAdapter);
namespace QbsProjectManager::Internal {
ArchitecturesAspect::ArchitecturesAspect(AspectContainer *container) ArchitecturesAspect::ArchitecturesAspect(AspectContainer *container)
: MultiSelectionAspect(container) : MultiSelectionAspect(container)
@@ -244,16 +271,9 @@ QbsBuildStep::QbsBuildStep(BuildStepList *bsl, Id id) :
setConfiguredArchitectures(selectedAbis.selectedArchitectures()); }); setConfiguredArchitectures(selectedAbis.selectedArchitectures()); });
} }
QbsBuildStep::~QbsBuildStep()
{
doCancel();
if (m_session)
m_session->disconnect(this);
}
bool QbsBuildStep::init() bool QbsBuildStep::init()
{ {
if (m_session) if (!BuildStep::init())
return false; return false;
auto bc = static_cast<QbsBuildConfiguration *>(buildConfiguration()); auto bc = static_cast<QbsBuildConfiguration *>(buildConfiguration());
@@ -274,27 +294,11 @@ void QbsBuildStep::setupOutputFormatter(OutputFormatter *formatter)
BuildStep::setupOutputFormatter(formatter); BuildStep::setupOutputFormatter(formatter);
} }
void QbsBuildStep::doRun()
{
// We need a pre-build parsing step in order not to lose project file changes done
// right before building (but before the delay has elapsed).
m_parsingAfterBuild = false;
parseProject();
}
QWidget *QbsBuildStep::createConfigWidget() QWidget *QbsBuildStep::createConfigWidget()
{ {
return new QbsBuildStepConfigWidget(this); return new QbsBuildStepConfigWidget(this);
} }
void QbsBuildStep::doCancel()
{
if (m_parsingProject)
qbsBuildSystem()->cancelParsing();
else if (m_session)
m_session->cancelCurrentJob();
}
QVariantMap QbsBuildStep::qbsConfiguration(VariableHandling variableHandling) const QVariantMap QbsBuildStep::qbsConfiguration(VariableHandling variableHandling) const
{ {
QVariantMap config = m_qbsConfiguration; QVariantMap config = m_qbsConfiguration;
@@ -386,91 +390,6 @@ void QbsBuildStep::toMap(QVariantMap &map) const
map.insert(QBS_CONFIG, m_qbsConfiguration); map.insert(QBS_CONFIG, m_qbsConfiguration);
} }
void QbsBuildStep::buildingDone(const ErrorInfo &error)
{
m_session->disconnect(this);
m_session = nullptr;
m_lastWasSuccess = !error.hasError();
for (const ErrorInfoItem &item : std::as_const(error.items)) {
createTaskAndOutput(
ProjectExplorer::Task::Error,
item.description,
item.filePath.toString(),
item.line);
}
// Building can uncover additional target artifacts.
qbsBuildSystem()->updateAfterBuild();
// The reparsing, if it is necessary, has to be done before finished() is emitted, as
// otherwise a potential additional build step could conflict with the parsing step.
if (qbsBuildSystem()->parsingScheduled()) {
m_parsingAfterBuild = true;
parseProject();
} else {
finish();
}
}
void QbsBuildStep::reparsingDone(bool success)
{
disconnect(target(), &Target::parsingFinished, this, &QbsBuildStep::reparsingDone);
m_parsingProject = false;
if (m_parsingAfterBuild) {
finish();
} else if (!success) {
m_lastWasSuccess = false;
finish();
} else {
build();
}
}
void QbsBuildStep::handleTaskStarted(const QString &desciption, int max)
{
m_currentTask = desciption;
m_maxProgress = max;
}
void QbsBuildStep::handleProgress(int value)
{
if (m_maxProgress > 0)
emit progress(value * 100 / m_maxProgress, m_currentTask);
}
void QbsBuildStep::handleCommandDescription(const QString &message)
{
emit addOutput(message, OutputFormat::Stdout);
}
void QbsBuildStep::handleProcessResult(
const FilePath &executable,
const QStringList &arguments,
const FilePath &workingDir,
const QStringList &stdOut,
const QStringList &stdErr,
bool success)
{
Q_UNUSED(workingDir);
const bool hasOutput = !stdOut.isEmpty() || !stdErr.isEmpty();
if (success && !hasOutput)
return;
emit addOutput(executable.toUserOutput() + ' ' + ProcessArgs::joinArgs(arguments),
OutputFormat::Stdout);
for (const QString &line : stdErr)
emit addOutput(line, OutputFormat::Stderr);
for (const QString &line : stdOut)
emit addOutput(line, OutputFormat::Stdout);
}
void QbsBuildStep::createTaskAndOutput(ProjectExplorer::Task::TaskType type, const QString &message,
const QString &file, int line)
{
emit addOutput(message, OutputFormat::Stdout);
emit addTask(CompileTask(type, message, FilePath::fromString(file), line), 1);
}
QString QbsBuildStep::buildVariant() const QString QbsBuildStep::buildVariant() const
{ {
return qbsConfiguration(PreserveVariables).value(Constants::QBS_CONFIG_VARIANT_KEY).toString(); return qbsConfiguration(PreserveVariables).value(Constants::QBS_CONFIG_VARIANT_KEY).toString();
@@ -496,59 +415,70 @@ QString QbsBuildStep::profile() const
return qbsConfiguration(PreserveVariables).value(Constants::QBS_CONFIG_PROFILE_KEY).toString(); return qbsConfiguration(PreserveVariables).value(Constants::QBS_CONFIG_PROFILE_KEY).toString();
} }
void QbsBuildStep::parseProject() GroupItem QbsBuildStep::runRecipe()
{ {
m_parsingProject = true; const auto onPreParserSetup = [this](QPointer<QbsBuildSystem> &buildSystem) {
connect(target(), &Target::parsingFinished, this, &QbsBuildStep::reparsingDone); buildSystem = qbsBuildSystem();
qbsBuildSystem()->parseCurrentBuildConfiguration(); };
} const auto onBuildSetup = [this](QbsRequest &request) {
QbsSession *session = static_cast<QbsBuildSystem*>(buildSystem())->session();
void QbsBuildStep::build() if (!session) {
{ emit addOutput(Tr::tr("No qbs session exists for this target."),
m_session = qbsBuildSystem()->session();
if (!m_session) {
emit addOutput(QbsProjectManager::Tr::tr("No qbs session exists for this target."),
OutputFormat::ErrorMessage); OutputFormat::ErrorMessage);
emit finished(false); return SetupResult::StopWithError;
return;
} }
QJsonObject requestData;
QJsonObject request; requestData.insert("type", "build-project");
request.insert("type", "build-project"); requestData.insert("max-job-count", maxJobs());
request.insert("max-job-count", maxJobs()); requestData.insert("keep-going", keepGoing());
request.insert("keep-going", keepGoing()); requestData.insert("command-echo-mode", showCommandLines() ? "command-line" : "summary");
request.insert("command-echo-mode", showCommandLines() ? "command-line" : "summary"); requestData.insert("install", install());
request.insert("install", install()); QbsSession::insertRequestedModuleProperties(requestData);
QbsSession::insertRequestedModuleProperties(request); requestData.insert("clean-install-root", cleanInstallRoot());
request.insert("clean-install-root", cleanInstallRoot());
if (!m_products.isEmpty()) if (!m_products.isEmpty())
request.insert("products", QJsonArray::fromStringList(m_products)); requestData.insert("products", QJsonArray::fromStringList(m_products));
if (!m_changedFiles.isEmpty()) { if (!m_changedFiles.isEmpty()) {
const auto changedFilesArray = QJsonArray::fromStringList(m_changedFiles); const auto changedFilesArray = QJsonArray::fromStringList(m_changedFiles);
request.insert("changed-files", changedFilesArray); requestData.insert("changed-files", changedFilesArray);
request.insert("files-to-consider", changedFilesArray); requestData.insert("files-to-consider", changedFilesArray);
} }
if (!m_activeFileTags.isEmpty()) if (!m_activeFileTags.isEmpty())
request.insert("active-file-tags", QJsonArray::fromStringList(m_activeFileTags)); requestData.insert("active-file-tags", QJsonArray::fromStringList(m_activeFileTags));
request.insert("data-mode", "only-if-changed"); requestData.insert("data-mode", "only-if-changed");
m_session->sendRequest(request); request.setSession(session);
m_maxProgress = 0; request.setRequestData(requestData);
connect(m_session, &QbsSession::projectBuilt, this, &QbsBuildStep::buildingDone); connect(&request, &QbsRequest::progressChanged, this, &BuildStep::progress);
connect(m_session, &QbsSession::taskStarted, this, &QbsBuildStep::handleTaskStarted); connect(&request, &QbsRequest::outputAdded, this,
connect(m_session, &QbsSession::taskProgress, this, &QbsBuildStep::handleProgress); [this](const QString &output, OutputFormat format) {
connect(m_session, &QbsSession::commandDescription, emit addOutput(output, format);
this, &QbsBuildStep::handleCommandDescription);
connect(m_session, &QbsSession::processResult, this, &QbsBuildStep::handleProcessResult);
connect(m_session, &QbsSession::errorOccurred, this, [this] {
buildingDone(ErrorInfo(QbsProjectManager::Tr::tr("Build canceled: Qbs session failed.")));
}); });
} connect(&request, &QbsRequest::taskAdded, this, [this](const Task &task) {
emit addTask(task, 1);
});
return SetupResult::Continue;
};
const auto onPostParserSetup = [this](QPointer<QbsBuildSystem> &buildSystem) {
buildSystem = qbsBuildSystem();
return buildSystem->parsingScheduled() ? SetupResult::Continue : SetupResult::StopWithDone;
};
void QbsBuildStep::finish() const Group root {
{ // We need a pre-build parsing step in order not to lose project file changes done
m_session = nullptr; // right before building (but before the delay has elapsed).
emit finished(m_lastWasSuccess); QbsParserTask(onPreParserSetup),
Group {
continueOnError,
QbsRequestTask(onBuildSetup),
// Building can uncover additional target artifacts.
Sync([this] { qbsBuildSystem()->updateAfterBuild(); }),
// The reparsing, if it is necessary, has to be done before done() is emitted, as
// otherwise a potential additional build step could conflict with the parsing step.
QbsParserTask(onPostParserSetup)
}
};
return root;
} }
void QbsBuildStep::updateState() void QbsBuildStep::updateState()
@@ -589,16 +519,6 @@ QbsBuildStepData QbsBuildStep::stepData() const
return data; return data;
} }
void QbsBuildStep::dropSession()
{
if (m_session) {
doCancel();
m_session->disconnect(this);
m_session = nullptr;
}
}
// -------------------------------------------------------------------- // --------------------------------------------------------------------
// QbsBuildStepConfigWidget: // QbsBuildStepConfigWidget:
// -------------------------------------------------------------------- // --------------------------------------------------------------------
@@ -857,7 +777,6 @@ QbsBuildStepFactory::QbsBuildStepFactory()
setSupportedProjectType(Constants::PROJECT_ID); setSupportedProjectType(Constants::PROJECT_ID);
} }
} // namespace Internal } // namespace QbsProjectManager::Internal
} // namespace QbsProjectManager
#include "qbsbuildstep.moc" #include "qbsbuildstep.moc"

View File

@@ -3,19 +3,13 @@
#pragma once #pragma once
#include "qbsbuildconfiguration.h"
#include <projectexplorer/buildstep.h> #include <projectexplorer/buildstep.h>
#include <projectexplorer/task.h>
namespace QbsProjectManager { namespace QbsProjectManager::Internal {
namespace Internal {
class ErrorInfo;
class QbsProject;
class QbsSession;
class QbsBuildStepConfigWidget; class QbsBuildStepConfigWidget;
class QbsBuildStepData;
class QbsBuildSystem;
class ArchitecturesAspect : public Utils::MultiSelectionAspect class ArchitecturesAspect : public Utils::MultiSelectionAspect
{ {
@@ -50,7 +44,6 @@ public:
}; };
QbsBuildStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id); QbsBuildStep(ProjectExplorer::BuildStepList *bsl, Utils::Id id);
~QbsBuildStep() override;
QVariantMap qbsConfiguration(VariableHandling variableHandling) const; QVariantMap qbsConfiguration(VariableHandling variableHandling) const;
void setQbsConfiguration(const QVariantMap &config); void setQbsConfiguration(const QVariantMap &config);
@@ -70,11 +63,6 @@ public:
Utils::BoolAspect forceProbes{this}; Utils::BoolAspect forceProbes{this};
Utils::StringAspect commandLine{this}; Utils::StringAspect commandLine{this};
QbsBuildSystem *qbsBuildSystem() const;
QbsBuildStepData stepData() const;
void dropSession();
signals: signals:
void qbsConfigurationChanged(); void qbsConfigurationChanged();
void qbsBuildOptionsChanged(); void qbsBuildOptionsChanged();
@@ -82,36 +70,17 @@ signals:
private: private:
bool init() override; bool init() override;
void setupOutputFormatter(Utils::OutputFormatter *formatter) override; void setupOutputFormatter(Utils::OutputFormatter *formatter) override;
void doRun() override; Tasking::GroupItem runRecipe() final;
void doCancel() override;
QWidget *createConfigWidget() override; QWidget *createConfigWidget() override;
void fromMap(const QVariantMap &map) override; void fromMap(const QVariantMap &map) override;
void toMap(QVariantMap &map) const override; void toMap(QVariantMap &map) const override;
void buildingDone(const ErrorInfo &error); QbsBuildSystem *qbsBuildSystem() const;
void reparsingDone(bool success); QbsBuildStepData stepData() const;
void handleTaskStarted(const QString &desciption, int max);
void handleProgress(int value);
void handleCommandDescription(const QString &message);
void handleProcessResult(
const Utils::FilePath &executable,
const QStringList &arguments,
const Utils::FilePath &workingDir,
const QStringList &stdOut,
const QStringList &stdErr,
bool success);
void createTaskAndOutput(ProjectExplorer::Task::TaskType type,
const QString &message, const QString &file, int line);
void setBuildVariant(const QString &variant); void setBuildVariant(const QString &variant);
void setConfiguredArchitectures(const QStringList &architectures); void setConfiguredArchitectures(const QStringList &architectures);
QString profile() const; QString profile() const;
void parseProject();
void build();
void finish();
void updateState(); void updateState();
QStringList configuredArchitectures() const; QStringList configuredArchitectures() const;
@@ -122,14 +91,6 @@ private:
QStringList m_activeFileTags; QStringList m_activeFileTags;
QStringList m_products; QStringList m_products;
QbsSession *m_session = nullptr;
QString m_currentTask;
int m_maxProgress;
bool m_lastWasSuccess;
bool m_parsingProject = false;
bool m_parsingAfterBuild = false;
friend class QbsBuildStepConfigWidget; friend class QbsBuildStepConfigWidget;
}; };
@@ -139,5 +100,4 @@ public:
QbsBuildStepFactory(); QbsBuildStepFactory();
}; };
} // namespace Internal } // namespace QbsProjectManager::Internal
} // namespace QbsProjectManager

View File

@@ -3,7 +3,9 @@
#include "qbsinstallstep.h" #include "qbsinstallstep.h"
#include "qbsbuildconfiguration.h"
#include "qbsbuildstep.h" #include "qbsbuildstep.h"
#include "qbsproject.h"
#include "qbsprojectmanagerconstants.h" #include "qbsprojectmanagerconstants.h"
#include "qbsprojectmanagertr.h" #include "qbsprojectmanagertr.h"
#include "qbsrequest.h" #include "qbsrequest.h"