anthropics / anthropics/cargo-nix-plugin
CARGO_MANIFEST_DIR is set to the per-build directory, making crates that use env!("CARGO_MANIFEST_DIR") non-reproducible
- Lingua principale
- Rust
- Stelle
- 56
- Fork
- 17
- Merge medio
- 12h 50m
- PR unite (30g)
- 2
Descrizione
## Summary
`build-rust-crate` sets `CARGO_MANIFEST_DIR` to the build's current working directory:
- `builder/src/build_rust_crate/configure.rs:264` — `let cwd = std::env::current_dir()`
- `builder/src/build_rust_crate/configure.rs:300` — `("CARGO_MANIFEST_DIR".into(), cwd)`
Unless the Nix sandbox is enabled with a fixed `sandbox-build-dir`, that directory is unique per build (e.g. `/nix/var/nix/builds/nix--`). Any crate that expands `env!("CARGO_MANIFEST_DIR")` therefore embeds a different string on every build, which changes the crate's **SVH**. Two builds of the *same* derivation produce rlibs that consumers cannot use interchangeably.
Because store paths are input-addressed, Nix cannot detect this: the path, the `-C metadata` hash, the artifact filename and `crate-metadata.json` are all identical. Only the compiled bytes differ.
## Impact
A binary cache accumulates mutually inconsistent artifacts. Once a consumer is cached against one build of such a crate and the cache serves a different build of it, every downstream compile fails with:
```
error[E0463]: can't find crate for `datafusion`
--> src/query/exec.rs:10:5
10 | use datafusion::error::{DataFusionError, Result as DfResult};
| ^^^^^^^^^^ can't find crate
```
The error names the *consumer* (`datafusion`), never the crate that actually diverged (`datafusion-common`), and emits no note. This is very hard to diagnose: the rlib is present, correctly named, and individually valid, so it looks like a missing dependency rather than a fingerprint mismatch. `RUSTC_LOG=rustc_metadata=debug` reveals the real reason:
```
resolving dep `datafusion`->`datafusion_common` hash: `11777f18…` extra filename: `-b0520d2bcd`
Rejecting via hash: expected 11777f18… got 1c395f19…
```
It also silently poisons caches: the artifacts are signed and structurally complete, so nothing flags them as bad, and the failure only appears in a *later* build that mixes generations.
## Reproduction
`datafusion-common` 54.1.0 is a real-world example — `src/test_util.rs:306` does `let dir = env!("CARGO_MANIFEST_DIR");` to locate test data.
```
$ nix build /nix/store/-rust_datafusion-common-54.1.0.drv^lib --rebuild
error: derivation '/nix/store/-rust_datafusion-common-54.1.0.drv' may not be
deterministic: output "/nix/store/-rust_datafusion-common-54.1.0-lib" differs
```
Diffing the two outputs, `lib.rmeta` differs in **exactly 16 bytes** (the SVH), and the object files contain the build directory:
```
build A: /nix/var/nix/builds/nix-86834-218531466/datafusion-common-54.1.0
build B: /nix/var/nix/builds/nix-8433-2030001221/datafusion-common-54.1.0
```
Note that the plugin already passes `--remap-path-prefix==/` to rustc, so the intent to normalize build paths is there — but `--remap-path-prefix` only rewrites spans and debug info, not the string value expanded by `env!`.
## Suggested fix
Set `CARGO_MANIFEST_DIR` to a stable, build-independent path rather than the raw cwd — for example the same normalized value already used for `--remap-path-prefix`, or a constant such as `/build/-`. Anything that does not vary between builds of one derivation would do.
Worth checking the other absolute paths derived from `current_dir()` in the same file for the same issue (`configure.rs:115` `abs_out_dir`, `:478`, `:481`), since `OUT_DIR` is commonly embedded via `include!(concat!(env!("OUT_DIR"), …))`.
## Workaround
Either enable the Nix sandbox so builds get a constant `sandbox-build-dir` (not an option for us — our CI runners are containerized and the sandbox is disabled there), or patch the offending crate per-project:
```nix
datafusion-common = _: {
postPatch = ''
substituteInPlace src/test_util.rs \
--replace-fail 'env!("CARGO_MANIFEST_DIR")' '"/datafusion-common"'
'';
};
```
This makes the crate reproducible (`--rebuild` passes), but it is per-crate whack-a-mole — every crate using this common pattern needs its own patch.
Guida per i contributori
Apri la guida per i contributori
Valutazione
Questa issue non è ancora stata valutata.