1use crate::{
5 errors::{FeatureBuildStage, FeatureGraphWarning},
6 graph::{
7 feature::{
8 ConditionalLinkImpl, FeatureEdge, FeatureGraphImpl, FeatureLabel, FeatureMetadataImpl,
9 FeatureNode, WeakDependencies, WeakIndex,
10 },
11 DepRequiredOrOptional, DependencyReq, FeatureIndexInPackage, FeatureIx, NamedFeatureDep,
12 PackageGraph, PackageIx, PackageLink, PackageMetadata,
13 },
14 platform::PlatformStatusImpl,
15};
16use ahash::AHashMap;
17use cargo_metadata::DependencyKind;
18use once_cell::sync::OnceCell;
19use petgraph::{prelude::*, visit::IntoEdgeReferences};
20use smallvec::SmallVec;
21use std::iter;
22
23pub(super) type FeaturePetgraph = Graph<FeatureNode, FeatureEdge, Directed, FeatureIx>;
24pub(super) type FeatureEdgeReference<'g> = <&'g FeaturePetgraph as IntoEdgeReferences>::EdgeRef;
25
26#[derive(Debug)]
27pub(super) struct FeatureGraphBuildState {
28 graph: FeaturePetgraph,
29 base_ixs: Vec<NodeIndex<FeatureIx>>,
31 map: AHashMap<FeatureNode, FeatureMetadataImpl>,
32 weak: WeakDependencies,
33 warnings: Vec<FeatureGraphWarning>,
34}
35
36impl FeatureGraphBuildState {
37 pub(super) fn new(package_graph: &PackageGraph) -> Self {
38 let package_count = package_graph.package_count();
39 Self {
40 graph: Graph::with_capacity(package_count, package_count),
42 base_ixs: Vec::with_capacity(package_count + 1),
45 map: AHashMap::with_capacity(package_count),
46 weak: WeakDependencies::new(),
47 warnings: vec![],
48 }
49 }
50
51 pub(super) fn add_nodes(&mut self, package: PackageMetadata<'_>) {
54 let base_node = FeatureNode::base(package.package_ix());
55 let base_ix = self.add_node(base_node);
56 self.base_ixs.push(base_ix);
57 FeatureNode::named_features(package)
58 .chain(FeatureNode::optional_deps(package))
59 .for_each(|feature_node| {
60 let feature_ix = self.add_node(feature_node);
61 self.graph
62 .update_edge(feature_ix, base_ix, FeatureEdge::FeatureToBase);
63 });
64 }
65
66 pub(super) fn end_nodes(&mut self) {
68 self.base_ixs.push(NodeIndex::new(self.graph.node_count()));
69 }
70
71 pub(super) fn add_named_feature_edges(&mut self, metadata: PackageMetadata<'_>) {
72 let dep_name_to_link: AHashMap<_, _> = metadata
73 .direct_links()
74 .map(|link| (link.dep_name(), link))
75 .collect();
76
77 metadata
78 .named_features_full()
79 .for_each(|(n, from_feature, feature_deps)| {
80 let from_node = FeatureNode::new(metadata.package_ix(), n);
81
82 let to_nodes_edges: Vec<_> = feature_deps
83 .iter()
84 .flat_map(|feature_dep| {
85 self.nodes_for_named_feature_dep(
86 metadata,
87 from_feature,
88 feature_dep,
89 &dep_name_to_link,
90 )
91 })
92 .collect();
95
96 self.add_edges(from_node, to_nodes_edges, metadata.graph());
99 })
100 }
101
102 fn nodes_for_named_feature_dep(
103 &mut self,
104 metadata: PackageMetadata<'_>,
105 from_named_feature: &str,
106 feature_dep: &NamedFeatureDep,
107 dep_name_to_link: &AHashMap<&str, PackageLink>,
108 ) -> SmallVec<[(FeatureNode, FeatureEdge); 3]> {
109 let from_label = FeatureLabel::Named(from_named_feature);
110 let mut nodes_edges: SmallVec<[(FeatureNode, FeatureEdge); 3]> = SmallVec::new();
111
112 match feature_dep {
113 NamedFeatureDep::DependencyNamedFeature {
114 dep_name,
115 feature,
116 weak,
117 } => {
118 if let Some(link) = dep_name_to_link.get(dep_name.as_ref()) {
119 let weak_index = weak.then(|| self.weak.insert(link.edge_ix()));
120
121 if let Some(cross_node) = self.make_named_feature_node(
123 &metadata,
124 from_label,
125 &link.to(),
126 FeatureLabel::Named(feature.as_ref()),
127 true,
128 ) {
129 nodes_edges.push((
133 cross_node,
134 Self::make_named_feature_cross_edge(link, weak_index),
135 ));
136 };
137
138 if let Some(same_node) = self.make_named_feature_node(
142 &metadata,
143 from_label,
144 &metadata,
145 FeatureLabel::OptionalDependency(dep_name),
146 false,
148 ) {
149 nodes_edges.push((
150 same_node,
151 Self::make_named_feature_cross_edge(link, weak_index),
152 ));
153 }
154
155 if !*weak && &**dep_name != from_named_feature {
173 if let Some(same_named_feature_node) = self.make_named_feature_node(
174 &metadata,
175 from_label,
176 &metadata,
177 FeatureLabel::Named(dep_name),
178 false,
180 ) {
181 nodes_edges.push((
182 same_named_feature_node,
183 Self::make_named_feature_cross_edge(link, None),
184 ));
185 }
186 }
187 }
188 }
189 NamedFeatureDep::NamedFeature(feature_name) => {
190 if let Some(same_node) = self.make_named_feature_node(
191 &metadata,
192 from_label,
193 &metadata,
194 FeatureLabel::Named(feature_name.as_ref()),
195 true,
196 ) {
197 nodes_edges.push((same_node, FeatureEdge::NamedFeature));
198 }
199 }
200 NamedFeatureDep::OptionalDependency(dep_name) => {
201 if let Some(same_node) = self.make_named_feature_node(
202 &metadata,
203 from_label,
204 &metadata,
205 FeatureLabel::OptionalDependency(dep_name.as_ref()),
206 true,
207 ) {
208 if let Some(link) = dep_name_to_link.get(dep_name.as_ref()) {
209 nodes_edges.push((
210 same_node,
211 FeatureEdge::NamedFeatureDepColon(
212 Self::make_full_conditional_link_impl(link),
213 ),
214 ));
215 }
216 }
217 }
218 };
219
220 nodes_edges
221 }
222
223 fn make_named_feature_node(
224 &mut self,
225 from_package: &PackageMetadata<'_>,
226 from_label: FeatureLabel<'_>,
227 to_package: &PackageMetadata<'_>,
228 to_label: FeatureLabel<'_>,
229 warn: bool,
230 ) -> Option<FeatureNode> {
231 match to_package.get_feature_idx(to_label) {
232 Some(idx) => Some(FeatureNode::new(to_package.package_ix(), idx)),
233 None => {
234 if warn {
243 self.warnings.push(FeatureGraphWarning::MissingFeature {
244 stage: FeatureBuildStage::AddNamedFeatureEdges {
245 package_id: from_package.id().clone(),
246 from_feature: from_label.to_string(),
247 },
248 package_id: to_package.id().clone(),
249 feature_name: to_label.to_string(),
250 });
251 }
252 None
253 }
254 }
255 }
256
257 fn make_named_feature_cross_edge(
269 link: &PackageLink<'_>,
270 weak_index: Option<WeakIndex>,
271 ) -> FeatureEdge {
272 FeatureEdge::NamedFeatureWithSlash {
275 link: Self::make_full_conditional_link_impl(link),
276 weak_index,
277 }
278 }
279
280 fn make_full_conditional_link_impl(link: &PackageLink<'_>) -> ConditionalLinkImpl {
283 fn combine_req_opt(req: DependencyReq<'_>) -> PlatformStatusImpl {
286 let mut required = req.inner.required.build_if.clone();
287 required.extend(&req.inner.optional.build_if);
288 required
289 }
290
291 ConditionalLinkImpl {
292 package_edge_ix: link.edge_ix(),
293 normal: combine_req_opt(link.normal()),
294 build: combine_req_opt(link.build()),
295 dev: combine_req_opt(link.dev()),
296 }
297 }
298
299 pub(super) fn add_dependency_edges(&mut self, link: PackageLink<'_>) {
300 let from = link.from();
301
302 let unified_metadata = iter::once((DependencyKind::Normal, link.normal()))
343 .chain(iter::once((DependencyKind::Build, link.build())))
344 .chain(iter::once((DependencyKind::Development, link.dev())));
345
346 let mut required_req = FeatureReq::new(link);
347 let mut optional_req = FeatureReq::new(link);
348 for (kind, dependency_req) in unified_metadata {
349 required_req.add_features(kind, &dependency_req.inner.required, &mut self.warnings);
350 optional_req.add_features(kind, &dependency_req.inner.optional, &mut self.warnings);
351 }
352
353 self.add_edges(
355 FeatureNode::base(from.package_ix()),
356 required_req.finish(),
357 link.from().graph(),
358 );
359
360 if !optional_req.is_empty() {
361 let from_node = FeatureNode::new(
365 from.package_ix(),
366 from.get_feature_idx(FeatureLabel::OptionalDependency(link.dep_name()))
367 .unwrap_or_else(|| {
368 panic!(
369 "while adding feature edges, for package '{}', optional dep '{}' missing",
370 from.id(),
371 link.dep_name(),
372 );
373 }),
374 );
375 self.add_edges(from_node, optional_req.finish(), link.from().graph());
376 }
377 }
378
379 fn add_node(&mut self, feature_id: FeatureNode) -> NodeIndex<FeatureIx> {
380 let feature_ix = self.graph.add_node(feature_id);
381 self.map
382 .insert(feature_id, FeatureMetadataImpl { feature_ix });
383 feature_ix
384 }
385
386 fn add_edges(
387 &mut self,
388 from_node: FeatureNode,
389 to_nodes_edges: impl IntoIterator<Item = (FeatureNode, FeatureEdge)>,
390 graph: &PackageGraph,
391 ) {
392 let from_ix = self.lookup_node(&from_node).unwrap_or_else(|| {
394 panic!(
395 "while adding feature edges, missing 'from': {:?}",
396 from_node
397 );
398 });
399
400 let to_nodes_edges = to_nodes_edges.into_iter().collect::<Vec<_>>();
401
402 to_nodes_edges.into_iter().for_each(|(to_node, edge)| {
403 let to_ix = self.lookup_node(&to_node).unwrap_or_else(|| {
404 panic!("while adding feature edges, missing 'to': {:?}", to_node)
405 });
406
407 if from_ix == to_ix {
408 let (package_id, feature_label) = from_node.package_id_and_feature_label(graph);
409 self.warnings.push(FeatureGraphWarning::SelfLoop {
410 package_id: package_id.clone(),
411 feature_name: feature_label.to_string(),
412 });
413 }
414
415 match self.graph.find_edge(from_ix, to_ix) {
416 Some(edge_ix) => {
417 let old_edge = self
438 .graph
439 .edge_weight_mut(edge_ix)
440 .expect("this edge was just found");
441 #[allow(clippy::single_match)]
442 match (old_edge, edge) {
443 (
444 FeatureEdge::NamedFeatureWithSlash {
445 weak_index: old_weak_index,
446 ..
447 },
448 FeatureEdge::NamedFeatureWithSlash { weak_index, .. },
449 ) => {
450 if old_weak_index.is_some() && weak_index.is_some() {
451 debug_assert_eq!(
452 *old_weak_index, weak_index,
453 "weak indexes should match if some"
454 );
455 }
456 if weak_index.is_none() {
458 *old_weak_index = None;
459 }
460 }
461 (
462 old_edge @ FeatureEdge::NamedFeatureWithSlash { .. },
463 edge @ FeatureEdge::NamedFeature
464 | edge @ FeatureEdge::NamedFeatureDepColon(_),
465 ) => {
466 *old_edge = edge;
468 }
469 (
470 old_edge @ FeatureEdge::NamedFeatureDepColon(_),
471 edge @ FeatureEdge::NamedFeature,
472 ) => {
473 *old_edge = edge;
476 }
477
478 _ => {
479 }
481 }
482 }
483 None => {
484 self.graph.add_edge(from_ix, to_ix, edge);
485 }
486 }
487 })
488 }
489
490 fn lookup_node(&self, node: &FeatureNode) -> Option<NodeIndex<FeatureIx>> {
491 self.map.get(node).map(|metadata| metadata.feature_ix)
492 }
493
494 pub(super) fn build(self) -> FeatureGraphImpl {
495 FeatureGraphImpl {
496 graph: self.graph,
497 base_ixs: self.base_ixs,
498 map: self.map,
499 warnings: self.warnings,
500 sccs: OnceCell::new(),
501 weak: self.weak,
502 }
503 }
504}
505
506#[derive(Debug)]
507struct FeatureReq<'g> {
508 link: PackageLink<'g>,
509 to: PackageMetadata<'g>,
510 edge_ix: EdgeIndex<PackageIx>,
511 to_default_idx: FeatureIndexInPackage,
512 features: AHashMap<FeatureIndexInPackage, DependencyBuildState>,
514}
515
516impl<'g> FeatureReq<'g> {
517 fn new(link: PackageLink<'g>) -> Self {
518 let to = link.to();
519 Self {
520 link,
521 to,
522 edge_ix: link.edge_ix(),
523 to_default_idx: to
524 .get_feature_idx(FeatureLabel::Named("default"))
525 .unwrap_or(FeatureIndexInPackage::Base),
526 features: AHashMap::new(),
527 }
528 }
529
530 fn is_empty(&self) -> bool {
531 self.features.is_empty()
533 }
534
535 fn add_features(
536 &mut self,
537 dep_kind: DependencyKind,
538 req: &DepRequiredOrOptional,
539 warnings: &mut Vec<FeatureGraphWarning>,
540 ) {
541 self.extend(FeatureIndexInPackage::Base, dep_kind, &req.build_if);
543 self.extend(self.to_default_idx, dep_kind, &req.default_features_if);
545
546 for (feature, status) in &req.feature_targets {
547 match self.to.get_feature_idx(FeatureLabel::Named(feature)) {
548 Some(feature_idx) => {
549 self.extend(feature_idx, dep_kind, status);
550 }
551 None => {
552 warnings.push(FeatureGraphWarning::MissingFeature {
555 stage: FeatureBuildStage::AddDependencyEdges {
556 package_id: self.link.from().id().clone(),
557 dep_name: self.link.dep_name().to_string(),
558 },
559 package_id: self.to.id().clone(),
560 feature_name: feature.to_string(),
561 });
562 }
563 }
564 }
565 }
566
567 fn extend(
568 &mut self,
569 feature_idx: FeatureIndexInPackage,
570 dep_kind: DependencyKind,
571 status: &PlatformStatusImpl,
572 ) {
573 let package_edge_ix = self.edge_ix;
574 if !status.is_never() {
575 self.features
576 .entry(feature_idx)
577 .or_insert_with(|| DependencyBuildState::new(package_edge_ix))
578 .extend(dep_kind, status);
579 }
580 }
581
582 fn finish(self) -> impl Iterator<Item = (FeatureNode, FeatureEdge)> {
583 let package_ix = self.to.package_ix();
584 self.features
585 .into_iter()
586 .map(move |(feature_idx, build_state)| {
587 debug_assert!(!build_state.is_empty(), "build states are always non-empty");
589 (
590 FeatureNode::new(package_ix, feature_idx),
591 build_state.finish(),
592 )
593 })
594 }
595}
596
597#[derive(Debug)]
598struct DependencyBuildState {
599 package_edge_ix: EdgeIndex<PackageIx>,
600 normal: PlatformStatusImpl,
601 build: PlatformStatusImpl,
602 dev: PlatformStatusImpl,
603}
604
605impl DependencyBuildState {
606 fn new(package_edge_ix: EdgeIndex<PackageIx>) -> Self {
607 Self {
608 package_edge_ix,
609 normal: PlatformStatusImpl::default(),
610 build: PlatformStatusImpl::default(),
611 dev: PlatformStatusImpl::default(),
612 }
613 }
614
615 fn extend(&mut self, dep_kind: DependencyKind, status: &PlatformStatusImpl) {
616 match dep_kind {
617 DependencyKind::Normal => self.normal.extend(status),
618 DependencyKind::Build => self.build.extend(status),
619 DependencyKind::Development => self.dev.extend(status),
620 _ => panic!("unknown dependency kind"),
621 }
622 }
623
624 fn is_empty(&self) -> bool {
625 self.normal.is_never() && self.build.is_never() && self.dev.is_never()
626 }
627
628 fn finish(self) -> FeatureEdge {
629 FeatureEdge::DependenciesSection(ConditionalLinkImpl {
630 package_edge_ix: self.package_edge_ix,
631 normal: self.normal,
632 build: self.build,
633 dev: self.dev,
634 })
635 }
636}