Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
98 changes: 74 additions & 24 deletions src/cargo/core/compiler/build_runner/compilation_files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ impl fmt::Debug for UnitHash {
pub struct Metadata {
unit_id: UnitHash,
c_metadata: UnitHash,
c_extra_filename: Option<UnitHash>,
c_extra_filename: bool,
pkg_dir: bool,
}

impl Metadata {
Expand All @@ -108,7 +109,12 @@ impl Metadata {

/// A hash to add to file names through `-C extra-filename`
pub fn c_extra_filename(&self) -> Option<UnitHash> {
self.c_extra_filename
self.c_extra_filename.then_some(self.unit_id)
}

/// A hash to add to Cargo directory names
pub fn pkg_dir(&self) -> Option<UnitHash> {
self.pkg_dir.then_some(self.unit_id)
}
}

Expand Down Expand Up @@ -245,11 +251,11 @@ impl<'a, 'gctx: 'a> CompilationFiles<'a, 'gctx> {
};
let name = unit.pkg.package_id().name();
let meta = self.metas[unit];
if let Some(c_extra_filename) = meta.c_extra_filename() {
format!("{}{}{}", name, separator, c_extra_filename)
} else {
format!("{}{}{}", name, separator, self.target_short_hash(unit))
}
let hash = meta
.pkg_dir()
.map(|h| h.to_string())
.unwrap_or_else(|| self.target_short_hash(unit));
format!("{name}{separator}{hash}")
}

/// Returns the final artifact path for the host (`/…/target/debug`)
Expand Down Expand Up @@ -681,7 +687,8 @@ fn compute_metadata(
.iter()
.map(|dep| *metadata_of(&dep.unit, build_runner, metas))
.collect::<Vec<_>>();
let use_extra_filename = use_extra_filename(bcx, unit);
let c_extra_filename = use_extra_filename(bcx, unit);
let pkg_dir = use_pkg_dir(bcx, unit);

let mut shared_hasher = StableHasher::new();

Expand Down Expand Up @@ -784,42 +791,37 @@ fn compute_metadata(
dep_c_metadata_hashes.sort();
dep_c_metadata_hashes.hash(&mut c_metadata_hasher);

let mut c_extra_filename_hasher = shared_hasher.clone();
let mut unit_id_hasher = shared_hasher.clone();
// Mix in the target-metadata of all the dependencies of this target.
let mut dep_c_extra_filename_hashes = deps_metadata
.iter()
.map(|m| m.c_extra_filename)
.collect::<Vec<_>>();
dep_c_extra_filename_hashes.sort();
dep_c_extra_filename_hashes.hash(&mut c_extra_filename_hasher);
// Avoid trashing the caches on RUSTFLAGS changing via `c_extra_filename`
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`
//
// Limited to `c_extra_filename` to help with reproducible build / PGO issues.
// Limited to `unit_id` to help with reproducible build / PGO issues.
let default = Vec::new();
let extra_args = build_runner.bcx.extra_args_for(unit).unwrap_or(&default);
if !has_remap_path_prefix(&extra_args) {
extra_args.hash(&mut c_extra_filename_hasher);
extra_args.hash(&mut unit_id_hasher);
}
if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
if !has_remap_path_prefix(&unit.rustdocflags) {
unit.rustdocflags.hash(&mut c_extra_filename_hasher);
unit.rustdocflags.hash(&mut unit_id_hasher);
}
} else {
if !has_remap_path_prefix(&unit.rustflags) {
unit.rustflags.hash(&mut c_extra_filename_hasher);
unit.rustflags.hash(&mut unit_id_hasher);
}
}

let c_metadata = UnitHash(Hasher::finish(&c_metadata_hasher));
let c_extra_filename = UnitHash(Hasher::finish(&c_extra_filename_hasher));
let unit_id = c_extra_filename;

let c_extra_filename = use_extra_filename.then_some(c_extra_filename);
let unit_id = UnitHash(Hasher::finish(&unit_id_hasher));

Metadata {
unit_id,
c_metadata,
c_extra_filename,
pkg_dir,
}
}

Expand Down Expand Up @@ -922,3 +924,51 @@ fn use_extra_filename(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
}
true
}

/// Returns whether or not this unit should use a hash in the pkg_dir to make it unique.
fn use_pkg_dir(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool {
if unit.mode.is_doc_test() || unit.mode.is_doc() {
// Doc tests do not have metadata.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems fine for now until we want to store rustdoc artifacts separately.

A future idea, with -Zrustdoc-mergeable-info, we could keep the each rustdoc artifacts separately. And later we do a merge that

  • Uplift everything at the end.
  • Run rustdoc --merge=finalize then

A downside of it: we'll need to copy a lot of files. Probably not worthy

return false;
}
if bcx.gctx.cli_unstable().build_dir_new_layout {
// These always use metadata.
return true;
}
Comment on lines +934 to +937
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is no test showing the behavior change. Should we add some no-metadata test cases for both old and new layout, so at least new layout will show it start having metadata

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The change here is from target_short_hash to unit_id, not from no hash to hash, see f5166c4 and 6b14543

When we stabilize build-dir-new-layout, a couple of existing tests will remove a [DIRTY] entry because a new cache entry will be used instead. I could copy those over and adjust them for the new build dir layout (like I did with #16348) but that seemed disproportionate to the benefit (unlike #16348) but if you still want me to, I can.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reasonable. Thanks!

if unit.mode.is_any_test() || unit.mode.is_check() {
// These always use metadata.
return true;
}
// No metadata in these cases:
//
// - dylibs:
// - if any dylib names are encoded in executables, so they can't be renamed.
// - TODO: Maybe use `-install-name` on macOS or `-soname` on other UNIX systems
// to specify the dylib name to be used by the linker instead of the filename.
// - Windows MSVC executables: The path to the PDB is embedded in the
// executable, and we don't want the PDB path to include the hash in it.
// - wasm32-unknown-emscripten executables: When using emscripten, the path to the
// .wasm file is embedded in the .js file, so we don't want the hash in there.
//
// This is only done for local packages, as we don't expect to export
// dependencies.
//
// The __CARGO_DEFAULT_LIB_METADATA env var is used to override this to
// force metadata in the hash. This is only used for building libstd. For
// example, if libstd is placed in a common location, we don't want a file
// named /usr/lib/libstd.so which could conflict with other rustc
// installs. In addition it prevents accidentally loading a libstd of a
// different compiler at runtime.
// See https://github.com/rust-lang/cargo/issues/3005
let short_name = bcx.target_data.short_name(&unit.kind);
if (unit.target.is_dylib()
|| unit.target.is_cdylib()
|| (unit.target.is_executable() && short_name == "wasm32-unknown-emscripten")
|| (unit.target.is_executable() && short_name.contains("msvc")))
&& unit.pkg.package_id().source_id().is_path()
&& bcx.gctx.get_env("__CARGO_DEFAULT_LIB_METADATA").is_err()
{
return false;
}
true
}
Comment on lines 928 to 974
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't touch docs because I've not fully investigated them yet.
On some playing around with this change outside of the new build dir
layout, I did see some breakages.

What kind of breakages did you see?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test failure had the appearance of the docs being cleared but not rebuilt. It was the test from #16344.

Note that docs are having no change in behavior in this PR. This PR is mostly unblocking resolving #8332.

50 changes: 26 additions & 24 deletions src/cargo/core/compiler/fingerprint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,30 +64,30 @@
//! recompile, but it is desired to reuse the same filenames. A comparison
//! of what is tracked:
//!
//! Value | Fingerprint | `Metadata::unit_id` | `Metadata::c_metadata` | `Metadata::c_extra_filename`
//! -------------------------------------------|-------------|---------------------|------------------------|----------
//! rustc | ✓ | ✓ | ✓ | ✓
//! [`Profile`] | ✓ | ✓ | ✓ | ✓
//! `cargo rustc` extra args | ✓ | ✓[^7] | | ✓[^7]
//! [`CompileMode`] | ✓ | ✓ | ✓ | ✓
//! Target Name | ✓ | ✓ | ✓ | ✓
//! `TargetKind` (bin/lib/etc.) | ✓ | ✓ | ✓ | ✓
//! Enabled Features | ✓ | ✓ | ✓ | ✓
//! Declared Features | ✓ | | |
//! Immediate dependency’s hashes | ✓[^1] | ✓ | ✓ | ✓
//! [`CompileKind`] (host/target) | ✓ | ✓ | ✓ | ✓
//! `__CARGO_DEFAULT_LIB_METADATA`[^4] | | ✓ | ✓ | ✓
//! `package_id` | | ✓ | ✓ | ✓
//! Target src path relative to ws | ✓ | | |
//! Target flags (test/bench/for_host/edition) | ✓ | | |
//! -C incremental=… flag | ✓ | | |
//! mtime of sources | ✓[^3] | | |
//! RUSTFLAGS/RUSTDOCFLAGS | ✓ | ✓[^7] | | ✓[^7]
//! [`Lto`] flags | ✓ | ✓ | ✓ | ✓
//! config settings[^5] | ✓ | | |
//! `is_std` | | ✓ | ✓ | ✓
//! `[lints]` table[^6] | ✓ | | |
//! `[lints.rust.unexpected_cfgs.check-cfg]` | ✓ | | |
//! Value | Fingerprint | `Metadata::unit_id` [^8] | `Metadata::c_metadata`
//! -------------------------------------------|-------------|--------------------------|-----------------------
//! rustc | ✓ | ✓ | ✓
//! [`Profile`] | ✓ | ✓ | ✓
//! `cargo rustc` extra args | ✓ | ✓[^7] |
//! [`CompileMode`] | ✓ | ✓ | ✓
//! Target Name | ✓ | ✓ | ✓
//! `TargetKind` (bin/lib/etc.) | ✓ | ✓ | ✓
//! Enabled Features | ✓ | ✓ | ✓
//! Declared Features | ✓ | |
//! Immediate dependency’s hashes | ✓[^1] | ✓ | ✓
//! [`CompileKind`] (host/target) | ✓ | ✓ | ✓
//! `__CARGO_DEFAULT_LIB_METADATA`[^4] | | ✓ | ✓
//! `package_id` | | ✓ | ✓
//! Target src path relative to ws | ✓ | |
//! Target flags (test/bench/for_host/edition) | ✓ | |
//! -C incremental=… flag | ✓ | |
//! mtime of sources | ✓[^3] | |
//! RUSTFLAGS/RUSTDOCFLAGS | ✓ | ✓[^7] |
//! [`Lto`] flags | ✓ | ✓ | ✓
//! config settings[^5] | ✓ | |
//! `is_std` | | ✓ | ✓
//! `[lints]` table[^6] | ✓ | |
//! `[lints.rust.unexpected_cfgs.check-cfg]` | ✓ | |
//!
//! [^1]: Bin dependencies are not included.
//!
Expand All @@ -104,6 +104,8 @@
//! [^7]: extra-flags and RUSTFLAGS are conditionally excluded when `--remap-path-prefix` is
//! present to avoid breaking build reproducibility while we wait for trim-paths
//!
//! [^8]: including `-Cextra-filename`
//!
//! 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,
Expand Down