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}