#
cargo/core/compiler/
unit.rs

    1//! Types and impls for [`Unit`].
    2
    3use serde::Deserialize;
    4use serde::Serialize;
    5
    6use crate::core::Package;
    7use crate::core::compiler::unit_dependencies::IsArtifact;
    8use crate::core::compiler::{CompileKind, CompileMode, CompileTarget, CrateType};
    9use crate::core::manifest::{Target, TargetKind};
    10use crate::core::profiles::Profile;
    11use crate::util::GlobalContext;
    12use crate::util::interning::InternedString;
    13use std::cell::RefCell;
    14use std::collections::{BTreeMap, HashSet};
    15use std::fmt;
    16use std::hash::{Hash, Hasher};
    17use std::ops::Deref;
    18use std::rc::Rc;
    19
    20use super::BuildOutput;
    21
    22/// Stable identifier for referencing a [`Unit`].
    23///
    24/// This is an index into the unit graph, assigned when units are registered.
    25/// It provides a compact way to reference units.
    26#[derive(
    27    Serialize, Deserialize, Debug, Default, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord,
    28)]
    29#[serde(transparent)]
    30pub struct UnitIndex(pub u64);
    31
    32impl fmt::Display for UnitIndex {
    33    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    34        write!(f, "{}", self.0)
    35    }
    36}
    37
    38/// All information needed to define a unit.
    39///
    40/// A unit is an object that has enough information so that cargo knows how to build it.
    41/// For example, if your package has dependencies, then every dependency will be built as a library
    42/// unit. If your package is a library, then it will be built as a library unit as well, or if it
    43/// is a binary with `main.rs`, then a binary will be output. There are also separate unit types
    44/// for `test`ing and `check`ing, amongst others.
    45///
    46/// The unit also holds information about all possible metadata about the package in `pkg`.
    47///
    48/// A unit needs to know extra information in addition to the type and root source file. For
    49/// example, it needs to know the target architecture (OS, chip arch etc.) and it needs to know
    50/// whether you want a debug or release build. There is enough information in this struct to figure
    51/// all that out.
    52#[derive(Clone, PartialOrd, Ord)]
    53pub struct Unit {
    54    inner: Rc<UnitInner>,
    55}
    56
    57/// Internal fields of `Unit` which `Unit` will dereference to.
    58#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
    59pub struct UnitInner {
    60    /// Information about available targets, which files to include/exclude, etc. Basically stuff in
    61    /// `Cargo.toml`.
    62    pub pkg: Package,
    63    /// Information about the specific target to build, out of the possible targets in `pkg`. Not
    64    /// to be confused with *target-triple* (or *target architecture* ...), the target arch for a
    65    /// build.
    66    pub target: Target,
    67    /// The profile contains information about *how* the build should be run, including debug
    68    /// level, etc.
    69    pub profile: Profile,
    70    /// Whether this compilation unit is for the host or target architecture.
    71    ///
    72    /// For example, when
    73    /// cross compiling and using a custom build script, the build script needs to be compiled for
    74    /// the host architecture so the host rustc can use it (when compiling to the target
    75    /// architecture).
    76    pub kind: CompileKind,
    77    /// The "mode" this unit is being compiled for. See [`CompileMode`] for more details.
    78    pub mode: CompileMode,
    79    /// The `cfg` features to enable for this unit.
    80    /// This must be sorted.
    81    pub features: Vec<InternedString>,
    82    /// Extra compiler flags to pass to `rustc` for a given unit.
    83    ///
    84    /// Although it depends on the caller, in the current Cargo implementation,
    85    /// these flags take precedence over those from [`BuildContext::extra_args_for`].
    86    ///
    87    /// As of now, these flags come from environment variables and configurations.
    88    /// See [`TargetInfo.rustflags`] for more on how Cargo collects them.
    89    ///
    90    /// [`BuildContext::extra_args_for`]: crate::core::compiler::build_context::BuildContext::extra_args_for
    91    /// [`TargetInfo.rustflags`]: crate::core::compiler::build_context::TargetInfo::rustflags
    92    pub rustflags: Rc<[String]>,
    93    /// Extra compiler flags to pass to `rustdoc` for a given unit.
    94    ///
    95    /// Although it depends on the caller, in the current Cargo implementation,
    96    /// these flags take precedence over those from [`BuildContext::extra_args_for`].
    97    ///
    98    /// As of now, these flags come from environment variables and configurations.
    99    /// See [`TargetInfo.rustdocflags`] for more on how Cargo collects them.
    100    ///
    101    /// [`BuildContext::extra_args_for`]: crate::core::compiler::build_context::BuildContext::extra_args_for
    102    /// [`TargetInfo.rustdocflags`]: crate::core::compiler::build_context::TargetInfo::rustdocflags
    103    pub rustdocflags: Rc<[String]>,
    104    /// Build script override for the given library name.
    105    ///
    106    /// Any package with a `links` value for the given library name will skip
    107    /// running its build script and instead use the given output from the
    108    /// config file.
    109    pub links_overrides: Rc<BTreeMap<String, BuildOutput>>,
    110    // if `true`, the dependency is an artifact dependency, requiring special handling when
    111    // calculating output directories, linkage and environment variables provided to builds.
    112    pub artifact: IsArtifact,
    113    /// Whether this is a standard library unit.
    114    pub is_std: bool,
    115    /// A hash of all dependencies of this unit.
    116    ///
    117    /// This is used to keep the `Unit` unique in the situation where two
    118    /// otherwise identical units need to link to different dependencies. This
    119    /// can happen, for example, when there are shared dependencies that need
    120    /// to be built with different features between normal and build
    121    /// dependencies. See `rebuild_unit_graph_shared` for more on why this is
    122    /// done.
    123    ///
    124    /// This value initially starts as 0, and then is filled in via a
    125    /// second-pass after all the unit dependencies have been computed.
    126    pub dep_hash: u64,
    127
    128    /// This is used for target-dependent feature resolution and is copied from
    129    /// [`FeaturesFor::ArtifactDep`], if the enum matches the variant.
    130    ///
    131    /// [`FeaturesFor::ArtifactDep`]: crate::core::resolver::features::FeaturesFor::ArtifactDep
    132    pub artifact_target_for_features: Option<CompileTarget>,
    133
    134    /// Skip compiling this unit because `--compile-time-deps` flag is set and
    135    /// this is not a compile time dependency.
    136    ///
    137    /// Since dependencies of this unit might be compile time dependencies, we
    138    /// set this field instead of completely dropping out this unit from unit graph.
    139    pub skip_non_compile_time_dep: bool,
    140}
    141
    142impl UnitInner {
    143    /// Returns whether compilation of this unit requires all upstream artifacts
    144    /// to be available.
    145    ///
    146    /// This effectively means that this unit is a synchronization point (if the
    147    /// return value is `true`) that all previously pipelined units need to
    148    /// finish in their entirety before this one is started.
    149    pub fn requires_upstream_objects(&self) -> bool {
    150        self.mode.is_any_test() || self.target.kind().requires_upstream_objects()
    151    }
    152
    153    /// Returns whether compilation of this unit could benefit from splitting metadata
    154    /// into a .rmeta file.
    155    pub fn benefits_from_no_embed_metadata(&self) -> bool {
    156        matches!(self.mode, CompileMode::Build)
    157            && self.target.kind().benefits_from_no_embed_metadata()
    158    }
    159
    160    /// Returns whether or not this is a "local" package.
    161    ///
    162    /// A "local" package is one that the user can likely edit, or otherwise
    163    /// wants warnings, etc.
    164    pub fn is_local(&self) -> bool {
    165        self.pkg.package_id().source_id().is_path() && !self.is_std
    166    }
    167
    168    /// Returns whether or not warnings should be displayed for this unit.
    169    pub fn show_warnings(&self, gctx: &GlobalContext) -> bool {
    170        self.is_local() || gctx.extra_verbose()
    171    }
    172}
    173
    174// Just hash the pointer for fast hashing
    175impl Hash for Unit {
    176    fn hash<H: Hasher>(&self, hasher: &mut H) {
    177        std::ptr::hash(&*self.inner, hasher)
    178    }
    179}
    180
    181// Just equate the pointer since these are interned
    182impl PartialEq for Unit {
    183    fn eq(&self, other: &Unit) -> bool {
    184        std::ptr::eq(&*self.inner, &*other.inner)
    185    }
    186}
    187
    188impl Eq for Unit {}
    189
    190impl Deref for Unit {
    191    type Target = UnitInner;
    192
    193    fn deref(&self) -> &UnitInner {
    194        &*self.inner
    195    }
    196}
    197
    198impl fmt::Debug for Unit {
    199    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    200        f.debug_struct("Unit")
    201            .field("pkg", &self.pkg)
    202            .field("target", &self.target)
    203            .field("profile", &self.profile)
    204            .field("kind", &self.kind)
    205            .field("mode", &self.mode)
    206            .field("features", &self.features)
    207            .field("rustflags", &self.rustflags)
    208            .field("rustdocflags", &self.rustdocflags)
    209            .field("links_overrides", &self.links_overrides)
    210            .field("artifact", &self.artifact.is_true())
    211            .field(
    212                "artifact_target_for_features",
    213                &self.artifact_target_for_features,
    214            )
    215            .field("is_std", &self.is_std)
    216            .field("dep_hash", &self.dep_hash)
    217            .finish()
    218    }
    219}
    220
    221/// A small structure used to "intern" `Unit` values.
    222///
    223/// A `Unit` is just a thin pointer to an internal `UnitInner`. This is done to
    224/// ensure that `Unit` itself is quite small as well as enabling a very
    225/// efficient hash/equality implementation for `Unit`. All units are
    226/// manufactured through an interner which guarantees that each equivalent value
    227/// is only produced once.
    228pub struct UnitInterner {
    229    state: RefCell<InternerState>,
    230}
    231
    232struct InternerState {
    233    cache: HashSet<Rc<UnitInner>>,
    234}
    235
    236impl UnitInterner {
    237    /// Creates a new blank interner
    238    pub fn new() -> UnitInterner {
    239        UnitInterner {
    240            state: RefCell::new(InternerState {
    241                cache: HashSet::new(),
    242            }),
    243        }
    244    }
    245
    246    /// Creates a new `unit` from its components. The returned `Unit`'s fields
    247    /// will all be equivalent to the provided arguments, although they may not
    248    /// be the exact same instance.
    249    pub fn intern(
    250        &self,
    251        pkg: &Package,
    252        target: &Target,
    253        profile: Profile,
    254        kind: CompileKind,
    255        mode: CompileMode,
    256        features: Vec<InternedString>,
    257        rustflags: Rc<[String]>,
    258        rustdocflags: Rc<[String]>,
    259        links_overrides: Rc<BTreeMap<String, BuildOutput>>,
    260        is_std: bool,
    261        dep_hash: u64,
    262        artifact: IsArtifact,
    263        artifact_target_for_features: Option<CompileTarget>,
    264        skip_non_compile_time_dep: bool,
    265    ) -> Unit {
    266        let target = match (is_std, target.kind()) {
    267            // This is a horrible hack to support build-std. `libstd` declares
    268            // itself with both rlib and dylib. We don't want the dylib for a
    269            // few reasons:
    270            //
    271            // - dylibs don't have a hash in the filename. If you do something
    272            //   (like switch rustc versions), it will stomp on the dylib
    273            //   file, invalidating the entire cache (because std is a dep of
    274            //   everything).
    275            // - We don't want to publicize the presence of dylib for the
    276            //   standard library.
    277            //
    278            // At some point in the future, it would be nice to have a
    279            // first-class way of overriding or specifying crate-types.
    280            (true, TargetKind::Lib(crate_types)) if crate_types.contains(&CrateType::Dylib) => {
    281                let mut new_target = Target::clone(target);
    282                new_target.set_kind(TargetKind::Lib(vec![CrateType::Rlib]));
    283                new_target
    284            }
    285            _ => target.clone(),
    286        };
    287        let inner = self.intern_inner(&UnitInner {
    288            pkg: pkg.clone(),
    289            target,
    290            profile,
    291            kind,
    292            mode,
    293            features,
    294            rustflags,
    295            rustdocflags,
    296            links_overrides,
    297            is_std,
    298            dep_hash,
    299            artifact,
    300            artifact_target_for_features,
    301            skip_non_compile_time_dep,
    302        });
    303        Unit { inner }
    304    }
    305
    306    fn intern_inner(&self, item: &UnitInner) -> Rc<UnitInner> {
    307        let mut me = self.state.borrow_mut();
    308        if let Some(item) = me.cache.get(item) {
    309            return item.clone();
    310        }
    311        let item = Rc::new(item.clone());
    312        me.cache.insert(item.clone());
    313        item
    314    }
    315}