Skip to content

Commit

Permalink
Moved manifest metadata tracking from fingerprint to dep info (#14973)
Browse files Browse the repository at this point in the history
<!--
Thanks for submitting a pull request 🎉! Here are some tips for you:

* If this is your first contribution, read "Cargo Contribution Guide"
first:
  https://doc.crates.io/contrib/
* Run `cargo fmt --all` to format your code changes.
* Small commits and pull requests are always preferable and easy to
review.
* If your idea is large and needs feedback from the community, read how:
  https://doc.crates.io/contrib/process/#working-on-large-features
* Cargo takes care of compatibility. Read our design principles:
  https://doc.crates.io/contrib/design.html
* When changing help text of cargo commands, follow the steps to
generate docs:

https://github.com/rust-lang/cargo/tree/master/src/doc#building-the-man-pages
* If your PR is not finished, set it as "draft" PR or add "WIP" in its
title.
* It's ok to use the CI resources to test your PR, but please don't
abuse them.

### What does this PR try to resolve?

Explain the motivation behind this change.
A clear overview along with an in-depth explanation are helpful.

You can use `Fixes #<issue number>` to associate this PR to an existing
issue.

### How should we test and review this PR?

Demonstrate how you test this change and guide reviewers through your
PR.
With a smooth review process, a pull request usually gets reviewed
quicker.

If you don't know how to write and run your tests, please read the
guide:
https://doc.crates.io/contrib/tests

### Additional information

Other information you want to mention in this PR, such as prior arts,
future extensions, an unresolved problem, or a TODO list.
-->

### What does this PR try to resolve?

Fixes #14154

~~Added some of the missing package metadata to the fingerprint so that
rebuilds are triggered correctly.~~
~~Also updated the test to prevent a future regression.~~

Moved the manifest metadata tracking to use dep info in favor of
fingerprint so that rebuilds are only triggered if the metadata is
actually used.
  • Loading branch information
weihanglo authored Dec 24, 2024
2 parents 8721d52 + 3d7b154 commit 3447b11
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 142 deletions.
41 changes: 7 additions & 34 deletions src/cargo/core/compiler/compilation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -351,54 +351,27 @@ impl<'gctx> Compilation<'gctx> {
}
}

let metadata = pkg.manifest().metadata();

let cargo_exe = self.gctx.cargo_exe()?;
cmd.env(crate::CARGO_ENV, cargo_exe);

// When adding new environment variables depending on
// crate properties which might require rebuild upon change
// consider adding the corresponding properties to the hash
// in BuildContext::target_metadata()
let rust_version = pkg.rust_version().as_ref().map(ToString::to_string);
cmd.env("CARGO_MANIFEST_DIR", pkg.root())
.env("CARGO_MANIFEST_PATH", pkg.manifest_path())
.env("CARGO_PKG_VERSION_MAJOR", &pkg.version().major.to_string())
.env("CARGO_PKG_VERSION_MINOR", &pkg.version().minor.to_string())
.env("CARGO_PKG_VERSION_PATCH", &pkg.version().patch.to_string())
.env("CARGO_PKG_VERSION_PRE", pkg.version().pre.as_str())
.env("CARGO_PKG_VERSION", &pkg.version().to_string())
.env("CARGO_PKG_NAME", &*pkg.name())
.env(
"CARGO_PKG_DESCRIPTION",
metadata.description.as_ref().unwrap_or(&String::new()),
)
.env(
"CARGO_PKG_HOMEPAGE",
metadata.homepage.as_ref().unwrap_or(&String::new()),
)
.env(
"CARGO_PKG_REPOSITORY",
metadata.repository.as_ref().unwrap_or(&String::new()),
)
.env(
"CARGO_PKG_LICENSE",
metadata.license.as_ref().unwrap_or(&String::new()),
)
.env(
"CARGO_PKG_LICENSE_FILE",
metadata.license_file.as_ref().unwrap_or(&String::new()),
)
.env("CARGO_PKG_AUTHORS", &pkg.authors().join(":"))
.env(
"CARGO_PKG_RUST_VERSION",
&rust_version.as_deref().unwrap_or_default(),
)
.env(
"CARGO_PKG_README",
metadata.readme.as_ref().unwrap_or(&String::new()),
)
.cwd(pkg.root());
.env("CARGO_PKG_NAME", &*pkg.name());

for (key, value) in pkg.manifest().metadata().env_vars() {
cmd.env(key, value.as_ref());
}

cmd.cwd(pkg.root());

apply_env_config(self.gctx, &mut cmd)?;

Expand Down
6 changes: 5 additions & 1 deletion src/cargo/core/compiler/fingerprint/dep_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use cargo_util::paths;
use cargo_util::ProcessBuilder;
use cargo_util::Sha256;

use crate::core::manifest::ManifestMetadata;
use crate::CargoResult;
use crate::CARGO_ENV;

Expand Down Expand Up @@ -334,7 +335,10 @@ pub fn translate_dep_info(
//
// For cargo#13280, We trace env vars that are defined in the `[env]` config table.
on_disk_info.env.retain(|(key, _)| {
env_config.contains_key(key) || !rustc_cmd.get_envs().contains_key(key) || key == CARGO_ENV
ManifestMetadata::should_track(key)
|| env_config.contains_key(key)
|| !rustc_cmd.get_envs().contains_key(key)
|| key == CARGO_ENV
});

let serialize_path = |file| {
Expand Down
2 changes: 0 additions & 2 deletions src/cargo/core/compiler/fingerprint/dirty_reason.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ pub enum DirtyReason {
old: Vec<String>,
new: Vec<String>,
},
MetadataChanged,
ConfigSettingsChanged,
CompileKindChanged,
LocalLengthsChanged,
Expand Down Expand Up @@ -168,7 +167,6 @@ impl DirtyReason {
s.dirty_because(unit, "the profile configuration changed")
}
DirtyReason::RustflagsChanged { .. } => s.dirty_because(unit, "the rustflags changed"),
DirtyReason::MetadataChanged => s.dirty_because(unit, "the metadata changed"),
DirtyReason::ConfigSettingsChanged => {
s.dirty_because(unit, "the config settings changed")
}
Expand Down
33 changes: 14 additions & 19 deletions src/cargo/core/compiler/fingerprint/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@
//! [`CompileKind`] (host/target) | ✓ | ✓ | ✓ | ✓
//! `__CARGO_DEFAULT_LIB_METADATA`[^4] | | ✓ | ✓ | ✓
//! `package_id` | | ✓ | ✓ | ✓
//! authors, description, homepage, repo | ✓ | | |
//! Target src path relative to ws | ✓ | | |
//! Target flags (test/bench/for_host/edition) | ✓ | | |
//! -C incremental=… flag | ✓ | | |
Expand Down Expand Up @@ -189,6 +188,8 @@
//! files to learn about environment variables that the rustc compile depends on.
//! Cargo then later uses this to trigger a recompile if a referenced env var
//! changes (even if the source didn't change).
//! This also includes env vars generated from Cargo metadata like `CARGO_PKG_DESCRIPTION`.
//! (See [`crate::core::manifest::ManifestMetadata`]
//!
//! #### dep-info files for build system integration.
//!
Expand Down Expand Up @@ -612,10 +613,6 @@ pub struct Fingerprint {
memoized_hash: Mutex<Option<u64>>,
/// RUSTFLAGS/RUSTDOCFLAGS environment variable value (or config value).
rustflags: Vec<String>,
/// Hash of some metadata from the manifest, such as "authors", or
/// "description", which are exposed as environment variables during
/// compilation.
metadata: u64,
/// Hash of various config settings that change how things are compiled.
config: u64,
/// The rustc target. This is only relevant for `.json` files, otherwise
Expand Down Expand Up @@ -831,11 +828,12 @@ impl LocalFingerprint {
&self,
mtime_cache: &mut HashMap<PathBuf, FileTime>,
checksum_cache: &mut HashMap<PathBuf, Checksum>,
pkg_root: &Path,
pkg: &Package,
target_root: &Path,
cargo_exe: &Path,
gctx: &GlobalContext,
) -> CargoResult<Option<StaleItem>> {
let pkg_root = pkg.root();
match self {
// We need to parse `dep_info`, learn about the crate's dependencies.
//
Expand All @@ -849,6 +847,12 @@ impl LocalFingerprint {
return Ok(Some(StaleItem::MissingFile(dep_info)));
};
for (key, previous) in info.env.iter() {
if let Some(value) = pkg.manifest().metadata().env_var(key.as_str()) {
if Some(value.as_ref()) == previous.as_deref() {
continue;
}
}

let current = if key == CARGO_ENV {
Some(cargo_exe.to_str().ok_or_else(|| {
format_err!(
Expand Down Expand Up @@ -932,7 +936,6 @@ impl Fingerprint {
local: Mutex::new(Vec::new()),
memoized_hash: Mutex::new(None),
rustflags: Vec::new(),
metadata: 0,
config: 0,
compile_kind: 0,
fs_status: FsStatus::Stale,
Expand Down Expand Up @@ -995,9 +998,6 @@ impl Fingerprint {
new: self.rustflags.clone(),
};
}
if self.metadata != old.metadata {
return DirtyReason::MetadataChanged;
}
if self.config != old.config {
return DirtyReason::ConfigSettingsChanged;
}
Expand Down Expand Up @@ -1142,13 +1142,14 @@ impl Fingerprint {
&mut self,
mtime_cache: &mut HashMap<PathBuf, FileTime>,
checksum_cache: &mut HashMap<PathBuf, Checksum>,
pkg_root: &Path,
pkg: &Package,
target_root: &Path,
cargo_exe: &Path,
gctx: &GlobalContext,
) -> CargoResult<()> {
assert!(!self.fs_status.up_to_date());

let pkg_root = pkg.root();
let mut mtimes = HashMap::new();

// Get the `mtime` of all outputs. Optionally update their mtime
Expand Down Expand Up @@ -1249,7 +1250,7 @@ impl Fingerprint {
if let Some(item) = local.find_stale_item(
mtime_cache,
checksum_cache,
pkg_root,
pkg,
target_root,
cargo_exe,
gctx,
Expand Down Expand Up @@ -1279,7 +1280,6 @@ impl hash::Hash for Fingerprint {
profile,
ref deps,
ref local,
metadata,
config,
compile_kind,
ref rustflags,
Expand All @@ -1294,7 +1294,6 @@ impl hash::Hash for Fingerprint {
path,
profile,
&*local,
metadata,
config,
compile_kind,
rustflags,
Expand Down Expand Up @@ -1445,7 +1444,7 @@ fn calculate(build_runner: &mut BuildRunner<'_, '_>, unit: &Unit) -> CargoResult
fingerprint.check_filesystem(
&mut build_runner.mtime_cache,
&mut build_runner.checksum_cache,
unit.pkg.root(),
&unit.pkg,
&target_root,
cargo_exe,
build_runner.bcx.gctx,
Expand Down Expand Up @@ -1529,9 +1528,6 @@ fn calculate_normal(
build_runner.lto[unit],
unit.pkg.manifest().lint_rustflags(),
));
// Include metadata since it is exposed as environment variables.
let m = unit.pkg.manifest().metadata();
let metadata = util::hash_u64((&m.authors, &m.description, &m.homepage, &m.repository));
let mut config = StableHasher::new();
if let Some(linker) = build_runner.compilation.target_linker(unit.kind) {
linker.hash(&mut config);
Expand Down Expand Up @@ -1560,7 +1556,6 @@ fn calculate_normal(
deps,
local: Mutex::new(local),
memoized_hash: Mutex::new(None),
metadata,
config: Hasher::finish(&config),
compile_kind,
rustflags: extra_flags,
Expand Down
81 changes: 81 additions & 0 deletions src/cargo/core/manifest.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::borrow::Cow;
use std::collections::{BTreeMap, HashMap};
use std::fmt;
use std::hash::{Hash, Hasher};
Expand Down Expand Up @@ -146,6 +147,86 @@ pub struct ManifestMetadata {
pub rust_version: Option<RustVersion>,
}

macro_rules! get_metadata_env {
($meta:ident, $field:ident) => {
$meta.$field.as_deref().unwrap_or_default().into()
};
($meta:ident, $field:ident, $to_var:expr) => {
$to_var($meta).into()
};
}

macro_rules! metadata_envs {
(
$(
($field:ident, $key:literal$(, $to_var:expr)?),
)*
) => {
struct MetadataEnvs;
impl MetadataEnvs {
$(
fn $field(meta: &ManifestMetadata) -> Cow<'_, str> {
get_metadata_env!(meta, $field$(, $to_var)?)
}
)*

pub fn should_track(key: &str) -> bool {
let keys = [$($key),*];
key.strip_prefix("CARGO_PKG_")
.map(|key| keys.iter().any(|k| *k == key))
.unwrap_or_default()
}

pub fn var<'a>(meta: &'a ManifestMetadata, key: &str) -> Option<Cow<'a, str>> {
key.strip_prefix("CARGO_PKG_").and_then(|key| match key {
$($key => Some(Self::$field(meta)),)*
_ => None,
})
}

pub fn vars(meta: &ManifestMetadata) -> impl Iterator<Item = (&'static str, Cow<'_, str>)> {
[
$(
(
concat!("CARGO_PKG_", $key),
Self::$field(meta),
),
)*
].into_iter()
}
}
}
}

// Metadata enviromental variables that are emitted to rustc. Usable by `env!()`
// If these change we need to trigger a rebuild.
// NOTE: The env var name will be prefixed with `CARGO_PKG_`
metadata_envs! {
(description, "DESCRIPTION"),
(homepage, "HOMEPAGE"),
(repository, "REPOSITORY"),
(license, "LICENSE"),
(license_file, "LICENSE_FILE"),
(authors, "AUTHORS", |m: &ManifestMetadata| m.authors.join(":")),
(rust_version, "RUST_VERSION", |m: &ManifestMetadata| m.rust_version.as_ref().map(ToString::to_string).unwrap_or_default()),
(readme, "README"),
}

impl ManifestMetadata {
/// Whether the given env var should be tracked by Cargo's dep-info.
pub fn should_track(env_key: &str) -> bool {
MetadataEnvs::should_track(env_key)
}

pub fn env_var<'a>(&'a self, env_key: &str) -> Option<Cow<'a, str>> {
MetadataEnvs::var(self, env_key)
}

pub fn env_vars(&self) -> impl Iterator<Item = (&'static str, Cow<'_, str>)> {
MetadataEnvs::vars(self)
}
}

#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum TargetKind {
Lib(Vec<CrateType>),
Expand Down
Loading

0 comments on commit 3447b11

Please sign in to comment.