From b0818846b21ca12f7c7fb5211c69171054d705b0 Mon Sep 17 00:00:00 2001 From: Amadey Vorontsov Date: Sun, 10 May 2026 00:56:15 +0000 Subject: [PATCH] [M5] cargoxx add without version (wildcard) --- CHANGELOG.md | 10 +++++++--- src/cli/cmd_add.cpp | 17 ++++++----------- src/util/semver.cpp | 5 +++-- tests/cmd_add.cpp | 19 +++++++++++++++++-- tests/semver_satisfies.cpp | 8 ++++++++ 5 files changed, 41 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 691a10e..9c5a2b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 @ [--components a,b]` edits `Cargoxx.toml`, +- `cargoxx add [@] [--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 ` 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` diff --git a/src/cli/cmd_add.cpp b/src/cli/cmd_add.cpp index 4f8bbad..626de4d 100644 --- a/src/cli/cmd_add.cpp +++ b/src/cli/cmd_add.cpp @@ -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 @' " - "(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), }); diff --git a/src/util/semver.cpp b/src/util/semver.cpp index 7a84979..264245a 100644 --- a/src/util/semver.cpp +++ b/src/util/semver.cpp @@ -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; } diff --git a/tests/cmd_add.cpp b/tests/cmd_add.cpp index e69072a..dd80a27 100644 --- a/tests/cmd_add.cpp +++ b/tests/cmd_add.cpp @@ -61,13 +61,28 @@ TEST_CASE("cmd_add stores components when provided", "[cli][add]") { std::vector{"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]") { diff --git a/tests/semver_satisfies.cpp b/tests/semver_satisfies.cpp index 6a0abb8..beb9227 100644 --- a/tests/semver_satisfies.cpp +++ b/tests/semver_satisfies.cpp @@ -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("*", "*")); +}