Auto merge of #11407 - dnbln:reasons-for-rebuilding, r=weihanglo

Reasons for rebuilding
This commit is contained in:
bors 2022-12-30 13:25:46 +00:00
commit dd9900585a
18 changed files with 768 additions and 166 deletions

View File

@ -16,6 +16,11 @@
//! `[WARNING]`) to match cargo's "status" output and allows you to ignore
//! the alignment. See the source of `substitute_macros` for a complete list
//! of substitutions.
//! - `[DIRTY-MSVC]` (only when the line starts with it) would be replaced by
//! `[DIRTY]` when `cfg(target_env = "msvc")` or the line will be ignored otherwise.
//! Tests that work around [issue 7358](https://github.com/rust-lang/cargo/issues/7358)
//! can use this to avoid duplicating the `with_stderr` call like:
//! `if cfg!(target_env = "msvc") {e.with_stderr("...[DIRTY]...");} else {e.with_stderr("...");}`.
//!
//! # Normalization
//!
@ -108,7 +113,9 @@ fn normalize_actual(actual: &str, cwd: Option<&Path>) -> String {
/// Normalizes the expected string so that it can be compared against the actual output.
fn normalize_expected(expected: &str, cwd: Option<&Path>) -> String {
let expected = substitute_macros(expected);
let expected = replace_dirty_msvc(expected);
let expected = substitute_macros(&expected);
if cfg!(windows) {
normalize_windows(&expected, cwd)
} else {
@ -121,6 +128,29 @@ fn normalize_expected(expected: &str, cwd: Option<&Path>) -> String {
}
}
fn replace_dirty_msvc_impl(s: &str, is_msvc: bool) -> String {
if is_msvc {
s.replace("[DIRTY-MSVC]", "[DIRTY]")
} else {
use itertools::Itertools;
let mut new = s
.lines()
.filter(|it| !it.starts_with("[DIRTY-MSVC]"))
.join("\n");
if s.ends_with("\n") {
new.push_str("\n");
}
new
}
}
fn replace_dirty_msvc(s: &str) -> String {
replace_dirty_msvc_impl(s, cfg!(target_env = "msvc"))
}
/// Normalizes text for both actual and expected strings on Windows.
fn normalize_windows(text: &str, cwd: Option<&Path>) -> String {
// Let's not deal with / vs \ (windows...)
@ -170,6 +200,7 @@ fn substitute_macros(input: &str) -> String {
("[DOCUMENTING]", " Documenting"),
("[SCRAPING]", " Scraping"),
("[FRESH]", " Fresh"),
("[DIRTY]", " Dirty"),
("[UPDATING]", " Updating"),
("[ADDING]", " Adding"),
("[REMOVING]", " Removing"),
@ -637,3 +668,114 @@ fn wild_str_cmp() {
assert_ne!(WildStr::new(a), WildStr::new(b));
}
}
#[test]
fn dirty_msvc() {
let case = |expected: &str, wild: &str, msvc: bool| {
assert_eq!(expected, &replace_dirty_msvc_impl(wild, msvc));
};
// no replacements
case("aa", "aa", false);
case("aa", "aa", true);
// with replacements
case(
"\
[DIRTY] a",
"\
[DIRTY-MSVC] a",
true,
);
case(
"",
"\
[DIRTY-MSVC] a",
false,
);
case(
"\
[DIRTY] a
[COMPILING] a",
"\
[DIRTY-MSVC] a
[COMPILING] a",
true,
);
case(
"\
[COMPILING] a",
"\
[DIRTY-MSVC] a
[COMPILING] a",
false,
);
// test trailing newline behavior
case(
"\
A
B
", "\
A
B
", true,
);
case(
"\
A
B
", "\
A
B
", false,
);
case(
"\
A
B", "\
A
B", true,
);
case(
"\
A
B", "\
A
B", false,
);
case(
"\
[DIRTY] a
",
"\
[DIRTY-MSVC] a
",
true,
);
case(
"\n",
"\
[DIRTY-MSVC] a
",
false,
);
case(
"\
[DIRTY] a",
"\
[DIRTY-MSVC] a",
true,
);
case(
"",
"\
[DIRTY-MSVC] a",
false,
);
}

View File

@ -1,4 +1,4 @@
use super::job::{Freshness, Job, Work};
use super::job::{Job, Work};
use super::{fingerprint, Context, LinkType, Unit};
use crate::core::compiler::artifact;
use crate::core::compiler::context::Metadata;
@ -484,11 +484,11 @@ fn build_work(cx: &mut Context<'_, '_>, unit: &Unit) -> CargoResult<Job> {
});
let mut job = if cx.bcx.build_config.build_plan {
Job::new_dirty(Work::noop())
Job::new_dirty(Work::noop(), None)
} else {
fingerprint::prepare_target(cx, unit, false)?
};
if job.freshness() == Freshness::Dirty {
if job.freshness().is_dirty() {
job.before(dirty);
} else {
job.before(fresh);

View File

@ -312,7 +312,10 @@
//! See the `A-rebuild-detection` flag on the issue tracker for more:
//! <https://github.com/rust-lang/cargo/issues?q=is%3Aissue+is%3Aopen+label%3AA-rebuild-detection>
mod dirty_reason;
use std::collections::hash_map::{Entry, HashMap};
use std::env;
use std::hash::{self, Hash, Hasher};
use std::io;
@ -341,6 +344,8 @@ use super::custom_build::BuildDeps;
use super::job::{Job, Work};
use super::{BuildContext, Context, FileFlavor, Unit};
pub use dirty_reason::DirtyReason;
/// Determines if a `unit` is up-to-date, and if not prepares necessary work to
/// update the persisted fingerprint.
///
@ -393,9 +398,17 @@ pub fn prepare_target(cx: &mut Context<'_, '_>, unit: &Unit, force: bool) -> Car
source.verify(unit.pkg.package_id())?;
}
if compare.is_ok() && !force {
return Ok(Job::new_fresh());
}
let dirty_reason = match compare {
Ok(None) => {
if force {
Some(DirtyReason::Forced)
} else {
return Ok(Job::new_fresh());
}
}
Ok(reason) => reason,
Err(_) => None,
};
// Clear out the old fingerprint file if it exists. This protects when
// compilation is interrupted leaving a corrupt file. For example, a
@ -466,7 +479,7 @@ pub fn prepare_target(cx: &mut Context<'_, '_>, unit: &Unit, force: bool) -> Car
Work::new(move |_| write_fingerprint(&loc, &fingerprint))
};
Ok(Job::new_dirty(write_fingerprint))
Ok(Job::new_dirty(write_fingerprint, dirty_reason))
}
/// Dependency edge information for fingerprints. This is generated for each
@ -559,14 +572,28 @@ pub struct Fingerprint {
}
/// Indication of the status on the filesystem for a particular unit.
#[derive(Default)]
enum FsStatus {
#[derive(Clone, Default, Debug)]
pub enum FsStatus {
/// This unit is to be considered stale, even if hash information all
/// matches. The filesystem inputs have changed (or are missing) and the
/// unit needs to subsequently be recompiled.
/// matches.
#[default]
Stale,
/// File system inputs have changed (or are missing), or there were
/// changes to the environment variables that affect this unit. See
/// the variants of [`StaleItem`] for more information.
StaleItem(StaleItem),
/// A dependency was stale.
StaleDependency {
name: InternedString,
dep_mtime: FileTime,
max_mtime: FileTime,
},
/// A dependency was stale.
StaleDepFingerprint { name: InternedString },
/// This unit is up-to-date. All outputs and their corresponding mtime are
/// listed in the payload here for other dependencies to compare against.
UpToDate { mtimes: HashMap<PathBuf, FileTime> },
@ -576,7 +603,10 @@ impl FsStatus {
fn up_to_date(&self) -> bool {
match self {
FsStatus::UpToDate { .. } => true,
FsStatus::Stale => false,
FsStatus::Stale
| FsStatus::StaleItem(_)
| FsStatus::StaleDependency { .. }
| FsStatus::StaleDepFingerprint { .. } => false,
}
}
}
@ -677,7 +707,8 @@ enum LocalFingerprint {
RerunIfEnvChanged { var: String, val: Option<String> },
}
enum StaleItem {
#[derive(Clone, Debug)]
pub enum StaleItem {
MissingFile(PathBuf),
ChangedFile {
reference: PathBuf,
@ -823,56 +854,53 @@ impl Fingerprint {
/// The purpose of this is exclusively to produce a diagnostic message
/// indicating why we're recompiling something. This function always returns
/// an error, it will never return success.
fn compare(&self, old: &Fingerprint) -> CargoResult<()> {
fn compare(&self, old: &Fingerprint) -> DirtyReason {
if self.rustc != old.rustc {
bail!("rust compiler has changed")
return DirtyReason::RustcChanged;
}
if self.features != old.features {
bail!(
"features have changed: previously {}, now {}",
old.features,
self.features
)
return DirtyReason::FeaturesChanged {
old: old.features.clone(),
new: self.features.clone(),
};
}
if self.target != old.target {
bail!("target configuration has changed")
return DirtyReason::TargetConfigurationChanged;
}
if self.path != old.path {
bail!("path to the source has changed")
return DirtyReason::PathToSourceChanged;
}
if self.profile != old.profile {
bail!("profile configuration has changed")
return DirtyReason::ProfileConfigurationChanged;
}
if self.rustflags != old.rustflags {
bail!(
"RUSTFLAGS has changed: previously {:?}, now {:?}",
old.rustflags,
self.rustflags
)
return DirtyReason::RustflagsChanged {
old: old.rustflags.clone(),
new: self.rustflags.clone(),
};
}
if self.metadata != old.metadata {
bail!("metadata changed")
return DirtyReason::MetadataChanged;
}
if self.config != old.config {
bail!("configuration settings have changed")
return DirtyReason::ConfigSettingsChanged;
}
if self.compile_kind != old.compile_kind {
bail!("compile kind (rustc target) changed")
return DirtyReason::CompileKindChanged;
}
let my_local = self.local.lock().unwrap();
let old_local = old.local.lock().unwrap();
if my_local.len() != old_local.len() {
bail!("local lens changed");
return DirtyReason::LocalLengthsChanged;
}
for (new, old) in my_local.iter().zip(old_local.iter()) {
match (new, old) {
(LocalFingerprint::Precalculated(a), LocalFingerprint::Precalculated(b)) => {
if a != b {
bail!(
"precalculated components have changed: previously {}, now {}",
b,
a
)
return DirtyReason::PrecalculatedComponentsChanged {
old: b.to_string(),
new: a.to_string(),
};
}
}
(
@ -880,11 +908,10 @@ impl Fingerprint {
LocalFingerprint::CheckDepInfo { dep_info: bdep },
) => {
if adep != bdep {
bail!(
"dep info output changed: previously {:?}, now {:?}",
bdep,
adep
)
return DirtyReason::DepInfoOutputChanged {
old: bdep.clone(),
new: adep.clone(),
};
}
}
(
@ -898,18 +925,16 @@ impl Fingerprint {
},
) => {
if aout != bout {
bail!(
"rerun-if-changed output changed: previously {:?}, now {:?}",
bout,
aout
)
return DirtyReason::RerunIfChangedOutputFileChanged {
old: bout.clone(),
new: aout.clone(),
};
}
if apaths != bpaths {
bail!(
"rerun-if-changed output changed: previously {:?}, now {:?}",
bpaths,
apaths,
)
return DirtyReason::RerunIfChangedOutputPathsChanged {
old: bpaths.clone(),
new: apaths.clone(),
};
}
}
(
@ -923,57 +948,61 @@ impl Fingerprint {
},
) => {
if *akey != *bkey {
bail!("env vars changed: previously {}, now {}", bkey, akey);
return DirtyReason::EnvVarsChanged {
old: bkey.clone(),
new: akey.clone(),
};
}
if *avalue != *bvalue {
bail!(
"env var `{}` changed: previously {:?}, now {:?}",
akey,
bvalue,
avalue
)
return DirtyReason::EnvVarChanged {
name: akey.clone(),
old_value: bvalue.clone(),
new_value: avalue.clone(),
};
}
}
(a, b) => {
return DirtyReason::LocalFingerprintTypeChanged {
old: b.kind(),
new: a.kind(),
}
}
(a, b) => bail!(
"local fingerprint type has changed ({} => {})",
b.kind(),
a.kind()
),
}
}
if self.deps.len() != old.deps.len() {
bail!("number of dependencies has changed")
return DirtyReason::NumberOfDependenciesChanged {
old: old.deps.len(),
new: self.deps.len(),
};
}
for (a, b) in self.deps.iter().zip(old.deps.iter()) {
if a.name != b.name {
let e = format_err!("`{}` != `{}`", a.name, b.name)
.context("unit dependency name changed");
return Err(e);
return DirtyReason::UnitDependencyNameChanged {
old: b.name.clone(),
new: a.name.clone(),
};
}
if a.fingerprint.hash_u64() != b.fingerprint.hash_u64() {
let e = format_err!(
"new ({}/{:x}) != old ({}/{:x})",
a.name,
a.fingerprint.hash_u64(),
b.name,
b.fingerprint.hash_u64()
)
.context("unit dependency information changed");
return Err(e);
return DirtyReason::UnitDependencyInfoChanged {
new_name: a.name.clone(),
new_fingerprint: a.fingerprint.hash_u64(),
old_name: b.name.clone(),
old_fingerprint: b.fingerprint.hash_u64(),
};
}
}
if !self.fs_status.up_to_date() {
bail!("current filesystem status shows we're outdated");
return DirtyReason::FsStatusOutdated(self.fs_status.clone());
}
// This typically means some filesystem modifications happened or
// something transitive was odd. In general we should strive to provide
// a better error message than this, so if you see this message a lot it
// likely means this method needs to be updated!
bail!("two fingerprint comparison turned up nothing obvious");
DirtyReason::NothingObvious
}
/// Dynamically inspect the local filesystem to update the `fs_status` field
@ -1034,7 +1063,15 @@ impl Fingerprint {
let dep_mtimes = match &dep.fingerprint.fs_status {
FsStatus::UpToDate { mtimes } => mtimes,
// If our dependency is stale, so are we, so bail out.
FsStatus::Stale => return Ok(()),
FsStatus::Stale
| FsStatus::StaleItem(_)
| FsStatus::StaleDependency { .. }
| FsStatus::StaleDepFingerprint { .. } => {
self.fs_status = FsStatus::StaleDepFingerprint {
name: dep.name.clone(),
};
return Ok(());
}
};
// If our dependency edge only requires the rmeta file to be present
@ -1072,6 +1109,13 @@ impl Fingerprint {
"dependency on `{}` is newer than we are {} > {} {:?}",
dep.name, dep_mtime, max_mtime, pkg_root
);
self.fs_status = FsStatus::StaleDependency {
name: dep.name.clone(),
dep_mtime: *dep_mtime,
max_mtime: *max_mtime,
};
return Ok(());
}
}
@ -1085,6 +1129,7 @@ impl Fingerprint {
local.find_stale_item(mtime_cache, pkg_root, target_root, cargo_exe)?
{
item.log();
self.fs_status = FsStatus::StaleItem(item);
return Ok(());
}
}
@ -1625,7 +1670,7 @@ fn compare_old_fingerprint(
loc: &Path,
new_fingerprint: &Fingerprint,
mtime_on_use: bool,
) -> CargoResult<()> {
) -> CargoResult<Option<DirtyReason>> {
let old_fingerprint_short = paths::read(loc)?;
if mtime_on_use {
@ -1638,7 +1683,7 @@ fn compare_old_fingerprint(
let new_hash = new_fingerprint.hash_u64();
if util::to_hex(new_hash) == old_fingerprint_short && new_fingerprint.fs_status.up_to_date() {
return Ok(());
return Ok(None);
}
let old_fingerprint_json = paths::read(&loc.with_extension("json"))?;
@ -1651,21 +1696,28 @@ fn compare_old_fingerprint(
old_fingerprint_short
);
}
let result = new_fingerprint.compare(&old_fingerprint);
assert!(result.is_err());
result
Ok(Some(new_fingerprint.compare(&old_fingerprint)))
}
fn log_compare(unit: &Unit, compare: &CargoResult<()>) {
let ce = match compare {
Ok(..) => return,
Err(e) => e,
};
info!(
"fingerprint error for {}/{:?}/{:?}",
unit.pkg, unit.mode, unit.target,
);
info!(" err: {:?}", ce);
fn log_compare(unit: &Unit, compare: &CargoResult<Option<DirtyReason>>) {
match compare {
Ok(None) => {}
Ok(Some(reason)) => {
info!(
"fingerprint dirty for {}/{:?}/{:?}",
unit.pkg, unit.mode, unit.target,
);
info!(" dirty: {reason:?}");
}
Err(e) => {
info!(
"fingerprint error for {}/{:?}/{:?}",
unit.pkg, unit.mode, unit.target,
);
info!(" err: {e:?}");
}
}
}
/// Parses Cargo's internal `EncodedDepInfo` structure that was previously

View File

@ -0,0 +1,251 @@
use std::fmt;
use std::fmt::Debug;
use super::*;
use crate::core::Shell;
#[derive(Clone, Debug)]
pub enum DirtyReason {
RustcChanged,
FeaturesChanged {
old: String,
new: String,
},
TargetConfigurationChanged,
PathToSourceChanged,
ProfileConfigurationChanged,
RustflagsChanged {
old: Vec<String>,
new: Vec<String>,
},
MetadataChanged,
ConfigSettingsChanged,
CompileKindChanged,
LocalLengthsChanged,
PrecalculatedComponentsChanged {
old: String,
new: String,
},
DepInfoOutputChanged {
old: PathBuf,
new: PathBuf,
},
RerunIfChangedOutputFileChanged {
old: PathBuf,
new: PathBuf,
},
RerunIfChangedOutputPathsChanged {
old: Vec<PathBuf>,
new: Vec<PathBuf>,
},
EnvVarsChanged {
old: String,
new: String,
},
EnvVarChanged {
name: String,
old_value: Option<String>,
new_value: Option<String>,
},
LocalFingerprintTypeChanged {
old: &'static str,
new: &'static str,
},
NumberOfDependenciesChanged {
old: usize,
new: usize,
},
UnitDependencyNameChanged {
old: InternedString,
new: InternedString,
},
UnitDependencyInfoChanged {
old_name: InternedString,
old_fingerprint: u64,
new_name: InternedString,
new_fingerprint: u64,
},
FsStatusOutdated(FsStatus),
NothingObvious,
Forced,
}
trait ShellExt {
fn dirty_because(&mut self, unit: &Unit, s: impl fmt::Display) -> CargoResult<()>;
}
impl ShellExt for Shell {
fn dirty_because(&mut self, unit: &Unit, s: impl fmt::Display) -> CargoResult<()> {
self.status("Dirty", format_args!("{}: {s}", &unit.pkg))
}
}
struct FileTimeDiff {
old_time: FileTime,
new_time: FileTime,
}
impl fmt::Display for FileTimeDiff {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s_diff = self.new_time.seconds() - self.old_time.seconds();
if s_diff >= 1 {
fmt::Display::fmt(
&humantime::Duration::from(std::time::Duration::from_secs(s_diff as u64)),
f,
)
} else {
// format nanoseconds as it is, humantime would display ms, us and ns
let ns_diff = self.new_time.nanoseconds() - self.old_time.nanoseconds();
write!(f, "{ns_diff}ns")
}
}
}
#[derive(Copy, Clone)]
struct After {
old_time: FileTime,
new_time: FileTime,
what: &'static str,
}
impl fmt::Display for After {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self {
old_time,
new_time,
what,
} = *self;
let diff = FileTimeDiff { old_time, new_time };
write!(f, "{new_time}, {diff} after {what} at {old_time}")
}
}
impl DirtyReason {
fn after(old_time: FileTime, new_time: FileTime, what: &'static str) -> After {
After {
old_time,
new_time,
what,
}
}
pub fn present_to(&self, s: &mut Shell, unit: &Unit, root: &Path) -> CargoResult<()> {
match self {
DirtyReason::RustcChanged => s.dirty_because(unit, "the toolchain changed"),
DirtyReason::FeaturesChanged { .. } => {
s.dirty_because(unit, "the list of features changed")
}
DirtyReason::TargetConfigurationChanged => {
s.dirty_because(unit, "the target configuration changed")
}
DirtyReason::PathToSourceChanged => {
s.dirty_because(unit, "the path to the source changed")
}
DirtyReason::ProfileConfigurationChanged => {
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")
}
DirtyReason::CompileKindChanged => {
s.dirty_because(unit, "the rustc compile kind changed")
}
DirtyReason::LocalLengthsChanged => {
s.dirty_because(unit, "the local lengths changed")?;
s.note(
"This could happen because of added/removed `cargo:rerun-if` instructions in the build script",
)?;
Ok(())
}
DirtyReason::PrecalculatedComponentsChanged { .. } => {
s.dirty_because(unit, "the precalculated components changed")
}
DirtyReason::DepInfoOutputChanged { .. } => {
s.dirty_because(unit, "the dependency info output changed")
}
DirtyReason::RerunIfChangedOutputFileChanged { .. } => {
s.dirty_because(unit, "rerun-if-changed output file path changed")
}
DirtyReason::RerunIfChangedOutputPathsChanged { .. } => {
s.dirty_because(unit, "the rerun-if-changed instructions changed")
}
DirtyReason::EnvVarsChanged { .. } => {
s.dirty_because(unit, "the environment variables changed")
}
DirtyReason::EnvVarChanged { name, .. } => {
s.dirty_because(unit, format_args!("the env variable {name} changed"))
}
DirtyReason::LocalFingerprintTypeChanged { .. } => {
s.dirty_because(unit, "the local fingerprint type changed")
}
DirtyReason::NumberOfDependenciesChanged { old, new } => s.dirty_because(
unit,
format_args!("number of dependencies changed ({old} => {new})",),
),
DirtyReason::UnitDependencyNameChanged { old, new } => s.dirty_because(
unit,
format_args!("name of dependency changed ({old} => {new})"),
),
DirtyReason::UnitDependencyInfoChanged { .. } => {
s.dirty_because(unit, "dependency info changed")
}
DirtyReason::FsStatusOutdated(status) => match status {
FsStatus::Stale => s.dirty_because(unit, "stale, unknown reason"),
FsStatus::StaleItem(item) => match item {
StaleItem::MissingFile(missing_file) => {
let file = missing_file.strip_prefix(root).unwrap_or(&missing_file);
s.dirty_because(
unit,
format_args!("the file `{}` is missing", file.display()),
)
}
StaleItem::ChangedFile {
stale,
stale_mtime,
reference_mtime,
..
} => {
let file = stale.strip_prefix(root).unwrap_or(&stale);
let after = Self::after(*reference_mtime, *stale_mtime, "last build");
s.dirty_because(
unit,
format_args!("the file `{}` has changed ({after})", file.display()),
)
}
StaleItem::ChangedEnv { var, .. } => s.dirty_because(
unit,
format_args!("the environment variable {var} changed"),
),
},
FsStatus::StaleDependency {
name,
dep_mtime,
max_mtime,
..
} => {
let after = Self::after(*max_mtime, *dep_mtime, "last build");
s.dirty_because(
unit,
format_args!("the dependency {name} was rebuilt ({after})"),
)
}
FsStatus::StaleDepFingerprint { name } => {
s.dirty_because(unit, format_args!("the dependency {name} was rebuilt"))
}
FsStatus::UpToDate { .. } => {
unreachable!()
}
},
DirtyReason::NothingObvious => {
// See comment in fingerprint compare method.
s.dirty_because(unit, "the fingerprint comparison turned up nothing obvious")
}
DirtyReason::Forced => s.dirty_because(unit, "forced"),
}
}
}

View File

@ -2,6 +2,7 @@ use std::fmt;
use std::mem;
use super::job_queue::JobState;
use crate::core::compiler::fingerprint::DirtyReason;
use crate::util::CargoResult;
pub struct Job {
@ -49,10 +50,10 @@ impl Job {
}
/// Creates a new job representing a unit of work.
pub fn new_dirty(work: Work) -> Job {
pub fn new_dirty(work: Work, dirty_reason: Option<DirtyReason>) -> Job {
Job {
work,
fresh: Freshness::Dirty,
fresh: Freshness::Dirty(dirty_reason),
}
}
@ -65,8 +66,8 @@ impl Job {
/// Returns whether this job was fresh/dirty, where "fresh" means we're
/// likely to perform just some small bookkeeping where "dirty" means we'll
/// probably do something slow like invoke rustc.
pub fn freshness(&self) -> Freshness {
self.fresh
pub fn freshness(&self) -> &Freshness {
&self.fresh
}
pub fn before(&mut self, next: Work) {
@ -85,8 +86,18 @@ impl fmt::Debug for Job {
///
/// A fresh package does not necessarily need to be rebuilt (unless a dependency
/// was also rebuilt), and a dirty package must always be rebuilt.
#[derive(PartialEq, Eq, Debug, Clone, Copy)]
#[derive(Debug, Clone)]
pub enum Freshness {
Fresh,
Dirty,
Dirty(Option<DirtyReason>),
}
impl Freshness {
pub fn is_dirty(&self) -> bool {
matches!(self, Freshness::Dirty(_))
}
pub fn is_fresh(&self) -> bool {
matches!(self, Freshness::Fresh)
}
}

View File

@ -54,7 +54,7 @@ use std::collections::{BTreeMap, HashMap, HashSet};
use std::fmt::Write as _;
use std::io;
use std::marker;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::thread::{self, Scope};
use std::time::Duration;
@ -676,7 +676,7 @@ impl<'cfg> DrainState<'cfg> {
// NOTE: An error here will drop the job without starting it.
// That should be OK, since we want to exit as soon as
// possible during an error.
self.note_working_on(cx.bcx.config, &unit, job.freshness())?;
self.note_working_on(cx.bcx.config, cx.bcx.ws.root(), &unit, job.freshness())?;
}
self.run(&unit, job, cx, scope);
}
@ -1116,7 +1116,7 @@ impl<'cfg> DrainState<'cfg> {
assert!(self.active.insert(id, unit.clone()).is_none());
let messages = self.messages.clone();
let fresh = job.freshness();
let is_fresh = job.freshness().is_fresh();
let rmeta_required = cx.rmeta_required(unit);
let doit = move |state: JobState<'_, '_>| {
@ -1167,8 +1167,8 @@ impl<'cfg> DrainState<'cfg> {
}
};
match fresh {
Freshness::Fresh => {
match is_fresh {
true => {
self.timings.add_fresh();
// Running a fresh job on the same thread is often much faster than spawning a new
// thread to run the job.
@ -1180,7 +1180,7 @@ impl<'cfg> DrainState<'cfg> {
_marker: marker::PhantomData,
});
}
Freshness::Dirty => {
false => {
self.timings.add_dirty();
scope.spawn(move || {
doit(JobState {
@ -1355,8 +1355,9 @@ impl<'cfg> DrainState<'cfg> {
fn note_working_on(
&mut self,
config: &Config,
ws_root: &Path,
unit: &Unit,
fresh: Freshness,
fresh: &Freshness,
) -> CargoResult<()> {
if (self.compiled.contains(&unit.pkg.package_id())
&& !unit.mode.is_doc()
@ -1370,7 +1371,13 @@ impl<'cfg> DrainState<'cfg> {
match fresh {
// Any dirty stage which runs at least one command gets printed as
// being a compiled package.
Dirty => {
Dirty(dirty_reason) => {
if let Some(reason) = dirty_reason {
config
.shell()
.verbose(|shell| reason.present_to(shell, unit, ws_root))?;
}
if unit.mode.is_doc() {
self.documented.insert(unit.pkg.package_id());
config.shell().status("Documenting", &unit.pkg)?;

View File

@ -45,6 +45,7 @@ pub use self::compile_kind::{CompileKind, CompileTarget};
pub use self::context::{Context, Metadata};
pub use self::crate_type::CrateType;
pub use self::custom_build::{BuildOutput, BuildScriptOutputs, BuildScripts};
pub(crate) use self::fingerprint::DirtyReason;
pub use self::job::Freshness;
use self::job::{Job, Work};
use self::job_queue::{JobQueue, JobState};
@ -164,11 +165,11 @@ fn compile<'cfg>(
// We run these targets later, so this is just a no-op for now.
Job::new_fresh()
} else if build_plan {
Job::new_dirty(rustc(cx, unit, &exec.clone())?)
Job::new_dirty(rustc(cx, unit, &exec.clone())?, None)
} else {
let force = exec.force_rebuild(unit) || force_rebuild;
let mut job = fingerprint::prepare_target(cx, unit, force)?;
job.before(if job.freshness() == Freshness::Dirty {
job.before(if job.freshness().is_dirty() {
let work = if unit.mode.is_doc() || unit.mode.is_doc_scrape() {
rustdoc(cx, unit)?
} else {

View File

@ -3,7 +3,7 @@ use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::{env, fs};
use crate::core::compiler::{CompileKind, DefaultExecutor, Executor, Freshness, UnitOutput};
use crate::core::compiler::{CompileKind, DefaultExecutor, Executor, UnitOutput};
use crate::core::{Dependency, Edition, Package, PackageId, Source, SourceId, Workspace};
use crate::ops::CompileFilter;
use crate::ops::{common_for_install_and_uninstall::*, FilterRule};
@ -683,7 +683,7 @@ fn is_installed(
let tracker = InstallTracker::load(config, root)?;
let (freshness, _duplicates) =
tracker.check_upgrade(dst, pkg, force, opts, target, &rustc.verbose_version)?;
Ok(freshness == Freshness::Fresh)
Ok(freshness.is_fresh())
}
/// Checks if vers can only be satisfied by exactly one version of a package in a registry, and it's

View File

@ -11,7 +11,7 @@ use ops::FilterRule;
use serde::{Deserialize, Serialize};
use toml_edit::easy as toml;
use crate::core::compiler::Freshness;
use crate::core::compiler::{DirtyReason, Freshness};
use crate::core::Target;
use crate::core::{Dependency, FeatureValue, Package, PackageId, QueryKind, Source, SourceId};
use crate::ops::{self, CompileFilter, CompileOptions};
@ -170,7 +170,7 @@ impl InstallTracker {
// Check if any tracked exe's are already installed.
let duplicates = self.find_duplicates(dst, &exes);
if force || duplicates.is_empty() {
return Ok((Freshness::Dirty, duplicates));
return Ok((Freshness::Dirty(Some(DirtyReason::Forced)), duplicates));
}
// Check if all duplicates come from packages of the same name. If
// there are duplicates from other packages, then --force will be
@ -200,7 +200,7 @@ impl InstallTracker {
let source_id = pkg.package_id().source_id();
if source_id.is_path() {
// `cargo install --path ...` is always rebuilt.
return Ok((Freshness::Dirty, duplicates));
return Ok((Freshness::Dirty(Some(DirtyReason::Forced)), duplicates));
}
let is_up_to_date = |dupe_pkg_id| {
let info = self
@ -224,7 +224,7 @@ impl InstallTracker {
if matching_duplicates.iter().all(is_up_to_date) {
Ok((Freshness::Fresh, duplicates))
} else {
Ok((Freshness::Dirty, duplicates))
Ok((Freshness::Dirty(Some(DirtyReason::Forced)), duplicates))
}
} else {
// Format the error message.

View File

@ -2321,8 +2321,10 @@ fn calc_bin_artifact_fingerprint() {
.masquerade_as_nightly_cargo(&["bindeps"])
.with_stderr(
"\
[DIRTY] bar v0.5.0 ([CWD]/bar): the file `bar/src/main.rs` has changed ([..])
[COMPILING] bar v0.5.0 ([CWD]/bar)
[RUNNING] `rustc --crate-name bar [..]`
[DIRTY] foo v0.1.0 ([CWD]): the dependency bar was rebuilt
[CHECKING] foo v0.1.0 ([CWD])
[RUNNING] `rustc --crate-name foo [..]`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]

View File

@ -1214,6 +1214,7 @@ fn only_rerun_build_script() {
p.cargo("build -v")
.with_stderr(
"\
[DIRTY] foo v0.5.0 ([CWD]): the precalculated components changed
[COMPILING] foo v0.5.0 ([CWD])
[RUNNING] `[..]/build-script-build`
[RUNNING] `rustc --crate-name foo [..]`
@ -1320,6 +1321,7 @@ fn testing_and_such() {
p.cargo("test -vj1")
.with_stderr(
"\
[DIRTY] foo v0.5.0 ([CWD]): the precalculated components changed
[COMPILING] foo v0.5.0 ([CWD])
[RUNNING] `[..]/build-script-build`
[RUNNING] `rustc --crate-name foo [..]`
@ -1717,6 +1719,7 @@ fn out_dir_is_preserved() {
p.cargo("build -v")
.with_stderr(
"\
[DIRTY] foo [..]: the file `build.rs` has changed ([..])
[COMPILING] foo [..]
[RUNNING] `rustc --crate-name build_script_build [..]
[RUNNING] `[..]/build-script-build`
@ -1741,6 +1744,7 @@ fn out_dir_is_preserved() {
p.cargo("build -v")
.with_stderr(
"\
[DIRTY] foo [..]: the precalculated components changed
[COMPILING] foo [..]
[RUNNING] `[..]build-script-build`
[RUNNING] `rustc --crate-name foo [..]
@ -3005,6 +3009,7 @@ fn changing_an_override_invalidates() {
p.cargo("build -v")
.with_stderr(
"\
[DIRTY] foo v0.5.0 ([..]): the precalculated components changed
[COMPILING] foo v0.5.0 ([..]
[RUNNING] `rustc [..] -L native=bar`
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
@ -3295,6 +3300,7 @@ fn rebuild_only_on_explicit_paths() {
p.cargo("build -v")
.with_stderr(
"\
[DIRTY] foo v0.5.0 ([..]): the file `foo` is missing
[COMPILING] foo v0.5.0 ([..])
[RUNNING] `[..]/build-script-build`
[RUNNING] `rustc [..] src/lib.rs [..]`
@ -3313,6 +3319,7 @@ fn rebuild_only_on_explicit_paths() {
p.cargo("build -v")
.with_stderr(
"\
[DIRTY] foo v0.5.0 ([..]): the file `foo` has changed ([..])
[COMPILING] foo v0.5.0 ([..])
[RUNNING] `[..]/build-script-build`
[RUNNING] `rustc [..] src/lib.rs [..]`
@ -3351,6 +3358,7 @@ fn rebuild_only_on_explicit_paths() {
p.cargo("build -v")
.with_stderr(
"\
[DIRTY] foo v0.5.0 ([..]): the file `foo` has changed ([..])
[COMPILING] foo v0.5.0 ([..])
[RUNNING] `[..]/build-script-build`
[RUNNING] `rustc [..] src/lib.rs [..]`
@ -3360,11 +3368,12 @@ fn rebuild_only_on_explicit_paths() {
.run();
// .. as does deleting a file
println!("run foo delete");
println!("run bar delete");
fs::remove_file(p.root().join("bar")).unwrap();
p.cargo("build -v")
.with_stderr(
"\
[DIRTY] foo v0.5.0 ([..]): the file `bar` is missing
[COMPILING] foo v0.5.0 ([..])
[RUNNING] `[..]/build-script-build`
[RUNNING] `rustc [..] src/lib.rs [..]`
@ -4692,12 +4701,29 @@ fn rerun_if_directory() {
)
.build();
let dirty = || {
p.cargo("check")
.with_stderr(
"[COMPILING] foo [..]\n\
[FINISHED] [..]",
)
let dirty = |dirty_line: &str, compile_build_script: bool| {
let mut dirty_line = dirty_line.to_string();
if !dirty_line.is_empty() {
dirty_line.push('\n');
}
let compile_build_script_line = if compile_build_script {
"[RUNNING] `rustc --crate-name build_script_build [..]\n"
} else {
""
};
p.cargo("check -v")
.with_stderr(format!(
"\
{dirty_line}\
[COMPILING] foo [..]
{compile_build_script_line}\
[RUNNING] `[..]build-script-build[..]`
[RUNNING] `rustc --crate-name foo [..]
[FINISHED] [..]",
))
.run();
};
@ -4706,10 +4732,13 @@ fn rerun_if_directory() {
};
// Start with a missing directory.
dirty();
dirty("", true);
// Because the directory doesn't exist, it will trigger a rebuild every time.
// https://github.com/rust-lang/cargo/issues/6003
dirty();
dirty(
"[DIRTY] foo v0.1.0 ([..]): the file `somedir` is missing",
false,
);
if is_coarse_mtime() {
sleep_ms(1000);
@ -4717,7 +4746,10 @@ fn rerun_if_directory() {
// Empty directory.
fs::create_dir(p.root().join("somedir")).unwrap();
dirty();
dirty(
"[DIRTY] foo v0.1.0 ([..]): the file `somedir` has changed ([..])",
false,
);
fresh();
if is_coarse_mtime() {
@ -4727,7 +4759,10 @@ fn rerun_if_directory() {
// Add a file.
p.change_file("somedir/foo", "");
p.change_file("somedir/bar", "");
dirty();
dirty(
"[DIRTY] foo v0.1.0 ([..]): the file `somedir` has changed ([..])",
false,
);
fresh();
if is_coarse_mtime() {
@ -4736,7 +4771,10 @@ fn rerun_if_directory() {
// Add a symlink.
p.symlink("foo", "somedir/link");
dirty();
dirty(
"[DIRTY] foo v0.1.0 ([..]): the file `somedir` has changed ([..])",
false,
);
fresh();
if is_coarse_mtime() {
@ -4746,7 +4784,10 @@ fn rerun_if_directory() {
// Move the symlink.
fs::remove_file(p.root().join("somedir/link")).unwrap();
p.symlink("bar", "somedir/link");
dirty();
dirty(
"[DIRTY] foo v0.1.0 ([..]): the file `somedir` has changed ([..])",
false,
);
fresh();
if is_coarse_mtime() {
@ -4755,7 +4796,10 @@ fn rerun_if_directory() {
// Remove a file.
fs::remove_file(p.root().join("somedir/foo")).unwrap();
dirty();
dirty(
"[DIRTY] foo v0.1.0 ([..]): the file `somedir` has changed ([..])",
false,
);
fresh();
}

View File

@ -74,6 +74,7 @@ fn works_with_cli() {
.masquerade_as_nightly_cargo(&["config-include"])
.with_stderr(
"\
[DIRTY] foo v0.0.1 ([..]): the rustflags changed
[COMPILING] foo v0.0.1 [..]
[RUNNING] `rustc [..]-W unsafe-code -W unused`
[FINISHED] [..]

View File

@ -480,11 +480,14 @@ fn no_feature_doesnt_build() {
.run();
p.process(&p.bin("foo")).with_stdout("").run();
p.cargo("build --features bar")
p.cargo("build --features bar -v")
.with_stderr(
"\
[COMPILING] bar v0.0.1 ([CWD]/bar)
[RUNNING] `rustc --crate-name bar [..]
[DIRTY-MSVC] foo v0.0.1 ([CWD]): the list of features changed
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc --crate-name foo [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
@ -537,10 +540,12 @@ fn default_feature_pulled_in() {
.run();
p.process(&p.bin("foo")).with_stdout("bar\n").run();
p.cargo("build --no-default-features")
p.cargo("build --no-default-features -v")
.with_stderr(
"\
[DIRTY-MSVC] foo v0.0.1 ([CWD]): the list of features changed
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc --crate-name foo [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)

View File

@ -38,10 +38,12 @@ fn modifying_and_moving() {
p.root().join("target").move_into_the_past();
p.change_file("src/a.rs", "#[allow(unused)]fn main() {}");
p.cargo("build")
p.cargo("build -v")
.with_stderr(
"\
[DIRTY] foo v0.0.1 ([CWD]): the file `src/a.rs` has changed ([..])
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc --crate-name foo [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
@ -83,10 +85,12 @@ fn modify_only_some_files() {
lib.move_into_the_past();
// Make sure the binary is rebuilt, not the lib
p.cargo("build")
p.cargo("build -v")
.with_stderr(
"\
[DIRTY] foo v0.0.1 ([CWD]): the file `src/b.rs` has changed ([..])
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc --crate-name foo [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
@ -147,6 +151,7 @@ fn rebuild_sub_package_then_while_package() {
p.cargo("build -pb -v")
.with_stderr(
"\
[DIRTY] b v0.0.1 ([..]): the file `b/src/lib.rs` has changed ([..])
[COMPILING] b [..]
[RUNNING] `rustc --crate-name b [..]
[FINISHED] dev [..]
@ -163,8 +168,10 @@ fn rebuild_sub_package_then_while_package() {
.with_stderr(
"\
[FRESH] b [..]
[DIRTY] a [..]: the dependency b was rebuilt ([..])
[COMPILING] a [..]
[RUNNING] `rustc --crate-name a [..]
[DIRTY] foo [..]: the dependency b was rebuilt ([..])
[COMPILING] foo [..]
[RUNNING] `rustc --crate-name foo [..]
[FINISHED] dev [..]
@ -492,21 +499,38 @@ fn changing_bin_features_caches_targets() {
/* Targets should be cached from the first build */
let mut e = p.cargo("build");
let mut e = p.cargo("build -v");
// MSVC does not include hash in binary filename, so it gets recompiled.
if cfg!(target_env = "msvc") {
e.with_stderr("[COMPILING] foo[..]\n[FINISHED] dev[..]");
e.with_stderr(
"\
[DIRTY] foo v0.0.1 ([..]): the list of features changed
[COMPILING] foo[..]
[RUNNING] `rustc [..]
[FINISHED] dev[..]",
);
} else {
e.with_stderr("[FINISHED] dev[..]");
e.with_stderr("[FRESH] foo v0.0.1 ([..])\n[FINISHED] dev[..]");
}
e.run();
p.rename_run("foo", "off2").with_stdout("feature off").run();
let mut e = p.cargo("build --features foo");
let mut e = p.cargo("build --features foo -v");
if cfg!(target_env = "msvc") {
e.with_stderr("[COMPILING] foo[..]\n[FINISHED] dev[..]");
e.with_stderr(
"\
[DIRTY] foo v0.0.1 ([..]): the list of features changed
[COMPILING] foo[..]
[RUNNING] `rustc [..]
[FINISHED] dev[..]",
);
} else {
e.with_stderr("[FINISHED] dev[..]");
e.with_stderr(
"\
[FRESH] foo v0.0.1 ([..])
[FINISHED] dev[..]",
);
}
e.run();
p.rename_run("foo", "on2").with_stdout("feature on").run();
@ -843,11 +867,13 @@ fn rebuild_if_environment_changes() {
"#,
);
p.cargo("run")
p.cargo("run -v")
.with_stdout("new desc")
.with_stderr(
"\
[DIRTY] foo v0.0.1 ([CWD]): the metadata changed
[COMPILING] foo v0.0.1 ([CWD])
[RUNNING] `rustc [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
[RUNNING] `target/debug/foo[EXE]`
",
@ -1186,23 +1212,43 @@ fn changing_rustflags_is_cached() {
let p = project().file("src/lib.rs", "").build();
// This isn't ever cached, we always have to recompile
for _ in 0..2 {
p.cargo("build")
.with_stderr(
"\
p.cargo("build")
.with_stderr(
"\
[COMPILING] foo v0.0.1 ([..])
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
)
.run();
p.cargo("build")
.env("RUSTFLAGS", "-C linker=cc")
.with_stderr(
"\
)
.run();
p.cargo("build -v")
.env("RUSTFLAGS", "-C linker=cc")
.with_stderr(
"\
[DIRTY] foo v0.0.1 ([..]): the rustflags changed
[COMPILING] foo v0.0.1 ([..])
[RUNNING] `rustc [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
)
.run();
}
)
.run();
p.cargo("build -v")
.with_stderr(
"\
[DIRTY] foo v0.0.1 ([..]): the rustflags changed
[COMPILING] foo v0.0.1 ([..])
[RUNNING] `rustc [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
)
.run();
p.cargo("build -v")
.env("RUSTFLAGS", "-C linker=cc")
.with_stderr(
"\
[DIRTY] foo v0.0.1 ([..]): the rustflags changed
[COMPILING] foo v0.0.1 ([..])
[RUNNING] `rustc [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]",
)
.run();
}
#[cargo_test]
@ -1480,12 +1526,18 @@ fn bust_patched_dep() {
sleep_ms(1000);
}
p.cargo("build")
p.cargo("build -v")
.with_stderr(
"\
[DIRTY] registry1 v0.1.0 ([..]): the file `reg1new/src/lib.rs` has changed ([..])
[COMPILING] registry1 v0.1.0 ([..])
[RUNNING] `rustc [..]
[DIRTY] registry2 v0.1.0: the dependency registry1 was rebuilt
[COMPILING] registry2 v0.1.0
[RUNNING] `rustc [..]
[DIRTY] foo v0.0.1 ([..]): the dependency registry2 was rebuilt
[COMPILING] foo v0.0.1 ([..])
[RUNNING] `rustc [..]
[FINISHED] [..]
",
)
@ -1598,10 +1650,13 @@ fn rebuild_on_mid_build_file_modification() {
)
.run();
p.cargo("build")
p.cargo("build -v")
.with_stderr(
"\
[FRESH] proc_macro_dep v0.1.0 ([..]/proc_macro_dep)
[DIRTY] root v0.1.0 ([..]/root): the file `root/src/lib.rs` has changed ([..])
[COMPILING] root v0.1.0 ([..]/root)
[RUNNING] `rustc [..]
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
",
)
@ -1877,6 +1932,7 @@ fn simulated_docker_deps_stay_cached() {
[FRESH] regdep_env [..]
[FRESH] regdep_old_style [..]
[FRESH] regdep_rerun [..]
[DIRTY] foo [..]: the precalculated components changed
[COMPILING] foo [..]
[RUNNING] [..]/foo-[..]/build-script-build[..]
[RUNNING] `rustc --crate-name foo[..]
@ -2108,6 +2164,7 @@ fn rerun_if_changes() {
.env("FOO", "1")
.with_stderr(
"\
[DIRTY] foo [..]: the env variable FOO changed
[COMPILING] foo [..]
[RUNNING] `[..]build-script-build`
[RUNNING] `rustc [..]
@ -2125,6 +2182,7 @@ fn rerun_if_changes() {
.env("BAR", "1")
.with_stderr(
"\
[DIRTY] foo [..]: the env variable BAR changed
[COMPILING] foo [..]
[RUNNING] `[..]build-script-build`
[RUNNING] `rustc [..]
@ -2142,6 +2200,7 @@ fn rerun_if_changes() {
.env("BAR", "2")
.with_stderr(
"\
[DIRTY] foo [..]: the env variable FOO changed
[COMPILING] foo [..]
[RUNNING] `[..]build-script-build`
[RUNNING] `rustc [..]
@ -2439,12 +2498,15 @@ fn linking_interrupted() {
drop(rustc_conn.read_exact(&mut buf));
// Build again, shouldn't be fresh.
p.cargo("test --test t1")
p.cargo("test --test t1 -v")
.with_stderr(
"\
[DIRTY] foo v0.0.1 ([..]): the config settings changed
[COMPILING] foo [..]
[RUNNING] `rustc --crate-name foo [..]
[RUNNING] `rustc --crate-name t1 [..]
[FINISHED] [..]
[RUNNING] tests/t1.rs (target/debug/deps/t1[..])
[RUNNING] `[..]target/debug/deps/t1-[..][EXE]`
",
)
.run();
@ -2513,25 +2575,43 @@ fn env_in_code_causes_rebuild() {
.env_remove("FOO")
.with_stderr("[FINISHED] [..]")
.run();
p.cargo("build")
p.cargo("build -v")
.env("FOO", "bar")
.with_stderr("[COMPILING][..]\n[FINISHED][..]")
.with_stderr(
"\
[DIRTY] foo [..]: the environment variable FOO changed
[COMPILING][..]
[RUNNING] `rustc [..]
[FINISHED][..]",
)
.run();
p.cargo("build")
.env("FOO", "bar")
.with_stderr("[FINISHED][..]")
.run();
p.cargo("build")
p.cargo("build -v")
.env("FOO", "baz")
.with_stderr("[COMPILING][..]\n[FINISHED][..]")
.with_stderr(
"\
[DIRTY] foo [..]: the environment variable FOO changed
[COMPILING][..]
[RUNNING] `rustc [..]
[FINISHED][..]",
)
.run();
p.cargo("build")
.env("FOO", "baz")
.with_stderr("[FINISHED][..]")
.run();
p.cargo("build")
p.cargo("build -v")
.env_remove("FOO")
.with_stderr("[COMPILING][..]\n[FINISHED][..]")
.with_stderr(
"\
[DIRTY] foo [..]: the environment variable FOO changed
[COMPILING][..]
[RUNNING] `rustc [..]
[FINISHED][..]",
)
.run();
p.cargo("build")
.env_remove("FOO")
@ -2616,6 +2696,7 @@ fn cargo_env_changes() {
.arg("-v")
.with_stderr(
"\
[DIRTY] foo v1.0.0 ([..]): the environment variable CARGO changed
[CHECKING] foo [..]
[RUNNING] `rustc [..]
[FINISHED] [..]

View File

@ -2521,6 +2521,7 @@ fn include_overrides_gitignore() {
p.cargo("build -v")
.with_stderr(
"\
[DIRTY] foo v0.5.0 ([..]): the precalculated components changed
[COMPILING] foo v0.5.0 ([..])
[RUNNING] `[..]build-script-build[..]`
[RUNNING] `rustc --crate-name foo src/lib.rs [..]`

View File

@ -595,6 +595,7 @@ fn dylib() {
[COMPILING] registry-shared v0.0.1
[FRESH] registry v0.0.1
[RUNNING] `rustc --crate-name registry_shared [..]-C embed-bitcode=no[..]
[DIRTY] bar v0.0.0 ([..]): dependency info changed
[COMPILING] bar [..]
[RUNNING] `rustc --crate-name bar [..]--crate-type dylib [..]-C embed-bitcode=no[..]
[FINISHED] [..]
@ -612,6 +613,7 @@ fn dylib() {
[FRESH] registry-shared v0.0.1
[COMPILING] registry v0.0.1
[RUNNING] `rustc --crate-name registry [..]
[DIRTY] bar v0.0.0 ([..]): dependency info changed
[COMPILING] bar [..]
[RUNNING] `rustc --crate-name bar [..]--crate-type dylib [..]-C embed-bitcode=no[..]
[RUNNING] `rustc --crate-name bar [..]-C lto [..]--test[..]

View File

@ -639,6 +639,7 @@ fn rustc_fingerprint() {
.with_stderr_does_not_contain("-C debug-assertions")
.with_stderr(
"\
[DIRTY] foo [..]: the profile configuration changed
[COMPILING] foo [..]
[RUNNING] `rustc [..]
[FINISHED] [..]

View File

@ -2661,6 +2661,7 @@ fn bin_does_not_rebuild_tests() {
p.cargo("test -v --no-run")
.with_stderr(
"\
[DIRTY] foo v0.0.1 ([..]): the file `src/main.rs` has changed ([..])
[COMPILING] foo v0.0.1 ([..])
[RUNNING] `rustc [..] src/main.rs [..]`
[RUNNING] `rustc [..] src/main.rs [..]`