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

Commit e42abca

Browse files
committed
Add reverse dependency graph to the build plan
1 parent 4179e7d commit e42abca

File tree

1 file changed

+92
-43
lines changed

1 file changed

+92
-43
lines changed

src/build/plan.rs

Lines changed: 92 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -8,96 +8,145 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
use std::collections::HashMap;
11+
//! This contains a build plan that is created during the Cargo build routine
12+
//! and stored afterwards, which can be later queried, given a list of dirty
13+
//! files, to retrieve a queue of compiler calls to be invoked (including
14+
//! appropriate arguments and env variables).
15+
//! The underlying structure is a dependency graph between simplified units
16+
//! (package id and crate target kind), as opposed to Cargo units (package with
17+
//! a target info, including crate target kind, profile and host/target kind).
18+
//! This will be used for a quick check recompilation and does not aim to
19+
//! reimplement all the intricacies of Cargo.
20+
//! The unit dependency graph in Cargo also distinguishes between compiling the
21+
//! build script and running it and collecting the build script output to modify
22+
//! the subsequent compilations etc. Since build script executions (not building)
23+
//! are not exposed via `Executor` trait in Cargo, we simply coalesce every unit
24+
//! with a same package and crate target kind (e.g. both building and running
25+
//! build scripts).
26+
27+
use std::collections::{HashMap, HashSet};
1228
use std::fmt;
1329

1430
use cargo::core::{PackageId, Profile, Target, TargetKind};
1531
use cargo::ops::{Kind, Unit, Context};
1632
use cargo::util::{CargoResult, ProcessBuilder};
1733

18-
pub type DependencyGraph = HashMap<OwnedUnit, Vec<OwnedUnit>>;
34+
/// Main key type by which `Unit`s will be distinguished in the build plan.
35+
pub type UnitKey = (PackageId, TargetKind);
1936
/// Holds the information how exactly the build will be performed for a given
2037
/// workspace with given, specified features.
21-
/// **TODO:** Use it to schedule an analysis build instead of relying on Cargo
22-
/// invocations.
2338
pub struct Plan {
24-
pub dep_graph: DependencyGraph,
25-
/// We don't make a distinction between Units with different Profiles,
26-
/// as we're practically interested in bin, lib and (built, not run)
27-
/// build scripts for each package, because for these we can run `rustc` job
28-
pub compiler_jobs: HashMap<(PackageId, TargetKind), ProcessBuilder>,
39+
// Stores a full Cargo `Unit` data for a first processed unit with a given key.
40+
pub units: HashMap<UnitKey, OwnedUnit>,
41+
// Main dependency graph between the simplified units.
42+
pub dep_graph: HashMap<UnitKey, HashSet<UnitKey>>,
43+
/// Reverse dependency graph that's used to construct a dirty compiler call queue.
44+
pub rev_dep_graph: HashMap<UnitKey, HashSet<UnitKey>>,
45+
/// Cached compiler calls used when creating a compiler call queue.
46+
pub compiler_jobs: HashMap<UnitKey, ProcessBuilder>,
2947
}
3048

3149
impl Plan {
3250
pub fn new() -> Plan {
3351
Plan {
52+
units: HashMap::new(),
3453
dep_graph: HashMap::new(),
54+
rev_dep_graph: HashMap::new(),
3555
compiler_jobs: HashMap::new(),
3656
}
3757
}
3858

59+
pub fn clear(&mut self) {
60+
*self = Plan::new();
61+
}
3962

40-
/// Cache a given compiler invocation in `ProcessBuilder` for a given `PackageId`
41-
/// and `TargetKind` in `Target`, to be used when processing cached build plan
63+
/// Cache a given compiler invocation in `ProcessBuilder` for a given
64+
/// `PackageId` and `TargetKind` in `Target`, to be used when processing
65+
/// cached build plan.
4266
pub fn cache_compiler_job(&mut self, id: &PackageId, target: &Target, cmd: &ProcessBuilder) {
4367
let pkg_key = (id.clone(), target.kind().clone());
4468
self.compiler_jobs.insert(pkg_key, cmd.clone());
4569
}
4670

4771
/// Emplace a given `Unit`, along with its `Unit` dependencies (recursively)
48-
/// into dependency graph
72+
/// into the dependency graph.
4973
#[allow(dead_code)]
5074
pub fn emplace_dep(&mut self, unit: &Unit, cx: &Context) -> CargoResult<()> {
5175
let null_filter = |_unit: &Unit| { true };
5276
self.emplace_dep_with_filter(unit, cx, &null_filter)
5377
}
5478

5579
/// Emplace a given `Unit`, along with its `Unit` dependencies (recursively)
56-
/// into dependency graph as long as the passed `Unit` isn't filtered out by
57-
/// the `filter` closure.
80+
/// into the dependency graph as long as the passed `Unit` isn't filtered
81+
/// out by the `filter` closure.
5882
pub fn emplace_dep_with_filter<Filter>(&mut self,
5983
unit: &Unit,
6084
cx: &Context,
6185
filter: &Filter) -> CargoResult<()>
6286
where Filter: Fn(&Unit) -> bool {
63-
// We might not want certain deps to be added transitively (e.g. when
64-
// creating only a sub-dep-graph, limiting the scope to the workspace)
65-
if filter(unit) == false { return Ok(()); }
66-
67-
let key: OwnedUnit = unit.into();
68-
// Process only those units, which are not yet in the dep graph
69-
if let None = self.dep_graph.get(&key) {
70-
let units = cx.dep_targets(unit)?;
71-
let dep_keys: Vec<OwnedUnit> = units
72-
.iter()
73-
.map(|x| x.into())
74-
.collect();
75-
self.dep_graph.insert(key, dep_keys);
76-
// Recursively process other remaining dependencies.
77-
// TODO: Should we be careful about blowing the stack and do it
78-
// iteratively instead?
79-
for unit in units {
80-
self.emplace_dep_with_filter(&unit, cx, filter)?;
81-
}
87+
if !filter(unit) { return Ok(()); }
88+
89+
let key = key_from_unit(unit);
90+
self.units.entry(key.clone()).or_insert(unit.into());
91+
// Process only those units, which are not yet in the dep graph.
92+
if self.dep_graph.get(&key).is_some() { return Ok(()); }
93+
94+
// Keep all the additional Unit information for a given unit (It's
95+
// worth remembering, that the units are only discriminated by a
96+
// pair of (PackageId, TargetKind), so only first occurrence will be saved.
97+
self.units.insert(key.clone(), unit.into());
98+
99+
// Fetch and insert relevant unit dependencies to the forward dep graph.
100+
let units = cx.dep_targets(unit)?;
101+
let dep_keys: HashSet<UnitKey> = units.iter()
102+
// We might not want certain deps to be added transitively (e.g.
103+
// when creating only a sub-dep-graph, limiting the scope).
104+
.filter(|unit| filter(unit))
105+
.map(key_from_unit)
106+
// Units can depend on others with different Targets or Profiles
107+
// (e.g. different `run_custom_build`) despite having the same UnitKey.
108+
// We coalesce them here while creating the UnitKey dep graph.
109+
.filter(|dep| key != *dep)
110+
.collect();
111+
self.dep_graph.insert(key.clone(), dep_keys.clone());
112+
113+
// We also need to track reverse dependencies here, as it's needed
114+
// to quickly construct a work sub-graph from a set of dirty units.
115+
self.rev_dep_graph.entry(key.clone()).or_insert(HashSet::new());
116+
for unit in dep_keys {
117+
let revs = self.rev_dep_graph.entry(unit).or_insert(HashSet::new());
118+
revs.insert(key.clone());
119+
}
120+
121+
// Recursively process other remaining forward dependencies.
122+
for unit in units {
123+
self.emplace_dep_with_filter(&unit, cx, filter)?;
82124
}
83125
Ok(())
84126
}
127+
}
85128

86-
pub fn clear(&mut self) {
87-
self.dep_graph.clear();
88-
self.compiler_jobs.clear();
89-
}
129+
fn key_from_unit(unit: &Unit) -> UnitKey {
130+
(unit.pkg.package_id().clone(), unit.target.kind().clone())
90131
}
91132

92-
impl fmt::Debug for Plan {
93-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
94-
f.write_str("Dep graph:\n")?;
95-
for (key, deps) in &self.dep_graph {
96-
f.write_str(&format!("{:?}\n", key))?;
133+
macro_rules! print_dep_graph {
134+
($name: expr, $graph: expr, $f: expr) => {
135+
$f.write_str(&format!("{}:\n", $name))?;
136+
for (key, deps) in &$graph {
137+
$f.write_str(&format!("{:?}\n", key))?;
97138
for dep in deps {
98-
f.write_str(&format!("- {:?}\n", dep))?;
139+
$f.write_str(&format!("- {:?}\n", dep))?;
99140
}
100141
}
142+
}
143+
}
144+
145+
impl fmt::Debug for Plan {
146+
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
147+
f.write_str(&format!("Units: {:?}\n", self.units))?;
148+
print_dep_graph!("Dependency graph", self.dep_graph, f);
149+
print_dep_graph!("Reverse dependency graph", self.rev_dep_graph, f);
101150
f.write_str(&format!("Compiler jobs: {:?}\n", self.compiler_jobs))?;
102151
Ok(())
103152
}

0 commit comments

Comments
 (0)