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();
|
||||
bool isRunning() const;
|
||||
bool isTask() const;
|
||||
@@ -375,7 +375,8 @@ void TaskContainer::start()
|
||||
const int childCount = m_selectedChildren.size();
|
||||
const int startCount = m_parallelLimit ? qMin(m_parallelLimit, childCount) : childCount;
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -544,19 +545,32 @@ void TaskContainer::deactivateStorages()
|
||||
m_parentContainer->deactivateStorages();
|
||||
}
|
||||
|
||||
bool TaskNode::start()
|
||||
void TaskNode::start()
|
||||
{
|
||||
if (!isTask()) {
|
||||
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());
|
||||
{
|
||||
StorageActivator activator(m_container);
|
||||
GuardLocker locker(m_container.m_taskTreePrivate->m_guard);
|
||||
m_taskHandler.m_setupHandler(*m_task.get());
|
||||
TaskAction action = TaskAction::Continue;
|
||||
{
|
||||
StorageActivator activator(m_container);
|
||||
GuardLocker locker(m_container.m_taskTreePrivate->m_guard);
|
||||
action = m_taskHandler.m_setupHandler(*m_task.get());
|
||||
}
|
||||
if (action != TaskAction::Continue) {
|
||||
finalize(action == TaskAction::StopWithDone);
|
||||
return;
|
||||
}
|
||||
}
|
||||
connect(m_task.get(), &TaskInterface::done, this, [this](bool success) {
|
||||
connect(m_task.get(), &TaskInterface::done, this, [=](bool success) {
|
||||
{
|
||||
StorageActivator activator(m_container);
|
||||
if (success && m_taskHandler.m_doneHandler) {
|
||||
@@ -567,16 +581,10 @@ bool TaskNode::start()
|
||||
m_taskHandler.m_errorHandler(*m_task.get());
|
||||
}
|
||||
}
|
||||
m_container.m_taskTreePrivate->advanceProgress(1);
|
||||
|
||||
m_task.release()->deleteLater();
|
||||
|
||||
QTC_CHECK(m_container.m_parentContainer);
|
||||
m_container.m_parentContainer->childDone(success);
|
||||
finalize(success);
|
||||
});
|
||||
|
||||
m_task->start();
|
||||
return m_task.get(); // In case of failed to start, done handler already released process
|
||||
}
|
||||
|
||||
void TaskNode::stop()
|
||||
|
@@ -109,6 +109,13 @@ enum class GroupAction
|
||||
StopWithError
|
||||
};
|
||||
|
||||
enum class TaskAction
|
||||
{
|
||||
Continue,
|
||||
StopWithDone,
|
||||
StopWithError
|
||||
};
|
||||
|
||||
class GroupConfig
|
||||
{
|
||||
public:
|
||||
@@ -122,7 +129,7 @@ public:
|
||||
// Internal, provided by QTC_DECLARE_CUSTOM_TASK
|
||||
using TaskCreateHandler = std::function<TaskInterface *(void)>;
|
||||
// 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
|
||||
using TaskEndHandler = std::function<void(const TaskInterface &)>;
|
||||
// Called when group entered / after group ended with success or failure
|
||||
@@ -263,22 +270,47 @@ class CustomTask : public TaskItem
|
||||
{
|
||||
public:
|
||||
using Task = typename Adapter::Type;
|
||||
using SetupHandler = std::function<void(Task &)>;
|
||||
using EndHandler = std::function<void(const Task &)>;
|
||||
static Adapter *createAdapter() { return new Adapter; }
|
||||
CustomTask(const SetupHandler &setup, const EndHandler &done = {}, const EndHandler &error = {})
|
||||
: TaskItem({&createAdapter, wrapSetup(setup),
|
||||
wrapEnd(done), wrapEnd(error)}) {}
|
||||
template <typename SetupFunction>
|
||||
CustomTask(SetupFunction &&function, const EndHandler &done = {}, const EndHandler &error = {})
|
||||
: TaskItem({&createAdapter, wrapSetup(function), wrapEnd(done), wrapEnd(error)}) {}
|
||||
|
||||
private:
|
||||
static TaskSetupHandler wrapSetup(const SetupHandler &handler) {
|
||||
if (!handler)
|
||||
return {};
|
||||
return [handler](TaskInterface &taskInterface) {
|
||||
template<typename SetupFunction>
|
||||
using IsDynamic = typename std::is_same<TaskAction,
|
||||
std::invoke_result_t<std::decay_t<SetupFunction>, typename Adapter::Type &>>;
|
||||
|
||||
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);
|
||||
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) {
|
||||
if (!handler)
|
||||
return {};
|
||||
|
@@ -163,6 +163,11 @@ void tst_TaskTree::processTree_data()
|
||||
const auto rootError = [storage] {
|
||||
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 {
|
||||
Storage(storage),
|
||||
@@ -171,6 +176,25 @@ void tst_TaskTree::processTree_data()
|
||||
const Log emptyLog{{-1, Handler::GroupDone}};
|
||||
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 {
|
||||
Storage(storage),
|
||||
Group {
|
||||
|
Reference in New Issue
Block a user