[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 and `[build]` honoring `warnings_as_errors` and `sanitizers`. Source
paths emitted relative to `build/` (i.e. prefixed with `../`). paths emitted relative to `build/` (i.e. prefixed with `../`).
Output is deterministic. `tests/codegen_cmake.cpp` covers 11 cases. 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 validates the new dep against the curated linkdb (so unknown packages
and missing components fail before any disk write), and rejects and missing components fail before any disk write), and rejects
already-declared deps. Auto-resolution against nixhub.io/lazamar is already-declared deps. Version is now optional; an omitted version
deferred — for v0.1 the version is required. 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`, - `cargoxx remove <pkg>` drops a declared dep from `Cargoxx.toml`,
errors when the dep is not declared. Other deps are preserved. errors when the dep is not declared. Other deps are preserved.
- End-to-end verified on a freshly-scaffolded project. `tests/cmd_add.cpp` - 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, "", std::nullopt, std::nullopt,
}); });
} }
if (version_spec.empty()) { // Empty version → wildcard. The generated flake.nix tracks `nixos-unstable`
return std::unexpected(util::Error{ // and the linkdb resolver picks whichever recipe is listed first for the
util::ErrorCode::ManifestVersionInvalid, // package. Concrete versions land when the resolver against nixhub.io ships.
std::format("version required for '{}'", name), const std::string effective_version = version_spec.empty() ? std::string{"*"} : version_spec;
"use the form 'cargoxx add <pkg>@<version>' "
"(auto-resolve against nixhub is deferred)",
std::nullopt, std::nullopt,
});
}
auto manifest_path = project_root / "Cargoxx.toml"; auto manifest_path = project_root / "Cargoxx.toml";
auto m = manifest::parse(manifest_path); auto m = manifest::parse(manifest_path);
@@ -50,13 +45,13 @@ auto cmd_add(const fs::path& project_root, const std::string& name,
if (!db) { if (!db) {
return std::unexpected(db.error()); 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()); return std::unexpected(check.error());
} }
m->dependencies.push_back(manifest::Dependency{ m->dependencies.push_back(manifest::Dependency{
.name = name, .name = name,
.version_spec = version_spec, .version_spec = effective_version,
.components = std::move(components), .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 { auto satisfies(std::string_view version, std::string_view range) -> bool {
range = trim(range); range = trim(range);
if (range == "*") { version = trim(version);
if (range == "*" || version == "*") {
return true; return true;
} }
auto v = parse_version(trim(version)); auto v = parse_version(version);
if (!v) { if (!v) {
return false; return false;
} }

View File

@@ -61,13 +61,28 @@ TEST_CASE("cmd_add stores components when provided", "[cli][add]") {
std::vector<std::string>{"filesystem", "system"}); 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 parent = fresh_dir();
auto root = scaffold(parent); auto root = scaffold(parent);
auto r = cmd_add(root, "fmt", "", {}, overlay_path(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_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]") { 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("not-a-version", ">=1.0.0"));
REQUIRE_FALSE(satisfies("1.0.0", "garbage")); 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("*", "*"));
}