diff --git a/crates/cargo-util-schemas/src/core/source_kind.rs b/crates/cargo-util-schemas/src/core/source_kind.rs index 3794791114d..1c16a251371 100644 --- a/crates/cargo-util-schemas/src/core/source_kind.rs +++ b/crates/cargo-util-schemas/src/core/source_kind.rs @@ -15,6 +15,8 @@ pub enum SourceKind { LocalRegistry, /// A directory-based registry. Directory, + /// Package sources distributed with the rust toolchain + Builtin, } // The hash here is important for what folder packages get downloaded into. @@ -40,6 +42,7 @@ impl SourceKind { SourceKind::SparseRegistry => None, SourceKind::LocalRegistry => Some("local-registry"), SourceKind::Directory => Some("directory"), + SourceKind::Builtin => Some("builtin"), } } } @@ -71,6 +74,10 @@ impl Ord for SourceKind { (_, SourceKind::Directory) => Ordering::Greater, (SourceKind::Git(a), SourceKind::Git(b)) => a.cmp(b), + (SourceKind::Git(_), _) => Ordering::Less, + (_, SourceKind::Git(_)) => Ordering::Greater, + + (SourceKind::Builtin, SourceKind::Builtin) => Ordering::Equal, } } } diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index 45a313a921d..d47400ae95b 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -99,6 +99,7 @@ pub fn resolve_std<'gctx>( HasDevUnits::No, crate::core::resolver::features::ForceAllTargets::No, dry_run, + false, )?; debug_assert_eq!(resolve.specs_and_features.len(), 1); Ok(( diff --git a/src/cargo/core/compiler/unit_dependencies.rs b/src/cargo/core/compiler/unit_dependencies.rs index 573d10a40b8..cee47c9ff76 100644 --- a/src/cargo/core/compiler/unit_dependencies.rs +++ b/src/cargo/core/compiler/unit_dependencies.rs @@ -52,6 +52,8 @@ struct State<'a, 'gctx> { std_resolve: Option<&'a Resolve>, /// Like `usr_features` but for building standard library (`-Zbuild-std`). std_features: Option<&'a ResolvedFeatures>, + // The root units of any opaque dependencies present in the user resolve + opaque_roots: &'a HashMap>, /// `true` while generating the dependencies for the standard library. is_std: bool, /// The high-level operation requested by the user. @@ -92,7 +94,7 @@ pub fn build_unit_dependencies<'a, 'gctx>( resolve: &'a Resolve, features: &'a ResolvedFeatures, std_resolve: Option<&'a (Resolve, ResolvedFeatures)>, - roots: &[Unit], + roots: &[Unit], //TODO: builtins can be roots if requested on the command line scrape_units: &[Unit], std_roots: &HashMap>, intent: UserIntent, @@ -119,6 +121,7 @@ pub fn build_unit_dependencies<'a, 'gctx>( usr_features: features, std_resolve, std_features, + opaque_roots: std_roots, is_std: false, intent, target_data, @@ -129,15 +132,14 @@ pub fn build_unit_dependencies<'a, 'gctx>( }; let std_unit_deps = calc_deps_of_std(&mut state, std_roots)?; + if let Some(std_unit_deps) = std_unit_deps { + attach_std_deps(&mut state, std_unit_deps); + } deps_of_roots(roots, &mut state)?; super::links::validate_links(state.resolve(), &state.unit_dependencies)?; // Hopefully there aren't any links conflicts with the standard library? - if let Some(std_unit_deps) = std_unit_deps { - attach_std_deps(&mut state, std_roots, std_unit_deps); - } - connect_run_custom_build_deps(&mut state); // Dependencies are used in tons of places throughout the backend, many of @@ -188,35 +190,14 @@ fn calc_deps_of_std( Ok(Some(std::mem::take(&mut state.unit_dependencies))) } -/// Add the standard library units to the `unit_dependencies`. -fn attach_std_deps( - state: &mut State<'_, '_>, - std_roots: &HashMap>, - std_unit_deps: UnitGraph, -) { - // Attach the standard library as a dependency of every target unit. - let mut found = false; - for (unit, deps) in state.unit_dependencies.iter_mut() { - if !unit.kind.is_host() && !unit.mode.is_run_custom_build() { - deps.extend(std_roots[&unit.kind].iter().map(|unit| UnitDep { - unit: unit.clone(), - unit_for: UnitFor::new_normal(unit.kind), - extern_crate_name: unit.pkg.name(), - dep_name: None, - // TODO: Does this `public` make sense? - public: true, - noprelude: true, - })); - found = true; +/// Add the dependencies of standard library units to the `unit_dependencies`. +fn attach_std_deps(state: &mut State<'_, '_>, std_unit_deps: UnitGraph) { + for (unit, deps) in std_unit_deps.into_iter() { + if unit.pkg.package_id().name() == "sysroot" { + continue; } - } - // And also include the dependencies of the standard library itself. Don't - // include these if no units actually needed the standard library. - if found { - for (unit, deps) in std_unit_deps.into_iter() { - if let Some(other_unit) = state.unit_dependencies.insert(unit, deps) { - panic!("std unit collision with existing unit: {:?}", other_unit); - } + if let Some(other_unit) = state.unit_dependencies.insert(unit, deps) { + panic!("std unit collision with existing unit: {:?}", other_unit); } } } @@ -333,16 +314,37 @@ fn compute_deps( )?; ret.push(unit_dep); } else { - let unit_dep = new_unit_dep( - state, - unit, - dep_pkg, - dep_lib, - dep_unit_for, - unit.kind.for_target(dep_lib), - mode, - IS_NO_ARTIFACT_DEP, - )?; + // if builtin, return from state.opaque_roots + let unit_dep = if dep_pkg_id.source_id().is_builtin() { + let unit: Vec<_> = state.opaque_roots[&unit.kind.for_target(dep_lib)] + .iter() + .filter(|&u| u.pkg.name() == dep_pkg_id.name()) + .collect(); + assert!( + unit.len() == 1, + "libstd was resolved with all possible builtin deps as roots" + ); + let unit = unit[0]; + UnitDep { + unit: unit.clone(), + unit_for: UnitFor::new_normal(unit.kind), + extern_crate_name: unit.pkg.name(), + dep_name: None, + public: true, + noprelude: true, + } + } else { + new_unit_dep( + state, + unit, + dep_pkg, + dep_lib, + dep_unit_for, + unit.kind.for_target(dep_lib), + mode, + IS_NO_ARTIFACT_DEP, + )? + }; ret.push(unit_dep); } diff --git a/src/cargo/core/dependency.rs b/src/cargo/core/dependency.rs index db421dd0c24..672de3413f9 100644 --- a/src/cargo/core/dependency.rs +++ b/src/cargo/core/dependency.rs @@ -51,6 +51,10 @@ struct Inner { // This dependency should be used only for this platform. // `None` means *all platforms*. platform: Option, + + // Opaque dependencies should be resolved with a separate resolver run, and handled + // by unit generation. + opaque: bool, } #[derive(Serialize)] @@ -162,6 +166,30 @@ impl Dependency { platform: None, explicit_name_in_toml: None, artifact: None, + opaque: false, + }), + } + } + + pub fn new_injected_builtin(name: InternedString) -> Dependency { + assert!(!name.is_empty()); + Dependency { + inner: Arc::new(Inner { + name, + source_id: SourceId::new_builtin(&name).expect("package name is valid url"), + registry_id: None, + req: OptVersionReq::Any, + kind: DepKind::Normal, + only_match_name: true, + optional: false, + public: false, + features: Vec::new(), + default_features: true, + specified_req: false, + platform: None, + explicit_name_in_toml: None, + artifact: None, + opaque: true, }), } } @@ -455,6 +483,10 @@ impl Dependency { pub(crate) fn maybe_lib(&self) -> bool { self.artifact().map(|a| a.is_lib).unwrap_or(true) } + + pub fn is_opaque(&self) -> bool { + self.inner.opaque + } } /// The presence of an artifact turns an ordinary dependency into an Artifact dependency. diff --git a/src/cargo/core/registry.rs b/src/cargo/core/registry.rs index 99f1d857ac0..57911051866 100644 --- a/src/cargo/core/registry.rs +++ b/src/cargo/core/registry.rs @@ -9,7 +9,7 @@ //! The former is just one kind of source, //! while the latter involves operations on the registry Web API. -use std::collections::{HashMap, HashSet}; +use std::collections::{BTreeMap, HashMap, HashSet}; use std::task::{Poll, ready}; use crate::core::{Dependency, PackageId, PackageSet, Patch, SourceId, Summary}; @@ -24,6 +24,7 @@ use crate::util::{CanonicalUrl, GlobalContext}; use annotate_snippets::Level; use anyhow::Context as _; use itertools::Itertools; +use semver::Version; use tracing::{debug, trace}; use url::Url; @@ -724,6 +725,36 @@ impl<'gctx> Registry for PackageRegistry<'gctx> { ) })?; + if dep.is_opaque() { + // Currently, all opaque dependencies are builtins. + // Create a dummy Summary that can be replaced with a real package during + // unit generation + trace!( + "Injecting package to satisfy builtin dependency on {}", + dep.package_name() + ); + let ver = if dep.package_name() == "compiler_builtins" { + //TODO: hack + Version::new(0, 1, 160) + } else { + Version::new(0, 0, 0) + }; + let pkg_id = PackageId::new( + dep.package_name(), + ver, + SourceId::new_builtin(&dep.package_name()).expect("SourceId ok"), + ); + + let summary = Summary::new( + pkg_id, + vec![], + &BTreeMap::new(), // TODO: bodge + Option::::None, + None, + )?; + f(IndexSummary::Candidate(summary)); + return Poll::Ready(Ok(())); + } let source = self.sources.get_mut(dep.source_id()); match (override_summary, source) { (Some(_), None) => { diff --git a/src/cargo/core/resolver/context.rs b/src/cargo/core/resolver/context.rs index ae0f520dd74..a6a66be26f2 100644 --- a/src/cargo/core/resolver/context.rs +++ b/src/cargo/core/resolver/context.rs @@ -25,6 +25,10 @@ pub struct ResolverContext { /// a way to look up for a package in activations what packages required it /// and all of the exact deps that it fulfilled. pub parents: Graph>, + // Opaque dependencies require a separate resolver run as they allow for multiple + // different semver-compatible versions of crates in the final resolve. This is the + // (unactivated) set of Summaries that need handling in a future invocation + //pub promises: HashSet, } /// When backtracking it can be useful to know how far back to go. diff --git a/src/cargo/core/resolver/dep_cache.rs b/src/cargo/core/resolver/dep_cache.rs index 54bde19dd35..d50e8a93ed3 100644 --- a/src/cargo/core/resolver/dep_cache.rs +++ b/src/cargo/core/resolver/dep_cache.rs @@ -48,6 +48,9 @@ pub struct RegistryQueryer<'a> { >, /// all the cases we ended up using a supplied replacement used_replacements: HashMap, + /// Cached builtin dependencies that should be injected. Empty implies that builtins shouldn't + /// be injected + builtins: Vec, } impl<'a> RegistryQueryer<'a> { @@ -55,7 +58,24 @@ impl<'a> RegistryQueryer<'a> { registry: &'a mut dyn Registry, replacements: &'a [(PackageIdSpec, Dependency)], version_prefs: &'a VersionPreferences, + inject_builtins: bool, ) -> Self { + let builtins = if inject_builtins { + [ + "std", + "alloc", + "core", + "panic_unwind", + "proc_macro", + "compiler_builtins", + ] + .iter() + .map(|&krate| Dependency::new_injected_builtin(krate.into())) + .collect() + } else { + vec![] + }; + RegistryQueryer { registry, replacements, @@ -63,6 +83,7 @@ impl<'a> RegistryQueryer<'a> { registry_cache: HashMap::new(), summary_cache: HashMap::new(), used_replacements: HashMap::new(), + builtins, } } @@ -238,10 +259,11 @@ impl<'a> RegistryQueryer<'a> { { return Ok(out.0.clone()); } + // First, figure out our set of dependencies based on the requested set // of features. This also calculates what features we're going to enable // for our own dependencies. - let (used_features, deps) = resolve_features(parent, candidate, opts)?; + let (used_features, deps) = resolve_features(parent, candidate, opts, &self.builtins)?; // Next, transform all dependencies into a list of possible candidates // which can satisfy that dependency. @@ -291,10 +313,20 @@ pub fn resolve_features<'b>( parent: Option, s: &'b Summary, opts: &'b ResolveOpts, + builtins: &[Dependency], ) -> ActivateResult<(HashSet, Vec<(Dependency, FeaturesSet)>)> { // First, filter by dev-dependencies. let deps = s.dependencies(); - let deps = deps.iter().filter(|d| d.is_transitive() || opts.dev_deps); + + let deps = deps + .into_iter() + .filter(|d| d.is_transitive() || opts.dev_deps); + let builtin_deps = if s.source_id().is_builtin() { + // Don't add builtin deps to dummy builtin packages + None + } else { + Some(builtins.iter()) + }; let reqs = build_requirements(parent, s, opts)?; let mut ret = Vec::new(); @@ -302,7 +334,7 @@ pub fn resolve_features<'b>( let mut valid_dep_names = HashSet::new(); // Next, collect all actually enabled dependencies and their features. - for dep in deps { + for dep in deps.chain(builtin_deps.into_iter().flatten()) { // Skip optional dependencies, but not those enabled through a // feature if dep.is_optional() && !reqs.deps.contains_key(&dep.name_in_toml()) { diff --git a/src/cargo/core/resolver/encode.rs b/src/cargo/core/resolver/encode.rs index 4f437123282..f30746b2dc7 100644 --- a/src/cargo/core/resolver/encode.rs +++ b/src/cargo/core/resolver/encode.rs @@ -661,7 +661,8 @@ pub fn encodable_package_id( } fn encodable_source_id(id: SourceId, version: ResolveVersion) -> Option { - if id.is_path() { + // TODO: Not enough to stop builtins from appearing in the lockfile + if id.is_path() || id.is_builtin() { None } else { Some( diff --git a/src/cargo/core/resolver/mod.rs b/src/cargo/core/resolver/mod.rs index dd65e17d3f1..630019fb0cd 100644 --- a/src/cargo/core/resolver/mod.rs +++ b/src/cargo/core/resolver/mod.rs @@ -126,6 +126,7 @@ pub fn resolve( version_prefs: &VersionPreferences, resolve_version: ResolveVersion, gctx: Option<&GlobalContext>, + inject_builtins: bool, ) -> CargoResult { let first_version = match gctx { Some(config) if config.cli_unstable().direct_minimal_versions => { @@ -133,7 +134,7 @@ pub fn resolve( } _ => None, }; - let mut registry = RegistryQueryer::new(registry, replacements, version_prefs); + let mut registry = RegistryQueryer::new(registry, replacements, version_prefs, inject_builtins); // Global cache of the reasons for each time we backtrack. let mut past_conflicting_activations = conflict_cache::ConflictCache::new(); diff --git a/src/cargo/core/resolver/types.rs b/src/cargo/core/resolver/types.rs index 345b6ccbdd1..9187892e462 100644 --- a/src/cargo/core/resolver/types.rs +++ b/src/cargo/core/resolver/types.rs @@ -138,7 +138,7 @@ impl ResolveBehavior { } } -/// Options for how the resolve should work. +/// Options for how a Summary should be activated during the resolve. #[derive(Clone, Debug, Eq, PartialEq, Hash)] pub struct ResolveOpts { /// Whether or not dev-dependencies should be included. diff --git a/src/cargo/core/source_id.rs b/src/cargo/core/source_id.rs index dade72b5a4b..6daa3d7a70b 100644 --- a/src/cargo/core/source_id.rs +++ b/src/cargo/core/source_id.rs @@ -140,6 +140,21 @@ impl SourceId { } } + pub fn new_builtin(name: &str) -> CargoResult { + // Injecting builtins earlier (somewhere with access to RustcTargetData) is needed instead of this + let home = std::env::var("HOME").expect("HOME is set"); + let path = format!( + "file://{home}/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/" + ); + if name == "compiler_builtins" { + Self::from_url(&format!( + "builtin+{path}compiler-builtins/compiler-builtins" + )) + } else { + Self::from_url(&format!("builtin+{path}{name}")) + } + } + /// Parses a source URL and returns the corresponding ID. /// /// ## Example @@ -176,6 +191,10 @@ impl SourceId { let url = url.into_url()?; SourceId::new(SourceKind::Path, url, None) } + "builtin" => { + let url = url.into_url()?; + SourceId::new(SourceKind::Builtin, url, None) + } kind => Err(anyhow::format_err!("unsupported source protocol: {}", kind)), } } @@ -387,6 +406,10 @@ impl SourceId { matches!(self.inner.kind, SourceKind::Git(_)) } + pub fn is_builtin(self) -> bool { + matches!(self.inner.kind, SourceKind::Builtin) + } + /// Creates an implementation of `Source` corresponding to this ID. /// /// * `yanked_whitelist` --- Packages allowed to be used, even if they are yanked. @@ -433,6 +456,14 @@ impl SourceId { .expect("path sources cannot be remote"); Ok(Box::new(DirectorySource::new(&path, self, gctx))) } + SourceKind::Builtin => { + let path = self + .inner + .url + .to_file_path() + .expect("builtin sources should not be remote"); + Ok(Box::new(PathSource::new(&path, self, gctx))) + } } } @@ -679,6 +710,7 @@ impl fmt::Display for SourceId { } SourceKind::LocalRegistry => write!(f, "registry `{}`", url_display(&self.inner.url)), SourceKind::Directory => write!(f, "dir {}", url_display(&self.inner.url)), + SourceKind::Builtin => write!(f, "builtin {}", url_display(&self.inner.url)), } } } diff --git a/src/cargo/core/summary.rs b/src/cargo/core/summary.rs index 7d9dd83fc2e..2eeeb6c88a6 100644 --- a/src/cargo/core/summary.rs +++ b/src/cargo/core/summary.rs @@ -82,6 +82,7 @@ impl Summary { ) } } + let feature_map = build_feature_map(features, &dependencies)?; Ok(Summary { inner: Arc::new(Inner { @@ -96,6 +97,10 @@ impl Summary { }) } + // Virtual summary representing a package Cargo knows how to retrieve later + // pub fn new_builtin(pkg_id: PackageId, + // features: &BTreeMap>) -> + pub fn package_id(&self) -> PackageId { self.inner.package_id } diff --git a/src/cargo/ops/cargo_compile/mod.rs b/src/cargo/ops/cargo_compile/mod.rs index 64e81fdf3a4..f6b75eb0fca 100644 --- a/src/cargo/ops/cargo_compile/mod.rs +++ b/src/cargo/ops/cargo_compile/mod.rs @@ -322,6 +322,7 @@ pub fn create_bcx<'a, 'gctx>( has_dev_units, ForceAllTargets::No, dry_run, + true, )?; let WorkspaceResolve { mut pkg_set, diff --git a/src/cargo/ops/cargo_output_metadata.rs b/src/cargo/ops/cargo_output_metadata.rs index 929a4892e97..9f94d7664f7 100644 --- a/src/cargo/ops/cargo_output_metadata.rs +++ b/src/cargo/ops/cargo_output_metadata.rs @@ -165,6 +165,7 @@ fn build_resolve_graph( HasDevUnits::Yes, force_all, dry_run, + true, )?; let package_map: BTreeMap = ws_resolve diff --git a/src/cargo/ops/cargo_package/mod.rs b/src/cargo/ops/cargo_package/mod.rs index 472acf41cb1..9ba9311bf0d 100644 --- a/src/cargo/ops/cargo_package/mod.rs +++ b/src/cargo/ops/cargo_package/mod.rs @@ -778,6 +778,7 @@ fn build_lock( None, &[], true, + true, )?; let pkg_set = ops::get_resolved_packages(&new_resolve, tmp_reg)?; diff --git a/src/cargo/ops/cargo_update.rs b/src/cargo/ops/cargo_update.rs index 080401cbe07..974d16fa980 100644 --- a/src/cargo/ops/cargo_update.rs +++ b/src/cargo/ops/cargo_update.rs @@ -47,6 +47,7 @@ pub fn generate_lockfile(ws: &Workspace<'_>) -> CargoResult<()> { None, &[], true, + true, )?; ops::write_pkg_lockfile(ws, &mut resolve)?; print_lockfile_changes(ws, previous_resolve, &resolve, &mut registry)?; @@ -87,6 +88,7 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes None, &[], true, + true, )? } } @@ -177,6 +179,7 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes Some(&keep), &[], true, + true, )?; print_lockfile_updates( diff --git a/src/cargo/ops/fix/mod.rs b/src/cargo/ops/fix/mod.rs index 6bd0199a494..fdf992ba57b 100644 --- a/src/cargo/ops/fix/mod.rs +++ b/src/cargo/ops/fix/mod.rs @@ -578,6 +578,7 @@ fn check_resolver_change<'gctx>( has_dev_units, crate::core::resolver::features::ForceAllTargets::No, dry_run, + true, )?; let feature_opts = FeatureOpts::new_behavior(ResolveBehavior::V2, has_dev_units); diff --git a/src/cargo/ops/resolve.rs b/src/cargo/ops/resolve.rs index 661dc05f1c0..9715d7a11c0 100644 --- a/src/cargo/ops/resolve.rs +++ b/src/cargo/ops/resolve.rs @@ -133,7 +133,7 @@ version. This may also occur with an optional dependency that is not enabled."; /// `package`, which don't specify any options or features. pub fn resolve_ws<'a>(ws: &Workspace<'a>, dry_run: bool) -> CargoResult<(PackageSet<'a>, Resolve)> { let mut registry = ws.package_registry()?; - let resolve = resolve_with_registry(ws, &mut registry, dry_run)?; + let resolve = resolve_with_registry(ws, &mut registry, dry_run, true)?; let packages = get_resolved_packages(&resolve, registry)?; Ok((packages, resolve)) } @@ -157,6 +157,7 @@ pub fn resolve_ws_with_opts<'gctx>( has_dev_units: HasDevUnits, force_all_targets: ForceAllTargets, dry_run: bool, + inject_builtins: bool, ) -> CargoResult> { let feature_unification = ws.resolve_feature_unification(); let individual_specs = match feature_unification { @@ -186,13 +187,14 @@ pub fn resolve_ws_with_opts<'gctx>( None, specs, add_patches, + inject_builtins, )?; ops::print_lockfile_changes(ws, None, &resolved_with_overrides, &mut registry)?; (resolve, resolved_with_overrides) } else if ws.require_optional_deps() { // First, resolve the root_package's *listed* dependencies, as well as // downloading and updating all remotes and such. - let resolve = resolve_with_registry(ws, &mut registry, dry_run)?; + let resolve = resolve_with_registry(ws, &mut registry, dry_run, inject_builtins)?; // No need to add patches again, `resolve_with_registry` has done it. let add_patches = false; @@ -244,6 +246,7 @@ pub fn resolve_ws_with_opts<'gctx>( None, specs, add_patches, + inject_builtins, )?; (Some(resolve), resolved_with_overrides) } else { @@ -258,6 +261,7 @@ pub fn resolve_ws_with_opts<'gctx>( None, specs, add_patches, + inject_builtins, )?; // Skipping `print_lockfile_changes` as there are cases where this prints irrelevant // information @@ -352,6 +356,7 @@ fn resolve_with_registry<'gctx>( ws: &Workspace<'gctx>, registry: &mut PackageRegistry<'gctx>, dry_run: bool, + inject_builtins: bool, ) -> CargoResult { let prev = ops::load_pkg_lockfile(ws)?; let mut resolve = resolve_with_previous( @@ -363,6 +368,7 @@ fn resolve_with_registry<'gctx>( None, &[], true, + inject_builtins, )?; let print = if !ws.is_ephemeral() && ws.require_optional_deps() { @@ -410,6 +416,7 @@ pub fn resolve_with_previous<'gctx>( keep_previous: Option>, specs: &[PackageIdSpec], register_patches: bool, + inject_builtins: bool, ) -> CargoResult { // We only want one Cargo at a time resolving a crate graph since this can // involve a lot of frobbing of the global caches. @@ -509,6 +516,7 @@ pub fn resolve_with_previous<'gctx>( &version_prefs, ResolveVersion::with_rust_version(ws.lowest_rust_version()), Some(ws.gctx()), + inject_builtins, )?; let patches = registry.patches().values().flat_map(|v| v.iter()); diff --git a/src/cargo/ops/tree/mod.rs b/src/cargo/ops/tree/mod.rs index 4344daa49e7..65f044fc71a 100644 --- a/src/cargo/ops/tree/mod.rs +++ b/src/cargo/ops/tree/mod.rs @@ -173,6 +173,7 @@ pub fn build_and_print(ws: &Workspace<'_>, opts: &TreeOptions) -> CargoResult<() has_dev, force_all, dry_run, + true, )?; let package_map: HashMap = ws_resolve diff --git a/src/cargo/util/command_prelude.rs b/src/cargo/util/command_prelude.rs index c32fb25fabb..31f3648ecf9 100644 --- a/src/cargo/util/command_prelude.rs +++ b/src/cargo/util/command_prelude.rs @@ -1422,6 +1422,7 @@ fn get_packages() -> CargoResult> { has_dev_units, force_all_targets, dry_run, + true, )?; let packages = ws_resolve