[M5] cargoxx add without version (wildcard)

This commit is contained in:
2026-05-10 00:56:15 +00:00
parent 8b87d98083
commit b0818846b2
5 changed files with 41 additions and 18 deletions

View File

@@ -78,11 +78,15 @@ All notable changes to cargoxx will be documented in this file.
and `[build]` honoring `warnings_as_errors` and `sanitizers`. Source
paths emitted relative to `build/` (i.e. prefixed with `../`).
Output is deterministic. `tests/codegen_cmake.cpp` covers 11 cases.
- `cargoxx add <pkg>@<version> [--components a,b]` edits `Cargoxx.toml`,
- `cargoxx add <pkg>[@<version>] [--components a,b]` edits `Cargoxx.toml`,
validates the new dep against the curated linkdb (so unknown packages
and missing components fail before any disk write), and rejects
already-declared deps. Auto-resolution against nixhub.io/lazamar is
deferred — for v0.1 the version is required.
already-declared deps. Version is now optional; an omitted version
is stored as `"*"` and resolves against the linkdb's first matching
recipe. Generated `flake.nix` continues to track the `nixos-unstable`
branch when the lockfile carries no rev.
- `util::satisfies` treats `version == "*"` as a match against any
range, mirroring the existing `range == "*"` shortcut.
- `cargoxx remove <pkg>` drops a declared dep from `Cargoxx.toml`,
errors when the dep is not declared. Other deps are preserved.
- End-to-end verified on a freshly-scaffolded project. `tests/cmd_add.cpp`

View File

@@ -19,15 +19,10 @@ auto cmd_add(const fs::path& project_root, const std::string& name,
"", std::nullopt, std::nullopt,
});
}
if (version_spec.empty()) {
return std::unexpected(util::Error{
util::ErrorCode::ManifestVersionInvalid,
std::format("version required for '{}'", name),
"use the form 'cargoxx add <pkg>@<version>' "
"(auto-resolve against nixhub is deferred)",
std::nullopt, std::nullopt,
});
}
// Empty version → wildcard. The generated flake.nix tracks `nixos-unstable`
// and the linkdb resolver picks whichever recipe is listed first for the
// package. Concrete versions land when the resolver against nixhub.io ships.
const std::string effective_version = version_spec.empty() ? std::string{"*"} : version_spec;
auto manifest_path = project_root / "Cargoxx.toml";
auto m = manifest::parse(manifest_path);
@@ -50,13 +45,13 @@ auto cmd_add(const fs::path& project_root, const std::string& name,
if (!db) {
return std::unexpected(db.error());
}
if (auto check = db->resolve(name, version_spec, components); !check) {
if (auto check = db->resolve(name, effective_version, components); !check) {
return std::unexpected(check.error());
}
m->dependencies.push_back(manifest::Dependency{
.name = name,
.version_spec = version_spec,
.version_spec = effective_version,
.components = std::move(components),
});

View File

@@ -113,11 +113,12 @@ auto check(const Clause& c, const Version& v) -> bool {
auto satisfies(std::string_view version, std::string_view range) -> bool {
range = trim(range);
if (range == "*") {
version = trim(version);
if (range == "*" || version == "*") {
return true;
}
auto v = parse_version(trim(version));
auto v = parse_version(version);
if (!v) {
return false;
}

View File

@@ -61,13 +61,28 @@ TEST_CASE("cmd_add stores components when provided", "[cli][add]") {
std::vector<std::string>{"filesystem", "system"});
}
TEST_CASE("cmd_add rejects an empty version", "[cli][add]") {
TEST_CASE("cmd_add accepts an empty version and stores '*'", "[cli][add]") {
auto parent = fresh_dir();
auto root = scaffold(parent);
auto r = cmd_add(root, "fmt", "", {}, overlay_path(parent));
REQUIRE(r.has_value());
auto m = manifest::parse(root / "Cargoxx.toml");
REQUIRE(m.has_value());
REQUIRE(m->dependencies.size() == 1);
REQUIRE(m->dependencies[0].name == "fmt");
REQUIRE(m->dependencies[0].version_spec == "*");
}
TEST_CASE("cmd_add with wildcard version still rejects unknown packages",
"[cli][add]") {
auto parent = fresh_dir();
auto root = scaffold(parent);
auto r = cmd_add(root, "obscurelib", "", {}, overlay_path(parent));
REQUIRE_FALSE(r.has_value());
REQUIRE(r.error().code == ErrorCode::ManifestVersionInvalid);
REQUIRE(r.error().code == ErrorCode::LinkdbUnknownPackage);
}
TEST_CASE("cmd_add rejects an unknown package", "[cli][add]") {

View File

@@ -52,3 +52,11 @@ TEST_CASE("satisfies returns false on malformed inputs", "[util][semver]") {
REQUIRE_FALSE(satisfies("not-a-version", ">=1.0.0"));
REQUIRE_FALSE(satisfies("1.0.0", "garbage"));
}
TEST_CASE("satisfies treats version='*' as a match for any range",
"[util][semver]") {
REQUIRE(satisfies("*", ">=10.0.0"));
REQUIRE(satisfies("*", "<2.0.0"));
REQUIRE(satisfies("*", "==1.2.3"));
REQUIRE(satisfies("*", "*"));
}