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

Commit 1ce92c1

Browse files
committed
Create a dirty unit dep graph based on currently modified files
1 parent e42abca commit 1ce92c1

File tree

1 file changed

+95
-0
lines changed

1 file changed

+95
-0
lines changed

src/build/plan.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
2727
use std::collections::{HashMap, HashSet};
2828
use std::fmt;
29+
use std::path::Path;
2930

3031
use cargo::core::{PackageId, Profile, Target, TargetKind};
3132
use cargo::ops::{Kind, Unit, Context};
@@ -124,6 +125,100 @@ impl Plan {
124125
}
125126
Ok(())
126127
}
128+
129+
/// TODO: Improve detecting dirty crate targets for a set of dirty file paths.
130+
/// This uses a lousy heuristic of checking path prefix for a given crate
131+
/// target to determine whether a given unit (crate target) is dirty. This
132+
/// can easily backfire, e.g. when build script is under src/. Any change
133+
/// to a file under src/ would imply the build script is always dirty, so we
134+
/// never do work and always offload to Cargo in such case.
135+
/// Because of that, build scripts are checked separately and only other
136+
/// crate targets are checked with path prefixes.
137+
fn fetch_dirty_units<T: AsRef<Path> + fmt::Debug>(&self, files: &[T]) -> HashSet<UnitKey> {
138+
let mut result = HashSet::new();
139+
140+
let build_scripts: HashMap<&Path, UnitKey> = self.units.iter()
141+
.filter(|&(&(_, ref kind), _)| *kind == TargetKind::CustomBuild)
142+
.map(|(key, ref unit)| (unit.target.src_path(), key.clone())).collect();
143+
let other_targets: HashMap<UnitKey, &Path> = self.units.iter()
144+
.filter(|&(&(_, ref kind), _)| *kind != TargetKind::CustomBuild)
145+
.map(|(key, ref unit)| (key.clone(), unit.target.src_path().parent().unwrap())).collect();
146+
147+
for modified in files {
148+
if let Some(unit) = build_scripts.get(modified.as_ref()) {
149+
result.insert(unit.clone());
150+
} else {
151+
// It's not a build script, so we can check for path prefix now
152+
for (unit, src_dir) in &other_targets {
153+
if modified.as_ref().starts_with(src_dir) {
154+
trace!("Adding {:?}, as a modified file {:?} starts with {:?}", &unit, modified, src_dir);
155+
result.insert(unit.clone());
156+
}
157+
}
158+
}
159+
}
160+
result
161+
}
162+
163+
/// For a given set of select dirty units, returns a set of all the
164+
/// dependencies that has to be rebuilt transitively.
165+
fn transitive_dirty_units(&self, dirties: &HashSet<UnitKey>) -> HashSet<UnitKey> {
166+
let mut transitive = dirties.clone();
167+
// Walk through a rev dep graph using a stack of nodes to collect
168+
// transitively every dirty node
169+
let mut to_process: Vec<_> = dirties.iter().cloned().collect();
170+
while let Some(top) = to_process.pop() {
171+
if transitive.get(&top).is_some() { continue; }
172+
else { transitive.insert(top.clone()); }
173+
174+
// Process every dirty rev dep of the processed node
175+
let dirty_rev_deps = self.rev_dep_graph.get(&top).unwrap()
176+
.iter().filter(|dep| dirties.contains(dep));
177+
for rev_dep in dirty_rev_deps {
178+
to_process.push(rev_dep.clone());
179+
}
180+
}
181+
transitive
182+
}
183+
184+
/// Creates a dirty reverse dependency graph using a set of given dirty units.
185+
fn dirty_rev_dep_graph(&self, dirties: &HashSet<UnitKey>) -> HashMap<UnitKey, HashSet<UnitKey>> {
186+
let dirties = self.transitive_dirty_units(dirties);
187+
trace!("transitive_dirty_units: {:?}", dirties);
188+
189+
self.rev_dep_graph.iter()
190+
// Remove nodes that are not dirty
191+
.filter(|&(unit, _)| dirties.contains(&unit))
192+
// Retain only dirty dependencies of the ones that are dirty
193+
.map(|(k, deps)| (k.clone(), deps.iter().cloned().filter(|d| dirties.contains(&d)).collect()))
194+
.collect()
195+
}
196+
197+
pub fn prepare_work<T: AsRef<Path> + fmt::Debug>(&self, modified: &[T]) -> WorkStatus {
198+
let dirties = self.fetch_dirty_units(modified);
199+
trace!("fetch_dirty_units: for files {:?}, these units are dirty: {:?}", modified, dirties);
200+
201+
if dirties.iter().any(|&(_, ref kind)| *kind == TargetKind::CustomBuild) {
202+
WorkStatus::NeedsCargo
203+
} else {
204+
let graph = self.dirty_rev_dep_graph(&dirties);
205+
trace!("Constructed dirty rev dep graph: {:?}", graph);
206+
207+
// TODO: Then sort topologically the deps
208+
// TODO: Then map those with a ProcessBuilder
209+
210+
WorkStatus::Execute(JobQueue { })
211+
}
212+
}
213+
}
214+
215+
pub enum WorkStatus {
216+
NeedsCargo,
217+
Execute(JobQueue)
218+
}
219+
220+
pub struct JobQueue {
221+
127222
}
128223

129224
fn key_from_unit(unit: &Unit) -> UnitKey {

0 commit comments

Comments
 (0)