cargo/ops/tree/
graph.rs

1//! Code for building the graph used by `cargo tree`.
2
3use super::TreeOptions;
4use crate::core::compiler::{CompileKind, RustcTargetData};
5use crate::core::dependency::DepKind;
6use crate::core::resolver::features::{CliFeatures, FeaturesFor, ResolvedFeatures};
7use crate::core::resolver::Resolve;
8use crate::core::{FeatureMap, FeatureValue, Package, PackageId, PackageIdSpec, Workspace};
9use crate::util::interning::{InternedString, INTERNED_DEFAULT};
10use crate::util::CargoResult;
11use std::collections::{HashMap, HashSet};
12
13#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
14pub 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)]
31pub 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)]
45struct Edges(HashMap<EdgeKind, Vec<usize>>);
46
47impl 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.
62pub 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
84impl<'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)| {
237                indexes
238                    .into_iter()
239                    .map(|(node, _)| {
240                        match node {
241                            Node::Package {
242                                package_id,
243                                features,
244                                ..
245                            } => {
246                                // Do not treat duplicates on the host or target as duplicates.
247                                Node::Package {
248                                    package_id: package_id.clone(),
249                                    features: features.clone(),
250                                    kind: CompileKind::Host,
251                                }
252                            }
253                            _ => unreachable!(),
254                        }
255                    })
256                    .collect::<HashSet<_>>()
257                    .len()
258                    > 1
259            })
260            .flat_map(|(_name, indexes)| indexes)
261            .collect();
262
263        // For consistent output.
264        dupes.sort_unstable();
265        dupes.into_iter().map(|(_node, i)| i).collect()
266    }
267}
268
269/// Builds the graph.
270pub fn build<'a>(
271    ws: &Workspace<'_>,
272    resolve: &Resolve,
273    resolved_features: &ResolvedFeatures,
274    specs: &[PackageIdSpec],
275    cli_features: &CliFeatures,
276    target_data: &RustcTargetData<'_>,
277    requested_kinds: &[CompileKind],
278    package_map: HashMap<PackageId, &'a Package>,
279    opts: &TreeOptions,
280) -> CargoResult<Graph<'a>> {
281    let mut graph = Graph::new(package_map);
282    let mut members_with_features = ws.members_with_features(specs, cli_features)?;
283    members_with_features.sort_unstable_by_key(|e| e.0.package_id());
284    for (member, cli_features) in members_with_features {
285        let member_id = member.package_id();
286        let features_for = FeaturesFor::from_for_host(member.proc_macro());
287        for kind in requested_kinds {
288            let member_index = add_pkg(
289                &mut graph,
290                resolve,
291                resolved_features,
292                member_id,
293                features_for,
294                target_data,
295                *kind,
296                opts,
297            );
298            if opts.graph_features {
299                let fmap = resolve.summary(member_id).features();
300                add_cli_features(&mut graph, member_index, &cli_features, fmap);
301            }
302        }
303    }
304    if opts.graph_features {
305        add_internal_features(&mut graph, resolve);
306    }
307    Ok(graph)
308}
309
310/// Adds a single package node (if it does not already exist).
311///
312/// This will also recursively add all of its dependencies.
313///
314/// Returns the index to the package node.
315fn add_pkg(
316    graph: &mut Graph<'_>,
317    resolve: &Resolve,
318    resolved_features: &ResolvedFeatures,
319    package_id: PackageId,
320    features_for: FeaturesFor,
321    target_data: &RustcTargetData<'_>,
322    requested_kind: CompileKind,
323    opts: &TreeOptions,
324) -> usize {
325    let node_features = resolved_features.activated_features(package_id, features_for);
326    let node_kind = match features_for {
327        FeaturesFor::HostDep => CompileKind::Host,
328        FeaturesFor::ArtifactDep(target) => CompileKind::Target(target),
329        FeaturesFor::NormalOrDev => requested_kind,
330    };
331    let node = Node::Package {
332        package_id,
333        features: node_features,
334        kind: node_kind,
335    };
336    if let Some(idx) = graph.index.get(&node) {
337        return *idx;
338    }
339    let from_index = graph.add_node(node);
340    // Compute the dep name map which is later used for foo/bar feature lookups.
341    let mut dep_name_map: HashMap<InternedString, HashSet<(usize, bool)>> = HashMap::new();
342    let mut deps: Vec<_> = resolve.deps(package_id).collect();
343    deps.sort_unstable_by_key(|(dep_id, _)| *dep_id);
344    let show_all_targets = opts.target == super::Target::All;
345    for (dep_id, deps) in deps {
346        let mut deps: Vec<_> = deps
347            .iter()
348            // This filter is *similar* to the one found in `unit_dependencies::compute_deps`.
349            // Try to keep them in sync!
350            .filter(|dep| {
351                let kind = match (node_kind, dep.kind()) {
352                    (CompileKind::Host, _) => CompileKind::Host,
353                    (_, DepKind::Build) => CompileKind::Host,
354                    (_, DepKind::Normal) => node_kind,
355                    (_, DepKind::Development) => node_kind,
356                };
357                // Filter out inactivated targets.
358                if !show_all_targets && !target_data.dep_platform_activated(dep, kind) {
359                    return false;
360                }
361                // Filter out dev-dependencies if requested.
362                if !opts.edge_kinds.contains(&EdgeKind::Dep(dep.kind())) {
363                    return false;
364                }
365                // Filter out proc-macrcos if requested.
366                if opts.no_proc_macro && graph.package_for_id(dep_id).proc_macro() {
367                    return false;
368                }
369                if dep.is_optional() {
370                    // If the new feature resolver does not enable this
371                    // optional dep, then don't use it.
372                    if !resolved_features.is_dep_activated(
373                        package_id,
374                        features_for,
375                        dep.name_in_toml(),
376                    ) {
377                        return false;
378                    }
379                }
380                true
381            })
382            .collect();
383
384        // This dependency is eliminated from the dependency tree under
385        // the current target and feature set.
386        if deps.is_empty() {
387            continue;
388        }
389
390        deps.sort_unstable_by_key(|dep| dep.name_in_toml());
391        let dep_pkg = graph.package_map[&dep_id];
392
393        for dep in deps {
394            let dep_features_for = match dep
395                .artifact()
396                .and_then(|artifact| artifact.target())
397                .and_then(|target| target.to_resolved_compile_target(requested_kind))
398            {
399                // Dependency has a `{ …, target = <triple> }`
400                Some(target) => FeaturesFor::ArtifactDep(target),
401                // Get the information of the dependent crate from `features_for`.
402                // If a dependent crate is
403                //
404                // * specified as an artifact dep with a `target`, or
405                // * a host dep,
406                //
407                // its transitive deps, including build-deps, need to be built on that target.
408                None if features_for != FeaturesFor::default() => features_for,
409                // Dependent crate is a normal dep, then back to old rules:
410                //
411                // * normal deps, dev-deps -> inherited target
412                // * build-deps -> host
413                None => {
414                    if dep.is_build() || dep_pkg.proc_macro() {
415                        FeaturesFor::HostDep
416                    } else {
417                        features_for
418                    }
419                }
420            };
421            let dep_index = add_pkg(
422                graph,
423                resolve,
424                resolved_features,
425                dep_id,
426                dep_features_for,
427                target_data,
428                requested_kind,
429                opts,
430            );
431            if opts.graph_features {
432                // Add the dependency node with feature nodes in-between.
433                dep_name_map
434                    .entry(dep.name_in_toml())
435                    .or_default()
436                    .insert((dep_index, dep.is_optional()));
437                if dep.uses_default_features() {
438                    add_feature(
439                        graph,
440                        INTERNED_DEFAULT,
441                        Some(from_index),
442                        dep_index,
443                        EdgeKind::Dep(dep.kind()),
444                    );
445                }
446                for feature in dep.features().iter() {
447                    add_feature(
448                        graph,
449                        *feature,
450                        Some(from_index),
451                        dep_index,
452                        EdgeKind::Dep(dep.kind()),
453                    );
454                }
455                if !dep.uses_default_features() && dep.features().is_empty() {
456                    // No features, use a direct connection.
457                    graph.edges[from_index].add_edge(EdgeKind::Dep(dep.kind()), dep_index);
458                }
459            } else {
460                graph.edges[from_index].add_edge(EdgeKind::Dep(dep.kind()), dep_index);
461            }
462        }
463    }
464    if opts.graph_features {
465        assert!(graph
466            .dep_name_map
467            .insert(from_index, dep_name_map)
468            .is_none());
469    }
470
471    from_index
472}
473
474/// Adds a feature node between two nodes.
475///
476/// That is, it adds the following:
477///
478/// ```text
479/// from -Edge-> featname -Edge::Feature-> to
480/// ```
481///
482/// Returns a tuple `(missing, index)`.
483/// `missing` is true if this feature edge was already added.
484/// `index` is the index of the index in the graph of the `Feature` node.
485fn add_feature(
486    graph: &mut Graph<'_>,
487    name: InternedString,
488    from: Option<usize>,
489    to: usize,
490    kind: EdgeKind,
491) -> (bool, usize) {
492    // `to` *must* point to a package node.
493    assert!(matches! {graph.nodes[to], Node::Package{..}});
494    let node = Node::Feature {
495        node_index: to,
496        name,
497    };
498    let (missing, node_index) = match graph.index.get(&node) {
499        Some(idx) => (false, *idx),
500        None => (true, graph.add_node(node)),
501    };
502    if let Some(from) = from {
503        graph.edges[from].add_edge(kind, node_index);
504    }
505    graph.edges[node_index].add_edge(EdgeKind::Feature, to);
506    (missing, node_index)
507}
508
509/// Adds nodes for features requested on the command-line for the given member.
510///
511/// Feature nodes are added as "roots" (i.e., they have no "from" index),
512/// because they come from the outside world. They usually only appear with
513/// `--invert`.
514fn add_cli_features(
515    graph: &mut Graph<'_>,
516    package_index: usize,
517    cli_features: &CliFeatures,
518    feature_map: &FeatureMap,
519) {
520    // NOTE: Recursive enabling of features will be handled by
521    // add_internal_features.
522
523    // Create a set of feature names requested on the command-line.
524    let mut to_add: HashSet<FeatureValue> = HashSet::new();
525    if cli_features.all_features {
526        to_add.extend(feature_map.keys().map(|feat| FeatureValue::Feature(*feat)));
527    }
528
529    if cli_features.uses_default_features {
530        to_add.insert(FeatureValue::Feature(INTERNED_DEFAULT));
531    }
532    to_add.extend(cli_features.features.iter().cloned());
533
534    // Add each feature as a node, and mark as "from command-line" in graph.cli_features.
535    for fv in to_add {
536        match fv {
537            FeatureValue::Feature(feature) => {
538                let index = add_feature(graph, feature, None, package_index, EdgeKind::Feature).1;
539                graph.cli_features.insert(index);
540            }
541            // This is enforced by CliFeatures.
542            FeatureValue::Dep { .. } => panic!("unexpected cli dep feature {}", fv),
543            FeatureValue::DepFeature {
544                dep_name,
545                dep_feature,
546                weak,
547            } => {
548                let dep_connections = match graph.dep_name_map[&package_index].get(&dep_name) {
549                    // Clone to deal with immutable borrow of `graph`. :(
550                    Some(dep_connections) => dep_connections.clone(),
551                    None => {
552                        // --features bar?/feat where `bar` is not activated should be ignored.
553                        // If this wasn't weak, then this is a bug.
554                        if weak {
555                            continue;
556                        }
557                        panic!(
558                            "missing dep graph connection for CLI feature `{}` for member {:?}\n\
559                             Please file a bug report at https://github.com/rust-lang/cargo/issues",
560                            fv,
561                            graph.nodes.get(package_index)
562                        );
563                    }
564                };
565                for (dep_index, is_optional) in dep_connections {
566                    if is_optional {
567                        // Activate the optional dep on self.
568                        let index =
569                            add_feature(graph, dep_name, None, package_index, EdgeKind::Feature).1;
570                        graph.cli_features.insert(index);
571                    }
572                    let index =
573                        add_feature(graph, dep_feature, None, dep_index, EdgeKind::Feature).1;
574                    graph.cli_features.insert(index);
575                }
576            }
577        }
578    }
579}
580
581/// Recursively adds connections between features in the `[features]` table
582/// for every package.
583fn add_internal_features(graph: &mut Graph<'_>, resolve: &Resolve) {
584    // Collect features already activated by dependencies or command-line.
585    let feature_nodes: Vec<(PackageId, usize, usize, InternedString)> = graph
586        .nodes
587        .iter()
588        .enumerate()
589        .filter_map(|(i, node)| match node {
590            Node::Package { .. } => None,
591            Node::Feature { node_index, name } => {
592                let package_id = graph.package_id_for_index(*node_index);
593                Some((package_id, *node_index, i, *name))
594            }
595        })
596        .collect();
597
598    for (package_id, package_index, feature_index, feature_name) in feature_nodes {
599        add_feature_rec(
600            graph,
601            resolve,
602            feature_name,
603            package_id,
604            feature_index,
605            package_index,
606        );
607    }
608}
609
610/// Recursively add feature nodes for all features enabled by the given feature.
611///
612/// `from` is the index of the node that enables this feature.
613/// `package_index` is the index of the package node for the feature.
614fn add_feature_rec(
615    graph: &mut Graph<'_>,
616    resolve: &Resolve,
617    feature_name: InternedString,
618    package_id: PackageId,
619    from: usize,
620    package_index: usize,
621) {
622    let feature_map = resolve.summary(package_id).features();
623    let Some(fvs) = feature_map.get(&feature_name) else {
624        return;
625    };
626    for fv in fvs {
627        match fv {
628            FeatureValue::Feature(dep_name) => {
629                let (missing, feat_index) = add_feature(
630                    graph,
631                    *dep_name,
632                    Some(from),
633                    package_index,
634                    EdgeKind::Feature,
635                );
636                // Don't recursive if the edge already exists to deal with cycles.
637                if missing {
638                    add_feature_rec(
639                        graph,
640                        resolve,
641                        *dep_name,
642                        package_id,
643                        feat_index,
644                        package_index,
645                    );
646                }
647            }
648            // Dependencies are already shown in the graph as dep edges. I'm
649            // uncertain whether or not this might be confusing in some cases
650            // (like feature `"somefeat" = ["dep:somedep"]`), so maybe in the
651            // future consider explicitly showing this?
652            FeatureValue::Dep { .. } => {}
653            FeatureValue::DepFeature {
654                dep_name,
655                dep_feature,
656                // Note: `weak` is mostly handled when the graph is built in
657                // `is_dep_activated` which is responsible for skipping
658                // unactivated weak dependencies. Here it is only used to
659                // determine if the feature of the dependency name is
660                // activated on self.
661                weak,
662            } => {
663                let dep_indexes = match graph.dep_name_map[&package_index].get(dep_name) {
664                    Some(indexes) => indexes.clone(),
665                    None => {
666                        tracing::debug!(
667                            "enabling feature {} on {}, found {}/{}, \
668                             dep appears to not be enabled",
669                            feature_name,
670                            package_id,
671                            dep_name,
672                            dep_feature
673                        );
674                        continue;
675                    }
676                };
677                for (dep_index, is_optional) in dep_indexes {
678                    let dep_pkg_id = graph.package_id_for_index(dep_index);
679                    if is_optional && !weak {
680                        // Activate the optional dep on self.
681                        add_feature(
682                            graph,
683                            *dep_name,
684                            Some(from),
685                            package_index,
686                            EdgeKind::Feature,
687                        );
688                    }
689                    let (missing, feat_index) = add_feature(
690                        graph,
691                        *dep_feature,
692                        Some(from),
693                        dep_index,
694                        EdgeKind::Feature,
695                    );
696                    if missing {
697                        add_feature_rec(
698                            graph,
699                            resolve,
700                            *dep_feature,
701                            dep_pkg_id,
702                            feat_index,
703                            dep_index,
704                        );
705                    }
706                }
707            }
708        }
709    }
710}