#
cargo/core/compiler/
unit.rs

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