Skip to main content

cargo/compiler/
unit.rs

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