cargo/core/compiler/unit.rs
1use crate::core::compiler::{CompileKind, CompileMode};
2use crate::core::{profiles::Profile, InternedString, Package, Target};
3use crate::util::hex::short_hash;
4use std::cell::RefCell;
5use std::collections::HashSet;
6use std::fmt;
7use std::hash::{Hash, Hasher};
8use std::ops::Deref;
9
10/// All information needed to define a unit.
11///
12/// A unit is an object that has enough information so that cargo knows how to build it.
13/// For example, if your package has dependencies, then every dependency will be built as a library
14/// unit. If your package is a library, then it will be built as a library unit as well, or if it
15/// is a binary with `main.rs`, then a binary will be output. There are also separate unit types
16/// for `test`ing and `check`ing, amongst others.
17///
18/// The unit also holds information about all possible metadata about the package in `pkg`.
19///
20/// A unit needs to know extra information in addition to the type and root source file. For
21/// example, it needs to know the target architecture (OS, chip arch etc.) and it needs to know
22/// whether you want a debug or release build. There is enough information in this struct to figure
23/// all that out.
24#[derive(Clone, Copy, PartialOrd, Ord)]
25pub struct Unit<'a> {
26 inner: &'a UnitInner<'a>,
27}
28
29/// Internal fields of `Unit` which `Unit` will dereference to.
30#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
31pub struct UnitInner<'a> {
32 /// Information about available targets, which files to include/exclude, etc. Basically stuff in
33 /// `Cargo.toml`.
34 pub pkg: &'a Package,
35 /// Information about the specific target to build, out of the possible targets in `pkg`. Not
36 /// to be confused with *target-triple* (or *target architecture* ...), the target arch for a
37 /// build.
38 pub target: &'a Target,
39 /// The profile contains information about *how* the build should be run, including debug
40 /// level, etc.
41 pub profile: Profile,
42 /// Whether this compilation unit is for the host or target architecture.
43 ///
44 /// For example, when
45 /// cross compiling and using a custom build script, the build script needs to be compiled for
46 /// the host architecture so the host rustc can use it (when compiling to the target
47 /// architecture).
48 pub kind: CompileKind,
49 /// The "mode" this unit is being compiled for. See [`CompileMode`] for more details.
50 pub mode: CompileMode,
51 /// The `cfg` features to enable for this unit.
52 /// This must be sorted.
53 pub features: Vec<InternedString>,
54 /// Whether this is a standard library unit.
55 pub is_std: bool,
56}
57
58impl UnitInner<'_> {
59 /// Returns whether compilation of this unit requires all upstream artifacts
60 /// to be available.
61 ///
62 /// This effectively means that this unit is a synchronization point (if the
63 /// return value is `true`) that all previously pipelined units need to
64 /// finish in their entirety before this one is started.
65 pub fn requires_upstream_objects(&self) -> bool {
66 self.mode.is_any_test() || self.target.kind().requires_upstream_objects()
67 }
68}
69
70impl<'a> Unit<'a> {
71 pub fn buildkey(&self) -> String {
72 format!("{}-{}", self.pkg.name(), short_hash(self))
73 }
74}
75
76// Just hash the pointer for fast hashing
77impl<'a> Hash for Unit<'a> {
78 fn hash<H: Hasher>(&self, hasher: &mut H) {
79 (self.inner as *const UnitInner<'a>).hash(hasher)
80 }
81}
82
83// Just equate the pointer since these are interned
84impl<'a> PartialEq for Unit<'a> {
85 fn eq(&self, other: &Unit<'a>) -> bool {
86 self.inner as *const UnitInner<'a> == other.inner as *const UnitInner<'a>
87 }
88}
89
90impl<'a> Eq for Unit<'a> {}
91
92impl<'a> Deref for Unit<'a> {
93 type Target = UnitInner<'a>;
94
95 fn deref(&self) -> &UnitInner<'a> {
96 self.inner
97 }
98}
99
100impl<'a> fmt::Debug for Unit<'a> {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 f.debug_struct("Unit")
103 .field("pkg", &self.pkg)
104 .field("target", &self.target)
105 .field("profile", &self.profile)
106 .field("kind", &self.kind)
107 .field("mode", &self.mode)
108 .field("features", &self.features)
109 .finish()
110 }
111}
112
113/// A small structure used to "intern" `Unit` values.
114///
115/// A `Unit` is just a thin pointer to an internal `UnitInner`. This is done to
116/// ensure that `Unit` itself is quite small as well as enabling a very
117/// efficient hash/equality implementation for `Unit`. All units are
118/// manufactured through an interner which guarantees that each equivalent value
119/// is only produced once.
120pub struct UnitInterner<'a> {
121 state: RefCell<InternerState<'a>>,
122}
123
124struct InternerState<'a> {
125 cache: HashSet<Box<UnitInner<'a>>>,
126}
127
128impl<'a> UnitInterner<'a> {
129 /// Creates a new blank interner
130 pub fn new() -> UnitInterner<'a> {
131 UnitInterner {
132 state: RefCell::new(InternerState {
133 cache: HashSet::new(),
134 }),
135 }
136 }
137
138 /// Creates a new `unit` from its components. The returned `Unit`'s fields
139 /// will all be equivalent to the provided arguments, although they may not
140 /// be the exact same instance.
141 pub fn intern(
142 &'a self,
143 pkg: &'a Package,
144 target: &'a Target,
145 profile: Profile,
146 kind: CompileKind,
147 mode: CompileMode,
148 features: Vec<InternedString>,
149 is_std: bool,
150 ) -> Unit<'a> {
151 let inner = self.intern_inner(&UnitInner {
152 pkg,
153 target,
154 profile,
155 kind,
156 mode,
157 features,
158 is_std,
159 });
160 Unit { inner }
161 }
162
163 // Ok so interning here is a little unsafe, hence the usage of `unsafe`
164 // internally. The primary issue here is that we've got an internal cache of
165 // `UnitInner` instances added so far, but we may need to mutate it to add
166 // it, and the mutation for an interner happens behind a shared borrow.
167 //
168 // Our goal though is to escape the lifetime `borrow_mut` to the same
169 // lifetime as the borrowed passed into this function. That's where `unsafe`
170 // comes into play. What we're subverting here is resizing internally in the
171 // `HashSet` as well as overwriting previous keys in the `HashSet`.
172 //
173 // As a result we store `Box<UnitInner>` internally to have an extra layer
174 // of indirection. That way `*const UnitInner` is a stable address that
175 // doesn't change with `HashSet` resizing. Furthermore we're careful to
176 // never overwrite an entry once inserted.
177 //
178 // Ideally we'd use an off-the-shelf interner from crates.io which avoids a
179 // small amount of unsafety here, but at the time this was written one
180 // wasn't obviously available.
181 fn intern_inner(&'a self, item: &UnitInner<'a>) -> &'a UnitInner<'a> {
182 let mut me = self.state.borrow_mut();
183 if let Some(item) = me.cache.get(item) {
184 // note that `item` has type `&Box<UnitInner<'a>`. Use `&**` to
185 // convert that to `&UnitInner<'a>`, then do some trickery to extend
186 // the lifetime to the `'a` on the function here.
187 return unsafe { &*(&**item as *const UnitInner<'a>) };
188 }
189 me.cache.insert(Box::new(item.clone()));
190 let item = me.cache.get(item).unwrap();
191 unsafe { &*(&**item as *const UnitInner<'a>) }
192 }
193}