1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
use crate::core::compiler::{CompileKind, CompileMode, CrateType};
use crate::core::manifest::{Target, TargetKind};
use crate::core::{profiles::Profile, Package};
use crate::util::hex::short_hash;
use crate::util::interning::InternedString;
use crate::util::Config;
use std::cell::RefCell;
use std::collections::HashSet;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use std::rc::Rc;
/// All information needed to define a unit.
///
/// A unit is an object that has enough information so that cargo knows how to build it.
/// For example, if your package has dependencies, then every dependency will be built as a library
/// unit. If your package is a library, then it will be built as a library unit as well, or if it
/// is a binary with `main.rs`, then a binary will be output. There are also separate unit types
/// for `test`ing and `check`ing, amongst others.
///
/// The unit also holds information about all possible metadata about the package in `pkg`.
///
/// A unit needs to know extra information in addition to the type and root source file. For
/// example, it needs to know the target architecture (OS, chip arch etc.) and it needs to know
/// whether you want a debug or release build. There is enough information in this struct to figure
/// all that out.
#[derive(Clone, PartialOrd, Ord)]
pub struct Unit {
inner: Rc<UnitInner>,
}
/// Internal fields of `Unit` which `Unit` will dereference to.
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct UnitInner {
/// Information about available targets, which files to include/exclude, etc. Basically stuff in
/// `Cargo.toml`.
pub pkg: Package,
/// Information about the specific target to build, out of the possible targets in `pkg`. Not
/// to be confused with *target-triple* (or *target architecture* ...), the target arch for a
/// build.
pub target: Target,
/// The profile contains information about *how* the build should be run, including debug
/// level, etc.
pub profile: Profile,
/// Whether this compilation unit is for the host or target architecture.
///
/// For example, when
/// cross compiling and using a custom build script, the build script needs to be compiled for
/// the host architecture so the host rustc can use it (when compiling to the target
/// architecture).
pub kind: CompileKind,
/// The "mode" this unit is being compiled for. See [`CompileMode`] for more details.
pub mode: CompileMode,
/// The `cfg` features to enable for this unit.
/// This must be sorted.
pub features: Vec<InternedString>,
/// Whether this is a standard library unit.
pub is_std: bool,
/// A hash of all dependencies of this unit.
///
/// This is used to keep the `Unit` unique in the situation where two
/// otherwise identical units need to link to different dependencies. This
/// can happen, for example, when there are shared dependencies that need
/// to be built with different features between normal and build
/// dependencies. See `rebuild_unit_graph_shared` for more on why this is
/// done.
///
/// This value initially starts as 0, and then is filled in via a
/// second-pass after all the unit dependencies have been computed.
pub dep_hash: u64,
}
impl UnitInner {
/// Returns whether compilation of this unit requires all upstream artifacts
/// to be available.
///
/// This effectively means that this unit is a synchronization point (if the
/// return value is `true`) that all previously pipelined units need to
/// finish in their entirety before this one is started.
pub fn requires_upstream_objects(&self) -> bool {
self.mode.is_any_test() || self.target.kind().requires_upstream_objects()
}
/// Returns whether or not this is a "local" package.
///
/// A "local" package is one that the user can likely edit, or otherwise
/// wants warnings, etc.
pub fn is_local(&self) -> bool {
self.pkg.package_id().source_id().is_path() && !self.is_std
}
/// Returns whether or not warnings should be displayed for this unit.
pub fn show_warnings(&self, config: &Config) -> bool {
self.is_local() || config.extra_verbose()
}
}
impl Unit {
pub fn buildkey(&self) -> String {
format!("{}-{}", self.pkg.name(), short_hash(self))
}
}
// Just hash the pointer for fast hashing
impl Hash for Unit {
fn hash<H: Hasher>(&self, hasher: &mut H) {
std::ptr::hash(&*self.inner, hasher)
}
}
// Just equate the pointer since these are interned
impl PartialEq for Unit {
fn eq(&self, other: &Unit) -> bool {
std::ptr::eq(&*self.inner, &*other.inner)
}
}
impl Eq for Unit {}
impl Deref for Unit {
type Target = UnitInner;
fn deref(&self) -> &UnitInner {
&*self.inner
}
}
impl fmt::Debug for Unit {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Unit")
.field("pkg", &self.pkg)
.field("target", &self.target)
.field("profile", &self.profile)
.field("kind", &self.kind)
.field("mode", &self.mode)
.field("features", &self.features)
.field("is_std", &self.is_std)
.field("dep_hash", &self.dep_hash)
.finish()
}
}
/// A small structure used to "intern" `Unit` values.
///
/// A `Unit` is just a thin pointer to an internal `UnitInner`. This is done to
/// ensure that `Unit` itself is quite small as well as enabling a very
/// efficient hash/equality implementation for `Unit`. All units are
/// manufactured through an interner which guarantees that each equivalent value
/// is only produced once.
pub struct UnitInterner {
state: RefCell<InternerState>,
}
struct InternerState {
cache: HashSet<Rc<UnitInner>>,
}
impl UnitInterner {
/// Creates a new blank interner
pub fn new() -> UnitInterner {
UnitInterner {
state: RefCell::new(InternerState {
cache: HashSet::new(),
}),
}
}
/// Creates a new `unit` from its components. The returned `Unit`'s fields
/// will all be equivalent to the provided arguments, although they may not
/// be the exact same instance.
pub fn intern(
&self,
pkg: &Package,
target: &Target,
profile: Profile,
kind: CompileKind,
mode: CompileMode,
features: Vec<InternedString>,
is_std: bool,
dep_hash: u64,
) -> Unit {
let target = match (is_std, target.kind()) {
// This is a horrible hack to support build-std. `libstd` declares
// itself with both rlib and dylib. We don't want the dylib for a
// few reasons:
//
// - dylibs don't have a hash in the filename. If you do something
// (like switch rustc versions), it will stomp on the dylib
// file, invalidating the entire cache (because std is a dep of
// everything).
// - We don't want to publicize the presence of dylib for the
// standard library.
//
// At some point in the future, it would be nice to have a
// first-class way of overriding or specifying crate-types.
(true, TargetKind::Lib(crate_types)) if crate_types.contains(&CrateType::Dylib) => {
let mut new_target = Target::clone(target);
new_target.set_kind(TargetKind::Lib(vec![CrateType::Rlib]));
new_target
}
_ => target.clone(),
};
let inner = self.intern_inner(&UnitInner {
pkg: pkg.clone(),
target,
profile,
kind,
mode,
features,
is_std,
dep_hash,
});
Unit { inner }
}
fn intern_inner(&self, item: &UnitInner) -> Rc<UnitInner> {
let mut me = self.state.borrow_mut();
if let Some(item) = me.cache.get(item) {
return item.clone();
}
let item = Rc::new(item.clone());
me.cache.insert(item.clone());
item
}
}