cargo/core/
workspace.rs

1use std::cell::RefCell;
2use std::collections::hash_map::{Entry, HashMap};
3use std::collections::{BTreeMap, BTreeSet, HashSet};
4use std::path::{Path, PathBuf};
5use std::rc::Rc;
6
7use anyhow::{anyhow, bail, Context as _};
8use glob::glob;
9use itertools::Itertools;
10use tracing::debug;
11use url::Url;
12
13use crate::core::compiler::Unit;
14use crate::core::features::Features;
15use crate::core::registry::PackageRegistry;
16use crate::core::resolver::features::CliFeatures;
17use crate::core::resolver::ResolveBehavior;
18use crate::core::{
19    Dependency, Edition, FeatureValue, PackageId, PackageIdSpec, PackageIdSpecQuery,
20};
21use crate::core::{EitherManifest, Package, SourceId, VirtualManifest};
22use crate::ops;
23use crate::sources::{PathSource, SourceConfigMap, CRATES_IO_INDEX, CRATES_IO_REGISTRY};
24use crate::util::context::FeatureUnification;
25use crate::util::edit_distance;
26use crate::util::errors::{CargoResult, ManifestError};
27use crate::util::interning::InternedString;
28use crate::util::lints::{analyze_cargo_lints_table, check_im_a_teapot};
29use crate::util::toml::{read_manifest, InheritableFields};
30use crate::util::{
31    context::CargoResolverConfig, context::ConfigRelativePath, context::IncompatibleRustVersions,
32    Filesystem, GlobalContext, IntoUrl,
33};
34use cargo_util::paths;
35use cargo_util::paths::normalize_path;
36use cargo_util_schemas::manifest;
37use cargo_util_schemas::manifest::RustVersion;
38use cargo_util_schemas::manifest::{TomlDependency, TomlProfiles};
39use pathdiff::diff_paths;
40
41/// The core abstraction in Cargo for working with a workspace of crates.
42///
43/// A workspace is often created very early on and then threaded through all
44/// other functions. It's typically through this object that the current
45/// package is loaded and/or learned about.
46#[derive(Debug)]
47pub struct Workspace<'gctx> {
48    /// Cargo configuration information. See [`GlobalContext`].
49    gctx: &'gctx GlobalContext,
50
51    /// This path is a path to where the current cargo subcommand was invoked
52    /// from. That is the `--manifest-path` argument to Cargo, and
53    /// points to the "main crate" that we're going to worry about.
54    current_manifest: PathBuf,
55
56    /// A list of packages found in this workspace. Always includes at least the
57    /// package mentioned by `current_manifest`.
58    packages: Packages<'gctx>,
59
60    /// If this workspace includes more than one crate, this points to the root
61    /// of the workspace. This is `None` in the case that `[workspace]` is
62    /// missing, `package.workspace` is missing, and no `Cargo.toml` above
63    /// `current_manifest` was found on the filesystem with `[workspace]`.
64    root_manifest: Option<PathBuf>,
65
66    /// Shared target directory for all the packages of this workspace.
67    /// `None` if the default path of `root/target` should be used.
68    target_dir: Option<Filesystem>,
69
70    /// List of members in this workspace with a listing of all their manifest
71    /// paths. The packages themselves can be looked up through the `packages`
72    /// set above.
73    members: Vec<PathBuf>,
74    /// Set of ids of workspace members
75    member_ids: HashSet<PackageId>,
76
77    /// The subset of `members` that are used by the
78    /// `build`, `check`, `test`, and `bench` subcommands
79    /// when no package is selected with `--package` / `-p` and `--workspace`
80    /// is not used.
81    ///
82    /// This is set by the `default-members` config
83    /// in the `[workspace]` section.
84    /// When unset, this is the same as `members` for virtual workspaces
85    /// (`--workspace` is implied)
86    /// or only the root package for non-virtual workspaces.
87    default_members: Vec<PathBuf>,
88
89    /// `true` if this is a temporary workspace created for the purposes of the
90    /// `cargo install` or `cargo package` commands.
91    is_ephemeral: bool,
92
93    /// `true` if this workspace should enforce optional dependencies even when
94    /// not needed; false if this workspace should only enforce dependencies
95    /// needed by the current configuration (such as in cargo install). In some
96    /// cases `false` also results in the non-enforcement of dev-dependencies.
97    require_optional_deps: bool,
98
99    /// A cache of loaded packages for particular paths which is disjoint from
100    /// `packages` up above, used in the `load` method down below.
101    loaded_packages: RefCell<HashMap<PathBuf, Package>>,
102
103    /// If `true`, then the resolver will ignore any existing `Cargo.lock`
104    /// file. This is set for `cargo install` without `--locked`.
105    ignore_lock: bool,
106
107    /// Requested path of the lockfile (i.e. passed as the cli flag)
108    requested_lockfile_path: Option<PathBuf>,
109
110    /// The resolver behavior specified with the `resolver` field.
111    resolve_behavior: ResolveBehavior,
112    /// If `true`, then workspace `rust_version` would be used in `cargo resolve`
113    /// and other places that use rust version.
114    /// This is set based on the resolver version, config settings, and CLI flags.
115    resolve_honors_rust_version: bool,
116    /// The feature unification mode used when building packages.
117    resolve_feature_unification: FeatureUnification,
118    /// Workspace-level custom metadata
119    custom_metadata: Option<toml::Value>,
120
121    /// Local overlay configuration. See [`crate::sources::overlay`].
122    local_overlays: HashMap<SourceId, PathBuf>,
123}
124
125// Separate structure for tracking loaded packages (to avoid loading anything
126// twice), and this is separate to help appease the borrow checker.
127#[derive(Debug)]
128struct Packages<'gctx> {
129    gctx: &'gctx GlobalContext,
130    packages: HashMap<PathBuf, MaybePackage>,
131}
132
133#[derive(Debug)]
134pub enum MaybePackage {
135    Package(Package),
136    Virtual(VirtualManifest),
137}
138
139/// Configuration of a workspace in a manifest.
140#[derive(Debug, Clone)]
141pub enum WorkspaceConfig {
142    /// Indicates that `[workspace]` was present and the members were
143    /// optionally specified as well.
144    Root(WorkspaceRootConfig),
145
146    /// Indicates that `[workspace]` was present and the `root` field is the
147    /// optional value of `package.workspace`, if present.
148    Member { root: Option<String> },
149}
150
151impl WorkspaceConfig {
152    pub fn inheritable(&self) -> Option<&InheritableFields> {
153        match self {
154            WorkspaceConfig::Root(root) => Some(&root.inheritable_fields),
155            WorkspaceConfig::Member { .. } => None,
156        }
157    }
158
159    /// Returns the path of the workspace root based on this `[workspace]` configuration.
160    ///
161    /// Returns `None` if the root is not explicitly known.
162    ///
163    /// * `self_path` is the path of the manifest this `WorkspaceConfig` is located.
164    /// * `look_from` is the path where discovery started (usually the current
165    ///   working directory), used for `workspace.exclude` checking.
166    fn get_ws_root(&self, self_path: &Path, look_from: &Path) -> Option<PathBuf> {
167        match self {
168            WorkspaceConfig::Root(ances_root_config) => {
169                debug!("find_root - found a root checking exclusion");
170                if !ances_root_config.is_excluded(look_from) {
171                    debug!("find_root - found!");
172                    Some(self_path.to_owned())
173                } else {
174                    None
175                }
176            }
177            WorkspaceConfig::Member {
178                root: Some(path_to_root),
179            } => {
180                debug!("find_root - found pointer");
181                Some(read_root_pointer(self_path, path_to_root))
182            }
183            WorkspaceConfig::Member { .. } => None,
184        }
185    }
186}
187
188/// Intermediate configuration of a workspace root in a manifest.
189///
190/// Knows the Workspace Root path, as well as `members` and `exclude` lists of path patterns, which
191/// together tell if some path is recognized as a member by this root or not.
192#[derive(Debug, Clone)]
193pub struct WorkspaceRootConfig {
194    root_dir: PathBuf,
195    members: Option<Vec<String>>,
196    default_members: Option<Vec<String>>,
197    exclude: Vec<String>,
198    inheritable_fields: InheritableFields,
199    custom_metadata: Option<toml::Value>,
200}
201
202impl<'gctx> Workspace<'gctx> {
203    /// Creates a new workspace given the target manifest pointed to by
204    /// `manifest_path`.
205    ///
206    /// This function will construct the entire workspace by determining the
207    /// root and all member packages. It will then validate the workspace
208    /// before returning it, so `Ok` is only returned for valid workspaces.
209    pub fn new(manifest_path: &Path, gctx: &'gctx GlobalContext) -> CargoResult<Workspace<'gctx>> {
210        let mut ws = Workspace::new_default(manifest_path.to_path_buf(), gctx);
211        ws.target_dir = gctx.target_dir()?;
212
213        if manifest_path.is_relative() {
214            bail!(
215                "manifest_path:{:?} is not an absolute path. Please provide an absolute path.",
216                manifest_path
217            )
218        } else {
219            ws.root_manifest = ws.find_root(manifest_path)?;
220        }
221
222        ws.custom_metadata = ws
223            .load_workspace_config()?
224            .and_then(|cfg| cfg.custom_metadata);
225        ws.find_members()?;
226        ws.set_resolve_behavior()?;
227        ws.validate()?;
228        Ok(ws)
229    }
230
231    fn new_default(current_manifest: PathBuf, gctx: &'gctx GlobalContext) -> Workspace<'gctx> {
232        Workspace {
233            gctx,
234            current_manifest,
235            packages: Packages {
236                gctx,
237                packages: HashMap::new(),
238            },
239            root_manifest: None,
240            target_dir: None,
241            members: Vec::new(),
242            member_ids: HashSet::new(),
243            default_members: Vec::new(),
244            is_ephemeral: false,
245            require_optional_deps: true,
246            loaded_packages: RefCell::new(HashMap::new()),
247            ignore_lock: false,
248            requested_lockfile_path: None,
249            resolve_behavior: ResolveBehavior::V1,
250            resolve_honors_rust_version: false,
251            resolve_feature_unification: FeatureUnification::Selected,
252            custom_metadata: None,
253            local_overlays: HashMap::new(),
254        }
255    }
256
257    /// Creates a "temporary workspace" from one package which only contains
258    /// that package.
259    ///
260    /// This constructor will not touch the filesystem and only creates an
261    /// in-memory workspace. That is, all configuration is ignored, it's just
262    /// intended for that one package.
263    ///
264    /// This is currently only used in niche situations like `cargo install` or
265    /// `cargo package`.
266    pub fn ephemeral(
267        package: Package,
268        gctx: &'gctx GlobalContext,
269        target_dir: Option<Filesystem>,
270        require_optional_deps: bool,
271    ) -> CargoResult<Workspace<'gctx>> {
272        let mut ws = Workspace::new_default(package.manifest_path().to_path_buf(), gctx);
273        ws.is_ephemeral = true;
274        ws.require_optional_deps = require_optional_deps;
275        let id = package.package_id();
276        let package = MaybePackage::Package(package);
277        ws.packages
278            .packages
279            .insert(ws.current_manifest.clone(), package);
280        ws.target_dir = if let Some(dir) = target_dir {
281            Some(dir)
282        } else {
283            ws.gctx.target_dir()?
284        };
285        ws.members.push(ws.current_manifest.clone());
286        ws.member_ids.insert(id);
287        ws.default_members.push(ws.current_manifest.clone());
288        ws.set_resolve_behavior()?;
289        Ok(ws)
290    }
291
292    fn set_resolve_behavior(&mut self) -> CargoResult<()> {
293        // - If resolver is specified in the workspace definition, use that.
294        // - If the root package specifies the resolver, use that.
295        // - If the root package specifies edition 2021, use v2.
296        // - Otherwise, use the default v1.
297        self.resolve_behavior = match self.root_maybe() {
298            MaybePackage::Package(p) => p
299                .manifest()
300                .resolve_behavior()
301                .unwrap_or_else(|| p.manifest().edition().default_resolve_behavior()),
302            MaybePackage::Virtual(vm) => vm.resolve_behavior().unwrap_or(ResolveBehavior::V1),
303        };
304
305        match self.resolve_behavior() {
306            ResolveBehavior::V1 | ResolveBehavior::V2 => {}
307            ResolveBehavior::V3 => {
308                if self.resolve_behavior == ResolveBehavior::V3 {
309                    self.resolve_honors_rust_version = true;
310                }
311            }
312        }
313        let config = self.gctx().get::<CargoResolverConfig>("resolver")?;
314        if let Some(incompatible_rust_versions) = config.incompatible_rust_versions {
315            self.resolve_honors_rust_version =
316                incompatible_rust_versions == IncompatibleRustVersions::Fallback;
317        }
318        if self.gctx().cli_unstable().feature_unification {
319            self.resolve_feature_unification = config
320                .feature_unification
321                .unwrap_or(FeatureUnification::Selected);
322        } else if config.feature_unification.is_some() {
323            self.gctx()
324                .shell()
325                .warn("ignoring `resolver.feature-unification` without `-Zfeature-unification`")?;
326        };
327
328        Ok(())
329    }
330
331    /// Returns the current package of this workspace.
332    ///
333    /// Note that this can return an error if it the current manifest is
334    /// actually a "virtual Cargo.toml", in which case an error is returned
335    /// indicating that something else should be passed.
336    pub fn current(&self) -> CargoResult<&Package> {
337        let pkg = self.current_opt().ok_or_else(|| {
338            anyhow::format_err!(
339                "manifest path `{}` is a virtual manifest, but this \
340                 command requires running against an actual package in \
341                 this workspace",
342                self.current_manifest.display()
343            )
344        })?;
345        Ok(pkg)
346    }
347
348    pub fn current_mut(&mut self) -> CargoResult<&mut Package> {
349        let cm = self.current_manifest.clone();
350        let pkg = self.current_opt_mut().ok_or_else(|| {
351            anyhow::format_err!(
352                "manifest path `{}` is a virtual manifest, but this \
353                 command requires running against an actual package in \
354                 this workspace",
355                cm.display()
356            )
357        })?;
358        Ok(pkg)
359    }
360
361    pub fn current_opt(&self) -> Option<&Package> {
362        match *self.packages.get(&self.current_manifest) {
363            MaybePackage::Package(ref p) => Some(p),
364            MaybePackage::Virtual(..) => None,
365        }
366    }
367
368    pub fn current_opt_mut(&mut self) -> Option<&mut Package> {
369        match *self.packages.get_mut(&self.current_manifest) {
370            MaybePackage::Package(ref mut p) => Some(p),
371            MaybePackage::Virtual(..) => None,
372        }
373    }
374
375    pub fn is_virtual(&self) -> bool {
376        match *self.packages.get(&self.current_manifest) {
377            MaybePackage::Package(..) => false,
378            MaybePackage::Virtual(..) => true,
379        }
380    }
381
382    /// Returns the `GlobalContext` this workspace is associated with.
383    pub fn gctx(&self) -> &'gctx GlobalContext {
384        self.gctx
385    }
386
387    pub fn profiles(&self) -> Option<&TomlProfiles> {
388        match self.root_maybe() {
389            MaybePackage::Package(p) => p.manifest().profiles(),
390            MaybePackage::Virtual(vm) => vm.profiles(),
391        }
392    }
393
394    /// Returns the root path of this workspace.
395    ///
396    /// That is, this returns the path of the directory containing the
397    /// `Cargo.toml` which is the root of this workspace.
398    pub fn root(&self) -> &Path {
399        self.root_manifest().parent().unwrap()
400    }
401
402    /// Returns the path of the `Cargo.toml` which is the root of this
403    /// workspace.
404    pub fn root_manifest(&self) -> &Path {
405        self.root_manifest
406            .as_ref()
407            .unwrap_or(&self.current_manifest)
408    }
409
410    /// Returns the root Package or `VirtualManifest`.
411    pub fn root_maybe(&self) -> &MaybePackage {
412        self.packages.get(self.root_manifest())
413    }
414
415    pub fn target_dir(&self) -> Filesystem {
416        self.target_dir
417            .clone()
418            .unwrap_or_else(|| self.default_target_dir())
419    }
420
421    fn default_target_dir(&self) -> Filesystem {
422        if self.root_maybe().is_embedded() {
423            let hash = crate::util::hex::short_hash(&self.root_manifest().to_string_lossy());
424            let mut rel_path = PathBuf::new();
425            rel_path.push("target");
426            rel_path.push(&hash[0..2]);
427            rel_path.push(&hash[2..]);
428
429            self.gctx().home().join(rel_path)
430        } else {
431            Filesystem::new(self.root().join("target"))
432        }
433    }
434
435    /// Returns the root `[replace]` section of this workspace.
436    ///
437    /// This may be from a virtual crate or an actual crate.
438    pub fn root_replace(&self) -> &[(PackageIdSpec, Dependency)] {
439        match self.root_maybe() {
440            MaybePackage::Package(p) => p.manifest().replace(),
441            MaybePackage::Virtual(vm) => vm.replace(),
442        }
443    }
444
445    fn config_patch(&self) -> CargoResult<HashMap<Url, Vec<Dependency>>> {
446        let config_patch: Option<
447            BTreeMap<String, BTreeMap<String, TomlDependency<ConfigRelativePath>>>,
448        > = self.gctx.get("patch")?;
449
450        let source = SourceId::for_manifest_path(self.root_manifest())?;
451
452        let mut warnings = Vec::new();
453
454        let mut patch = HashMap::new();
455        for (url, deps) in config_patch.into_iter().flatten() {
456            let url = match &url[..] {
457                CRATES_IO_REGISTRY => CRATES_IO_INDEX.parse().unwrap(),
458                url => self
459                    .gctx
460                    .get_registry_index(url)
461                    .or_else(|_| url.into_url())
462                    .with_context(|| {
463                        format!("[patch] entry `{}` should be a URL or registry name", url)
464                    })?,
465            };
466            patch.insert(
467                url,
468                deps.iter()
469                    .map(|(name, dep)| {
470                        crate::util::toml::to_dependency(
471                            dep,
472                            name,
473                            source,
474                            self.gctx,
475                            &mut warnings,
476                            /* platform */ None,
477                            // NOTE: Since we use ConfigRelativePath, this root isn't used as
478                            // any relative paths are resolved before they'd be joined with root.
479                            Path::new("unused-relative-path"),
480                            /* kind */ None,
481                        )
482                    })
483                    .collect::<CargoResult<Vec<_>>>()?,
484            );
485        }
486
487        for message in warnings {
488            self.gctx
489                .shell()
490                .warn(format!("[patch] in cargo config: {}", message))?
491        }
492
493        Ok(patch)
494    }
495
496    /// Returns the root `[patch]` section of this workspace.
497    ///
498    /// This may be from a virtual crate or an actual crate.
499    pub fn root_patch(&self) -> CargoResult<HashMap<Url, Vec<Dependency>>> {
500        let from_manifest = match self.root_maybe() {
501            MaybePackage::Package(p) => p.manifest().patch(),
502            MaybePackage::Virtual(vm) => vm.patch(),
503        };
504
505        let from_config = self.config_patch()?;
506        if from_config.is_empty() {
507            return Ok(from_manifest.clone());
508        }
509        if from_manifest.is_empty() {
510            return Ok(from_config);
511        }
512
513        // We could just chain from_manifest and from_config,
514        // but that's not quite right as it won't deal with overlaps.
515        let mut combined = from_config;
516        for (url, deps_from_manifest) in from_manifest {
517            if let Some(deps_from_config) = combined.get_mut(url) {
518                // We want from_config to take precedence for each patched name.
519                // NOTE: This is inefficient if the number of patches is large!
520                let mut from_manifest_pruned = deps_from_manifest.clone();
521                for dep_from_config in &mut *deps_from_config {
522                    if let Some(i) = from_manifest_pruned.iter().position(|dep_from_manifest| {
523                        // XXX: should this also take into account version numbers?
524                        dep_from_config.name_in_toml() == dep_from_manifest.name_in_toml()
525                    }) {
526                        from_manifest_pruned.swap_remove(i);
527                    }
528                }
529                // Whatever is left does not exist in manifest dependencies.
530                deps_from_config.extend(from_manifest_pruned);
531            } else {
532                combined.insert(url.clone(), deps_from_manifest.clone());
533            }
534        }
535        Ok(combined)
536    }
537
538    /// Returns an iterator over all packages in this workspace
539    pub fn members(&self) -> impl Iterator<Item = &Package> {
540        let packages = &self.packages;
541        self.members
542            .iter()
543            .filter_map(move |path| match packages.get(path) {
544                MaybePackage::Package(p) => Some(p),
545                _ => None,
546            })
547    }
548
549    /// Returns a mutable iterator over all packages in this workspace
550    pub fn members_mut(&mut self) -> impl Iterator<Item = &mut Package> {
551        let packages = &mut self.packages.packages;
552        let members: HashSet<_> = self.members.iter().map(|path| path).collect();
553
554        packages.iter_mut().filter_map(move |(path, package)| {
555            if members.contains(path) {
556                if let MaybePackage::Package(ref mut p) = package {
557                    return Some(p);
558                }
559            }
560
561            None
562        })
563    }
564
565    /// Returns an iterator over default packages in this workspace
566    pub fn default_members<'a>(&'a self) -> impl Iterator<Item = &'a Package> {
567        let packages = &self.packages;
568        self.default_members
569            .iter()
570            .filter_map(move |path| match packages.get(path) {
571                MaybePackage::Package(p) => Some(p),
572                _ => None,
573            })
574    }
575
576    /// Returns an iterator over default packages in this workspace
577    pub fn default_members_mut(&mut self) -> impl Iterator<Item = &mut Package> {
578        let packages = &mut self.packages.packages;
579        let members: HashSet<_> = self
580            .default_members
581            .iter()
582            .map(|path| path.parent().unwrap().to_owned())
583            .collect();
584
585        packages.iter_mut().filter_map(move |(path, package)| {
586            if members.contains(path) {
587                if let MaybePackage::Package(ref mut p) = package {
588                    return Some(p);
589                }
590            }
591
592            None
593        })
594    }
595
596    /// Returns true if the package is a member of the workspace.
597    pub fn is_member(&self, pkg: &Package) -> bool {
598        self.member_ids.contains(&pkg.package_id())
599    }
600
601    /// Returns true if the given package_id is a member of the workspace.
602    pub fn is_member_id(&self, package_id: PackageId) -> bool {
603        self.member_ids.contains(&package_id)
604    }
605
606    pub fn is_ephemeral(&self) -> bool {
607        self.is_ephemeral
608    }
609
610    pub fn require_optional_deps(&self) -> bool {
611        self.require_optional_deps
612    }
613
614    pub fn set_require_optional_deps(
615        &mut self,
616        require_optional_deps: bool,
617    ) -> &mut Workspace<'gctx> {
618        self.require_optional_deps = require_optional_deps;
619        self
620    }
621
622    pub fn ignore_lock(&self) -> bool {
623        self.ignore_lock
624    }
625
626    pub fn set_ignore_lock(&mut self, ignore_lock: bool) -> &mut Workspace<'gctx> {
627        self.ignore_lock = ignore_lock;
628        self
629    }
630
631    /// Returns the directory where the lockfile is in.
632    pub fn lock_root(&self) -> Filesystem {
633        if let Some(requested) = self.requested_lockfile_path.as_ref() {
634            return Filesystem::new(
635                requested
636                    .parent()
637                    .expect("Lockfile path can't be root")
638                    .to_owned(),
639            );
640        }
641        self.default_lock_root()
642    }
643
644    fn default_lock_root(&self) -> Filesystem {
645        if self.root_maybe().is_embedded() {
646            self.target_dir()
647        } else {
648            Filesystem::new(self.root().to_owned())
649        }
650    }
651
652    pub fn set_requested_lockfile_path(&mut self, path: Option<PathBuf>) {
653        self.requested_lockfile_path = path;
654    }
655
656    pub fn requested_lockfile_path(&self) -> Option<&Path> {
657        self.requested_lockfile_path.as_deref()
658    }
659
660    /// Get the lowest-common denominator `package.rust-version` within the workspace, if specified
661    /// anywhere
662    pub fn lowest_rust_version(&self) -> Option<&RustVersion> {
663        self.members().filter_map(|pkg| pkg.rust_version()).min()
664    }
665
666    pub fn set_resolve_honors_rust_version(&mut self, honor_rust_version: Option<bool>) {
667        if let Some(honor_rust_version) = honor_rust_version {
668            self.resolve_honors_rust_version = honor_rust_version;
669        }
670    }
671
672    pub fn resolve_honors_rust_version(&self) -> bool {
673        self.resolve_honors_rust_version
674    }
675
676    pub fn set_resolve_feature_unification(&mut self, feature_unification: FeatureUnification) {
677        self.resolve_feature_unification = feature_unification;
678    }
679
680    pub fn resolve_feature_unification(&self) -> FeatureUnification {
681        self.resolve_feature_unification
682    }
683
684    pub fn custom_metadata(&self) -> Option<&toml::Value> {
685        self.custom_metadata.as_ref()
686    }
687
688    pub fn load_workspace_config(&mut self) -> CargoResult<Option<WorkspaceRootConfig>> {
689        // If we didn't find a root, it must mean there is no [workspace] section, and thus no
690        // metadata.
691        if let Some(root_path) = &self.root_manifest {
692            let root_package = self.packages.load(root_path)?;
693            match root_package.workspace_config() {
694                WorkspaceConfig::Root(ref root_config) => {
695                    return Ok(Some(root_config.clone()));
696                }
697
698                _ => bail!(
699                    "root of a workspace inferred but wasn't a root: {}",
700                    root_path.display()
701                ),
702            }
703        }
704
705        Ok(None)
706    }
707
708    /// Finds the root of a workspace for the crate whose manifest is located
709    /// at `manifest_path`.
710    ///
711    /// This will parse the `Cargo.toml` at `manifest_path` and then interpret
712    /// the workspace configuration, optionally walking up the filesystem
713    /// looking for other workspace roots.
714    ///
715    /// Returns an error if `manifest_path` isn't actually a valid manifest or
716    /// if some other transient error happens.
717    fn find_root(&mut self, manifest_path: &Path) -> CargoResult<Option<PathBuf>> {
718        let current = self.packages.load(manifest_path)?;
719        match current
720            .workspace_config()
721            .get_ws_root(manifest_path, manifest_path)
722        {
723            Some(root_path) => {
724                debug!("find_root - is root {}", manifest_path.display());
725                Ok(Some(root_path))
726            }
727            None => find_workspace_root_with_loader(manifest_path, self.gctx, |self_path| {
728                Ok(self
729                    .packages
730                    .load(self_path)?
731                    .workspace_config()
732                    .get_ws_root(self_path, manifest_path))
733            }),
734        }
735    }
736
737    /// After the root of a workspace has been located, probes for all members
738    /// of a workspace.
739    ///
740    /// If the `workspace.members` configuration is present, then this just
741    /// verifies that those are all valid packages to point to. Otherwise, this
742    /// will transitively follow all `path` dependencies looking for members of
743    /// the workspace.
744    #[tracing::instrument(skip_all)]
745    fn find_members(&mut self) -> CargoResult<()> {
746        let Some(workspace_config) = self.load_workspace_config()? else {
747            debug!("find_members - only me as a member");
748            self.members.push(self.current_manifest.clone());
749            self.default_members.push(self.current_manifest.clone());
750            if let Ok(pkg) = self.current() {
751                let id = pkg.package_id();
752                self.member_ids.insert(id);
753            }
754            return Ok(());
755        };
756
757        // self.root_manifest must be Some to have retrieved workspace_config
758        let root_manifest_path = self.root_manifest.clone().unwrap();
759
760        let members_paths = workspace_config
761            .members_paths(workspace_config.members.as_deref().unwrap_or_default())?;
762        let default_members_paths = if root_manifest_path == self.current_manifest {
763            if let Some(ref default) = workspace_config.default_members {
764                Some(workspace_config.members_paths(default)?)
765            } else {
766                None
767            }
768        } else {
769            None
770        };
771
772        for (path, glob) in &members_paths {
773            self.find_path_deps(&path.join("Cargo.toml"), &root_manifest_path, false)
774                .with_context(|| {
775                    format!(
776                        "failed to load manifest for workspace member `{}`\n\
777                        referenced{} by workspace at `{}`",
778                        path.display(),
779                        glob.map(|g| format!(" via `{g}`")).unwrap_or_default(),
780                        root_manifest_path.display(),
781                    )
782                })?;
783        }
784
785        self.find_path_deps(&root_manifest_path, &root_manifest_path, false)?;
786
787        if let Some(default) = default_members_paths {
788            for (path, default_member_glob) in default {
789                let normalized_path = paths::normalize_path(&path);
790                let manifest_path = normalized_path.join("Cargo.toml");
791                if !self.members.contains(&manifest_path) {
792                    // default-members are allowed to be excluded, but they
793                    // still must be referred to by the original (unfiltered)
794                    // members list. Note that we aren't testing against the
795                    // manifest path, both because `members_paths` doesn't
796                    // include `/Cargo.toml`, and because excluded paths may not
797                    // be crates.
798                    let exclude = members_paths.iter().any(|(m, _)| *m == normalized_path)
799                        && workspace_config.is_excluded(&normalized_path);
800                    if exclude {
801                        continue;
802                    }
803                    bail!(
804                        "package `{}` is listed in default-members{} but is not a member\n\
805                        for workspace at `{}`.",
806                        path.display(),
807                        default_member_glob
808                            .map(|g| format!(" via `{g}`"))
809                            .unwrap_or_default(),
810                        root_manifest_path.display(),
811                    )
812                }
813                self.default_members.push(manifest_path)
814            }
815        } else if self.is_virtual() {
816            self.default_members = self.members.clone()
817        } else {
818            self.default_members.push(self.current_manifest.clone())
819        }
820
821        Ok(())
822    }
823
824    fn find_path_deps(
825        &mut self,
826        manifest_path: &Path,
827        root_manifest: &Path,
828        is_path_dep: bool,
829    ) -> CargoResult<()> {
830        let manifest_path = paths::normalize_path(manifest_path);
831        if self.members.contains(&manifest_path) {
832            return Ok(());
833        }
834        if is_path_dep && self.root_maybe().is_embedded() {
835            // Embedded manifests cannot have workspace members
836            return Ok(());
837        }
838        if is_path_dep
839            && !manifest_path.parent().unwrap().starts_with(self.root())
840            && self.find_root(&manifest_path)? != self.root_manifest
841        {
842            // If `manifest_path` is a path dependency outside of the workspace,
843            // don't add it, or any of its dependencies, as a members.
844            return Ok(());
845        }
846
847        if let WorkspaceConfig::Root(ref root_config) =
848            *self.packages.load(root_manifest)?.workspace_config()
849        {
850            if root_config.is_excluded(&manifest_path) {
851                return Ok(());
852            }
853        }
854
855        debug!("find_path_deps - {}", manifest_path.display());
856        self.members.push(manifest_path.clone());
857
858        let candidates = {
859            let pkg = match *self.packages.load(&manifest_path)? {
860                MaybePackage::Package(ref p) => p,
861                MaybePackage::Virtual(_) => return Ok(()),
862            };
863            self.member_ids.insert(pkg.package_id());
864            pkg.dependencies()
865                .iter()
866                .map(|d| (d.source_id(), d.package_name()))
867                .filter(|(s, _)| s.is_path())
868                .filter_map(|(s, n)| s.url().to_file_path().ok().map(|p| (p, n)))
869                .map(|(p, n)| (p.join("Cargo.toml"), n))
870                .collect::<Vec<_>>()
871        };
872        for (path, name) in candidates {
873            self.find_path_deps(&path, root_manifest, true)
874                .with_context(|| format!("failed to load manifest for dependency `{}`", name))
875                .map_err(|err| ManifestError::new(err, manifest_path.clone()))?;
876        }
877        Ok(())
878    }
879
880    /// Returns the unstable nightly-only features enabled via `cargo-features` in the manifest.
881    pub fn unstable_features(&self) -> &Features {
882        match self.root_maybe() {
883            MaybePackage::Package(p) => p.manifest().unstable_features(),
884            MaybePackage::Virtual(vm) => vm.unstable_features(),
885        }
886    }
887
888    pub fn resolve_behavior(&self) -> ResolveBehavior {
889        self.resolve_behavior
890    }
891
892    /// Returns `true` if this workspace uses the new CLI features behavior.
893    ///
894    /// The old behavior only allowed choosing the features from the package
895    /// in the current directory, regardless of which packages were chosen
896    /// with the -p flags. The new behavior allows selecting features from the
897    /// packages chosen on the command line (with -p or --workspace flags),
898    /// ignoring whatever is in the current directory.
899    pub fn allows_new_cli_feature_behavior(&self) -> bool {
900        self.is_virtual()
901            || match self.resolve_behavior() {
902                ResolveBehavior::V1 => false,
903                ResolveBehavior::V2 | ResolveBehavior::V3 => true,
904            }
905    }
906
907    /// Validates a workspace, ensuring that a number of invariants are upheld:
908    ///
909    /// 1. A workspace only has one root.
910    /// 2. All workspace members agree on this one root as the root.
911    /// 3. The current crate is a member of this workspace.
912    #[tracing::instrument(skip_all)]
913    fn validate(&mut self) -> CargoResult<()> {
914        // The rest of the checks require a VirtualManifest or multiple members.
915        if self.root_manifest.is_none() {
916            return Ok(());
917        }
918
919        self.validate_unique_names()?;
920        self.validate_workspace_roots()?;
921        self.validate_members()?;
922        self.error_if_manifest_not_in_members()?;
923        self.validate_manifest()
924    }
925
926    fn validate_unique_names(&self) -> CargoResult<()> {
927        let mut names = BTreeMap::new();
928        for member in self.members.iter() {
929            let package = self.packages.get(member);
930            let name = match *package {
931                MaybePackage::Package(ref p) => p.name(),
932                MaybePackage::Virtual(_) => continue,
933            };
934            if let Some(prev) = names.insert(name, member) {
935                bail!(
936                    "two packages named `{}` in this workspace:\n\
937                         - {}\n\
938                         - {}",
939                    name,
940                    prev.display(),
941                    member.display()
942                );
943            }
944        }
945        Ok(())
946    }
947
948    fn validate_workspace_roots(&self) -> CargoResult<()> {
949        let roots: Vec<PathBuf> = self
950            .members
951            .iter()
952            .filter(|&member| {
953                let config = self.packages.get(member).workspace_config();
954                matches!(config, WorkspaceConfig::Root(_))
955            })
956            .map(|member| member.parent().unwrap().to_path_buf())
957            .collect();
958        match roots.len() {
959            1 => Ok(()),
960            0 => bail!(
961                "`package.workspace` configuration points to a crate \
962                 which is not configured with [workspace]: \n\
963                 configuration at: {}\n\
964                 points to: {}",
965                self.current_manifest.display(),
966                self.root_manifest.as_ref().unwrap().display()
967            ),
968            _ => {
969                bail!(
970                    "multiple workspace roots found in the same workspace:\n{}",
971                    roots
972                        .iter()
973                        .map(|r| format!("  {}", r.display()))
974                        .collect::<Vec<_>>()
975                        .join("\n")
976                );
977            }
978        }
979    }
980
981    #[tracing::instrument(skip_all)]
982    fn validate_members(&mut self) -> CargoResult<()> {
983        for member in self.members.clone() {
984            let root = self.find_root(&member)?;
985            if root == self.root_manifest {
986                continue;
987            }
988
989            match root {
990                Some(root) => {
991                    bail!(
992                        "package `{}` is a member of the wrong workspace\n\
993                         expected: {}\n\
994                         actual:   {}",
995                        member.display(),
996                        self.root_manifest.as_ref().unwrap().display(),
997                        root.display()
998                    );
999                }
1000                None => {
1001                    bail!(
1002                        "workspace member `{}` is not hierarchically below \
1003                         the workspace root `{}`",
1004                        member.display(),
1005                        self.root_manifest.as_ref().unwrap().display()
1006                    );
1007                }
1008            }
1009        }
1010        Ok(())
1011    }
1012
1013    fn error_if_manifest_not_in_members(&mut self) -> CargoResult<()> {
1014        if self.members.contains(&self.current_manifest) {
1015            return Ok(());
1016        }
1017
1018        let root = self.root_manifest.as_ref().unwrap();
1019        let root_dir = root.parent().unwrap();
1020        let current_dir = self.current_manifest.parent().unwrap();
1021        let root_pkg = self.packages.get(root);
1022
1023        // FIXME: Make this more generic by using a relative path resolver between member and root.
1024        let members_msg = match current_dir.strip_prefix(root_dir) {
1025            Ok(rel) => format!(
1026                "this may be fixable by adding `{}` to the \
1027                     `workspace.members` array of the manifest \
1028                     located at: {}",
1029                rel.display(),
1030                root.display()
1031            ),
1032            Err(_) => format!(
1033                "this may be fixable by adding a member to \
1034                     the `workspace.members` array of the \
1035                     manifest located at: {}",
1036                root.display()
1037            ),
1038        };
1039        let extra = match *root_pkg {
1040            MaybePackage::Virtual(_) => members_msg,
1041            MaybePackage::Package(ref p) => {
1042                let has_members_list = match *p.manifest().workspace_config() {
1043                    WorkspaceConfig::Root(ref root_config) => root_config.has_members_list(),
1044                    WorkspaceConfig::Member { .. } => unreachable!(),
1045                };
1046                if !has_members_list {
1047                    format!(
1048                        "this may be fixable by ensuring that this \
1049                             crate is depended on by the workspace \
1050                             root: {}",
1051                        root.display()
1052                    )
1053                } else {
1054                    members_msg
1055                }
1056            }
1057        };
1058        bail!(
1059            "current package believes it's in a workspace when it's not:\n\
1060                 current:   {}\n\
1061                 workspace: {}\n\n{}\n\
1062                 Alternatively, to keep it out of the workspace, add the package \
1063                 to the `workspace.exclude` array, or add an empty `[workspace]` \
1064                 table to the package's manifest.",
1065            self.current_manifest.display(),
1066            root.display(),
1067            extra
1068        );
1069    }
1070
1071    fn validate_manifest(&mut self) -> CargoResult<()> {
1072        if let Some(ref root_manifest) = self.root_manifest {
1073            for pkg in self
1074                .members()
1075                .filter(|p| p.manifest_path() != root_manifest)
1076            {
1077                let manifest = pkg.manifest();
1078                let emit_warning = |what| -> CargoResult<()> {
1079                    let msg = format!(
1080                        "{} for the non root package will be ignored, \
1081                         specify {} at the workspace root:\n\
1082                         package:   {}\n\
1083                         workspace: {}",
1084                        what,
1085                        what,
1086                        pkg.manifest_path().display(),
1087                        root_manifest.display(),
1088                    );
1089                    self.gctx.shell().warn(&msg)
1090                };
1091                if manifest.normalized_toml().has_profiles() {
1092                    emit_warning("profiles")?;
1093                }
1094                if !manifest.replace().is_empty() {
1095                    emit_warning("replace")?;
1096                }
1097                if !manifest.patch().is_empty() {
1098                    emit_warning("patch")?;
1099                }
1100                if let Some(behavior) = manifest.resolve_behavior() {
1101                    if behavior != self.resolve_behavior {
1102                        // Only warn if they don't match.
1103                        emit_warning("resolver")?;
1104                    }
1105                }
1106            }
1107            if let MaybePackage::Virtual(vm) = self.root_maybe() {
1108                if vm.resolve_behavior().is_none() {
1109                    if let Some(edition) = self
1110                        .members()
1111                        .filter(|p| p.manifest_path() != root_manifest)
1112                        .map(|p| p.manifest().edition())
1113                        .filter(|&e| e >= Edition::Edition2021)
1114                        .max()
1115                    {
1116                        let resolver = edition.default_resolve_behavior().to_manifest();
1117                        self.gctx.shell().warn(format_args!(
1118                            "virtual workspace defaulting to `resolver = \"1\"` despite one or more workspace members being on edition {edition} which implies `resolver = \"{resolver}\"`"
1119                        ))?;
1120                        self.gctx.shell().note(
1121                            "to keep the current resolver, specify `workspace.resolver = \"1\"` in the workspace root's manifest",
1122                        )?;
1123                        self.gctx.shell().note(format_args!(
1124                            "to use the edition {edition} resolver, specify `workspace.resolver = \"{resolver}\"` in the workspace root's manifest"
1125                        ))?;
1126                        self.gctx.shell().note(
1127                            "for more details see https://doc.rust-lang.org/cargo/reference/resolver.html#resolver-versions",
1128                        )?;
1129                    }
1130                }
1131            }
1132        }
1133        Ok(())
1134    }
1135
1136    pub fn load(&self, manifest_path: &Path) -> CargoResult<Package> {
1137        match self.packages.maybe_get(manifest_path) {
1138            Some(MaybePackage::Package(p)) => return Ok(p.clone()),
1139            Some(&MaybePackage::Virtual(_)) => bail!("cannot load workspace root"),
1140            None => {}
1141        }
1142
1143        let mut loaded = self.loaded_packages.borrow_mut();
1144        if let Some(p) = loaded.get(manifest_path).cloned() {
1145            return Ok(p);
1146        }
1147        let source_id = SourceId::for_manifest_path(manifest_path)?;
1148        let package = ops::read_package(manifest_path, source_id, self.gctx)?;
1149        loaded.insert(manifest_path.to_path_buf(), package.clone());
1150        Ok(package)
1151    }
1152
1153    /// Preload the provided registry with already loaded packages.
1154    ///
1155    /// A workspace may load packages during construction/parsing/early phases
1156    /// for various operations, and this preload step avoids doubly-loading and
1157    /// parsing crates on the filesystem by inserting them all into the registry
1158    /// with their in-memory formats.
1159    pub fn preload(&self, registry: &mut PackageRegistry<'gctx>) {
1160        // These can get weird as this generally represents a workspace during
1161        // `cargo install`. Things like git repositories will actually have a
1162        // `PathSource` with multiple entries in it, so the logic below is
1163        // mostly just an optimization for normal `cargo build` in workspaces
1164        // during development.
1165        if self.is_ephemeral {
1166            return;
1167        }
1168
1169        for pkg in self.packages.packages.values() {
1170            let pkg = match *pkg {
1171                MaybePackage::Package(ref p) => p.clone(),
1172                MaybePackage::Virtual(_) => continue,
1173            };
1174            let src = PathSource::preload_with(pkg, self.gctx);
1175            registry.add_preloaded(Box::new(src));
1176        }
1177    }
1178
1179    pub fn emit_warnings(&self) -> CargoResult<()> {
1180        for (path, maybe_pkg) in &self.packages.packages {
1181            if let MaybePackage::Package(pkg) = maybe_pkg {
1182                if self.gctx.cli_unstable().cargo_lints {
1183                    self.emit_lints(pkg, &path)?
1184                }
1185            }
1186            let warnings = match maybe_pkg {
1187                MaybePackage::Package(pkg) => pkg.manifest().warnings().warnings(),
1188                MaybePackage::Virtual(vm) => vm.warnings().warnings(),
1189            };
1190            for warning in warnings {
1191                if warning.is_critical {
1192                    let err = anyhow::format_err!("{}", warning.message);
1193                    let cx =
1194                        anyhow::format_err!("failed to parse manifest at `{}`", path.display());
1195                    return Err(err.context(cx));
1196                } else {
1197                    let msg = if self.root_manifest.is_none() {
1198                        warning.message.to_string()
1199                    } else {
1200                        // In a workspace, it can be confusing where a warning
1201                        // originated, so include the path.
1202                        format!("{}: {}", path.display(), warning.message)
1203                    };
1204                    self.gctx.shell().warn(msg)?
1205                }
1206            }
1207        }
1208        Ok(())
1209    }
1210
1211    pub fn emit_lints(&self, pkg: &Package, path: &Path) -> CargoResult<()> {
1212        let mut error_count = 0;
1213        let toml_lints = pkg
1214            .manifest()
1215            .normalized_toml()
1216            .lints
1217            .clone()
1218            .map(|lints| lints.lints)
1219            .unwrap_or(manifest::TomlLints::default());
1220        let cargo_lints = toml_lints
1221            .get("cargo")
1222            .cloned()
1223            .unwrap_or(manifest::TomlToolLints::default());
1224
1225        let ws_contents = match self.root_maybe() {
1226            MaybePackage::Package(pkg) => pkg.manifest().contents(),
1227            MaybePackage::Virtual(v) => v.contents(),
1228        };
1229
1230        let ws_document = match self.root_maybe() {
1231            MaybePackage::Package(pkg) => pkg.manifest().document(),
1232            MaybePackage::Virtual(v) => v.document(),
1233        };
1234
1235        analyze_cargo_lints_table(
1236            pkg,
1237            &path,
1238            &cargo_lints,
1239            ws_contents,
1240            ws_document,
1241            self.root_manifest(),
1242            self.gctx,
1243        )?;
1244        check_im_a_teapot(pkg, &path, &cargo_lints, &mut error_count, self.gctx)?;
1245        if error_count > 0 {
1246            Err(crate::util::errors::AlreadyPrintedError::new(anyhow!(
1247                "encountered {error_count} errors(s) while running lints"
1248            ))
1249            .into())
1250        } else {
1251            Ok(())
1252        }
1253    }
1254
1255    pub fn set_target_dir(&mut self, target_dir: Filesystem) {
1256        self.target_dir = Some(target_dir);
1257    }
1258
1259    /// Returns a Vec of `(&Package, RequestedFeatures)` tuples that
1260    /// represent the workspace members that were requested on the command-line.
1261    ///
1262    /// `specs` may be empty, which indicates it should return all workspace
1263    /// members. In this case, `requested_features.all_features` must be
1264    /// `true`. This is used for generating `Cargo.lock`, which must include
1265    /// all members with all features enabled.
1266    pub fn members_with_features(
1267        &self,
1268        specs: &[PackageIdSpec],
1269        cli_features: &CliFeatures,
1270    ) -> CargoResult<Vec<(&Package, CliFeatures)>> {
1271        assert!(
1272            !specs.is_empty() || cli_features.all_features,
1273            "no specs requires all_features"
1274        );
1275        if specs.is_empty() {
1276            // When resolving the entire workspace, resolve each member with
1277            // all features enabled.
1278            return Ok(self
1279                .members()
1280                .map(|m| (m, CliFeatures::new_all(true)))
1281                .collect());
1282        }
1283        if self.allows_new_cli_feature_behavior() {
1284            self.members_with_features_new(specs, cli_features)
1285        } else {
1286            Ok(self.members_with_features_old(specs, cli_features))
1287        }
1288    }
1289
1290    /// Returns the requested features for the given member.
1291    /// This filters out any named features that the member does not have.
1292    fn collect_matching_features(
1293        member: &Package,
1294        cli_features: &CliFeatures,
1295        found_features: &mut BTreeSet<FeatureValue>,
1296    ) -> CliFeatures {
1297        if cli_features.features.is_empty() {
1298            return cli_features.clone();
1299        }
1300
1301        // Only include features this member defines.
1302        let summary = member.summary();
1303
1304        // Features defined in the manifest
1305        let summary_features = summary.features();
1306
1307        // Dependency name -> dependency
1308        let dependencies: BTreeMap<InternedString, &Dependency> = summary
1309            .dependencies()
1310            .iter()
1311            .map(|dep| (dep.name_in_toml(), dep))
1312            .collect();
1313
1314        // Features that enable optional dependencies
1315        let optional_dependency_names: BTreeSet<_> = dependencies
1316            .iter()
1317            .filter(|(_, dep)| dep.is_optional())
1318            .map(|(name, _)| name)
1319            .copied()
1320            .collect();
1321
1322        let mut features = BTreeSet::new();
1323
1324        // Checks if a member contains the given feature.
1325        let summary_or_opt_dependency_feature = |feature: &InternedString| -> bool {
1326            summary_features.contains_key(feature) || optional_dependency_names.contains(feature)
1327        };
1328
1329        for feature in cli_features.features.iter() {
1330            match feature {
1331                FeatureValue::Feature(f) => {
1332                    if summary_or_opt_dependency_feature(f) {
1333                        // feature exists in this member.
1334                        features.insert(feature.clone());
1335                        found_features.insert(feature.clone());
1336                    }
1337                }
1338                // This should be enforced by CliFeatures.
1339                FeatureValue::Dep { .. } => panic!("unexpected dep: syntax {}", feature),
1340                FeatureValue::DepFeature {
1341                    dep_name,
1342                    dep_feature,
1343                    weak: _,
1344                } => {
1345                    if dependencies.contains_key(dep_name) {
1346                        // pkg/feat for a dependency.
1347                        // Will rely on the dependency resolver to validate `dep_feature`.
1348                        features.insert(feature.clone());
1349                        found_features.insert(feature.clone());
1350                    } else if *dep_name == member.name()
1351                        && summary_or_opt_dependency_feature(dep_feature)
1352                    {
1353                        // member/feat where "feat" is a feature in member.
1354                        //
1355                        // `weak` can be ignored here, because the member
1356                        // either is or isn't being built.
1357                        features.insert(FeatureValue::Feature(*dep_feature));
1358                        found_features.insert(feature.clone());
1359                    }
1360                }
1361            }
1362        }
1363        CliFeatures {
1364            features: Rc::new(features),
1365            all_features: cli_features.all_features,
1366            uses_default_features: cli_features.uses_default_features,
1367        }
1368    }
1369
1370    fn missing_feature_spelling_suggestions(
1371        &self,
1372        selected_members: &[&Package],
1373        cli_features: &CliFeatures,
1374        found_features: &BTreeSet<FeatureValue>,
1375    ) -> Vec<String> {
1376        // Keeps track of which features were contained in summary of `member` to suggest similar features in errors
1377        let mut summary_features: Vec<InternedString> = Default::default();
1378
1379        // Keeps track of `member` dependencies (`dep/feature`) and their features names to suggest similar features in error
1380        let mut dependencies_features: BTreeMap<InternedString, &[InternedString]> =
1381            Default::default();
1382
1383        // Keeps track of `member` optional dependencies names (which can be enabled with feature) to suggest similar features in error
1384        let mut optional_dependency_names: Vec<InternedString> = Default::default();
1385
1386        // Keeps track of which features were contained in summary of `member` to suggest similar features in errors
1387        let mut summary_features_per_member: BTreeMap<&Package, BTreeSet<InternedString>> =
1388            Default::default();
1389
1390        // Keeps track of `member` optional dependencies (which can be enabled with feature) to suggest similar features in error
1391        let mut optional_dependency_names_per_member: BTreeMap<&Package, BTreeSet<InternedString>> =
1392            Default::default();
1393
1394        for &member in selected_members {
1395            // Only include features this member defines.
1396            let summary = member.summary();
1397
1398            // Features defined in the manifest
1399            summary_features.extend(summary.features().keys());
1400            summary_features_per_member
1401                .insert(member, summary.features().keys().copied().collect());
1402
1403            // Dependency name -> dependency
1404            let dependencies: BTreeMap<InternedString, &Dependency> = summary
1405                .dependencies()
1406                .iter()
1407                .map(|dep| (dep.name_in_toml(), dep))
1408                .collect();
1409
1410            dependencies_features.extend(
1411                dependencies
1412                    .iter()
1413                    .map(|(name, dep)| (*name, dep.features())),
1414            );
1415
1416            // Features that enable optional dependencies
1417            let optional_dependency_names_raw: BTreeSet<_> = dependencies
1418                .iter()
1419                .filter(|(_, dep)| dep.is_optional())
1420                .map(|(name, _)| name)
1421                .copied()
1422                .collect();
1423
1424            optional_dependency_names.extend(optional_dependency_names_raw.iter());
1425            optional_dependency_names_per_member.insert(member, optional_dependency_names_raw);
1426        }
1427
1428        let edit_distance_test = |a: InternedString, b: InternedString| {
1429            edit_distance(a.as_str(), b.as_str(), 3).is_some()
1430        };
1431
1432        cli_features
1433            .features
1434            .difference(found_features)
1435            .map(|feature| match feature {
1436                // Simple feature, check if any of the optional dependency features or member features are close enough
1437                FeatureValue::Feature(typo) => {
1438                    // Finds member features which are similar to the requested feature.
1439                    let summary_features = summary_features
1440                        .iter()
1441                        .filter(move |feature| edit_distance_test(**feature, *typo));
1442
1443                    // Finds optional dependencies which name is similar to the feature
1444                    let optional_dependency_features = optional_dependency_names
1445                        .iter()
1446                        .filter(move |feature| edit_distance_test(**feature, *typo));
1447
1448                    summary_features
1449                        .chain(optional_dependency_features)
1450                        .map(|s| s.to_string())
1451                        .collect::<Vec<_>>()
1452                }
1453                FeatureValue::Dep { .. } => panic!("unexpected dep: syntax {}", feature),
1454                FeatureValue::DepFeature {
1455                    dep_name,
1456                    dep_feature,
1457                    weak: _,
1458                } => {
1459                    // Finds set of `pkg/feat` that are very similar to current `pkg/feat`.
1460                    let pkg_feat_similar = dependencies_features
1461                        .iter()
1462                        .filter(|(name, _)| edit_distance_test(**name, *dep_name))
1463                        .map(|(name, features)| {
1464                            (
1465                                name,
1466                                features
1467                                    .iter()
1468                                    .filter(|feature| edit_distance_test(**feature, *dep_feature))
1469                                    .collect::<Vec<_>>(),
1470                            )
1471                        })
1472                        .map(|(name, features)| {
1473                            features
1474                                .into_iter()
1475                                .map(move |feature| format!("{}/{}", name, feature))
1476                        })
1477                        .flatten();
1478
1479                    // Finds set of `member/optional_dep` features which name is similar to current `pkg/feat`.
1480                    let optional_dependency_features = optional_dependency_names_per_member
1481                        .iter()
1482                        .filter(|(package, _)| edit_distance_test(package.name(), *dep_name))
1483                        .map(|(package, optional_dependencies)| {
1484                            optional_dependencies
1485                                .into_iter()
1486                                .filter(|optional_dependency| {
1487                                    edit_distance_test(**optional_dependency, *dep_name)
1488                                })
1489                                .map(move |optional_dependency| {
1490                                    format!("{}/{}", package.name(), optional_dependency)
1491                                })
1492                        })
1493                        .flatten();
1494
1495                    // Finds set of `member/feat` features which name is similar to current `pkg/feat`.
1496                    let summary_features = summary_features_per_member
1497                        .iter()
1498                        .filter(|(package, _)| edit_distance_test(package.name(), *dep_name))
1499                        .map(|(package, summary_features)| {
1500                            summary_features
1501                                .into_iter()
1502                                .filter(|summary_feature| {
1503                                    edit_distance_test(**summary_feature, *dep_feature)
1504                                })
1505                                .map(move |summary_feature| {
1506                                    format!("{}/{}", package.name(), summary_feature)
1507                                })
1508                        })
1509                        .flatten();
1510
1511                    pkg_feat_similar
1512                        .chain(optional_dependency_features)
1513                        .chain(summary_features)
1514                        .collect::<Vec<_>>()
1515                }
1516            })
1517            .map(|v| v.into_iter())
1518            .flatten()
1519            .unique()
1520            .filter(|element| {
1521                let feature = FeatureValue::new(InternedString::new(element));
1522                !cli_features.features.contains(&feature) && !found_features.contains(&feature)
1523            })
1524            .sorted()
1525            .take(5)
1526            .collect()
1527    }
1528
1529    fn report_unknown_features_error(
1530        &self,
1531        specs: &[PackageIdSpec],
1532        cli_features: &CliFeatures,
1533        found_features: &BTreeSet<FeatureValue>,
1534    ) -> CargoResult<()> {
1535        let unknown: Vec<_> = cli_features
1536            .features
1537            .difference(found_features)
1538            .map(|feature| feature.to_string())
1539            .sorted()
1540            .collect();
1541
1542        let (selected_members, unselected_members): (Vec<_>, Vec<_>) = self
1543            .members()
1544            .partition(|member| specs.iter().any(|spec| spec.matches(member.package_id())));
1545
1546        let missing_packages_with_the_features = unselected_members
1547            .into_iter()
1548            .filter(|member| {
1549                unknown
1550                    .iter()
1551                    .any(|feature| member.summary().features().contains_key(&**feature))
1552            })
1553            .map(|m| m.name())
1554            .collect_vec();
1555
1556        let these_features = if unknown.len() == 1 {
1557            "this feature"
1558        } else {
1559            "these features"
1560        };
1561        let mut msg = if let [singular] = &selected_members[..] {
1562            format!(
1563                "the package '{}' does not contain {these_features}: {}",
1564                singular.name(),
1565                unknown.join(", ")
1566            )
1567        } else {
1568            let names = selected_members.iter().map(|m| m.name()).join(", ");
1569            format!("none of the selected packages contains {these_features}: {}\nselected packages: {names}", unknown.join(", "))
1570        };
1571
1572        use std::fmt::Write;
1573        if !missing_packages_with_the_features.is_empty() {
1574            write!(
1575                &mut msg,
1576                "\nhelp: package{} with the missing feature{}: {}",
1577                if missing_packages_with_the_features.len() != 1 {
1578                    "s"
1579                } else {
1580                    ""
1581                },
1582                if unknown.len() != 1 { "s" } else { "" },
1583                missing_packages_with_the_features.join(", ")
1584            )?;
1585        } else {
1586            let suggestions = self.missing_feature_spelling_suggestions(
1587                &selected_members,
1588                cli_features,
1589                found_features,
1590            );
1591            if !suggestions.is_empty() {
1592                write!(
1593                    &mut msg,
1594                    "\nhelp: there {}: {}",
1595                    if suggestions.len() == 1 {
1596                        "is a similarly named feature"
1597                    } else {
1598                        "are similarly named features"
1599                    },
1600                    suggestions.join(", ")
1601                )?;
1602            }
1603        }
1604
1605        bail!("{msg}")
1606    }
1607
1608    /// New command-line feature selection behavior with resolver = "2" or the
1609    /// root of a virtual workspace. See `allows_new_cli_feature_behavior`.
1610    fn members_with_features_new(
1611        &self,
1612        specs: &[PackageIdSpec],
1613        cli_features: &CliFeatures,
1614    ) -> CargoResult<Vec<(&Package, CliFeatures)>> {
1615        // Keeps track of which features matched `member` to produce an error
1616        // if any of them did not match anywhere.
1617        let mut found_features = Default::default();
1618
1619        let members: Vec<(&Package, CliFeatures)> = self
1620            .members()
1621            .filter(|m| specs.iter().any(|spec| spec.matches(m.package_id())))
1622            .map(|m| {
1623                (
1624                    m,
1625                    Workspace::collect_matching_features(m, cli_features, &mut found_features),
1626                )
1627            })
1628            .collect();
1629
1630        if members.is_empty() {
1631            // `cargo build -p foo`, where `foo` is not a member.
1632            // Do not allow any command-line flags (defaults only).
1633            if !(cli_features.features.is_empty()
1634                && !cli_features.all_features
1635                && cli_features.uses_default_features)
1636            {
1637                bail!("cannot specify features for packages outside of workspace");
1638            }
1639            // Add all members from the workspace so we can ensure `-p nonmember`
1640            // is in the resolve graph.
1641            return Ok(self
1642                .members()
1643                .map(|m| (m, CliFeatures::new_all(false)))
1644                .collect());
1645        }
1646        if *cli_features.features != found_features {
1647            self.report_unknown_features_error(specs, cli_features, &found_features)?;
1648        }
1649        Ok(members)
1650    }
1651
1652    /// This is the "old" behavior for command-line feature selection.
1653    /// See `allows_new_cli_feature_behavior`.
1654    fn members_with_features_old(
1655        &self,
1656        specs: &[PackageIdSpec],
1657        cli_features: &CliFeatures,
1658    ) -> Vec<(&Package, CliFeatures)> {
1659        // Split off any features with the syntax `member-name/feature-name` into a map
1660        // so that those features can be applied directly to those workspace-members.
1661        let mut member_specific_features: HashMap<InternedString, BTreeSet<FeatureValue>> =
1662            HashMap::new();
1663        // Features for the member in the current directory.
1664        let mut cwd_features = BTreeSet::new();
1665        for feature in cli_features.features.iter() {
1666            match feature {
1667                FeatureValue::Feature(_) => {
1668                    cwd_features.insert(feature.clone());
1669                }
1670                // This should be enforced by CliFeatures.
1671                FeatureValue::Dep { .. } => panic!("unexpected dep: syntax {}", feature),
1672                FeatureValue::DepFeature {
1673                    dep_name,
1674                    dep_feature,
1675                    weak: _,
1676                } => {
1677                    // I think weak can be ignored here.
1678                    // * With `--features member?/feat -p member`, the ? doesn't
1679                    //   really mean anything (either the member is built or it isn't).
1680                    // * With `--features nonmember?/feat`, cwd_features will
1681                    //   handle processing it correctly.
1682                    let is_member = self.members().any(|member| {
1683                        // Check if `dep_name` is member of the workspace, but isn't associated with current package.
1684                        self.current_opt() != Some(member) && member.name() == *dep_name
1685                    });
1686                    if is_member && specs.iter().any(|spec| spec.name() == dep_name.as_str()) {
1687                        member_specific_features
1688                            .entry(*dep_name)
1689                            .or_default()
1690                            .insert(FeatureValue::Feature(*dep_feature));
1691                    } else {
1692                        cwd_features.insert(feature.clone());
1693                    }
1694                }
1695            }
1696        }
1697
1698        let ms: Vec<_> = self
1699            .members()
1700            .filter_map(|member| {
1701                let member_id = member.package_id();
1702                match self.current_opt() {
1703                    // The features passed on the command-line only apply to
1704                    // the "current" package (determined by the cwd).
1705                    Some(current) if member_id == current.package_id() => {
1706                        let feats = CliFeatures {
1707                            features: Rc::new(cwd_features.clone()),
1708                            all_features: cli_features.all_features,
1709                            uses_default_features: cli_features.uses_default_features,
1710                        };
1711                        Some((member, feats))
1712                    }
1713                    _ => {
1714                        // Ignore members that are not enabled on the command-line.
1715                        if specs.iter().any(|spec| spec.matches(member_id)) {
1716                            // -p for a workspace member that is not the "current"
1717                            // one.
1718                            //
1719                            // The odd behavior here is due to backwards
1720                            // compatibility. `--features` and
1721                            // `--no-default-features` used to only apply to the
1722                            // "current" package. As an extension, this allows
1723                            // member-name/feature-name to set member-specific
1724                            // features, which should be backwards-compatible.
1725                            let feats = CliFeatures {
1726                                features: Rc::new(
1727                                    member_specific_features
1728                                        .remove(member.name().as_str())
1729                                        .unwrap_or_default(),
1730                                ),
1731                                uses_default_features: true,
1732                                all_features: cli_features.all_features,
1733                            };
1734                            Some((member, feats))
1735                        } else {
1736                            // This member was not requested on the command-line, skip.
1737                            None
1738                        }
1739                    }
1740                }
1741            })
1742            .collect();
1743
1744        // If any member specific features were not removed while iterating over members
1745        // some features will be ignored.
1746        assert!(member_specific_features.is_empty());
1747
1748        ms
1749    }
1750
1751    /// Returns true if `unit` should depend on the output of Docscrape units.
1752    pub fn unit_needs_doc_scrape(&self, unit: &Unit) -> bool {
1753        // We do not add scraped units for Host units, as they're either build scripts
1754        // (not documented) or proc macros (have no scrape-able exports). Additionally,
1755        // naively passing a proc macro's unit_for to new_unit_dep will currently cause
1756        // Cargo to panic, see issue #10545.
1757        self.is_member(&unit.pkg) && !(unit.target.for_host() || unit.pkg.proc_macro())
1758    }
1759
1760    /// Adds a local package registry overlaying a `SourceId`.
1761    ///
1762    /// See [`crate::sources::overlay::DependencyConfusionThreatOverlaySource`] for why you shouldn't use this.
1763    pub fn add_local_overlay(&mut self, id: SourceId, registry_path: PathBuf) {
1764        self.local_overlays.insert(id, registry_path);
1765    }
1766
1767    /// Builds a package registry that reflects this workspace configuration.
1768    pub fn package_registry(&self) -> CargoResult<PackageRegistry<'gctx>> {
1769        let source_config =
1770            SourceConfigMap::new_with_overlays(self.gctx(), self.local_overlays()?)?;
1771        PackageRegistry::new_with_source_config(self.gctx(), source_config)
1772    }
1773
1774    /// Returns all the configured local overlays, including the ones from our secret environment variable.
1775    fn local_overlays(&self) -> CargoResult<impl Iterator<Item = (SourceId, SourceId)>> {
1776        let mut ret = self
1777            .local_overlays
1778            .iter()
1779            .map(|(id, path)| Ok((*id, SourceId::for_local_registry(path)?)))
1780            .collect::<CargoResult<Vec<_>>>()?;
1781
1782        if let Ok(overlay) = self
1783            .gctx
1784            .get_env("__CARGO_TEST_DEPENDENCY_CONFUSION_VULNERABILITY_DO_NOT_USE_THIS")
1785        {
1786            let (url, path) = overlay.split_once('=').ok_or(anyhow::anyhow!(
1787                "invalid overlay format. I won't tell you why; you shouldn't be using it anyway"
1788            ))?;
1789            ret.push((
1790                SourceId::from_url(url)?,
1791                SourceId::for_local_registry(path.as_ref())?,
1792            ));
1793        }
1794
1795        Ok(ret.into_iter())
1796    }
1797}
1798
1799impl<'gctx> Packages<'gctx> {
1800    fn get(&self, manifest_path: &Path) -> &MaybePackage {
1801        self.maybe_get(manifest_path).unwrap()
1802    }
1803
1804    fn get_mut(&mut self, manifest_path: &Path) -> &mut MaybePackage {
1805        self.maybe_get_mut(manifest_path).unwrap()
1806    }
1807
1808    fn maybe_get(&self, manifest_path: &Path) -> Option<&MaybePackage> {
1809        self.packages.get(manifest_path)
1810    }
1811
1812    fn maybe_get_mut(&mut self, manifest_path: &Path) -> Option<&mut MaybePackage> {
1813        self.packages.get_mut(manifest_path)
1814    }
1815
1816    fn load(&mut self, manifest_path: &Path) -> CargoResult<&MaybePackage> {
1817        match self.packages.entry(manifest_path.to_path_buf()) {
1818            Entry::Occupied(e) => Ok(e.into_mut()),
1819            Entry::Vacant(v) => {
1820                let source_id = SourceId::for_manifest_path(manifest_path)?;
1821                let manifest = read_manifest(manifest_path, source_id, self.gctx)?;
1822                Ok(v.insert(match manifest {
1823                    EitherManifest::Real(manifest) => {
1824                        MaybePackage::Package(Package::new(manifest, manifest_path))
1825                    }
1826                    EitherManifest::Virtual(vm) => MaybePackage::Virtual(vm),
1827                }))
1828            }
1829        }
1830    }
1831}
1832
1833impl MaybePackage {
1834    fn workspace_config(&self) -> &WorkspaceConfig {
1835        match *self {
1836            MaybePackage::Package(ref p) => p.manifest().workspace_config(),
1837            MaybePackage::Virtual(ref vm) => vm.workspace_config(),
1838        }
1839    }
1840
1841    /// Has an embedded manifest (single-file package)
1842    pub fn is_embedded(&self) -> bool {
1843        match self {
1844            MaybePackage::Package(p) => p.manifest().is_embedded(),
1845            MaybePackage::Virtual(_) => false,
1846        }
1847    }
1848}
1849
1850impl WorkspaceRootConfig {
1851    /// Creates a new Intermediate Workspace Root configuration.
1852    pub fn new(
1853        root_dir: &Path,
1854        members: &Option<Vec<String>>,
1855        default_members: &Option<Vec<String>>,
1856        exclude: &Option<Vec<String>>,
1857        inheritable: &Option<InheritableFields>,
1858        custom_metadata: &Option<toml::Value>,
1859    ) -> WorkspaceRootConfig {
1860        WorkspaceRootConfig {
1861            root_dir: root_dir.to_path_buf(),
1862            members: members.clone(),
1863            default_members: default_members.clone(),
1864            exclude: exclude.clone().unwrap_or_default(),
1865            inheritable_fields: inheritable.clone().unwrap_or_default(),
1866            custom_metadata: custom_metadata.clone(),
1867        }
1868    }
1869    /// Checks the path against the `excluded` list.
1870    ///
1871    /// This method does **not** consider the `members` list.
1872    fn is_excluded(&self, manifest_path: &Path) -> bool {
1873        let excluded = self
1874            .exclude
1875            .iter()
1876            .any(|ex| manifest_path.starts_with(self.root_dir.join(ex)));
1877
1878        let explicit_member = match self.members {
1879            Some(ref members) => members
1880                .iter()
1881                .any(|mem| manifest_path.starts_with(self.root_dir.join(mem))),
1882            None => false,
1883        };
1884
1885        !explicit_member && excluded
1886    }
1887
1888    fn has_members_list(&self) -> bool {
1889        self.members.is_some()
1890    }
1891
1892    /// Returns expanded paths along with the glob that they were expanded from.
1893    /// The glob is `None` if the path matched exactly.
1894    #[tracing::instrument(skip_all)]
1895    fn members_paths<'g>(
1896        &self,
1897        globs: &'g [String],
1898    ) -> CargoResult<Vec<(PathBuf, Option<&'g str>)>> {
1899        let mut expanded_list = Vec::new();
1900
1901        for glob in globs {
1902            let pathbuf = self.root_dir.join(glob);
1903            let expanded_paths = Self::expand_member_path(&pathbuf)?;
1904
1905            // If glob does not find any valid paths, then put the original
1906            // path in the expanded list to maintain backwards compatibility.
1907            if expanded_paths.is_empty() {
1908                expanded_list.push((pathbuf, None));
1909            } else {
1910                let used_glob_pattern = expanded_paths.len() > 1 || expanded_paths[0] != pathbuf;
1911                let glob = used_glob_pattern.then_some(glob.as_str());
1912
1913                // Some OS can create system support files anywhere.
1914                // (e.g. macOS creates `.DS_Store` file if you visit a directory using Finder.)
1915                // Such files can be reported as a member path unexpectedly.
1916                // Check and filter out non-directory paths to prevent pushing such accidental unwanted path
1917                // as a member.
1918                for expanded_path in expanded_paths {
1919                    if expanded_path.is_dir() {
1920                        expanded_list.push((expanded_path, glob));
1921                    }
1922                }
1923            }
1924        }
1925
1926        Ok(expanded_list)
1927    }
1928
1929    fn expand_member_path(path: &Path) -> CargoResult<Vec<PathBuf>> {
1930        let Some(path) = path.to_str() else {
1931            return Ok(Vec::new());
1932        };
1933        let res = glob(path).with_context(|| format!("could not parse pattern `{}`", &path))?;
1934        let res = res
1935            .map(|p| p.with_context(|| format!("unable to match path to pattern `{}`", &path)))
1936            .collect::<Result<Vec<_>, _>>()?;
1937        Ok(res)
1938    }
1939
1940    pub fn inheritable(&self) -> &InheritableFields {
1941        &self.inheritable_fields
1942    }
1943}
1944
1945pub fn resolve_relative_path(
1946    label: &str,
1947    old_root: &Path,
1948    new_root: &Path,
1949    rel_path: &str,
1950) -> CargoResult<String> {
1951    let joined_path = normalize_path(&old_root.join(rel_path));
1952    match diff_paths(joined_path, new_root) {
1953        None => Err(anyhow!(
1954            "`{}` was defined in {} but could not be resolved with {}",
1955            label,
1956            old_root.display(),
1957            new_root.display()
1958        )),
1959        Some(path) => Ok(path
1960            .to_str()
1961            .ok_or_else(|| {
1962                anyhow!(
1963                    "`{}` resolved to non-UTF value (`{}`)",
1964                    label,
1965                    path.display()
1966                )
1967            })?
1968            .to_owned()),
1969    }
1970}
1971
1972/// Finds the path of the root of the workspace.
1973pub fn find_workspace_root(
1974    manifest_path: &Path,
1975    gctx: &GlobalContext,
1976) -> CargoResult<Option<PathBuf>> {
1977    find_workspace_root_with_loader(manifest_path, gctx, |self_path| {
1978        let source_id = SourceId::for_manifest_path(self_path)?;
1979        let manifest = read_manifest(self_path, source_id, gctx)?;
1980        Ok(manifest
1981            .workspace_config()
1982            .get_ws_root(self_path, manifest_path))
1983    })
1984}
1985
1986/// Finds the path of the root of the workspace.
1987///
1988/// This uses a callback to determine if the given path tells us what the
1989/// workspace root is.
1990fn find_workspace_root_with_loader(
1991    manifest_path: &Path,
1992    gctx: &GlobalContext,
1993    mut loader: impl FnMut(&Path) -> CargoResult<Option<PathBuf>>,
1994) -> CargoResult<Option<PathBuf>> {
1995    // Check if there are any workspace roots that have already been found that would work
1996    {
1997        let roots = gctx.ws_roots.borrow();
1998        // Iterate through the manifests parent directories until we find a workspace
1999        // root. Note we skip the first item since that is just the path itself
2000        for current in manifest_path.ancestors().skip(1) {
2001            if let Some(ws_config) = roots.get(current) {
2002                if !ws_config.is_excluded(manifest_path) {
2003                    // Add `Cargo.toml` since ws_root is the root and not the file
2004                    return Ok(Some(current.join("Cargo.toml")));
2005                }
2006            }
2007        }
2008    }
2009
2010    for ances_manifest_path in find_root_iter(manifest_path, gctx) {
2011        debug!("find_root - trying {}", ances_manifest_path.display());
2012        if let Some(ws_root_path) = loader(&ances_manifest_path)? {
2013            return Ok(Some(ws_root_path));
2014        }
2015    }
2016    Ok(None)
2017}
2018
2019fn read_root_pointer(member_manifest: &Path, root_link: &str) -> PathBuf {
2020    let path = member_manifest
2021        .parent()
2022        .unwrap()
2023        .join(root_link)
2024        .join("Cargo.toml");
2025    debug!("find_root - pointer {}", path.display());
2026    paths::normalize_path(&path)
2027}
2028
2029fn find_root_iter<'a>(
2030    manifest_path: &'a Path,
2031    gctx: &'a GlobalContext,
2032) -> impl Iterator<Item = PathBuf> + 'a {
2033    LookBehind::new(paths::ancestors(manifest_path, None).skip(2))
2034        .take_while(|path| !path.curr.ends_with("target/package"))
2035        // Don't walk across `CARGO_HOME` when we're looking for the
2036        // workspace root. Sometimes a package will be organized with
2037        // `CARGO_HOME` pointing inside of the workspace root or in the
2038        // current package, but we don't want to mistakenly try to put
2039        // crates.io crates into the workspace by accident.
2040        .take_while(|path| {
2041            if let Some(last) = path.last {
2042                gctx.home() != last
2043            } else {
2044                true
2045            }
2046        })
2047        .map(|path| path.curr.join("Cargo.toml"))
2048        .filter(|ances_manifest_path| ances_manifest_path.exists())
2049}
2050
2051struct LookBehindWindow<'a, T: ?Sized> {
2052    curr: &'a T,
2053    last: Option<&'a T>,
2054}
2055
2056struct LookBehind<'a, T: ?Sized, K: Iterator<Item = &'a T>> {
2057    iter: K,
2058    last: Option<&'a T>,
2059}
2060
2061impl<'a, T: ?Sized, K: Iterator<Item = &'a T>> LookBehind<'a, T, K> {
2062    fn new(items: K) -> Self {
2063        Self {
2064            iter: items,
2065            last: None,
2066        }
2067    }
2068}
2069
2070impl<'a, T: ?Sized, K: Iterator<Item = &'a T>> Iterator for LookBehind<'a, T, K> {
2071    type Item = LookBehindWindow<'a, T>;
2072
2073    fn next(&mut self) -> Option<Self::Item> {
2074        match self.iter.next() {
2075            None => None,
2076            Some(next) => {
2077                let last = self.last;
2078                self.last = Some(next);
2079                Some(LookBehindWindow { curr: next, last })
2080            }
2081        }
2082    }
2083}