forked from qt-creator/qt-creator
TaskTree: Implement dynamic setup for tasks
Before it was possible to dynamically setup groups only. This functionalily is probably good enough to replace Group's DynamicSetup, as it seems much more convenient. Change-Id: I56080f0ffa844847ca955cf52ccb07f3b4e4c731 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -200,7 +200,7 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
bool start();
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
bool isRunning() const;
|
bool isRunning() const;
|
||||||
bool isTask() const;
|
bool isTask() const;
|
||||||
@@ -375,7 +375,8 @@ void TaskContainer::start()
|
|||||||
const int childCount = m_selectedChildren.size();
|
const int childCount = m_selectedChildren.size();
|
||||||
const int startCount = m_parallelLimit ? qMin(m_parallelLimit, childCount) : childCount;
|
const int startCount = m_parallelLimit ? qMin(m_parallelLimit, childCount) : childCount;
|
||||||
for (int i = 0; i < startCount; ++i) {
|
for (int i = 0; i < startCount; ++i) {
|
||||||
if (!m_selectedChildren.at(i)->start()) // TODO: take m_groupConfig.action into account
|
m_selectedChildren.at(i)->start();
|
||||||
|
if (!isRunning())
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -544,19 +545,32 @@ void TaskContainer::deactivateStorages()
|
|||||||
m_parentContainer->deactivateStorages();
|
m_parentContainer->deactivateStorages();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TaskNode::start()
|
void TaskNode::start()
|
||||||
{
|
{
|
||||||
if (!isTask()) {
|
if (!isTask()) {
|
||||||
m_container.start();
|
m_container.start();
|
||||||
return true;
|
return;
|
||||||
}
|
}
|
||||||
|
const auto finalize = [this](bool success) {
|
||||||
|
m_container.m_taskTreePrivate->advanceProgress(1);
|
||||||
|
m_task.release()->deleteLater();
|
||||||
|
QTC_CHECK(m_container.m_parentContainer);
|
||||||
|
m_container.m_parentContainer->childDone(success);
|
||||||
|
};
|
||||||
m_task.reset(m_taskHandler.m_createHandler());
|
m_task.reset(m_taskHandler.m_createHandler());
|
||||||
|
{
|
||||||
|
TaskAction action = TaskAction::Continue;
|
||||||
{
|
{
|
||||||
StorageActivator activator(m_container);
|
StorageActivator activator(m_container);
|
||||||
GuardLocker locker(m_container.m_taskTreePrivate->m_guard);
|
GuardLocker locker(m_container.m_taskTreePrivate->m_guard);
|
||||||
m_taskHandler.m_setupHandler(*m_task.get());
|
action = m_taskHandler.m_setupHandler(*m_task.get());
|
||||||
}
|
}
|
||||||
connect(m_task.get(), &TaskInterface::done, this, [this](bool success) {
|
if (action != TaskAction::Continue) {
|
||||||
|
finalize(action == TaskAction::StopWithDone);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connect(m_task.get(), &TaskInterface::done, this, [=](bool success) {
|
||||||
{
|
{
|
||||||
StorageActivator activator(m_container);
|
StorageActivator activator(m_container);
|
||||||
if (success && m_taskHandler.m_doneHandler) {
|
if (success && m_taskHandler.m_doneHandler) {
|
||||||
@@ -567,16 +581,10 @@ bool TaskNode::start()
|
|||||||
m_taskHandler.m_errorHandler(*m_task.get());
|
m_taskHandler.m_errorHandler(*m_task.get());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_container.m_taskTreePrivate->advanceProgress(1);
|
finalize(success);
|
||||||
|
|
||||||
m_task.release()->deleteLater();
|
|
||||||
|
|
||||||
QTC_CHECK(m_container.m_parentContainer);
|
|
||||||
m_container.m_parentContainer->childDone(success);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
m_task->start();
|
m_task->start();
|
||||||
return m_task.get(); // In case of failed to start, done handler already released process
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void TaskNode::stop()
|
void TaskNode::stop()
|
||||||
|
@@ -109,6 +109,13 @@ enum class GroupAction
|
|||||||
StopWithError
|
StopWithError
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class TaskAction
|
||||||
|
{
|
||||||
|
Continue,
|
||||||
|
StopWithDone,
|
||||||
|
StopWithError
|
||||||
|
};
|
||||||
|
|
||||||
class GroupConfig
|
class GroupConfig
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -122,7 +129,7 @@ public:
|
|||||||
// Internal, provided by QTC_DECLARE_CUSTOM_TASK
|
// Internal, provided by QTC_DECLARE_CUSTOM_TASK
|
||||||
using TaskCreateHandler = std::function<TaskInterface *(void)>;
|
using TaskCreateHandler = std::function<TaskInterface *(void)>;
|
||||||
// Called prior to task start, just after createHandler
|
// Called prior to task start, just after createHandler
|
||||||
using TaskSetupHandler = std::function<void(TaskInterface &)>;
|
using TaskSetupHandler = std::function<TaskAction(TaskInterface &)>;
|
||||||
// Called on task done / error
|
// Called on task done / error
|
||||||
using TaskEndHandler = std::function<void(const TaskInterface &)>;
|
using TaskEndHandler = std::function<void(const TaskInterface &)>;
|
||||||
// Called when group entered / after group ended with success or failure
|
// Called when group entered / after group ended with success or failure
|
||||||
@@ -263,22 +270,47 @@ class CustomTask : public TaskItem
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
using Task = typename Adapter::Type;
|
using Task = typename Adapter::Type;
|
||||||
using SetupHandler = std::function<void(Task &)>;
|
|
||||||
using EndHandler = std::function<void(const Task &)>;
|
using EndHandler = std::function<void(const Task &)>;
|
||||||
static Adapter *createAdapter() { return new Adapter; }
|
static Adapter *createAdapter() { return new Adapter; }
|
||||||
CustomTask(const SetupHandler &setup, const EndHandler &done = {}, const EndHandler &error = {})
|
template <typename SetupFunction>
|
||||||
: TaskItem({&createAdapter, wrapSetup(setup),
|
CustomTask(SetupFunction &&function, const EndHandler &done = {}, const EndHandler &error = {})
|
||||||
wrapEnd(done), wrapEnd(error)}) {}
|
: TaskItem({&createAdapter, wrapSetup(function), wrapEnd(done), wrapEnd(error)}) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static TaskSetupHandler wrapSetup(const SetupHandler &handler) {
|
template<typename SetupFunction>
|
||||||
if (!handler)
|
using IsDynamic = typename std::is_same<TaskAction,
|
||||||
return {};
|
std::invoke_result_t<std::decay_t<SetupFunction>, typename Adapter::Type &>>;
|
||||||
return [handler](TaskInterface &taskInterface) {
|
|
||||||
|
template<typename SetupFunction>
|
||||||
|
using IsVoid = typename std::is_same<void,
|
||||||
|
std::invoke_result_t<std::decay_t<SetupFunction>, typename Adapter::Type &>>;
|
||||||
|
|
||||||
|
template<typename SetupFunction, std::enable_if_t<IsDynamic<SetupFunction>::value, bool> = true>
|
||||||
|
static TaskItem::TaskSetupHandler wrapSetup(SetupFunction &&function) {
|
||||||
|
return [=](TaskInterface &taskInterface) {
|
||||||
Adapter &adapter = static_cast<Adapter &>(taskInterface);
|
Adapter &adapter = static_cast<Adapter &>(taskInterface);
|
||||||
handler(*adapter.task());
|
return std::invoke(function, *adapter.task());
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename SetupFunction, std::enable_if_t<IsVoid<SetupFunction>::value, bool> = true>
|
||||||
|
static TaskItem::TaskSetupHandler wrapSetup(SetupFunction &&function) {
|
||||||
|
return [=](TaskInterface &taskInterface) {
|
||||||
|
Adapter &adapter = static_cast<Adapter &>(taskInterface);
|
||||||
|
std::invoke(function, *adapter.task());
|
||||||
|
return TaskAction::Continue;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename SetupFunction, std::enable_if_t<!IsDynamic<SetupFunction>::value
|
||||||
|
&& !IsVoid<SetupFunction>::value, bool> = true>
|
||||||
|
static TaskItem::TaskSetupHandler wrapSetup(SetupFunction &&) {
|
||||||
|
static_assert(IsDynamic<SetupFunction>::value || IsVoid<SetupFunction>::value,
|
||||||
|
"Task setup handler needs to take (Task &) as an argument and has to return "
|
||||||
|
"void or TaskAction. The passed handler doesn't fulfill these requirements.");
|
||||||
|
return {};
|
||||||
|
};
|
||||||
|
|
||||||
static TaskEndHandler wrapEnd(const EndHandler &handler) {
|
static TaskEndHandler wrapEnd(const EndHandler &handler) {
|
||||||
if (!handler)
|
if (!handler)
|
||||||
return {};
|
return {};
|
||||||
|
@@ -163,6 +163,11 @@ void tst_TaskTree::processTree_data()
|
|||||||
const auto rootError = [storage] {
|
const auto rootError = [storage] {
|
||||||
storage->m_log.append({-1, Handler::GroupError});
|
storage->m_log.append({-1, Handler::GroupError});
|
||||||
};
|
};
|
||||||
|
const auto setupDynamicProcess = [storage](QtcProcess &process, TaskAction action) {
|
||||||
|
Q_UNUSED(process)
|
||||||
|
storage->m_log.append({-1, Handler::Setup});
|
||||||
|
return action;
|
||||||
|
};
|
||||||
|
|
||||||
const Group emptyRoot {
|
const Group emptyRoot {
|
||||||
Storage(storage),
|
Storage(storage),
|
||||||
@@ -171,6 +176,25 @@ void tst_TaskTree::processTree_data()
|
|||||||
const Log emptyLog{{-1, Handler::GroupDone}};
|
const Log emptyLog{{-1, Handler::GroupDone}};
|
||||||
QTest::newRow("Empty") << emptyRoot << storage << emptyLog << false << true << 0;
|
QTest::newRow("Empty") << emptyRoot << storage << emptyLog << false << true << 0;
|
||||||
|
|
||||||
|
const Group dynamicTaskDoneRoot {
|
||||||
|
Storage(storage),
|
||||||
|
Process(std::bind(setupDynamicProcess, _1, TaskAction::StopWithDone)),
|
||||||
|
Process(std::bind(setupDynamicProcess, _1, TaskAction::StopWithDone))
|
||||||
|
};
|
||||||
|
const Log dynamicTaskDoneLog{{-1, Handler::Setup},
|
||||||
|
{-1, Handler::Setup}};
|
||||||
|
QTest::newRow("DynamicTaskDone") << dynamicTaskDoneRoot << storage << dynamicTaskDoneLog
|
||||||
|
<< false << true << 2;
|
||||||
|
|
||||||
|
const Group dynamicTaskErrorRoot {
|
||||||
|
Storage(storage),
|
||||||
|
Process(std::bind(setupDynamicProcess, _1, TaskAction::StopWithError)),
|
||||||
|
Process(std::bind(setupDynamicProcess, _1, TaskAction::StopWithError))
|
||||||
|
};
|
||||||
|
const Log dynamicTaskErrorLog{{-1, Handler::Setup}};
|
||||||
|
QTest::newRow("DynamicTaskError") << dynamicTaskErrorRoot << storage << dynamicTaskErrorLog
|
||||||
|
<< false << false << 2;
|
||||||
|
|
||||||
const Group nestedRoot {
|
const Group nestedRoot {
|
||||||
Storage(storage),
|
Storage(storage),
|
||||||
Group {
|
Group {
|
||||||
|
Reference in New Issue
Block a user