Skip to content

native modules: build rust workspace crates through the repo flake - #3770

Open
jeff-hykin wants to merge 22 commits into
mainfrom
jeff/fix/native_build_cargo_path
Open

native modules: build rust workspace crates through the repo flake#3770
jeff-hykin wants to merge 22 commits into
mainfrom
jeff/fix/native_build_cargo_path

Conversation

@jeff-hykin

@jeff-hykin jeff-hykin commented Aug 30, 2026

Copy link
Copy Markdown
Member

Use nix build instead of raw cargo (fails on Jetson)

…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.
Comment thread dimos/core/native_module.py Outdated
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.
@jeff-hykin jeff-hykin changed the title native_module: find cargo in ~/.cargo/bin during auto-build native modules: build rust workspace crates through the repo flake Aug 30, 2026
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.
Comment thread dimos/mapping/ray_tracing/module.py Outdated
Comment thread dimos/navigation/nav_3d/mls_planner/mls_planner_native.py Outdated
@jeff-hykin
jeff-hykin marked this pull request as ready for review August 31, 2026 06:06
@codecov

codecov Bot commented Aug 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

@@           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     
Flag Coverage Δ
OS-ubuntu-24.04-arm 74.25% <100.00%> (-0.01%) ⬇️
OS-ubuntu-latest 74.80% <100.00%> (+<0.01%) ⬆️
Py-3.10 74.79% <100.00%> (-0.01%) ⬇️
Py-3.11 74.79% <100.00%> (-0.01%) ⬇️
Py-3.12 74.79% <100.00%> (-0.01%) ⬇️
Py-3.13 74.79% <100.00%> (-0.01%) ⬇️
Py-3.14 74.79% <100.00%> (+<0.01%) ⬆️
Py-3.14t 74.79% <100.00%> (+<0.01%) ⬆️
SelfHosted-Linux 34.75% <100.00%> (-0.01%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
dimos/mapping/ray_tracing/module.py 100.00% <100.00%> (ø)
...avigation/nav_3d/mls_planner/mls_planner_native.py 100.00% <100.00%> (ø)

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@greptile-apps

greptile-apps Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This 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 flake.nix is present. Equivalent release Cargo builds completed successfully and created target/release/voxel_ray_tracing and target/release/mls_planner.

T-Rex validation blocked

The exact Nix-wrapped commands could not run because the nix tool is missing from this environment. Both commands stopped with nix: not found (exit 127), so this environment could not directly confirm that nix develop supplies Cargo and rustc.

Confidence Score: 5/5

No 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.

T-Rex T-Rex Logs

What T-Rex did

  • I confirmed that both dimos/mapping/ray_tracing/rust and dimos/navigation/nav_3d/mls_planner/rust resolve to the repository root and that a flake.nix exists there.
  • I attempted to run the Nix-based build commands from both Rust working directories, but Nix was not found in this environment.
  • I ran equivalent cargo builds for both crates; dimos-voxel-ray-tracing completed in 29.92 seconds and dimos-mls-planner completed in 44.66 seconds, producing the expected executables under target/release.
  • I re-ran the commands with PATH set to /usr/bin:/bin; both Nix commands still failed and both cargo commands failed because cargo was not available, confirming the wrapper boundary for tool provisioning.

View all artifacts

T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "native modules: drop the build_command c..." | Re-trigger Greptile

@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Aug 31, 2026
Comment thread dimos/mapping/ray_tracing/module.py Outdated
# 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"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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.
@github-actions github-actions Bot added ready-to-merge Required CI checks have passed on this PR and removed ready-to-merge Required CI checks have passed on this PR labels Aug 31, 2026
`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.
@github-actions github-actions Bot removed the ready-to-merge Required CI checks have passed on this PR label Aug 31, 2026
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.
@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Sep 1, 2026
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.
@github-actions github-actions Bot removed the ready-to-merge Required CI checks have passed on this PR label Sep 1, 2026
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.
@github-actions github-actions Bot added ready-to-merge Required CI checks have passed on this PR and removed ready-to-merge Required CI checks have passed on this PR labels Sep 1, 2026
@github-actions github-actions Bot added the ready-to-merge Required CI checks have passed on this PR label Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-to-merge Required CI checks have passed on this PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants