]> git.proxmox.com Git - cargo.git/blob - src/cargo/core/summary.rs
Add comment on relationship of RunCustomBuild and UnitFor::host.
[cargo.git] / src / cargo / core / summary.rs
1 use std::borrow::Borrow;
2 use std::collections::{BTreeMap, HashMap};
3 use std::fmt::Display;
4 use std::hash::{Hash, Hasher};
5 use std::mem;
6 use std::rc::Rc;
7
8 use serde::{Serialize, Serializer};
9
10 use crate::core::interning::InternedString;
11 use crate::core::{Dependency, PackageId, SourceId};
12 use semver::Version;
13
14 use crate::util::CargoResult;
15
16 /// Subset of a `Manifest`. Contains only the most important information about
17 /// a package.
18 ///
19 /// Summaries are cloned, and should not be mutated after creation
20 #[derive(Debug, Clone)]
21 pub struct Summary {
22 inner: Rc<Inner>,
23 }
24
25 #[derive(Debug, Clone)]
26 struct Inner {
27 package_id: PackageId,
28 dependencies: Vec<Dependency>,
29 features: Rc<FeatureMap>,
30 checksum: Option<String>,
31 links: Option<InternedString>,
32 namespaced_features: bool,
33 }
34
35 impl Summary {
36 pub fn new<K>(
37 pkg_id: PackageId,
38 dependencies: Vec<Dependency>,
39 features: &BTreeMap<K, Vec<impl AsRef<str>>>,
40 links: Option<impl Into<InternedString>>,
41 namespaced_features: bool,
42 ) -> CargoResult<Summary>
43 where
44 K: Borrow<str> + Ord + Display,
45 {
46 for dep in dependencies.iter() {
47 let feature = dep.name_in_toml();
48 if !namespaced_features && features.get(&*feature).is_some() {
49 anyhow::bail!(
50 "Features and dependencies cannot have the \
51 same name: `{}`",
52 feature
53 )
54 }
55 if dep.is_optional() && !dep.is_transitive() {
56 anyhow::bail!(
57 "Dev-dependencies are not allowed to be optional: `{}`",
58 feature
59 )
60 }
61 }
62 let feature_map = build_feature_map(features, &dependencies, namespaced_features)?;
63 Ok(Summary {
64 inner: Rc::new(Inner {
65 package_id: pkg_id,
66 dependencies,
67 features: Rc::new(feature_map),
68 checksum: None,
69 links: links.map(|l| l.into()),
70 namespaced_features,
71 }),
72 })
73 }
74
75 pub fn package_id(&self) -> PackageId {
76 self.inner.package_id
77 }
78 pub fn name(&self) -> InternedString {
79 self.package_id().name()
80 }
81 pub fn version(&self) -> &Version {
82 self.package_id().version()
83 }
84 pub fn source_id(&self) -> SourceId {
85 self.package_id().source_id()
86 }
87 pub fn dependencies(&self) -> &[Dependency] {
88 &self.inner.dependencies
89 }
90 pub fn features(&self) -> &FeatureMap {
91 &self.inner.features
92 }
93 pub fn checksum(&self) -> Option<&str> {
94 self.inner.checksum.as_ref().map(|s| &s[..])
95 }
96 pub fn links(&self) -> Option<InternedString> {
97 self.inner.links
98 }
99 pub fn namespaced_features(&self) -> bool {
100 self.inner.namespaced_features
101 }
102
103 pub fn override_id(mut self, id: PackageId) -> Summary {
104 Rc::make_mut(&mut self.inner).package_id = id;
105 self
106 }
107
108 pub fn set_checksum(&mut self, cksum: String) {
109 Rc::make_mut(&mut self.inner).checksum = Some(cksum);
110 }
111
112 pub fn map_dependencies<F>(mut self, f: F) -> Summary
113 where
114 F: FnMut(Dependency) -> Dependency,
115 {
116 {
117 let slot = &mut Rc::make_mut(&mut self.inner).dependencies;
118 let deps = mem::replace(slot, Vec::new());
119 *slot = deps.into_iter().map(f).collect();
120 }
121 self
122 }
123
124 pub fn map_source(self, to_replace: SourceId, replace_with: SourceId) -> Summary {
125 let me = if self.package_id().source_id() == to_replace {
126 let new_id = self.package_id().with_source_id(replace_with);
127 self.override_id(new_id)
128 } else {
129 self
130 };
131 me.map_dependencies(|dep| dep.map_source(to_replace, replace_with))
132 }
133 }
134
135 impl PartialEq for Summary {
136 fn eq(&self, other: &Summary) -> bool {
137 self.inner.package_id == other.inner.package_id
138 }
139 }
140
141 impl Eq for Summary {}
142
143 impl Hash for Summary {
144 fn hash<H: Hasher>(&self, state: &mut H) {
145 self.inner.package_id.hash(state);
146 }
147 }
148
149 // Checks features for errors, bailing out a CargoResult:Err if invalid,
150 // and creates FeatureValues for each feature.
151 fn build_feature_map<K>(
152 features: &BTreeMap<K, Vec<impl AsRef<str>>>,
153 dependencies: &[Dependency],
154 namespaced: bool,
155 ) -> CargoResult<FeatureMap>
156 where
157 K: Borrow<str> + Ord + Display,
158 {
159 use self::FeatureValue::*;
160 let mut dep_map = HashMap::new();
161 for dep in dependencies.iter() {
162 dep_map
163 .entry(dep.name_in_toml())
164 .or_insert_with(Vec::new)
165 .push(dep);
166 }
167
168 let mut map = BTreeMap::new();
169 for (feature, list) in features.iter() {
170 // If namespaced features is active and the key is the same as that of an
171 // optional dependency, that dependency must be included in the values.
172 // Thus, if a `feature` is found that has the same name as a dependency, we
173 // (a) bail out if the dependency is non-optional, and (b) we track if the
174 // feature requirements include the dependency `crate:feature` in the list.
175 // This is done with the `dependency_found` variable, which can only be
176 // false if features are namespaced and the current feature key is the same
177 // as the name of an optional dependency. If so, it gets set to true during
178 // iteration over the list if the dependency is found in the list.
179 let mut dependency_found = if namespaced {
180 match dep_map.get(feature.borrow()) {
181 Some(dep_data) => {
182 if !dep_data.iter().any(|d| d.is_optional()) {
183 anyhow::bail!(
184 "Feature `{}` includes the dependency of the same name, but this is \
185 left implicit in the features included by this feature.\n\
186 Additionally, the dependency must be marked as optional to be \
187 included in the feature definition.\n\
188 Consider adding `crate:{}` to this feature's requirements \
189 and marking the dependency as `optional = true`",
190 feature,
191 feature
192 )
193 } else {
194 false
195 }
196 }
197 None => true,
198 }
199 } else {
200 true
201 };
202
203 let mut values = vec![];
204 for dep in list {
205 let val = FeatureValue::build(
206 InternedString::new(dep.as_ref()),
207 |fs| features.contains_key(fs.as_str()),
208 namespaced,
209 );
210
211 // Find data for the referenced dependency...
212 let dep_data = {
213 match val {
214 Feature(ref dep_name) | Crate(ref dep_name) | CrateFeature(ref dep_name, _) => {
215 dep_map.get(dep_name.as_str())
216 }
217 }
218 };
219 let is_optional_dep = dep_data
220 .iter()
221 .flat_map(|d| d.iter())
222 .any(|d| d.is_optional());
223 if let FeatureValue::Crate(ref dep_name) = val {
224 // If we have a dependency value, check if this is the dependency named
225 // the same as the feature that we were looking for.
226 if !dependency_found && feature.borrow() == dep_name.as_str() {
227 dependency_found = true;
228 }
229 }
230
231 match (&val, dep_data.is_some(), is_optional_dep) {
232 // The value is a feature. If features are namespaced, this just means
233 // it's not prefixed with `crate:`, so we have to check whether the
234 // feature actually exist. If the feature is not defined *and* an optional
235 // dependency of the same name exists, the feature is defined implicitly
236 // here by adding it to the feature map, pointing to the dependency.
237 // If features are not namespaced, it's been validated as a feature already
238 // while instantiating the `FeatureValue` in `FeatureValue::build()`, so
239 // we don't have to do so here.
240 (&Feature(feat), _, true) => {
241 if namespaced && !features.contains_key(&*feat) {
242 map.insert(feat, vec![FeatureValue::Crate(feat)]);
243 }
244 }
245 // If features are namespaced and the value is not defined as a feature
246 // and there is no optional dependency of the same name, error out.
247 // If features are not namespaced, there must be an existing feature
248 // here (checked by `FeatureValue::build()`), so it will always be defined.
249 (&Feature(feat), dep_exists, false) => {
250 if namespaced && !features.contains_key(&*feat) {
251 if dep_exists {
252 anyhow::bail!(
253 "Feature `{}` includes `{}` which is not defined as a feature.\n\
254 A non-optional dependency of the same name is defined; consider \
255 adding `optional = true` to its definition",
256 feature,
257 feat
258 )
259 } else {
260 anyhow::bail!(
261 "Feature `{}` includes `{}` which is not defined as a feature",
262 feature,
263 feat
264 )
265 }
266 }
267 }
268 // The value is a dependency. If features are namespaced, it is explicitly
269 // tagged as such (`crate:value`). If features are not namespaced, any value
270 // not recognized as a feature is pegged as a `Crate`. Here we handle the case
271 // where the dependency exists but is non-optional. It branches on namespaced
272 // just to provide the correct string for the crate dependency in the error.
273 (&Crate(ref dep), true, false) => {
274 if namespaced {
275 anyhow::bail!(
276 "Feature `{}` includes `crate:{}` which is not an \
277 optional dependency.\nConsider adding \
278 `optional = true` to the dependency",
279 feature,
280 dep
281 )
282 } else {
283 anyhow::bail!(
284 "Feature `{}` depends on `{}` which is not an \
285 optional dependency.\nConsider adding \
286 `optional = true` to the dependency",
287 feature,
288 dep
289 )
290 }
291 }
292 // If namespaced, the value was tagged as a dependency; if not namespaced,
293 // this could be anything not defined as a feature. This handles the case
294 // where no such dependency is actually defined; again, the branch on
295 // namespaced here is just to provide the correct string in the error.
296 (&Crate(ref dep), false, _) => {
297 if namespaced {
298 anyhow::bail!(
299 "Feature `{}` includes `crate:{}` which is not a known \
300 dependency",
301 feature,
302 dep
303 )
304 } else {
305 anyhow::bail!(
306 "Feature `{}` includes `{}` which is neither a dependency nor \
307 another feature",
308 feature,
309 dep
310 )
311 }
312 }
313 (&Crate(_), true, true) => {}
314 // If the value is a feature for one of the dependencies, bail out if no such
315 // dependency is actually defined in the manifest.
316 (&CrateFeature(ref dep, _), false, _) => anyhow::bail!(
317 "Feature `{}` requires a feature of `{}` which is not a \
318 dependency",
319 feature,
320 dep
321 ),
322 (&CrateFeature(_, _), true, _) => {}
323 }
324 values.push(val);
325 }
326
327 if !dependency_found {
328 // If we have not found the dependency of the same-named feature, we should
329 // bail here.
330 anyhow::bail!(
331 "Feature `{}` includes the optional dependency of the \
332 same name, but this is left implicit in the features \
333 included by this feature.\nConsider adding \
334 `crate:{}` to this feature's requirements.",
335 feature,
336 feature
337 )
338 }
339
340 map.insert(InternedString::new(feature.borrow()), values);
341 }
342 Ok(map)
343 }
344
345 /// FeatureValue represents the types of dependencies a feature can have:
346 ///
347 /// * Another feature
348 /// * An optional dependency
349 /// * A feature in a dependency
350 ///
351 /// The selection between these 3 things happens as part of the construction of the FeatureValue.
352 #[derive(Clone, Debug)]
353 pub enum FeatureValue {
354 Feature(InternedString),
355 Crate(InternedString),
356 CrateFeature(InternedString, InternedString),
357 }
358
359 impl FeatureValue {
360 fn build<T>(feature: InternedString, is_feature: T, namespaced: bool) -> FeatureValue
361 where
362 T: Fn(InternedString) -> bool,
363 {
364 match (feature.find('/'), namespaced) {
365 (Some(pos), _) => {
366 let (dep, dep_feat) = feature.split_at(pos);
367 let dep_feat = &dep_feat[1..];
368 FeatureValue::CrateFeature(InternedString::new(dep), InternedString::new(dep_feat))
369 }
370 (None, true) if feature.starts_with("crate:") => {
371 FeatureValue::Crate(InternedString::new(&feature[6..]))
372 }
373 (None, true) => FeatureValue::Feature(feature),
374 (None, false) if is_feature(feature) => FeatureValue::Feature(feature),
375 (None, false) => FeatureValue::Crate(feature),
376 }
377 }
378
379 pub fn new(feature: InternedString, s: &Summary) -> FeatureValue {
380 Self::build(
381 feature,
382 |fs| s.features().contains_key(&fs),
383 s.namespaced_features(),
384 )
385 }
386
387 pub fn to_string(&self, s: &Summary) -> String {
388 use self::FeatureValue::*;
389 match *self {
390 Feature(ref f) => f.to_string(),
391 Crate(ref c) => {
392 if s.namespaced_features() {
393 format!("crate:{}", &c)
394 } else {
395 c.to_string()
396 }
397 }
398 CrateFeature(ref c, ref f) => [c.as_ref(), f.as_ref()].join("/"),
399 }
400 }
401 }
402
403 impl Serialize for FeatureValue {
404 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
405 where
406 S: Serializer,
407 {
408 use self::FeatureValue::*;
409 match *self {
410 Feature(ref f) => serializer.serialize_str(f),
411 Crate(ref c) => serializer.serialize_str(c),
412 CrateFeature(ref c, ref f) => {
413 serializer.serialize_str(&[c.as_ref(), f.as_ref()].join("/"))
414 }
415 }
416 }
417 }
418
419 pub type FeatureMap = BTreeMap<InternedString, Vec<FeatureValue>>;