From 4843fcc77fa5bc761da39dd848c4f03d04dc1da9 Mon Sep 17 00:00:00 2001 From: blue Date: Sat, 30 Sep 2023 17:13:44 -0300 Subject: [PATCH] some progress over building --- src/collection.cpp | 32 ++++++-- src/collection.h | 4 +- src/component.cpp | 193 ++++++++++++++++++++++++++++++++++++++++++--- src/component.h | 16 +++- src/download.cpp | 1 - src/main.cpp | 8 +- test/mason.json | 1 + 7 files changed, 228 insertions(+), 27 deletions(-) diff --git a/src/collection.cpp b/src/collection.cpp index 5a015c5..77cca49 100644 --- a/src/collection.cpp +++ b/src/collection.cpp @@ -12,12 +12,11 @@ Collection::Collection( const std::shared_ptr& taskManager ): Loggable(logger), - std::enable_shared_from_this(), mutex(), destination(destination), target(target), taskManager(taskManager), - root(std::make_unique(location, shared_from_this(), logger)), + root(std::make_unique(location, this, logger)), librariesPath(), knownSources(), successSources(), @@ -35,7 +34,7 @@ Collection::Collection( } root->read(); - if (root->successfullyRead()) + if (!root->successfullyRead()) throw std::runtime_error("Error reading project in " + location.string()); if (root->getType() != Component::mason) @@ -73,6 +72,7 @@ void Collection::addSource(const std::string& source) { case Component::building: case Component::done: processSource(source); + break; case Component::error: throw std::runtime_error("Project in " + root->getLocation().string() + " doesn't seem to be a valid mason project"); } @@ -109,7 +109,7 @@ void Collection::sourceRead(const std::string& source, TaskManager::Error err) { return; } - if (itr->second->successfullyRead()) { + if (!itr->second->successfullyRead()) { error("Source " + source + " had an error during read process, canceling build of this component"); readingSources.erase(itr); return; @@ -127,19 +127,34 @@ void Collection::sourceRead(const std::string& source, TaskManager::Error err) { void Collection::sourceBuilt(const std::string& source, TaskManager::Error err) { std::lock_guard lock(mutex); - debug("Source " + source + " has been built"); Components::iterator itr = buildingSources.find(source); + bool success = itr->second->successfullyBuilt(); buildingSources.erase(itr); if (err.has_value()) { error("Coundn't read " + source + ": " + err.value()->what()); return; } + if (!success) { + error("Source " + source + " had an error during build process"); + return; + } + + debug("Source " + source + " has been built"); successSources.emplace(source); } void Collection::rootBuilt(TaskManager::Error err) { + std::string loc = root->getLocation(); + if (err.has_value()) { + error("Coundn't build project at" + loc + ": " + err.value()->what()); + return; + } + if (root->successfullyBuilt()) + info("Project at " + loc + " was successfully built"); + else + error("Project at " + loc + " had some errors during build process"); } bool Collection::isRemote(const std::string& source) { @@ -166,7 +181,7 @@ void Collection::queueRead(const std::string& source, const std::filesystem::pat std::pair res = readingSources.emplace(std::piecewise_construct, std::forward_as_tuple(source), std::forward_as_tuple( - std::make_unique(location, shared_from_this(), logger) + std::make_unique(location, this, logger) ) ); @@ -201,6 +216,11 @@ unsigned int Collection::sourcesError() const { return _sourcesError(); } +bool Collection::success() const { + std::lock_guard lock(mutex); + return _sourcesTotal() == _sourcesSuccess() && root->successfullyBuilt(); +} + bool Collection::_hasSource(const std::string& source) const { return knownSources.count(source) > 0; } diff --git a/src/collection.h b/src/collection.h index 0d555c7..970b94e 100644 --- a/src/collection.h +++ b/src/collection.h @@ -15,7 +15,7 @@ #include "download.h" #include "taskmanager.h" -class Collection : protected Loggable, public std::enable_shared_from_this { +class Collection : protected Loggable { using Sources = std::set; using SourcesQueue = std::queue; using Downloads = std::map>; @@ -30,6 +30,8 @@ public: const std::shared_ptr& taskManager ); + bool success() const; + unsigned int sourcesTotal() const; unsigned int sourcesPending() const; unsigned int sourcesError() const; diff --git a/src/component.cpp b/src/component.cpp index 9f624dc..b4aabe9 100644 --- a/src/component.cpp +++ b/src/component.cpp @@ -3,7 +3,25 @@ #include "collection.h" constexpr std::string_view masonJSON("mason.json"); + constexpr std::string_view dependencies("dependencies"); + +constexpr std::array sourceVariants({ + "source", + "src", + "from", + "in", + "file" +}); + +constexpr std::array targetVariants({ + "target", + "dst", + "to", + "out", + "destination" +}); + constexpr std::array stringStates { "initial", "reading", @@ -22,7 +40,7 @@ constexpr std::array stringTypes { Component::Component( const std::filesystem::path& path, - const std::weak_ptr& collection, + Collection* collection, const std::shared_ptr& logger ): Loggable(logger), @@ -101,10 +119,9 @@ bool Component::readMasonDependencies(const nlohmann::json& deps, const std::str return errorScenario(manifestPath + " dependencies are not organized as an array"); for (const nlohmann::json& dep : deps) { - std::shared_ptr col = collection.lock(); switch (dep.type()) { case nlohmann::json::value_t::string: - col->addSource(dep); + collection->addSource(dep); break; case nlohmann::json::value_t::object: { nlohmann::json::const_iterator ditr = dep.find("path"); @@ -115,7 +132,7 @@ bool Component::readMasonDependencies(const nlohmann::json& deps, const std::str if (!path.is_string()) return errorScenario(manifestPath + " object of unexpected format in dependecies"); - col->addSource(path); + collection->addSource(path); } break; default: @@ -146,7 +163,7 @@ void Component::build(const std::filesystem::path& destination, const std::strin buildAsFile(destination); break; case directory: - buildAsDiractory(destination); + buildAsDirectory(destination); break; case mason: buildAsMason(destination, target); @@ -154,19 +171,21 @@ void Component::build(const std::filesystem::path& destination, const std::strin default: break; } +} + +void Component::buildAsFile(const std::filesystem::path& destination) { + copyFile(location, destination/location.filename()); state = done; } -void Component::buildAsFile(const std::filesystem::path& destination) { - std::filesystem::copy(location, destination/location.filename()); -} - -void Component::buildAsDiractory(const std::filesystem::path& destination) { +void Component::buildAsDirectory(const std::filesystem::path& destination) { std::filesystem::copy(location, destination/location.filename(), std::filesystem::copy_options::recursive | std::filesystem::copy_options::overwrite_existing ); + + state = done; } std::string Component::getLibrariesPath() const { @@ -196,8 +215,160 @@ bool Component::successfullyRead() const { return state > reading && state != error; } +bool Component::successfullyBuilt() const { + return state == done; +} + void Component::buildAsMason(const std::filesystem::path& destination, const std::string& target) { - warn("mason build is not implemented yet"); + const nlohmann::json& mnfst = manifest.value(); + nlohmann::json::const_iterator itr = mnfst.find("build"); + if (itr == mnfst.end()) { + warn("Mason project at " + location.string() + " has no \"build\" section in its manifest"); + state = done; + return; + } + + const nlohmann::json& build = *itr; + if (!build.is_array()) { + warn("Mason project at " + location.string() + " has unexpected \"build\" section in its manifest"); + state = done; + return; + } + + for (const nlohmann::json& entry : build) { + switch (entry.type()) { + case nlohmann::json::value_t::string: + copyFile(location/entry, destination/entry); + break; + case nlohmann::json::value_t::array: + masonFilesArray(entry, destination); + break; + case nlohmann::json::value_t::object: + if (allowedForTarget(entry, target)) { + nlohmann::json::const_iterator fitr = entry.find("files"); + if (fitr != entry.end()) { + const nlohmann::json& files = *fitr; + if (files.is_array()) + masonFilesArray(files, destination); + else + warn(std::string("Unexpected ") + entry.type_name() + + " of files subsection of build section of its manifest in " + + location.string() + ", ignoring"); + } + + } + break; + default: + warn(std::string("Unexpected ") + entry.type_name() + " in build section of manifest in " + location.string() + ", ignoring"); + break; + } + } + + state = done; +} + +void Component::copyFile(const std::filesystem::path& from, const std::filesystem::path& to) const { + std::filesystem::create_directories(to.parent_path()); + + if (std::filesystem::exists(to)) + minor("File " + to.string() + " already exists, overwriting from " + from.string()); + else + debug("Copying " + from.string() + " to " + to.string()); + + std::filesystem::copy(from, to, std::filesystem::copy_options::overwrite_existing); +} + +void Component::masonFilesArray(const nlohmann::json& files, const std::filesystem::path& destination) { + for (const nlohmann::json& entry : files) { + switch (entry.type()) { + case nlohmann::json::value_t::string: + copyFile(location/entry, destination/entry); + break; + case nlohmann::json::value_t::object: { + std::optional src = getFileSource(entry); + if (!src.has_value()) { + warn("Couldn't understand file source processing file array in " + location.string() + ", ignoring"); + break; + } + std::optional dst = getFileTarget(entry); + if (!src.has_value()) { + warn("Couldn't understand file destination processing file array in " + location.string() + ", ignoring"); + break; + } + + copyFile(location/src.value(), destination/src.value()); + } break; + default: + warn(std::string("Unexpected ") + entry.type_name() + + " in files subsection of build section of manifest in " + + location.string() + ", ignoring"); + } + } +} + +bool Component::allowedForTarget(const nlohmann::json& object, const std::string& target) const { + nlohmann::json::const_iterator itr = object.find("target"); + if (itr != object.end()) { + const nlohmann::json& tg = *itr; + if (tg.is_string()) + return tg == target; + else + warn("\"target\" in build section has unexpected type, ignoring"); + } + + itr = object.find("targets"); + if (itr != object.end()) { + const nlohmann::json& targets = *itr; + if (targets.is_array()) { + for (const nlohmann::json& entry : targets) { + if (entry.is_string()) { + if (entry == target) + return true; + } else { + warn("one of the targets in \"targets\" in build section has unexpected type, ignoring it"); + } + } + + return false; + } else { + warn("\"targets\" in build section has unexpected type, ignoring"); + } + } + + //TODO may be notTarget and notTargets? + + return true; +} + +std::optional Component::getFileSource(const nlohmann::json& object) const { + for (const std::string_view& key : sourceVariants) { + std::optional value = tryStringValue(object, key.data()); + if (value.has_value()) + return value; + } + + return std::nullopt; +} + +std::optional Component::getFileTarget(const nlohmann::json& object) const { + for (const std::string_view& key : targetVariants) { + std::optional value = tryStringValue(object, key.data()); + if (value.has_value()) + return value; + } + + return std::nullopt; +} + +std::optional Component::tryStringValue(const nlohmann::json& object, const std::string& key) { + nlohmann::json::const_iterator itr = object.find(key); + if (itr != object.end()) { + const nlohmann::json& value = *itr; + if (value.is_string()) + return value; + } + + return std::nullopt; } Component::State Component::getState() const { diff --git a/src/component.h b/src/component.h index 736d294..197480c 100644 --- a/src/component.h +++ b/src/component.h @@ -38,7 +38,7 @@ public: Component( const std::filesystem::path& path, - const std::weak_ptr& collection, + Collection* collection, const std::shared_ptr& logger ); @@ -47,6 +47,7 @@ public: std::filesystem::path getLocation() const; std::string getLibrariesPath() const; bool successfullyRead() const; + bool successfullyBuilt() const; void read(); void build(const std::filesystem::path& destination, const std::string& target); @@ -57,15 +58,24 @@ private: bool errorScenario(const std::string& message); void buildAsFile(const std::filesystem::path& destination); - void buildAsDiractory(const std::filesystem::path& destination); + void buildAsDirectory(const std::filesystem::path& destination); void buildAsMason(const std::filesystem::path& destination, const std::string& target); bool readMasonDependencies(const nlohmann::json& deps, const std::string& manifestPath); + void copyFile(const std::filesystem::path& from, const std::filesystem::path& to) const; + + void masonFilesArray(const nlohmann::json& files, const std::filesystem::path& destination); + bool allowedForTarget(const nlohmann::json& object, const std::string& target) const; + + std::optional getFileSource(const nlohmann::json& object) const; + std::optional getFileTarget(const nlohmann::json& object) const; + + static std::optional tryStringValue (const nlohmann::json& object, const std::string& key); private: State state; Type type; - std::weak_ptr collection; + Collection* collection; std::filesystem::path location; std::optional manifest; }; diff --git a/src/download.cpp b/src/download.cpp index feeb69e..54dc789 100644 --- a/src/download.cpp +++ b/src/download.cpp @@ -71,7 +71,6 @@ void Download::proceed() { const FileInfo& fl = file.value(); std::optional path = downloadAsFile(fl); if (path.has_value()) { - info("Successfully downloaded " + url); if (fl.archive) { info(url + " appears to be an archive, extracting"); bool success = extract(path.value(), destination/fl.name); diff --git a/src/main.cpp b/src/main.cpp index c15074d..581a7de 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -32,17 +32,15 @@ int main(int argc, char *argv[]) { taskManager->wait(); taskManager->stop(); - unsigned int success = collection->sourcesSuccess(); - unsigned int total = collection->sourcesTotal(); - if (total == success) { + if (collection->success()) { logger->log(Logger::Severity::info, "Successfully built " + firstArg); return 0; } else { logger->log(Logger::Severity::error, "Failed to build " + firstArg); - logger->log(Logger::Severity::info, "Success: " + std::to_string(success)); + logger->log(Logger::Severity::info, "Success: " + std::to_string(collection->sourcesSuccess())); logger->log(Logger::Severity::info, "Failed: " + std::to_string(collection->sourcesError())); logger->log(Logger::Severity::info, "Unfinished: " + std::to_string(collection->sourcesPending())); - logger->log(Logger::Severity::info, "Total: " + std::to_string(total)); + logger->log(Logger::Severity::info, "Total: " + std::to_string(collection->sourcesTotal())); return -1; } diff --git a/test/mason.json b/test/mason.json index a4415fd..5656088 100644 --- a/test/mason.json +++ b/test/mason.json @@ -4,6 +4,7 @@ "dependencies" : [ "somePath", "https://git.macaw.me/blue/mason.git", + "https://git.macaw.me/blue/mimicry.git", { "name" : "aProject", "path" : "https://nowhere/to/get.from"