[M8] cargoxx-git dependencies: { git = ..., rev = ... } deps
This commit is contained in:
30
CHANGELOG.md
30
CHANGELOG.md
@@ -498,3 +498,33 @@ All notable changes to cargoxx will be documented in this file.
|
||||
validation + binary-cache substitution. Phase 1a (install rules)
|
||||
and Phase 1b (path deps) shipped in this commit; phases 1c, 1d,
|
||||
and 2 remain to be built.
|
||||
- M8 cargoxx-git dependencies (`{ git = "<url>", rev = "<40-char>" }`).
|
||||
The manifest accepts a third dep-table form:
|
||||
```toml
|
||||
[dependencies]
|
||||
mylib = { git = "https://gitea/me/mylib", rev = "abc…" }
|
||||
```
|
||||
Branch/tag refs are not yet supported — the rev must be a literal
|
||||
40-char commit. `manifest::Dependency` carries `git_url` + `git_rev`;
|
||||
parser branches on `git`, rejects git deps that omit `rev`.
|
||||
Lockfile schema adds `source_git_url` + `source_git_commit` +
|
||||
`source_git_sha256` (SRI form, e.g. `sha256-<base64>`). The sha256
|
||||
pins the fetched source as a fixed-output derivation: `pkgs.fetchgit`
|
||||
in the consumer's `buildCppPackage` substitutes from cache when the
|
||||
same `(url, rev, sha256)` triple appears elsewhere.
|
||||
`cmd_build::resolve_git_dep` reuses the lockfile's prior sha256 on
|
||||
re-builds; on a fresh dep it calls
|
||||
`resolver::prefetch_flake_source(git+<url>?rev=<rev>)` which now
|
||||
returns `{store_path, hash}` (extended via a new
|
||||
`PrefetchedSource` struct, with `realize_flake_source` kept as a
|
||||
thin compat wrapper). Verifies the dep's name by reading the
|
||||
fetched tree's `Cargoxx.toml`. In `--offline` mode, a git dep
|
||||
without a cached hash errors with a clear "run online first"
|
||||
message.
|
||||
`flake.nix`'s `evalDep` branches on `source_kind == "cargoxx-git"`
|
||||
and feeds `pkgs.fetchgit { url, rev, hash }` into a recursive
|
||||
`buildCppPackage` call. The fetched source is content-addressed,
|
||||
so the entire `(fetch → install)` closure is cacheable end-to-end.
|
||||
Codegen unchanged — the synthesized recipe (find_package +
|
||||
`<name>::<name>` target) is identical to the path-dep case, just
|
||||
with a different `linkdb_source` discriminator.
|
||||
|
||||
19
flake.nix
19
flake.nix
@@ -69,12 +69,19 @@
|
||||
(builtins.getFlake "github:NixOS/nixpkgs/${rev}")
|
||||
.legacyPackages.${system};
|
||||
|
||||
# cargoxx-path deps recurse into buildCppPackage on the sibling
|
||||
# source tree; the result joins buildInputs so the consumer's
|
||||
# find_package(<dep> CONFIG REQUIRED) resolves via CMAKE_PREFIX_PATH.
|
||||
# cargoxx-source deps recurse into buildCppPackage; the result
|
||||
# joins buildInputs so the consumer's find_package(<dep> CONFIG
|
||||
# REQUIRED) resolves via CMAKE_PREFIX_PATH.
|
||||
evalDep = p:
|
||||
if (p.source_kind or "") == "cargoxx-path" then
|
||||
buildCppPackage { src = src + ("/" + p.source_path); }
|
||||
else if (p.source_kind or "") == "cargoxx-git" then
|
||||
let depSrc = pkgs.fetchgit {
|
||||
url = p.source_git_url;
|
||||
rev = p.source_git_commit;
|
||||
hash = p.source_git_sha256;
|
||||
};
|
||||
in buildCppPackage { src = depSrc; }
|
||||
else
|
||||
let rev = if (p ? nixpkgs_rev) && (p.nixpkgs_rev != "")
|
||||
then p.nixpkgs_rev
|
||||
@@ -97,9 +104,9 @@
|
||||
# For cargoxx-source deps we don't have a nixpkgs rev/attr — the
|
||||
# vendor.toml entry just needs a name + store_path so cargoxx's
|
||||
# offline pathway can find the dep's installed prefix.
|
||||
isPath = (p.source_kind or "") == "cargoxx-path";
|
||||
attr = if isPath then "" else p.nixpkgs_attr;
|
||||
rev = if isPath then ""
|
||||
isCargoxx = (p.source_kind or "") != "";
|
||||
attr = if isCargoxx then "" else p.nixpkgs_attr;
|
||||
rev = if isCargoxx then ""
|
||||
else if (p ? nixpkgs_rev) && (p.nixpkgs_rev != "")
|
||||
then p.nixpkgs_rev else lock.nixpkgs_rev;
|
||||
in ''
|
||||
|
||||
@@ -74,7 +74,9 @@ auto query_flake_rev(std::string_view flake_ref) -> std::optional<std::string> {
|
||||
auto merge_lockfile(const manifest::Manifest& m,
|
||||
const std::vector<linkdb::Recipe>& recipes,
|
||||
const std::vector<linkdb::Recipe>& dev_recipes,
|
||||
const lockfile::Lockfile& prior) -> lockfile::Lockfile {
|
||||
const lockfile::Lockfile& prior,
|
||||
const std::map<std::string, std::string>& git_sha256s)
|
||||
-> lockfile::Lockfile {
|
||||
auto find_prior = [&](const std::string& name, const std::string& version)
|
||||
-> std::optional<lockfile::LockfilePackage> {
|
||||
for (const auto& p : prior.packages) {
|
||||
@@ -122,9 +124,19 @@ auto merge_lockfile(const manifest::Manifest& m,
|
||||
}
|
||||
std::optional<std::string> source_kind;
|
||||
std::optional<std::string> source_path;
|
||||
std::optional<std::string> source_git_url;
|
||||
std::optional<std::string> source_git_commit;
|
||||
std::optional<std::string> source_git_sha256;
|
||||
if (dep.source == manifest::DepSource::CargoxxPath) {
|
||||
source_kind = "cargoxx-path";
|
||||
source_path = dep.path;
|
||||
} else if (dep.source == manifest::DepSource::CargoxxGit) {
|
||||
source_kind = "cargoxx-git";
|
||||
source_git_url = dep.git_url;
|
||||
source_git_commit = dep.git_rev;
|
||||
if (auto it = git_sha256s.find(dep.name); it != git_sha256s.end()) {
|
||||
source_git_sha256 = it->second;
|
||||
}
|
||||
}
|
||||
lock.packages.push_back(lockfile::LockfilePackage{
|
||||
.name = dep.name,
|
||||
@@ -140,6 +152,9 @@ auto merge_lockfile(const manifest::Manifest& m,
|
||||
.brute_force_includes = rec.brute_force_includes,
|
||||
.source_kind = std::move(source_kind),
|
||||
.source_path = std::move(source_path),
|
||||
.source_git_url = std::move(source_git_url),
|
||||
.source_git_commit = std::move(source_git_commit),
|
||||
.source_git_sha256 = std::move(source_git_sha256),
|
||||
});
|
||||
};
|
||||
for (std::size_t i = 0; i < m.dependencies.size(); ++i) {
|
||||
@@ -288,6 +303,70 @@ auto cmd_build(const fs::path& project_root, bool no_build, bool release,
|
||||
};
|
||||
};
|
||||
|
||||
// Side channel: (dep name) → SRI hash captured during git resolution,
|
||||
// consumed by merge_lockfile to persist source_git_sha256.
|
||||
std::map<std::string, std::string> git_sha256s;
|
||||
|
||||
// For { git = "...", rev = "..." } deps: if the prior lockfile already
|
||||
// records an SRI hash for this (url, commit), reuse it. Otherwise run
|
||||
// `nix flake prefetch` to fetch + hash the source tree (FOD-compatible),
|
||||
// then verify the dep's Cargoxx.toml name matches.
|
||||
auto resolve_git_dep = [&](const manifest::Dependency& dep)
|
||||
-> util::Result<linkdb::Recipe> {
|
||||
std::optional<std::string> cached_sha;
|
||||
for (const auto& p : prior.packages) {
|
||||
if (p.name == dep.name && p.source_kind == "cargoxx-git" &&
|
||||
p.source_git_url == dep.git_url &&
|
||||
p.source_git_commit == dep.git_rev && p.source_git_sha256) {
|
||||
cached_sha = p.source_git_sha256;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!cached_sha) {
|
||||
if (offline) {
|
||||
return std::unexpected(util::Error{
|
||||
util::ErrorCode::BuildCmakeFailed,
|
||||
std::format("git dep '{}' has no cached hash and --offline "
|
||||
"forbids network fetch", dep.name),
|
||||
"run `cargoxx build` once online to populate Cargoxx.lock",
|
||||
std::nullopt, std::nullopt,
|
||||
});
|
||||
}
|
||||
auto flake_ref = std::format("git+{}?rev={}", *dep.git_url, *dep.git_rev);
|
||||
auto prefetched = resolver::prefetch_flake_source(flake_ref);
|
||||
if (!prefetched) {
|
||||
return std::unexpected(prefetched.error());
|
||||
}
|
||||
// Verify name matches by reading the fetched tree's Cargoxx.toml.
|
||||
auto dep_manifest = manifest::parse(
|
||||
std::filesystem::path{prefetched->store_path} / "Cargoxx.toml");
|
||||
if (!dep_manifest) {
|
||||
return std::unexpected(dep_manifest.error());
|
||||
}
|
||||
if (dep_manifest->package.name != dep.name) {
|
||||
return std::unexpected(util::Error{
|
||||
util::ErrorCode::ManifestInvalidField,
|
||||
std::format("git dep '{}' points to a project named '{}'",
|
||||
dep.name, dep_manifest->package.name),
|
||||
"rename the dep or use a repo whose [package].name matches",
|
||||
std::nullopt, std::nullopt,
|
||||
});
|
||||
}
|
||||
cached_sha = prefetched->hash;
|
||||
}
|
||||
git_sha256s[dep.name] = *cached_sha;
|
||||
return linkdb::Recipe{
|
||||
.nixpkgs_attr = "",
|
||||
.find_package = std::format("{} CONFIG REQUIRED", dep.name),
|
||||
.targets = {std::format("{}::{}", dep.name, dep.name)},
|
||||
.source = "cargoxx-git",
|
||||
.pkg_config_module = std::nullopt,
|
||||
.brute_force_libs = {},
|
||||
.brute_force_includes = {},
|
||||
};
|
||||
};
|
||||
|
||||
auto resolve_list = [&](const std::vector<manifest::Dependency>& deps)
|
||||
-> util::Result<std::vector<linkdb::Recipe>> {
|
||||
std::vector<linkdb::Recipe> out;
|
||||
@@ -301,6 +380,14 @@ auto cmd_build(const fs::path& project_root, bool no_build, bool release,
|
||||
out.push_back(std::move(*r));
|
||||
continue;
|
||||
}
|
||||
if (dep.source == manifest::DepSource::CargoxxGit) {
|
||||
auto r = resolve_git_dep(dep);
|
||||
if (!r) {
|
||||
return std::unexpected(r.error());
|
||||
}
|
||||
out.push_back(std::move(*r));
|
||||
continue;
|
||||
}
|
||||
if (auto cached = recipe_from_lock(dep.name, dep.version_spec); cached) {
|
||||
out.push_back(std::move(*cached));
|
||||
continue;
|
||||
@@ -329,7 +416,7 @@ auto cmd_build(const fs::path& project_root, bool no_build, bool release,
|
||||
if (!dev_recipes) {
|
||||
return std::unexpected(dev_recipes.error());
|
||||
}
|
||||
auto lock = merge_lockfile(*m, *recipes, *dev_recipes, prior);
|
||||
auto lock = merge_lockfile(*m, *recipes, *dev_recipes, prior, git_sha256s);
|
||||
|
||||
std::optional<codegen::VendorIndex> vendor_index;
|
||||
if (offline) {
|
||||
|
||||
@@ -112,6 +112,15 @@ auto parse_package(const toml::table& tbl, const std::filesystem::path& path)
|
||||
if (auto v = tbl["source_path"].value<std::string>()) {
|
||||
pkg.source_path = *v;
|
||||
}
|
||||
if (auto v = tbl["source_git_url"].value<std::string>()) {
|
||||
pkg.source_git_url = *v;
|
||||
}
|
||||
if (auto v = tbl["source_git_commit"].value<std::string>()) {
|
||||
pkg.source_git_commit = *v;
|
||||
}
|
||||
if (auto v = tbl["source_git_sha256"].value<std::string>()) {
|
||||
pkg.source_git_sha256 = *v;
|
||||
}
|
||||
|
||||
return pkg;
|
||||
}
|
||||
@@ -227,6 +236,15 @@ auto write(const Lockfile& lock, const std::filesystem::path& path) -> util::Res
|
||||
if (p.source_path) {
|
||||
tbl.insert_or_assign("source_path", *p.source_path);
|
||||
}
|
||||
if (p.source_git_url) {
|
||||
tbl.insert_or_assign("source_git_url", *p.source_git_url);
|
||||
}
|
||||
if (p.source_git_commit) {
|
||||
tbl.insert_or_assign("source_git_commit", *p.source_git_commit);
|
||||
}
|
||||
if (p.source_git_sha256) {
|
||||
tbl.insert_or_assign("source_git_sha256", *p.source_git_sha256);
|
||||
}
|
||||
packages.push_back(std::move(tbl));
|
||||
}
|
||||
root.insert_or_assign("package", std::move(packages));
|
||||
|
||||
@@ -18,10 +18,15 @@ struct LockfilePackage {
|
||||
std::optional<std::string> pkg_config_module;
|
||||
std::vector<std::string> brute_force_libs;
|
||||
std::vector<std::string> brute_force_includes;
|
||||
// For cargoxx-source deps (not nixpkgs/linkdb-resolved). v1 supports
|
||||
// "cargoxx-path"; "cargoxx-git" / "cargoxx-registry" land in 1c/1d.
|
||||
// For cargoxx-source deps (not nixpkgs/linkdb-resolved).
|
||||
// "cargoxx-path" → source_path only
|
||||
// "cargoxx-git" → source_git_url + source_git_commit + source_git_sha256
|
||||
// "cargoxx-registry" → (1d)
|
||||
std::optional<std::string> source_kind;
|
||||
std::optional<std::string> source_path;
|
||||
std::optional<std::string> source_git_url;
|
||||
std::optional<std::string> source_git_commit;
|
||||
std::optional<std::string> source_git_sha256;
|
||||
|
||||
bool operator==(const LockfilePackage&) const = default;
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ export namespace cargoxx::manifest {
|
||||
enum class DepSource {
|
||||
Auto, // string form or { version = ... } only → existing resolver chain
|
||||
CargoxxPath, // { path = "../foo" } → recursive cargoxx build
|
||||
CargoxxGit, // { git = "...", rev = "..." } → fetch + recursive cargoxx build
|
||||
};
|
||||
|
||||
struct Dependency {
|
||||
@@ -15,7 +16,9 @@ struct Dependency {
|
||||
std::string version_spec;
|
||||
std::vector<std::string> components;
|
||||
DepSource source = DepSource::Auto;
|
||||
std::optional<std::string> path; // when source == CargoxxPath
|
||||
std::optional<std::string> path; // when source == CargoxxPath
|
||||
std::optional<std::string> git_url; // when source == CargoxxGit
|
||||
std::optional<std::string> git_rev; // when source == CargoxxGit (40-char)
|
||||
|
||||
bool operator==(const Dependency&) const = default;
|
||||
};
|
||||
|
||||
@@ -153,9 +153,7 @@ auto parse_dependency(std::string name, const toml::node& value,
|
||||
}
|
||||
|
||||
if (const auto* tbl = value.as_table()) {
|
||||
// Path form takes precedence: { path = "../foo" } → cargoxx-source dep.
|
||||
// Version is optional in the path form (defaulting to "*"); the dep's
|
||||
// own Cargoxx.toml supplies the real version at resolve time.
|
||||
// Path form: { path = "../foo" } → cargoxx-source dep.
|
||||
if (auto path_str = (*tbl)["path"].value<std::string>()) {
|
||||
dep.source = DepSource::CargoxxPath;
|
||||
dep.path = *path_str;
|
||||
@@ -167,13 +165,37 @@ auto parse_dependency(std::string name, const toml::node& value,
|
||||
return dep;
|
||||
}
|
||||
|
||||
// Git form: { git = "...", rev = "<40-char>" } → cargoxx-source dep
|
||||
// fetched at the pinned commit. Branch/tag resolution is not yet
|
||||
// supported; require a literal commit.
|
||||
if (auto git_url = (*tbl)["git"].value<std::string>()) {
|
||||
auto rev = (*tbl)["rev"].value<std::string>();
|
||||
if (!rev) {
|
||||
return std::unexpected(err(
|
||||
ErrorCode::ManifestInvalidField,
|
||||
std::format("git dependency '{}' must specify a 'rev' (40-char commit)",
|
||||
dep.name),
|
||||
path, source_pos(value)));
|
||||
}
|
||||
dep.source = DepSource::CargoxxGit;
|
||||
dep.git_url = *git_url;
|
||||
dep.git_rev = *rev;
|
||||
if (auto v = (*tbl)["version"].value<std::string>()) {
|
||||
dep.version_spec = *v;
|
||||
} else {
|
||||
dep.version_spec = "*";
|
||||
}
|
||||
return dep;
|
||||
}
|
||||
|
||||
if (auto v = (*tbl)["version"].value<std::string>()) {
|
||||
dep.version_spec = *v;
|
||||
} else {
|
||||
return std::unexpected(err(
|
||||
ErrorCode::ManifestInvalidField,
|
||||
std::format("dependency '{}' table must have a 'version' or 'path' string",
|
||||
dep.name),
|
||||
std::format(
|
||||
"dependency '{}' table must have one of: 'version', 'path', 'git'",
|
||||
dep.name),
|
||||
path, source_pos(value)));
|
||||
}
|
||||
if (const auto* comps = (*tbl)["components"].as_array()) {
|
||||
|
||||
@@ -54,6 +54,12 @@ auto build_table(const Manifest& m) -> toml::table {
|
||||
dep_tbl.insert_or_assign("path", *dep.path);
|
||||
dep_tbl.is_inline(true);
|
||||
out.insert_or_assign(dep.name, std::move(dep_tbl));
|
||||
} else if (dep.source == DepSource::CargoxxGit) {
|
||||
toml::table dep_tbl;
|
||||
dep_tbl.insert_or_assign("git", *dep.git_url);
|
||||
dep_tbl.insert_or_assign("rev", *dep.git_rev);
|
||||
dep_tbl.is_inline(true);
|
||||
out.insert_or_assign(dep.name, std::move(dep_tbl));
|
||||
} else if (dep.components.empty()) {
|
||||
out.insert_or_assign(dep.name, dep.version_spec);
|
||||
} else {
|
||||
|
||||
@@ -207,12 +207,31 @@ auto realize_path_at_rev(const std::string& rev, const std::string& attr)
|
||||
return path;
|
||||
}
|
||||
|
||||
auto realize_flake_source(const std::string& flake_ref)
|
||||
-> util::Result<std::string> {
|
||||
namespace {
|
||||
|
||||
auto extract_json_string(std::string_view body, std::string_view key)
|
||||
-> std::optional<std::string> {
|
||||
auto needle = std::format("\"{}\":\"", key);
|
||||
auto pos = body.find(needle);
|
||||
if (pos == std::string_view::npos) {
|
||||
return std::nullopt;
|
||||
}
|
||||
pos += needle.size();
|
||||
auto end = body.find('"', pos);
|
||||
if (end == std::string_view::npos) {
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::string{body.substr(pos, end - pos)};
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
auto prefetch_flake_source(const std::string& flake_ref)
|
||||
-> util::Result<PrefetchedSource> {
|
||||
if (flake_ref.empty()) {
|
||||
return std::unexpected(make_error(
|
||||
util::ErrorCode::ResolutionUnknownPackage,
|
||||
"realize_flake_source: flake_ref is empty"));
|
||||
"prefetch_flake_source: flake_ref is empty"));
|
||||
}
|
||||
std::vector<std::string> args{
|
||||
"--extra-experimental-features", "nix-command flakes",
|
||||
@@ -234,23 +253,33 @@ auto realize_flake_source(const std::string& flake_ref)
|
||||
std::format("nix flake prefetch failed (exit {}): {}",
|
||||
r->exit_code, r->stderr_text)));
|
||||
}
|
||||
std::string_view body = r->stdout_text;
|
||||
constexpr std::string_view key = "\"storePath\":\"";
|
||||
auto pos = body.find(key);
|
||||
if (pos == std::string_view::npos) {
|
||||
auto store_path = extract_json_string(r->stdout_text, "storePath");
|
||||
auto hash = extract_json_string(r->stdout_text, "hash");
|
||||
if (!store_path) {
|
||||
return std::unexpected(make_error(
|
||||
util::ErrorCode::ResolutionNetworkError,
|
||||
std::format("nix flake prefetch emitted no storePath for '{}'",
|
||||
flake_ref)));
|
||||
}
|
||||
pos += key.size();
|
||||
auto end = body.find('"', pos);
|
||||
if (end == std::string_view::npos) {
|
||||
if (!hash) {
|
||||
return std::unexpected(make_error(
|
||||
util::ErrorCode::ResolutionNetworkError,
|
||||
"nix flake prefetch JSON malformed"));
|
||||
std::format("nix flake prefetch emitted no hash for '{}'",
|
||||
flake_ref)));
|
||||
}
|
||||
return std::string{body.substr(pos, end - pos)};
|
||||
return PrefetchedSource{
|
||||
.store_path = std::move(*store_path),
|
||||
.hash = std::move(*hash),
|
||||
};
|
||||
}
|
||||
|
||||
auto realize_flake_source(const std::string& flake_ref)
|
||||
-> util::Result<std::string> {
|
||||
auto r = prefetch_flake_source(flake_ref);
|
||||
if (!r) {
|
||||
return std::unexpected(r.error());
|
||||
}
|
||||
return std::move(r->store_path);
|
||||
}
|
||||
|
||||
} // namespace cargoxx::resolver
|
||||
|
||||
@@ -59,6 +59,17 @@ auto realize_path_at_rev(const std::string& rev, const std::string& attr)
|
||||
auto realize_flake_source(const std::string& flake_ref)
|
||||
-> util::Result<std::string>;
|
||||
|
||||
struct PrefetchedSource {
|
||||
std::string store_path;
|
||||
std::string hash; // SRI form, e.g. "sha256-<base64>"
|
||||
};
|
||||
|
||||
// Same as realize_flake_source but also returns the SRI hash so the
|
||||
// caller can persist it in a lockfile and feed it to `pkgs.fetchgit`
|
||||
// as a fixed-output derivation pin. Used by cargoxx-git deps.
|
||||
auto prefetch_flake_source(const std::string& flake_ref)
|
||||
-> util::Result<PrefetchedSource>;
|
||||
|
||||
// One CMake config-file's IMPORTED targets together with the find_package
|
||||
// expression derived from its filename stem.
|
||||
struct NixCmakeCandidate {
|
||||
|
||||
@@ -142,6 +142,33 @@ TEST_CASE("write round-trips cargoxx-path source fields", "[lockfile]") {
|
||||
REQUIRE(round_trip(l) == l);
|
||||
}
|
||||
|
||||
TEST_CASE("write round-trips cargoxx-git source fields", "[lockfile]") {
|
||||
Lockfile l{
|
||||
.version = 1,
|
||||
.packages = {
|
||||
LockfilePackage{
|
||||
.name = "mylib",
|
||||
.version = "*",
|
||||
.dependencies = {},
|
||||
.nixpkgs_attr = std::nullopt,
|
||||
.nixpkgs_rev = std::nullopt,
|
||||
.linkdb_source = "cargoxx-git",
|
||||
.find_package = "mylib CONFIG REQUIRED",
|
||||
.targets = {"mylib::mylib"},
|
||||
.pkg_config_module = std::nullopt,
|
||||
.brute_force_libs = {},
|
||||
.brute_force_includes = {},
|
||||
.source_kind = "cargoxx-git",
|
||||
.source_path = std::nullopt,
|
||||
.source_git_url = "https://gitea.example/me/mylib",
|
||||
.source_git_commit = "0123456789012345678901234567890123456789",
|
||||
.source_git_sha256 = "sha256-abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123=",
|
||||
},
|
||||
},
|
||||
};
|
||||
REQUIRE(round_trip(l) == l);
|
||||
}
|
||||
|
||||
TEST_CASE("Lockfile::nixpkgs_rev returns the shared rev", "[lockfile]") {
|
||||
Lockfile l{
|
||||
.version = 1,
|
||||
|
||||
@@ -336,3 +336,40 @@ mylib = { components = ["a"] }
|
||||
REQUIRE_FALSE(r.has_value());
|
||||
REQUIRE(r.error().code == ErrorCode::ManifestInvalidField);
|
||||
}
|
||||
|
||||
TEST_CASE("parse recognizes { git = \"...\", rev = \"...\" } as a cargoxx git dep",
|
||||
"[manifest][parse]") {
|
||||
auto p = write_manifest(R"(
|
||||
[package]
|
||||
name = "consumer"
|
||||
version = "0.1.0"
|
||||
edition = "cpp23"
|
||||
|
||||
[dependencies]
|
||||
mylib = { git = "https://gitea.example/me/mylib", rev = "0123456789012345678901234567890123456789" }
|
||||
)");
|
||||
auto r = parse(p);
|
||||
REQUIRE(r.has_value());
|
||||
REQUIRE(r->dependencies.size() == 1);
|
||||
const auto& dep = r->dependencies[0];
|
||||
REQUIRE(dep.name == "mylib");
|
||||
REQUIRE(dep.source == cargoxx::manifest::DepSource::CargoxxGit);
|
||||
REQUIRE(dep.git_url == "https://gitea.example/me/mylib");
|
||||
REQUIRE(dep.git_rev == "0123456789012345678901234567890123456789");
|
||||
REQUIRE(dep.version_spec == "*");
|
||||
}
|
||||
|
||||
TEST_CASE("parse rejects git dep without rev", "[manifest][parse]") {
|
||||
auto p = write_manifest(R"(
|
||||
[package]
|
||||
name = "consumer"
|
||||
version = "0.1.0"
|
||||
edition = "cpp23"
|
||||
|
||||
[dependencies]
|
||||
mylib = { git = "https://gitea.example/me/mylib" }
|
||||
)");
|
||||
auto r = parse(p);
|
||||
REQUIRE_FALSE(r.has_value());
|
||||
REQUIRE(r.error().code == ErrorCode::ManifestInvalidField);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user