Skip to content

Commit 811a4f0

Browse files
committed
Auto merge of #6832 - alexcrichton:freshness, r=ehuss
Remove `Freshness` from `DependencyQueue` Ever since the inception of Cargo and the advent of incremental compilation at the crate level via Cargo, Cargo has tracked whether it needs to recompile something at a unit level in its "dependency queue" which manages when items are ready for execution. Over time we've fixed lots and lots of bugs related to incremental compilation, and perhaps one of the most impactful realizations was that the model Cargo started with fundamentally doesn't handle interrupting Cargo halfway through and resuming the build later. The previous model relied upon implicitly propagating "dirtiness" based on whether the one of the dependencies of a build was rebuilt or not. This information is not available, however, if Cargo is interrupted and resumed (or performs a subset of steps and then later performs more). We've fixed this in a number of places historically but the purpose of this commit is to put a nail in this coffin once and for all. Implicit propagation of whether a unit is fresh or dirty is no longer present at all. Instead Cargo should always know, irrespective of it's in-memory state, whether a unit needs to be recompiled or not. This commit actually turns up a few bugs in the test suite, so later commits will be targeted at fixing this. Note that this required a good deal of work on the `fingerprint` module to fix some longstanding bugs (like #6780) and some serious hoops had to be jumped through for others (like #6779). While these were fallout from this change they weren't necessarily the primary motivation, but rather to help make `fingerprints` a bit more straightforward in what's an already confusing system! Closes #6780
2 parents 449411f + 22691b9 commit 811a4f0

32 files changed

+1254
-658
lines changed

src/bin/cargo/commands/owner.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,14 @@ pub fn cli() -> App {
77
.about("Manage the owners of a crate on the registry")
88
.arg(opt("quiet", "No output printed to stdout").short("q"))
99
.arg(Arg::with_name("crate"))
10-
.arg(multi_opt("add", "LOGIN", "Name of a user or team to invite as an owner").short("a"))
10+
.arg(
11+
multi_opt(
12+
"add",
13+
"LOGIN",
14+
"Name of a user or team to invite as an owner",
15+
)
16+
.short("a"),
17+
)
1118
.arg(
1219
multi_opt(
1320
"remove",

src/bin/cargo/commands/test.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ pub fn cli() -> App {
1919
.last(true),
2020
)
2121
.arg(
22-
opt("quiet", "Display one character per test instead of one line")
23-
.short("q")
22+
opt(
23+
"quiet",
24+
"Display one character per test instead of one line",
25+
)
26+
.short("q"),
2427
)
2528
.arg_targets_all(
2629
"Test only this package's library unit tests",
@@ -131,9 +134,9 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
131134
} else if test_name.is_some() {
132135
if let CompileFilter::Default { .. } = compile_opts.filter {
133136
compile_opts.filter = ops::CompileFilter::new(
134-
LibRule::Default, // compile the library, so the unit tests can be run filtered
135-
FilterRule::All, // compile the binaries, so the unit tests in binaries can be run filtered
136-
FilterRule::All, // compile the tests, so the integration tests can be run filtered
137+
LibRule::Default, // compile the library, so the unit tests can be run filtered
138+
FilterRule::All, // compile the binaries, so the unit tests in binaries can be run filtered
139+
FilterRule::All, // compile the tests, so the integration tests can be run filtered
137140
FilterRule::none(), // specify --examples to unit test binaries filtered
138141
FilterRule::none(), // specify --benches to unit test benchmarks filtered
139142
); // also, specify --doc to run doc tests filtered

src/bin/cargo/commands/version.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ use crate::command_prelude::*;
33
use crate::cli;
44

55
pub fn cli() -> App {
6-
subcommand("version").about("Show version information")
6+
subcommand("version")
7+
.about("Show version information")
78
.arg(opt("quiet", "No output printed to stdout").short("q"))
89
}
910

src/cargo/core/compiler/build_context/mod.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,7 @@ impl TargetConfig {
265265
}
266266
"rustc-cdylib-link-arg" => {
267267
let args = value.list(k)?;
268-
output
269-
.linker_args
270-
.extend(args.iter().map(|v| v.0.clone()));
268+
output.linker_args.extend(args.iter().map(|v| v.0.clone()));
271269
}
272270
"rustc-cfg" => {
273271
let list = value.list(k)?;

src/cargo/core/compiler/context/compilation_files.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -166,8 +166,13 @@ impl<'a, 'cfg: 'a> CompilationFiles<'a, 'cfg> {
166166
}
167167
}
168168

169-
/// Returns the root of the build output tree.
169+
/// Returns the root of the build output tree for the target
170170
pub fn target_root(&self) -> &Path {
171+
self.target.as_ref().unwrap_or(&self.host).dest()
172+
}
173+
174+
/// Returns the root of the build output tree for the host
175+
pub fn host_root(&self) -> &Path {
171176
self.host.dest()
172177
}
173178

src/cargo/core/compiler/context/mod.rs

+4-3
Original file line numberDiff line numberDiff line change
@@ -430,9 +430,10 @@ impl<'a, 'cfg> Context<'a, 'cfg> {
430430
path.display()
431431
)
432432
};
433-
let suggestion = "Consider changing their names to be unique or compiling them separately.\n\
434-
This may become a hard error in the future; see \
435-
<https://github.com/rust-lang/cargo/issues/6313>.";
433+
let suggestion =
434+
"Consider changing their names to be unique or compiling them separately.\n\
435+
This may become a hard error in the future; see \
436+
<https://github.com/rust-lang/cargo/issues/6313>.";
436437
let report_collision = |unit: &Unit<'_>,
437438
other_unit: &Unit<'_>,
438439
path: &PathBuf|

src/cargo/core/compiler/custom_build.rs

+80-54
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ use std::sync::{Arc, Mutex};
88
use crate::core::PackageId;
99
use crate::util::errors::{CargoResult, CargoResultExt};
1010
use crate::util::machine_message;
11+
use crate::util::Cfg;
1112
use crate::util::{self, internal, paths, profile};
12-
use crate::util::{Cfg, Freshness};
1313

14-
use super::job::Work;
14+
use super::job::{Freshness, Job, Work};
1515
use super::{fingerprint, Context, Kind, TargetConfig, Unit};
1616

1717
/// Contains the parsed output of a custom build script.
@@ -80,32 +80,19 @@ pub struct BuildDeps {
8080
/// prepare work for. If the requirement is specified as both the target and the
8181
/// host platforms it is assumed that the two are equal and the build script is
8282
/// only run once (not twice).
83-
pub fn prepare<'a, 'cfg>(
84-
cx: &mut Context<'a, 'cfg>,
85-
unit: &Unit<'a>,
86-
) -> CargoResult<(Work, Work, Freshness)> {
83+
pub fn prepare<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult<Job> {
8784
let _p = profile::start(format!(
8885
"build script prepare: {}/{}",
8986
unit.pkg,
9087
unit.target.name()
9188
));
9289

9390
let key = (unit.pkg.package_id(), unit.kind);
94-
let overridden = cx.build_script_overridden.contains(&key);
95-
let (work_dirty, work_fresh) = if overridden {
96-
(Work::noop(), Work::noop())
97-
} else {
98-
build_work(cx, unit)?
99-
};
10091

101-
if cx.bcx.build_config.build_plan {
102-
Ok((work_dirty, work_fresh, Freshness::Dirty))
92+
if cx.build_script_overridden.contains(&key) {
93+
fingerprint::prepare_target(cx, unit, false)
10394
} else {
104-
// Now that we've prep'd our work, build the work needed to manage the
105-
// fingerprint and then start returning that upwards.
106-
let (freshness, dirty, fresh) = fingerprint::prepare_build_cmd(cx, unit)?;
107-
108-
Ok((work_dirty.then(dirty), work_fresh.then(fresh), freshness))
95+
build_work(cx, unit)
10996
}
11097
}
11198

@@ -125,7 +112,7 @@ fn emit_build_output(output: &BuildOutput, package_id: PackageId) {
125112
});
126113
}
127114

128-
fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult<(Work, Work)> {
115+
fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult<Job> {
129116
assert!(unit.mode.is_run_custom_build());
130117
let bcx = &cx.bcx;
131118
let dependencies = cx.dep_targets(unit);
@@ -248,7 +235,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
248235
let output_file = script_run_dir.join("output");
249236
let err_file = script_run_dir.join("stderr");
250237
let root_output_file = script_run_dir.join("root-output");
251-
let host_target_root = cx.files().target_root().to_path_buf();
238+
let host_target_root = cx.files().host_root().to_path_buf();
252239
let all = (
253240
id,
254241
pkg_name.clone(),
@@ -260,22 +247,7 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
260247
let kind = unit.kind;
261248
let json_messages = bcx.build_config.json_messages();
262249
let extra_verbose = bcx.config.extra_verbose();
263-
264-
// Check to see if the build script has already run, and if it has, keep
265-
// track of whether it has told us about some explicit dependencies.
266-
let prev_script_out_dir = paths::read_bytes(&root_output_file)
267-
.and_then(|bytes| util::bytes2path(&bytes))
268-
.unwrap_or_else(|_| script_out_dir.clone());
269-
270-
let prev_output = BuildOutput::parse_file(
271-
&output_file,
272-
&pkg_name,
273-
&prev_script_out_dir,
274-
&script_out_dir,
275-
)
276-
.ok();
277-
let deps = BuildDeps::new(&output_file, prev_output.as_ref());
278-
cx.build_explicit_deps.insert(*unit, deps);
250+
let (prev_output, prev_script_out_dir) = prev_build_output(cx, unit);
279251

280252
fs::create_dir_all(&script_dir)?;
281253
fs::create_dir_all(&script_out_dir)?;
@@ -392,7 +364,17 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
392364
Ok(())
393365
});
394366

395-
Ok((dirty, fresh))
367+
let mut job = if cx.bcx.build_config.build_plan {
368+
Job::new(Work::noop(), Freshness::Dirty)
369+
} else {
370+
fingerprint::prepare_target(cx, unit, false)?
371+
};
372+
if job.freshness() == Freshness::Dirty {
373+
job.before(dirty);
374+
} else {
375+
job.before(fresh);
376+
}
377+
Ok(job)
396378
}
397379

398380
impl BuildState {
@@ -637,22 +619,20 @@ pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> Ca
637619
return Ok(&out[unit]);
638620
}
639621

640-
{
641-
let key = unit
642-
.pkg
643-
.manifest()
644-
.links()
645-
.map(|l| (l.to_string(), unit.kind));
646-
let build_state = &cx.build_state;
647-
if let Some(output) = key.and_then(|k| build_state.overrides.get(&k)) {
648-
let key = (unit.pkg.package_id(), unit.kind);
649-
cx.build_script_overridden.insert(key);
650-
build_state
651-
.outputs
652-
.lock()
653-
.unwrap()
654-
.insert(key, output.clone());
655-
}
622+
let key = unit
623+
.pkg
624+
.manifest()
625+
.links()
626+
.map(|l| (l.to_string(), unit.kind));
627+
let build_state = &cx.build_state;
628+
if let Some(output) = key.and_then(|k| build_state.overrides.get(&k)) {
629+
let key = (unit.pkg.package_id(), unit.kind);
630+
cx.build_script_overridden.insert(key);
631+
build_state
632+
.outputs
633+
.lock()
634+
.unwrap()
635+
.insert(key, output.clone());
656636
}
657637

658638
let mut ret = BuildScripts::default();
@@ -661,6 +641,10 @@ pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> Ca
661641
add_to_link(&mut ret, unit.pkg.package_id(), unit.kind);
662642
}
663643

644+
if unit.mode.is_run_custom_build() {
645+
parse_previous_explicit_deps(cx, unit)?;
646+
}
647+
664648
// We want to invoke the compiler deterministically to be cache-friendly
665649
// to rustc invocation caching schemes, so be sure to generate the same
666650
// set of build script dependency orderings via sorting the targets that
@@ -694,4 +678,46 @@ pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> Ca
694678
scripts.to_link.push((pkg, kind));
695679
}
696680
}
681+
682+
fn parse_previous_explicit_deps<'a, 'cfg>(
683+
cx: &mut Context<'a, 'cfg>,
684+
unit: &Unit<'a>,
685+
) -> CargoResult<()> {
686+
let script_run_dir = cx.files().build_script_run_dir(unit);
687+
let output_file = script_run_dir.join("output");
688+
let (prev_output, _) = prev_build_output(cx, unit);
689+
let deps = BuildDeps::new(&output_file, prev_output.as_ref());
690+
cx.build_explicit_deps.insert(*unit, deps);
691+
Ok(())
692+
}
693+
}
694+
695+
/// Returns the previous parsed `BuildOutput`, if any, from a previous
696+
/// execution.
697+
///
698+
/// Also returns the directory containing the output, typically used later in
699+
/// processing.
700+
fn prev_build_output<'a, 'cfg>(
701+
cx: &mut Context<'a, 'cfg>,
702+
unit: &Unit<'a>,
703+
) -> (Option<BuildOutput>, PathBuf) {
704+
let script_out_dir = cx.files().build_script_out_dir(unit);
705+
let script_run_dir = cx.files().build_script_run_dir(unit);
706+
let root_output_file = script_run_dir.join("root-output");
707+
let output_file = script_run_dir.join("output");
708+
709+
let prev_script_out_dir = paths::read_bytes(&root_output_file)
710+
.and_then(|bytes| util::bytes2path(&bytes))
711+
.unwrap_or_else(|_| script_out_dir.clone());
712+
713+
(
714+
BuildOutput::parse_file(
715+
&output_file,
716+
&unit.pkg.to_string(),
717+
&prev_script_out_dir,
718+
&script_out_dir,
719+
)
720+
.ok(),
721+
prev_script_out_dir,
722+
)
697723
}

0 commit comments

Comments
 (0)