1use crate::{
5 errors::{FeatureBuildStage, FeatureGraphWarning},
6 graph::{
7 DepRequiredOrOptional, DependencyReq, FeatureIndexInPackage, FeatureIx, NamedFeatureDep,
8 PackageGraph, PackageIx, PackageLink, PackageMetadata,
9 feature::{
10 ConditionalLinkImpl, FeatureEdge, FeatureGraphImpl, FeatureLabel, FeatureMetadataImpl,
11 FeatureNode, WeakDependencies, WeakIndex,
12 },
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!("while adding feature edges, missing 'from': {from_node:?}");
395 });
396
397 let to_nodes_edges = to_nodes_edges.into_iter().collect::<Vec<_>>();
398
399 to_nodes_edges.into_iter().for_each(|(to_node, edge)| {
400 let to_ix = self
401 .lookup_node(&to_node)
402 .unwrap_or_else(|| panic!("while adding feature edges, missing 'to': {to_node:?}"));
403
404 if from_ix == to_ix {
405 let (package_id, feature_label) = from_node.package_id_and_feature_label(graph);
406 self.warnings.push(FeatureGraphWarning::SelfLoop {
407 package_id: package_id.clone(),
408 feature_name: feature_label.to_string(),
409 });
410 }
411
412 match self.graph.find_edge(from_ix, to_ix) {
413 Some(edge_ix) => {
414 let old_edge = self
435 .graph
436 .edge_weight_mut(edge_ix)
437 .expect("this edge was just found");
438 #[allow(clippy::single_match)]
439 match (old_edge, edge) {
440 (
441 FeatureEdge::NamedFeatureWithSlash {
442 weak_index: old_weak_index,
443 ..
444 },
445 FeatureEdge::NamedFeatureWithSlash { weak_index, .. },
446 ) => {
447 if old_weak_index.is_some() && weak_index.is_some() {
448 debug_assert_eq!(
449 *old_weak_index, weak_index,
450 "weak indexes should match if some"
451 );
452 }
453 if weak_index.is_none() {
455 *old_weak_index = None;
456 }
457 }
458 (
459 old_edge @ FeatureEdge::NamedFeatureWithSlash { .. },
460 edge @ FeatureEdge::NamedFeature
461 | edge @ FeatureEdge::NamedFeatureDepColon(_),
462 ) => {
463 *old_edge = edge;
465 }
466 (
467 old_edge @ FeatureEdge::NamedFeatureDepColon(_),
468 edge @ FeatureEdge::NamedFeature,
469 ) => {
470 *old_edge = edge;
473 }
474
475 _ => {
476 }
478 }
479 }
480 None => {
481 self.graph.add_edge(from_ix, to_ix, edge);
482 }
483 }
484 })
485 }
486
487 fn lookup_node(&self, node: &FeatureNode) -> Option<NodeIndex<FeatureIx>> {
488 self.map.get(node).map(|metadata| metadata.feature_ix)
489 }
490
491 pub(super) fn build(self) -> FeatureGraphImpl {
492 FeatureGraphImpl {
493 graph: self.graph,
494 base_ixs: self.base_ixs,
495 map: self.map,
496 warnings: self.warnings,
497 sccs: OnceCell::new(),
498 weak: self.weak,
499 }
500 }
501}
502
503#[derive(Debug)]
504struct FeatureReq<'g> {
505 link: PackageLink<'g>,
506 to: PackageMetadata<'g>,
507 edge_ix: EdgeIndex<PackageIx>,
508 to_default_idx: FeatureIndexInPackage,
509 features: AHashMap<FeatureIndexInPackage, DependencyBuildState>,
511}
512
513impl<'g> FeatureReq<'g> {
514 fn new(link: PackageLink<'g>) -> Self {
515 let to = link.to();
516 Self {
517 link,
518 to,
519 edge_ix: link.edge_ix(),
520 to_default_idx: to
521 .get_feature_idx(FeatureLabel::Named("default"))
522 .unwrap_or(FeatureIndexInPackage::Base),
523 features: AHashMap::new(),
524 }
525 }
526
527 fn is_empty(&self) -> bool {
528 self.features.is_empty()
530 }
531
532 fn add_features(
533 &mut self,
534 dep_kind: DependencyKind,
535 req: &DepRequiredOrOptional,
536 warnings: &mut Vec<FeatureGraphWarning>,
537 ) {
538 self.extend(FeatureIndexInPackage::Base, dep_kind, &req.build_if);
540 self.extend(self.to_default_idx, dep_kind, &req.default_features_if);
542
543 for (feature, status) in &req.feature_targets {
544 match self.to.get_feature_idx(FeatureLabel::Named(feature)) {
545 Some(feature_idx) => {
546 self.extend(feature_idx, dep_kind, status);
547 }
548 None => {
549 warnings.push(FeatureGraphWarning::MissingFeature {
552 stage: FeatureBuildStage::AddDependencyEdges {
553 package_id: self.link.from().id().clone(),
554 dep_name: self.link.dep_name().to_string(),
555 },
556 package_id: self.to.id().clone(),
557 feature_name: feature.to_string(),
558 });
559 }
560 }
561 }
562 }
563
564 fn extend(
565 &mut self,
566 feature_idx: FeatureIndexInPackage,
567 dep_kind: DependencyKind,
568 status: &PlatformStatusImpl,
569 ) {
570 let package_edge_ix = self.edge_ix;
571 if !status.is_never() {
572 self.features
573 .entry(feature_idx)
574 .or_insert_with(|| DependencyBuildState::new(package_edge_ix))
575 .extend(dep_kind, status);
576 }
577 }
578
579 fn finish(self) -> impl Iterator<Item = (FeatureNode, FeatureEdge)> + use<> {
580 let package_ix = self.to.package_ix();
581 self.features
582 .into_iter()
583 .map(move |(feature_idx, build_state)| {
584 debug_assert!(!build_state.is_empty(), "build states are always non-empty");
586 (
587 FeatureNode::new(package_ix, feature_idx),
588 build_state.finish(),
589 )
590 })
591 }
592}
593
594#[derive(Debug)]
595struct DependencyBuildState {
596 package_edge_ix: EdgeIndex<PackageIx>,
597 normal: PlatformStatusImpl,
598 build: PlatformStatusImpl,
599 dev: PlatformStatusImpl,
600}
601
602impl DependencyBuildState {
603 fn new(package_edge_ix: EdgeIndex<PackageIx>) -> Self {
604 Self {
605 package_edge_ix,
606 normal: PlatformStatusImpl::default(),
607 build: PlatformStatusImpl::default(),
608 dev: PlatformStatusImpl::default(),
609 }
610 }
611
612 fn extend(&mut self, dep_kind: DependencyKind, status: &PlatformStatusImpl) {
613 match dep_kind {
614 DependencyKind::Normal => self.normal.extend(status),
615 DependencyKind::Build => self.build.extend(status),
616 DependencyKind::Development => self.dev.extend(status),
617 _ => panic!("unknown dependency kind"),
618 }
619 }
620
621 fn is_empty(&self) -> bool {
622 self.normal.is_never() && self.build.is_never() && self.dev.is_never()
623 }
624
625 fn finish(self) -> FeatureEdge {
626 FeatureEdge::DependenciesSection(ConditionalLinkImpl {
627 package_edge_ix: self.package_edge_ix,
628 normal: self.normal,
629 build: self.build,
630 dev: self.dev,
631 })
632 }
633}