| "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( |