guppy/graph/cargo/cargo_api.rs
1// Copyright (c) The cargo-guppy Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use crate::{
5 Error, PackageId,
6 graph::{
7 DependencyDirection, PackageGraph, PackageIx, PackageLink, PackageLinkContext, PackageSet,
8 cargo::build::CargoSetBuildState,
9 feature::{FeatureGraph, FeatureSet},
10 },
11 platform::PlatformSpec,
12 sorted_set::SortedSet,
13};
14use petgraph::prelude::*;
15use serde::{Deserialize, Serialize};
16use std::{collections::HashSet, fmt};
17
18/// Options for queries which simulate what Cargo does.
19///
20/// This provides control over the resolution algorithm used by `guppy`'s simulation of Cargo.
21#[derive(Clone, Debug)]
22pub struct CargoOptions<'a> {
23 pub(crate) resolver: CargoResolverVersion,
24 pub(crate) include_dev: bool,
25 pub(crate) initials_platform: InitialsPlatform,
26 pub(crate) host_platform: PlatformSpec,
27 pub(crate) target_platform: PlatformSpec,
28 pub(crate) omitted_packages: HashSet<&'a PackageId>,
29}
30
31impl<'a> CargoOptions<'a> {
32 /// Creates a new `CargoOptions` with default settings.
33 ///
34 /// The default settings are similar to what a plain `cargo build` does:
35 ///
36 /// * use version 3 of the Cargo resolver
37 /// * exclude dev-dependencies
38 /// * do not build proc macros specified in the query on the target platform
39 /// * resolve dependencies assuming any possible host or target platform
40 /// * do not omit any packages.
41 pub fn new() -> Self {
42 Self {
43 resolver: CargoResolverVersion::V3,
44 include_dev: false,
45 initials_platform: InitialsPlatform::Standard,
46 host_platform: PlatformSpec::Any,
47 target_platform: PlatformSpec::Any,
48 omitted_packages: HashSet::new(),
49 }
50 }
51
52 /// Sets the Cargo feature resolver version.
53 ///
54 /// For more about feature resolution, see the documentation for `CargoResolverVersion`.
55 pub fn set_resolver(&mut self, resolver: CargoResolverVersion) -> &mut Self {
56 self.resolver = resolver;
57 self
58 }
59
60 /// If set to true, causes dev-dependencies of the initial set to be followed.
61 ///
62 /// This does not affect transitive dependencies -- for example, a build or dev-dependency's
63 /// further dev-dependencies are never followed.
64 ///
65 /// The default is false, which matches what a plain `cargo build` does.
66 pub fn set_include_dev(&mut self, include_dev: bool) -> &mut Self {
67 self.include_dev = include_dev;
68 self
69 }
70
71 /// Configures the way initials are treated on the target and the host.
72 ///
73 /// The default is a "standard" build and this does not usually need to be set, but some
74 /// advanced use cases may require it. For more about this option, see the documentation for
75 /// [`InitialsPlatform`](InitialsPlatform).
76 pub fn set_initials_platform(&mut self, initials_platform: InitialsPlatform) -> &mut Self {
77 self.initials_platform = initials_platform;
78 self
79 }
80
81 /// Sets both the target and host platforms to the provided spec.
82 pub fn set_platform(&mut self, platform_spec: impl Into<PlatformSpec>) -> &mut Self {
83 let platform_spec = platform_spec.into();
84 self.target_platform = platform_spec.clone();
85 self.host_platform = platform_spec;
86 self
87 }
88
89 /// Sets the target platform to the provided spec.
90 pub fn set_target_platform(&mut self, target_platform: impl Into<PlatformSpec>) -> &mut Self {
91 self.target_platform = target_platform.into();
92 self
93 }
94
95 /// Sets the host platform to the provided spec.
96 pub fn set_host_platform(&mut self, host_platform: impl Into<PlatformSpec>) -> &mut Self {
97 self.host_platform = host_platform.into();
98 self
99 }
100
101 /// Omits edges into the given packages.
102 ///
103 /// This may be useful in order to figure out what additional dependencies or features a
104 /// particular set of packages pulls in.
105 ///
106 /// This method is additive.
107 pub fn add_omitted_packages(
108 &mut self,
109 package_ids: impl IntoIterator<Item = &'a PackageId>,
110 ) -> &mut Self {
111 self.omitted_packages.extend(package_ids);
112 self
113 }
114
115 // Note that the lifetime of the returned value is `'g` rather than being
116 // tied to `self`.
117 pub(crate) fn resolve_package_ids<'g>(
118 &self,
119 graph: &'g PackageGraph,
120 ) -> Result<CargoOptions<'g>, Error> {
121 let omitted_packages = self
122 .omitted_packages
123 .iter()
124 .map(|package_id| graph.metadata(package_id).map(|metadata| metadata.id()))
125 .collect::<Result<HashSet<_>, _>>()?;
126
127 Ok(CargoOptions {
128 resolver: self.resolver,
129 include_dev: self.include_dev,
130 initials_platform: self.initials_platform,
131 host_platform: self.host_platform.clone(),
132 target_platform: self.target_platform.clone(),
133 omitted_packages,
134 })
135 }
136}
137
138impl Default for CargoOptions<'_> {
139 fn default() -> Self {
140 Self::new()
141 }
142}
143
144/// Represents whether a particular link within a package graph should be
145/// followed while building a [`CargoSet`].
146///
147/// This is similar to [`PackageLinkVisitor`], but is passed a
148/// [`CargoLinkContext`] with additional information about the Cargo build.
149///
150/// [`PackageLinkVisitor`]: crate::graph::PackageLinkVisitor
151pub trait CargoLinkVisitor<'g> {
152 /// Returns true if this link should be followed.
153 ///
154 /// Returning false does not prevent the `to` package from being included
155 /// if it's reachable through other means.
156 fn visit_link(&mut self, cx: &CargoLinkContext<'_, 'g>, link: PackageLink<'g>) -> bool;
157}
158
159impl<'g, T> CargoLinkVisitor<'g> for &mut T
160where
161 T: CargoLinkVisitor<'g>,
162{
163 fn visit_link(&mut self, cx: &CargoLinkContext<'_, 'g>, link: PackageLink<'g>) -> bool {
164 (**self).visit_link(cx, link)
165 }
166}
167
168impl<'g> CargoLinkVisitor<'g> for Box<dyn CargoLinkVisitor<'g> + '_> {
169 fn visit_link(&mut self, cx: &CargoLinkContext<'_, 'g>, link: PackageLink<'g>) -> bool {
170 (**self).visit_link(cx, link)
171 }
172}
173
174impl<'g> CargoLinkVisitor<'g> for &mut dyn CargoLinkVisitor<'g> {
175 fn visit_link(&mut self, cx: &CargoLinkContext<'_, 'g>, link: PackageLink<'g>) -> bool {
176 (**self).visit_link(cx, link)
177 }
178}
179
180/// Context passed to a [`CargoLinkVisitor`] for each link visited while
181/// building a [`CargoSet`].
182#[derive(Clone, Debug)]
183pub struct CargoLinkContext<'a, 'g> {
184 package_context: &'a PackageLinkContext<'g>,
185 build_platform: BuildPlatform,
186 platform_spec: &'a PlatformSpec,
187 build_dep_platform_spec: &'a PlatformSpec,
188 include_dev: bool,
189}
190
191impl<'a, 'g> CargoLinkContext<'a, 'g> {
192 pub(super) fn new(
193 package_context: &'a PackageLinkContext<'g>,
194 build_platform: BuildPlatform,
195 opts: &'a CargoOptions<'_>,
196 ) -> Self {
197 let (platform_spec, build_dep_platform_spec) = match build_platform {
198 BuildPlatform::Target => (&opts.target_platform, &opts.host_platform),
199 BuildPlatform::Host => (&opts.host_platform, &opts.host_platform),
200 };
201 Self {
202 package_context,
203 build_platform,
204 platform_spec,
205 build_dep_platform_spec,
206 include_dev: opts.include_dev,
207 }
208 }
209
210 /// Returns the context for the underlying package graph traversal.
211 pub fn package_context(&self) -> &PackageLinkContext<'g> {
212 self.package_context
213 }
214
215 /// Returns the platform this link is being evaluated for: the target pass
216 /// or the host pass.
217 pub fn build_platform(&self) -> BuildPlatform {
218 self.build_platform
219 }
220
221 /// Returns the platform spec that normal and dev dependencies are
222 /// evaluated against in this pass.
223 pub fn platform_spec(&self) -> &PlatformSpec {
224 self.platform_spec
225 }
226
227 /// Returns the platform spec that build dependencies are evaluated against
228 /// in this pass (always the host platform).
229 pub fn build_dep_platform_spec(&self) -> &PlatformSpec {
230 self.build_dep_platform_spec
231 }
232
233 /// Returns true if dev-dependencies of this link may be followed:
234 /// [`CargoOptions::set_include_dev`] is set and the `from` package is an
235 /// initial.
236 pub fn considers_dev_deps(&self, link: &PackageLink<'g>) -> bool {
237 self.include_dev && self.package_context.starts_from_initial(link)
238 }
239
240 /// Returns true if build-dependencies of this link may be followed: the
241 /// `from` package has a build script.
242 pub fn considers_build_deps(&self, link: &PackageLink<'g>) -> bool {
243 link.from().has_build_script()
244 }
245}
246
247/// The version of Cargo's feature resolver to use.
248#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
249#[cfg_attr(feature = "proptest1", derive(proptest_derive::Arbitrary))]
250#[serde(rename_all = "kebab-case")]
251#[non_exhaustive]
252pub enum CargoResolverVersion {
253 /// The "classic" feature resolver in Rust.
254 ///
255 /// This feature resolver unifies features across inactive platforms, and also unifies features
256 /// across normal, build and dev dependencies for initials. This may produce results that are
257 /// surprising at times.
258 #[serde(rename = "1", alias = "v1")]
259 V1,
260
261 /// The "classic" feature resolver in Rust, as used by commands like `cargo install`.
262 ///
263 /// This resolver is the same as `V1`, except it doesn't unify features across dev dependencies
264 /// for initials. However, if `CargoOptions::set_include_dev` is set to true, it behaves
265 /// identically to the V1 resolver.
266 ///
267 /// For more, see
268 /// [avoid-dev-deps](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#avoid-dev-deps)
269 /// in the Cargo reference.
270 #[serde(rename = "install", alias = "v1-install")]
271 V1Install,
272
273 /// [Version 2 of the feature resolver](https://doc.rust-lang.org/cargo/reference/resolver.html#feature-resolver-version-2),
274 /// available since Rust 1.51. This feature resolver does not unify features:
275 ///
276 /// * across host (build) and target (regular) dependencies
277 /// * with dev-dependencies for initials, if tests aren't currently being built
278 /// * with [platform-specific dependencies](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#platform-specific-dependencies) that are currently inactive
279 ///
280 /// Version 2 of the feature resolver can be enabled by specifying `resolver
281 /// = "2"` in the workspace's `Cargo.toml`. It is also [the default resolver
282 /// version](https://doc.rust-lang.org/beta/edition-guide/rust-2021/default-cargo-resolver.html)
283 /// for [the Rust 2021
284 /// edition](https://doc.rust-lang.org/edition-guide/rust-2021/index.html).
285 #[serde(rename = "2", alias = "v2")]
286 V2,
287
288 /// [Version 3 of the dependency
289 /// resolver](https://doc.rust-lang.org/beta/cargo/reference/resolver.html#resolver-versions),
290 /// available since Rust 1.84.
291 ///
292 /// Version 3 of the resolver enables [MSRV-aware dependency
293 /// resolution](https://doc.rust-lang.org/beta/cargo/reference/config.html#resolverincompatible-rust-versions).
294 /// There are no changes to feature resolution compared to version 2.
295 ///
296 /// Version 3 of the feature resolver can be enabled by specifying `resolver
297 /// = "3"` in the workspace's `Cargo.toml`. It is also [the default resolver
298 /// version](https://doc.rust-lang.org/beta/edition-guide/rust-2024/cargo-resolver.html)
299 /// for [the Rust 2024
300 /// edition](https://doc.rust-lang.org/beta/edition-guide/rust-2024/index.html).
301 #[serde(rename = "3", alias = "v3")]
302 V3,
303}
304
305/// For a given Cargo build simulation, what platform to assume the initials are being built on.
306#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
307#[cfg_attr(feature = "proptest1", derive(proptest_derive::Arbitrary))]
308#[serde(rename_all = "kebab-case")]
309pub enum InitialsPlatform {
310 /// Assume that the initials are being built on the host platform.
311 ///
312 /// This is most useful for "continuing" simulations, where it is already known that some
313 /// packages are being built on the host and one wishes to find their dependencies.
314 Host,
315
316 /// Assume a standard build.
317 ///
318 /// In this mode, all initials other than proc-macros are built on the target platform. Proc-
319 /// macros, being compiler plugins, are built on the host.
320 ///
321 /// This is the default for `InitialsPlatform`.
322 Standard,
323
324 /// Perform a standard build, and also build proc-macros on the target.
325 ///
326 /// Proc-macro crates may include tests, which are run on the target platform. This option is
327 /// most useful for such situations.
328 ProcMacrosOnTarget,
329}
330
331/// The default for `InitialsPlatform`: the `Standard` option.
332impl Default for InitialsPlatform {
333 fn default() -> Self {
334 InitialsPlatform::Standard
335 }
336}
337
338/// The inputs to a `CargoSet` other than the initials.
339///
340/// The initials are kept separate so that one `CargoSetInputs` can be reused
341/// across many `CargoSet`s via [`to_cargo_set`](Self::to_cargo_set).
342///
343/// Both fields must refer to the same `PackageGraph` as the initials they
344/// are used with.
345#[derive(Clone, Debug)]
346#[non_exhaustive]
347pub struct CargoSetInputs<'g> {
348 /// The Cargo options used to build the `CargoSet`.
349 pub options: CargoOptions<'g>,
350
351 /// The features-only set: see [`CargoSet::new`].
352 pub features_only: FeatureSet<'g>,
353}
354
355assert_covariant!(CargoSetInputs);
356
357impl<'g> CargoSetInputs<'g> {
358 /// Creates a new `CargoSetInputs` from the given inputs.
359 pub fn new(options: CargoOptions<'g>, features_only: FeatureSet<'g>) -> Self {
360 Self {
361 options,
362 features_only,
363 }
364 }
365
366 /// Simulates a Cargo build of `initials` with these inputs.
367 ///
368 /// This is a shorthand for [`CargoSet::new`].
369 ///
370 /// ## Panics
371 ///
372 /// Panics if `initials` was built from a different package graph than
373 /// these inputs.
374 pub fn to_cargo_set(&self, initials: FeatureSet<'g>) -> Result<CargoSet<'g>, Error> {
375 CargoSet::new(initials, self.features_only.clone(), &self.options)
376 }
377}
378
379/// A set of packages and features, as would be built by Cargo.
380///
381/// Cargo implements a set of algorithms to figure out which packages or features are built in
382/// a given situation. `guppy` implements those algorithms.
383#[derive(Clone, Debug)]
384pub struct CargoSet<'g> {
385 pub(super) initials: FeatureSet<'g>,
386 pub(super) inputs: CargoSetInputs<'g>,
387 pub(super) target_features: FeatureSet<'g>,
388 pub(super) host_features: FeatureSet<'g>,
389 pub(super) target_direct_deps: PackageSet<'g>,
390 pub(super) host_direct_deps: PackageSet<'g>,
391 pub(super) proc_macro_edge_ixs: SortedSet<EdgeIndex<PackageIx>>,
392 pub(super) build_dep_edge_ixs: SortedSet<EdgeIndex<PackageIx>>,
393 pub(super) target_edge_ixs: SortedSet<EdgeIndex<PackageIx>>,
394 pub(super) host_edge_ixs: SortedSet<EdgeIndex<PackageIx>>,
395}
396
397assert_covariant!(CargoSet);
398
399impl<'g> CargoSet<'g> {
400 /// Simulates a Cargo build of this feature set, with the given options.
401 ///
402 /// The feature sets are expected to be entirely within the workspace. Its behavior outside the
403 /// workspace isn't defined and may be surprising.
404 ///
405 /// `CargoSet::new` takes two `FeatureSet` instances:
406 /// * `initials`, from which dependencies are followed to build the `CargoSet`.
407 /// * `features_only`, which are additional inputs that are only used for feature
408 /// unification. This may be used to simulate, e.g. `cargo build --package foo --package bar`,
409 /// when you only care about the results of `foo` but specifying `bar` influences the build.
410 ///
411 /// Note that even if a package is in `features_only`, it may be included in the final build set
412 /// through other means (for example, if it is also in `initials` or it is a dependency of one
413 /// of them).
414 ///
415 /// In many cases `features_only` is empty -- in that case you may wish to use
416 /// `FeatureSet::into_cargo_set()`, and it may be more convenient to use that if the code is
417 /// written in a "fluent" style.
418 ///
419 ///
420 pub fn new(
421 initials: FeatureSet<'g>,
422 features_only: FeatureSet<'g>,
423 opts: &CargoOptions<'_>,
424 ) -> Result<Self, Error> {
425 Self::new_internal(initials, features_only, None, opts)
426 }
427
428 /// Like `Cargo.new`, but takes an additional [`CargoLinkVisitor`] which can
429 /// be used to filter out some dependency edges, or to collect additional
430 /// information.
431 ///
432 /// [`visitor.visit_link`] is called for both target and host dependencies. It
433 /// is called after static filtering through
434 /// [`CargoOptions::add_omitted_packages`], but before any other decisions
435 /// are made.
436 ///
437 /// [`visitor.visit_link`]: CargoLinkVisitor::visit_link
438 pub fn with_cargo_link_visitor(
439 initials: FeatureSet<'g>,
440 features_only: FeatureSet<'g>,
441 mut visitor: impl CargoLinkVisitor<'g>,
442 opts: &CargoOptions<'_>,
443 ) -> Result<Self, Error> {
444 Self::new_internal(initials, features_only, Some(&mut visitor), opts)
445 }
446
447 /// Internal helper to deduplicate code across `CargoSet::new` and
448 /// `CargoSet::with_cargo_link_visitor`.
449 fn new_internal(
450 initials: FeatureSet<'g>,
451 features_only: FeatureSet<'g>,
452 visitor: Option<&mut dyn CargoLinkVisitor<'g>>,
453 opts: &CargoOptions<'_>,
454 ) -> Result<Self, Error> {
455 let graph = initials.graph().package_graph;
456 let build_state = CargoSetBuildState::new(graph, opts.resolve_package_ids(graph)?)?;
457 Ok(build_state.build(initials, features_only, visitor))
458 }
459
460 /// Creates a new `CargoIntermediateSet` based on the given query and options.
461 ///
462 /// This set contains an over-estimate of targets and features.
463 ///
464 /// Not part of the stable API, exposed for testing.
465 #[doc(hidden)]
466 pub fn new_intermediate(
467 initials: &FeatureSet<'g>,
468 opts: &CargoOptions<'_>,
469 ) -> Result<CargoIntermediateSet<'g>, Error> {
470 let graph = initials.graph().package_graph;
471 let build_state = CargoSetBuildState::new(graph, opts.resolve_package_ids(graph)?)?;
472 Ok(build_state.build_intermediate(initials.to_feature_query(DependencyDirection::Forward)))
473 }
474
475 /// Returns the feature graph for this `CargoSet` instance.
476 pub fn feature_graph(&self) -> &FeatureGraph<'g> {
477 self.initials.graph()
478 }
479
480 /// Returns the package graph for this `CargoSet` instance.
481 pub fn package_graph(&self) -> &'g PackageGraph {
482 self.feature_graph().package_graph
483 }
484
485 /// Returns the initial packages and features from which the `CargoSet` instance was
486 /// constructed.
487 pub fn initials(&self) -> &FeatureSet<'g> {
488 &self.initials
489 }
490
491 /// Returns the inputs, other than the initials, from which this `CargoSet`
492 /// instance was constructed.
493 pub fn inputs(&self) -> &CargoSetInputs<'g> {
494 &self.inputs
495 }
496
497 /// Returns the feature set enabled on the target platform.
498 ///
499 /// This represents the packages and features that are included as code in the final build
500 /// artifacts. This is relevant for both cross-compilation and auditing.
501 pub fn target_features(&self) -> &FeatureSet<'g> {
502 &self.target_features
503 }
504
505 /// Returns the feature set enabled on the host platform.
506 ///
507 /// This represents the packages and features that influence the final build artifacts, but
508 /// whose code is generally not directly included.
509 ///
510 /// This includes all procedural macros, including those specified in the initial query.
511 pub fn host_features(&self) -> &FeatureSet<'g> {
512 &self.host_features
513 }
514
515 /// Returns the feature set enabled on the specified build platform.
516 pub fn platform_features(&self, build_platform: BuildPlatform) -> &FeatureSet<'g> {
517 match build_platform {
518 BuildPlatform::Target => self.target_features(),
519 BuildPlatform::Host => self.host_features(),
520 }
521 }
522
523 /// Returns the feature sets across the target and host build platforms.
524 pub fn all_features(&self) -> [(BuildPlatform, &FeatureSet<'g>); 2] {
525 [
526 (BuildPlatform::Target, self.target_features()),
527 (BuildPlatform::Host, self.host_features()),
528 ]
529 }
530
531 /// Returns the set of workspace and direct dependency packages on the target platform.
532 ///
533 /// The packages in this set are a subset of the packages in `target_features`.
534 pub fn target_direct_deps(&self) -> &PackageSet<'g> {
535 &self.target_direct_deps
536 }
537
538 /// Returns the set of workspace and direct dependency packages on the host platform.
539 ///
540 /// The packages in this set are a subset of the packages in `host_features`.
541 pub fn host_direct_deps(&self) -> &PackageSet<'g> {
542 &self.host_direct_deps
543 }
544
545 /// Returns the set of workspace and direct dependency packages on the specified build platform.
546 pub fn platform_direct_deps(&self, build_platform: BuildPlatform) -> &PackageSet<'g> {
547 match build_platform {
548 BuildPlatform::Target => self.target_direct_deps(),
549 BuildPlatform::Host => self.host_direct_deps(),
550 }
551 }
552
553 /// Returns the set of workspace and direct dependency packages across the target and host
554 /// build platforms.
555 pub fn all_direct_deps(&self) -> [(BuildPlatform, &PackageSet<'g>); 2] {
556 [
557 (BuildPlatform::Target, self.target_direct_deps()),
558 (BuildPlatform::Host, self.host_direct_deps()),
559 ]
560 }
561
562 /// Returns `PackageLink` instances for procedural macro dependencies from target packages.
563 ///
564 /// Procedural macros straddle the line between target and host: they're built for the host
565 /// but generate code that is compiled for the target platform.
566 ///
567 /// ## Notes
568 ///
569 /// Procedural macro packages will be included in the *host* feature set.
570 /// See also [`Self::host_features`].
571 ///
572 /// The returned iterator will include proc macros that are depended on normally or in dev
573 /// builds from initials (if `include_dev` is set), but not the ones in the
574 /// `[build-dependencies]` section.
575 pub fn proc_macro_links<'a>(&'a self) -> impl ExactSizeIterator<Item = PackageLink<'g>> + 'a {
576 let package_graph = self.target_features.graph().package_graph;
577 self.proc_macro_edge_ixs
578 .iter()
579 .map(move |edge_ix| package_graph.edge_ix_to_link(*edge_ix))
580 }
581
582 /// Returns `PackageLink` instances for build dependencies from target packages.
583 ///
584 /// ## Notes
585 ///
586 /// For each link, the `from` is built on the target while the `to` is built on the host.
587 /// It is possible (though rare) that a build dependency is also included as a normal
588 /// dependency, or as a dev dependency in which case it will also be built on the target.
589 ///
590 /// The returned iterators will not include build dependencies of host packages -- those are
591 /// also built on the host.
592 pub fn build_dep_links<'a>(&'a self) -> impl ExactSizeIterator<Item = PackageLink<'g>> + 'a {
593 let package_graph = self.target_features.graph().package_graph;
594 self.build_dep_edge_ixs
595 .iter()
596 .map(move |edge_ix| package_graph.edge_ix_to_link(*edge_ix))
597 }
598
599 /// Returns `PackageLink` instances for normal dependencies between target packages.
600 ///
601 /// ## Notes
602 ///
603 /// For each link, both the `from` and the `to` package are built on the target.
604 ///
605 /// Target packages will be included in the *target* feature set.
606 /// See also [`Self::target_features`].
607 pub fn target_links<'a>(&'a self) -> impl ExactSizeIterator<Item = PackageLink<'g>> + 'a {
608 let package_graph = self.target_features.graph().package_graph;
609 self.target_edge_ixs
610 .iter()
611 .map(move |edge_ix| package_graph.edge_ix_to_link(*edge_ix))
612 }
613
614 /// Returns `PackageLink` instances for dependencies between host packages.
615 ///
616 /// ## Notes
617 ///
618 /// For each link, both the `from` and the `to` package are built on the host.
619 /// Typically most links are normal dependencies, but it is possible to have
620 /// build dependencies as well (e.g. dependencies of a build script used
621 /// in a proc-macro package).
622 ///
623 /// Host packages will be included in the *host* feature set.
624 /// See also [`Self::host_features`].
625 pub fn host_links<'a>(&'a self) -> impl ExactSizeIterator<Item = PackageLink<'g>> + 'a {
626 let package_graph = self.target_features.graph().package_graph;
627 self.host_edge_ixs
628 .iter()
629 .map(move |edge_ix| package_graph.edge_ix_to_link(*edge_ix))
630 }
631}
632
633/// Either the target or the host platform.
634///
635/// When Cargo computes the platforms it is building on, it computes two separate build graphs: one
636/// for the target platform and one for the host. This is most useful in cross-compilation
637/// situations where the target is different from the host, but the separate graphs are computed
638/// whether or not a build cross-compiles.
639///
640/// A `cargo check` can be looked at as a kind of cross-compilation as well--machine code is
641/// generated and run for the host platform but not the target platform. This is why `cargo check`
642/// output usually has some lines that say `Compiling` (for the host platform) and some that say
643/// `Checking` (for the target platform).
644#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
645pub enum BuildPlatform {
646 /// The target platform.
647 ///
648 /// This represents the packages and features that are included as code in the final build
649 /// artifacts.
650 Target,
651
652 /// The host platform.
653 ///
654 /// This represents build scripts, proc macros and other code that is run on the machine doing
655 /// the compiling.
656 Host,
657}
658
659impl BuildPlatform {
660 /// A list of all possible variants of `BuildPlatform`.
661 pub const VALUES: &'static [Self; 2] = &[BuildPlatform::Target, BuildPlatform::Host];
662
663 /// Returns the build platform that's not `self`.
664 pub fn flip(self) -> Self {
665 match self {
666 BuildPlatform::Host => BuildPlatform::Target,
667 BuildPlatform::Target => BuildPlatform::Host,
668 }
669 }
670}
671
672impl fmt::Display for BuildPlatform {
673 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
674 match self {
675 BuildPlatform::Target => write!(f, "target"),
676 BuildPlatform::Host => write!(f, "host"),
677 }
678 }
679}
680
681/// An intermediate set representing an overestimate of what packages are built, but an accurate
682/// summary of what features are built given a particular package.
683///
684/// Not part of the stable API, exposed for cargo-compare.
685#[doc(hidden)]
686#[derive(Debug)]
687pub enum CargoIntermediateSet<'g> {
688 Unified(FeatureSet<'g>),
689 TargetHost {
690 target: FeatureSet<'g>,
691 host: FeatureSet<'g>,
692 },
693}
694
695impl<'g> CargoIntermediateSet<'g> {
696 #[doc(hidden)]
697 pub fn target_host_sets(&self) -> (&FeatureSet<'g>, &FeatureSet<'g>) {
698 match self {
699 CargoIntermediateSet::Unified(set) => (set, set),
700 CargoIntermediateSet::TargetHost { target, host } => (target, host),
701 }
702 }
703}