#
cargo/core/compiler/
unit.rs

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