    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135

    //! Serialization of [`UnitGraph`] for unstable option [`--unit-graph`].
    //!
    //! [`--unit-graph`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#unit-graph

    use crate::core::compiler::Unit;
    use crate::core::compiler::{CompileKind, CompileMode};
    use crate::core::profiles::{Profile, UnitFor};
    use crate::core::{PackageId, Target};
    use crate::util::interning::InternedString;
    use crate::util::CargoResult;
    use crate::Config;
    use std::collections::HashMap;
    use std::io::Write;

    /// The dependency graph of Units.
    pub type UnitGraph = HashMap<Unit, Vec<UnitDep>>;

    /// A unit dependency.
    #[derive(Debug, Clone, Hash, Eq, PartialEq, PartialOrd, Ord)]
    pub struct UnitDep {
        /// The dependency unit.
        pub unit: Unit,
        /// The purpose of this dependency (a dependency for a test, or a build
        /// script, etc.). Do not use this after the unit graph has been built.
        pub unit_for: UnitFor,
        /// The name the parent uses to refer to this dependency.
        pub extern_crate_name: InternedString,
        /// If `Some`, the name of the dependency if renamed in toml.
        /// It's particularly interesting to artifact dependencies which rely on it
        /// for naming their environment variables. Note that the `extern_crate_name`
        /// cannot be used for this as it also may be the build target itself,
        /// which isn't always the renamed dependency name.
        pub dep_name: Option<InternedString>,
        /// Whether or not this is a public dependency.
        pub public: bool,
        /// If `true`, the dependency should not be added to Rust's prelude.
        pub noprelude: bool,
    }

    const VERSION: u32 = 1;

    #[derive(serde::Serialize)]
    struct SerializedUnitGraph<'a> {
        version: u32,
        units: Vec<SerializedUnit<'a>>,
        roots: Vec<usize>,
    }

    #[derive(serde::Serialize)]
    struct SerializedUnit<'a> {
        pkg_id: PackageId,
        target: &'a Target,
        profile: &'a Profile,
        platform: CompileKind,
        mode: CompileMode,
        features: &'a Vec<InternedString>,
        #[serde(skip_serializing_if = "std::ops::Not::not")] // hide for unstable build-std
        is_std: bool,
        dependencies: Vec<SerializedUnitDep>,
    }

    #[derive(serde::Serialize)]
    struct SerializedUnitDep {
        index: usize,
        extern_crate_name: InternedString,
        // This is only set on nightly since it is unstable.
        #[serde(skip_serializing_if = "Option::is_none")]
        public: Option<bool>,
        // This is only set on nightly since it is unstable.
        #[serde(skip_serializing_if = "Option::is_none")]
        noprelude: Option<bool>,
        // Intentionally not including `unit_for` because it is a low-level
        // internal detail that is mostly used for building the graph.
    }

    /// Outputs a JSON serialization of [`UnitGraph`] for given `root_units`
    /// to the standard output.
    pub fn emit_serialized_unit_graph(
        root_units: &[Unit],
        unit_graph: &UnitGraph,
        config: &Config,
    ) -> CargoResult<()> {
        let mut units: Vec<(&Unit, &Vec<UnitDep>)> = unit_graph.iter().collect();
        units.sort_unstable();
        // Create a map for quick lookup for dependencies.
        let indices: HashMap<&Unit, usize> = units
            .iter()
            .enumerate()
            .map(|(i, val)| (val.0, i))
            .collect();
        let roots = root_units.iter().map(|root| indices[root]).collect();
        let ser_units = units
            .iter()
            .map(|(unit, unit_deps)| {
                let dependencies = unit_deps
                    .iter()
                    .map(|unit_dep| {
                        // https://github.com/rust-lang/rust/issues/64260 when stabilized.
                        let (public, noprelude) = if config.nightly_features_allowed {
                            (Some(unit_dep.public), Some(unit_dep.noprelude))
                        } else {
                            (None, None)
                        };
                        SerializedUnitDep {
                            index: indices[&unit_dep.unit],
                            extern_crate_name: unit_dep.extern_crate_name,
                            public,
                            noprelude,
                        }
                    })
                    .collect();
                SerializedUnit {
                    pkg_id: unit.pkg.package_id(),
                    target: &unit.target,
                    profile: &unit.profile,
                    platform: unit.kind,
                    mode: unit.mode,
                    features: &unit.features,
                    is_std: unit.is_std,
                    dependencies,
                }
            })
            .collect();
        let s = SerializedUnitGraph {
            version: VERSION,
            units: ser_units,
            roots,
        };

        let stdout = std::io::stdout();
        let mut lock = stdout.lock();
        serde_json::to_writer(&mut lock, &s)?;
        drop(writeln!(lock));
        Ok(())
    }