"depends": {}, | "depends": {}, | ||||
"description": "The Windows Implementation Library", | "description": "The Windows Implementation Library", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/2020.03.16", | "ref": "dds/2020.03.16", | ||||
"url": "https://github.com/vector-of-bool/wil.git" | "url": "https://github.com/vector-of-bool/wil.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "Minimal C++ concepts library. Contains many definitions from C++20.", | "description": "Minimal C++ concepts library. Contains many definitions from C++20.", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.1.0", | "ref": "0.1.0", | ||||
"url": "https://github.com/vector-of-bool/neo-concepts.git" | "url": "https://github.com/vector-of-bool/neo-concepts.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "Minimal C++ concepts library. Contains many definitions from C++20.", | "description": "Minimal C++ concepts library. Contains many definitions from C++20.", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.2.0", | "ref": "0.2.0", | ||||
"url": "https://github.com/vector-of-bool/neo-concepts.git" | "url": "https://github.com/vector-of-bool/neo-concepts.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "Minimal C++ concepts library. Contains many definitions from C++20.", | "description": "Minimal C++ concepts library. Contains many definitions from C++20.", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.2.1", | "ref": "0.2.1", | ||||
"url": "https://github.com/vector-of-bool/neo-concepts.git" | "url": "https://github.com/vector-of-bool/neo-concepts.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "Minimal C++ concepts library. Contains many definitions from C++20.", | "description": "Minimal C++ concepts library. Contains many definitions from C++20.", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.2.2", | "ref": "0.2.2", | ||||
"url": "https://github.com/vector-of-bool/neo-concepts.git" | "url": "https://github.com/vector-of-bool/neo-concepts.git" | ||||
} | } | ||||
}, | |||||
"0.3.0": { | |||||
"depends": {}, | |||||
"description": "Minimal C++ concepts library. Contains many definitions from C++20.", | |||||
"git": { | |||||
"ref": "0.3.0", | |||||
"url": "https://github.com/vector-of-bool/neo-concepts.git" | |||||
} | |||||
}, | |||||
"0.3.1": { | |||||
"depends": {}, | |||||
"description": "Minimal C++ concepts library. Contains many definitions from C++20.", | |||||
"git": { | |||||
"ref": "0.3.1", | |||||
"url": "https://github.com/vector-of-bool/neo-concepts.git" | |||||
} | |||||
}, | |||||
"0.3.2": { | |||||
"depends": {}, | |||||
"description": "Minimal C++ concepts library. Contains many definitions from C++20.", | |||||
"git": { | |||||
"ref": "0.3.2", | |||||
"url": "https://github.com/vector-of-bool/neo-concepts.git" | |||||
} | |||||
} | } | ||||
}, | }, | ||||
"neo-fun": { | "neo-fun": { | ||||
"depends": {}, | "depends": {}, | ||||
"description": "Some library fundamentals that you might find useful", | "description": "Some library fundamentals that you might find useful", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.1.0", | "ref": "0.1.0", | ||||
"url": "https://github.com/vector-of-bool/neo-fun.git" | "url": "https://github.com/vector-of-bool/neo-fun.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "Some library fundamentals that you might find useful", | "description": "Some library fundamentals that you might find useful", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.1.1", | "ref": "0.1.1", | ||||
"url": "https://github.com/vector-of-bool/neo-fun.git" | "url": "https://github.com/vector-of-bool/neo-fun.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "Some library fundamentals that you might find useful", | "description": "Some library fundamentals that you might find useful", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.2.0", | "ref": "0.2.0", | ||||
"url": "https://github.com/vector-of-bool/neo-fun.git" | "url": "https://github.com/vector-of-bool/neo-fun.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "Some library fundamentals that you might find useful", | "description": "Some library fundamentals that you might find useful", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.2.1", | "ref": "0.2.1", | ||||
"url": "https://github.com/vector-of-bool/neo-fun.git" | "url": "https://github.com/vector-of-bool/neo-fun.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "Some library fundamentals that you might find useful", | "description": "Some library fundamentals that you might find useful", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.3.0", | "ref": "0.3.0", | ||||
"url": "https://github.com/vector-of-bool/neo-fun.git" | "url": "https://github.com/vector-of-bool/neo-fun.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "Some library fundamentals that you might find useful", | "description": "Some library fundamentals that you might find useful", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.3.1", | "ref": "0.3.1", | ||||
"url": "https://github.com/vector-of-bool/neo-fun.git" | "url": "https://github.com/vector-of-bool/neo-fun.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "Some library fundamentals that you might find useful", | "description": "Some library fundamentals that you might find useful", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.3.2", | "ref": "0.3.2", | ||||
"url": "https://github.com/vector-of-bool/neo-fun.git" | "url": "https://github.com/vector-of-bool/neo-fun.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "A modern and low-level C++ SQLite API", | "description": "A modern and low-level C++ SQLite API", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.1.0", | "ref": "0.1.0", | ||||
"url": "https://github.com/vector-of-bool/neo-sqlite3.git" | "url": "https://github.com/vector-of-bool/neo-sqlite3.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "A modern and low-level C++ SQLite API", | "description": "A modern and low-level C++ SQLite API", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.2.0", | "ref": "0.2.0", | ||||
"url": "https://github.com/vector-of-bool/neo-sqlite3.git" | "url": "https://github.com/vector-of-bool/neo-sqlite3.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "A modern and low-level C++ SQLite API", | "description": "A modern and low-level C++ SQLite API", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.2.1", | "ref": "0.2.1", | ||||
"url": "https://github.com/vector-of-bool/neo-sqlite3.git" | "url": "https://github.com/vector-of-bool/neo-sqlite3.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "A modern and low-level C++ SQLite API", | "description": "A modern and low-level C++ SQLite API", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.2.2", | "ref": "0.2.2", | ||||
"url": "https://github.com/vector-of-bool/neo-sqlite3.git" | "url": "https://github.com/vector-of-bool/neo-sqlite3.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "A modern and low-level C++ SQLite API", | "description": "A modern and low-level C++ SQLite API", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.2.3", | "ref": "0.2.3", | ||||
"url": "https://github.com/vector-of-bool/neo-sqlite3.git" | "url": "https://github.com/vector-of-bool/neo-sqlite3.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "JSON for Modern C++", | "description": "JSON for Modern C++", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/3.0.0", | "ref": "dds/3.0.0", | ||||
"url": "https://github.com/vector-of-bool/json.git" | "url": "https://github.com/vector-of-bool/json.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "JSON for Modern C++", | "description": "JSON for Modern C++", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/3.0.1", | "ref": "dds/3.0.1", | ||||
"url": "https://github.com/vector-of-bool/json.git" | "url": "https://github.com/vector-of-bool/json.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "JSON for Modern C++", | "description": "JSON for Modern C++", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/3.1.0", | "ref": "dds/3.1.0", | ||||
"url": "https://github.com/vector-of-bool/json.git" | "url": "https://github.com/vector-of-bool/json.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "JSON for Modern C++", | "description": "JSON for Modern C++", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/3.1.1", | "ref": "dds/3.1.1", | ||||
"url": "https://github.com/vector-of-bool/json.git" | "url": "https://github.com/vector-of-bool/json.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "JSON for Modern C++", | "description": "JSON for Modern C++", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/3.1.2", | "ref": "dds/3.1.2", | ||||
"url": "https://github.com/vector-of-bool/json.git" | "url": "https://github.com/vector-of-bool/json.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "JSON for Modern C++", | "description": "JSON for Modern C++", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/3.2.0", | "ref": "dds/3.2.0", | ||||
"url": "https://github.com/vector-of-bool/json.git" | "url": "https://github.com/vector-of-bool/json.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "JSON for Modern C++", | "description": "JSON for Modern C++", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/3.3.0", | "ref": "dds/3.3.0", | ||||
"url": "https://github.com/vector-of-bool/json.git" | "url": "https://github.com/vector-of-bool/json.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "JSON for Modern C++", | "description": "JSON for Modern C++", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/3.4.0", | "ref": "dds/3.4.0", | ||||
"url": "https://github.com/vector-of-bool/json.git" | "url": "https://github.com/vector-of-bool/json.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "JSON for Modern C++", | "description": "JSON for Modern C++", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/3.5.0", | "ref": "dds/3.5.0", | ||||
"url": "https://github.com/vector-of-bool/json.git" | "url": "https://github.com/vector-of-bool/json.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "JSON for Modern C++", | "description": "JSON for Modern C++", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/3.6.0", | "ref": "dds/3.6.0", | ||||
"url": "https://github.com/vector-of-bool/json.git" | "url": "https://github.com/vector-of-bool/json.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "JSON for Modern C++", | "description": "JSON for Modern C++", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/3.6.1", | "ref": "dds/3.6.1", | ||||
"url": "https://github.com/vector-of-bool/json.git" | "url": "https://github.com/vector-of-bool/json.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "JSON for Modern C++", | "description": "JSON for Modern C++", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/3.7.0", | "ref": "dds/3.7.0", | ||||
"url": "https://github.com/vector-of-bool/json.git" | "url": "https://github.com/vector-of-bool/json.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "JSON for Modern C++", | "description": "JSON for Modern C++", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/3.7.1", | "ref": "dds/3.7.1", | ||||
"url": "https://github.com/vector-of-bool/json.git" | "url": "https://github.com/vector-of-bool/json.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "JSON for Modern C++", | "description": "JSON for Modern C++", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/3.7.2", | "ref": "dds/3.7.2", | ||||
"url": "https://github.com/vector-of-bool/json.git" | "url": "https://github.com/vector-of-bool/json.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "JSON for Modern C++", | "description": "JSON for Modern C++", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "dds/3.7.3", | "ref": "dds/3.7.3", | ||||
"url": "https://github.com/vector-of-bool/json.git" | "url": "https://github.com/vector-of-bool/json.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "A C++ implementation of the Pubgrub version solving algorithm", | "description": "A C++ implementation of the Pubgrub version solving algorithm", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.1.2", | "ref": "0.1.2", | ||||
"url": "https://github.com/vector-of-bool/pubgrub.git" | "url": "https://github.com/vector-of-bool/pubgrub.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "A C++ implementation of the Pubgrub version solving algorithm", | "description": "A C++ implementation of the Pubgrub version solving algorithm", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.2.0", | "ref": "0.2.0", | ||||
"url": "https://github.com/vector-of-bool/pubgrub.git" | "url": "https://github.com/vector-of-bool/pubgrub.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "A C++ implementation of the Pubgrub version solving algorithm", | "description": "A C++ implementation of the Pubgrub version solving algorithm", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.2.1", | "ref": "0.2.1", | ||||
"url": "https://github.com/vector-of-bool/pubgrub.git" | "url": "https://github.com/vector-of-bool/pubgrub.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "A C++ library that implements Semantic Versioning parsing, emitting, types, ordering, and operations. See https://semver.org/", | "description": "A C++ library that implements Semantic Versioning parsing, emitting, types, ordering, and operations. See https://semver.org/", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.2.1", | "ref": "0.2.1", | ||||
"url": "https://github.com/vector-of-bool/semver.git" | "url": "https://github.com/vector-of-bool/semver.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "A C++ library that implements Semantic Versioning parsing, emitting, types, ordering, and operations. See https://semver.org/", | "description": "A C++ library that implements Semantic Versioning parsing, emitting, types, ordering, and operations. See https://semver.org/", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.2.2", | "ref": "0.2.2", | ||||
"url": "https://github.com/vector-of-bool/semver.git" | "url": "https://github.com/vector-of-bool/semver.git" | ||||
} | } | ||||
"depends": {}, | "depends": {}, | ||||
"description": "A C++ implementation of a JSON5 parser", | "description": "A C++ implementation of a JSON5 parser", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.1.5", | "ref": "0.1.5", | ||||
"url": "https://github.com/vector-of-bool/json5.git" | "url": "https://github.com/vector-of-bool/json5.git" | ||||
} | } | ||||
}, | }, | ||||
"description": "A C++ library to process recursive dynamic data", | "description": "A C++ library to process recursive dynamic data", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.1.0", | "ref": "0.1.0", | ||||
"url": "https://github.com/vector-of-bool/semester.git" | "url": "https://github.com/vector-of-bool/semester.git" | ||||
} | } | ||||
}, | }, | ||||
"description": "A C++ library to process recursive dynamic data", | "description": "A C++ library to process recursive dynamic data", | ||||
"git": { | "git": { | ||||
"auto-lib": null, | |||||
"ref": "0.1.1", | "ref": "0.1.1", | ||||
"url": "https://github.com/vector-of-bool/semester.git" | "url": "https://github.com/vector-of-bool/semester.git" | ||||
} | } | ||||
}, | |||||
"0.2.0": { | |||||
"depends": { | |||||
"neo-concepts": "^0.3.2", | |||||
"neo-fun": "^0.3.2" | |||||
}, | |||||
"description": "A C++ library to process recursive dynamic data", | |||||
"git": { | |||||
"ref": "0.2.0", | |||||
"url": "https://github.com/vector-of-bool/semester.git" | |||||
} | |||||
}, | |||||
"0.2.1": { | |||||
"depends": { | |||||
"neo-concepts": "^0.3.2", | |||||
"neo-fun": "^0.3.2" | |||||
}, | |||||
"description": "A C++ library to process recursive dynamic data", | |||||
"git": { | |||||
"ref": "0.2.1", | |||||
"url": "https://github.com/vector-of-bool/semester.git" | |||||
} | |||||
} | } | ||||
} | } | ||||
}, | }, |
"range-v3": "0.10.0", | "range-v3": "0.10.0", | ||||
"nlohmann-json": "3.7.1", | "nlohmann-json": "3.7.1", | ||||
"neo-sqlite3": "0.2.3", | "neo-sqlite3": "0.2.3", | ||||
"neo-fun": "0.3.0", | |||||
"neo-fun": "0.3.2", | |||||
"semver": "0.2.2", | "semver": "0.2.2", | ||||
"pubgrub": "0.2.1", | "pubgrub": "0.2.1", | ||||
"vob-json5": "0.1.5", | "vob-json5": "0.1.5", | ||||
"vob-semester": "0.1.1", | |||||
"vob-semester": "0.2.1", | |||||
"ctre": "2.7.0", | "ctre": "2.7.0", | ||||
}, | }, | ||||
"test_driver": "Catch-Main" | "test_driver": "Catch-Main" |
// deps.push_back({dep_id.name, dep_id.version}); | // deps.push_back({dep_id.name, dep_id.version}); | ||||
} | } | ||||
dds::package_info info{ident, std::move(deps), description.Get(), {}, {}}; | |||||
dds::package_info info{ident, std::move(deps), description.Get(), {}}; | |||||
if (git_url) { | if (git_url) { | ||||
if (!git_ref) { | if (!git_ref) { | ||||
dds::throw_user_error<dds::errc::git_url_ref_mutual_req>(); | dds::throw_user_error<dds::errc::git_url_ref_mutual_req>(); | ||||
} | } | ||||
auto git = dds::git_remote_listing{git_url.Get(), git_ref.Get(), std::nullopt}; | |||||
auto git = dds::git_remote_listing{git_url.Get(), git_ref.Get(), std::nullopt, {}}; | |||||
if (auto_lib) { | if (auto_lib) { | ||||
git.auto_lib = lm::split_usage_string(auto_lib.Get()); | git.auto_lib = lm::split_usage_string(auto_lib.Get()); | ||||
} | } |
#include <dds/error/errors.hpp> | #include <dds/error/errors.hpp> | ||||
#include <dds/solve/solve.hpp> | #include <dds/solve/solve.hpp> | ||||
#include <json5/parse_data.hpp> | |||||
#include <neo/assert.hpp> | #include <neo/assert.hpp> | ||||
#include <neo/concepts.hpp> | |||||
#include <neo/sqlite3/exec.hpp> | #include <neo/sqlite3/exec.hpp> | ||||
#include <neo/sqlite3/iter_tuples.hpp> | #include <neo/sqlite3/iter_tuples.hpp> | ||||
#include <neo/sqlite3/single.hpp> | #include <neo/sqlite3/single.hpp> | ||||
} | } | ||||
} | } | ||||
std::vector<dds::glob> parse_glob_list(const nlohmann::json& data, std::string_view what) { | |||||
std::vector<dds::glob> ret; | |||||
if (!data.is_null()) { | |||||
check_json(data.is_array(), fmt::format("'{}' must be an array of strings", what)); | |||||
for (nlohmann::json const& glob : data) { | |||||
check_json(glob.is_string(), fmt::format("'{}[.]' must be strings", what)); | |||||
ret.emplace_back(dds::glob::compile(std::string(glob))); | |||||
} | |||||
} | |||||
return ret; | |||||
} | |||||
} // namespace | } // namespace | ||||
catalog catalog::open(const std::string& db_path) { | catalog catalog::open(const std::string& db_path) { | ||||
pkg.ident.to_string()); | pkg.ident.to_string()); | ||||
} | } | ||||
namespace { | |||||
std::string transforms_to_json(const std::vector<fs_transformation>& trs) { | |||||
std::string acc = "["; | |||||
for (auto it = trs.begin(); it != trs.end(); ++it) { | |||||
acc += it->as_json(); | |||||
if (std::next(it) != trs.end()) { | |||||
acc += ", "; | |||||
} | |||||
} | |||||
return acc + "]"; | |||||
} | |||||
} // namespace | |||||
void catalog::_store_pkg(const package_info& pkg, const git_remote_listing& git) { | void catalog::_store_pkg(const package_info& pkg, const git_remote_listing& git) { | ||||
auto lm_usage = git.auto_lib.value_or(lm::usage{}); | auto lm_usage = git.auto_lib.value_or(lm::usage{}); | ||||
sqlite3::exec( // | sqlite3::exec( // | ||||
git.ref, | git.ref, | ||||
lm_usage.name, | lm_usage.name, | ||||
lm_usage.namespace_, | lm_usage.namespace_, | ||||
pkg.description | |||||
//, transform_to_json(pkg.transforms)) | |||||
)); | |||||
pkg.description, | |||||
transforms_to_json(git.transforms))); | |||||
} | } | ||||
void catalog::store(const package_info& pkg) { | void catalog::store(const package_info& pkg) { | ||||
*git_url, | *git_url, | ||||
*git_ref, | *git_ref, | ||||
lm_name ? std::make_optional(lm::usage{*lm_namespace, *lm_name}) : std::nullopt, | lm_name ? std::make_optional(lm::usage{*lm_namespace, *lm_name}) : std::nullopt, | ||||
{}, | |||||
}, | }, | ||||
{}, | |||||
}; | }; | ||||
auto append_transform = [](auto transform) { | |||||
return [transform = std::move(transform)](auto& remote) { | |||||
if constexpr (neo::alike<decltype(remote), std::monostate>) { | |||||
// Do nothing | |||||
} else { | |||||
remote.transforms.push_back(std::move(transform)); | |||||
} | |||||
}; | |||||
}; | |||||
if (!repo_transform.empty()) { | if (!repo_transform.empty()) { | ||||
auto tr_json = nlohmann::json::parse(repo_transform); | |||||
auto tr_json = json5::parse_data(repo_transform); | |||||
check_json(tr_json.is_array(), | check_json(tr_json.is_array(), | ||||
fmt::format("Database record for {} has an invalid 'repo_transform' field", | |||||
fmt::format("Database record for {} has an invalid 'repo_transform' field [1]", | |||||
pkg_id)); | pkg_id)); | ||||
/// XXX: | |||||
// for (const auto& el : tr_json) { | |||||
// info.transforms.push_back(parse_transform(el)); | |||||
// } | |||||
for (const auto& el : tr_json.as_array()) { | |||||
check_json( | |||||
el.is_object(), | |||||
fmt::format("Database record for {} has an invalid 'repo_transform' field [2]", | |||||
pkg_id)); | |||||
auto tr = fs_transformation::from_json(el); | |||||
std::visit(append_transform(tr), info.remote); | |||||
} | |||||
} | } | ||||
return info; | return info; | ||||
} | } |
spdlog::info("Cloning Git repository: {} [{}] ...", git.url, git.ref); | spdlog::info("Cloning Git repository: {} [{}] ...", git.url, git.ref); | ||||
git.clone(tmpdir.path()); | git.clone(tmpdir.path()); | ||||
/// XXX: | |||||
// for (const auto& tr : listing.transforms) { | |||||
// tr.apply_to(tmpdir.path()); | |||||
// } | |||||
for (const auto& tr : git.transforms) { | |||||
tr.apply_to(tmpdir.path()); | |||||
} | |||||
spdlog::info("Create sdist from clone ..."); | spdlog::info("Create sdist from clone ..."); | ||||
if (git.auto_lib.has_value()) { | if (git.auto_lib.has_value()) { |
#include <json5/parse_data.hpp> | #include <json5/parse_data.hpp> | ||||
#include <neo/assert.hpp> | #include <neo/assert.hpp> | ||||
#include <semester/decomp.hpp> | |||||
#include <semester/walk.hpp> | |||||
#include <spdlog/fmt/fmt.h> | #include <spdlog/fmt/fmt.h> | ||||
#include <optional> | #include <optional> | ||||
using namespace dds; | using namespace dds; | ||||
template <typename... Args> | |||||
template <typename KeyFunc, typename... Args> | |||||
struct any_key { | struct any_key { | ||||
semester::try_seq<Args...> _seq; | |||||
std::string_view& _key; | |||||
KeyFunc _key_fn; | |||||
semester::walk_seq<Args...> _seq; | |||||
any_key(std::string_view& key_var, Args&&... args) | |||||
: _seq(NEO_FWD(args)...) | |||||
, _key{key_var} {} | |||||
any_key(KeyFunc&& kf, Args&&... args) | |||||
: _key_fn(kf) | |||||
, _seq(NEO_FWD(args)...) {} | |||||
template <typename Data> | template <typename Data> | ||||
semester::dc_result_t operator()(std::string_view key, Data&& dat) const { | |||||
_key = key; | |||||
return _seq.invoke(dat); | |||||
semester::walk_result operator()(std::string_view key, Data&& dat) { | |||||
auto res = _key_fn(key); | |||||
if (res.rejected()) { | |||||
return res; | |||||
} | |||||
return _seq.invoke(NEO_FWD(dat)); | |||||
} | } | ||||
}; | }; | ||||
template <typename... Args> | |||||
any_key(std::string_view, Args&&...) -> any_key<Args&&...>; | |||||
template <typename KF, typename... Args> | |||||
any_key(KF&&, Args&&...) -> any_key<KF, Args...>; | |||||
namespace { | namespace { | ||||
semester::dc_result_t reject(std::string s) { return semester::dc_reject_t{s}; } | |||||
semester::dc_result_t pass = semester::dc_pass; | |||||
semester::dc_result_t accept = semester::dc_accept; | |||||
using require_obj = semester::require_type<json5::data::mapping_type>; | |||||
using require_obj = semester::require_type<json5::data::mapping_type>; | |||||
using require_array = semester::require_type<json5::data::array_type>; | |||||
using require_str = semester::require_type<std::string>; | |||||
auto reject_unknown_key(std::string_view path) { | |||||
return [path = std::string(path)](auto key, auto&&) { // | |||||
return reject(fmt::format("{}: unknown key '{}'", path, key)); | |||||
}; | |||||
template <typename... Args> | |||||
[[noreturn]] void import_error(Args&&... args) { | |||||
throw_user_error<dds::errc::invalid_catalog_json>(NEO_FWD(args)...); | |||||
} | } | ||||
std::vector<dependency> parse_deps_json_v1(const json5::data& deps, std::string_view path) { | |||||
std::vector<dependency> acc_deps; | |||||
std::string_view dep_name; | |||||
std::string_view dep_version_range_str; | |||||
using namespace semester::decompose_ops; | |||||
auto result = semester::decompose( // | |||||
deps, | |||||
mapping{any_key{ | |||||
dep_name, | |||||
[&](auto&& range_str) { | |||||
if (!range_str.is_string()) { | |||||
throw_user_error< | |||||
errc::invalid_catalog_json>("{}/{} should be a string version range", | |||||
path, | |||||
dep_name); | |||||
} | |||||
try { | |||||
auto rng = semver::range::parse_restricted(range_str.as_string()); | |||||
acc_deps.push_back(dependency{std::string{dep_name}, {rng.low(), rng.high()}}); | |||||
return accept; | |||||
} catch (const semver::invalid_range&) { | |||||
throw_user_error<errc::invalid_version_range_string>( | |||||
"Invalid version range string '{}' at {}/{}", | |||||
range_str.as_string(), | |||||
path, | |||||
dep_name); | |||||
} | |||||
}, | |||||
}}); | |||||
neo_assert(invariant, | |||||
std::holds_alternative<semester::dc_accept_t>(result), | |||||
"Parsing dependency object did not accept??"); | |||||
return acc_deps; | |||||
git_remote_listing parse_git_remote(const json5::data& data) { | |||||
git_remote_listing git; | |||||
using namespace semester::walk_ops; | |||||
walk(data, | |||||
require_obj{"Git remote should be an object"}, | |||||
mapping{required_key{"url", | |||||
"A git 'url' string is required", | |||||
require_str("Git URL should be a string"), | |||||
put_into(git.url)}, | |||||
required_key{"ref", | |||||
"A git 'ref' is required, and must be a tag or branch name", | |||||
require_str("Git ref should be a string"), | |||||
put_into(git.ref)}, | |||||
if_key{"auto-lib", | |||||
require_str("'auto-lib' should be a string"), | |||||
put_into(git.auto_lib, | |||||
[](std::string const& str) { | |||||
try { | |||||
return lm::split_usage_string(str); | |||||
} catch (const std::runtime_error& e) { | |||||
import_error("{}: {}", walk.path(), e.what()); | |||||
} | |||||
})}, | |||||
if_key{"transform", | |||||
require_array{"Expect an array of transforms"}, | |||||
for_each{put_into(std::back_inserter(git.transforms), [](auto&& dat) { | |||||
try { | |||||
return fs_transformation::from_json(dat); | |||||
} catch (const semester::walk_error& e) { | |||||
import_error(e.what()); | |||||
} | |||||
})}}}); | |||||
return git; | |||||
} | } | ||||
package_info parse_pkg_json_v1(std::string_view name, | |||||
semver::version version, | |||||
std::string_view path, | |||||
const json5::data& pkg) { | |||||
using namespace semester::decompose_ops; | |||||
package_info | |||||
parse_pkg_json_v1(std::string_view name, semver::version version, const json5::data& data) { | |||||
package_info ret; | package_info ret; | ||||
ret.ident = package_id{std::string{name}, version}; | ret.ident = package_id{std::string{name}, version}; | ||||
auto result = semester::decompose( // | |||||
pkg, | |||||
mapping{if_key{"description", | |||||
require_type<std::string>{ | |||||
fmt::format("{}/description should be a string", path)}, | |||||
put_into{ret.description}}, | |||||
if_key{"depends", | |||||
require_obj{fmt::format("{}/depends must be a JSON object", path)}, | |||||
[&](auto&& dep_obj) { | |||||
ret.deps = parse_deps_json_v1(dep_obj, fmt::format("{}/depends", path)); | |||||
return accept; | |||||
}}, | |||||
if_key{ | |||||
"git", | |||||
require_obj{fmt::format("{}/git must be a JSON object", path)}, | |||||
[&](auto&& git_obj) { | |||||
git_remote_listing git_remote; | |||||
auto r = semester::decompose( | |||||
git_obj, | |||||
mapping{ | |||||
if_key{"url", put_into{git_remote.url}}, | |||||
if_key{"ref", put_into{git_remote.ref}}, | |||||
if_key{"auto-lib", | |||||
require_type<std::string>{ | |||||
fmt::format("{}/git/auto-lib must be a string", path)}, | |||||
[&](auto&& al) { | |||||
git_remote.auto_lib | |||||
= lm::split_usage_string(al.as_string()); | |||||
return accept; | |||||
}}, | |||||
reject_unknown_key(std::string(path) + "/git"), | |||||
}); | |||||
if (git_remote.url.empty() || git_remote.ref.empty()) { | |||||
throw_user_error<errc::invalid_catalog_json>( | |||||
"{}/git requires both 'url' and 'ref' non-empty string properties", | |||||
path); | |||||
} | |||||
ret.remote = git_remote; | |||||
return r; | |||||
}, | |||||
}, | |||||
reject_unknown_key(path)}); | |||||
if (std::holds_alternative<std::monostate>(ret.remote)) { | |||||
throw_user_error< | |||||
errc::invalid_catalog_json>("{}: Requires a remote listing (e.g. a 'git' proprety).", | |||||
path); | |||||
} | |||||
auto rej = std::get_if<semester::dc_reject_t>(&result); | |||||
if (rej) { | |||||
throw_user_error<errc::invalid_catalog_json>("{}: {}", path, rej->message); | |||||
using namespace semester::walk_ops; | |||||
std::string dep_name; | |||||
auto dep_range = semver::range::everything(); | |||||
auto parse_dep_range = [&](const std::string& s) { | |||||
try { | |||||
return semver::range::parse_restricted(s); | |||||
} catch (const semver::invalid_range& e) { | |||||
import_error(std::string(walk.path()) + e.what()); | |||||
} | |||||
}; | |||||
auto make_dep = [&](auto&&) { | |||||
return dependency{dep_name, {dep_range.low(), dep_range.high()}}; | |||||
}; | |||||
auto check_one_remote = [&](auto&&) { | |||||
if (!semester::holds_alternative<std::monostate>(ret.remote)) { | |||||
return walk.reject("Cannot specify multiple remotes for a package"); | |||||
} | |||||
return walk.pass; | |||||
}; | |||||
auto add_dep = any_key{put_into(dep_name), | |||||
require_str{"Dependency should specify a version range string"}, | |||||
put_into_pass{dep_range, parse_dep_range}, | |||||
put_into{std::back_inserter(ret.deps), make_dep}}; | |||||
walk(data, | |||||
mapping{if_key{"description", | |||||
require_str{"'description' should be a string"}, | |||||
put_into{ret.description}}, | |||||
if_key{"depends", | |||||
require_obj{"'depends' must be a JSON object"}, | |||||
mapping{add_dep}}, | |||||
if_key{ | |||||
"git", | |||||
check_one_remote, | |||||
put_into(ret.remote, parse_git_remote), | |||||
}}); | |||||
if (semester::holds_alternative<std::monostate>(ret.remote)) { | |||||
import_error("{}: Package listing for {} does not have any remote information", | |||||
walk.path(), | |||||
ret.ident.to_string()); | |||||
} | } | ||||
return ret; | return ret; | ||||
} | } | ||||
std::vector<package_info> parse_json_v1(const json5::data& data) { | std::vector<package_info> parse_json_v1(const json5::data& data) { | ||||
using namespace semester::decompose_ops; | |||||
auto packages_it = data.as_object().find("packages"); | |||||
if (packages_it == data.as_object().end() || !packages_it->second.is_object()) { | |||||
throw_user_error<errc::invalid_catalog_json>( | |||||
"Root JSON object requires a 'packages' property"); | |||||
} | |||||
std::vector<package_info> acc_pkgs; | std::vector<package_info> acc_pkgs; | ||||
std::string_view pkg_name; | |||||
std::string_view pkg_version_str; | |||||
auto result = semester::decompose( | |||||
data, | |||||
mapping{ | |||||
// Ignore the "version" key at this level | |||||
if_key{"version", just_accept}, | |||||
if_key{ | |||||
"packages", | |||||
mapping{any_key{ | |||||
pkg_name, | |||||
[&](auto&& entry) { | |||||
if (!entry.is_object()) { | |||||
return reject( | |||||
fmt::format("/packages/{} must be a JSON object", pkg_name)); | |||||
} | |||||
return pass; | |||||
}, | |||||
mapping{any_key{ | |||||
pkg_version_str, | |||||
[&](auto&& pkg_def) { | |||||
semver::version version; | |||||
try { | |||||
version = semver::version::parse(pkg_version_str); | |||||
} catch (const semver::invalid_version& e) { | |||||
throw_user_error<errc::invalid_catalog_json>( | |||||
"/packages/{} version string '{}' is invalid: {}", | |||||
pkg_name, | |||||
pkg_version_str, | |||||
e.what()); | |||||
} | |||||
if (!pkg_def.is_object()) { | |||||
return reject(fmt::format("/packages/{}/{} must be a JSON object")); | |||||
} | |||||
auto pkg = parse_pkg_json_v1(pkg_name, | |||||
version, | |||||
fmt::format("/packages/{}/{}", | |||||
pkg_name, | |||||
pkg_version_str), | |||||
pkg_def); | |||||
acc_pkgs.emplace_back(std::move(pkg)); | |||||
return accept; | |||||
}, | |||||
}}, | |||||
}}, | |||||
}, | |||||
reject_unknown_key("/"), | |||||
}); | |||||
auto rej = std::get_if<semester::dc_reject_t>(&result); | |||||
if (rej) { | |||||
throw_user_error<errc::invalid_catalog_json>(rej->message); | |||||
} | |||||
std::string pkg_name; | |||||
semver::version pkg_version; | |||||
package_info dummy; | |||||
using namespace semester::walk_ops; | |||||
auto convert_pkg_obj | |||||
= [&](auto&& dat) { return parse_pkg_json_v1(pkg_name, pkg_version, dat); }; | |||||
auto convert_version_str = [&](std::string_view str) { | |||||
try { | |||||
return semver::version::parse(str); | |||||
} catch (const semver::invalid_version& e) { | |||||
throw_user_error<errc::invalid_catalog_json>("{}: version string '{}' is invalid: {}", | |||||
walk.path(), | |||||
pkg_name, | |||||
str, | |||||
e.what()); | |||||
} | |||||
}; | |||||
auto import_pkg_versions | |||||
= walk_seq{require_obj{"Package entries must be JSON objects"}, | |||||
mapping{any_key{put_into(pkg_version, convert_version_str), | |||||
require_obj{"Package+version entries must be JSON"}, | |||||
put_into{std::back_inserter(acc_pkgs), convert_pkg_obj}}}}; | |||||
auto import_pkgs = walk_seq{require_obj{"'packages' should be a JSON object"}, | |||||
mapping{any_key{put_into(pkg_name), import_pkg_versions}}}; | |||||
walk(data, | |||||
mapping{ | |||||
if_key{"version", just_accept}, | |||||
required_key{"packages", "'packages' should be an object of packages", import_pkgs}, | |||||
}); | |||||
return acc_pkgs; | return acc_pkgs; | ||||
} | } | ||||
double version = version_it->second.as_number(); | double version = version_it->second.as_number(); | ||||
if (version == 1.0) { | |||||
return parse_json_v1(data); | |||||
} else { | |||||
throw_user_error<errc::invalid_catalog_json>("Unknown catalog JSON version '{}'", version); | |||||
try { | |||||
if (version == 1.0) { | |||||
return parse_json_v1(data); | |||||
} else { | |||||
throw_user_error<errc::invalid_catalog_json>("Unknown catalog JSON version '{}'", | |||||
version); | |||||
} | |||||
} catch (const semester::walk_error& e) { | |||||
throw_user_error<errc::invalid_catalog_json>(e.what()); | |||||
} | } | ||||
} | } |
// Objects in 'packages' shuold have version strings | // Objects in 'packages' shuold have version strings | ||||
"{version:1, packages:{foo:{'lol':{}}}}", | "{version:1, packages:{foo:{'lol':{}}}}", | ||||
"{version:1, packages:{foo:{'1.2':{}}}}", | "{version:1, packages:{foo:{'1.2':{}}}}", | ||||
// No remote | |||||
"{version:1, packages:{foo:{'1.2.3':{}}}}", | |||||
// Bad empty git | |||||
"{version:1, packages:{foo:{'1.2.3':{git:{}}}}}", | |||||
// Git `url` and `ref` should be a string | |||||
"{version:1, packages:{foo:{'1.2.3':{git:{url:2, ref:''}}}}}", | |||||
"{version:1, packages:{foo:{'1.2.3':{git:{url:'', ref:2}}}}}", | |||||
// 'auto-lib' should be a usage string | |||||
"{version:1, packages:{foo:{'1.2.3':{git:{url:'', ref:'', 'auto-lib':3}}}}}", | |||||
"{version:1, packages:{foo:{'1.2.3':{git:{url:'', ref:'', 'auto-lib':'ffasdf'}}}}}", | |||||
// 'transform' should be an array | |||||
R"( | |||||
{ | |||||
version: 1, | |||||
packages: {foo: {'1.2.3': { | |||||
git: { | |||||
url: '', | |||||
ref: '', | |||||
'auto-lib': 'a/b', | |||||
transform: 'lol hi', | |||||
} | |||||
}}} | |||||
} | |||||
)", | |||||
}; | }; | ||||
for (auto bad : bads) { | for (auto bad : bads) { | ||||
"{version:1, packages:{}}", | "{version:1, packages:{}}", | ||||
// No versions for 'foo' is weird, but okay | // No versions for 'foo' is weird, but okay | ||||
"{version:1, packages:{foo:{}}}", | "{version:1, packages:{foo:{}}}", | ||||
// Basic package with minimum info: | |||||
"{version:1, packages:{foo:{'1.2.3':{git:{url:'', ref:''}}}}}", | |||||
// Minimal auto-lib: | |||||
"{version:1, packages:{foo:{'1.2.3':{git:{url:'', ref:'', 'auto-lib':'a/b'}}}}}", | |||||
// Empty transforms: | |||||
R"( | |||||
{ | |||||
version: 1, | |||||
packages: {foo: {'1.2.3': { | |||||
git: { | |||||
url: '', | |||||
ref: '', | |||||
'auto-lib': 'a/b', | |||||
transform: [], | |||||
} | |||||
}}} | |||||
} | |||||
)", | |||||
// Basic transform: | |||||
R"( | |||||
{ | |||||
version: 1, | |||||
packages: {foo: {'1.2.3': { | |||||
git: { | |||||
url: '', | |||||
ref: '', | |||||
'auto-lib': 'a/b', | |||||
transform: [{ | |||||
copy: { | |||||
from: 'here', | |||||
to: 'there', | |||||
include: [ | |||||
"*.c", | |||||
"*.cpp", | |||||
"*.h", | |||||
'*.txt' | |||||
] | |||||
} | |||||
}], | |||||
} | |||||
}}} | |||||
} | |||||
)", | |||||
}; | }; | ||||
for (auto good : goods) { | for (auto good : goods) { | ||||
INFO("Parse: " << good); | INFO("Parse: " << good); | ||||
'1.2.3': { | '1.2.3': { | ||||
git: { | git: { | ||||
url: 'foo', | url: 'foo', | ||||
ref: 'fasdf' | |||||
ref: 'fasdf', | |||||
'auto-lib': 'a/b', | |||||
} | } | ||||
} | } | ||||
} | } | ||||
} | } | ||||
})"); | })"); | ||||
CHECK(pkgs.size() == 1); | |||||
REQUIRE(pkgs.size() == 1); | |||||
CHECK(pkgs[0].ident.name == "foo"); | CHECK(pkgs[0].ident.name == "foo"); | ||||
CHECK(pkgs[0].ident.to_string() == "foo@1.2.3"); | CHECK(pkgs[0].ident.to_string() == "foo@1.2.3"); | ||||
CHECK(std::holds_alternative<dds::git_remote_listing>(pkgs[0].remote)); | CHECK(std::holds_alternative<dds::git_remote_listing>(pkgs[0].remote)); | ||||
auto git = std::get<dds::git_remote_listing>(pkgs[0].remote); | |||||
CHECK(git.url == "foo"); | |||||
CHECK(git.ref == "fasdf"); | |||||
REQUIRE(git.auto_lib); | |||||
CHECK(git.auto_lib->namespace_ == "a"); | |||||
CHECK(git.auto_lib->name == "b"); | |||||
} | } |
std::string description; | std::string description; | ||||
std::variant<std::monostate, git_remote_listing> remote; | std::variant<std::monostate, git_remote_listing> remote; | ||||
std::vector<fs_transformation> transforms; | |||||
}; | }; | ||||
} // namespace dds | } // namespace dds |
#include <dds/catalog/get.hpp> | #include <dds/catalog/get.hpp> | ||||
#include <dds/util/fs.hpp> | #include <dds/util/fs.hpp> | ||||
#include <dds/util/fs_transform.hpp> | |||||
#include <libman/package.hpp> | #include <libman/package.hpp> | ||||
std::string ref; | std::string ref; | ||||
std::optional<lm::usage> auto_lib; | std::optional<lm::usage> auto_lib; | ||||
std::vector<fs_transformation> transforms; | |||||
void clone(path_ref path) const; | void clone(path_ref path) const; | ||||
}; | }; | ||||
no_catalog_remote_info, | no_catalog_remote_info, | ||||
git_clone_failure, | git_clone_failure, | ||||
invalid_repo_transform, | |||||
sdist_ident_mismatch, | sdist_ident_mismatch, | ||||
sdist_exists, | sdist_exists, | ||||
#include "./fs_transform.hpp" | |||||
#include <dds/error/errors.hpp> | |||||
#include <dds/util/fs.hpp> | |||||
#include <range/v3/algorithm/any_of.hpp> | |||||
#include <range/v3/distance.hpp> | |||||
#include <range/v3/numeric/accumulate.hpp> | |||||
#include <semester/walk.hpp> | |||||
#include <nlohmann/json.hpp> | |||||
#include <iostream> | |||||
using namespace dds; | |||||
using require_obj = semester::require_type<json5::data::mapping_type>; | |||||
using require_array = semester::require_type<json5::data::array_type>; | |||||
using require_str = semester::require_type<std::string>; | |||||
dds::fs_transformation dds::fs_transformation::from_json(const json5::data& data) { | |||||
fs_transformation ret; | |||||
using namespace semester::walk_ops; | |||||
auto prep_optional = [](auto& opt) { | |||||
return [&](auto&&) { | |||||
opt.emplace(); | |||||
return walk.pass; | |||||
}; | |||||
}; | |||||
auto str_to_path = [](std::string const& s) { | |||||
auto p = fs::path(s); | |||||
if (p.is_absolute()) { | |||||
throw semester::walk_error(std::string(walk.path()) | |||||
+ ": Only relative paths are accepted"); | |||||
} | |||||
return p; | |||||
}; | |||||
auto get_strip_components = [](double d) { | |||||
if (d != double(int(d)) || d < 0) { | |||||
throw semester::walk_error(std::string(walk.path()) + ": " | |||||
+ "'strip-components' should be a positive whole number"); | |||||
} | |||||
return int(d); | |||||
}; | |||||
auto populate_globs = [&](std::vector<dds::glob>& globs) { | |||||
return for_each{ | |||||
require_str{"Include/exclude list should be a list of globs"}, | |||||
put_into(std::back_inserter(globs), | |||||
[](const std::string& glob) { | |||||
try { | |||||
return dds::glob::compile(glob); | |||||
} catch (const std::runtime_error& e) { | |||||
throw semester::walk_error{std::string(walk.path()) + ": " + e.what()}; | |||||
} | |||||
}), | |||||
}; | |||||
}; | |||||
auto populate_reloc = [&](auto& op) { | |||||
return [&](auto&& dat) { | |||||
op.emplace(); | |||||
return mapping{ | |||||
required_key{"from", | |||||
"a 'from' path is required", | |||||
require_str{"'from' should be a path string"}, | |||||
put_into(op->from, str_to_path)}, | |||||
required_key{"to", | |||||
"a 'to' path is required", | |||||
require_str{"'to' should be a path string"}, | |||||
put_into(op->to, str_to_path)}, | |||||
if_key{"strip-components", | |||||
require_type<double>{"'strip-components' should be an integer"}, | |||||
put_into(op->strip_components, get_strip_components)}, | |||||
if_key{"include", | |||||
require_array{"'include' should be an array"}, | |||||
populate_globs(op->include)}, | |||||
if_key{"exclude", | |||||
require_array{"'exclude' should be an array"}, | |||||
populate_globs(op->exclude)}, | |||||
}(dat); | |||||
}; | |||||
}; | |||||
walk(data, | |||||
require_obj{"Each transform must be a JSON object"}, | |||||
mapping{ | |||||
if_key{"copy", populate_reloc(ret.copy)}, | |||||
if_key{"move", populate_reloc(ret.move)}, | |||||
if_key{"remove", | |||||
require_obj{"'remove' should be a JSON object"}, | |||||
prep_optional(ret.remove), | |||||
mapping{ | |||||
required_key{"path", | |||||
"'path' is required", | |||||
require_str{"'path' should be a string path to remove"}, | |||||
put_into(ret.remove->path, str_to_path)}, | |||||
if_key{"only-matching", | |||||
require_array{"'only-matching' should be an array of globs"}, | |||||
populate_globs(ret.remove->only_matching)}, | |||||
}}, | |||||
if_key{"write", | |||||
require_obj{"'write' should be a JSON object"}, | |||||
prep_optional(ret.write), | |||||
mapping{ | |||||
required_key{"path", | |||||
"'path' is required", | |||||
require_str{"'path' should be a string path to write to"}, | |||||
put_into(ret.write->path, str_to_path)}, | |||||
required_key{"content", | |||||
"'content' is required", | |||||
require_str{"'content' must be a string"}, | |||||
put_into(ret.write->content)}, | |||||
}}, | |||||
}); | |||||
return ret; | |||||
} | |||||
namespace { | |||||
bool matches_any(path_ref path, const std::vector<glob>& globs) { | |||||
return std::any_of(globs.begin(), globs.end(), [&](auto&& gl) { return gl.match(path); }); | |||||
} | |||||
bool parent_dir_of(fs::path root, fs::path child) { | |||||
auto root_str = (root += "/").lexically_normal().generic_string(); | |||||
auto child_str = (child += "/").lexically_normal().generic_string(); | |||||
return child_str.find(root_str) == 0; | |||||
} | |||||
void do_relocate(const dds::fs_transformation::copy_move_base& oper, | |||||
dds::path_ref root, | |||||
bool is_copy) { | |||||
auto from = fs::weakly_canonical(root / oper.from); | |||||
auto to = fs::weakly_canonical(root / oper.to); | |||||
if (!parent_dir_of(root, from)) { | |||||
throw_external_error<errc::invalid_repo_transform>( | |||||
"Filesystem transformation attempts to copy/move a file/directory from outside of the " | |||||
"root [{}] into the root [{}].", | |||||
from.string(), | |||||
root.string()); | |||||
} | |||||
if (!parent_dir_of(root, to)) { | |||||
throw_external_error<errc::invalid_repo_transform>( | |||||
"Filesystem transformation attempts to copy/move a file/directory [{}] to a " | |||||
"destination outside of the restricted root [{}].", | |||||
to.string(), | |||||
root.string()); | |||||
} | |||||
if (!fs::exists(from)) { | |||||
throw_external_error<errc::invalid_repo_transform>( | |||||
"Filesystem transformation attempting to copy/move a non-existint file/directory [{}] " | |||||
"to [{}].", | |||||
from.string(), | |||||
to.string()); | |||||
} | |||||
fs::create_directories(to.parent_path()); | |||||
if (fs::is_regular_file(from)) { | |||||
if (is_copy) { | |||||
fs::copy_file(from, to, fs::copy_options::overwrite_existing); | |||||
} else { | |||||
safe_rename(from, to); | |||||
} | |||||
return; | |||||
} | |||||
for (auto item : fs::recursive_directory_iterator(from)) { | |||||
auto relpath = fs::relative(item, from); | |||||
auto matches_glob = [&](auto glob) { return glob.match(relpath.string()); }; | |||||
auto included = oper.include.empty() || ranges::any_of(oper.include, matches_glob); | |||||
auto excluded = ranges::any_of(oper.exclude, matches_glob); | |||||
if (!included || excluded) { | |||||
continue; | |||||
} | |||||
auto n_components = ranges::distance(relpath); | |||||
if (n_components <= oper.strip_components) { | |||||
continue; | |||||
} | |||||
auto it = relpath.begin(); | |||||
std::advance(it, oper.strip_components); | |||||
relpath = ranges::accumulate(it, relpath.end(), fs::path(), std::divides<>()); | |||||
auto dest = to / relpath; | |||||
fs::create_directories(dest.parent_path()); | |||||
if (item.is_directory()) { | |||||
fs::create_directories(dest); | |||||
} else { | |||||
if (is_copy) { | |||||
fs::copy_file(item, dest, fs::copy_options::overwrite_existing); | |||||
} else { | |||||
safe_rename(item, dest); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
void do_remove(const struct fs_transformation::remove& oper, path_ref root) { | |||||
auto from = fs::weakly_canonical(root / oper.path); | |||||
if (!parent_dir_of(root, from)) { | |||||
throw_external_error<errc::invalid_repo_transform>( | |||||
"Filesystem transformation attempts to deletes files/directories outside of the " | |||||
"root. Attempted to remove [{}]. Removal is restricted to [{}].", | |||||
from.string(), | |||||
root.string()); | |||||
} | |||||
if (!fs::exists(from)) { | |||||
throw_external_error<errc::invalid_repo_transform>( | |||||
"Filesystem transformation attempts to delete a non-existint file/directory [{}].", | |||||
from.string()); | |||||
} | |||||
if (fs::is_directory(from)) { | |||||
for (auto child : fs::recursive_directory_iterator{from}) { | |||||
if (child.is_directory()) { | |||||
continue; | |||||
} | |||||
if (!oper.only_matching.empty() && !matches_any(child, oper.only_matching)) { | |||||
continue; | |||||
} | |||||
fs::remove_all(child); | |||||
} | |||||
} else { | |||||
fs::remove_all(from); | |||||
} | |||||
} | |||||
void do_write(const struct fs_transformation::write& oper, path_ref root) { | |||||
auto dest = fs::weakly_canonical(root / oper.path); | |||||
if (!parent_dir_of(root, dest)) { | |||||
throw_external_error<errc::invalid_repo_transform>( | |||||
"Filesystem transformation is trying to write outside of the root. Attempted to write " | |||||
"to [{}]. Writing is restricted to [{}].", | |||||
dest.string(), | |||||
root.string()); | |||||
} | |||||
std::cout << "Write content: " << oper.content; | |||||
auto of = dds::open(dest, std::ios::binary | std::ios::out); | |||||
of << oper.content; | |||||
} | |||||
} // namespace | |||||
void dds::fs_transformation::apply_to(dds::path_ref root) const { | |||||
if (copy) { | |||||
do_relocate(*copy, root, true); | |||||
} | |||||
if (move) { | |||||
do_relocate(*move, root, false); | |||||
} | |||||
if (remove) { | |||||
do_remove(*remove, root); | |||||
} | |||||
if (write) { | |||||
do_write(*write, root); | |||||
} | |||||
} | |||||
namespace { | |||||
nlohmann::json reloc_as_json(const fs_transformation::copy_move_base& oper) { | |||||
auto obj = nlohmann::json::object(); | |||||
obj["from"] = oper.from.string(); | |||||
obj["to"] = oper.to.string(); | |||||
obj["strip-components"] = oper.strip_components; | |||||
auto inc_list = nlohmann::json::array(); | |||||
for (auto& inc : oper.include) { | |||||
inc_list.push_back(inc.string()); | |||||
} | |||||
auto exc_list = nlohmann::json::array(); | |||||
for (auto& exc : oper.exclude) { | |||||
exc_list.push_back(exc.string()); | |||||
} | |||||
if (!inc_list.empty()) { | |||||
obj["include"] = inc_list; | |||||
} | |||||
if (!exc_list.empty()) { | |||||
obj["exclude"] = exc_list; | |||||
} | |||||
return obj; | |||||
} | |||||
} // namespace | |||||
std::string fs_transformation::as_json() const noexcept { | |||||
auto obj = nlohmann::json::object(); | |||||
if (copy) { | |||||
obj["copy"] = reloc_as_json(*copy); | |||||
} | |||||
if (move) { | |||||
obj["move"] = reloc_as_json(*move); | |||||
} | |||||
if (remove) { | |||||
auto rm = nlohmann::json::object(); | |||||
rm["path"] = remove->path.string(); | |||||
if (!remove->only_matching.empty()) { | |||||
auto if_arr = nlohmann::json::array(); | |||||
for (auto&& gl : remove->only_matching) { | |||||
if_arr.push_back(gl.string()); | |||||
} | |||||
rm["only-matching"] = rm; | |||||
} | |||||
obj["remove"] = rm; | |||||
} | |||||
if (write) { | |||||
auto wr = nlohmann::json::object(); | |||||
wr["path"] = write->path.string(); | |||||
wr["content"] = write->content; | |||||
obj["write"] = wr; | |||||
} | |||||
return to_string(obj); | |||||
} |
#include "./fs.hpp" | #include "./fs.hpp" | ||||
#include "./glob.hpp" | #include "./glob.hpp" | ||||
#include <json5/data.hpp> | |||||
#include <optional> | #include <optional> | ||||
#include <variant> | #include <variant> | ||||
namespace dds { | namespace dds { | ||||
class fs_transformation { | |||||
struct fs_transformation { | |||||
struct copy_move_base { | struct copy_move_base { | ||||
fs::path from; | fs::path from; | ||||
fs::path to; | fs::path to; | ||||
std::vector<dds::glob> only_matching; | std::vector<dds::glob> only_matching; | ||||
}; | }; | ||||
std::optional<struct copy> copy; | |||||
std::optional<struct move> move; | |||||
std::optional<remove> remove; | |||||
struct write { | |||||
fs::path path; | |||||
std::string content; | |||||
}; | |||||
std::optional<struct copy> copy; | |||||
std::optional<struct move> move; | |||||
std::optional<remove> remove; | |||||
std::optional<struct write> write; | |||||
void apply_to(path_ref root) const; | void apply_to(path_ref root) const; | ||||
static fs_transformation from_json(const json5::data&); | |||||
std::string as_json() const noexcept; | |||||
}; | }; | ||||
} // namespace dds | } // namespace dds |
using namespace dds::detail; | using namespace dds::detail; | ||||
glob_impl acc{}; | glob_impl acc{}; | ||||
acc.spelling = std::string(pattern); | |||||
while (!pattern.empty()) { | while (!pattern.empty()) { | ||||
const auto next_slash = pattern.find('/'); | const auto next_slash = pattern.find('/'); | ||||
throw std::runtime_error("Invalid path glob expression (Must not be empty!)"); | throw std::runtime_error("Invalid path glob expression (Must not be empty!)"); | ||||
} | } | ||||
acc.spelling = std::string(pattern); | |||||
return acc; | return acc; | ||||
} | } | ||||
{ | |||||
"compiler_id": 'gnu', | |||||
"cxx_version": 'c++17', | |||||
"cxx_compiler": 'g++-9', | |||||
} |
{ | |||||
"compiler_id": 'msvc', | |||||
} |
{ | |||||
"version": 1, | |||||
"packages": { | |||||
"catch2": { | |||||
"2.12.4": { | |||||
"git": { | |||||
"url": "https://github.com/catchorg/Catch2.git", | |||||
"ref": "v2.12.4", | |||||
"auto-lib": "catch2/catch2", | |||||
"transform": [ | |||||
{ | |||||
"move": { | |||||
"from": "include", | |||||
"to": "include/catch2", | |||||
} | |||||
}, | |||||
{ | |||||
"copy": { | |||||
"from": "include", | |||||
"to": "src" | |||||
}, | |||||
write: { | |||||
path: 'include/catch2/catch_with_main.hpp', | |||||
content: '\ | |||||
#pragma once \n\ | |||||
\n\ | |||||
#define CATCH_CONFIG_MAIN \n\ | |||||
#include "./catch.hpp" \n\ | |||||
\n\ | |||||
namespace Catch { \n\ | |||||
\n\ | |||||
CATCH_REGISTER_REPORTER("console", ConsoleReporter) \n\ | |||||
\n\ | |||||
} // namespace Catch \n\ | |||||
' | |||||
} | |||||
} | |||||
] | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
{ | |||||
name: 'use-catch2', | |||||
uses: ['catch2/catch2'] | |||||
} |
{ | |||||
name: 'use-catch2', | |||||
version: '1.0.0', | |||||
namespace: 'test', | |||||
depends: { | |||||
'catch2': '2.12.4' | |||||
} | |||||
} |
#include <catch2/catch_with_main.hpp> | |||||
TEST_CASE("I am a simple test case") { | |||||
CHECK((2 + 2) == 4); | |||||
CHECK_FALSE((2 + 2) == 5); | |||||
} |
from tests import DDS | |||||
from dds_ci import proc | |||||
def test_get_build_use_catch2(dds: DDS): | |||||
dds.catalog_import(dds.source_root / 'catalog.json5') | |||||
tc_fname = 'gcc.tc.jsonc' if 'gcc' in dds.default_builtin_toolchain else 'msvc.tc.jsonc' | |||||
tc = str(dds.test_dir / tc_fname) | |||||
dds.build(toolchain=tc) | |||||
proc.check_run((dds.build_dir / 'use-catch2').with_suffix(dds.exe_suffix)) |
{ | |||||
"compiler_id": 'gnu', | |||||
"cxx_version": 'c++17', | |||||
"cxx_compiler": 'g++-9', | |||||
"flags": '-march=native', | |||||
"link_flags": '-static-libgcc -static-libstdc++' | |||||
} |
{ | |||||
"compiler_id": 'msvc', | |||||
} |
{ | |||||
"version": 1, | |||||
"packages": { | |||||
"cryptopp": { | |||||
"8.2.0": { | |||||
"git": { | |||||
"url": "https://github.com/weidai11/cryptopp.git", | |||||
"ref": "CRYPTOPP_8_2_0", | |||||
"auto-lib": "cryptopp/cryptopp", | |||||
"transform": [ | |||||
{ | |||||
"copy": { | |||||
"from": ".", | |||||
"to": "src/cryptopp", | |||||
"include": [ | |||||
"*.c", | |||||
"*.cpp", | |||||
"*.h" | |||||
] | |||||
} | |||||
} | |||||
] | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
{ | |||||
name: 'use-cryptopp', | |||||
uses: ['cryptopp/cryptopp'] | |||||
} |
{ | |||||
name: 'use-cryptopp', | |||||
version: '1.0.0', | |||||
namespace: 'test', | |||||
depends: { | |||||
'cryptopp': '8.2.0' | |||||
} | |||||
} |
#include <cryptopp/osrng.h> | |||||
#include <string> | |||||
int main() { | |||||
std::string arr; | |||||
arr.resize(256); | |||||
CryptoPP::OS_GenerateRandomBlock(false, | |||||
reinterpret_cast<CryptoPP::byte*>(arr.data()), | |||||
arr.size()); | |||||
for (auto b : arr) { | |||||
if (b != '\x00') { | |||||
return 0; | |||||
} | |||||
} | |||||
return 1; | |||||
} |
from tests import DDS | |||||
from dds_ci import proc | |||||
def test_get_build_use_libsodium(dds: DDS): | |||||
dds.catalog_import(dds.source_root / 'catalog.json') | |||||
tc_fname = 'gcc.tc.jsonc' if 'gcc' in dds.default_builtin_toolchain else 'msvc.tc.jsonc' | |||||
tc = str(dds.test_dir / tc_fname) | |||||
dds.build(toolchain=tc) | |||||
proc.check_run( | |||||
(dds.build_dir / 'use-cryptopp').with_suffix(dds.exe_suffix)) |
"git": { | "git": { | ||||
"url": "https://github.com/jedisct1/libsodium.git", | "url": "https://github.com/jedisct1/libsodium.git", | ||||
"ref": "1.0.18", | "ref": "1.0.18", | ||||
"auto-lib": "sodium/sodium" | |||||
}, | |||||
"transform": [ | |||||
{ | |||||
"move": { | |||||
"from": "src/libsodium/include", | |||||
"to": "include/" | |||||
"auto-lib": "sodium/sodium", | |||||
"transform": [ | |||||
{ | |||||
"move": { | |||||
"from": "src/libsodium/include", | |||||
"to": "include/" | |||||
} | |||||
}, | |||||
{ | |||||
"copy": { | |||||
"from": "builds/msvc/version.h", | |||||
"to": "include/sodium/version.h" | |||||
} | |||||
}, | |||||
{ | |||||
"copy": { | |||||
"from": "include/sodium", | |||||
"to": "src/libsodium" | |||||
}, | |||||
"move": { | |||||
"from": "src/libsodium", | |||||
"to": "src_root" | |||||
}, | |||||
"remove": { | |||||
"path": "src" | |||||
} | |||||
}, | |||||
{ | |||||
"move": { | |||||
"from": "src_root", | |||||
"to": "src" | |||||
} | |||||
} | } | ||||
}, | |||||
{ | |||||
"copy": { | |||||
"from": "builds/msvc/version.h", | |||||
"to": "include/sodium/version.h" | |||||
} | |||||
}, | |||||
{ | |||||
"copy": { | |||||
"from": "include/sodium", | |||||
"to": "src/libsodium" | |||||
} | |||||
}, | |||||
{ | |||||
"move": { | |||||
"from": "src/libsodium", | |||||
"to": "src" | |||||
} | |||||
} | |||||
] | |||||
] | |||||
} | |||||
} | } | ||||
} | } | ||||
} | } |
auto_lib: Optional[str] = None | auto_lib: Optional[str] = None | ||||
def to_dict(self) -> dict: | def to_dict(self) -> dict: | ||||
return { | |||||
d = { | |||||
'url': self.url, | 'url': self.url, | ||||
'ref': self.ref, | 'ref': self.ref, | ||||
'auto-lib': self.auto_lib, | |||||
} | } | ||||
if self.auto_lib: | |||||
d['auto-lib'] = self.auto_lib | |||||
return d | |||||
RemoteInfo = Union[Git] | RemoteInfo = Union[Git] | ||||
'0.2.0', | '0.2.0', | ||||
'0.2.1', | '0.2.1', | ||||
'0.2.2', | '0.2.2', | ||||
'0.3.0', | |||||
'0.3.1', | |||||
'0.3.2', | |||||
), | ), | ||||
description= | description= | ||||
'Minimal C++ concepts library. Contains many definitions from C++20.', | 'Minimal C++ concepts library. Contains many definitions from C++20.', | ||||
'neo-fun': '^0.1.1', | 'neo-fun': '^0.1.1', | ||||
'neo-concepts': '^0.2.2', | 'neo-concepts': '^0.2.2', | ||||
}), | }), | ||||
Version( | |||||
'0.2.0', | |||||
description='A C++ library to process recursive dynamic data', | |||||
remote=Git('https://github.com/vector-of-bool/semester.git', | |||||
'0.2.0'), | |||||
depends={ | |||||
'neo-fun': '^0.3.2', | |||||
'neo-concepts': '^0.3.2', | |||||
}), | |||||
Version( | |||||
'0.2.1', | |||||
description='A C++ library to process recursive dynamic data', | |||||
remote=Git('https://github.com/vector-of-bool/semester.git', | |||||
'0.2.1'), | |||||
depends={ | |||||
'neo-fun': '^0.3.2', | |||||
'neo-concepts': '^0.3.2', | |||||
}), | |||||
]), | ]), | ||||
Package('ctre', [ | Package('ctre', [ | ||||
Version( | Version( |