@@ -234,7 +234,6 @@ | |||
"depends": {}, | |||
"description": "The Windows Implementation Library", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/2020.03.16", | |||
"url": "https://github.com/vector-of-bool/wil.git" | |||
} | |||
@@ -245,7 +244,6 @@ | |||
"depends": {}, | |||
"description": "Minimal C++ concepts library. Contains many definitions from C++20.", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.1.0", | |||
"url": "https://github.com/vector-of-bool/neo-concepts.git" | |||
} | |||
@@ -254,7 +252,6 @@ | |||
"depends": {}, | |||
"description": "Minimal C++ concepts library. Contains many definitions from C++20.", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.2.0", | |||
"url": "https://github.com/vector-of-bool/neo-concepts.git" | |||
} | |||
@@ -263,7 +260,6 @@ | |||
"depends": {}, | |||
"description": "Minimal C++ concepts library. Contains many definitions from C++20.", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.2.1", | |||
"url": "https://github.com/vector-of-bool/neo-concepts.git" | |||
} | |||
@@ -272,10 +268,33 @@ | |||
"depends": {}, | |||
"description": "Minimal C++ concepts library. Contains many definitions from C++20.", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.2.2", | |||
"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": { | |||
@@ -283,7 +302,6 @@ | |||
"depends": {}, | |||
"description": "Some library fundamentals that you might find useful", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.1.0", | |||
"url": "https://github.com/vector-of-bool/neo-fun.git" | |||
} | |||
@@ -292,7 +310,6 @@ | |||
"depends": {}, | |||
"description": "Some library fundamentals that you might find useful", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.1.1", | |||
"url": "https://github.com/vector-of-bool/neo-fun.git" | |||
} | |||
@@ -301,7 +318,6 @@ | |||
"depends": {}, | |||
"description": "Some library fundamentals that you might find useful", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.2.0", | |||
"url": "https://github.com/vector-of-bool/neo-fun.git" | |||
} | |||
@@ -310,7 +326,6 @@ | |||
"depends": {}, | |||
"description": "Some library fundamentals that you might find useful", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.2.1", | |||
"url": "https://github.com/vector-of-bool/neo-fun.git" | |||
} | |||
@@ -319,7 +334,6 @@ | |||
"depends": {}, | |||
"description": "Some library fundamentals that you might find useful", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.3.0", | |||
"url": "https://github.com/vector-of-bool/neo-fun.git" | |||
} | |||
@@ -328,7 +342,6 @@ | |||
"depends": {}, | |||
"description": "Some library fundamentals that you might find useful", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.3.1", | |||
"url": "https://github.com/vector-of-bool/neo-fun.git" | |||
} | |||
@@ -337,7 +350,6 @@ | |||
"depends": {}, | |||
"description": "Some library fundamentals that you might find useful", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.3.2", | |||
"url": "https://github.com/vector-of-bool/neo-fun.git" | |||
} | |||
@@ -348,7 +360,6 @@ | |||
"depends": {}, | |||
"description": "A modern and low-level C++ SQLite API", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.1.0", | |||
"url": "https://github.com/vector-of-bool/neo-sqlite3.git" | |||
} | |||
@@ -357,7 +368,6 @@ | |||
"depends": {}, | |||
"description": "A modern and low-level C++ SQLite API", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.2.0", | |||
"url": "https://github.com/vector-of-bool/neo-sqlite3.git" | |||
} | |||
@@ -366,7 +376,6 @@ | |||
"depends": {}, | |||
"description": "A modern and low-level C++ SQLite API", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.2.1", | |||
"url": "https://github.com/vector-of-bool/neo-sqlite3.git" | |||
} | |||
@@ -375,7 +384,6 @@ | |||
"depends": {}, | |||
"description": "A modern and low-level C++ SQLite API", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.2.2", | |||
"url": "https://github.com/vector-of-bool/neo-sqlite3.git" | |||
} | |||
@@ -384,7 +392,6 @@ | |||
"depends": {}, | |||
"description": "A modern and low-level C++ SQLite API", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.2.3", | |||
"url": "https://github.com/vector-of-bool/neo-sqlite3.git" | |||
} | |||
@@ -395,7 +402,6 @@ | |||
"depends": {}, | |||
"description": "JSON for Modern C++", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/3.0.0", | |||
"url": "https://github.com/vector-of-bool/json.git" | |||
} | |||
@@ -404,7 +410,6 @@ | |||
"depends": {}, | |||
"description": "JSON for Modern C++", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/3.0.1", | |||
"url": "https://github.com/vector-of-bool/json.git" | |||
} | |||
@@ -413,7 +418,6 @@ | |||
"depends": {}, | |||
"description": "JSON for Modern C++", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/3.1.0", | |||
"url": "https://github.com/vector-of-bool/json.git" | |||
} | |||
@@ -422,7 +426,6 @@ | |||
"depends": {}, | |||
"description": "JSON for Modern C++", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/3.1.1", | |||
"url": "https://github.com/vector-of-bool/json.git" | |||
} | |||
@@ -431,7 +434,6 @@ | |||
"depends": {}, | |||
"description": "JSON for Modern C++", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/3.1.2", | |||
"url": "https://github.com/vector-of-bool/json.git" | |||
} | |||
@@ -440,7 +442,6 @@ | |||
"depends": {}, | |||
"description": "JSON for Modern C++", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/3.2.0", | |||
"url": "https://github.com/vector-of-bool/json.git" | |||
} | |||
@@ -449,7 +450,6 @@ | |||
"depends": {}, | |||
"description": "JSON for Modern C++", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/3.3.0", | |||
"url": "https://github.com/vector-of-bool/json.git" | |||
} | |||
@@ -458,7 +458,6 @@ | |||
"depends": {}, | |||
"description": "JSON for Modern C++", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/3.4.0", | |||
"url": "https://github.com/vector-of-bool/json.git" | |||
} | |||
@@ -467,7 +466,6 @@ | |||
"depends": {}, | |||
"description": "JSON for Modern C++", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/3.5.0", | |||
"url": "https://github.com/vector-of-bool/json.git" | |||
} | |||
@@ -476,7 +474,6 @@ | |||
"depends": {}, | |||
"description": "JSON for Modern C++", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/3.6.0", | |||
"url": "https://github.com/vector-of-bool/json.git" | |||
} | |||
@@ -485,7 +482,6 @@ | |||
"depends": {}, | |||
"description": "JSON for Modern C++", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/3.6.1", | |||
"url": "https://github.com/vector-of-bool/json.git" | |||
} | |||
@@ -494,7 +490,6 @@ | |||
"depends": {}, | |||
"description": "JSON for Modern C++", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/3.7.0", | |||
"url": "https://github.com/vector-of-bool/json.git" | |||
} | |||
@@ -503,7 +498,6 @@ | |||
"depends": {}, | |||
"description": "JSON for Modern C++", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/3.7.1", | |||
"url": "https://github.com/vector-of-bool/json.git" | |||
} | |||
@@ -512,7 +506,6 @@ | |||
"depends": {}, | |||
"description": "JSON for Modern C++", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/3.7.2", | |||
"url": "https://github.com/vector-of-bool/json.git" | |||
} | |||
@@ -521,7 +514,6 @@ | |||
"depends": {}, | |||
"description": "JSON for Modern C++", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "dds/3.7.3", | |||
"url": "https://github.com/vector-of-bool/json.git" | |||
} | |||
@@ -532,7 +524,6 @@ | |||
"depends": {}, | |||
"description": "A C++ implementation of the Pubgrub version solving algorithm", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.1.2", | |||
"url": "https://github.com/vector-of-bool/pubgrub.git" | |||
} | |||
@@ -541,7 +532,6 @@ | |||
"depends": {}, | |||
"description": "A C++ implementation of the Pubgrub version solving algorithm", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.2.0", | |||
"url": "https://github.com/vector-of-bool/pubgrub.git" | |||
} | |||
@@ -550,7 +540,6 @@ | |||
"depends": {}, | |||
"description": "A C++ implementation of the Pubgrub version solving algorithm", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.2.1", | |||
"url": "https://github.com/vector-of-bool/pubgrub.git" | |||
} | |||
@@ -599,7 +588,6 @@ | |||
"depends": {}, | |||
"description": "A C++ library that implements Semantic Versioning parsing, emitting, types, ordering, and operations. See https://semver.org/", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.2.1", | |||
"url": "https://github.com/vector-of-bool/semver.git" | |||
} | |||
@@ -608,7 +596,6 @@ | |||
"depends": {}, | |||
"description": "A C++ library that implements Semantic Versioning parsing, emitting, types, ordering, and operations. See https://semver.org/", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.2.2", | |||
"url": "https://github.com/vector-of-bool/semver.git" | |||
} | |||
@@ -792,7 +779,6 @@ | |||
"depends": {}, | |||
"description": "A C++ implementation of a JSON5 parser", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.1.5", | |||
"url": "https://github.com/vector-of-bool/json5.git" | |||
} | |||
@@ -806,7 +792,6 @@ | |||
}, | |||
"description": "A C++ library to process recursive dynamic data", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.1.0", | |||
"url": "https://github.com/vector-of-bool/semester.git" | |||
} | |||
@@ -818,10 +803,31 @@ | |||
}, | |||
"description": "A C++ library to process recursive dynamic data", | |||
"git": { | |||
"auto-lib": null, | |||
"ref": "0.1.1", | |||
"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" | |||
} | |||
} | |||
} | |||
}, |
@@ -9,11 +9,11 @@ | |||
"range-v3": "0.10.0", | |||
"nlohmann-json": "3.7.1", | |||
"neo-sqlite3": "0.2.3", | |||
"neo-fun": "0.3.0", | |||
"neo-fun": "0.3.2", | |||
"semver": "0.2.2", | |||
"pubgrub": "0.2.1", | |||
"vob-json5": "0.1.5", | |||
"vob-semester": "0.1.1", | |||
"vob-semester": "0.2.1", | |||
"ctre": "2.7.0", | |||
}, | |||
"test_driver": "Catch-Main" |
@@ -270,13 +270,13 @@ struct cli_catalog { | |||
// 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_ref) { | |||
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) { | |||
git.auto_lib = lm::split_usage_string(auto_lib.Get()); | |||
} |
@@ -6,7 +6,9 @@ | |||
#include <dds/error/errors.hpp> | |||
#include <dds/solve/solve.hpp> | |||
#include <json5/parse_data.hpp> | |||
#include <neo/assert.hpp> | |||
#include <neo/concepts.hpp> | |||
#include <neo/sqlite3/exec.hpp> | |||
#include <neo/sqlite3/iter_tuples.hpp> | |||
#include <neo/sqlite3/single.hpp> | |||
@@ -120,20 +122,6 @@ void check_json(bool b, std::string_view what) { | |||
} | |||
} | |||
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 | |||
catalog catalog::open(const std::string& db_path) { | |||
@@ -165,6 +153,21 @@ void catalog::_store_pkg(const package_info& pkg, std::monostate) { | |||
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) { | |||
auto lm_usage = git.auto_lib.value_or(lm::usage{}); | |||
sqlite3::exec( // | |||
@@ -197,9 +200,8 @@ void catalog::_store_pkg(const package_info& pkg, const git_remote_listing& git) | |||
git.ref, | |||
lm_usage.name, | |||
lm_usage.namespace_, | |||
pkg.description | |||
//, transform_to_json(pkg.transforms)) | |||
)); | |||
pkg.description, | |||
transforms_to_json(git.transforms))); | |||
} | |||
void catalog::store(const package_info& pkg) { | |||
@@ -293,18 +295,33 @@ std::optional<package_info> catalog::get(const package_id& pk_id) const noexcept | |||
*git_url, | |||
*git_ref, | |||
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()) { | |||
auto tr_json = nlohmann::json::parse(repo_transform); | |||
auto tr_json = json5::parse_data(repo_transform); | |||
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)); | |||
/// 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; | |||
} |
@@ -31,10 +31,9 @@ temporary_sdist do_pull_sdist(const package_info& listing, const git_remote_list | |||
spdlog::info("Cloning Git repository: {} [{}] ...", git.url, git.ref); | |||
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 ..."); | |||
if (git.auto_lib.has_value()) { |
@@ -4,213 +4,177 @@ | |||
#include <json5/parse_data.hpp> | |||
#include <neo/assert.hpp> | |||
#include <semester/decomp.hpp> | |||
#include <semester/walk.hpp> | |||
#include <spdlog/fmt/fmt.h> | |||
#include <optional> | |||
using namespace dds; | |||
template <typename... Args> | |||
template <typename KeyFunc, typename... Args> | |||
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> | |||
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 { | |||
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; | |||
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; | |||
} | |||
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::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; | |||
} | |||
@@ -237,9 +201,14 @@ std::vector<package_info> dds::parse_packages_json(std::string_view content) { | |||
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()); | |||
} | |||
} |
@@ -37,6 +37,30 @@ TEST_CASE("Valid/invalid package JSON5") { | |||
// Objects in 'packages' shuold have version strings | |||
"{version:1, packages:{foo:{'lol':{}}}}", | |||
"{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) { | |||
@@ -50,6 +74,49 @@ TEST_CASE("Valid/invalid package JSON5") { | |||
"{version:1, packages:{}}", | |||
// No versions for 'foo' is weird, but okay | |||
"{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) { | |||
INFO("Parse: " << good); | |||
@@ -66,14 +133,22 @@ TEST_CASE("Check a single object") { | |||
'1.2.3': { | |||
git: { | |||
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.to_string() == "foo@1.2.3"); | |||
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"); | |||
} |
@@ -20,8 +20,6 @@ struct package_info { | |||
std::string description; | |||
std::variant<std::monostate, git_remote_listing> remote; | |||
std::vector<fs_transformation> transforms; | |||
}; | |||
} // namespace dds |
@@ -2,6 +2,7 @@ | |||
#include <dds/catalog/get.hpp> | |||
#include <dds/util/fs.hpp> | |||
#include <dds/util/fs_transform.hpp> | |||
#include <libman/package.hpp> | |||
@@ -15,6 +16,8 @@ struct git_remote_listing { | |||
std::string ref; | |||
std::optional<lm::usage> auto_lib; | |||
std::vector<fs_transformation> transforms; | |||
void clone(path_ref path) const; | |||
}; | |||
@@ -24,6 +24,7 @@ enum class errc { | |||
no_catalog_remote_info, | |||
git_clone_failure, | |||
invalid_repo_transform, | |||
sdist_ident_mismatch, | |||
sdist_exists, | |||
@@ -0,0 +1,329 @@ | |||
#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); | |||
} |
@@ -3,12 +3,14 @@ | |||
#include "./fs.hpp" | |||
#include "./glob.hpp" | |||
#include <json5/data.hpp> | |||
#include <optional> | |||
#include <variant> | |||
namespace dds { | |||
class fs_transformation { | |||
struct fs_transformation { | |||
struct copy_move_base { | |||
fs::path from; | |||
fs::path to; | |||
@@ -27,11 +29,21 @@ class fs_transformation { | |||
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; | |||
static fs_transformation from_json(const json5::data&); | |||
std::string as_json() const noexcept; | |||
}; | |||
} // namespace dds |
@@ -144,6 +144,7 @@ dds::detail::glob_impl compile_glob_expr(std::string_view pattern) { | |||
using namespace dds::detail; | |||
glob_impl acc{}; | |||
acc.spelling = std::string(pattern); | |||
while (!pattern.empty()) { | |||
const auto next_slash = pattern.find('/'); | |||
@@ -165,7 +166,6 @@ dds::detail::glob_impl compile_glob_expr(std::string_view pattern) { | |||
throw std::runtime_error("Invalid path glob expression (Must not be empty!)"); | |||
} | |||
acc.spelling = std::string(pattern); | |||
return acc; | |||
} | |||
@@ -0,0 +1,5 @@ | |||
{ | |||
"compiler_id": 'gnu', | |||
"cxx_version": 'c++17', | |||
"cxx_compiler": 'g++-9', | |||
} |
@@ -0,0 +1,3 @@ | |||
{ | |||
"compiler_id": 'msvc', | |||
} |
@@ -0,0 +1,43 @@ | |||
{ | |||
"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\ | |||
' | |||
} | |||
} | |||
] | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,4 @@ | |||
{ | |||
name: 'use-catch2', | |||
uses: ['catch2/catch2'] | |||
} |
@@ -0,0 +1,8 @@ | |||
{ | |||
name: 'use-catch2', | |||
version: '1.0.0', | |||
namespace: 'test', | |||
depends: { | |||
'catch2': '2.12.4' | |||
} | |||
} |
@@ -0,0 +1,6 @@ | |||
#include <catch2/catch_with_main.hpp> | |||
TEST_CASE("I am a simple test case") { | |||
CHECK((2 + 2) == 4); | |||
CHECK_FALSE((2 + 2) == 5); | |||
} |
@@ -0,0 +1,11 @@ | |||
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)) |
@@ -0,0 +1,7 @@ | |||
{ | |||
"compiler_id": 'gnu', | |||
"cxx_version": 'c++17', | |||
"cxx_compiler": 'g++-9', | |||
"flags": '-march=native', | |||
"link_flags": '-static-libgcc -static-libstdc++' | |||
} |
@@ -0,0 +1,3 @@ | |||
{ | |||
"compiler_id": 'msvc', | |||
} |
@@ -0,0 +1,27 @@ | |||
{ | |||
"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" | |||
] | |||
} | |||
} | |||
] | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -0,0 +1,4 @@ | |||
{ | |||
name: 'use-cryptopp', | |||
uses: ['cryptopp/cryptopp'] | |||
} |
@@ -0,0 +1,8 @@ | |||
{ | |||
name: 'use-cryptopp', | |||
version: '1.0.0', | |||
namespace: 'test', | |||
depends: { | |||
'cryptopp': '8.2.0' | |||
} | |||
} |
@@ -0,0 +1,17 @@ | |||
#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; | |||
} |
@@ -0,0 +1,12 @@ | |||
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)) |
@@ -6,34 +6,41 @@ | |||
"git": { | |||
"url": "https://github.com/jedisct1/libsodium.git", | |||
"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" | |||
} | |||
} | |||
] | |||
] | |||
} | |||
} | |||
} | |||
} |
@@ -10,11 +10,13 @@ class Git(NamedTuple): | |||
auto_lib: Optional[str] = None | |||
def to_dict(self) -> dict: | |||
return { | |||
d = { | |||
'url': self.url, | |||
'ref': self.ref, | |||
'auto-lib': self.auto_lib, | |||
} | |||
if self.auto_lib: | |||
d['auto-lib'] = self.auto_lib | |||
return d | |||
RemoteInfo = Union[Git] | |||
@@ -135,6 +137,9 @@ packages = [ | |||
'0.2.0', | |||
'0.2.1', | |||
'0.2.2', | |||
'0.3.0', | |||
'0.3.1', | |||
'0.3.2', | |||
), | |||
description= | |||
'Minimal C++ concepts library. Contains many definitions from C++20.', | |||
@@ -192,6 +197,24 @@ packages = [ | |||
'neo-fun': '^0.1.1', | |||
'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', [ | |||
Version( |