Skip to main content

cargo/core/compiler/
unit_graph.rs

1//! Serialization of [`UnitGraph`] for unstable option [`--unit-graph`].
2//!
3//! [`--unit-graph`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#unit-graph
4
5use cargo_util_schemas::core::PackageIdSpec;
6
7use crate::GlobalContext;
8use crate::core::Target;
9use crate::core::compiler::Unit;
10use crate::core::compiler::{CompileKind, CompileMode};
11use crate::core::profiles::{Profile, UnitFor};
12use crate::util::CargoResult;
13use crate::util::interning::InternedString;
14use std::collections::HashMap;
15
16/// The dependency graph of Units.
17pub type UnitGraph = HashMap<Unit, Vec<UnitDep>>;
18
19/// A unit dependency.
20#[derive(Debug, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)]
21pub struct UnitDep {
22    /// The dependency unit.
23    pub unit: Unit,
24    /// The purpose of this dependency (a dependency for a test, or a build
25    /// script, etc.). Do not use this after the unit graph has been built.
26    pub unit_for: UnitFor,
27    /// The name the parent uses to refer to this dependency.
28    pub extern_crate_name: InternedString,
29    /// The dependency name as written in the manifest (including a rename).
30    ///
31    /// `None` means this edge does not carry a manifest dep name. For example,
32    /// std edges in build-std or synthetic edges for build script executions.
33    /// When `None`, the package name is typically used by callers as a fallback.
34    ///
35    /// This is mainly for Cargo-synthesized outputs
36    /// (artifact env vars and `CARGO_DEP_*` metadata env)
37    /// and is distinct from `extern_crate_name`.
38    pub dep_name: Option<InternedString>,
39    /// Whether or not this is a public dependency.
40    pub public: bool,
41    /// If `true`, the dependency should not be added to Rust's prelude.
42    pub noprelude: bool,
43    /// If `true`, the dependency will not trigger Rust lint.
44    pub nounused: bool,
45}
46
47const VERSION: u32 = 1;
48
49#[derive(serde::Serialize)]
50struct SerializedUnitGraph<'a> {
51    version: u32,
52    units: Vec<SerializedUnit<'a>>,
53    roots: Vec<usize>,
54}
55
56#[derive(serde::Serialize)]
57struct SerializedUnit<'a> {
58    pkg_id: PackageIdSpec,
59    target: &'a Target,
60    profile: &'a Profile,
61    platform: CompileKind,
62    mode: CompileMode,
63    features: &'a Vec<InternedString>,
64    #[serde(skip_serializing_if = "std::ops::Not::not")] // hide for unstable build-std
65    is_std: bool,
66    dependencies: Vec<SerializedUnitDep>,
67}
68
69#[derive(serde::Serialize)]
70struct SerializedUnitDep {
71    index: usize,
72    extern_crate_name: InternedString,
73    // This is only set on nightly since it is unstable.
74    #[serde(skip_serializing_if = "Option::is_none")]
75    public: Option<bool>,
76    // This is only set on nightly since it is unstable.
77    #[serde(skip_serializing_if = "Option::is_none")]
78    noprelude: Option<bool>,
79    // This is only set on nightly since it is unstable.
80    #[serde(skip_serializing_if = "Option::is_none")]
81    nounused: Option<bool>,
82    // Intentionally not including `unit_for` because it is a low-level
83    // internal detail that is mostly used for building the graph.
84}
85
86/// Outputs a JSON serialization of [`UnitGraph`] for given `root_units`
87/// to the standard output.
88pub fn emit_serialized_unit_graph(
89    root_units: &[Unit],
90    unit_graph: &UnitGraph,
91    gctx: &GlobalContext,
92) -> CargoResult<()> {
93    let mut units: Vec<(&Unit, &Vec<UnitDep>)> = unit_graph.iter().collect();
94    units.sort_unstable();
95    // Create a map for quick lookup for dependencies.
96    let indices: HashMap<&Unit, usize> = units
97        .iter()
98        .enumerate()
99        .map(|(i, val)| (val.0, i))
100        .collect();
101    let roots = root_units.iter().map(|root| indices[root]).collect();
102    let ser_units = units
103        .iter()
104        .map(|(unit, unit_deps)| {
105            let dependencies = unit_deps
106                .iter()
107                .map(|unit_dep| {
108                    // https://github.com/rust-lang/rust/issues/64260 when stabilized.
109                    let (public, noprelude, nounused) = if gctx.nightly_features_allowed {
110                        (
111                            Some(unit_dep.public),
112                            Some(unit_dep.noprelude),
113                            Some(unit_dep.nounused),
114                        )
115                    } else {
116                        (None, None, None)
117                    };
118                    SerializedUnitDep {
119                        index: indices[&unit_dep.unit],
120                        extern_crate_name: unit_dep.extern_crate_name,
121                        public,
122                        noprelude,
123                        nounused,
124                    }
125                })
126                .collect();
127            SerializedUnit {
128                pkg_id: unit.pkg.package_id().to_spec(),
129                target: &unit.target,
130                profile: &unit.profile,
131                platform: unit.kind,
132                mode: unit.mode,
133                features: &unit.features,
134                is_std: unit.is_std,
135                dependencies,
136            }
137        })
138        .collect();
139
140    gctx.shell().print_json(&SerializedUnitGraph {
141        version: VERSION,
142        units: ser_units,
143        roots,
144    })
145}