diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 06b48a4..2b871b1 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -7,6 +7,7 @@ set(SOURCES taskmanager.cpp download.cpp atomicmutex.cpp + source.cpp ) set(HEADERS @@ -17,6 +18,7 @@ set(HEADERS taskmanager.h download.h atomicmutex.h + source.h ) target_sources(${PROJECT_NAME} PRIVATE ${SOURCES}) diff --git a/src/collection.cpp b/src/collection.cpp index 093f821..9fe1df8 100644 --- a/src/collection.cpp +++ b/src/collection.cpp @@ -43,7 +43,7 @@ Collection::Collection( librariesPath = root->getLibrariesPath(); std::lock_guard lock(mutex); while (pendingSources.size() > 0) { - std::string source = pendingSources.front(); + Source source = pendingSources.front(); pendingSources.pop(); processSource(source); } @@ -54,15 +54,15 @@ Collection::Collection( ); } -void Collection::addSource(const std::string& source) { +void Collection::addSource(const Source& source) { std::lock_guard lock(mutex); std::pair result = knownSources.emplace(source); if (!result.second) { - major("Source " + source + " is already present, skipping as duplicate"); + major("Source " + source.print() + " is already present, skipping as duplicate"); return; } - debug("Adding source " + source); + debug("Adding source " + source.print()); switch (root->getState()) { case Component::initial: case Component::reading: @@ -78,69 +78,74 @@ void Collection::addSource(const std::string& source) { } } -void Collection::processSource(const std::string& source) { - if (isRemote(source)) +void Collection::processSource(const Source& source) { + std::string path = source.getPath(); + if (isRemote(path)) queueDownload(source); else - queueRead(source, source); + queueRead(source, path); } -void Collection::sourceDownaloded(const std::string& source, TaskManager::Error err) { +void Collection::sourceDownaloded(const Source& source, TaskManager::Error err) { std::lock_guard lock(mutex); Downloads::const_iterator itr = downloadingSources.find(source); std::optional location = itr->second->getLocation(); downloadingSources.erase(itr); if (err.has_value() || !location.has_value()) { - error("Coundn't download " + source + ": " + err.value()->what()); + error("Coundn't download " + source.print() + ": " + err.value()->what()); return; } queueRead(source, location.value()); } -void Collection::sourceRead(const std::string& source, TaskManager::Error err) { +void Collection::sourceRead(const Source& source, TaskManager::Error err) { std::lock_guard lock(mutex); Components::iterator itr = readingSources.find(source); if (err.has_value()) { - error("Coundn't read " + source + ": " + err.value()->what()); + error("Coundn't read " + source.print() + ": " + err.value()->what()); readingSources.erase(itr); return; } if (!itr->second->successfullyRead()) { - error("Source " + source + " had an error during read process, canceling build of this component"); + error("Source " + source.print() + " had an error during read process, canceling build of this component"); readingSources.erase(itr); return; } - debug("Source " + source + " has been read, scheduling build"); + debug("Source " + source.print() + " has been read, scheduling build"); std::pair res = buildingSources.emplace(source, std::move(itr->second)); readingSources.erase(itr); + std::string tgt = target; + if (source.hasExplicitTarget()) + tgt = source.getTarget(); + taskManager->queue( - std::bind(&Component::build, res.first->second.get(), destination/librariesPath, target, true), + std::bind(&Component::build, res.first->second.get(), destination/librariesPath, tgt, true), std::bind(&Collection::sourceBuilt, this, source, std::placeholders::_1) ); } -void Collection::sourceBuilt(const std::string& source, TaskManager::Error err) { +void Collection::sourceBuilt(const Source& source, TaskManager::Error err) { std::lock_guard lock(mutex); 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()); + error("Coundn't read " + source.print() + ": " + err.value()->what()); return; } if (!success) { - error("Source " + source + " had an error during build process"); + error("Source " + source.print() + " had an error during build process"); return; } - debug("Source " + source + " has been built"); + debug("Source " + source.print() + " has been built"); successSources.emplace(source); } @@ -161,12 +166,12 @@ bool Collection::isRemote(const std::string& source) { return std::regex_search(source, remote); } -void Collection::queueDownload(const std::string& source) { - debug("Scheduling download of " + source); +void Collection::queueDownload(const Source& source) { + debug("Scheduling download of " + source.print()); std::pair res = downloadingSources.emplace(std::piecewise_construct, std::forward_as_tuple(source), std::forward_as_tuple( - std::make_unique(source, destination/downloads, logger) + std::make_unique(source.getPath(), destination/downloads, logger) ) ); @@ -176,12 +181,12 @@ void Collection::queueDownload(const std::string& source) { ); } -void Collection::queueRead(const std::string& source, const std::filesystem::path& location) { - debug("Scheduling read of " + source); +void Collection::queueRead(const Source& source, const std::filesystem::path& location) { + debug("Scheduling read of " + source.print()); std::pair res = readingSources.emplace(std::piecewise_construct, std::forward_as_tuple(source), std::forward_as_tuple( - std::make_unique(location, this, logger) + std::make_unique(location, this, logger, source.getOptionalName()) ) ); @@ -191,7 +196,7 @@ void Collection::queueRead(const std::string& source, const std::filesystem::pat ); } -bool Collection::hasSource(const std::string& source) const { +bool Collection::hasSource(const Source& source) const { std::lock_guard lock(mutex); return _hasSource(source); } @@ -221,7 +226,7 @@ bool Collection::success() const { return _sourcesTotal() == _sourcesSuccess() && root->successfullyBuilt(); } -bool Collection::_hasSource(const std::string& source) const { +bool Collection::_hasSource(const Source& source) const { return knownSources.count(source) > 0; } diff --git a/src/collection.h b/src/collection.h index 970b94e..732e6ff 100644 --- a/src/collection.h +++ b/src/collection.h @@ -14,12 +14,13 @@ #include "component.h" #include "download.h" #include "taskmanager.h" +#include "source.h" class Collection : protected Loggable { - using Sources = std::set; - using SourcesQueue = std::queue; - using Downloads = std::map>; - using Components = std::map>; + using Sources = std::set; + using SourcesQueue = std::queue; + using Downloads = std::map>; + using Components = std::map>; public: Collection( @@ -37,22 +38,22 @@ public: unsigned int sourcesError() const; unsigned int sourcesSuccess() const; - bool hasSource(const std::string& source) const; - void addSource(const std::string& source); + bool hasSource(const Source& source) const; + void addSource(const Source& source); private: - void sourceDownaloded(const std::string& source, TaskManager::Error err); - void sourceRead(const std::string& source, TaskManager::Error err); - void sourceBuilt(const std::string& source, TaskManager::Error err); + void sourceDownaloded(const Source& source, TaskManager::Error err); + void sourceRead(const Source& source, TaskManager::Error err); + void sourceBuilt(const Source& source, TaskManager::Error err); void rootBuilt(TaskManager::Error err); static bool isRemote(const std::string& source); - void processSource(const std::string& source); - void queueDownload(const std::string& source); - void queueRead(const std::string& source, const std::filesystem::path& location); + void processSource(const Source& source); + void queueDownload(const Source& source); + void queueRead(const Source& source, const std::filesystem::path& location); - bool _hasSource(const std::string& source) const; + bool _hasSource(const Source& source) const; unsigned int _sourcesTotal() const; unsigned int _sourcesPending() const; unsigned int _sourcesError() const; diff --git a/src/component.cpp b/src/component.cpp index cde13e5..db41587 100644 --- a/src/component.cpp +++ b/src/component.cpp @@ -42,7 +42,7 @@ Component::Component( const std::filesystem::path& path, Collection* collection, const std::shared_ptr& logger, - const std::string& name + const std::optional& name ): Loggable(logger), state(initial), @@ -64,14 +64,14 @@ void Component::read() { case std::filesystem::file_type::directory: type = directory; state = ready; - if (!tryReadingBuildScenarios() && name.empty()) + if (!tryReadingBuildScenarios() && !name.has_value()) name = location.filename(); break; case std::filesystem::file_type::regular: type = file; state = ready; - if (name.empty()) + if (!name.has_value()) name = location.filename(); break; @@ -115,12 +115,12 @@ bool Component::readAsMason() { return false; } - if (name.empty()) { + if (!name.has_value()) { std::optional manifestName = tryStringValue(mnfst, "name"); if (manifestName.has_value()) name = manifestName.value(); - if (name.empty()) { + if (!name.has_value()) { major("Couldn't define name of the project at " + location.string() + ", using directory name as project name"); name = location.filename(); } @@ -141,18 +141,14 @@ bool Component::readMasonDependencies(const nlohmann::json& deps, const std::str for (const nlohmann::json& dep : deps) { switch (dep.type()) { case nlohmann::json::value_t::string: - collection->addSource(dep); + collection->addSource({dep}); break; case nlohmann::json::value_t::object: { - nlohmann::json::const_iterator ditr = dep.find("path"); - if (ditr == dep.end()) + std::optional path = tryStringValue(dep, "path"); + if (!path.has_value()) return errorScenario(manifestPath + " object of unexpected format in dependecies"); - nlohmann::json path = *ditr; - if (!path.is_string()) - return errorScenario(manifestPath + " object of unexpected format in dependecies"); - - collection->addSource(path); + collection->addSource({path.value(), tryStringValue(dep, "target"), tryStringValue(dep, "name")}); } break; default: @@ -181,20 +177,20 @@ void Component::build(const std::filesystem::path& destination, const std::strin switch (type) { case file: if (useName) - buildAsFile(destination/name); + buildAsFile(destination/name.value()); else buildAsFile(destination); break; case directory: if (useName) - buildAsDirectory(destination/name); + buildAsDirectory(destination/name.value()); else buildAsDirectory(destination); break; case mason: if (useName) - buildAsMason(destination/name, target); + buildAsMason(destination/name.value(), target); else buildAsMason(destination, target); break; diff --git a/src/component.h b/src/component.h index 23f394b..67d409c 100644 --- a/src/component.h +++ b/src/component.h @@ -41,7 +41,7 @@ public: const std::filesystem::path& path, Collection* collection, const std::shared_ptr& logger, - const std::string& name = "" + const std::optional& name = std::nullopt ); Type getType() const; @@ -84,7 +84,7 @@ private: Collection* collection; std::filesystem::path location; std::optional manifest; - std::string name; + std::optional name; }; class Component::WrongState : public std::runtime_error { diff --git a/src/main.cpp b/src/main.cpp index 581a7de..4c7359f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -7,6 +7,7 @@ int main(int argc, char *argv[]) { std::string firstArg; std::string secondArg; + std::string thirdArg; if (argc > 1) firstArg = argv[1]; else @@ -17,13 +18,18 @@ int main(int argc, char *argv[]) { else secondArg = "./"; + if (argc > 3) + thirdArg = argv[3]; + else + thirdArg = ""; - std::shared_ptr logger = std::make_shared(Logger::Severity::debug); + + std::shared_ptr logger = std::make_shared(Logger::Severity::info); std::shared_ptr taskManager = std::make_shared(logger); std::shared_ptr collection = std::make_shared( firstArg, secondArg, - "", + thirdArg, logger, taskManager ); diff --git a/src/source.cpp b/src/source.cpp new file mode 100644 index 0000000..a338772 --- /dev/null +++ b/src/source.cpp @@ -0,0 +1,129 @@ +#include "source.h" + +Source::Source(const std::string& path) : + path(path), + target(std::nullopt), + name(std::nullopt) +{} + +Source::Source(const std::string& path, const std::string& target) : + path(path), + target(target), + name(std::nullopt) +{} + +Source::Source(const std::string& path, const std::string& target, const std::string& name) : + path(path), + target(target), + name(name) +{} + +Source::Source(const std::string& path, const std::optional& target, const std::optional& name) : + path(path), + target(target), + name(name) +{} + +void Source::clearName() { + name = std::nullopt; +} + +void Source::clearTarget() { + target = std::nullopt; +} + +std::string Source::getPath() const { + return path; +} + +std::string Source::getTarget() const { + if (target.has_value()) + return target.value(); + else + return ""; +} + +std::string Source::getName() const { + if (name.has_value()) + return name.value(); + else + return ""; +} + +std::optional Source::getOptionalTarget() const { + return target; +} + +std::optional Source::getOptionalName() const { + return name; +} + +bool Source::hasExplicitTarget() const { + return target.has_value(); +} + +bool Source::hasExplicitName() const { + return name.has_value(); +} + +void Source::setPath(const std::string& path) { + Source::path = path; +} + +void Source::setTarget(const std::string& target) { + Source::target = target; +} + +void Source::setName(const std::string& name) { + Source::name = name; +} + +bool Source::operator < (const Source& other) const { + if (path < other.path) + return true; + else if (path > other.path) + return false; + else { + if (hasExplicitTarget()) { + if (other.hasExplicitTarget()) { + if (target.value() < other.target.value()) + return true; + else if (target.value() > other.target.value()) { + return false; + } else { + if (hasExplicitName()) { + if (other.hasExplicitName()) { + return name.value() < other.name.value(); + } else + return false; + } else + return other.hasExplicitName(); + } + } else + return false; + } else { + if (other.hasExplicitTarget()) + return true; + else { + if (hasExplicitName()) { + if (other.hasExplicitName()) { + return name.value() < other.name.value(); + } else + return false; + } else + return other.hasExplicitName(); + } + } + } +} + +std::string Source::print() const { + std::string result = path; + if (hasExplicitTarget()) + result += " -> " + target.value(); + + if (hasExplicitName()) + result += " (as " + name.value() + ")"; + + return result; +} diff --git a/src/source.h b/src/source.h new file mode 100644 index 0000000..5ea2652 --- /dev/null +++ b/src/source.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +class Source { +public: + Source(const std::string& path); + Source(const std::string& path, const std::string& target); + Source(const std::string& path, const std::string& target, const std::string& name); + Source(const std::string& path, const std::optional& target, const std::optional& name); + + std::string getPath() const; + std::string getTarget() const; + std::string getName() const; + std::optional getOptionalTarget() const; + std::optional getOptionalName() const; + + void setPath(const std::string& path); + void setTarget(const std::string& target); + void setName(const std::string& name); + + std::string print() const; + + void clearTarget(); + void clearName(); + + bool hasExplicitTarget() const; + bool hasExplicitName() const; + + bool operator < (const Source& other) const; + +private: + std::string path; + std::optional target; + std::optional name; +}; diff --git a/src/taskmanager.cpp b/src/taskmanager.cpp index a13c5e2..ea9070c 100644 --- a/src/taskmanager.cpp +++ b/src/taskmanager.cpp @@ -95,8 +95,13 @@ void TaskManager::executeTask(const Task& task) const { try { task.first(); } catch (const std::exception& e) { - if (task.second.has_value()) - task.second.value()(&e); + if (task.second.has_value()) { + try { + task.second.value()(&e); + } catch (const std::exception& e) { + error(std::string("Uncaught exception: ") + e.what()); + } + } return; } diff --git a/test/mason.json b/test/mason.json index 02ac87d..b83409f 100644 --- a/test/mason.json +++ b/test/mason.json @@ -10,7 +10,16 @@ "path" : "https://nowhere/to/get.from" }, "https://git.macaw.me/blue/mason/archive/main.tar.gz", - "https://git.macaw.me/blue/mason/raw/branch/main/src2/atomicmutex.h" + "https://git.macaw.me/blue/mason/raw/branch/main/src2/atomicmutex.h", + { + "path" : "https://git.macaw.me/blue/mason/raw/branch/main/src2/atomicmutex.h", + "name" : "changed.h" + }, + { + "path" : "https://git.macaw.me/blue/mimicry.git", + "name" : "mimicryNode", + "target" : "node" + } ], "build": [ "mason.json",