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
117impl UnitInner {
118    /// Returns whether compilation of this unit requires all upstream artifacts
119    /// to be available.
120    ///
121    /// This effectively means that this unit is a synchronization point (if the
122    /// return value is `true`) that all previously pipelined units need to
123    /// finish in their entirety before this one is started.
124    pub fn requires_upstream_objects(&self) -> bool {
125        self.mode.is_any_test() || self.target.kind().requires_upstream_objects()
126    }
127
128    /// Returns whether or not this is a "local" package.
129    ///
130    /// A "local" package is one that the user can likely edit, or otherwise
131    /// wants warnings, etc.
132    pub fn is_local(&self) -> bool {
133        self.pkg.package_id().source_id().is_path() && !self.is_std
134    }
135
136    /// Returns whether or not warnings should be displayed for this unit.
137    pub fn show_warnings(&self, gctx: &GlobalContext) -> bool {
138        self.is_local() || gctx.extra_verbose()
139    }
140}
141
142impl Unit {
143    /// Gets the unique key for [`-Zbuild-plan`].
144    ///
145    /// [`-Zbuild-plan`]: https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-plan
146    pub fn buildkey(&self) -> String {
147        format!("{}-{}", self.pkg.name(), short_hash(self))
148    }
149}
150
151// Just hash the pointer for fast hashing
152impl Hash for Unit {
153    fn hash<H: Hasher>(&self, hasher: &mut H) {
154        std::ptr::hash(&*self.inner, hasher)
155    }
156}
157
158// Just equate the pointer since these are interned
159impl PartialEq for Unit {
160    fn eq(&self, other: &Unit) -> bool {
161        std::ptr::eq(&*self.inner, &*other.inner)
162    }
163}
164
165impl Eq for Unit {}
166
167impl Deref for Unit {
168    type Target = UnitInner;
169
170    fn deref(&self) -> &UnitInner {
171        &*self.inner
172    }
173}
174
175impl fmt::Debug for Unit {
176    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
177        f.debug_struct("Unit")
178            .field("pkg", &self.pkg)
179            .field("target", &self.target)
180            .field("profile", &self.profile)
181            .field("kind", &self.kind)
182            .field("mode", &self.mode)
183            .field("features", &self.features)
184            .field("rustflags", &self.rustflags)
185            .field("rustdocflags", &self.rustdocflags)
186            .field("links_overrides", &self.links_overrides)
187            .field("artifact", &self.artifact.is_true())
188            .field(
189                "artifact_target_for_features",
190                &self.artifact_target_for_features,
191            )
192            .field("is_std", &self.is_std)
193            .field("dep_hash", &self.dep_hash)
194            .finish()
195    }
196}
197
198/// A small structure used to "intern" `Unit` values.
199///
200/// A `Unit` is just a thin pointer to an internal `UnitInner`. This is done to
201/// ensure that `Unit` itself is quite small as well as enabling a very
202/// efficient hash/equality implementation for `Unit`. All units are
203/// manufactured through an interner which guarantees that each equivalent value
204/// is only produced once.
205pub struct UnitInterner {
206    state: RefCell<InternerState>,
207}
208
209struct InternerState {
210    cache: HashSet<Rc<UnitInner>>,
211}
212
213impl UnitInterner {
214    /// Creates a new blank interner
215    pub fn new() -> UnitInterner {
216        UnitInterner {
217            state: RefCell::new(InternerState {
218                cache: HashSet::new(),
219            }),
220        }
221    }
222
223    /// Creates a new `unit` from its components. The returned `Unit`'s fields
224    /// will all be equivalent to the provided arguments, although they may not
225    /// be the exact same instance.
226    pub fn intern(
227        &self,
228        pkg: &Package,
229        target: &Target,
230        profile: Profile,
231        kind: CompileKind,
232        mode: CompileMode,
233        features: Vec<InternedString>,
234        rustflags: Rc<[String]>,
235        rustdocflags: Rc<[String]>,
236        links_overrides: Rc<BTreeMap<String, BuildOutput>>,
237        is_std: bool,
238        dep_hash: u64,
239        artifact: IsArtifact,
240        artifact_target_for_features: Option<CompileTarget>,
241    ) -> Unit {
242        let target = match (is_std, target.kind()) {
243            // This is a horrible hack to support build-std. `libstd` declares
244            // itself with both rlib and dylib. We don't want the dylib for a
245            // few reasons:
246            //
247            // - dylibs don't have a hash in the filename. If you do something
248            //   (like switch rustc versions), it will stomp on the dylib
249            //   file, invalidating the entire cache (because std is a dep of
250            //   everything).
251            // - We don't want to publicize the presence of dylib for the
252            //   standard library.
253            //
254            // At some point in the future, it would be nice to have a
255            // first-class way of overriding or specifying crate-types.
256            (true, TargetKind::Lib(crate_types)) if crate_types.contains(&CrateType::Dylib) => {
257                let mut new_target = Target::clone(target);
258                new_target.set_kind(TargetKind::Lib(vec![CrateType::Rlib]));
259                new_target
260            }
261            _ => target.clone(),
262        };
263        let inner = self.intern_inner(&UnitInner {
264            pkg: pkg.clone(),
265            target,
266            profile,
267            kind,
268            mode,
269            features,
270            rustflags,
271            rustdocflags,
272            links_overrides,
273            is_std,
274            dep_hash,
275            artifact,
276            artifact_target_for_features,
277        });
278        Unit { inner }
279    }
280
281    fn intern_inner(&self, item: &UnitInner) -> Rc<UnitInner> {
282        let mut me = self.state.borrow_mut();
283        if let Some(item) = me.cache.get(item) {
284            return item.clone();
285        }
286        let item = Rc::new(item.clone());
287        me.cache.insert(item.clone());
288        item
289    }
290}