diff --git a/src/collection.cpp b/src/collection.cpp index 77cca49..093f821 100644 --- a/src/collection.cpp +++ b/src/collection.cpp @@ -49,7 +49,7 @@ Collection::Collection( } taskManager->queue( - std::bind(&Component::build, root.get(), destination, target), + std::bind(&Component::build, root.get(), destination, target, false), std::bind(&Collection::rootBuilt, this, std::placeholders::_1) ); } @@ -120,7 +120,7 @@ void Collection::sourceRead(const std::string& source, TaskManager::Error err) { readingSources.erase(itr); taskManager->queue( - std::bind(&Component::build, res.first->second.get(), destination/librariesPath, target), + std::bind(&Component::build, res.first->second.get(), destination/librariesPath, target, true), std::bind(&Collection::sourceBuilt, this, source, std::placeholders::_1) ); } diff --git a/src/component.cpp b/src/component.cpp index b4aabe9..cde13e5 100644 --- a/src/component.cpp +++ b/src/component.cpp @@ -41,14 +41,16 @@ constexpr std::array stringTypes { Component::Component( const std::filesystem::path& path, Collection* collection, - const std::shared_ptr& logger + const std::shared_ptr& logger, + const std::string& name ): Loggable(logger), state(initial), type(unknown), collection(collection), location(path), - manifest(std::nullopt) + manifest(std::nullopt), + name(name) {} void Component::read() { @@ -62,11 +64,16 @@ void Component::read() { case std::filesystem::file_type::directory: type = directory; state = ready; - tryReadingBuildScenarios(); + if (!tryReadingBuildScenarios() && name.empty()) + name = location.filename(); + break; case std::filesystem::file_type::regular: type = file; state = ready; + if (name.empty()) + name = location.filename(); + break; default: warn(location.string() + " doesn't appear to be a file nor a directory"); @@ -75,9 +82,11 @@ void Component::read() { } } -void Component::tryReadingBuildScenarios() { +bool Component::tryReadingBuildScenarios() { if (readAsMason()) - return; + return true; + + return false; } bool Component::readAsMason() { @@ -106,6 +115,17 @@ bool Component::readAsMason() { return false; } + if (name.empty()) { + std::optional manifestName = tryStringValue(mnfst, "name"); + if (manifestName.has_value()) + name = manifestName.value(); + + if (name.empty()) { + major("Couldn't define name of the project at " + location.string() + ", using directory name as project name"); + name = location.filename(); + } + } + type = mason; info(location.string() + " seems to be a valid mason project"); return true; @@ -151,7 +171,7 @@ bool Component::errorScenario(const std::string& message) { return false; } -void Component::build(const std::filesystem::path& destination, const std::string& target) { +void Component::build(const std::filesystem::path& destination, const std::string& target, bool useName) { if (state != ready) throw WrongState(state, "build"); @@ -160,13 +180,23 @@ void Component::build(const std::filesystem::path& destination, const std::strin switch (type) { case file: - buildAsFile(destination); + if (useName) + buildAsFile(destination/name); + else + buildAsFile(destination); + break; case directory: - buildAsDirectory(destination); + if (useName) + buildAsDirectory(destination/name); + else + buildAsDirectory(destination); break; case mason: - buildAsMason(destination, target); + if (useName) + buildAsMason(destination/name, target); + else + buildAsMason(destination, target); break; default: break; @@ -174,13 +204,13 @@ void Component::build(const std::filesystem::path& destination, const std::strin } void Component::buildAsFile(const std::filesystem::path& destination) { - copyFile(location, destination/location.filename()); + copyFile(location, destination); state = done; } void Component::buildAsDirectory(const std::filesystem::path& destination) { - std::filesystem::copy(location, destination/location.filename(), + std::filesystem::copy(location, destination, std::filesystem::copy_options::recursive | std::filesystem::copy_options::overwrite_existing ); @@ -219,7 +249,7 @@ bool Component::successfullyBuilt() const { return state == done; } -void Component::buildAsMason(const std::filesystem::path& destination, const std::string& target) { +void Component::buildAsMason(const std::filesystem::path& destination, std::string target) { const nlohmann::json& mnfst = manifest.value(); nlohmann::json::const_iterator itr = mnfst.find("build"); if (itr == mnfst.end()) { @@ -230,11 +260,13 @@ void Component::buildAsMason(const std::filesystem::path& destination, const std const nlohmann::json& build = *itr; if (!build.is_array()) { - warn("Mason project at " + location.string() + " has unexpected \"build\" section in its manifest"); + warn("Mason project at " + location.string() + " has unexpected type \"build\" section in its manifest"); state = done; return; } + target = validateMasonTarget(target); + for (const nlohmann::json& entry : build) { switch (entry.type()) { case nlohmann::json::value_t::string: @@ -244,7 +276,7 @@ void Component::buildAsMason(const std::filesystem::path& destination, const std masonFilesArray(entry, destination); break; case nlohmann::json::value_t::object: - if (allowedForTarget(entry, target)) { + if (allowedForMasonTarget(entry, target)) { nlohmann::json::const_iterator fitr = entry.find("files"); if (fitr != entry.end()) { const nlohmann::json& files = *fitr; @@ -254,6 +286,8 @@ void Component::buildAsMason(const std::filesystem::path& destination, const std warn(std::string("Unexpected ") + entry.type_name() + " of files subsection of build section of its manifest in " + location.string() + ", ignoring"); + } else { + masonFileObject(entry, destination); } } @@ -284,20 +318,9 @@ void Component::masonFilesArray(const nlohmann::json& files, const std::filesyst 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; + case nlohmann::json::value_t::object: + masonFileObject(entry, destination); + break; default: warn(std::string("Unexpected ") + entry.type_name() + " in files subsection of build section of manifest in " @@ -306,7 +329,65 @@ void Component::masonFilesArray(const nlohmann::json& files, const std::filesyst } } -bool Component::allowedForTarget(const nlohmann::json& object, const std::string& target) const { +bool Component::masonFileObject(const nlohmann::json& object, const std::filesystem::path& destination) { + std::optional src = getFileSource(object); + if (!src.has_value()) { + warn("Couldn't understand file source processing file array in " + location.string() + ", ignoring"); + return false; + } + std::optional dst = getFileTarget(object); + if (!src.has_value()) { + warn("Couldn't understand file destination processing file array in " + location.string() + ", ignoring"); + return false; + } + + copyFile(location/src.value(), destination/dst.value()); + return true; +} + +std::string Component::validateMasonTarget(std::string target) const { + const nlohmann::json& mnfst = manifest.value(); + std::set allowedTarges; + + nlohmann::json::const_iterator itr = mnfst.find("targets"); + if (itr != mnfst.end()) { + const nlohmann::json& targets = *itr; + if (targets.is_array()) { + for (const nlohmann::json& entry : targets) { + if (entry.is_string()) + allowedTarges.emplace(entry); + } + } else { + warn("Mason project at " + location.string() + " has unexpected type \"targets\" section in its manifest, ignoring"); + } + } else { + minor("Mason project at " + location.string() + " has no \"targets\" section, trying to figure out utilized targets from \"build\" section"); + const nlohmann::json& build = mnfst.at("build"); + for (const nlohmann::json& entry : build) { + if (entry.is_object()) + getMasonTargets(entry, allowedTarges); + } + } + + if (allowedTarges.count(target) > 0) + return target; + + minor("Mason project at " + location.string() + " doesn't seem to support target \"" + target + "\""); + std::optional defaultTarget = tryStringValue(mnfst, "defaultTarget"); + if (defaultTarget.has_value()) { + target = defaultTarget.value(); + minor("Mason project at " + location.string() + " will use project default target \"" + target + "\" instead of specified"); + } else { + target = "default"; + warn("Mason project at " + location.string() + " will use hardcoded default target \"" + + target + "\" because field \"defaultTarget\" is either abscent or invalid"); + } + + return target; +} + +//this method could utilize Component::getMasonTargets but it doesn't because this way has less overhead +bool Component::allowedForMasonTarget(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; @@ -340,6 +421,35 @@ bool Component::allowedForTarget(const nlohmann::json& object, const std::string return true; } +void Component::getMasonTargets(const nlohmann::json& object, std::set& out) const { + nlohmann::json::const_iterator itr = object.find("target"); + if (itr != object.end()) { + const nlohmann::json& tg = *itr; + if (tg.is_string()) + out.emplace(tg); + 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()) + out.emplace(entry); + else + warn("one of the targets in \"targets\" in build section has unexpected type, ignoring it"); + } + + } else { + warn("\"targets\" in build section has unexpected type, ignoring"); + } + } + + //TODO may be notTarget and notTargets? +} + std::optional Component::getFileSource(const nlohmann::json& object) const { for (const std::string_view& key : sourceVariants) { std::optional value = tryStringValue(object, key.data()); diff --git a/src/component.h b/src/component.h index 197480c..23f394b 100644 --- a/src/component.h +++ b/src/component.h @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "loggable.h" @@ -39,33 +40,38 @@ public: Component( const std::filesystem::path& path, Collection* collection, - const std::shared_ptr& logger + const std::shared_ptr& logger, + const std::string& name = "" ); Type getType() const; State getState() const; std::filesystem::path getLocation() const; std::string getLibrariesPath() const; + std::string getName() const; bool successfullyRead() const; bool successfullyBuilt() const; void read(); - void build(const std::filesystem::path& destination, const std::string& target); + void build(const std::filesystem::path& destination, const std::string& target, bool useName = true); private: - void tryReadingBuildScenarios(); + bool tryReadingBuildScenarios(); bool readAsMason(); bool errorScenario(const std::string& message); void buildAsFile(const std::filesystem::path& destination); void buildAsDirectory(const std::filesystem::path& destination); - void buildAsMason(const std::filesystem::path& destination, const std::string& target); + void buildAsMason(const std::filesystem::path& destination, 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; + std::string validateMasonTarget(std::string target) const; void masonFilesArray(const nlohmann::json& files, const std::filesystem::path& destination); - bool allowedForTarget(const nlohmann::json& object, const std::string& target) const; + bool masonFileObject(const nlohmann::json& object, const std::filesystem::path& destination); + bool allowedForMasonTarget(const nlohmann::json& object, const std::string& target) const; + void getMasonTargets(const nlohmann::json& object, std::set& out) const; std::optional getFileSource(const nlohmann::json& object) const; std::optional getFileTarget(const nlohmann::json& object) const; @@ -78,6 +84,7 @@ private: Collection* collection; std::filesystem::path location; std::optional manifest; + std::string name; }; class Component::WrongState : public std::runtime_error { diff --git a/test/mason.json b/test/mason.json index 5656088..02ac87d 100644 --- a/test/mason.json +++ b/test/mason.json @@ -12,5 +12,12 @@ "https://git.macaw.me/blue/mason/archive/main.tar.gz", "https://git.macaw.me/blue/mason/raw/branch/main/src2/atomicmutex.h" ], + "build": [ + "mason.json", + { + "src" : "mason.json", + "dst" : "another/mason.json" + } + ], "libraries": "lib" }