Skip to content

Commit e9428cb

Browse files
committed
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
1 parent f16efff commit e9428cb

12 files changed

+748
-441
lines changed

src/cargo/core/compiler/custom_build.rs

+74-52
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);
@@ -261,21 +248,19 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
261248
let json_messages = bcx.build_config.json_messages();
262249
let extra_verbose = bcx.config.extra_verbose();
263250

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);
251+
// TODO: no duplicate
252+
let prev_script_out_dir = paths::read_bytes(&root_output_file)
253+
.and_then(|bytes| util::bytes2path(&bytes))
254+
.unwrap_or_else(|_| script_out_dir.clone());
255+
256+
// TODO: no duplicate
257+
let prev_output = BuildOutput::parse_file(
258+
&output_file,
259+
&unit.pkg.to_string(),
260+
&prev_script_out_dir,
261+
&script_out_dir,
262+
)
263+
.ok();
279264

280265
fs::create_dir_all(&script_dir)?;
281266
fs::create_dir_all(&script_out_dir)?;
@@ -392,7 +377,17 @@ fn build_work<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoRes
392377
Ok(())
393378
});
394379

395-
Ok((dirty, fresh))
380+
let mut job = if cx.bcx.build_config.build_plan {
381+
Job::new(Work::noop(), Freshness::Dirty)
382+
} else {
383+
fingerprint::prepare_target(cx, unit, false)?
384+
};
385+
if job.freshness() == Freshness::Dirty {
386+
job.before(dirty);
387+
} else {
388+
job.before(fresh);
389+
}
390+
Ok(job)
396391
}
397392

398393
impl BuildState {
@@ -637,22 +632,20 @@ pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> Ca
637632
return Ok(&out[unit]);
638633
}
639634

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-
}
635+
let key = unit
636+
.pkg
637+
.manifest()
638+
.links()
639+
.map(|l| (l.to_string(), unit.kind));
640+
let build_state = &cx.build_state;
641+
if let Some(output) = key.and_then(|k| build_state.overrides.get(&k)) {
642+
let key = (unit.pkg.package_id(), unit.kind);
643+
cx.build_script_overridden.insert(key);
644+
build_state
645+
.outputs
646+
.lock()
647+
.unwrap()
648+
.insert(key, output.clone());
656649
}
657650

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

657+
if unit.mode.is_run_custom_build() {
658+
parse_previous_explicit_deps(cx, unit)?;
659+
}
660+
664661
// We want to invoke the compiler deterministically to be cache-friendly
665662
// to rustc invocation caching schemes, so be sure to generate the same
666663
// set of build script dependency orderings via sorting the targets that
@@ -694,4 +691,29 @@ pub fn build_map<'b, 'cfg>(cx: &mut Context<'b, 'cfg>, units: &[Unit<'b>]) -> Ca
694691
scripts.to_link.push((pkg, kind));
695692
}
696693
}
694+
695+
fn parse_previous_explicit_deps<'a, 'cfg>(cx: &mut Context<'a, 'cfg>, unit: &Unit<'a>) -> CargoResult<()> {
696+
let script_out_dir = cx.files().build_script_out_dir(unit);
697+
let script_run_dir = cx.files().build_script_run_dir(unit);
698+
let root_output_file = script_run_dir.join("root-output");
699+
let output_file = script_run_dir.join("output");
700+
701+
// Check to see if the build script has already run, and if it has, keep
702+
// track of whether it has told us about some explicit dependencies.
703+
let prev_script_out_dir = paths::read_bytes(&root_output_file)
704+
.and_then(|bytes| util::bytes2path(&bytes))
705+
.unwrap_or_else(|_| script_out_dir.clone());
706+
707+
let prev_output = BuildOutput::parse_file(
708+
&output_file,
709+
&unit.pkg.to_string(),
710+
&prev_script_out_dir,
711+
&script_out_dir,
712+
)
713+
.ok();
714+
715+
let deps = BuildDeps::new(&output_file, prev_output.as_ref());
716+
cx.build_explicit_deps.insert(*unit, deps);
717+
Ok(())
718+
}
697719
}

0 commit comments

Comments
 (0)