Skip to content

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

Description

@esnunes

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_<LINKS>_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=<execroot>/${out_dir}/include

The ${out_dir} substring is never resolved, the directory does not exist, and C/C++ includes that consume DEP_<LINKS>_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:

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:

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}:

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_<links>_<KEY> 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions