Skip to content
This repository was archived by the owner on Dec 29, 2022. It is now read-only.

Commit ecafe4c

Browse files
committed
Invalidate build plan only when modifying new package
1 parent 2fd403a commit ecafe4c

File tree

3 files changed

+54
-51
lines changed

3 files changed

+54
-51
lines changed

src/build/cargo.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -120,14 +120,7 @@ fn run_cargo(
120120

121121
let mut restore_env = Environment::push_with_lock(&HashMap::new(), None, lock_guard);
122122

123-
let build_dir = {
124-
let mut compilation_cx = compilation_cx.lock().unwrap();
125-
// Since Cargo build routine will try to regenerate the unit dep graph,
126-
// we need to clear the existing dep graph.
127-
compilation_cx.build_plan = Plan::for_packages(package_arg.clone());
128-
129-
compilation_cx.build_dir.as_ref().unwrap().clone()
130-
};
123+
let build_dir = compilation_cx.lock().unwrap().build_dir.clone().unwrap();
131124

132125
// Note that this may not be equal build_dir when inside a workspace member
133126
let manifest_path = important_paths::find_root_manifest_for_wd(&build_dir)?;
@@ -149,8 +142,8 @@ fn run_cargo(
149142
let ws = Workspace::new(&manifest_path, &config)?;
150143

151144
let (all, packages) = match package_arg {
152-
PackageArg::All => (true, vec![]),
153-
PackageArg::Package(s) => (false, vec![s])
145+
PackageArg::Default => (false, vec![]),
146+
PackageArg::Packages(pkgs) => (false, pkgs.into_iter().collect())
154147
};
155148

156149
// TODO: It might be feasible to keep this CargoOptions structure cached and regenerate
@@ -175,6 +168,14 @@ fn run_cargo(
175168

176169
let spec = Packages::from_flags(all, Vec::new(), packages)?;
177170

171+
let pkg_names = spec.into_package_id_specs(&ws)?.iter()
172+
.map(|pkg_spec| pkg_spec.name().to_owned())
173+
.collect();
174+
175+
// Since Cargo build routine will try to regenerate the unit dep graph,
176+
// we need to clear the existing dep graph.
177+
compilation_cx.lock().unwrap().build_plan = Plan::for_packages(pkg_names);
178+
178179
let compile_opts = CompileOptions {
179180
spec,
180181
filter: CompileFilter::new(

src/build/mod.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use log::{log, trace};
2222

2323
use self::environment::EnvironmentLock;
2424

25-
use std::collections::HashMap;
25+
use std::collections::{HashMap, HashSet};
2626
use std::ffi::OsString;
2727
use std::io::{self, Write};
2828
use std::mem;
@@ -159,11 +159,10 @@ impl CompilationContext {
159159
}
160160

161161
#[derive(Debug, Clone, Eq, PartialEq)]
162+
/// Specified set of packages to be built by Cargo.
162163
pub enum PackageArg {
163-
// --all
164-
All,
165-
// -p ...
166-
Package(String),
164+
Default,
165+
Packages(HashSet<String>),
167166
}
168167

169168
/// Status of the build queue.

src/build/plan.rs

Lines changed: 39 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,17 @@ crate struct Plan {
6060
// An object for finding the package which a file belongs to and this inferring
6161
// a package argument.
6262
package_map: Option<PackageMap>,
63-
/// Packages for which this build plan was prepared.
63+
/// Packages (names) for which this build plan was prepared.
6464
/// Used to detect if the plan can reused when building certain packages.
65-
built_packages: PackageArg,
65+
built_packages: HashSet<String>,
6666
}
6767

6868
impl Plan {
6969
crate fn new() -> Plan {
70-
Self::for_packages(PackageArg::All)
70+
Self::for_packages(HashSet::new())
7171
}
7272

73-
crate fn for_packages(pkgs: PackageArg) -> Plan {
73+
crate fn for_packages(pkgs: HashSet<String>) -> Plan {
7474
Plan {
7575
units: HashMap::new(),
7676
dep_graph: HashMap::new(),
@@ -309,14 +309,29 @@ impl Plan {
309309
self.package_map = Some(PackageMap::new(manifest_path));
310310
}
311311

312-
let package_arg = self.package_map
312+
if !self.is_ready() || requested_cargo {
313+
return WorkStatus::NeedsCargo(PackageArg::Default);
314+
}
315+
316+
let dirty_packages = self.package_map
313317
.as_ref()
314318
.unwrap()
315-
.compute_package_arg(modified);
316-
let package_arg_changed = self.built_packages != package_arg;
319+
.compute_dirty_packages(modified);
320+
321+
let needs_more_packages = dirty_packages
322+
.difference(&self.built_packages)
323+
.next()
324+
.is_some();
317325

318-
if !self.is_ready() || requested_cargo || package_arg_changed {
319-
return WorkStatus::NeedsCargo(package_arg);
326+
let needed_packages = self.built_packages
327+
.union(&dirty_packages)
328+
.cloned()
329+
.collect();
330+
331+
// We modified a file from a packages, that are not included in the
332+
// cached build plan - run Cargo to recreate the build plan including them
333+
if needs_more_packages {
334+
return WorkStatus::NeedsCargo(PackageArg::Packages(needed_packages));
320335
}
321336

322337
let dirties = self.fetch_dirty_units(modified);
@@ -330,13 +345,13 @@ impl Plan {
330345
.iter()
331346
.any(|&(_, ref kind)| *kind == TargetKind::CustomBuild)
332347
{
333-
WorkStatus::NeedsCargo(package_arg)
348+
WorkStatus::NeedsCargo(PackageArg::Packages(needed_packages))
334349
} else {
335350
let graph = self.dirty_rev_dep_graph(&dirties);
336351
trace!("Constructed dirty rev dep graph: {:?}", graph);
337352

338353
if graph.is_empty() {
339-
return WorkStatus::NeedsCargo(package_arg);
354+
return WorkStatus::NeedsCargo(PackageArg::Default);
340355
}
341356

342357
let queue = self.topological_sort(&graph);
@@ -355,9 +370,8 @@ impl Plan {
355370
// crates within the crate that depend on the error-ing one have never been built.
356371
// In that case we need to build from scratch so that everything is in our cache, or
357372
// we cope with the error. In the error case, jobs will be None.
358-
359373
match jobs {
360-
None => WorkStatus::NeedsCargo(package_arg),
374+
None => WorkStatus::NeedsCargo(PackageArg::Default),
361375
Some(jobs) => {
362376
assert!(!jobs.is_empty());
363377
WorkStatus::Execute(JobQueue(jobs))
@@ -372,17 +386,13 @@ crate enum WorkStatus {
372386
Execute(JobQueue),
373387
}
374388

375-
// The point of the PackageMap is to compute the minimal work for Cargo to do,
376-
// given a list of changed files. That is, if things have changed all over the
377-
// place we have to do a `--all` build, but if they've only changed in one
378-
// package, then we can do `-p foo` which should mean less work.
379-
//
380-
// However, when we change package we throw away our build plan and must rebuild
381-
// it, so we must be careful that compiler jobs we expect to exist will in fact
382-
// exist. There is some wasted work too, but I don't think we can predict when
383-
// we definitely need to do this and it should be quick compared to compilation.
384-
385-
// Maps paths to packages.
389+
/// Maps paths to packages.
390+
///
391+
/// The point of the PackageMap is detect if additional packages need to be
392+
/// included in the cached build plan. The cache can represent only a subset of
393+
/// the entire workspace, hence why we need to detect if a package was modified
394+
/// that's outside the cached build plan - if so, we need to recreate it,
395+
/// including the new package.
386396
#[derive(Debug)]
387397
struct PackageMap {
388398
// A map from a manifest directory to the package name.
@@ -418,19 +428,12 @@ impl PackageMap {
418428
.collect()
419429
}
420430

421-
// Compute the `-p` argument to pass to Cargo by examining the current dirty
422-
// set of files and finding their package.
423-
fn compute_package_arg<T: AsRef<Path> + fmt::Debug>(&self, modified_files: &[T]) -> PackageArg {
424-
let mut packages: Option<HashSet<String>> = modified_files
431+
/// Given modified set of files, returns a set of corresponding dirty packages.
432+
fn compute_dirty_packages<T: AsRef<Path> + fmt::Debug>(&self, modified_files: &[T]) -> HashSet<String> {
433+
modified_files
425434
.iter()
426-
.map(|p| self.map(p.as_ref()))
427-
.collect();
428-
match packages {
429-
Some(ref mut packages) if packages.len() == 1 => {
430-
PackageArg::Package(packages.drain().next().unwrap())
431-
}
432-
_ => PackageArg::All,
433-
}
435+
.filter_map(|p| self.map(p.as_ref()))
436+
.collect()
434437
}
435438

436439
// Map a file to the package which it belongs to.

0 commit comments

Comments
 (0)