|
| 1 | +use crate::core::compiler::{CompileMode, Kind}; |
| 2 | +use crate::core::{profiles::Profile, Package, Target}; |
| 3 | +use crate::util::hex::short_hash; |
| 4 | +use std::cell::RefCell; |
| 5 | +use std::collections::HashSet; |
| 6 | +use std::fmt; |
| 7 | +use std::hash::{Hash, Hasher}; |
| 8 | +use 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)] |
| 25 | +pub 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)] |
| 31 | +pub 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: Kind, |
| 49 | + /// The "mode" this unit is being compiled for. See [`CompileMode`] for more details. |
| 50 | + pub mode: CompileMode, |
| 51 | +} |
| 52 | + |
| 53 | +impl<'a> Unit<'a> { |
| 54 | + pub fn buildkey(&self) -> String { |
| 55 | + format!("{}-{}", self.pkg.name(), short_hash(self)) |
| 56 | + } |
| 57 | +} |
| 58 | + |
| 59 | +// Just hash the pointer for fast hashing |
| 60 | +impl<'a> Hash for Unit<'a> { |
| 61 | + fn hash<H: Hasher>(&self, hasher: &mut H) { |
| 62 | + (self.inner as *const UnitInner<'a>).hash(hasher) |
| 63 | + } |
| 64 | +} |
| 65 | + |
| 66 | +// Just equate the pointer since these are interned |
| 67 | +impl<'a> PartialEq for Unit<'a> { |
| 68 | + fn eq(&self, other: &Unit<'a>) -> bool { |
| 69 | + self.inner as *const UnitInner<'a> == other.inner as *const UnitInner<'a> |
| 70 | + } |
| 71 | +} |
| 72 | + |
| 73 | +impl<'a> Eq for Unit<'a> {} |
| 74 | + |
| 75 | +impl<'a> Deref for Unit<'a> { |
| 76 | + type Target = UnitInner<'a>; |
| 77 | + |
| 78 | + fn deref(&self) -> &UnitInner<'a> { |
| 79 | + self.inner |
| 80 | + } |
| 81 | +} |
| 82 | + |
| 83 | +impl<'a> fmt::Debug for Unit<'a> { |
| 84 | + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 85 | + f.debug_struct("Unit") |
| 86 | + .field("pkg", &self.pkg) |
| 87 | + .field("target", &self.target) |
| 88 | + .field("profile", &self.profile) |
| 89 | + .field("kind", &self.kind) |
| 90 | + .field("mode", &self.mode) |
| 91 | + .finish() |
| 92 | + } |
| 93 | +} |
| 94 | + |
| 95 | +pub struct UnitInterner<'a> { |
| 96 | + state: RefCell<InternerState<'a>>, |
| 97 | +} |
| 98 | + |
| 99 | +struct InternerState<'a> { |
| 100 | + cache: HashSet<Box<UnitInner<'a>>>, |
| 101 | +} |
| 102 | + |
| 103 | +impl<'a> UnitInterner<'a> { |
| 104 | + pub fn new() -> UnitInterner<'a> { |
| 105 | + UnitInterner { |
| 106 | + state: RefCell::new(InternerState { |
| 107 | + cache: HashSet::new(), |
| 108 | + }), |
| 109 | + } |
| 110 | + } |
| 111 | + |
| 112 | + pub fn intern( |
| 113 | + &'a self, |
| 114 | + pkg: &'a Package, |
| 115 | + target: &'a Target, |
| 116 | + profile: Profile, |
| 117 | + kind: Kind, |
| 118 | + mode: CompileMode, |
| 119 | + ) -> Unit<'a> { |
| 120 | + let inner = self.intern_inner(&UnitInner { |
| 121 | + pkg, |
| 122 | + target, |
| 123 | + profile, |
| 124 | + kind, |
| 125 | + mode, |
| 126 | + }); |
| 127 | + Unit { inner } |
| 128 | + } |
| 129 | + |
| 130 | + fn intern_inner(&'a self, item: &UnitInner<'a>) -> &'a UnitInner<'a> { |
| 131 | + let mut me = self.state.borrow_mut(); |
| 132 | + if let Some(item) = me.cache.get(item) { |
| 133 | + return unsafe { &*(&**item as *const UnitInner<'a>) }; |
| 134 | + } |
| 135 | + me.cache.insert(Box::new(item.clone())); |
| 136 | + let item = me.cache.get(item).unwrap(); |
| 137 | + return unsafe { &*(&**item as *const UnitInner<'a>) }; |
| 138 | + } |
| 139 | +} |
0 commit comments