| //! Types and impls for [`Unit`]. | |
| use serde::Deserialize; | |
| use serde::Serialize; | |
| use super::BuildOutput; | |
| use crate::compiler::unit_dependencies::IsArtifact; | |
| use crate::compiler::{CompileKind, CompileMode, CompileTarget, CrateType}; | |
| use crate::util::GlobalContext; | |
| use crate::util::data_structures::HashSet; | |
| use crate::util::interning::InternedString; | |
| use crate::workspace::Package; | |
| use crate::workspace::manifest::{Target, TargetKind}; | |
| use crate::workspace::profiles::Profile; | |
| use std::cell::RefCell; | |
| use std::collections::BTreeMap; | |
| use std::fmt; | |
| use std::hash::{Hash, Hasher}; | |
| use std::ops::Deref; | |
| use std::rc::Rc; | |
| /// Stable identifier for referencing a [`Unit`]. | |
| /// | |
| /// This is an index into the unit graph, assigned when units are registered. | |
| /// It provides a compact way to reference units. | |
| #[derive( | |
| Serialize, Deserialize, Debug, Default, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, | |
| )] | |
| #[serde(transparent)] | |
| pub struct UnitIndex(pub u64); | |
| impl fmt::Display for UnitIndex { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| write!(f, "{}", self.0) | |
| } | |
| } | |
| /// All information needed to define a unit. | |
| /// | |
| /// A unit is an object that has enough information so that cargo knows how to build it. | |
| /// For example, if your package has dependencies, then every dependency will be built as a library | |
| /// unit. If your package is a library, then it will be built as a library unit as well, or if it | |
| /// is a binary with `main.rs`, then a binary will be output. There are also separate unit types | |
| /// for `test`ing and `check`ing, amongst others. | |
| /// | |
| /// The unit also holds information about all possible metadata about the package in `pkg`. | |
| /// | |
| /// A unit needs to know extra information in addition to the type and root source file. For | |
| /// example, it needs to know the target architecture (OS, chip arch etc.) and it needs to know | |
| /// whether you want a debug or release build. There is enough information in this struct to figure | |
| /// all that out. | |
| #[derive(Clone, PartialOrd, Ord)] | |
| pub struct Unit { | |
| inner: Rc<UnitInner>, | |
| } | |
| /// Internal fields of `Unit` which `Unit` will dereference to. | |
| #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] | |
| pub struct UnitInner { | |
| /// Information about available targets, which files to include/exclude, etc. Basically stuff in | |
| /// `Cargo.toml`. | |
| pub pkg: Package, | |
| /// Information about the specific target to build, out of the possible targets in `pkg`. Not | |
| /// to be confused with *target-tuple* (or *target architecture* ...), the target arch for a | |
| /// build. | |
| pub target: Target, | |
| /// The profile contains information about *how* the build should be run, including debug | |
| /// level, etc. | |
| pub profile: Profile, | |
| /// Whether this compilation unit is for the host or target architecture. | |
| /// | |
| /// For example, when | |
| /// cross compiling and using a custom build script, the build script needs to be compiled for | |
| /// the host architecture so the host rustc can use it (when compiling to the target | |
| /// architecture). | |
| pub kind: CompileKind, | |
| /// The "mode" this unit is being compiled for. See [`CompileMode`] for more details. | |
| pub mode: CompileMode, | |
| /// The `cfg` features to enable for this unit. | |
| /// This must be sorted. | |
| pub features: Vec<InternedString>, | |
| /// Extra compiler flags to pass to `rustc` for a given unit. | |
| /// | |
| /// Although it depends on the caller, in the current Cargo implementation, | |
| /// these flags take precedence over those from [`BuildContext::extra_args_for`]. | |
| /// | |
| /// As of now, these flags come from environment variables and configurations. | |
| /// See [`TargetInfo.rustflags`] for more on how Cargo collects them. | |
| /// | |
| /// [`BuildContext::extra_args_for`]: crate::compiler::build_context::BuildContext::extra_args_for | |
| /// [`TargetInfo.rustflags`]: crate::compiler::build_context::TargetInfo::rustflags | |
| pub rustflags: Rc<[String]>, | |
| /// Extra compiler flags to pass to `rustdoc` for a given unit. | |
| /// | |
| /// Although it depends on the caller, in the current Cargo implementation, | |
| /// these flags take precedence over those from [`BuildContext::extra_args_for`]. | |
| /// | |
| /// As of now, these flags come from environment variables and configurations. | |
| /// See [`TargetInfo.rustdocflags`] for more on how Cargo collects them. | |
| /// | |
| /// [`BuildContext::extra_args_for`]: crate::compiler::build_context::BuildContext::extra_args_for | |
| /// [`TargetInfo.rustdocflags`]: crate::compiler::build_context::TargetInfo::rustdocflags | |
| pub rustdocflags: Rc<[String]>, | |
| /// Build script override for the given library name. | |
| /// | |
| /// Any package with a `links` value for the given library name will skip | |
| /// running its build script and instead use the given output from the | |
| /// config file. | |
| pub links_overrides: Rc<BTreeMap<String, BuildOutput>>, | |
| // if `true`, the dependency is an artifact dependency, requiring special handling when | |
| // calculating output directories, linkage and environment variables provided to builds. | |
| pub artifact: IsArtifact, | |
| /// Whether this is a standard library unit. | |
| pub is_std: bool, | |
| /// A hash of all dependencies of this unit. | |
| /// | |
| /// This is used to keep the `Unit` unique in the situation where two | |
| /// otherwise identical units need to link to different dependencies. This | |
| /// can happen, for example, when there are shared dependencies that need | |
| /// to be built with different features between normal and build | |
| /// dependencies. See `rebuild_unit_graph_shared` for more on why this is | |
| /// done. | |
| /// | |
| /// This value initially starts as 0, and then is filled in via a | |
| /// second-pass after all the unit dependencies have been computed. | |
| pub dep_hash: u64, | |
| /// This is used for target-dependent feature resolution and is copied from | |
| /// [`FeaturesFor::ArtifactDep`], if the enum matches the variant. | |
| /// | |
| /// [`FeaturesFor::ArtifactDep`]: crate::resolver::features::FeaturesFor::ArtifactDep | |
| pub artifact_target_for_features: Option<CompileTarget>, | |
| /// Skip compiling this unit because `--compile-time-deps` flag is set and | |
| /// this is not a compile time dependency. | |
| /// | |
| /// Since dependencies of this unit might be compile time dependencies, we | |
| /// set this field instead of completely dropping out this unit from unit graph. | |
| pub skip_non_compile_time_dep: bool, | |
| } | |
| impl UnitInner { | |
| /// Returns whether compilation of this unit requires all upstream artifacts | |
| /// to be available. | |
| /// | |
| /// This effectively means that this unit is a synchronization point (if the | |
| /// return value is `true`) that all previously pipelined units need to | |
| /// finish in their entirety before this one is started. | |
| pub fn requires_upstream_objects(&self) -> bool { | |
| self.mode.is_any_test() || self.target.kind().requires_upstream_objects() | |
| } | |
| /// Returns whether compilation of this unit could benefit from splitting metadata | |
| /// into a .rmeta file. | |
| pub fn benefits_from_no_embed_metadata(&self) -> bool { | |
| matches!(self.mode, CompileMode::Build) | |
| && self.target.kind().benefits_from_no_embed_metadata() | |
| } | |
| /// Returns whether or not this is a "local" package. | |
| /// | |
| /// A "local" package is one that the user can likely edit, or otherwise | |
| /// wants warnings, etc. | |
| pub fn is_local(&self) -> bool { | |
| self.pkg.package_id().source_id().is_path() && !self.is_std | |
| } | |
| /// Returns whether or not warnings should be displayed for this unit. | |
| pub fn show_warnings(&self, gctx: &GlobalContext) -> bool { | |
| self.is_local() || gctx.extra_verbose() | |
| } | |
| } | |
| // Just hash the pointer for fast hashing | |
| impl Hash for Unit { | |
| fn hash<H: Hasher>(&self, hasher: &mut H) { | |
| std::ptr::hash(&*self.inner, hasher) | |
| } | |
| } | |
| // Just equate the pointer since these are interned | |
| impl PartialEq for Unit { | |
| fn eq(&self, other: &Unit) -> bool { | |
| std::ptr::eq(&*self.inner, &*other.inner) | |
| } | |
| } | |
| impl Eq for Unit {} | |
| impl Deref for Unit { | |
| type Target = UnitInner; | |
| fn deref(&self) -> &UnitInner { | |
| &*self.inner | |
| } | |
| } | |
| impl fmt::Debug for Unit { | |
| fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | |
| f.debug_struct("Unit") | |
| .field("pkg", &self.pkg) | |
| .field("target", &self.target) | |
| .field("profile", &self.profile) | |
| .field("kind", &self.kind) | |
| .field("mode", &self.mode) | |
| .field("features", &self.features) | |
| .field("rustflags", &self.rustflags) | |
| .field("rustdocflags", &self.rustdocflags) | |
| .field("links_overrides", &self.links_overrides) | |
| .field("artifact", &self.artifact.is_true()) | |
| .field( | |
| "artifact_target_for_features", | |
| &self.artifact_target_for_features, | |
| ) | |
| .field("is_std", &self.is_std) | |
| .field("dep_hash", &self.dep_hash) | |
| .finish() | |
| } | |
| } | |
| /// A small structure used to "intern" `Unit` values. | |
| /// | |
| /// A `Unit` is just a thin pointer to an internal `UnitInner`. This is done to | |
| /// ensure that `Unit` itself is quite small as well as enabling a very | |
| /// efficient hash/equality implementation for `Unit`. All units are | |
| /// manufactured through an interner which guarantees that each equivalent value | |
| /// is only produced once. | |
| pub struct UnitInterner { | |
| state: RefCell<InternerState>, | |
| } | |
| struct InternerState { | |
| cache: HashSet<Rc<UnitInner>>, | |
| } | |
| impl UnitInterner { | |
| /// Creates a new blank interner | |
| pub fn new() -> UnitInterner { | |
| UnitInterner { | |
| state: RefCell::new(InternerState { | |
| cache: HashSet::default(), | |
| }), | |
| } | |
| } | |
| /// Creates a new `unit` from its components. The returned `Unit`'s fields | |
| /// will all be equivalent to the provided arguments, although they may not | |
| /// be the exact same instance. | |
| pub fn intern( | |
| &self, | |
| pkg: &Package, | |
| target: &Target, | |
| profile: Profile, | |
| kind: CompileKind, | |
| mode: CompileMode, | |
| features: Vec<InternedString>, | |
| rustflags: Rc<[String]>, | |
| rustdocflags: Rc<[String]>, | |
| links_overrides: Rc<BTreeMap<String, BuildOutput>>, | |
| is_std: bool, | |
| dep_hash: u64, | |
| artifact: IsArtifact, | |
| artifact_target_for_features: Option<CompileTarget>, | |
| skip_non_compile_time_dep: bool, | |
| ) -> Unit { | |
| let target = match (is_std, target.kind()) { | |
| // This is a horrible hack to support build-std. `libstd` declares | |
| // itself with both rlib and dylib. We don't want the dylib for a | |
| // few reasons: | |
| // | |
| // - dylibs don't have a hash in the filename. If you do something | |
| // (like switch rustc versions), it will stomp on the dylib | |
| // file, invalidating the entire cache (because std is a dep of | |
| // everything). | |
| // - We don't want to publicize the presence of dylib for the | |
| // standard library. | |
| // | |
| // At some point in the future, it would be nice to have a | |
| // first-class way of overriding or specifying crate-types. | |
| (true, TargetKind::Lib(crate_types)) if crate_types.contains(&CrateType::Dylib) => { | |
| let mut new_target = Target::clone(target); | |
| new_target.set_kind(TargetKind::Lib(vec![CrateType::Rlib])); | |
| new_target | |
| } | |
| _ => target.clone(), | |
| }; | |
| let inner = self.intern_inner(&UnitInner { | |
| pkg: pkg.clone(), | |
| target, | |
| profile, | |
| kind, | |
| mode, | |
| features, | |
| rustflags, | |
| rustdocflags, | |
| links_overrides, | |
| is_std, | |
| dep_hash, | |
| artifact, | |
| artifact_target_for_features, | |
| skip_non_compile_time_dep, | |
| }); | |
| Unit { inner } | |
| } | |
| fn intern_inner(&self, item: &UnitInner) -> Rc<UnitInner> { | |
| let mut me = self.state.borrow_mut(); | |
| if let Some(item) = me.cache.get(item) { | |
| return item.clone(); | |
| } | |
| let item = Rc::new(item.clone()); | |
| me.cache.insert(item.clone()); | |
| item | |
| } | |
| } |