1use crate::{
5 DependencyKind, Error,
6 graph::{
7 DependencyDirection, PackageGraph, PackageIx, PackageLink, PackageResolver, PackageSet,
8 cargo::{
9 CargoIntermediateSet, CargoOptions, CargoResolverVersion, CargoSet, InitialsPlatform,
10 },
11 feature::{ConditionalLink, FeatureLabel, FeatureQuery, FeatureSet, StandardFeatures},
12 },
13 platform::{EnabledTernary, PlatformSpec},
14 sorted_set::SortedSet,
15};
16use fixedbitset::FixedBitSet;
17use petgraph::{prelude::*, visit::VisitMap};
18
19pub(super) struct CargoSetBuildState<'a> {
20 opts: &'a CargoOptions<'a>,
21 omitted_packages: SortedSet<NodeIndex<PackageIx>>,
22}
23
24impl<'a> CargoSetBuildState<'a> {
25 pub(super) fn new<'g>(
26 graph: &'g PackageGraph,
27 opts: &'a CargoOptions<'a>,
28 ) -> Result<Self, Error> {
29 let omitted_packages: SortedSet<_> =
30 graph.package_ixs(opts.omitted_packages.iter().copied())?;
31
32 Ok(Self {
33 opts,
34 omitted_packages,
35 })
36 }
37
38 pub(super) fn build<'g>(
39 self,
40 initials: FeatureSet<'g>,
41 features_only: FeatureSet<'g>,
42 resolver: Option<&mut dyn PackageResolver<'g>>,
43 ) -> CargoSet<'g> {
44 match self.opts.resolver {
45 CargoResolverVersion::V1 => self.new_v1(initials, features_only, resolver, false),
46 CargoResolverVersion::V1Install => {
47 let avoid_dev_deps = !self.opts.include_dev;
48 self.new_v1(initials, features_only, resolver, avoid_dev_deps)
49 }
50 CargoResolverVersion::V2 | CargoResolverVersion::V3 => {
52 self.new_v2(initials, features_only, resolver)
53 }
54 }
55 }
56
57 pub(super) fn build_intermediate(self, query: FeatureQuery) -> CargoIntermediateSet {
58 match self.opts.resolver {
59 CargoResolverVersion::V1 => self.new_v1_intermediate(query, false),
60 CargoResolverVersion::V1Install => {
61 let avoid_dev_deps = !self.opts.include_dev;
62 self.new_v1_intermediate(query, avoid_dev_deps)
63 }
64 CargoResolverVersion::V2 | CargoResolverVersion::V3 => self.new_v2_intermediate(query),
65 }
66 }
67
68 fn new_v1<'g>(
69 self,
70 initials: FeatureSet<'g>,
71 features_only: FeatureSet<'g>,
72 resolver: Option<&mut dyn PackageResolver<'g>>,
73 avoid_dev_deps: bool,
74 ) -> CargoSet<'g> {
75 self.build_set(initials, features_only, resolver, |query| {
76 self.new_v1_intermediate(query, avoid_dev_deps)
77 })
78 }
79
80 fn new_v2<'g>(
81 self,
82 initials: FeatureSet<'g>,
83 features_only: FeatureSet<'g>,
84 resolver: Option<&mut dyn PackageResolver<'g>>,
85 ) -> CargoSet<'g> {
86 self.build_set(initials, features_only, resolver, |query| {
87 self.new_v2_intermediate(query)
88 })
89 }
90
91 fn is_omitted(&self, package_ix: NodeIndex<PackageIx>) -> bool {
96 self.omitted_packages.contains(&package_ix)
97 }
98
99 fn build_set<'g>(
100 &self,
101 initials: FeatureSet<'g>,
102 features_only: FeatureSet<'g>,
103 mut resolver: Option<&mut dyn PackageResolver<'g>>,
104 intermediate_fn: impl FnOnce(FeatureQuery<'g>) -> CargoIntermediateSet<'g>,
105 ) -> CargoSet<'g> {
106 let graph = *initials.graph();
108 let mut host_ixs = Vec::new();
111 let target_ixs: Vec<_> = initials
112 .ixs_unordered()
113 .filter_map(|feature_ix| {
114 let metadata = graph.metadata_for_ix(feature_ix);
115 let package_ix = metadata.package_ix();
116 match self.opts.initials_platform {
117 InitialsPlatform::Host => {
118 host_ixs.push(package_ix);
120 None
121 }
122 InitialsPlatform::Standard => {
123 if metadata.package().is_proc_macro() {
125 host_ixs.push(package_ix);
126 None
127 } else {
128 Some(package_ix)
129 }
130 }
131 InitialsPlatform::ProcMacrosOnTarget => {
132 if metadata.package().is_proc_macro() {
135 host_ixs.push(package_ix);
136 }
137 Some(package_ix)
138 }
139 }
140 })
141 .collect();
142 let target_query = graph
143 .package_graph
144 .query_from_parts(SortedSet::new(target_ixs), DependencyDirection::Forward);
145
146 let initials_plus_features_only = initials.union(&features_only);
149 let intermediate_set = intermediate_fn(
150 initials_plus_features_only.to_feature_query(DependencyDirection::Forward),
151 );
152 let (target_set, host_set) = intermediate_set.target_host_sets();
153
154 let mut proc_macro_edge_ixs = Vec::new();
159 let mut build_dep_edge_ixs = Vec::new();
161 let mut target_edge_ixs = Vec::new();
163 let mut host_edge_ixs = Vec::new();
165
166 let is_enabled = |feature_set: &FeatureSet<'_>,
167 link: &PackageLink<'_>,
168 kind: DependencyKind,
169 platform_spec: &PlatformSpec| {
170 let (from, to) = link.endpoints();
171 let req_status = link.req_for_kind(kind).status();
172 let consider_optional = feature_set
175 .contains((from.id(), FeatureLabel::OptionalDependency(link.dep_name())))
176 .unwrap_or_else(|_| {
177 debug_assert!(
180 req_status.optional_status().is_never(),
181 "for {} -> {}, dep '{}' not declared as optional",
182 from.name(),
183 to.name(),
184 link.dep_name()
185 );
186 false
187 });
188
189 if consider_optional {
190 req_status.enabled_on(platform_spec) != EnabledTernary::Disabled
191 } else {
192 req_status.required_on(platform_spec) != EnabledTernary::Disabled
193 }
194 };
195
196 let mut target_direct_deps =
198 FixedBitSet::with_capacity(graph.package_graph.package_count());
199 let mut host_direct_deps = FixedBitSet::with_capacity(graph.package_graph.package_count());
200
201 let target_platform = &self.opts.target_platform;
204 let host_platform = &self.opts.host_platform;
205
206 let target_packages = target_query.resolve_with_fn(|query, link| {
207 let (from, to) = link.endpoints();
208
209 if from.in_workspace() {
210 target_direct_deps.visit(from.package_ix());
212 }
213
214 if self.is_omitted(to.package_ix()) {
215 return false;
217 }
218
219 let accepted = resolver
220 .as_mut()
221 .map(|r| r.accept(query, link))
222 .unwrap_or(true);
223 if !accepted {
224 return false;
225 }
226
227 let consider_dev =
229 self.opts.include_dev && query.starts_from(from.id()).expect("valid ID");
230 let consider_build = from.has_build_script();
232
233 let mut follow_target =
234 is_enabled(target_set, &link, DependencyKind::Normal, target_platform)
235 || (consider_dev
236 && is_enabled(
237 target_set,
238 &link,
239 DependencyKind::Development,
240 target_platform,
241 ));
242
243 let proc_macro_redirect = follow_target && to.is_proc_macro();
246
247 let build_dep_redirect = consider_build
249 && is_enabled(target_set, &link, DependencyKind::Build, host_platform);
250
251 if build_dep_redirect || proc_macro_redirect {
253 if from.in_workspace() {
254 host_direct_deps.visit(to.package_ix());
256 }
257 host_ixs.push(to.package_ix());
258 }
259 if build_dep_redirect {
260 build_dep_edge_ixs.push(link.edge_ix());
261 }
262 if proc_macro_redirect {
263 proc_macro_edge_ixs.push(link.edge_ix());
264 follow_target = false;
265 }
266
267 if from.in_workspace() && follow_target {
268 target_direct_deps.visit(to.package_ix());
270 }
271
272 if follow_target {
273 target_edge_ixs.push(link.edge_ix());
274 }
275 follow_target
276 });
277
278 let host_ixs = SortedSet::new(host_ixs);
280 let host_packages = graph
281 .package_graph
282 .query_from_parts(host_ixs, DependencyDirection::Forward)
283 .resolve_with_fn(|query, link| {
284 let (from, to) = link.endpoints();
285 if self.is_omitted(to.package_ix()) {
286 return false;
288 }
289
290 let accepted = resolver
291 .as_mut()
292 .map(|r| r.accept(query, link))
293 .unwrap_or(true);
294 if !accepted {
295 return false;
296 }
297
298 let consider_dev =
302 self.opts.include_dev && query.starts_from(from.id()).expect("valid ID");
303 let consider_build = from.has_build_script();
304
305 let res = is_enabled(host_set, &link, DependencyKind::Normal, host_platform)
308 || (consider_build
309 && is_enabled(host_set, &link, DependencyKind::Build, host_platform))
310 || (consider_dev
311 && is_enabled(host_set, &link, DependencyKind::Development, host_platform));
312
313 if res {
314 if from.in_workspace() {
315 host_direct_deps.visit(to.package_ix());
317 }
318 host_edge_ixs.push(link.edge_ix());
319 true
320 } else {
321 false
322 }
323 });
324
325 let target_features = target_packages
328 .to_feature_set(StandardFeatures::All)
329 .intersection(target_set);
330 let host_features = host_packages
331 .to_feature_set(StandardFeatures::All)
332 .intersection(host_set);
333
334 let target_direct_deps =
336 PackageSet::from_included(graph.package_graph(), target_direct_deps);
337 let host_direct_deps = PackageSet::from_included(graph.package_graph, host_direct_deps);
338
339 CargoSet {
340 initials,
341 features_only,
342 target_features,
343 host_features,
344 target_direct_deps,
345 host_direct_deps,
346 proc_macro_edge_ixs: SortedSet::new(proc_macro_edge_ixs),
347 build_dep_edge_ixs: SortedSet::new(build_dep_edge_ixs),
348 target_edge_ixs: SortedSet::new(target_edge_ixs),
349 host_edge_ixs: SortedSet::new(host_edge_ixs),
350 }
351 }
352
353 fn new_v1_intermediate<'g>(
354 &self,
355 query: FeatureQuery<'g>,
356 avoid_dev_deps: bool,
357 ) -> CargoIntermediateSet<'g> {
358 let complete_set = query.resolve_with_fn(|query, link| {
361 if self.is_omitted(link.to().package_ix()) {
362 false
364 } else if !avoid_dev_deps
365 && query
366 .starts_from(link.from().feature_id())
367 .expect("valid ID")
368 {
369 true
371 } else {
372 !link.dev_only()
374 }
375 });
376
377 CargoIntermediateSet::Unified(complete_set)
378 }
379
380 fn new_v2_intermediate<'g>(&self, query: FeatureQuery<'g>) -> CargoIntermediateSet<'g> {
381 let graph = *query.graph();
382 let mut host_ixs: Vec<_> = query
387 .params
388 .initials()
389 .iter()
390 .filter_map(|feature_ix| {
391 let metadata = graph.metadata_for_ix(*feature_ix);
392 if self.opts.initials_platform == InitialsPlatform::Host
393 || metadata.package().is_proc_macro()
394 {
395 Some(metadata.feature_ix())
397 } else {
398 None
400 }
401 })
402 .collect();
403
404 let is_enabled =
405 |link: &ConditionalLink<'_>, kind: DependencyKind, platform_spec: &PlatformSpec| {
406 let platform_status = link.status_for_kind(kind);
407 platform_status.enabled_on(platform_spec) != EnabledTernary::Disabled
408 };
409
410 let target_query = if self.opts.initials_platform == InitialsPlatform::Host {
411 graph.query_from_parts(SortedSet::new(vec![]), DependencyDirection::Forward)
413 } else {
414 query
415 };
416
417 let target_query_2 = target_query.clone();
419
420 let target_platform = &self.opts.target_platform;
422 let host_platform = &self.opts.host_platform;
423 let target = target_query.resolve_with_fn(|query, link| {
424 let (from, to) = link.endpoints();
425
426 if self.is_omitted(to.package_ix()) {
427 return false;
429 }
430
431 let consider_dev =
432 self.opts.include_dev && query.starts_from(from.feature_id()).expect("valid ID");
433 let mut follow_target = is_enabled(&link, DependencyKind::Normal, target_platform)
435 || (consider_dev
436 && is_enabled(&link, DependencyKind::Development, target_platform));
437
438 let proc_macro_redirect = follow_target && to.package().is_proc_macro();
441
442 let build_dep_redirect = {
444 from.package_id() != to.package_id()
461 && is_enabled(&link, DependencyKind::Build, host_platform)
462 };
463
464 if build_dep_redirect || proc_macro_redirect {
466 host_ixs.push(to.feature_ix());
467 }
468 if proc_macro_redirect {
469 follow_target = false;
470 }
471
472 follow_target
473 });
474
475 let host = graph
477 .query_from_parts(SortedSet::new(host_ixs), DependencyDirection::Forward)
478 .resolve_with_fn(|_, link| {
479 let (from, to) = link.endpoints();
480 if self.is_omitted(to.package_ix()) {
481 return false;
483 }
484 let consider_dev = self.opts.include_dev
488 && target_query_2
489 .starts_from(from.feature_id())
490 .expect("valid ID");
491
492 is_enabled(&link, DependencyKind::Normal, host_platform)
493 || is_enabled(&link, DependencyKind::Build, host_platform)
494 || (consider_dev
495 && is_enabled(&link, DependencyKind::Development, host_platform))
496 });
497
498 CargoIntermediateSet::TargetHost { target, host }
499 }
500}