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}