| //! Serialization of [`UnitGraph`] for unstable option [`--unit-graph`]. | |
| //! | |
| //! [`--unit-graph`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#unit-graph | |
| use cargo_util_schemas::core::PackageIdSpec; | |
| use crate::GlobalContext; | |
| use crate::compiler::Unit; | |
| use crate::compiler::{CompileKind, CompileMode}; | |
| use crate::util::CargoResult; | |
| use crate::util::Unhashed; | |
| use crate::util::data_structures::HashMap; | |
| use crate::util::interning::InternedString; | |
| use crate::workspace::Target; | |
| use crate::workspace::dependency::Dependency; | |
| use crate::workspace::profiles::{Profile, UnitFor}; | |
| /// 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, | |
| /// The dependency name as written in the manifest (including a rename). | |
| /// | |
| /// `None` means this edge does not carry a manifest dep name. For example, | |
| /// std edges in build-std or synthetic edges for build script executions. | |
| /// When `None`, the package name is typically used by callers as a fallback. | |
| /// | |
| /// This is mainly for Cargo-synthesized outputs | |
| /// (artifact env vars and `CARGO_DEP_*` metadata env) | |
| /// and is distinct from `extern_crate_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, | |
| /// If `true`, the dependency will not trigger Rust lint. | |
| pub nounused: bool, | |
| /// The manifest dependency that gave rise to this dependency | |
| /// | |
| /// Skip hashing as this is redundant and for book keekping purposes only | |
| pub manifest_deps: Unhashed<Option<Vec<Dependency>>>, | |
| } | |
| 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: PackageIdSpec, | |
| 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>, | |
| // This is only set on nightly since it is unstable. | |
| #[serde(skip_serializing_if = "Option::is_none")] | |
| nounused: 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, | |
| gctx: &GlobalContext, | |
| ) -> 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, nounused) = if gctx.nightly_features_allowed { | |
| ( | |
| Some(unit_dep.public), | |
| Some(unit_dep.noprelude), | |
| Some(unit_dep.nounused), | |
| ) | |
| } else { | |
| (None, None, None) | |
| }; | |
| SerializedUnitDep { | |
| index: indices[&unit_dep.unit], | |
| extern_crate_name: unit_dep.extern_crate_name, | |
| public, | |
| noprelude, | |
| nounused, | |
| } | |
| }) | |
| .collect(); | |
| SerializedUnit { | |
| pkg_id: unit.pkg.package_id().to_spec(), | |
| target: &unit.target, | |
| profile: &unit.profile, | |
| platform: unit.kind, | |
| mode: unit.mode, | |
| features: &unit.features, | |
| is_std: unit.is_std, | |
| dependencies, | |
| } | |
| }) | |
| .collect(); | |
| gctx.shell().print_json(&SerializedUnitGraph { | |
| version: VERSION, | |
| units: ser_units, | |
| roots, | |
| }) | |
| } |