Depends: ms-wil 2019.11.10 | Depends: ms-wil 2019.11.10 | ||||
Depends: range-v3 0.9.1 | Depends: range-v3 0.9.1 | ||||
Depends: nlohmann-json 3.7.1 | Depends: nlohmann-json 3.7.1 | ||||
Depends: neo-sqlite3 0.2.0 | |||||
Depends: semver 0.1.0 | |||||
Depends: neo-sqlite3 0.2.2 | |||||
Depends: semver 0.2.0 | |||||
Test-Driver: Catch-Main | Test-Driver: Catch-Main |
# XXX: Don't depend on a moving revision! | # XXX: Don't depend on a moving revision! | ||||
Remote-Package: neo-buffer 0.1.0; git url=https://github.com/vector-of-bool/neo-buffer.git ref=develop | Remote-Package: neo-buffer 0.1.0; git url=https://github.com/vector-of-bool/neo-buffer.git ref=develop | ||||
Remote-Package: neo-sqlite3 0.2.0; git url=https://github.com/vector-of-bool/neo-sqlite3.git ref=0.2.0 | |||||
Remote-Package: semver 0.1.0; git url=https://github.com/vector-of-bool/semver.git ref=0.1.0 | |||||
Remote-Package: neo-sqlite3 0.2.2; git url=https://github.com/vector-of-bool/neo-sqlite3.git ref=0.2.2 | |||||
Remote-Package: semver 0.2.0; git url=https://github.com/vector-of-bool/semver.git ref=0.2.0 |
#include "./catalog.hpp" | |||||
#include <neo/sqlite3/exec.hpp> | |||||
#include <neo/sqlite3/iter_tuples.hpp> | |||||
#include <neo/sqlite3/single.hpp> | |||||
#include <nlohmann/json.hpp> | |||||
#include <range/v3/range/conversion.hpp> | |||||
#include <range/v3/view/join.hpp> | |||||
#include <range/v3/view/transform.hpp> | |||||
#include <spdlog/spdlog.h> | |||||
using namespace dds; | |||||
namespace sqlite3 = neo::sqlite3; | |||||
using namespace sqlite3::literals; | |||||
namespace { | |||||
void migrate_repodb_1(sqlite3::database& db) { | |||||
db.exec(R"( | |||||
CREATE TABLE dds_cat_pkgs ( | |||||
pkg_id INTEGER PRIMARY KEY AUTOINCREMENT, | |||||
name TEXT NOT NULL, | |||||
version TEXT NOT NULL, | |||||
git_url TEXT, | |||||
git_ref TEXT, | |||||
lm_name TEXT, | |||||
lm_namespace TEXT, | |||||
UNIQUE(name, version), | |||||
CONSTRAINT has_remote_info CHECK( | |||||
( | |||||
git_url NOT NULL | |||||
AND git_ref NOT NULL | |||||
) | |||||
), | |||||
CONSTRAINT valid_lm_info CHECK( | |||||
( | |||||
lm_name NOT NULL | |||||
AND lm_namespace NOT NULL | |||||
) | |||||
+ | |||||
( | |||||
lm_name ISNULL | |||||
AND lm_namespace ISNULL | |||||
) | |||||
= 1 | |||||
) | |||||
); | |||||
CREATE TABLE dds_cat_pkg_deps ( | |||||
dep_id INTEGER PRIMARY KEY AUTOINCREMENT, | |||||
pkg_id INTEGER NOT NULL REFERENCES dds_cat_pkgs(pkg_id), | |||||
dep_name TEXT NOT NULL, | |||||
low TEXT NOT NULL, | |||||
high TEXT NOT NULL, | |||||
UNIQUE(pkg_id, dep_name) | |||||
); | |||||
)"); | |||||
} | |||||
void ensure_migrated(sqlite3::database& db) { | |||||
sqlite3::transaction_guard tr{db}; | |||||
db.exec(R"( | |||||
PRAGMA foreign_keys = 1; | |||||
CREATE TABLE IF NOT EXISTS dds_cat_meta AS | |||||
WITH init(meta) AS (VALUES ('{"version": 0}')) | |||||
SELECT * FROM init; | |||||
)"); | |||||
auto meta_st = db.prepare("SELECT meta FROM dds_cat_meta"); | |||||
auto [meta_json] = sqlite3::unpack_single<std::string>(meta_st); | |||||
auto meta = nlohmann::json::parse(meta_json); | |||||
if (!meta.is_object()) { | |||||
throw std::runtime_error("Corrupted repository database file."); | |||||
} | |||||
auto version_ = meta["version"]; | |||||
if (!version_.is_number_integer()) { | |||||
throw std::runtime_error("Corrupted repository database file [bad dds_meta.version]"); | |||||
} | |||||
int version = version_; | |||||
if (version < 1) { | |||||
migrate_repodb_1(db); | |||||
} | |||||
meta["version"] = 1; | |||||
exec(db, "UPDATE dds_cat_meta SET meta=?", std::forward_as_tuple(meta.dump())); | |||||
} | |||||
} // namespace | |||||
catalog catalog::open(const std::string& db_path) { | |||||
auto db = sqlite3::database::open(db_path); | |||||
try { | |||||
ensure_migrated(db); | |||||
} catch (const sqlite3::sqlite3_error& e) { | |||||
spdlog::critical( | |||||
"Failed to load the repository databsae. It appears to be invalid/corrupted. The " | |||||
"exception message is: {}", | |||||
e.what()); | |||||
throw; | |||||
} | |||||
return catalog(std::move(db)); | |||||
} | |||||
catalog::catalog(sqlite3::database db) | |||||
: _db(std::move(db)) {} | |||||
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( // | |||||
_stmt_cache, | |||||
R"( | |||||
INSERT INTO dds_cat_pkgs ( | |||||
name, | |||||
version, | |||||
git_url, | |||||
git_ref, | |||||
lm_name, | |||||
lm_namespace | |||||
) VALUES ( | |||||
?1, | |||||
?2, | |||||
?3, | |||||
?4, | |||||
CASE WHEN ?5 = '' THEN NULL ELSE ?5 END, | |||||
CASE WHEN ?6 = '' THEN NULL ELSE ?6 END | |||||
) | |||||
)"_sql, | |||||
std::forward_as_tuple( // | |||||
pkg.ident.name, | |||||
pkg.ident.version.to_string(), | |||||
git.url, | |||||
git.ref, | |||||
lm_usage.name, | |||||
lm_usage.namespace_)); | |||||
} | |||||
void catalog::store(const package_info& pkg) { | |||||
sqlite3::transaction_guard tr{_db}; | |||||
std::visit([&](auto&& remote) { _store_pkg(pkg, remote); }, pkg.remote); | |||||
auto db_pkg_id = _db.last_insert_rowid(); | |||||
auto& new_dep_st = _stmt_cache(R"( | |||||
INSERT INTO dds_cat_pkg_deps ( | |||||
pkg_id, | |||||
dep_name, | |||||
low, | |||||
high | |||||
) VALUES ( | |||||
?, | |||||
?, | |||||
?, | |||||
? | |||||
) | |||||
)"_sql); | |||||
for (const auto& dep : pkg.deps) { | |||||
new_dep_st.reset(); | |||||
sqlite3::exec(new_dep_st, | |||||
std::forward_as_tuple(db_pkg_id, | |||||
dep.name, | |||||
dep.version.to_string(), | |||||
"[placeholder]")); | |||||
} | |||||
} | |||||
std::optional<package_info> catalog::get(const package_id& pk_id) const noexcept { | |||||
auto& st = _stmt_cache(R"( | |||||
SELECT | |||||
pkg_id, | |||||
name, | |||||
version, | |||||
git_url, | |||||
git_ref, | |||||
lm_name, | |||||
lm_namespace | |||||
FROM dds_cat_pkgs | |||||
WHERE name = ? AND version = ? | |||||
)"_sql); | |||||
st.reset(); | |||||
st.bindings = std::forward_as_tuple(pk_id.name, pk_id.version.to_string()); | |||||
auto opt_tup = sqlite3::unpack_single_opt<std::int64_t, | |||||
std::string, | |||||
std::string, | |||||
std::optional<std::string>, | |||||
std::optional<std::string>, | |||||
std::optional<std::string>, | |||||
std::optional<std::string>>(st); | |||||
if (!opt_tup) { | |||||
return std::nullopt; | |||||
} | |||||
const auto& [pkg_id, name, version, git_url, git_ref, lm_name, lm_namespace] = *opt_tup; | |||||
assert(pk_id.name == name); | |||||
assert(pk_id.version == semver::version::parse(version)); | |||||
assert(git_url); | |||||
assert(git_ref); | |||||
auto deps = sqlite3::exec_iter<std::string, std::string>( // | |||||
_stmt_cache, | |||||
R"( | |||||
SELECT dep_name, low | |||||
FROM dds_cat_pkg_deps | |||||
WHERE pkg_id = ? | |||||
)"_sql, | |||||
std::tie(pkg_id)) | |||||
| ranges::views::transform([](auto&& pair) { | |||||
const auto& [name, ver] = pair; | |||||
return dependency{name, semver::version::parse(ver)}; | |||||
}) // | |||||
| ranges::to_vector; | |||||
return package_info{ | |||||
pk_id, | |||||
deps, | |||||
git_remote_listing{ | |||||
*git_url, | |||||
*git_ref, | |||||
lm_name ? std::make_optional(lm::usage{*lm_name, *lm_namespace}) : std::nullopt, | |||||
}, | |||||
}; | |||||
} | |||||
std::vector<package_id> catalog::by_name(std::string_view sv) const noexcept { | |||||
return sqlite3::exec_iter<std::string, std::string>( // | |||||
_stmt_cache, | |||||
R"( | |||||
SELECT name, version | |||||
FROM dds_cat_pkgs | |||||
WHERE name = ? | |||||
)"_sql, | |||||
std::tie(sv)) // | |||||
| ranges::views::transform([](auto& pair) { | |||||
auto& [name, ver] = pair; | |||||
return package_id{name, semver::version::parse(ver)}; | |||||
}) | |||||
| ranges::to_vector; | |||||
} | |||||
std::vector<dependency> catalog::dependencies_of(const package_id& pkg) const noexcept { | |||||
return sqlite3::exec_iter<std::string, | |||||
std::string>( // | |||||
_stmt_cache, | |||||
R"( | |||||
WITH this_pkg_id AS ( | |||||
SELECT pkg_id | |||||
FROM dds_cat_pkgs | |||||
WHERE name = ? AND version = ? | |||||
) | |||||
SELECT dep_name, low | |||||
FROM dds_cat_pkg_deps | |||||
WHERE pkg_id IN this_pkg_id | |||||
ORDER BY dep_name | |||||
)"_sql, | |||||
std::forward_as_tuple(pkg.name, pkg.version.to_string())) // | |||||
| ranges::views::transform([](auto&& pair) { | |||||
auto& [name, ver] = pair; | |||||
return dependency{name, semver::version::parse(ver)}; | |||||
}) // | |||||
| ranges::to_vector; | |||||
} | |||||
namespace { | |||||
void check_json(bool b, std::string_view what) { | |||||
if (!b) { | |||||
throw std::runtime_error("Unable to read repository JSON: " + std::string(what)); | |||||
} | |||||
} | |||||
} // namespace | |||||
void catalog::import_json_str(std::string_view content) { | |||||
using nlohmann::json; | |||||
auto root = json::parse(content); | |||||
check_json(root.is_object(), "Root of JSON must be an object (key-value mapping)"); | |||||
auto version = root["version"]; | |||||
check_json(version.is_number_integer(), "/version must be an integral value"); | |||||
check_json(version <= 1, "/version is too new. We don't know how to parse this."); | |||||
auto packages = root["packages"]; | |||||
check_json(packages.is_object(), "/packages must be an object"); | |||||
sqlite3::transaction_guard tr{_db}; | |||||
for (const auto& [pkg_name_, versions_map] : packages.items()) { | |||||
std::string pkg_name = pkg_name_; | |||||
check_json(versions_map.is_object(), | |||||
fmt::format("/packages/{} must be an object", pkg_name)); | |||||
for (const auto& [version_, pkg_info] : versions_map.items()) { | |||||
auto version = semver::version::parse(version_); | |||||
check_json(pkg_info.is_object(), | |||||
fmt::format("/packages/{}/{} must be an object", pkg_name, version_)); | |||||
auto deps = pkg_info["depends"]; | |||||
check_json(deps.is_object(), | |||||
fmt::format("/packages/{}/{}/depends must be an object", | |||||
pkg_name, | |||||
version_)); | |||||
package_info info{{pkg_name, version}, {}, {}}; | |||||
for (const auto& [dep_name, dep_version] : deps.items()) { | |||||
check_json(dep_version.is_string(), | |||||
fmt::format("/packages/{}/{}/depends/{} must be a string", | |||||
pkg_name, | |||||
version_, | |||||
dep_name)); | |||||
info.deps.push_back({ | |||||
std::string(dep_name), | |||||
semver::version::parse(std::string(dep_version)), | |||||
}); | |||||
} | |||||
auto git_remote = pkg_info["git"]; | |||||
if (!git_remote.is_null()) { | |||||
check_json(git_remote.is_object(), "`git` must be an object"); | |||||
std::string url = git_remote["url"]; | |||||
std::string ref = git_remote["ref"]; | |||||
auto lm_usage = git_remote["auto-lib"]; | |||||
std::optional<lm::usage> autolib; | |||||
if (!lm_usage.is_null()) { | |||||
autolib = lm::split_usage_string(std::string(lm_usage)); | |||||
} | |||||
info.remote = git_remote_listing{url, ref, autolib}; | |||||
} else { | |||||
throw std::runtime_error( | |||||
fmt::format("No remote info for /packages/{}/{}", pkg_name, version_)); | |||||
} | |||||
store(info); | |||||
} | |||||
} | |||||
} |
#pragma once | |||||
#include <dds/deps.hpp> | |||||
#include <dds/package_id.hpp> | |||||
#include <dds/util/fs.hpp> | |||||
#include <dds/catalog/git.hpp> | |||||
#include <neo/sqlite3/database.hpp> | |||||
#include <neo/sqlite3/statement.hpp> | |||||
#include <neo/sqlite3/statement_cache.hpp> | |||||
#include <neo/sqlite3/transaction.hpp> | |||||
#include <variant> | |||||
#include <vector> | |||||
namespace dds { | |||||
struct package_info { | |||||
package_id ident; | |||||
std::vector<dependency> deps; | |||||
std::variant<git_remote_listing> remote; | |||||
}; | |||||
class catalog { | |||||
neo::sqlite3::database _db; | |||||
mutable neo::sqlite3::statement_cache _stmt_cache{_db}; | |||||
explicit catalog(neo::sqlite3::database db); | |||||
catalog(const catalog&) = delete; | |||||
void _store_pkg(const package_info&, const git_remote_listing&); | |||||
public: | |||||
catalog(catalog&&) = default; | |||||
catalog& operator=(catalog&&) = default; | |||||
static catalog open(const std::string& db_path); | |||||
static catalog open(path_ref db_path) { return open(db_path.string()); } | |||||
void store(const package_info& info); | |||||
std::optional<package_info> get(const package_id& id) const noexcept; | |||||
std::vector<package_id> by_name(std::string_view sv) const noexcept; | |||||
std::vector<dependency> dependencies_of(const package_id& pkg) const noexcept; | |||||
void import_json_str(std::string_view json_str); | |||||
void import_json_file(path_ref json_path) { | |||||
auto content = dds::slurp_file(json_path); | |||||
import_json_str(content); | |||||
} | |||||
}; | |||||
} // namespace dds |
#include <dds/catalog/catalog.hpp> | |||||
#include <catch2/catch.hpp> | |||||
using namespace std::literals; | |||||
TEST_CASE("Create a simple database") { | |||||
// Just create and run migrations on an in-memory database | |||||
auto repo = dds::catalog::open(":memory:"s); | |||||
} | |||||
class catalog_test_case { | |||||
public: | |||||
dds::catalog db = dds::catalog::open(":memory:"s); | |||||
}; | |||||
TEST_CASE_METHOD(catalog_test_case, "Store a simple package") { | |||||
db.store(dds::package_info{ | |||||
dds::package_id("foo", semver::version::parse("1.2.3")), | |||||
{}, | |||||
dds::git_remote_listing{"http://example.com", "master", std::nullopt}, | |||||
}); | |||||
CHECK_THROWS(db.store(dds::package_info{ | |||||
dds::package_id("foo", semver::version::parse("1.2.3")), | |||||
{}, | |||||
dds::git_remote_listing{"http://example.com", "master", std::nullopt}, | |||||
})); | |||||
auto pkgs = db.by_name("foo"); | |||||
REQUIRE(pkgs.size() == 1); | |||||
} | |||||
TEST_CASE_METHOD(catalog_test_case, "Package requirements") { | |||||
db.store(dds::package_info{ | |||||
dds::package_id{"foo", semver::version::parse("1.2.3")}, | |||||
{ | |||||
{"bar", semver::version::parse("1.2.5")}, | |||||
{"baz", semver::version::parse("5.3.2")}, | |||||
}, | |||||
dds::git_remote_listing{"http://example.com", "master", std::nullopt}, | |||||
}); | |||||
auto pkgs = db.by_name("foo"); | |||||
REQUIRE(pkgs.size() == 1); | |||||
CHECK(pkgs[0].name == "foo"); | |||||
auto deps = db.dependencies_of(pkgs[0]); | |||||
CHECK(deps.size() == 2); | |||||
CHECK(deps[0].name == "bar"); | |||||
CHECK(deps[1].name == "baz"); | |||||
} | |||||
TEST_CASE_METHOD(catalog_test_case, "Parse JSON repo") { | |||||
db.import_json_str(R"({ | |||||
"version": 1, | |||||
"packages": { | |||||
"foo": { | |||||
"1.2.3": { | |||||
"depends": { | |||||
"bar": "4.2.1" | |||||
}, | |||||
"git": { | |||||
"url": "http://example.com", | |||||
"ref": "master" | |||||
} | |||||
} | |||||
} | |||||
} | |||||
})"); | |||||
auto pkgs = db.by_name("foo"); | |||||
REQUIRE(pkgs.size() == 1); | |||||
CHECK(pkgs[0].name == "foo"); | |||||
CHECK(pkgs[0].version == semver::version::parse("1.2.3")); | |||||
auto deps = db.dependencies_of(pkgs[0]); | |||||
REQUIRE(deps.size() == 1); | |||||
CHECK(deps[0].name == "bar"); | |||||
CHECK(deps[0].version == semver::version::parse("4.2.1")); | |||||
} |
#include "./get.hpp" | |||||
#include <dds/catalog/catalog.hpp> | |||||
#include <dds/proc.hpp> | |||||
#include <spdlog/spdlog.h> | |||||
using namespace dds; | |||||
namespace { | |||||
temporary_sdist do_pull_sdist(const package_info& listing, const git_remote_listing& git) { | |||||
auto tmpdir = dds::temporary_dir::create(); | |||||
using namespace std::literals; | |||||
spdlog::info("Cloning Git repository: {} [{}] ...", git.url, git.ref); | |||||
auto command = {"git"s, | |||||
"clone"s, | |||||
"--depth=1"s, | |||||
"--branch"s, | |||||
git.ref, | |||||
git.url, | |||||
tmpdir.path().generic_string()}; | |||||
auto git_res = run_proc(command); | |||||
if (!git_res.okay()) { | |||||
throw std::runtime_error( | |||||
fmt::format("Git clone operation failed [Git command: {}] [Exitted {}]:\n{}", | |||||
quote_command(command), | |||||
git_res.retc, | |||||
git_res.output)); | |||||
} | |||||
spdlog::info("Create sdist from clone ..."); | |||||
if (git.auto_lib.has_value()) { | |||||
spdlog::info("Generating library data automatically"); | |||||
auto pkg_strm = dds::open(tmpdir.path() / "package.dds", std::ios::binary | std::ios::out); | |||||
pkg_strm << "Name: " << listing.ident.name << '\n' // | |||||
<< "Version: " << listing.ident.version.to_string() << '\n' // | |||||
<< "Namespace: " << git.auto_lib->namespace_; | |||||
auto lib_strm = dds::open(tmpdir.path() / "library.dds", std::ios::binary | std::ios::out); | |||||
lib_strm << "Name: " << git.auto_lib->name; | |||||
} | |||||
sdist_params params; | |||||
params.project_dir = tmpdir.path(); | |||||
auto sd_tmp_dir = dds::temporary_dir::create(); | |||||
params.dest_path = sd_tmp_dir.path(); | |||||
params.force = true; | |||||
auto sd = create_sdist(params); | |||||
return {sd_tmp_dir, sd}; | |||||
} | |||||
} // namespace | |||||
temporary_sdist dds::get_package_sdist(const package_info& pkg) { | |||||
auto tsd = std::visit([&](auto&& remote) { return do_pull_sdist(pkg, remote); }, pkg.remote); | |||||
if (!(tsd.sdist.manifest.pk_id == pkg.ident)) { | |||||
throw std::runtime_error(fmt::format( | |||||
"The package name@version in the generated sdist does not match the name listed in " | |||||
"the remote listing file (expected '{}', but got '{}')", | |||||
pkg.ident.to_string(), | |||||
tsd.sdist.manifest.pk_id.to_string())); | |||||
} | |||||
return tsd; | |||||
} |
#pragma once | |||||
#include <dds/sdist.hpp> | |||||
#include <dds/temp.hpp> | |||||
namespace dds { | |||||
struct package_info; | |||||
struct temporary_sdist { | |||||
temporary_dir tmpdir; | |||||
dds::sdist sdist; | |||||
}; | |||||
temporary_sdist get_package_sdist(const package_info&); | |||||
} // namespace dds |
#pragma once | |||||
#include <dds/util/fs.hpp> | |||||
#include <libman/package.hpp> | |||||
#include <optional> | |||||
#include <string> | |||||
namespace dds { | |||||
struct git_remote_listing { | |||||
std::string url; | |||||
std::string ref; | |||||
std::optional<lm::usage> auto_lib; | |||||
void clone(path_ref path) const; | |||||
}; | |||||
} // namespace dds |
#include <dds/build.hpp> | #include <dds/build.hpp> | ||||
#include <dds/repo/remote.hpp> | |||||
#include <dds/catalog/catalog.hpp> | |||||
#include <dds/catalog/get.hpp> | |||||
#include <dds/repo/repo.hpp> | #include <dds/repo/repo.hpp> | ||||
#include <dds/sdist.hpp> | #include <dds/sdist.hpp> | ||||
#include <dds/toolchain/from_dds.hpp> | #include <dds/toolchain/from_dds.hpp> | ||||
} | } | ||||
}; | }; | ||||
struct repo_where_flag : path_flag { | |||||
repo_where_flag(args::Group& grp) | |||||
struct repo_path_flag : path_flag { | |||||
repo_path_flag(args::Group& grp) | |||||
: path_flag{grp, | : path_flag{grp, | ||||
"dir", | "dir", | ||||
"Path to the DDS repository directory", | "Path to the DDS repository directory", | ||||
dds::repository::default_local_path()} {} | dds::repository::default_local_path()} {} | ||||
}; | }; | ||||
struct catalog_path_flag : path_flag { | |||||
catalog_path_flag(args::Group& cmd) | |||||
: path_flag(cmd, | |||||
"catalog-path", | |||||
"Override the path to the catalog database", | |||||
{"catalog", 'c'}, | |||||
dds::dds_data_dir() / "catalog.db") {} | |||||
dds::catalog open() { return dds::catalog::open(Get()); } | |||||
}; | |||||
/** | /** | ||||
* Base class holds the actual argument parser | * Base class holds the actual argument parser | ||||
*/ | */ | ||||
dds::fs::current_path()}; | dds::fs::current_path()}; | ||||
}; | }; | ||||
/* | |||||
###### ### ######## ### ## ####### ###### | |||||
## ## ## ## ## ## ## ## ## ## ## ## | |||||
## ## ## ## ## ## ## ## ## ## | |||||
## ## ## ## ## ## ## ## ## ## #### | |||||
## ######### ## ######### ## ## ## ## ## | |||||
## ## ## ## ## ## ## ## ## ## ## ## | |||||
###### ## ## ## ## ## ######## ####### ###### | |||||
*/ | |||||
struct cli_catalog { | |||||
cli_base& base; | |||||
args::Command cmd{base.cmd_group, "catalog", "Manage the package catalog"}; | |||||
common_flags _common{cmd}; | |||||
args::Group cat_group{cmd, "Catalog subcommands"}; | |||||
struct { | |||||
cli_catalog& parent; | |||||
args::Command cmd{parent.cat_group, "create", "Create a catalog database"}; | |||||
common_flags _common{cmd}; | |||||
catalog_path_flag cat_path{cmd}; | |||||
int run() { | |||||
// Simply opening the DB will initialize the catalog | |||||
cat_path.open(); | |||||
return 0; | |||||
} | |||||
} create{*this}; | |||||
struct { | |||||
cli_catalog& parent; | |||||
args::Command cmd{parent.cat_group, "import", "Import entries into a catalog"}; | |||||
common_flags _common{cmd}; | |||||
catalog_path_flag cat_path{cmd}; | |||||
args::ValueFlagList<std::string> | |||||
json_paths{cmd, | |||||
"json", | |||||
"Import catalog entries from the given JSON files", | |||||
{"json", 'j'}}; | |||||
int run() { | |||||
auto cat = cat_path.open(); | |||||
for (const auto& json_fpath : json_paths.Get()) { | |||||
cat.import_json_file(json_fpath); | |||||
} | |||||
return 0; | |||||
} | |||||
} import{*this}; | |||||
struct { | |||||
cli_catalog& parent; | |||||
args::Command cmd{parent.cat_group, "get", "Obtain an sdist from a catalog listing"}; | |||||
common_flags _common{cmd}; | |||||
catalog_path_flag cat_path{cmd}; | |||||
path_flag out{cmd, | |||||
"out", | |||||
"The directory where the source distributions will be placed", | |||||
{"out-dir", 'o'}, | |||||
dds::fs::current_path()}; | |||||
args::PositionalList<std::string> requirements{cmd, | |||||
"requirement", | |||||
"The package IDs to obtain"}; | |||||
int run() { | |||||
auto cat = cat_path.open(); | |||||
for (const auto& req : requirements.Get()) { | |||||
auto id = dds::package_id::parse(req); | |||||
auto info = cat.get(id); | |||||
if (!info) { | |||||
throw std::runtime_error( | |||||
fmt::format("No package in the catalog matched the ID '{}'", req)); | |||||
} | |||||
auto tsd = dds::get_package_sdist(*info); | |||||
auto out_path = out.Get(); | |||||
auto dest = out_path / id.to_string(); | |||||
spdlog::info("Create sdist at {}", dest.string()); | |||||
dds::fs::remove_all(dest); | |||||
dds::safe_rename(tsd.sdist.path, dest); | |||||
} | |||||
return 0; | |||||
} | |||||
} get{*this}; | |||||
struct { | |||||
cli_catalog& parent; | |||||
args::Command cmd{parent.cat_group, "add", "Manually add an entry to the catalog database"}; | |||||
common_flags _common{cmd}; | |||||
catalog_path_flag cat_path{cmd}; | |||||
args::Positional<std::string> pkg_id{cmd, | |||||
"id", | |||||
"The name@version ID of the package to add", | |||||
args::Options::Required}; | |||||
string_flag auto_lib{cmd, | |||||
"auto-lib", | |||||
"Set the auto-library information for this package", | |||||
{"auto-lib"}}; | |||||
args::ValueFlagList<std::string> deps{cmd, | |||||
"depends", | |||||
"The dependencies of this package", | |||||
{"depends", 'd'}}; | |||||
string_flag git_url{cmd, "git-url", "The Git url for the package", {"git-url"}}; | |||||
string_flag git_ref{cmd, | |||||
"git-ref", | |||||
"The Git ref to from which the source distribution should be created", | |||||
{"git-ref"}}; | |||||
int run() { | |||||
auto ident = dds::package_id::parse(pkg_id.Get()); | |||||
std::vector<dds::dependency> deps; | |||||
for (const auto& dep : this->deps.Get()) { | |||||
auto dep_id = dds::package_id::parse(dep); | |||||
deps.push_back({dep_id.name, dep_id.version}); | |||||
} | |||||
dds::package_info info{ident, std::move(deps), {}}; | |||||
if (git_url) { | |||||
if (!git_ref) { | |||||
throw std::runtime_error( | |||||
"`--git-ref` must be specified when using `--git-url`"); | |||||
} | |||||
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()); | |||||
} | |||||
info.remote = std::move(git); | |||||
} | |||||
cat_path.open().store(info); | |||||
return 0; | |||||
} | |||||
} add{*this}; | |||||
int run() { | |||||
if (create.cmd) { | |||||
return create.run(); | |||||
} else if (import.cmd) { | |||||
return import.run(); | |||||
} else if (get.cmd) { | |||||
return get.run(); | |||||
} else if (add.cmd) { | |||||
return add.run(); | |||||
} else { | |||||
assert(false); | |||||
std::terminate(); | |||||
} | |||||
} | |||||
}; | |||||
/* | /* | ||||
######## ######## ######## ####### | ######## ######## ######## ####### | ||||
## ## ## ## ## ## ## | ## ## ## ## ## ## ## | ||||
args::Command cmd{base.cmd_group, "repo", "Manage the package repository"}; | args::Command cmd{base.cmd_group, "repo", "Manage the package repository"}; | ||||
common_flags _common{cmd}; | common_flags _common{cmd}; | ||||
repo_where_flag where{cmd}; | |||||
repo_path_flag where{cmd}; | |||||
args::Group repo_group{cmd, "Repo subcommands"}; | args::Group repo_group{cmd, "Repo subcommands"}; | ||||
int run() { | int run() { | ||||
auto list_contents = [&](dds::repository repo) { | auto list_contents = [&](dds::repository repo) { | ||||
auto same_name | |||||
= [](auto&& a, auto&& b) { return a.manifest.pk_id.name == b.manifest.pk_id.name; }; | |||||
auto same_name = [](auto&& a, auto&& b) { | |||||
return a.manifest.pk_id.name == b.manifest.pk_id.name; | |||||
}; | |||||
auto all = repo.iter_sdists(); | auto all = repo.iter_sdists(); | ||||
auto grp_by_name = all // | auto grp_by_name = all // | ||||
common_project_flags project{cmd}; | common_project_flags project{cmd}; | ||||
repo_where_flag repo_where{cmd}; | |||||
args::Flag force{cmd, | |||||
repo_path_flag repo_where{cmd}; | |||||
args::Flag force{cmd, | |||||
"replace-if-exists", | "replace-if-exists", | ||||
"Replace an existing export in the repository", | "Replace an existing export in the repository", | ||||
{"replace"}}; | {"replace"}}; | ||||
args::Flag build_apps{cmd, "build_apps", "Build applications", {"apps", 'A'}}; | args::Flag build_apps{cmd, "build_apps", "Build applications", {"apps", 'A'}}; | ||||
args::Flag export_{cmd, "export", "Generate a library export", {"export", 'E'}}; | args::Flag export_{cmd, "export", "Generate a library export", {"export", 'E'}}; | ||||
toolchain_flag tc_filepath{cmd}; | toolchain_flag tc_filepath{cmd}; | ||||
args::Flag enable_warnings{cmd, | args::Flag enable_warnings{cmd, | ||||
"enable_warnings", | "enable_warnings", | ||||
"Enable compiler warnings", | "Enable compiler warnings", | ||||
"Ensure we have local copies of the project dependencies"}; | "Ensure we have local copies of the project dependencies"}; | ||||
common_flags _common{cmd}; | common_flags _common{cmd}; | ||||
repo_where_flag repo_where{cmd}; | |||||
path_flag remote_listing_file{ | |||||
cmd, | |||||
"remote-listing", | |||||
"Path to a file containing listing of remote sdists and how to obtain them", | |||||
{'R', "remote-list"}, | |||||
"remote.dds"}; | |||||
repo_path_flag repo_where{cmd}; | |||||
catalog_path_flag catalog_path{cmd}; | |||||
int run() { | int run() { | ||||
auto man = parent.load_package_manifest(); | |||||
auto rd = dds::remote_directory::load_from_file(remote_listing_file.Get()); | |||||
bool failed = false; | |||||
auto man = parent.load_package_manifest(); | |||||
auto catalog = catalog_path.open(); | |||||
bool failed = false; | |||||
dds::repository::with_repository( // | dds::repository::with_repository( // | ||||
repo_where.Get(), | repo_where.Get(), | ||||
dds::repo_flags::write_lock | dds::repo_flags::create_if_absent, | dds::repo_flags::write_lock | dds::repo_flags::create_if_absent, | ||||
for (auto& dep : man.dependencies) { | for (auto& dep : man.dependencies) { | ||||
auto exists = !!repo.find(dep.name, dep.version); | auto exists = !!repo.find(dep.name, dep.version); | ||||
if (!exists) { | if (!exists) { | ||||
spdlog::info("Pull remote: {} {}", dep.name, dep.version.to_string()); | |||||
auto opt_remote = rd.find(dep.name, dep.version); | |||||
if (opt_remote) { | |||||
auto tsd = opt_remote->pull_sdist(); | |||||
spdlog::info("Pull remote: {}@{}", dep.name, dep.version.to_string()); | |||||
auto opt_pkg = catalog.get(dds::package_id{dep.name, dep.version}); | |||||
if (opt_pkg) { | |||||
auto tsd = dds::get_package_sdist(*opt_pkg); | |||||
repo.add_sdist(tsd.sdist, dds::if_exists::ignore); | repo.add_sdist(tsd.sdist, dds::if_exists::ignore); | ||||
} else { | } else { | ||||
spdlog::error("No remote listing for {} {}", | |||||
spdlog::error("No remote listing for {}@{}", | |||||
dep.name, | dep.name, | ||||
dep.version.to_string()); | dep.version.to_string()); | ||||
failed = true; | failed = true; | ||||
"If specified, will not generate an INDEX.lmi", | "If specified, will not generate an INDEX.lmi", | ||||
{"skip-lmi"}}; | {"skip-lmi"}}; | ||||
repo_where_flag repo_where{cmd}; | |||||
repo_path_flag repo_where{cmd}; | |||||
toolchain_flag tc_filepath{cmd}; | toolchain_flag tc_filepath{cmd}; | ||||
spdlog::set_pattern("[%H:%M:%S] [%^%-5l%$] %v"); | spdlog::set_pattern("[%H:%M:%S] [%^%-5l%$] %v"); | ||||
args::ArgumentParser parser("DDS - The drop-dead-simple library manager"); | args::ArgumentParser parser("DDS - The drop-dead-simple library manager"); | ||||
cli_base cli{parser}; | |||||
cli_build build{cli}; | |||||
cli_sdist sdist{cli}; | |||||
cli_repo repo{cli}; | |||||
cli_deps deps{cli}; | |||||
cli_base cli{parser}; | |||||
cli_build build{cli}; | |||||
cli_sdist sdist{cli}; | |||||
cli_repo repo{cli}; | |||||
cli_deps deps{cli}; | |||||
cli_catalog catalog{cli}; | |||||
try { | try { | ||||
parser.ParseCLI(argc, argv); | parser.ParseCLI(argc, argv); | ||||
} catch (const args::Help&) { | } catch (const args::Help&) { | ||||
return repo.run(); | return repo.run(); | ||||
} else if (deps.cmd) { | } else if (deps.cmd) { | ||||
return deps.run(); | return deps.run(); | ||||
} else if (catalog.cmd) { | |||||
return catalog.run(); | |||||
} else { | } else { | ||||
assert(false); | assert(false); | ||||
std::terminate(); | std::terminate(); |
#include "./remote.hpp" | |||||
#include <dds/deps.hpp> | |||||
#include <dds/proc.hpp> | |||||
#include <dds/repo/repo.hpp> | |||||
#include <dds/sdist.hpp> | |||||
#include <dds/temp.hpp> | |||||
#include <dds/toolchain/toolchain.hpp> | |||||
#include <dds/util/shlex.hpp> | |||||
#include <spdlog/spdlog.h> | |||||
#include <libman/parse.hpp> | |||||
#include <algorithm> | |||||
using namespace dds; | |||||
namespace { | |||||
struct read_listing_item { | |||||
std::string_view _key; | |||||
std::set<remote_listing, remote_listing_compare_t>& out; | |||||
bool operator()(std::string_view context, std::string_view key, std::string_view value) { | |||||
if (key != _key) { | |||||
return false; | |||||
} | |||||
auto nested = lm::nested_kvlist::parse(value); | |||||
auto pk_id = package_id::parse(nested.primary); | |||||
put_listing(context, std::move(pk_id), nested.pairs); | |||||
return true; | |||||
} | |||||
void put_listing(std::string_view context, package_id pk_id, const lm::pair_list& pairs) { | |||||
if (pairs.find("git")) { | |||||
std::string url; | |||||
std::string ref; | |||||
std::optional<lm::usage> auto_id; | |||||
lm::read(fmt::format("{}: Parsing Git remote listing", context), | |||||
pairs, | |||||
lm::read_required("url", url), | |||||
lm::read_required("ref", ref), | |||||
lm::read_check_eq("git", ""), | |||||
lm::read_opt("auto", auto_id, &lm::split_usage_string), | |||||
lm::reject_unknown()); | |||||
auto did_insert = out.emplace(remote_listing{std::move(pk_id), | |||||
git_remote_listing{url, ref, auto_id}}) | |||||
.second; | |||||
if (!did_insert) { | |||||
spdlog::warn("Duplicate remote package defintion for {}", pk_id.to_string()); | |||||
} | |||||
} else { | |||||
throw std::runtime_error( | |||||
fmt::format("Unable to determine remote type of package {}", pk_id.to_string())); | |||||
} | |||||
} | |||||
}; | |||||
temporary_sdist do_pull_sdist(const remote_listing& listing, const git_remote_listing& git) { | |||||
auto tmpdir = dds::temporary_dir::create(); | |||||
using namespace std::literals; | |||||
spdlog::info("Cloning repository: {} [{}] ...", git.url, git.ref); | |||||
auto command = {"git"s, | |||||
"clone"s, | |||||
"--depth=1"s, | |||||
"--branch"s, | |||||
git.ref, | |||||
git.url, | |||||
tmpdir.path().generic_string()}; | |||||
auto git_res = run_proc(command); | |||||
if (!git_res.okay()) { | |||||
throw std::runtime_error( | |||||
fmt::format("Git clone operation failed [Git command: {}] [Exitted {}]:\n{}", | |||||
quote_command(command), | |||||
git_res.retc, | |||||
git_res.output)); | |||||
} | |||||
spdlog::info("Create sdist from clone ..."); | |||||
if (git.auto_lib.has_value()) { | |||||
spdlog::info("Generating library data automatically"); | |||||
auto pkg_strm = dds::open(tmpdir.path() / "package.dds", std::ios::binary | std::ios::out); | |||||
pkg_strm << "Name: " << listing.pk_id.name << '\n' // | |||||
<< "Version: " << listing.pk_id.version.to_string() << '\n' // | |||||
<< "Namespace: " << git.auto_lib->namespace_; | |||||
auto lib_strm = dds::open(tmpdir.path() / "library.dds", std::ios::binary | std::ios::out); | |||||
lib_strm << "Name: " << git.auto_lib->name; | |||||
} | |||||
sdist_params params; | |||||
params.project_dir = tmpdir.path(); | |||||
auto sd_tmp_dir = dds::temporary_dir::create(); | |||||
params.dest_path = sd_tmp_dir.path(); | |||||
params.force = true; | |||||
auto sd = create_sdist(params); | |||||
return {sd_tmp_dir, sd}; | |||||
} | |||||
} // namespace | |||||
temporary_sdist remote_listing::pull_sdist() const { | |||||
auto tsd = visit([&](auto&& actual) { return do_pull_sdist(*this, actual); }); | |||||
if (!(tsd.sdist.manifest.pk_id == pk_id)) { | |||||
throw std::runtime_error(fmt::format( | |||||
"The package name@version in the generated sdist does not match the name listed in " | |||||
"the remote listing file (expected '{}', but got '{}')", | |||||
pk_id.to_string(), | |||||
tsd.sdist.manifest.pk_id.to_string())); | |||||
} | |||||
return tsd; | |||||
} | |||||
remote_directory remote_directory::load_from_file(path_ref filepath) { | |||||
auto kvs = lm::parse_file(filepath); | |||||
listing_set listings; | |||||
lm::read(fmt::format("Loading remote package listing from {}", filepath.string()), | |||||
kvs, | |||||
read_listing_item{"Remote-Package", listings}, | |||||
lm::reject_unknown()); | |||||
return {std::move(listings)}; | |||||
} | |||||
const remote_listing* remote_directory::find(std::string_view name, semver::version ver) const | |||||
noexcept { | |||||
auto found = _remotes.find(std::tie(name, ver)); | |||||
if (found == _remotes.end()) { | |||||
return nullptr; | |||||
} | |||||
return &*found; | |||||
} | |||||
void remote_directory::ensure_all_local(const repository&) const { | |||||
spdlog::critical("Dependency download is not fully implemented!"); | |||||
} |
#pragma once | |||||
#include <dds/util/fs.hpp> | |||||
#include <dds/sdist.hpp> | |||||
#include <dds/temp.hpp> | |||||
#include <libman/library.hpp> | |||||
#include <semver/version.hpp> | |||||
#include <set> | |||||
#include <string> | |||||
#include <tuple> | |||||
#include <utility> | |||||
#include <variant> | |||||
namespace dds { | |||||
struct temporary_sdist { | |||||
temporary_dir tmpdir; | |||||
dds::sdist sdist; | |||||
}; | |||||
struct git_remote_listing { | |||||
std::string url; | |||||
std::string ref; | |||||
std::optional<lm::usage> auto_lib; | |||||
void clone(path_ref path) const; | |||||
}; | |||||
struct remote_listing { | |||||
package_id pk_id; | |||||
std::variant<git_remote_listing> remote; | |||||
template <typename Func> | |||||
decltype(auto) visit(Func&& fn) const { | |||||
return std::visit(std::forward<Func>(fn), remote); | |||||
} | |||||
temporary_sdist pull_sdist() const; | |||||
}; | |||||
inline constexpr struct remote_listing_compare_t { | |||||
using is_transparent = int; | |||||
bool operator()(const remote_listing& lhs, const remote_listing& rhs) const { | |||||
return lhs.pk_id < rhs.pk_id; | |||||
} | |||||
template <typename Name, typename Version> | |||||
bool operator()(const remote_listing& lhs, const std::tuple<Name, Version>& rhs) const { | |||||
auto&& [name, ver] = rhs; | |||||
return lhs.pk_id < package_id{name, ver}; | |||||
} | |||||
template <typename Name, typename Version> | |||||
bool operator()(const std::tuple<Name, Version>& lhs, const remote_listing& rhs) const { | |||||
auto&& [name, ver] = lhs; | |||||
return package_id{name, ver} < rhs.pk_id; | |||||
} | |||||
} remote_listing_compare; | |||||
class remote_directory { | |||||
using listing_set = std::set<remote_listing, remote_listing_compare_t>; | |||||
listing_set _remotes; | |||||
remote_directory(listing_set s) | |||||
: _remotes(std::move(s)) {} | |||||
public: | |||||
static remote_directory load_from_file(path_ref); | |||||
void ensure_all_local(const class repository& repo) const; | |||||
const remote_listing* find(std::string_view name, semver::version ver) const noexcept; | |||||
}; | |||||
} // namespace dds |
from tests.fileutil import set_contents | from tests.fileutil import set_contents | ||||
def test_lib_with_just_app(dds: DDS, scope: ExitStack): | |||||
scope.enter_context( | |||||
def test_lib_with_just_app(dds: DDS): | |||||
dds.scope.enter_context( | |||||
set_contents( | set_contents( | ||||
dds.source_root / 'src/foo.main.cpp', | dds.source_root / 'src/foo.main.cpp', | ||||
b'int main() {}', | b'int main() {}', |
from tests import dds, DDS | |||||
from tests.fileutil import ensure_dir | |||||
def test_create_catalog(dds: DDS): | |||||
dds.scope.enter_context(ensure_dir(dds.build_dir)) | |||||
dds.catalog_create() | |||||
assert dds.catalog_path.is_file() |
import json | |||||
from tests import dds, DDS | |||||
from tests.fileutil import ensure_dir | |||||
def test_get(dds: DDS): | |||||
dds.scope.enter_context(ensure_dir(dds.build_dir)) | |||||
dds.catalog_create() | |||||
json_path = dds.build_dir / 'catalog.json' | |||||
import_data = { | |||||
'version': 1, | |||||
'packages': { | |||||
'neo-sqlite3': { | |||||
'0.2.2': { | |||||
'depends': {}, | |||||
'git': { | |||||
'url': 'https://github.com/vector-of-bool/neo-sqlite3.git', | |||||
'ref': '0.2.2', | |||||
}, | |||||
}, | |||||
}, | |||||
}, | |||||
} | |||||
dds.scope.enter_context( | |||||
dds.set_contents(json_path, | |||||
json.dumps(import_data).encode())) | |||||
dds.catalog_import(json_path) | |||||
dds.catalog_get('neo-sqlite3@0.2.2') | |||||
assert (dds.source_root / 'neo-sqlite3@0.2.2').is_dir() | |||||
assert (dds.source_root / 'neo-sqlite3@0.2.2/package.dds').is_file() |
import json | |||||
from tests import dds, DDS | |||||
from tests.fileutil import ensure_dir | |||||
def test_import_json(dds: DDS): | |||||
dds.scope.enter_context(ensure_dir(dds.build_dir)) | |||||
dds.catalog_create() | |||||
json_fpath = dds.build_dir / 'data.json' | |||||
import_data = { | |||||
'version': 1, | |||||
'packages': { | |||||
'foo': { | |||||
'1.2.4': { | |||||
'git': { | |||||
'url': 'http://example.com', | |||||
'ref': 'master', | |||||
}, | |||||
'depends': {}, | |||||
}, | |||||
}, | |||||
}, | |||||
} | |||||
dds.scope.enter_context( | |||||
dds.set_contents(json_fpath, | |||||
json.dumps(import_data).encode())) | |||||
dds.catalog_import(json_fpath) |
def repo_dir(self) -> Path: | def repo_dir(self) -> Path: | ||||
return self.scratch_dir / 'repo' | return self.scratch_dir / 'repo' | ||||
@property | |||||
def catalog_path(self) -> Path: | |||||
return self.scratch_dir / 'catalog.db' | |||||
@property | @property | ||||
def deps_build_dir(self) -> Path: | def deps_build_dir(self) -> Path: | ||||
return self.scratch_dir / 'deps-build' | return self.scratch_dir / 'deps-build' | ||||
def run(self, cmd: proc.CommandLine, *, | def run(self, cmd: proc.CommandLine, *, | ||||
cwd: Path = None) -> subprocess.CompletedProcess: | cwd: Path = None) -> subprocess.CompletedProcess: | ||||
cmdline = list(proc.flatten_cmd(cmd)) | cmdline = list(proc.flatten_cmd(cmd)) | ||||
res = self.run_unchecked(cmd) | |||||
res = self.run_unchecked(cmd, cwd=cwd) | |||||
if res.returncode != 0: | if res.returncode != 0: | ||||
raise subprocess.CalledProcessError( | raise subprocess.CalledProcessError( | ||||
res.returncode, [self.dds_exe] + cmdline, res.stdout) | res.returncode, [self.dds_exe] + cmdline, res.stdout) | ||||
return self.run([ | return self.run([ | ||||
'deps', | 'deps', | ||||
'get', | 'get', | ||||
f'--catalog={self.catalog_path}', | |||||
self.repo_dir_arg, | self.repo_dir_arg, | ||||
]) | ]) | ||||
f'We don\'t know the executable suffix for the platform "{os.name}"' | f'We don\'t know the executable suffix for the platform "{os.name}"' | ||||
) | ) | ||||
def catalog_create(self) -> subprocess.CompletedProcess: | |||||
self.scratch_dir.mkdir(parents=True, exist_ok=True) | |||||
return self.run( | |||||
['catalog', 'create', f'--catalog={self.catalog_path}'], | |||||
cwd=self.test_dir) | |||||
def catalog_import(self, json_path: Path) -> subprocess.CompletedProcess: | |||||
self.scratch_dir.mkdir(parents=True, exist_ok=True) | |||||
return self.run([ | |||||
'catalog', | |||||
'import', | |||||
f'--catalog={self.catalog_path}', | |||||
f'--json={json_path}', | |||||
]) | |||||
def catalog_get(self, req: str) -> subprocess.CompletedProcess: | |||||
return self.run([ | |||||
'catalog', | |||||
'get', | |||||
f'--catalog={self.catalog_path}', | |||||
req, | |||||
]) | |||||
def set_contents(self, path: Union[str, Path], | def set_contents(self, path: Union[str, Path], | ||||
content: bytes) -> ContextManager[Path]: | content: bytes) -> ContextManager[Path]: | ||||
return fileutil.set_contents(self.source_root / path, content) | return fileutil.set_contents(self.source_root / path, content) |
@dds_conf | @dds_conf | ||||
def test_deps_build(dds: DDS): | def test_deps_build(dds: DDS): | ||||
dds.catalog_import(dds.source_root / 'catalog.json') | |||||
assert not dds.repo_dir.exists() | assert not dds.repo_dir.exists() | ||||
dds.deps_get() | dds.deps_get() | ||||
assert dds.repo_dir.exists(), '`deps get` did not generate a repo directory' | assert dds.repo_dir.exists(), '`deps get` did not generate a repo directory' | ||||
@dds_fixture_conf_1('use-remote') | @dds_fixture_conf_1('use-remote') | ||||
def test_use_nlohmann_json_remote(dds: DDS): | def test_use_nlohmann_json_remote(dds: DDS): | ||||
dds.catalog_import(dds.source_root / 'catalog.json') | |||||
dds.deps_get() | dds.deps_get() | ||||
dds.deps_build() | dds.deps_build() | ||||
dds.build(apps=True) | dds.build(apps=True) |
{ | |||||
"version": 1, | |||||
"packages": { | |||||
"neo-buffer": { | |||||
"0.1.0": { | |||||
"git": { | |||||
"url": "https://github.com/vector-of-bool/neo-buffer.git", | |||||
"ref": "develop" | |||||
}, | |||||
"depends": {} | |||||
} | |||||
}, | |||||
"range-v3": { | |||||
"0.9.1": { | |||||
"git": { | |||||
"url": "https://github.com/ericniebler/range-v3.git", | |||||
"ref": "0.9.1", | |||||
"auto-lib": "Niebler/range-v3" | |||||
}, | |||||
"depends": {} | |||||
} | |||||
} | |||||
} | |||||
} |
Remote-Package: neo-buffer@0.1.0; git url=https://github.com/vector-of-bool/neo-buffer.git ref=develop | |||||
Remote-Package: range-v3@0.9.1; git url=https://github.com/ericniebler/range-v3.git ref=0.9.1 auto=Niebler/range-v3 |
{ | |||||
"version": 1, | |||||
"packages": {} | |||||
} |
{ | |||||
"version": 1, | |||||
"packages": { | |||||
"nlohmann-json": { | |||||
"3.7.1": { | |||||
"git": { | |||||
"url": "https://github.com/vector-of-bool/json.git", | |||||
"ref": "dds/3.7.1" | |||||
}, | |||||
"depends": {} | |||||
} | |||||
} | |||||
} | |||||
} |
Remote-Package: nlohmann-json@3.7.1; git url=https://github.com/vector-of-bool/json.git ref=dds/3.7.1 |
{ | |||||
"version": 1, | |||||
"packages": { | |||||
"spdlog": { | |||||
"1.4.2": { | |||||
"git": { | |||||
"url": "https://github.com/gabime/spdlog.git", | |||||
"ref": "v1.4.2", | |||||
"auto-lib": "spdlog/spdlog" | |||||
}, | |||||
"depends": {} | |||||
} | |||||
} | |||||
} | |||||
} |
Remote-Package: spdlog@1.4.2; git url=https://github.com/gabime/spdlog.git ref=v1.4.2 auto=spdlog/spdlog |
def test_get_build_use_spdlog(dds: DDS): | def test_get_build_use_spdlog(dds: DDS): | ||||
dds.catalog_import(dds.source_root / 'catalog.json') | |||||
dds.deps_get() | dds.deps_get() | ||||
tc_fname = 'gcc.tc.dds' if 'gcc' in dds.default_builtin_toolchain else 'msvc.tc.dds' | tc_fname = 'gcc.tc.dds' if 'gcc' in dds.default_builtin_toolchain else 'msvc.tc.dds' | ||||
tc = str(dds.test_dir / tc_fname) | tc = str(dds.test_dir / tc_fname) |