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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions