Allow specifying build tasks with more complicated build actions

The new data structures (e.g. concurrent flag) aren't actually used yet.
This commit is contained in:
Martchus 2021-04-02 00:14:00 +02:00
parent fdf40aa917
commit 7f0ec081c6
4 changed files with 151 additions and 39 deletions

View File

@ -10,3 +10,61 @@ using namespace CppUtilities;
namespace LibRepoMgr {
} // namespace LibRepoMgr
namespace ReflectiveRapidJSON {
namespace JsonReflector {
template <>
LIBREPOMGR_EXPORT void push<LibRepoMgr::BuildActionSequence>(
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<const LibRepoMgr::BuildActionSequenceData &>(reflectable), value, allocator);
push(static_cast<const LibRepoMgr::BuildActionSequenceNodes &>(reflectable), value, allocator);
}
}
template <>
LIBREPOMGR_EXPORT void pull<LibRepoMgr::BuildActionSequence>(LibRepoMgr::BuildActionSequence &reflectable,
const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors)
{
if (value.IsArray()) {
reflectable.name.clear();
reflectable.concurrent = false;
pull(reflectable.actions, value, errors);
} else if (value.IsObject()) {
pull(static_cast<LibRepoMgr::BuildActionSequenceData &>(reflectable), value, errors);
pull(static_cast<LibRepoMgr::BuildActionSequenceNodes &>(reflectable), value, errors);
} else if (errors) {
errors->reportTypeMismatch<LibRepoMgr::BuildActionSequence>(value.GetType());
}
}
template <>
LIBREPOMGR_EXPORT void push<LibRepoMgr::BuildActionSequenceNode>(const LibRepoMgr::BuildActionSequenceNode &reflectable,
RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator)
{
if (const auto *const name = std::get_if<std::string>(&reflectable)) {
push(*name, value, allocator);
} else if (const auto *const sequence = std::get_if<LibRepoMgr::BuildActionSequence>(&reflectable)) {
push(*sequence, value, allocator);
}
}
template <>
LIBREPOMGR_EXPORT void pull<LibRepoMgr::BuildActionSequenceNode>(LibRepoMgr::BuildActionSequenceNode &reflectable,
const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors)
{
if (value.IsString()) {
auto &name = reflectable.emplace<std::string>();
pull(name, value, errors);
} else {
auto &sequence = reflectable.emplace<LibRepoMgr::BuildActionSequence>();
pull(sequence, value, errors);
}
}
} // namespace JsonReflector
} // namespace ReflectiveRapidJSON

View File

@ -21,11 +21,22 @@ struct LIBREPOMGR_EXPORT BuildActionTemplate : public ReflectiveRapidJSON::JsonS
CppUtilities::TimeSpan maxFrequency = CppUtilities::TimeSpan::infinity();
};
struct LIBREPOMGR_EXPORT BuildTask : public ReflectiveRapidJSON::JsonSerializable<BuildTask> {
struct BuildActionSequenceNode;
struct LIBREPOMGR_EXPORT BuildActionSequenceData : public ReflectiveRapidJSON::JsonSerializable<BuildActionSequenceData> {
std::string name;
bool concurrent = false;
};
struct LIBREPOMGR_EXPORT BuildActionSequenceNodes : public ReflectiveRapidJSON::JsonSerializable<BuildActionSequenceData> {
std::vector<BuildActionSequenceNode> actions;
};
struct LIBREPOMGR_EXPORT BuildActionSequence : public BuildActionSequenceData, public BuildActionSequenceNodes {
};
struct LIBREPOMGR_EXPORT BuildActionSequenceNode : public std::variant<std::string, BuildActionSequence> {
};
struct LIBREPOMGR_EXPORT BuildTask : public BuildActionSequence, public ReflectiveRapidJSON::JsonSerializable<BuildTask> {
std::string desc;
std::string category;
std::vector<std::string> 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<LibRepoMgr::BuildActionSequence>(
const LibRepoMgr::BuildActionSequence &reflectable, RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator);
template <>
LIBREPOMGR_EXPORT void pull<LibRepoMgr::BuildActionSequence>(LibRepoMgr::BuildActionSequence &reflectable,
const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors);
// declare custom (de)serialization for BuildActionSequenceNode
template <>
LIBREPOMGR_EXPORT void push<LibRepoMgr::BuildActionSequenceNode>(const LibRepoMgr::BuildActionSequenceNode &reflectable,
RAPIDJSON_NAMESPACE::Value &value, RAPIDJSON_NAMESPACE::Document::AllocatorType &allocator);
template <>
LIBREPOMGR_EXPORT void pull<LibRepoMgr::BuildActionSequenceNode>(LibRepoMgr::BuildActionSequenceNode &reflectable,
const RAPIDJSON_NAMESPACE::GenericValue<RAPIDJSON_NAMESPACE::UTF8<char>> &value, JsonDeserializationErrors *errors);
} // namespace JsonReflector
} // namespace ReflectiveRapidJSON
#endif // LIBREPOMGR_BUILD_ACTION_TEMPLATE_H

View File

@ -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;
}
}

View File

@ -653,6 +653,53 @@ void postBuildAction(const Params &params, ResponseHandler &&handler)
handler(makeJson(params.request(), response));
}
static std::string allocateNewBuildAction(const BuildActionMetaInfo &metaInfo, const std::string &taskName,
const std::vector<std::string> &packageNames, const std::string &directory,
const std::unordered_map<std::string, BuildActionTemplate> &actionTemplates, std::vector<std::shared_ptr<BuildAction>> &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<std::size_t>(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<BuildAction>()); // 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<std::string> &packageNames, const std::string &directory,
const std::unordered_map<std::string, BuildActionTemplate> &actionTemplates, std::vector<std::shared_ptr<BuildAction>> &newBuildActions,
const BuildActionSequence &actionSequence)
{
auto error = std::string();
for (const auto &actionNode : actionSequence.actions) {
if (const auto *const actionName = std::get_if<std::string>(&actionNode)) {
error = allocateNewBuildAction(metaInfo, taskName, packageNames, directory, actionTemplates, newBuildActions, *actionName);
} else if (const auto *const actionSequence = std::get_if<BuildActionSequence>(&actionNode)) {
error = allocateNewBuildActionSequence(metaInfo, taskName, packageNames, directory, actionTemplates, newBuildActions, *actionSequence);
}
if (!error.empty()) {
return error;
}
}
return error;
}
void postBuildActionsFromTask(const Params &params, ResponseHandler &&handler, const std::string &taskName, const std::string &directory,
const std::vector<BuildActionIdType> &startAfterIds, bool startImmediately)
{
@ -681,7 +728,6 @@ void postBuildActionsFromTask(const Params &params, 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 &params, ResponseHandler &&handler, c
// allocate a vector to store build actions (temporarily) in
auto newBuildActions = std::vector<std::shared_ptr<BuildAction>>();
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<std::size_t>(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<BuildAction>()); // 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;