diff mbox series

[2/3] rust: keep host-kind dependency metadata out of target -C metadata

Message ID 20261105113205.1571267-3-sunilkumar.dora@windriver.com
State New
Headers show
Series rust/cargo-c: keep the build host out of cargo -C metadata | expand

Commit Message

Dora, Sunil Kumar Nov. 5, 2026, 11:32 a.m. UTC
From: Sunil Dora <sunilkumar.dora@windriver.com>

On the autobuilder the rust-rustdoc and librust-lib-example packages
were not reproducible between the x86_64 and the aarch64 workers.
Every symbol of librustdoc had a different hash on the aarch64 host,
nothing else in the package was different.

The reason is in how cargo computes -C metadata for a crate. It mixes
in the -C metadata of all the dependencies of the crate, including the
build script and the proc-macros. Those are built for the build host,
so their dependency tree can differ from one host to another.

For rustdoc the build script depends on sha2 -> cpufeatures, and
cpufeatures pulls in libc only on aarch64-linux. That one extra crate
changes the hash of the build script, from there the hash of librustdoc
itself, and with it every symbol in it.

Carry the fix sent upstream (rust-lang/cargo#17522)[1]: for a target unit,
host-kind dependencies contribute only their package id to -C metadata
instead of their full hash. They are not linked into the target
artifact anyway, so nothing is lost. unit_id, -C extra-filename and
fingerprints are not touched, so file names and rebuild decisions stay
as they are. Host units and builds without --target are not affected.

The upstream discussion is in rust-lang/cargo#8140[2]. The PR is still a
draft, the cargo team would rather solve this as part of a bigger
rework of fingerprinting (rust-lang/cargo#17439)[3], so we may have to
carry this for some time. Drop it on the rust upgrade that has the fix.

[YOCTO #16376]

[1] https://github.com/rust-lang/cargo/pull/17522/
[2] https://github.com/rust-lang/cargo/issues/8140
[3] https://github.com/rust-lang/cargo/issues/17439

Signed-off-by: Sunil Dora <sunilkumar.dora@windriver.com>
---
 ...ps-out-of-the-target-C-metadata-hash.patch | 123 ++++++++++++++++++
 meta/recipes-devtools/rust/rust-source.inc    |   1 +
 2 files changed, 124 insertions(+)
 create mode 100644 meta/recipes-devtools/rust/files/0006-cargo-keep-host-deps-out-of-the-target-C-metadata-hash.patch
diff mbox series

Patch

diff --git a/meta/recipes-devtools/rust/files/0006-cargo-keep-host-deps-out-of-the-target-C-metadata-hash.patch b/meta/recipes-devtools/rust/files/0006-cargo-keep-host-deps-out-of-the-target-C-metadata-hash.patch
new file mode 100644
index 0000000000..5e796b0164
--- /dev/null
+++ b/meta/recipes-devtools/rust/files/0006-cargo-keep-host-deps-out-of-the-target-C-metadata-hash.patch
@@ -0,0 +1,123 @@ 
+From f936d6a954455eff3a60968d155852f9de730228 Mon Sep 17 00:00:00 2001
+From: Sunil Dora <sunilkumar.dora@windriver.com>
+Date: Sun, 27 Sep 2026 08:47:23 +0530
+Subject: [PATCH] fix(compile): Keep host deps out of the target `-C metadata`
+ hash
+
+A target unit's `-C metadata` mixed in the `c_metadata` of its
+host-kind dependencies (build scripts, proc-macros). Their dependency
+graph depends on the host `cfg`, e.g. `cpufeatures` pulls in `libc`
+only on aarch64-linux, so the build host leaked into the symbols of
+cross-compiled crates such as rustdoc.
+
+Host-kind dependencies are not linked into the target artifact, so use
+their package id instead. `unit_id`, `-C extra-filename` and
+fingerprints are unchanged, as are host units and builds without
+`--target`. The upstream PR also adds a testsuite test that fakes the
+reported host; it is not carried here.
+
+Upstream-Status: Submitted [https://github.com/rust-lang/cargo/pull/17522]
+Signed-off-by: Sunil Dora <sunilkumar.dora@windriver.com>
+---
+
+diff --git a/src/tools/cargo/src/compiler/build_runner/compilation_files.rs b/src/tools/cargo/src/compiler/build_runner/compilation_files.rs
+index 593e57e..63855f3 100644
+--- a/src/tools/cargo/src/compiler/build_runner/compilation_files.rs
++++ b/src/tools/cargo/src/compiler/build_runner/compilation_files.rs
+@@ -89,6 +89,8 @@ impl fmt::Debug for UnitHash {
+ /// mangled symbols need to be stable between different builds with different
+ /// settings. For example, profile-guided optimizations need to swap
+ /// `RUSTFLAGS` between runs, but needs to keep the same symbol names.
++/// Likewise a target unit only mixes the package id of its host-kind
++/// dependencies into `c_metadata`, see `compute_metadata`.
+ #[derive(Copy, Clone, Debug)]
+ pub struct Metadata {
+     unit_id: UnitHash,
+@@ -744,7 +746,7 @@ fn compute_metadata(
+     let deps_metadata = build_runner
+         .unit_deps(unit)
+         .iter()
+-        .map(|dep| *metadata_of(&dep.unit, build_runner, metas))
++        .map(|dep| (&dep.unit, *metadata_of(&dep.unit, build_runner, metas)))
+         .collect::<Vec<_>>();
+     let c_extra_filename = use_extra_filename(bcx, unit);
+     let pkg_dir = use_pkg_dir(bcx, unit);
+@@ -843,16 +845,28 @@ fn compute_metadata(
+ 
+     let mut c_metadata_hasher = shared_hasher.clone();
+     // Mix in the target-metadata of all the dependencies of this target.
++    // Host-kind deps of a target unit (build scripts, proc-macros) are not
++    // linked into it, and their metadata depends on the build host, so use
++    // a host independent stand-in for them (rust-lang/cargo#8140).
+     let mut dep_c_metadata_hashes = deps_metadata
+         .iter()
+-        .map(|m| m.c_metadata)
++        .map(|(dep, m)| {
++            if !unit.kind.is_host() && dep.kind.is_host() {
++                host_independent_c_metadata(dep, ws_root)
++            } else {
++                m.c_metadata
++            }
++        })
+         .collect::<Vec<_>>();
+     dep_c_metadata_hashes.sort();
+     dep_c_metadata_hashes.hash(&mut c_metadata_hasher);
+ 
+     let mut unit_id_hasher = shared_hasher.clone();
+     // Mix in the target-metadata of all the dependencies of this target.
+-    let mut dep_unit_id_hashes = deps_metadata.iter().map(|m| m.unit_id).collect::<Vec<_>>();
++    let mut dep_unit_id_hashes = deps_metadata
++        .iter()
++        .map(|(_, m)| m.unit_id)
++        .collect::<Vec<_>>();
+     dep_unit_id_hashes.sort();
+     dep_unit_id_hashes.hash(&mut unit_id_hasher);
+     // Avoid trashing the caches on RUSTFLAGS changing via `unit_id`
+@@ -884,6 +898,15 @@ fn compute_metadata(
+     }
+ }
+ 
++/// Stand-in `c_metadata` for a host-kind dependency of a target unit:
++/// only the dependency's package id, see `compute_metadata`.
++fn host_independent_c_metadata(dep: &Unit, ws_root: &Path) -> UnitHash {
++    let mut hasher = StableHasher::new();
++    METADATA_VERSION.hash(&mut hasher);
++    dep.pkg.package_id().stable_hash(ws_root).hash(&mut hasher);
++    UnitHash(Hasher::finish(&hasher))
++}
++
+ /// HACK: Detect the *potential* presence of `--remap-path-prefix`
+ ///
+ /// As CLI parsing is contextual and dependent on the CLI definition to understand the context, we
+diff --git a/src/tools/cargo/src/compiler/fingerprint/mod.rs b/src/tools/cargo/src/compiler/fingerprint/mod.rs
+index 172165d..d7f1e55 100644
+--- a/src/tools/cargo/src/compiler/fingerprint/mod.rs
++++ b/src/tools/cargo/src/compiler/fingerprint/mod.rs
+@@ -74,7 +74,7 @@
+ //! `TargetKind` (bin/lib/etc.)                | ✓           | ✓                        | ✓
+ //! Enabled Features                           | ✓           | ✓                        | ✓
+ //! Declared Features                          | ✓           |                          |
+-//! Immediate dependency’s hashes              | ✓[^1]       | ✓                        | ✓
++//! Immediate dependency’s hashes              | ✓[^1]       | ✓                        | ✓[^9]
+ //! [`CompileKind`] (host/target)              | ✓           | ✓                        | ✓
+ //! `__CARGO_DEFAULT_LIB_METADATA`[^4]         |             | ✓                        | ✓
+ //! `__CARGO_RUSTC_BOOTSTRAP_WS_REMAP`[^2]     | ✓           |                          |
+@@ -91,7 +91,7 @@
+ //! `[lints.rust.unexpected_cfgs.check-cfg]`   | ✓           |                          |
+ //! `--extern priv:`                           | ✓           |                          |
+ //!
+-//! [^1]: Bin dependencies are not included.
++//! [^1]: Bin dependencies other than artifact dependencies are not included.
+ //!
+ //! [^2]: `__CARGO_RUSTC_BOOTSTRAP_WS_REMAP` is set by rustc bootstrap
+ //!       to customize remap-path-prefix
+@@ -111,6 +111,9 @@
+ //!
+ //! [^8]: including `-Cextra-filename`
+ //!
++//! [^9]: For a target-kind unit, host-kind dependencies (build scripts,
++//!       proc-macros) contribute only their `package_id`.
++//!
+ //! When deciding what should go in the Metadata vs the Fingerprint, consider
+ //! that some files (like dylibs) do not have a hash in their filename. Thus,
+ //! if a value changes, only the fingerprint will detect the change (consider,
diff --git a/meta/recipes-devtools/rust/rust-source.inc b/meta/recipes-devtools/rust/rust-source.inc
index b384a0c76e..42d45a1ca6 100644
--- a/meta/recipes-devtools/rust/rust-source.inc
+++ b/meta/recipes-devtools/rust/rust-source.inc
@@ -8,6 +8,7 @@  SRC_URI += "https://static.rust-lang.org/dist/rustc-${RUST_VERSION}-src.tar.xz;n
             file://0003-bootstrap-skip-StdarchVerify-when-remote-testing.patch;patchdir=${RUSTSRC} \
             file://0004-cargo-omit-host-triple-from-unit-metadata-hash.patch;patchdir=${RUSTSRC} \
             file://0005-rustc-span-make-hygiene-encoding-order-deterministic.patch;patchdir=${RUSTSRC} \
+            file://0006-cargo-keep-host-deps-out-of-the-target-C-metadata-hash.patch;patchdir=${RUSTSRC} \
 "
 SRC_URI[rust.sha256sum] = "6451598a99e77f07571960f6b21d577399d3a30f33e80cbd2e3a4aae6535d265"