1use crate::{
5 DependencyKind, Error,
6 graph::{
7 DependencyDirection, PackageGraph, PackageIx, PackageLink, 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 ) -> CargoSet<'g> {
43 match self.opts.resolver {
44 CargoResolverVersion::V1 => self.new_v1(initials, features_only, false),
45 CargoResolverVersion::V1Install => {
46 let avoid_dev_deps = !self.opts.include_dev;
47 self.new_v1(initials, features_only, avoid_dev_deps)
48 }
49 CargoResolverVersion::V2 | CargoResolverVersion::V3 => {
51 self.new_v2(initials, features_only)
52 }
53 }
54 }
55
56 pub(super) fn build_intermediate(self, query: FeatureQuery) -> CargoIntermediateSet {
57 match self.opts.resolver {
58 CargoResolverVersion::V1 => self.new_v1_intermediate(query, false),
59 CargoResolverVersion::V1Install => {
60 let avoid_dev_deps = !self.opts.include_dev;
61 self.new_v1_intermediate(query, avoid_dev_deps)
62 }
63 CargoResolverVersion::V2 | CargoResolverVersion::V3 => self.new_v2_intermediate(query),
64 }
65 }
66
67 fn new_v1<'g>(
68 self,
69 initials: FeatureSet<'g>,
70 features_only: FeatureSet<'g>,
71 avoid_dev_deps: bool,
72 ) -> CargoSet<'g> {
73 self.build_set(initials, features_only, |query| {
74 self.new_v1_intermediate(query, avoid_dev_deps)
75 })
76 }
77
78 fn new_v2<'g>(self, initials: FeatureSet<'g>, features_only: FeatureSet<'g>) -> CargoSet<'g> {
79 self.build_set(initials, features_only, |query| {
80 self.new_v2_intermediate(query)
81 })
82 }
83
84 fn is_omitted(&self, package_ix: NodeIndex<PackageIx>) -> bool {
89 self.omitted_packages.contains(&package_ix)
90 }
91
92 fn build_set<'g>(
93 &self,
94 initials: FeatureSet<'g>,
95 features_only: FeatureSet<'g>,
96 intermediate_fn: impl FnOnce(FeatureQuery<'g>) -> CargoIntermediateSet<'g>,
97 ) -> CargoSet<'g> {
98 let graph = *initials.graph();
100 let mut host_ixs = Vec::new();
103 let target_ixs: Vec<_> = initials
104 .ixs_unordered()
105 .filter_map(|feature_ix| {
106 let metadata = graph.metadata_for_ix(feature_ix);
107 let package_ix = metadata.package_ix();
108 match self.opts.initials_platform {
109 InitialsPlatform::Host => {
110 host_ixs.push(package_ix);
112 None
113 }
114 InitialsPlatform::Standard => {
115 if metadata.package().is_proc_macro() {
117 host_ixs.push(package_ix);
118 None
119 } else {
120 Some(package_ix)
121 }
122 }
123 InitialsPlatform::ProcMacrosOnTarget => {
124 if metadata.package().is_proc_macro() {
127 host_ixs.push(package_ix);
128 }
129 Some(package_ix)
130 }
131 }
132 })
133 .collect();
134 let target_query = graph
135 .package_graph
136 .query_from_parts(SortedSet::new(target_ixs), DependencyDirection::Forward);
137
138 let initials_plus_features_only = initials.union(&features_only);
141 let intermediate_set = intermediate_fn(
142 initials_plus_features_only.to_feature_query(DependencyDirection::Forward),
143 );
144 let (target_set, host_set) = intermediate_set.target_host_sets();
145
146 let mut proc_macro_edge_ixs = Vec::new();
151 let mut build_dep_edge_ixs = Vec::new();
153
154 let is_enabled = |feature_set: &FeatureSet<'_>,
155 link: &PackageLink<'_>,
156 kind: DependencyKind,
157 platform_spec: &PlatformSpec| {
158 let (from, to) = link.endpoints();
159 let req_status = link.req_for_kind(kind).status();
160 let consider_optional = feature_set
163 .contains((from.id(), FeatureLabel::OptionalDependency(link.dep_name())))
164 .unwrap_or_else(|_| {
165 debug_assert!(
168 req_status.optional_status().is_never(),
169 "for {} -> {}, dep '{}' not declared as optional",
170 from.name(),
171 to.name(),
172 link.dep_name()
173 );
174 false
175 });
176
177 if consider_optional {
178 req_status.enabled_on(platform_spec) != EnabledTernary::Disabled
179 } else {
180 req_status.required_on(platform_spec) != EnabledTernary::Disabled
181 }
182 };
183
184 let mut target_direct_deps =
186 FixedBitSet::with_capacity(graph.package_graph.package_count());
187 let mut host_direct_deps = FixedBitSet::with_capacity(graph.package_graph.package_count());
188
189 let target_platform = &self.opts.target_platform;
192 let host_platform = &self.opts.host_platform;
193
194 let target_packages = target_query.resolve_with_fn(|query, link| {
195 let (from, to) = link.endpoints();
196
197 if from.in_workspace() {
198 target_direct_deps.visit(from.package_ix());
200 }
201
202 if self.is_omitted(to.package_ix()) {
203 return false;
205 }
206
207 let consider_dev =
209 self.opts.include_dev && query.starts_from(from.id()).expect("valid ID");
210 let consider_build = from.has_build_script();
212
213 let mut follow_target =
214 is_enabled(target_set, &link, DependencyKind::Normal, target_platform)
215 || (consider_dev
216 && is_enabled(
217 target_set,
218 &link,
219 DependencyKind::Development,
220 target_platform,
221 ));
222
223 let proc_macro_redirect = follow_target && to.is_proc_macro();
226
227 let build_dep_redirect = consider_build
229 && is_enabled(target_set, &link, DependencyKind::Build, host_platform);
230
231 if build_dep_redirect || proc_macro_redirect {
233 if from.in_workspace() {
234 host_direct_deps.visit(to.package_ix());
236 }
237 host_ixs.push(to.package_ix());
238 }
239 if build_dep_redirect {
240 build_dep_edge_ixs.push(link.edge_ix());
241 }
242 if proc_macro_redirect {
243 proc_macro_edge_ixs.push(link.edge_ix());
244 follow_target = false;
245 }
246
247 if from.in_workspace() && follow_target {
248 target_direct_deps.visit(to.package_ix());
250 }
251
252 follow_target
253 });
254
255 let host_ixs = SortedSet::new(host_ixs);
257 let host_packages = graph
258 .package_graph
259 .query_from_parts(host_ixs, DependencyDirection::Forward)
260 .resolve_with_fn(|query, link| {
261 let (from, to) = link.endpoints();
262 if self.is_omitted(to.package_ix()) {
263 return false;
265 }
266
267 let consider_dev =
271 self.opts.include_dev && query.starts_from(from.id()).expect("valid ID");
272 let consider_build = from.has_build_script();
273
274 let res = is_enabled(host_set, &link, DependencyKind::Normal, host_platform)
277 || (consider_build
278 && is_enabled(host_set, &link, DependencyKind::Build, host_platform))
279 || (consider_dev
280 && is_enabled(host_set, &link, DependencyKind::Development, host_platform));
281
282 if res {
283 if from.in_workspace() {
284 host_direct_deps.visit(to.package_ix());
286 }
287 true
288 } else {
289 false
290 }
291 });
292
293 let target_features = target_packages
296 .to_feature_set(StandardFeatures::All)
297 .intersection(target_set);
298 let host_features = host_packages
299 .to_feature_set(StandardFeatures::All)
300 .intersection(host_set);
301
302 let target_direct_deps =
304 PackageSet::from_included(graph.package_graph(), target_direct_deps);
305 let host_direct_deps = PackageSet::from_included(graph.package_graph, host_direct_deps);
306
307 CargoSet {
308 initials,
309 features_only,
310 target_features,
311 host_features,
312 target_direct_deps,
313 host_direct_deps,
314 proc_macro_edge_ixs: SortedSet::new(proc_macro_edge_ixs),
315 build_dep_edge_ixs: SortedSet::new(build_dep_edge_ixs),
316 }
317 }
318
319 fn new_v1_intermediate<'g>(
320 &self,
321 query: FeatureQuery<'g>,
322 avoid_dev_deps: bool,
323 ) -> CargoIntermediateSet<'g> {
324 let complete_set = query.resolve_with_fn(|query, link| {
327 if self.is_omitted(link.to().package_ix()) {
328 false
330 } else if !avoid_dev_deps
331 && query
332 .starts_from(link.from().feature_id())
333 .expect("valid ID")
334 {
335 true
337 } else {
338 !link.dev_only()
340 }
341 });
342
343 CargoIntermediateSet::Unified(complete_set)
344 }
345
346 fn new_v2_intermediate<'g>(&self, query: FeatureQuery<'g>) -> CargoIntermediateSet<'g> {
347 let graph = *query.graph();
348 let mut host_ixs: Vec<_> = query
353 .params
354 .initials()
355 .iter()
356 .filter_map(|feature_ix| {
357 let metadata = graph.metadata_for_ix(*feature_ix);
358 if self.opts.initials_platform == InitialsPlatform::Host
359 || metadata.package().is_proc_macro()
360 {
361 Some(metadata.feature_ix())
363 } else {
364 None
366 }
367 })
368 .collect();
369
370 let is_enabled =
371 |link: &ConditionalLink<'_>, kind: DependencyKind, platform_spec: &PlatformSpec| {
372 let platform_status = link.status_for_kind(kind);
373 platform_status.enabled_on(platform_spec) != EnabledTernary::Disabled
374 };
375
376 let target_query = if self.opts.initials_platform == InitialsPlatform::Host {
377 graph.query_from_parts(SortedSet::new(vec![]), DependencyDirection::Forward)
379 } else {
380 query
381 };
382
383 let target_query_2 = target_query.clone();
385
386 let target_platform = &self.opts.target_platform;
388 let host_platform = &self.opts.host_platform;
389 let target = target_query.resolve_with_fn(|query, link| {
390 let (from, to) = link.endpoints();
391
392 if self.is_omitted(to.package_ix()) {
393 return false;
395 }
396
397 let consider_dev =
398 self.opts.include_dev && query.starts_from(from.feature_id()).expect("valid ID");
399 let mut follow_target = is_enabled(&link, DependencyKind::Normal, target_platform)
401 || (consider_dev
402 && is_enabled(&link, DependencyKind::Development, target_platform));
403
404 let proc_macro_redirect = follow_target && to.package().is_proc_macro();
407
408 let build_dep_redirect = {
410 from.package_id() != to.package_id()
427 && is_enabled(&link, DependencyKind::Build, host_platform)
428 };
429
430 if build_dep_redirect || proc_macro_redirect {
432 host_ixs.push(to.feature_ix());
433 }
434 if proc_macro_redirect {
435 follow_target = false;
436 }
437
438 follow_target
439 });
440
441 let host = graph
443 .query_from_parts(SortedSet::new(host_ixs), DependencyDirection::Forward)
444 .resolve_with_fn(|_, link| {
445 let (from, to) = link.endpoints();
446 if self.is_omitted(to.package_ix()) {
447 return false;
449 }
450 let consider_dev = self.opts.include_dev
454 && target_query_2
455 .starts_from(from.feature_id())
456 .expect("valid ID");
457
458 is_enabled(&link, DependencyKind::Normal, host_platform)
459 || is_enabled(&link, DependencyKind::Build, host_platform)
460 || (consider_dev
461 && is_enabled(&link, DependencyKind::Development, host_platform))
462 });
463
464 CargoIntermediateSet::TargetHost { target, host }
465 }
466}