bazelbuild / bazelbuild/rules_rust

cargo_build_script: `DEP_<links>_<KEY>` values pointing into OUT_DIR reach dependent build scripts with an unresolved `${out_dir}` token

Open
#4,103 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Starlark
Stars
843
Forks
651
Avg merge
2d 18h
Merged PRs (30d)
15

Description

### Summary

A `links` (`-sys`) crate whose build script exposes an `OUT_DIR`-relative path through the metadata convention (e.g. `cargo:include=$OUT_DIR/include`, or `cargo::metadata=include=$OUT_DIR/include`) propagates that value to a **dependent** crate's build script via `DEP__INCLUDE` with the producing crate's `OUT_DIR` rewritten to the literal, unresolved `${out_dir}` token.

The dependent build script therefore sees something like:

```
DEP_LZ4_INCLUDE=/${out_dir}/include
```

The `${out_dir}` substring is never resolved, the directory does not exist, and C/C++ includes that consume `DEP__INCLUDE` fail. Concretely, `librocksdb-sys` cannot find `lz4.h` when built against `lz4-sys`.

### Affected versions

This is a regression from the path-mapping work, not present in older releases:

- Introduced by #4011 ("Add support for `experimental_output_paths`", merged 2026-05-07), which added the `${out_dir}` tokenization of build-script outputs; refined by #4050.
- The `DEP__` metadata propagation that carries the include path comes from #3877.
- The latest tagged release at time of writing is `0.70.0` (2026-04-22), which **predates** #4011 and is unaffected. The bug currently exists on `main` only (not yet in a release).

### Root cause

In `cargo/private/cargo_build_script_runner/lib.rs`, `outputs_to_dep_env` redacts dep-env values with `redact_paths`, which rewrites the producing crate's `out_dir` to the generic `${out_dir}` token:

```rust
Self::escape_for_serializing(Self::redact_paths(env, exec_root, out_dir))
```

That generic token is only resolved by `process_wrapper`'s `--out-dir` flag, for the rustc action of the crate that **directly owns** the build script. But a dep-env (`.depenv`) file is consumed by a **dependent** crate's build-script runner (`bin.rs`), which only substitutes `${pwd}`:

```rust
command.env(key, value.replace("${pwd}", &exec_root.to_string_lossy()));
```

It has no notion of the *producing* crate's `out_dir` and no `${out_dir}` binding, so the token survives unresolved.

This is the same transitive-consumption scenario that `redact_flags` was deliberately written to handle (per-build-script unique tokens plus matching `--subst` entries added on the Starlark side). The dep-env path was left on the generic-token codepath, which has no resolution mechanism on the consuming side.

### Reproduction

`test/cargo_build_script/metadata_dep_env` already exercises `DEP__` propagation. Extending the producer to expose an `OUT_DIR`-relative include dir and asserting the dependent receives a resolved, existing path reproduces it (red before the fix, green after). A real-world trigger is `librocksdb-sys` with the bundled `lz4` feature.

### Proposed fix

In `outputs_to_dep_env`, redact only the exec root (`${pwd}`) and keep the producing crate's real `out_dir`-relative path. This is correct and safe because:

- The consumer is the dependent's build-script runner (`bin.rs`), which already resolves `${pwd}`.
- The producing crate's `out_dir` tree is already a declared input of the dependent build-script action at that same exec-root-relative path.
- Build-script actions do not advertise `supports-path-mapping`, so `--experimental_output_paths=strip` never rewrites that path out from under the literal value.
- The producing crate's own `.env` file (`outputs_to_env`) still uses the `${out_dir}` token, so path-mapping cache-shareability for the owning target is unchanged.

PR with the one-line fix plus unit and end-to-end regression tests to follow.

Reported downstream as hermeticbuild/rules_rs#163.

Contributor guide

Open the contributing guide

Research direction

Start in cargo/private/cargo_build_script_runner/lib.rs at outputs_to_dep_env and inspect the existing test/cargo_build_script/metadata_dep_env coverage. Extend the producer to expose an OUT_DIR-relative include directory, then verify the dependent build script receives a resolved, existing path and that the regression test passes.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
build-system
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
75/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.