From 7f0ec081c60f4701241e447dfc91db9f65ebf764 Mon Sep 17 00:00:00 2001 From: Martchus Date: Fri, 2 Apr 2021 00:14:00 +0200 Subject: [PATCH] Allow specifying build tasks with more complicated build actions The new data structures (e.g. concurrent flag) aren't actually used yet. --- .../buildactions/buildactiontemplate.cpp | 58 +++++++++++++ librepomgr/buildactions/buildactiontemplate.h | 37 +++++++- librepomgr/serversetup.cpp | 10 +-- librepomgr/webapi/routes.cpp | 85 ++++++++++++------- 4 files changed, 151 insertions(+), 39 deletions(-) diff --git a/librepomgr/buildactions/buildactiontemplate.cpp b/librepomgr/buildactions/buildactiontemplate.cpp index fe48c8e..5c4fd52 100644 --- a/librepomgr/buildactions/buildactiontemplate.cpp +++ b/librepomgr/buildactions/buildactiontemplate.cpp @@ -10,3 +10,61 @@ using namespace CppUtilities; namespace LibRepoMgr { } // namespace LibRepoMgr + +namespace ReflectiveRapidJSON { +namespace JsonReflector { + +template <> +LIBREPOMGR_EXPORT void push( + const LibRepoMgr::BuildActionSequence &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) +{ + if (reflectable.name.empty() && !reflectable.concurrent) { + push(reflectable.actions, value, allocator); + } else { + push(static_cast(reflectable), value, allocator); + push(static_cast(reflectable), value, allocator); + } +} + +template <> +LIBREPOMGR_EXPORT void pull(LibRepoMgr::BuildActionSequence &reflectable, + const RAPIDJSON_NAMESPACE::GenericValue> &value, JsonDeserializationErrors *errors) +{ + if (value.IsArray()) { + reflectable.name.clear(); + reflectable.concurrent = false; + pull(reflectable.actions, value, errors); + } else if (value.IsObject()) { + pull(static_cast(reflectable), value, errors); + pull(static_cast(reflectable), value, errors); + } else if (errors) { + errors->reportTypeMismatch(value.GetType()); + } +} + +template <> +LIBREPOMGR_EXPORT void push(const LibRepoMgr::BuildActionSequenceNode &reflectable, + RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator) +{ + if (const auto *const name = std::get_if(&reflectable)) { + push(*name, value, allocator); + } else if (const auto *const sequence = std::get_if(&reflectable)) { + push(*sequence, value, allocator); + } +} + +template <> +LIBREPOMGR_EXPORT void pull(LibRepoMgr::BuildActionSequenceNode &reflectable, + const RAPIDJSON_NAMESPACE::GenericValue> &value, JsonDeserializationErrors *errors) +{ + if (value.IsString()) { + auto &name = reflectable.emplace(); + pull(name, value, errors); + } else { + auto &sequence = reflectable.emplace(); + pull(sequence, value, errors); + } +} + +} // namespace JsonReflector +} // namespace ReflectiveRapidJSON diff --git a/librepomgr/buildactions/buildactiontemplate.h b/librepomgr/buildactions/buildactiontemplate.h index 63c008f..687ee63 100644 --- a/librepomgr/buildactions/buildactiontemplate.h +++ b/librepomgr/buildactions/buildactiontemplate.h @@ -21,11 +21,22 @@ struct LIBREPOMGR_EXPORT BuildActionTemplate : public ReflectiveRapidJSON::JsonS CppUtilities::TimeSpan maxFrequency = CppUtilities::TimeSpan::infinity(); }; -struct LIBREPOMGR_EXPORT BuildTask : public ReflectiveRapidJSON::JsonSerializable { +struct BuildActionSequenceNode; +struct LIBREPOMGR_EXPORT BuildActionSequenceData : public ReflectiveRapidJSON::JsonSerializable { std::string name; + bool concurrent = false; +}; +struct LIBREPOMGR_EXPORT BuildActionSequenceNodes : public ReflectiveRapidJSON::JsonSerializable { + std::vector actions; +}; +struct LIBREPOMGR_EXPORT BuildActionSequence : public BuildActionSequenceData, public BuildActionSequenceNodes { +}; +struct LIBREPOMGR_EXPORT BuildActionSequenceNode : public std::variant { +}; + +struct LIBREPOMGR_EXPORT BuildTask : public BuildActionSequence, public ReflectiveRapidJSON::JsonSerializable { std::string desc; std::string category; - std::vector actions; CppUtilities::TimeSpan frequency = CppUtilities::TimeSpan::infinity(); }; @@ -36,4 +47,26 @@ struct LIBREPOMGR_EXPORT BuildPresets : public ReflectiveRapidJSON::JsonSerializ } // namespace LibRepoMgr +namespace ReflectiveRapidJSON { +namespace JsonReflector { + +// declare custom (de)serialization for BuildActionSequence +template <> +LIBREPOMGR_EXPORT void push( + const LibRepoMgr::BuildActionSequence &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator); +template <> +LIBREPOMGR_EXPORT void pull(LibRepoMgr::BuildActionSequence &reflectable, + const RAPIDJSON_NAMESPACE::GenericValue> &value, JsonDeserializationErrors *errors); + +// declare custom (de)serialization for BuildActionSequenceNode +template <> +LIBREPOMGR_EXPORT void push(const LibRepoMgr::BuildActionSequenceNode &reflectable, + RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator); +template <> +LIBREPOMGR_EXPORT void pull(LibRepoMgr::BuildActionSequenceNode &reflectable, + const RAPIDJSON_NAMESPACE::GenericValue> &value, JsonDeserializationErrors *errors); + +} // namespace JsonReflector +} // namespace ReflectiveRapidJSON + #endif // LIBREPOMGR_BUILD_ACTION_TEMPLATE_H diff --git a/librepomgr/serversetup.cpp b/librepomgr/serversetup.cpp index fc74955..0e5d72b 100644 --- a/librepomgr/serversetup.cpp +++ b/librepomgr/serversetup.cpp @@ -136,13 +136,13 @@ void ServiceSetup::BuildSetup::readPresets(const std::string &configFilePath, co errors.throwOn = ReflectiveRapidJSON::JsonDeserializationErrors::ThrowOn::All; presets = BuildPresets::fromJson(readFile(presetsFilePath), &errors); } catch (const ReflectiveRapidJSON::JsonDeserializationError &e) { - cerr << Phrases::ErrorMessage << "Unable to deserialize presets file " << presetsFilePath << Phrases::SubMessage - << ReflectiveRapidJSON::formatJsonDeserializationError(e) << Phrases::End; + cerr << Phrases::ErrorMessage << "Unable to deserialize presets file " << presetsFilePath << '\n' + << Phrases::SubMessage << ReflectiveRapidJSON::formatJsonDeserializationError(e) << Phrases::End; } catch (const RAPIDJSON_NAMESPACE::ParseResult &e) { - cerr << Phrases::ErrorMessage << "Unable to parse presets file " << presetsFilePath << Phrases::SubMessage << "parse error at " << e.Offset() - << ": " << RAPIDJSON_NAMESPACE::GetParseError_En(e.Code()) << Phrases::End; + cerr << Phrases::ErrorMessage << "Unable to parse presets file " << presetsFilePath << '\n' + << Phrases::SubMessage << "parse error at " << e.Offset() << ": " << RAPIDJSON_NAMESPACE::GetParseError_En(e.Code()) << Phrases::End; } catch (const std::runtime_error &e) { - cerr << Phrases::ErrorMessage << "Unable to read presets file " << presetsFilePath << Phrases::SubMessage << e.what() << Phrases::End; + cerr << Phrases::ErrorMessage << "Unable to read presets file " << presetsFilePath << '\n' << Phrases::SubMessage << e.what() << Phrases::End; } } diff --git a/librepomgr/webapi/routes.cpp b/librepomgr/webapi/routes.cpp index 0c84a0b..d2f732f 100644 --- a/librepomgr/webapi/routes.cpp +++ b/librepomgr/webapi/routes.cpp @@ -653,6 +653,53 @@ void postBuildAction(const Params ¶ms, ResponseHandler &&handler) handler(makeJson(params.request(), response)); } +static std::string allocateNewBuildAction(const BuildActionMetaInfo &metaInfo, const std::string &taskName, + const std::vector &packageNames, const std::string &directory, + const std::unordered_map &actionTemplates, std::vector> &newBuildActions, + const std::string &actionName) +{ + const auto actionTemplateIterator = actionTemplates.find(actionName); + if (actionTemplateIterator == actionTemplates.end()) { + return "the action \"" % actionName + "\" of the specified task is not configured"; + } + const auto &actionTemplate = actionTemplateIterator->second; + const auto buildActionType = actionTemplate.type; + if (!metaInfo.isTypeIdValid(buildActionType)) { + return argsToString( + "the type \"", static_cast(buildActionType), "\" of action \"", actionName, "\" of the specified task is invalid"); + } + const auto &typeInfo = metaInfo.typeInfoForId(actionTemplate.type); + auto &buildAction = newBuildActions.emplace_back(std::make_shared()); // a real ID is set later + buildAction->taskName = taskName; + buildAction->directory = !typeInfo.directory || directory.empty() ? actionTemplate.directory : directory; + buildAction->type = buildActionType; + buildAction->sourceDbs = actionTemplate.sourceDbs; + buildAction->destinationDbs = actionTemplate.destinationDbs; + buildAction->packageNames = !typeInfo.packageNames || packageNames.empty() ? actionTemplate.packageNames : packageNames; + buildAction->flags = actionTemplate.flags; + buildAction->settings = actionTemplate.settings; + return std::string(); +} + +static std::string allocateNewBuildActionSequence(const BuildActionMetaInfo &metaInfo, const std::string &taskName, + const std::vector &packageNames, const std::string &directory, + const std::unordered_map &actionTemplates, std::vector> &newBuildActions, + const BuildActionSequence &actionSequence) +{ + auto error = std::string(); + for (const auto &actionNode : actionSequence.actions) { + if (const auto *const actionName = std::get_if(&actionNode)) { + error = allocateNewBuildAction(metaInfo, taskName, packageNames, directory, actionTemplates, newBuildActions, *actionName); + } else if (const auto *const actionSequence = std::get_if(&actionNode)) { + error = allocateNewBuildActionSequence(metaInfo, taskName, packageNames, directory, actionTemplates, newBuildActions, *actionSequence); + } + if (!error.empty()) { + return error; + } + } + return error; +} + void postBuildActionsFromTask(const Params ¶ms, ResponseHandler &&handler, const std::string &taskName, const std::string &directory, const std::vector &startAfterIds, bool startImmediately) { @@ -681,7 +728,6 @@ void postBuildActionsFromTask(const Params ¶ms, ResponseHandler &&handler, c return; } const auto &task = taskIterator->second; - const auto &actionsToCreate = task.actions; const auto &actionTemplates = presets.templates; if (task.actions.empty()) { setupLock.unlock(); @@ -691,43 +737,18 @@ void postBuildActionsFromTask(const Params ¶ms, ResponseHandler &&handler, c // allocate a vector to store build actions (temporarily) in auto newBuildActions = std::vector>(); - newBuildActions.reserve(actionsToCreate.size()); + newBuildActions.reserve(task.actions.size()); // copy data from templates into new build actions auto &metaInfo = params.setup.building.metaInfo; auto metaInfoLock = metaInfo.lockToRead(); - for (const auto &actionName : actionsToCreate) { - const auto actionTemplateIterator = actionTemplates.find(actionName); - if (actionTemplateIterator == actionTemplates.end()) { - metaInfoLock.unlock(); - auto errorMessage = "the action \"" % actionName + "\" of the specified task is not configured"; - setupLock.unlock(); - handler(makeBadRequest(params.request(), std::move(errorMessage))); - return; - } - const auto &actionTemplate = actionTemplateIterator->second; - const auto buildActionType = actionTemplate.type; - if (!metaInfo.isTypeIdValid(buildActionType)) { - metaInfoLock.unlock(); - auto errorMessage = argsToString( - "the type \"", static_cast(buildActionType), "\" of action \"", actionName, "\" of the specified task is invalid"); - setupLock.unlock(); - handler(makeBadRequest(params.request(), std::move(errorMessage))); - return; - } - const auto &typeInfo = metaInfo.typeInfoForId(actionTemplate.type); - auto &buildAction = newBuildActions.emplace_back(std::make_shared()); // a real ID is set later - buildAction->taskName = taskName; - buildAction->directory = !typeInfo.directory || directory.empty() ? actionTemplate.directory : directory; - buildAction->type = buildActionType; - buildAction->sourceDbs = actionTemplate.sourceDbs; - buildAction->destinationDbs = actionTemplate.destinationDbs; - buildAction->packageNames = !typeInfo.packageNames || packageNames.empty() ? actionTemplate.packageNames : packageNames; - buildAction->flags = actionTemplate.flags; - buildAction->settings = actionTemplate.settings; - } + auto error = allocateNewBuildActionSequence(metaInfo, taskName, packageNames, directory, actionTemplates, newBuildActions, task); metaInfoLock.unlock(); setupLock.unlock(); + if (!error.empty()) { + handler(makeBadRequest(params.request(), std::move(error))); + return; + } // allocate build action IDs and populate "start after ID" BuildAction *lastBuildAction = nullptr;