]> git.proxmox.com Git - cargo.git/blob - src/cargo/ops/tree/graph.rs
37442a4dafd8a1e26983a293408595ceecde217f
[cargo.git] / src / cargo / ops / tree / graph.rs
1 //! Code for building the graph used by `cargo tree`.
2
3 use super::TreeOptions;
4 use crate::core::compiler::{CompileKind, RustcTargetData};
5 use crate::core::dependency::DepKind;
6 use crate::core::resolver::features::{CliFeatures, FeaturesFor, ResolvedFeatures};
7 use crate::core::resolver::Resolve;
8 use crate::core::{FeatureMap, FeatureValue, Package, PackageId, PackageIdSpec, Workspace};
9 use crate::util::interning::InternedString;
10 use crate::util::CargoResult;
11 use std::collections::{HashMap, HashSet};
12
13 #[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
14 pub enum Node {
15 Package {
16 package_id: PackageId,
17 /// Features that are enabled on this package.
18 features: Vec<InternedString>,
19 kind: CompileKind,
20 },
21 Feature {
22 /// Index of the package node this feature is for.
23 node_index: usize,
24 /// Name of the feature.
25 name: InternedString,
26 },
27 }
28
29 /// The kind of edge, for separating dependencies into different sections.
30 #[derive(Debug, Copy, Hash, Eq, Clone, PartialEq)]
31 pub enum EdgeKind {
32 Dep(DepKind),
33 Feature,
34 }
35
36 /// Set of outgoing edges for a single node.
37 ///
38 /// Edges are separated by the edge kind (`DepKind` or `Feature`). This is
39 /// primarily done so that the output can easily display separate sections
40 /// like `[build-dependencies]`.
41 ///
42 /// The value is a `Vec` because each edge kind can have multiple outgoing
43 /// edges. For example, package "foo" can have multiple normal dependencies.
44 #[derive(Clone)]
45 struct Edges(HashMap<EdgeKind, Vec<usize>>);
46
47 impl Edges {
48 fn new() -> Edges {
49 Edges(HashMap::new())
50 }
51
52 /// Adds an edge pointing to the given node.
53 fn add_edge(&mut self, kind: EdgeKind, index: usize) {
54 let indexes = self.0.entry(kind).or_default();
55 if !indexes.contains(&index) {
56 indexes.push(index)
57 }
58 }
59 }
60
61 /// A graph of dependencies.
62 pub struct Graph<'a> {
63 nodes: Vec<Node>,
64 /// The indexes of `edges` correspond to the `nodes`. That is, `edges[0]`
65 /// is the set of outgoing edges for `nodes[0]`. They should always be in
66 /// sync.
67 edges: Vec<Edges>,
68 /// Index maps a node to an index, for fast lookup.
69 index: HashMap<Node, usize>,
70 /// Map for looking up packages.
71 package_map: HashMap<PackageId, &'a Package>,
72 /// Set of indexes of feature nodes that were added via the command-line.
73 ///
74 /// For example `--features foo` will mark the "foo" node here.
75 cli_features: HashSet<usize>,
76 /// Map of dependency names, used for building internal feature map for
77 /// dep_name/feat_name syntax.
78 ///
79 /// Key is the index of a package node, value is a map of dep_name to a
80 /// set of `(pkg_node_index, is_optional)`.
81 dep_name_map: HashMap<usize, HashMap<InternedString, HashSet<(usize, bool)>>>,
82 }
83
84 impl<'a> Graph<'a> {
85 fn new(package_map: HashMap<PackageId, &'a Package>) -> Graph<'a> {
86 Graph {
87 nodes: Vec::new(),
88 edges: Vec::new(),
89 index: HashMap::new(),
90 package_map,
91 cli_features: HashSet::new(),
92 dep_name_map: HashMap::new(),
93 }
94 }
95
96 /// Adds a new node to the graph, returning its new index.
97 fn add_node(&mut self, node: Node) -> usize {
98 let from_index = self.nodes.len();
99 self.nodes.push(node);
100 self.edges.push(Edges::new());
101 self.index
102 .insert(self.nodes[from_index].clone(), from_index);
103 from_index
104 }
105
106 /// Returns a list of nodes the given node index points to for the given kind.
107 pub fn connected_nodes(&self, from: usize, kind: &EdgeKind) -> Vec<usize> {
108 match self.edges[from].0.get(kind) {
109 Some(indexes) => {
110 // Created a sorted list for consistent output.
111 let mut indexes = indexes.clone();
112 indexes.sort_unstable_by(|a, b| self.nodes[*a].cmp(&self.nodes[*b]));
113 indexes
114 }
115 None => Vec::new(),
116 }
117 }
118
119 /// Returns `true` if the given node has any outgoing edges.
120 pub fn has_outgoing_edges(&self, index: usize) -> bool {
121 !self.edges[index].0.is_empty()
122 }
123
124 /// Gets a node by index.
125 pub fn node(&self, index: usize) -> &Node {
126 &self.nodes[index]
127 }
128
129 /// Given a slice of PackageIds, returns the indexes of all nodes that match.
130 pub fn indexes_from_ids(&self, package_ids: &[PackageId]) -> Vec<usize> {
131 let mut result: Vec<(&Node, usize)> = self
132 .nodes
133 .iter()
134 .enumerate()
135 .filter(|(_i, node)| match node {
136 Node::Package { package_id, .. } => package_ids.contains(package_id),
137 _ => false,
138 })
139 .map(|(i, node)| (node, i))
140 .collect();
141 // Sort for consistent output (the same command should always return
142 // the same output). "unstable" since nodes should always be unique.
143 result.sort_unstable();
144 result.into_iter().map(|(_node, i)| i).collect()
145 }
146
147 pub fn package_for_id(&self, id: PackageId) -> &Package {
148 self.package_map[&id]
149 }
150
151 fn package_id_for_index(&self, index: usize) -> PackageId {
152 match self.nodes[index] {
153 Node::Package { package_id, .. } => package_id,
154 Node::Feature { .. } => panic!("unexpected feature node"),
155 }
156 }
157
158 /// Returns `true` if the given feature node index is a feature enabled
159 /// via the command-line.
160 pub fn is_cli_feature(&self, index: usize) -> bool {
161 self.cli_features.contains(&index)
162 }
163
164 /// Returns a new graph by removing all nodes not reachable from the
165 /// given nodes.
166 pub fn from_reachable(&self, roots: &[usize]) -> Graph<'a> {
167 // Graph built with features does not (yet) support --duplicates.
168 assert!(self.dep_name_map.is_empty());
169 let mut new_graph = Graph::new(self.package_map.clone());
170 // Maps old index to new index. None if not yet visited.
171 let mut remap: Vec<Option<usize>> = vec![None; self.nodes.len()];
172
173 fn visit(
174 graph: &Graph<'_>,
175 new_graph: &mut Graph<'_>,
176 remap: &mut Vec<Option<usize>>,
177 index: usize,
178 ) -> usize {
179 if let Some(new_index) = remap[index] {
180 // Already visited.
181 return new_index;
182 }
183 let node = graph.node(index).clone();
184 let new_from = new_graph.add_node(node);
185 remap[index] = Some(new_from);
186 // Visit dependencies.
187 for (edge_kind, edge_indexes) in &graph.edges[index].0 {
188 for edge_index in edge_indexes {
189 let new_to_index = visit(graph, new_graph, remap, *edge_index);
190 new_graph.edges[new_from].add_edge(*edge_kind, new_to_index);
191 }
192 }
193 new_from
194 }
195
196 // Walk the roots, generating a new graph as it goes along.
197 for root in roots {
198 visit(self, &mut new_graph, &mut remap, *root);
199 }
200
201 new_graph
202 }
203
204 /// Inverts the direction of all edges.
205 pub fn invert(&mut self) {
206 let mut new_edges = vec![Edges::new(); self.edges.len()];
207 for (from_idx, node_edges) in self.edges.iter().enumerate() {
208 for (kind, edges) in &node_edges.0 {
209 for edge_idx in edges {
210 new_edges[*edge_idx].add_edge(*kind, from_idx);
211 }
212 }
213 }
214 self.edges = new_edges;
215 }
216
217 /// Returns a list of nodes that are considered "duplicates" (same package
218 /// name, with different versions/features/source/etc.).
219 pub fn find_duplicates(&self) -> Vec<usize> {
220 // Graph built with features does not (yet) support --duplicates.
221 assert!(self.dep_name_map.is_empty());
222
223 // Collect a map of package name to Vec<(&Node, usize)>.
224 let mut packages = HashMap::new();
225 for (i, node) in self.nodes.iter().enumerate() {
226 if let Node::Package { package_id, .. } = node {
227 packages
228 .entry(package_id.name())
229 .or_insert_with(Vec::new)
230 .push((node, i));
231 }
232 }
233
234 let mut dupes: Vec<(&Node, usize)> = packages
235 .into_iter()
236 .filter(|(_name, indexes)| indexes.len() > 1)
237 .flat_map(|(_name, indexes)| indexes)
238 .collect();
239 // For consistent output.
240 dupes.sort_unstable();
241 dupes.into_iter().map(|(_node, i)| i).collect()
242 }
243 }
244
245 /// Builds the graph.
246 pub fn build<'a>(
247 ws: &Workspace<'_>,
248 resolve: &Resolve,
249 resolved_features: &ResolvedFeatures,
250 specs: &[PackageIdSpec],
251 cli_features: &CliFeatures,
252 target_data: &RustcTargetData<'_>,
253 requested_kinds: &[CompileKind],
254 package_map: HashMap<PackageId, &'a Package>,
255 opts: &TreeOptions,
256 ) -> CargoResult<Graph<'a>> {
257 let mut graph = Graph::new(package_map);
258 let mut members_with_features = ws.members_with_features(specs, cli_features)?;
259 members_with_features.sort_unstable_by_key(|e| e.0.package_id());
260 for (member, cli_features) in members_with_features {
261 let member_id = member.package_id();
262 let features_for = FeaturesFor::from_for_host(member.proc_macro());
263 for kind in requested_kinds {
264 let member_index = add_pkg(
265 &mut graph,
266 resolve,
267 resolved_features,
268 member_id,
269 features_for,
270 target_data,
271 *kind,
272 opts,
273 );
274 if opts.graph_features {
275 let fmap = resolve.summary(member_id).features();
276 add_cli_features(&mut graph, member_index, &cli_features, fmap);
277 }
278 }
279 }
280 if opts.graph_features {
281 add_internal_features(&mut graph, resolve);
282 }
283 Ok(graph)
284 }
285
286 /// Adds a single package node (if it does not already exist).
287 ///
288 /// This will also recursively add all of its dependencies.
289 ///
290 /// Returns the index to the package node.
291 fn add_pkg(
292 graph: &mut Graph<'_>,
293 resolve: &Resolve,
294 resolved_features: &ResolvedFeatures,
295 package_id: PackageId,
296 features_for: FeaturesFor,
297 target_data: &RustcTargetData<'_>,
298 requested_kind: CompileKind,
299 opts: &TreeOptions,
300 ) -> usize {
301 let node_features = resolved_features.activated_features(package_id, features_for);
302 let node_kind = match features_for {
303 FeaturesFor::HostDep => CompileKind::Host,
304 FeaturesFor::NormalOrDev => requested_kind,
305 };
306 let node = Node::Package {
307 package_id,
308 features: node_features,
309 kind: node_kind,
310 };
311 if let Some(idx) = graph.index.get(&node) {
312 return *idx;
313 }
314 let from_index = graph.add_node(node);
315 // Compute the dep name map which is later used for foo/bar feature lookups.
316 let mut dep_name_map: HashMap<InternedString, HashSet<(usize, bool)>> = HashMap::new();
317 let mut deps: Vec<_> = resolve.deps(package_id).collect();
318 deps.sort_unstable_by_key(|(dep_id, _)| *dep_id);
319 let show_all_targets = opts.target == super::Target::All;
320 for (dep_id, deps) in deps {
321 let mut deps: Vec<_> = deps
322 .iter()
323 // This filter is *similar* to the one found in `unit_dependencies::compute_deps`.
324 // Try to keep them in sync!
325 .filter(|dep| {
326 let kind = match (node_kind, dep.kind()) {
327 (CompileKind::Host, _) => CompileKind::Host,
328 (_, DepKind::Build) => CompileKind::Host,
329 (_, DepKind::Normal) => node_kind,
330 (_, DepKind::Development) => node_kind,
331 };
332 // Filter out inactivated targets.
333 if !show_all_targets && !target_data.dep_platform_activated(dep, kind) {
334 return false;
335 }
336 // Filter out dev-dependencies if requested.
337 if !opts.edge_kinds.contains(&EdgeKind::Dep(dep.kind())) {
338 return false;
339 }
340 if dep.is_optional() {
341 // If the new feature resolver does not enable this
342 // optional dep, then don't use it.
343 if !resolved_features.is_dep_activated(
344 package_id,
345 features_for,
346 dep.name_in_toml(),
347 ) {
348 return false;
349 }
350 }
351 true
352 })
353 .collect();
354
355 // This dependency is eliminated from the dependency tree under
356 // the current target and feature set.
357 if deps.is_empty() {
358 continue;
359 }
360
361 deps.sort_unstable_by_key(|dep| dep.name_in_toml());
362 let dep_pkg = graph.package_map[&dep_id];
363
364 for dep in deps {
365 let dep_features_for = if dep.is_build() || dep_pkg.proc_macro() {
366 FeaturesFor::HostDep
367 } else {
368 features_for
369 };
370 let dep_index = add_pkg(
371 graph,
372 resolve,
373 resolved_features,
374 dep_id,
375 dep_features_for,
376 target_data,
377 requested_kind,
378 opts,
379 );
380 if opts.graph_features {
381 // Add the dependency node with feature nodes in-between.
382 dep_name_map
383 .entry(dep.name_in_toml())
384 .or_default()
385 .insert((dep_index, dep.is_optional()));
386 if dep.uses_default_features() {
387 add_feature(
388 graph,
389 InternedString::new("default"),
390 Some(from_index),
391 dep_index,
392 EdgeKind::Dep(dep.kind()),
393 );
394 }
395 for feature in dep.features().iter() {
396 add_feature(
397 graph,
398 *feature,
399 Some(from_index),
400 dep_index,
401 EdgeKind::Dep(dep.kind()),
402 );
403 }
404 if !dep.uses_default_features() && dep.features().is_empty() {
405 // No features, use a direct connection.
406 graph.edges[from_index].add_edge(EdgeKind::Dep(dep.kind()), dep_index);
407 }
408 } else {
409 graph.edges[from_index].add_edge(EdgeKind::Dep(dep.kind()), dep_index);
410 }
411 }
412 }
413 if opts.graph_features {
414 assert!(graph
415 .dep_name_map
416 .insert(from_index, dep_name_map)
417 .is_none());
418 }
419
420 from_index
421 }
422
423 /// Adds a feature node between two nodes.
424 ///
425 /// That is, it adds the following:
426 ///
427 /// ```text
428 /// from -Edge-> featname -Edge::Feature-> to
429 /// ```
430 fn add_feature(
431 graph: &mut Graph<'_>,
432 name: InternedString,
433 from: Option<usize>,
434 to: usize,
435 kind: EdgeKind,
436 ) -> usize {
437 // `to` *must* point to a package node.
438 assert!(matches! {graph.nodes[to], Node::Package{..}});
439 let node = Node::Feature {
440 node_index: to,
441 name,
442 };
443 let node_index = match graph.index.get(&node) {
444 Some(idx) => *idx,
445 None => graph.add_node(node),
446 };
447 if let Some(from) = from {
448 graph.edges[from].add_edge(kind, node_index);
449 }
450 graph.edges[node_index].add_edge(EdgeKind::Feature, to);
451 node_index
452 }
453
454 /// Adds nodes for features requested on the command-line for the given member.
455 ///
456 /// Feature nodes are added as "roots" (i.e., they have no "from" index),
457 /// because they come from the outside world. They usually only appear with
458 /// `--invert`.
459 fn add_cli_features(
460 graph: &mut Graph<'_>,
461 package_index: usize,
462 cli_features: &CliFeatures,
463 feature_map: &FeatureMap,
464 ) {
465 // NOTE: Recursive enabling of features will be handled by
466 // add_internal_features.
467
468 // Create a set of feature names requested on the command-line.
469 let mut to_add: HashSet<FeatureValue> = HashSet::new();
470 if cli_features.all_features {
471 to_add.extend(feature_map.keys().map(|feat| FeatureValue::Feature(*feat)));
472 } else {
473 if cli_features.uses_default_features {
474 to_add.insert(FeatureValue::Feature(InternedString::new("default")));
475 }
476 to_add.extend(cli_features.features.iter().cloned());
477 };
478
479 // Add each feature as a node, and mark as "from command-line" in graph.cli_features.
480 for fv in to_add {
481 match fv {
482 FeatureValue::Feature(feature) => {
483 let index = add_feature(graph, feature, None, package_index, EdgeKind::Feature);
484 graph.cli_features.insert(index);
485 }
486 // This is enforced by CliFeatures.
487 FeatureValue::Dep { .. } => panic!("unexpected cli dep feature {}", fv),
488 FeatureValue::DepFeature {
489 dep_name,
490 dep_feature,
491 dep_prefix: _,
492 weak,
493 } => {
494 let dep_connections = match graph.dep_name_map[&package_index].get(&dep_name) {
495 // Clone to deal with immutable borrow of `graph`. :(
496 Some(dep_connections) => dep_connections.clone(),
497 None => {
498 // --features bar?/feat where `bar` is not activated should be ignored.
499 // If this wasn't weak, then this is a bug.
500 if weak {
501 continue;
502 }
503 panic!(
504 "missing dep graph connection for CLI feature `{}` for member {:?}\n\
505 Please file a bug report at https://github.com/rust-lang/cargo/issues",
506 fv,
507 graph.nodes.get(package_index)
508 );
509 }
510 };
511 for (dep_index, is_optional) in dep_connections {
512 if is_optional {
513 // Activate the optional dep on self.
514 let index =
515 add_feature(graph, dep_name, None, package_index, EdgeKind::Feature);
516 graph.cli_features.insert(index);
517 }
518 let index = add_feature(graph, dep_feature, None, dep_index, EdgeKind::Feature);
519 graph.cli_features.insert(index);
520 }
521 }
522 }
523 }
524 }
525
526 /// Recursively adds connections between features in the `[features]` table
527 /// for every package.
528 fn add_internal_features(graph: &mut Graph<'_>, resolve: &Resolve) {
529 // Collect features already activated by dependencies or command-line.
530 let feature_nodes: Vec<(PackageId, usize, usize, InternedString)> = graph
531 .nodes
532 .iter()
533 .enumerate()
534 .filter_map(|(i, node)| match node {
535 Node::Package { .. } => None,
536 Node::Feature { node_index, name } => {
537 let package_id = graph.package_id_for_index(*node_index);
538 Some((package_id, *node_index, i, *name))
539 }
540 })
541 .collect();
542
543 for (package_id, package_index, feature_index, feature_name) in feature_nodes {
544 add_feature_rec(
545 graph,
546 resolve,
547 feature_name,
548 package_id,
549 feature_index,
550 package_index,
551 );
552 }
553 }
554
555 /// Recursively add feature nodes for all features enabled by the given feature.
556 ///
557 /// `from` is the index of the node that enables this feature.
558 /// `package_index` is the index of the package node for the feature.
559 fn add_feature_rec(
560 graph: &mut Graph<'_>,
561 resolve: &Resolve,
562 feature_name: InternedString,
563 package_id: PackageId,
564 from: usize,
565 package_index: usize,
566 ) {
567 let feature_map = resolve.summary(package_id).features();
568 let fvs = match feature_map.get(&feature_name) {
569 Some(fvs) => fvs,
570 None => return,
571 };
572 for fv in fvs {
573 match fv {
574 FeatureValue::Feature(dep_name) => {
575 let feat_index = add_feature(
576 graph,
577 *dep_name,
578 Some(from),
579 package_index,
580 EdgeKind::Feature,
581 );
582 add_feature_rec(
583 graph,
584 resolve,
585 *dep_name,
586 package_id,
587 feat_index,
588 package_index,
589 );
590 }
591 // Dependencies are already shown in the graph as dep edges. I'm
592 // uncertain whether or not this might be confusing in some cases
593 // (like feature `"somefeat" = ["dep:somedep"]`), so maybe in the
594 // future consider explicitly showing this?
595 FeatureValue::Dep { .. } => {}
596 FeatureValue::DepFeature {
597 dep_name,
598 dep_feature,
599 dep_prefix,
600 // `weak` is ignored, because it will be skipped if the
601 // corresponding dependency is not found in the map, which
602 // means it wasn't activated. Skipping is handled by
603 // `is_dep_activated` when the graph was built.
604 weak: _,
605 } => {
606 let dep_indexes = match graph.dep_name_map[&package_index].get(dep_name) {
607 Some(indexes) => indexes.clone(),
608 None => {
609 log::debug!(
610 "enabling feature {} on {}, found {}/{}, \
611 dep appears to not be enabled",
612 feature_name,
613 package_id,
614 dep_name,
615 dep_feature
616 );
617 continue;
618 }
619 };
620 for (dep_index, is_optional) in dep_indexes {
621 let dep_pkg_id = graph.package_id_for_index(dep_index);
622 if is_optional && !dep_prefix {
623 // Activate the optional dep on self.
624 add_feature(
625 graph,
626 *dep_name,
627 Some(from),
628 package_index,
629 EdgeKind::Feature,
630 );
631 }
632 let feat_index = add_feature(
633 graph,
634 *dep_feature,
635 Some(from),
636 dep_index,
637 EdgeKind::Feature,
638 );
639 add_feature_rec(
640 graph,
641 resolve,
642 *dep_feature,
643 dep_pkg_id,
644 feat_index,
645 dep_index,
646 );
647 }
648 }
649 }
650 }
651 }