    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251

    //! Types and impls for [`Unit`].

    use crate::core::compiler::unit_dependencies::IsArtifact;
    use crate::core::compiler::{CompileKind, CompileMode, CompileTarget, CrateType};
    use crate::core::manifest::{Target, TargetKind};
    use crate::core::profiles::Profile;
    use crate::core::Package;
    use crate::util::hex::short_hash;
    use crate::util::interning::InternedString;
    use crate::util::Config;
    use std::cell::RefCell;
    use std::collections::HashSet;
    use std::fmt;
    use std::hash::{Hash, Hasher};
    use std::ops::Deref;
    use std::rc::Rc;

    /// 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-triple* (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>,
        // 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::core::resolver::features::FeaturesFor::ArtifactDep
        pub artifact_target_for_features: Option<CompileTarget>,
    }

    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 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, config: &Config) -> bool {
            self.is_local() || config.extra_verbose()
        }
    }

    impl Unit {
        /// Gets the unique key for [`-Zbuild-plan`].
        ///
        /// [`-Zbuild-plan`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-plan
        pub fn buildkey(&self) -> String {
            format!("{}-{}", self.pkg.name(), short_hash(self))
        }
    }

    // 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("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::new(),
                }),
            }
        }

        /// 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>,
            is_std: bool,
            dep_hash: u64,
            artifact: IsArtifact,
            artifact_target_for_features: Option<CompileTarget>,
        ) -> 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,
                is_std,
                dep_hash,
                artifact,
                artifact_target_for_features,
            });
            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
        }
    }