34 KiB
Changelog
All notable changes to cargoxx will be documented in this file.
[Unreleased]
Added
-
M0 repo skeleton: hand-written
CMakeLists.txt, bootstrapflake.nix, empty C++23 module units for every component listed inTECH_SPEC.md§1 (util,exec,manifest,lockfile,layout,linkdb,resolver,codegen,cli), root modulecargoxx, and a stubmain.cppthat builds an emptycargoxxbinary. -
.clang-format(LLVM, 100-column) and.gitignore. -
SPEC.md,TECH_SPEC.md. -
M1 foundation in
cargoxx.util:ErrorCode(numbers perTECH_SPEC.md§4),Error,Result<T> = std::expected<T, Error>, andformat(Error)renderingerror[Ennnn]: ...with optional--> path:line:colandhint:lines.formatlives insrc/util/error.cppas a module implementation unit. -
Catch2 v3wired throughflake.nix(libc++-built override) and registered in CMake;tests/util_error.cppcovers six format cases viacatch_discover_tests. -
cargoxx.manifestpublic types (Package,Dependency,BuildSettings,Edition,Manifest) andparse(path)returningResult<Manifest>, backed by toml++ vendored asthird_party/toml.hpp. Unknown keys in[package]/[build]and unknown top-level keys are rejected; reserved fields (description,repository,[dev-dependencies],[features],[workspace]) are accepted.tests/manifest_parse.cppcovers 17 cases. -
manifest::write(m, path)serializes aManifestas TOML using toml++. Dependencies are emitted alphabetically (matches Cargo). Round-trip property is exercised bytests/manifest_write.cpp(9 cases). Defaultedoperator==on the manifest structs supports comparison. -
cargoxx.layoutpublic types (Target,TargetKind,DiscoveredLayout) anddiscover(project_root, package_name). Walkssrc/recursively for the library (excluding thesrc/bin/subtree), enumeratessrc/main.cpp,src/bin/*.cpp,tests/*.cpp,examples/*.cppflat, sorts results for deterministic output, and returnsLayoutNoTargetwhen neither a library nor any binary is present.tests/layout_discovery.cppcovers 12 cases. -
cargoxx new <name>andcargoxx new --lib <name>scaffold a project directory withCargoxx.toml,src/main.cpporsrc/lib.cppm, and a minimal.gitignore. Hyphens in the package name are mapped to underscores when generating the module/namespace identifier. Codegen offlake.nix/CMakeLists.txtis intentionally deferred to M3. CLI11 v2.6.2 vendored atthird_party/CLI11.hpp; entry point is nowcargoxx::cli::run(argc, argv).tests/cmd_new.cppcovers 9 cases. -
util::satisfies(version, range)— minimal semver range matcher supporting*,==/>=/<=/>/<operators, and comma-separated AND clauses. Versions are zero-padded to three components.tests/semver_satisfies.cppcovers 7 cases. -
cargoxx.linkdb:Recipe,Database::open(),Database::resolve(...)for curated lookups. The curated database ships atdata/linkdb.jsonwith all 25 packages fromSPEC.md§11. Component-substitution helpers expand{{components}}(space-joined) infind_packageand fan out{{component}}per-target. Path is injected via theCARGOXX_LINKDB_DEFAULT_PATHcompile definition. nlohmann/json 3.12.0 vendored atthird_party/json.hpp. SQLite overlay andadd_manualare deferred to the M2 follow-up commit.tests/linkdb_lookup.cppcovers 13 cases including a smoke test that resolves all 25 curated packages. -
cargoxx.lockfile:Lockfile,LockfilePackagetypes andparse(path)/write(lock, path)matching the format inSPEC.md§5. AlsoLockfile::nixpkgs_rev()returns the shared revision (codegen will consume this in M3).tests/lockfile_round_trip.cppcovers 9 cases. -
cargoxx.codegen:GenerateInputsplus the pure functionflake_nix(in) -> std::string. Substitutes the package name, the resolved nixpkgs revision (defaulting tonixos-unstablewhen the lockfile pins none), and a deduplicated list of depnixpkgs_attrentries intobuildInputs. Output is byte-deterministic.tests/codegen_flake.cppcovers 7 cases. Note: SPEC §7's template did not showbuildInputs; we add one betweennativeBuildInputsand theenv.NIX_CFLAGS_COMPILEblock as the natural slot for the deps that TECH_SPEC §10 says we splice in. -
codegen::cmake_lists(in) -> std::stringperSPEC.md§8 andTECH_SPEC.md§9:find_packageper dep, optional library target with module units + private impl sources, primary binary<pkg>_binlinked against the library, additional binaries fromsrc/bin/, tests withenable_testing()+add_test, examples, and[build]honoringwarnings_as_errorsandsanitizers. Source paths emitted relative tobuild/(i.e. prefixed with../). Output is deterministic.tests/codegen_cmake.cppcovers 11 cases. -
cargoxx.resolver::devbox_resolve(name, version)and pure parserparse_devbox_resolve(json)— port of devbox'sinternal/searcher/client.goResolve method. Hitshttps://search.devbox.sh/v1/resolve?name=<n>&version=<v>via curl and pulls outname,version,commit_hash,attr_paths. Falls back to the per-systemsystems.<plat>.commit_hashwhen the top-level field is empty (older response shapes). 404 →ResolutionUnknownPackage, missing commit_hash →ResolutionVersionNotFound, transport / parse errors →ResolutionNetworkError.tests/devbox_resolve_parse.cppcovers 6 cases against fixtures derived from a real fmt 10.2.1 response;tests/devbox_resolve_live.cpp(gated byCARGOXX_NETWORK_TESTS=1) hits the live API. -
Fix:
nix_cmake_scanwould silently fail when the dev output had not yet been realized on the local machine —nixpkgs_probe'snix evalonly computes a store path, so packages first-time encountered (libclang, anything not previously built) returned a path that didn't exist on disk. Discovery now realizes the candidate output vianix build --no-link --print-out-paths nixpkgs#<attr>.dev(or.<empty>) before scanning. New free functioncargoxx::resolver::realize_path(flake_attr)wraps the call. Live verified:cargoxx add libclangnow reaches the verify-link step (the subsequentfind_dependency(LLVM)failure inside Clang's CMake config is the pre-existing cross-package transitive-dep limit — workaround is tocargoxx add libllvmfirst). -
Fix:
nix_cmake_scanwalked only the package's default output. For multi-output Nix packages —boost,openssl,libllvm,libclang, glib, … — CMake configs live in thedevoutput (<store>/lib/cmake/llvm/LLVMConfig.cmake), not under the default output, socargoxx add libllvm(or any other multi-output dep) failed with "Conan/vcpkg/nix-scan all empty".NixpkgsInfonow carries an optionaldev_path; thenix evalapply expression reportsif p ? dev then p.dev.outPath else "".discoverscans the dev output preferentially and falls back toout. Live verified:cargoxx add libllvmnow reaches the verify-link step (the linker-time libstdc++/libc++ ABI mismatch is a separate documented limit and applies to abseil-cpp too). -
Fix: pinned-dep
buildInputsline used the curated linkdb'snixpkgs_attr(e.g.fmt_10) regardless of the pinned rev, socargoxx add fmt@12.1.0emittedpkgs_nixpkgs_fmt_12_1_0.fmt_10even though that rev only exposesfmt.resolve_versionnow returnsResolvedVersion { nixpkgs_rev, nixpkgs_attr }(the attr comes from devbox'sattr_pathsfor the resolved rev, with the package name as a best-effort fallback for the git path).cmd_addwrites the attr into the lockfile andcmd_build'smerge_lockfilenow also preserves it. Codegen prefers the lockfile's attr over the linkdb recipe's whenever it's set, so pinned deps reach the right attribute on their per-dep nixpkgs. Live verified:cargoxx add fmt@12.1.0 && cargoxx buildnow emitspkgs_nixpkgs_fmt_12_1_0.fmtand the binary builds and runs. -
flake.nixcodegen rewritten for per-dep nixpkgs revisions. The sharednixpkgsinput always tracksnixos-unstableand provides the toolchain. Each pinned manifest dep (<pkg>@<concrete-version>whose lockfile entry has a non-nullnixpkgs_rev) gets its ownnixpkgs_<sanitized>input, a matching outputs-lambda parameter, apkgs_nixpkgs_<sanitized>letbinding, and apkgs_nixpkgs_<sanitized>.<attr>line inbuildInputs. Wildcard / unpinned deps stay on the sharedpkgs.<attr>. Sanitization replaces every char outside[a-zA-Z0-9_]with_, giving a single identifier-safe form for all three Nix contexts. Two pinned deps that share the same(name, version)are deduplicated. Live verified:cargoxx new app && cargoxx add fmt@10.2.1 && cargoxx add zlib && cargoxx buildproduces a binary that calls fmt 10.2.1 + zlib from the toolchain nixpkgs; a secondcargoxx buildregenerates byte-identicalflake.nix+Cargoxx.lock. -
SPEC.md §7 (flake template) and §10 (version resolution) amended to describe the per-dep model. The previous single-shared-rev whole-project SAT was retired (user-directed; documented ABI trade-off).
-
cargoxx buildnow merges rather than overwritesCargoxx.lock: when an existing entry's(name, version)still matches the manifest, itsnixpkgs_revis preserved. New deps and version bumps still get a null rev (tracknixos-unstable). Means pins written bycargoxx add <pkg>@<ver>survive arbitrary rebuilds; cmd_build is now idempotent w.r.t. the lockfile.synthesize_lockfilewas renamedmerge_lockfile. -
cargoxx.resolver::resolve_version(name, version)orchestrator chainsdevbox_resolve(HTTP, primary) →nixpkgs_git_resolve(offline, fallback). Returns a 40-char nixpkgs SHA. Wildcards (*, empty) are rejected withResolutionVersionNotFoundsince they aren't concrete pins. -
cargoxx add <pkg>@<version>now resolves the pin to a nixpkgs rev before writing the manifest. Failure to resolve aborts the add cleanly (no manifest mutation, no lockfile mutation). On success the rev is persisted intoCargoxx.lock'snixpkgs_revfor that dep. Wildcards (cargoxx add fmt) skip resolution and tracknixos-unstableas before. Tests opt out of the network resolver viaCARGOXX_NO_AUTORESOLVE=1. -
cargoxx.resolver::nixpkgs_git_resolve(name, version, repo_path?)— fallback for when search.devbox.sh is unreachable. Lazily cloneshttps://github.com/NixOS/nixpkgs.gitinto$XDG_CACHE_HOME/cargoxx/nixpkgs/(or$HOME/.cache/...) on first use, then runsgit -C <repo> log --all -S 'version = "<v>"' --pretty='%H %ct' -- pkgs/and returns the youngest matching commit. Pure helperpick_youngest_commit(text)parses the%H %ctlines. The unit test builds a tiny throwaway git fixture that mirrors thepkgs/development/libraries/<pkg>/default.nixlayout (avoiding the real multi-GB clone) and verifies introducing-commit detection plus the not-found path; 5 cases. -
cargoxx add <pkg>now auto-resolves packages outside the curated linkdb. OnLinkdbUnknownPackage,cmd_addinvokesresolver::discoverwhich: probesnixpkgs#<pkg>to confirm the attribute exists and capture its store path; tries Conan, vcpkg, and the nix-store CMake scan in order; for each candidate runs a realcargoxx buildagainst an emptyint main() {}project viaverify_link; on first success persists a confirmed overlay row. Subsequentcargoxx add <pkg>for the same package is an overlay-cache hit (~ms). End-to-end live: a freshcargoxx add simdjson(not in our curated db) takes ~11 s including the verifying nix+cmake build, then 0.002 s on the second invocation.linkdb::default_overlay_path()is now exported as a public helper so the resolver and CLI agree on the overlay file when no path is explicitly passed. Tests opt out of the slow chain viaCARGOXX_NO_AUTORESOLVE=1. -
cargoxx.linkdb::Database::insert_provisional,confirm_provisional, andabort_provisional— three-step lifecycle for non-manualoverlay rows: insert withverified_at = 0, run a build, then either bumpverified_at = nowon success orDELETEthe row on failure. -
cargoxx.resolver::verify_link(req, build_fn)— scaffolds a tinyCargoxx.toml+int main() {}project under a scratch dir, writes a provisional overlay row pointing at the candidateRecipe, runs the caller-suppliedBuildFn(typicallycli::cmd_buildinjected to avoid a resolver-on-cli dep cycle), and on success/failure confirms or aborts the provisional row. Cleans the scratch project via RAII regardless of outcome.tests/verify_link_unit.cppexercises both success and failure paths against a mockBuildFn, verifying that the overlay row's lifecycle matches the build outcome. -
cargoxx.resolver::vcpkg_probe(name)— fetcheshttps://raw.githubusercontent.com/microsoft/vcpkg/master/ports/<name>/usageand feeds it throughparse_vcpkg_usage. The pure parser extracts the firstfind_package(...)arg block, addsREQUIREDif absent, and gatherstarget_link_librariestargets that contain::(skips generator expressions and bare names).tests/vcpkg_probe_parse.cppcovers 6 cases;tests/vcpkg_probe_live.cpp(gated byCARGOXX_NETWORK_TESTS=1) verifies fmt + a 404 path against real microsoft/vcpkg ports. -
cargoxx.resolver::conan_probe(name)— fetcheshttps://raw.githubusercontent.com/conan-io/conan-center-index/master/recipes/<name>/all/conanfile.pyviacurl(text-only — never executes Python, perSPEC.md§14) and feeds it throughparse_conanfile. Pure parser handles both the modernset_property("cmake_target_name", "...")form and the legacynames["cmake_find_package"] = "..."form, derives the missing field from the other when only one is set, normalizes bare names into the canonical<file>::<target>shape, and substitutes Python f-string{...}placeholders with the cmake_file_name so recipes like fmt'sf"fmt::{target}"parse correctly.tests/conan_probe_parse.cppcovers 6 cases;tests/conan_probe_live.cpp(gated byCARGOXX_NETWORK_TESTS=1) verifies fmt and a 404 path against the real conan-center-index. -
cargoxx.resolver::nix_cmake_scan(store_path, package_name)— walks<store_path>/lib/cmake/**for*Config.cmake/*-config.cmakefiles, scans them and their sibling.cmakefiles (e.g. the<pkg>-targets.cmakethat real packages like fmt put theiradd_library(... IMPORTED)calls in) for IMPORTED + ALIAS targets, and returns theNixCmakeCandidatewhose derived stem best matchespackage_name(case-insensitive equality > prefix > first non-empty). Pure helpersscan_imported_targets(text)andconfig_stem_to_package(filename)are exported for unit testing.tests/nix_cmake_scan_parse.cppcovers 11 cases including a fixture that mirrors fmt's<pkg>-config.cmake → <pkg>-targets.cmakelayout.tests/nix_cmake_scan_live.cpp(gated byCARGOXX_NETWORK_TESTS=1) realizesnixpkgs#fmt.devvianix buildand verifiesfmt::fmtis discovered end-to-end. -
cargoxx.resolver::nixpkgs_probe(attr)— runsnix eval nixpkgs#<attr> --json --apply 'p: { version, path }'viaexec::runand returns aNixpkgsInfo { attr, version, out_path }. Distinguishes missing attributes (ResolutionUnknownPackage) from evaluator/network errors (ResolutionNetworkError). Thepathfield name avoids nix'soutPath-driven derivation coercion in--jsonmode (which would otherwise serialize the result as a bare path string instead of a JSON object). Pure parserparse_nix_eval_json(attr, text)exposed for unit tests; live tests intests/nixpkgs_probe_live.cppare gated behindCARGOXX_NETWORK_TESTS=1(verified locally againsthello). -
cargoxx add <pkg>[@<version>] [--components a,b]editsCargoxx.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. Version is now optional; an omitted version is stored as"*"and resolves against the linkdb's first matching recipe. Generatedflake.nixcontinues to track thenixos-unstablebranch when the lockfile carries no rev. -
util::satisfiestreatsversion == "*"as a match against any range, mirroring the existingrange == "*"shortcut. -
scripts/verify-curated-db.shplus per-package using-snippets atscripts/curated-snippets/<pkg>.cpp. For each curated package the script scaffolds a fresh project,cargoxx adds the dep, drops in a snippet that exercises the library's API, and runscargoxx build, asserting the binary is produced. 21/25 packages pass againstnixos-unstable; the remaining 4 are documented as skipped:catch2/gtest(their default linkdb targets ship their ownmain()which collides with the snippet),grpc(needsprotobufdeclared as a transitive dep — cross-package transitives are out of v0.1 scope),abseil-cpp(nixpkgs builds it against libstdc++ while our flake uses libc++ → ABI mismatch on the linker step). -
data/linkdb.json:boostrecipe is now header-only —find_package(Boost REQUIRED)+Boost::headers. Boost 1.89'sBoostConfig.cmakesearches for separately-installedboost_<comp>Config.cmakefiles that nixpkgs doesn't ship, so the previousCOMPONENTSform failed at configure time. Linkdb-component tests now useabseil-cppfor component coverage. -
cargoxx remove <pkg>drops a declared dep fromCargoxx.toml, errors when the dep is not declared. Other deps are preserved. -
End-to-end verified on a freshly-scaffolded project.
tests/cmd_add.cppandtests/cmd_remove.cppcover 9 cases. Known cosmetic issue: toml++ writes top-level sections alphabetically, so[package]may end up after[dependencies]post-mutation; logged for M6 polish. -
cargoxx run [--release] [--bin <name>] [-- <args>...],cargoxx test [--release], andcargoxx clean.runbuilds first, picks the binary (errors with the available list when the project has multiple bins and--binis omitted), and execs it with the process exit code propagated.testinvokesnix develop -c ctest --test-dir build/<profile> --output-on-failure.cleanremovesbuild/while leavingCargoxx.lockandflake.lockintact. End-to-end verified against a freshly-scaffolded project.tests/cmd_run.cppandtests/cmd_clean.cppcover 6 cases. -
cargoxx build(without--no-build) now drives the full build:nix develop -c cmake -B build/<profile> -S build -G Ninja -DCMAKE_BUILD_TYPE=<Profile>thennix develop -c cmake --buildwith optional--target, both withinherit_stdio = trueso the user sees compilation output live. Non-zero cmake exit returnsBuildCmakeFailed. End-to-end verified:cargoxx new myapp && cargoxx buildproduces a workingbuild/debug/myappthat printsHello from myapp!. -
Generated
build/CMakeLists.txtnow opts into the experimentalimport std;UUID +CMAKE_CXX_MODULE_STD ON, and setsCMAKE_CXX_EXTENSIONS ON(deviation from SPEC §8 — required for libc++'s std module to load withoutmodule-file-config-mismatchon clang 21). Without these the templates fromcargoxx newfail to compile. -
cargoxx.exec:ExecResult,ExecOptions, andrun(program, args, opts)— argv-only subprocess wrapper around reproc 14.2.4. Captures stdout/stderr (or inherits stdio whenopts.inherit_stdiois set), supportscwd,env_overrides, and atimeout(enforced viareproc_options.deadlineso drains returnREPROC_ETIMEDOUTinstead of blocking on long-lived streams). On destruction reproc terminates and then kills any still-running child. ReturnsExecToolNotFoundforENOENTandExecCommandFailedfor other failures including timeouts.tests/exec_run.cppcovers 9 cases. -
cargoxx build --no-buildend-to-end. ReadsCargoxx.toml, discovers the layout, opens the curated linkdb, resolves aRecipeper manifest dep, synthesizes a freshCargoxx.lock, and writesflake.nix,build/CMakeLists.txt, andCargoxx.lock. With no resolver yet (M5),nixpkgs_revis left null and the generated flake falls back to thenixos-unstablebranch. Without--no-build, the command still generates files but returns aNotImplementederror pointing at M4.tests/cmd_build.cppcovers 8 cases. -
SQLite overlay:
Database::open(overlay_path)now opens (and creates, if missing) a per-userlinkdb.sqlitecache, applying the schema fromSPEC.md§9 idempotently. Default path is$XDG_CACHE_HOME/cargoxx/linkdb.sqlite(falling back to$HOME/.cache/cargoxx/...); tests pass an explicit temp path so they never touch the user cache.Database::add_manual(pkg, range, recipe)inserts a row withsource = 'manual'andverified_at = now();resolve()consults the overlay first and falls back to curated when no overlay row matches the requested version range. Manual entries never expire;nix-probe(v0.2) entries respect a 30-day freshness window.tests/linkdb_overlay.cppcovers 7 cases (insert/persist, override-curated, version-range gating, components rejection, move semantics). -
M7 generated flake.nix moves to
build/flake.nix. The project root belongs to the user — any hand-writtenflake.nixthere is never overwritten by cargoxx.cargoxx buildalways invokesnix developagainstpath:./build. -
M7 lockfile pins top-level
nixpkgs_revandflake_utils_rev. The generated flake'sinputs.nixpkgs.url/inputs.flake-utils.urlnow use the pinned revs (falling back to the branch tips during the transitional first build before the lock is written). Per-package schema gains the full recipe (find_package,targets,pkg_config_module,brute_force_libs,brute_force_includes,linkdb_source) so the lockfile is a complete dependency-pinning artifact andcmd_build'srecipe_from_lockcan short-circuit the linkdb entirely.tests/lockfile_round_trip.cppextended. -
M7
codegen::VendorIndex+parse_vendor_toml— new pure parser (src/codegen/vendor.cpp) returns a struct ofnixpkgs_store_path,flake_utils_store_path, and a per-depnixpkgs_attr → store_pathmap.GenerateInputsgains an optionalvendorfield; when set,emit_inputs_blockemitspath:inputs and drops the per-depgithub:pins. -
M7 new helpers in
cargoxx.resolver:realize_path_at_rev(rev, attr)realizesgithub:NixOS/nixpkgs/<rev>#<attr>to a/nix/store/...path (used bycmd_vendor);realize_flake_source(flake_ref)returns the source store path vianix flake prefetch --json(used to pinnixpkgsandflake-utilsfor offline mode). -
M7
cargoxx vendor [--output <path>]— new CLI verb. ReadsCargoxx.lock, realizes each locked dep at its pinned(nixpkgs_rev, nixpkgs_attr)into/nix/store, and writesvendor.toml(schema = 1) recording the resolved store paths for every dep plus thenixpkgsandflake-utilsflake sources. The output is the input tocargoxx build --offline. -
M7
cargoxx build --offline [--vendor <path>]— skips every network probe (Conan/vcpkg fuzzy, devbox, nixpkgs_git, linkdb auto-resolve), readsvendor.toml(default./vendor.toml), and emitsbuild/flake.nixwith literalpath:/nix/store/...inputs fornixpkgs,flake-utils, and every dep. Offline mode also runs cmake directly (no outernix developwrapper) since all paths are already realized in the local store. -
M7
cargoxx.lib.buildCppPackage— hermetic, sandbox-safe nix builder for downstream flakes. MirrorsrustPlatform.buildRustPackage's ergonomics: a consumer flake passessrcand gets a derivation. ReadsCargoxx.lockat outer eval time, resolves each dep's(nixpkgs_rev, nixpkgs_attr)viabuiltins.getFlakeinto concrete/nix/store/...paths, and synthesizes avendor.tomlviapkgs.writeText— no network or nestednixinvocations inside any build phase. The single derivation runscargoxx build --release --offline --vendor <store-path>/vendor.toml, which emits a hermeticbuild/flake.nixwith literalpath:/nix/store/...inputs and drives cmake directly. Works under the host's default sandbox (sandbox=true, non-trusted user, no__noChroot, no daemon escape). New e2e fixture attests/e2e/buildCppPackage/with arun.shsmoke test that scaffolds the fixture in a tmp dir and runsnix build .#defaultend-to-end. Live verified:Hello from world!from a binary built entirely inside the standard nix sandbox. -
Fix:
-Wparentheseswarning inlooks_like_missing_attribute(src/resolver/nixpkgs_probe.cpp:34) — explicitly parenthesize the&&clause inside||. -
M7
cargoxx-binwraps the binary withmakeWrapperto lock its runtime CLI dependencies —nix,cmake,ninja,curl,git— ontoPATH. Previously cargoxx silently relied on the user's ambient PATH, so it broke whenever invoked outside anix developshell. The wrapped binary works underenv -iwith a minimal PATH. -
M7 the wrapper also
--set-defaultsNIX_CONFIGtoexperimental-features = nix-command flakesandbuild-users-group =. Without this, the bundledpkgs.nixdefaults to the multi-user daemon model and fails on non-NixOS hosts (error: the group 'nixbld' specified in 'build-users-group' does not exist).--set-defaultlets a user with a properly- configured nix daemon override by exportingNIX_CONFIG=...before invoking cargoxx. Verified end-to-end inarchlinux:latestviadocker run: install the.pkg.tar.zst,cargoxx new demo && cargoxx add fmtruns the resolver chain through verify-link cmake/ninja and writes the dep without error. -
M7 packaging functions exposed as a stable
to*library and as ready-to-build flake packages, mirroring thegithub:NixOS/bundlersnaming. Available as bothpackages.<format>(fornix build .#<format>) andlib.to<Format>(for downstream flakes to package their own derivations):toAppImage/packages.appimage— Linux AppImage (~207 MB)toDockerImage/packages.dockerImage—docker load-able tar.gz (~213 MB)toDEB/packages.deb— Debian.deb(~211 MB)toRPM/packages.rpm— Red Hat.rpm(~212 MB)toArchPkg/packages.archpkg— Arch.pkg.tar.zst(~196 MB), implemented locally because no NixOS bundler exists. Builds a closure viapkgs.closureInfo, lays it under/nix/store, drops a/usr/bin/<mainProgram>symlink, writes.PKGINFOfromdrv.pname/drv.version/drv.meta, and packs withbsdtar --zstd. Generic — works for any derivation with abin/<mainProgram>and the usualpname/versionattrs. Addedinputs.bundlers.url = "github:NixOS/bundlers"(withinputs.nixpkgs.follows = "nixpkgs") to keep the closure aligned with the project's pinned nixpkgs.
-
M8 multiple binary build artifacts (Cargo
src/bin/parity). Codegen now routes every executable (add_executable) to${CMAKE_BINARY_DIR}/binviaset_target_properties(... RUNTIME_OUTPUT_DIRECTORY ...).buildCppPackageandcargoxx run --bin <name>follow suit, and layout discovery picks up Cargo'ssrc/bin/<sub>/main.cppform (subdir name becomes the binary name). End-to-end fixturetests/e2e/buildCppPackage/src/bin/extra.cppproves a second binary lands in$out/bin/alongside the primary. -
M8 reusable library install layout.
cargoxx-built libraries now ship a CMake-idiomatic$outtree:lib/lib<name>.a,lib/cmake/<name>/{<name>Config.cmake,<name>ConfigVersion.cmake, <name>Targets.cmake},lib/pkgconfig/<name>.pc, and modules underinclude/<name>/(viainstall(TARGETS ... FILE_SET CXX_MODULES ...)). Codegen emits the install rules + aConfig.cmake.intemplate (inlinefile(WRITE …)) consumed byconfigure_package_config_fileandwrite_basic_package_version_file, plus a basic.pc.intemplate. The exported library target carriestarget_compile_features(... PUBLIC cxx_std_<NN>)so consumersfind_package-ing it get the right standard for module BMI regeneration.buildCppPackage.installPhaseswitched fromcp -a build/release/bin/.tocmake --install build/release --prefix $out— bins, libs, headers, config, and pc files all land via one invocation.project(... VERSION <pkg.version> ...)is now part of the generated header so<name>ConfigVersion.cmakereflects the manifest's version. -
M8 cargoxx-path dependencies (Cargo's
{ path = "..." }). The manifest gains a discriminated dep table form:[dependencies] greeter = { path = "./greeter" }manifest::DependencycarriesDepSource source+optional<string> path. The parser branches onpath; unknown dep-table keys still rejected for tables that have neitherversionnorpath. Round-tripped by the writer. Lockfile schema adds per-packagesource_kind+source_pathso the consumer'sCargoxx.lockrecords "this dep is built from a cargoxx source tree at./greeter" (not nixpkgs).cmd_build::resolve_path_depreads<path>/Cargoxx.tomlto verify the dep's name matches, then synthesizes a Recipe (find_package = "<name> CONFIG REQUIRED",targets = ["<name>::<name>"],source = "cargoxx-path"). Codegen needs no special case — the synthesized recipe flows through the existingfind_packageemission path.buildCppPackage's recursion:evalDepbranches onsource_kind == "cargoxx-path"and recurses withsrc = src + "/" + source_path. The resulting derivation joinsbuildInputs; CMake'sCMAKE_PREFIX_PATHpicks it up so the consumer'sfind_package(<dep> CONFIG REQUIRED)resolves against the producer's installed Config.cmake. Path deps must be subdirectories of the consumer's source tree (no sibling form in v1 — sibling deps will land with git/registry sources in 1c/1d). New e2e fixturetests/e2e/pathDep/: aconsumerproject with[dependencies] greeter = { path = "./greeter" }.run.shgenerates locks in both, thennix build .#defaultproduces a binary that prints "Hello from greeter, world!". -
M8 design doc at
docs/library-reuse-and-publish.mdcovers the full two-part roadmap: library reuse (path → git → registry deps) and Gitea-hosted public registry withcargoxx publish+ bot 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:[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::Dependencycarriesgit_url+git_rev; parser branches ongit, rejects git deps that omitrev. Lockfile schema addssource_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.fetchgitin the consumer'sbuildCppPackagesubstitutes from cache when the same(url, rev, sha256)triple appears elsewhere.cmd_build::resolve_git_depreuses the lockfile's prior sha256 on re-builds; on a fresh dep it callsresolver::prefetch_flake_source(git+<url>?rev=<rev>)which now returns{store_path, hash}(extended via a newPrefetchedSourcestruct, withrealize_flake_sourcekept as a thin compat wrapper). Verifies the dep's name by reading the fetched tree'sCargoxx.toml. In--offlinemode, a git dep without a cached hash errors with a clear "run online first" message.flake.nix'sevalDepbranches onsource_kind == "cargoxx-git"and feedspkgs.fetchgit { url, rev, hash }into a recursivebuildCppPackagecall. 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 differentlinkdb_sourcediscriminator. -
M8
cargoxx publish(Phase 2c). Publishes the project's HEAD as a new version recipe in a Gitea-hosted cargoxx registry. Steps:- Validate
Cargoxx.tomlhasname,version,license. - Verify the working tree is clean and
Cargoxx.lockexists. - Reject path-dep deps (registry can't reference local trees).
- Read
git remote get-url origin+git rev-parse HEAD, normalize SSH URLs to https. - Compute the source SRI hash via
nix flake prefetch git+<url>?rev=<commit> --json. - Build the recipe TOML (mirrors
Cargoxx.toml's deps +[meta]). - POST
/repos/<registry>/contentswith{branch: "master", new_branch: "publish/<name>-<version>", files: [...]}— a single atomic commit creating bothrecipes/<name>/versions/ <v>.tomland (for new packages)recipes/<name>/maintainers.txtpre-populated with the publisher's Gitea username (tea api /user). - POST
/repos/<registry>/pullsto open the PR.
--dry-runprints the recipe TOML and exits without any network ops.--registry <owner>/<repo>overrides the default ($CARGOXX_REGISTRY, falling back tomozart/cargoxx-pkgs). Authentication comes fromtea login— no separate token plumbing.Manifest schema gains three optional
[package]fields used in the recipe's[meta]block:description,repository,homepage.The cargoxx wrapper (
flake.nix'scargoxxRuntimePath) now bundlespkgs.teaso the binary can shell out to it on non-Nix hosts.Verified end-to-end against
https://git.amadey.xyz/mozart/cargoxx-pkgs: from amozart/greetercheckout,cargoxx publishopens PR #3 with branchpublish/greeter-0.1.0containing a freshrecipes/greeter/versions/0.1.0.tomland a newrecipes/greeter/maintainers.txt. - Validate