native modules: build rust workspace crates through the repo flake - #3770
native modules: build rust workspace crates through the repo flake#3770jeff-hykin wants to merge 22 commits into
Conversation
…missing Non-interactive shells (ssh commands, systemd units) don't source the rustup PATH entry, so `cargo build --release` build_commands died with exit 127 on robots even though cargo was installed.
…argo is missing" This reverts commit 5241122.
rustup's PATH entry is absent in non-interactive shells (ssh commands, systemd units), so the plain `cargo build --release` build_commands died with exit 127 on robots. Run them through `nix develop` instead, like the realsense module already does, and put cargo/rustc in the flake so the shell is self-contained.
main's native-module publish gate AST-scans each module's config and refuses an f-string default, so the DIMOS_PROJECT_ROOT interpolation has to go. A cwd-relative nix path keeps the symlink behavior the resolve() was there for.
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #3770 +/- ##
=======================================
Coverage 77.59% 77.60%
=======================================
Files 1317 1317
Lines 124794 124792 -2
Branches 10868 10868
=======================================
+ Hits 96840 96841 +1
+ Misses 24818 24816 -2
+ Partials 3136 3135 -1
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 2 files with indirect coverage changes 🚀 New features to boost your workflow:
|
Greptile SummaryThis change routes automatic release builds for the ray-tracing and MLS planner native modules through the repository Nix development shell, and adds Cargo and rustc to that shell. Both configured relative paths resolve from their Rust working directories to the repository root, where T-Rex validation blockedThe exact Nix-wrapped commands could not run because the Confidence Score: 5/5No defects were found; the configured build locations and expected release outputs behaved as intended. The relative flake paths resolved to the repository root, and release builds for both affected native packages succeeded and produced their expected executables. No final review findings remain. Files Needing Attention: No files need changes. The Nix-wrapped commands should be exercised in a Nix-enabled development environment when available.
What T-Rex did
Reviews (1): Last reviewed commit: "native modules: drop the build_command c..." | Re-trigger Greptile |
| # The crate is a workspace member, so cargo builds into the repo-root target dir. | ||
| executable: str = str(DIMOS_PROJECT_ROOT / "target" / "release" / "voxel_ray_tracing") | ||
| build_command: str | None = "cargo build --release" | ||
| build_command: str | None = "nix develop path:../../../.. -c cargo build --release" |
There was a problem hiding this comment.
| build_command: str | None = "nix develop path:../../../.. -c cargo build --release" | |
| build_command: str | None = "nix develop ../../../.. -c cargo build --release" |
So if we use path that will cause the entire workspace to get snapshotted on every build, can you double check this isn't going to spam the nix store?
There was a problem hiding this comment.
Yeah... I'm afraid this is looking like a "pick your poison" kinda decision. I've got a couple ideas but I'm gonna have to do testing.
| # The crate is a workspace member, so cargo builds into the repo-root target dir. | ||
| executable: str = str(DIMOS_PROJECT_ROOT / "target" / "release" / "mls_planner") | ||
| build_command: str | None = "cargo build --release" | ||
| build_command: str | None = "nix develop path:../../../../.. -c cargo build --release" |
There was a problem hiding this comment.
| build_command: str | None = "nix develop path:../../../../.. -c cargo build --release" | |
| build_command: str | None = "nix develop ../../../../.. -c cargo build --release" |
A flake ref copies its whole source tree into the nix store, and a fresh copy each time that tree changes. Aimed at the repo root that is the build dirs, .venv and .git, or the tracked LFS blobs -- tens of gigabytes per build. nix/rust/ is two files, so the snapshot is stable and free. Also run cargo fmt and cargo clippy inside that shell, and stop the clippy hook from re-entering the repo-root flake: the root has its own flake.nix, so the per-crate branch was matching it and shelling into the whole tree.
`nix develop ... -c cargo build` left the result in a per-machine target dir and needed a working cargo cache. A `nix build` of the whole cargo workspace is one hermetic derivation that Cachix can substitute, and its src is assembled from path literals so a doc or python edit neither rebuilds it nor invalidates the publish marker. Also give the clippy/fmt shell librealsense on Linux, so the realsense crate can join the workspace without breaking macOS.
CI builds the PR merge commit, so the dependabot cargo bump on main (de2b0fc) changed the lock the vendor derivation sees. Take the value that commit's lock actually vendors to.
The root flake hardcoded every crate directory in the workspace, so adding a module meant editing a file far from it. Each module now owns a deps.nix naming the trees it contributes, the cargo packages that are module executables, and any system libraries it links; the flake only composes them. The inputs-hash scanner follows imported .nix files for the same reason it follows path: inputs -- otherwise a crate reachable only through a deps.nix would not key the Cachix publish marker.
Adding a rust native module no longer means editing the repo-root flake, which was the last place the module list was duplicated. The search rule is deliberately the dumbest one available -- every file named deps.nix, anywhere in the tree -- because the Cachix publish gate in bin/build-native-modules has to arrive at the same set without evaluating nix, and it does that with a glob. Anything cleverer would be two implementations that drift. Searching makes the flake name ./. , which the publish gate would otherwise hash as the whole repository, rekeying the marker on every commit. readDir reads directory names rather than file contents, so the deps.nix files and the crates they name are the only content that actually reaches the derivation, and both are already collected. The exemption is therefore limited to that one expression: any other ./. in the root flake is now a hard error.
cargoHash is one fixed-output hash over every vendored crate, so any Cargo.lock change invalidates it -- including a dependabot bump landing on main, which CI surfaces through the merge commit and so fails pull requests that never touched rust. Recovering means a human reading a hash out of a vendor-staging log and pasting it back. cargoLock uses the per-crate checksums already in the lock, so there is no aggregate hash to maintain. Git dependencies still need an entry because the lock records no checksum for them, but that hash is keyed to a pinned rev rather than to the lock as a whole. An earlier revision of this branch claimed nixpkgs could not fetch crates this way because crates.io rejects the curl user agent. That was measured with a manual curl against a different URL and never tested against the actual build, which fetches without trouble.
…gate hash" This reverts commit d06824e.
The 403 does not reproduce locally -- the crate tarballs are fixed-output, so they substitute from cache.nixos.org and the fetch never runs. Only CI, on a cache miss, actually hits crates.io. Note that, and the reason the CDN cannot be substituted in, so the next attempt does not have to rediscover it from a red build.
#3741 changed Cargo.lock on main. CI builds the merge commit, so the vendor hash went stale on a branch that never touched rust -- the failure mode the comment above the hash describes.
Cargo.nix gives every crate its own derivation, so editing one module does not rebuild the others and each crate caches on its own. Measured: a cold ray_tracing build is 11m43s, and mls_planner right after is 21.8s. It also removes the aggregate cargoHash, which had to be re-pasted by hand whenever Cargo.lock moved -- including from main, which breaks branches that never touched rust, because CI builds the merge commit. The replacement failure mode is a stale generated file, which the new cargo-nix-current job prints as a diff instead of a number to re-paste. Regenerate with bin/regen-cargo-nix. The generated pair is excluded from the byte-rewriting pre-commit hooks so it stays reproducible, and Cargo.nix is exempted from the large-files check -- LFS would be wrong for a file CI has to diff as text.
Adding a module is now a deps.nix and nothing else, so the examples can be real build targets instead of a workspace member nothing referenced. They build in 42s here because every crate under them is already a store path. Doubles as the regression test for that claim: if adding a module ever starts requiring a flake.nix edit again, this file is the one that stops building.
Its keys are scoped to a git dependency's branch and carry no rev, so a committed copy silently goes stale the second that branch moves, pinning a hash that no longer matches the revision Cargo.lock resolves to. That is the exact failure this whole change set exists to remove. It is also redundant: it is only crate2nix's prefetch cache, and the hashes are already inlined in Cargo.nix. Regenerating with the file absent produces a byte-identical Cargo.nix, so nothing in the repo depends on it. Dropping it also retires two problems rather than papering over them: it no longer needs a pre-commit exclusion (pretty-format-json was rewriting it on every commit, which had already introduced a trailing newline the generator never emits), and it no longer needs an editorconfig exception for that missing final newline.
crate2nix shells out to `cargo metadata`, so the generated file depends on whichever cargo is on PATH. The generator itself was pinned by flake.lock but cargo was not: locally that was a rustup 1.98.0, while the toolchain this repo pins for its clippy and fmt hooks is 1.94.0, and CI would have used whatever the runner image ships. The two happen to agree today -- checked by regenerating under 1.94.0 and getting a byte-identical Cargo.nix -- so this fixes a latent problem rather than a live one. Worth doing anyway: the failure it prevents is a diff that only appears on the machine with the odd cargo, which is the hardest kind to read when cargo-nix-current is what reports it.
I had commented it as crate2nix's per-crate override hook. It is not: the `crateConfig` argument Cargo.nix defaults to this path is declared and then never read anywhere in the generated file. Overrides added here would have silently done nothing, which is a worse trap than having no file at all. The file still has to exist -- `bin/build-native-modules --inputs-hash` walks nix path literals textually, cannot evaluate the `builtins.pathExists` guard around this one, and exits 1 with "reference './crate-config.nix' does not exist". Verified by moving the file away. Kept an empty file rather than teaching that parser about conditional paths, since a parser that learns to skip guarded references can also skip one that matters.
Use nix build instead of raw cargo (fails on Jetson)