diff --git a/src/libs/utils/tasktree.cpp b/src/libs/utils/tasktree.cpp index 0ef5f18c624..e06abc80858 100644 --- a/src/libs/utils/tasktree.cpp +++ b/src/libs/utils/tasktree.cpp @@ -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() diff --git a/src/libs/utils/tasktree.h b/src/libs/utils/tasktree.h index c67238dc46e..fabf7fe4286 100644 --- a/src/libs/utils/tasktree.h +++ b/src/libs/utils/tasktree.h @@ -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; // Called prior to task start, just after createHandler - using TaskSetupHandler = std::function; + using TaskSetupHandler = std::function; // Called on task done / error using TaskEndHandler = std::function; // 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; using EndHandler = std::function; 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 + 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 + using IsDynamic = typename std::is_same, typename Adapter::Type &>>; + + template + using IsVoid = typename std::is_same, typename Adapter::Type &>>; + + template::value, bool> = true> + static TaskItem::TaskSetupHandler wrapSetup(SetupFunction &&function) { + return [=](TaskInterface &taskInterface) { Adapter &adapter = static_cast(taskInterface); - handler(*adapter.task()); + return std::invoke(function, *adapter.task()); }; }; + + template::value, bool> = true> + static TaskItem::TaskSetupHandler wrapSetup(SetupFunction &&function) { + return [=](TaskInterface &taskInterface) { + Adapter &adapter = static_cast(taskInterface); + std::invoke(function, *adapter.task()); + return TaskAction::Continue; + }; + }; + + template::value + && !IsVoid::value, bool> = true> + static TaskItem::TaskSetupHandler wrapSetup(SetupFunction &&) { + static_assert(IsDynamic::value || IsVoid::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 {}; diff --git a/tests/auto/utils/tasktree/tst_tasktree.cpp b/tests/auto/utils/tasktree/tst_tasktree.cpp index 3153354fd56..682eeb0e620 100644 --- a/tests/auto/utils/tasktree/tst_tasktree.cpp +++ b/tests/auto/utils/tasktree/tst_tasktree.cpp @@ -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 {