Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
5bad531
typo: Fix previously added typo
adamgemmell Aug 10, 2026
c4ebca0
test(unit-graph): Capture existing behaviour in unit-graph test
adamgemmell Aug 10, 2026
15c1d49
test(resolver): Model builtin packages
adamgemmell Jul 21, 2026
d73fed6
feat(source): Allow specifying builtin sources
adamgemmell Jul 21, 2026
4c9c5dc
feat(resolver): Allow creating builtin dependencies
adamgemmell Jul 22, 2026
2963df7
test(resolver): Add negative resolver tests
adamgemmell Aug 7, 2026
50ef28a
feat(sources): generate opaque summaries to satisfy builtin dependencies
adamgemmell Aug 21, 2026
1657629
feat(resolver): Add directive to enable injecting builtins
adamgemmell Aug 4, 2026
e7b1e78
test(resolver): Test for implicit builtin deps
adamgemmell Aug 7, 2026
7b4c0c9
feat(resolver): Inject builtin dependencies
adamgemmell Aug 5, 2026
4f35cdd
test(resolve): Add test that build-std does not affect the lockfile
adamgemmell Aug 21, 2026
20cde82
test(package-diff): Test that build-std packages do not show in cargo…
adamgemmell Aug 21, 2026
50ce479
feat(build-std): Enable implicit builtin dependencies and unit
adamgemmell Feb 25, 2026
2e61e3f
test(build-std): Fix shared_std test
adamgemmell Aug 7, 2026
49a012f
test(build-std): Test when builtins are present as roots, such as when
adamgemmell Aug 7, 2026
a9aed01
feat(unit generation): Replace builtin roots with std roots
adamgemmell Aug 24, 2026
8f0473b
test(metadata): Do not emit builtins with cargo metadata
adamgemmell Aug 7, 2026
fd1ca3e
fix(metadata): Filter out builtin packages
adamgemmell Aug 7, 2026
5e9211f
test(build-std): Do not emit builtins with cargo tree
adamgemmell Aug 7, 2026
ff222a5
fix(tree): Don't emit builtins
adamgemmell Aug 7, 2026
8c65c05
test(vendor): builtins shouldn't be vendored
adamgemmell Aug 7, 2026
6b31035
fix(vendor): Don't vendor builtins
adamgemmell Aug 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ cargo-platform = { path = "crates/cargo-platform", version = "0.3.3" }
cargo-test-macro = { version = "0.4.15", path = "crates/cargo-test-macro" }
cargo-test-support = { version = "0.11.4", path = "crates/cargo-test-support" }
cargo-util = { version = "0.2.33", path = "crates/cargo-util" }
cargo-util-schemas = { version = "0.14.4", path = "crates/cargo-util-schemas" }
cargo-util-schemas = { version = "0.15.0", path = "crates/cargo-util-schemas" }
cargo-util-terminal = { version = "0.1.3", path = "crates/cargo-util-terminal" }
cargo_metadata = "0.23.1"
clap = "4.6.0"
Expand Down
2 changes: 1 addition & 1 deletion crates/cargo-util-schemas/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cargo-util-schemas"
version = "0.14.4"
version = "0.15.0"
rust-version = "1.98" # MSRV:1
edition.workspace = true
license.workspace = true
Expand Down
5 changes: 5 additions & 0 deletions crates/cargo-util-schemas/src/core/package_id_spec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,11 @@ fn strip_url_protocol(url: &Url) -> Url {

impl fmt::Display for PackageIdSpec {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.kind() == Some(&SourceKind::Builtin) {
// Builtins have a very specific pkgid output
write!(f, "builtin://.#{}", self.name)?;

@bjorn3 bjorn3 Aug 11, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where does this get exposed? None of the tests contain builtin://. And is this supposed to be roundtripable? The parse code didn't get changed afaict.

View changes since the review

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #16675 (comment) for the parsing code. I was worried it could be exposed through error messages or something but I'm not able to find an example. This pkgid format is described in the RFC and I included it because I judged it as low-risk mainly, but I'm happy to move it to a more comprehensive PR if you'd rather.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fine with keeping it in this PR myself, but I don't know much about the cargo internals.

The pkgid current format seems to requires a single version of every standard library dependency. I added a tidy lint for it a while back, but I don't think it is a policy of the libs team to never ever add multiple versions of a crate. I'm guessing the only parts that would ever end up in the lockfile would be things like core, alloc and std for which I would be very surprised if there were ever two versions of in the same rustc version.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PackageIdSpec's get exposed via cargo pkgid, cargo metadata, json messages

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general yes, but in this PR builtin sourcekinds are not present in the unit-graph. They're replaced by paths from the std resolve. Most ways I found that view pkgids operate on the unit-graph, with some exceptions.

cargo pkgid actually requires a lockfile, and can't print builtins as we make sure they're not emitted in the lockfile.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about cargo metadata?

There are also the places where PackageIdSpecs can be passed in. The only one that I can think of off the top of my head that can take non-local packages is meaningless yet should work: cargo update.

in this PR builtin sourcekinds are not present in the unit-graph. They're replaced by paths from the std resolv

Is that the long term plan or just for this PR?

Potential impacts

  • Source needs to be non-local to get various optimizations because local sources are presumed to be mutable
  • Whether this impacts caching on CI
  • What should be done for trim paths

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is that the long term plan or just for this PR?

I'm not currently sure. My plan for this PR is to ensure consistency with the current -Zbuild-std implementation where possible. Future PRs will address individual subcommands in a more detailed way. For example, cargo update -p std -Zbuild-std doesn't work before or after this PR (but succeeds if there's no lockfile as if -p wasn't passed).

Thanks for the list of impacts, I haven't considered these and have added them to our plan. The main factor I was previously weighing up was between wanting to hide/obscure builtins from the user, but still make them visible to tooling like rust-analyzer which only cares about the unit-graph really.

On Unit::is_local(), there's already an override for std present:

self.pkg.package_id().source_id().is_path() && !self.is_std

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As someone working on tooling to execute Cargo's build plans with other build systems, I absolutely want to get standard library deps in the unit graph for my purposes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As someone working on tooling to execute Cargo's build plans with other build systems, I absolutely want to get standard library deps in the unit graph for my purposes.

Yes, they definitely will be, I'm just not sure whether they should have a Builtin SourceKind or a Path as they do now.

What about cargo metadata?

Oh yes, metadata would do if it printed out builtins, but this PR defers that (1d50217) for a future PR as the behaviour of metadata is an unresolved question on the RFC

There are also the places where PackageIdSpecs can be passed in. The only one that I can think of off the top of my head that can take non-local packages is meaningless yet should work: cargo update.

I opted to leave parsing out for now given it doesn't work on the existing -Zbuild-std implementation (build-std packages aren't in the resolve) and this PR is already quite complex.

There's a lot left out that would pass through the logic of this patch - it's tough to decide what should be in it, and I appreciate it's probably tough to work out if I've forgotten to address something as a reviewer. My approach to this patch is driven by the fact that the existing -Zbuild-std implementation is an experiment, and given in many areas no decision was reached prior to the project goal's RFCs many parts of the implementation are stubs, doing something reasonable in lieu of a decision. Much of the behaviour is untested. I've attempted to cover a reasonable amount of common "good paths" in Cargo without adding new behaviour to -Zbuild-std. The RFCs (and the project goal's work plan) should ensure we address all user-facing behaviour before thinking about stabilisation. I'm not really sure of a better approach for this PR without making it much larger or creating a bunch of extra work.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I understand trying to reduce scope in the first PR.

I will add that from the perspective of the tools I work on, we care very little about whether a dependency was standard library or not --- it isn't a distinction material to actually building the crates. Only distinctions like "library", "binary", "proc macro" and (in the cross-complation case) the platform we are building for matter.

Perhaps we can just quickly vibe up whatever we need on top of that PR and then use that as mere data point for what work comes next.

return Ok(());
}
let mut printed_name = false;
match self.url {
Some(ref url) => {
Expand Down
7 changes: 7 additions & 0 deletions crates/cargo-util-schemas/src/core/source_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ pub enum SourceKind {
LocalRegistry,
/// A directory-based registry.
Directory,
/// Package sources distributed with the rust toolchain
Builtin,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will impact the unique identifier for the packages from this source in cargo's json output when compiling, cargo metadata, cargo <cmd> -p, etc

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll modify there too, and add a note to check the stdout in various use cases. The RFCs often make notes on what the output of various commands will be. Note that builtin doesn't actually appear in Units - they're all Path dependencies by that point.

An interesting point on cargo metadata is that we decided that we have an unresolved question regarding if deps of builtins should be shown on output, which will be a little hard here as they're not attached until unit generation.

@adamgemmell adamgemmell Aug 7, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've opted to implement pkg spec input/output in a later PR. I've added tests for common output commands like metadata/tree. json output (from build --message-format=json) isn't impacted in this PR - builtins do not exist in the unit graph (see this test which isn't changed in the rest of the branch c1f8964). I plan to address that in a future PR but I'm not sure on the best way to do that at this stage.

}

// The hash here is important for what folder packages get downloaded into.
Expand All @@ -40,6 +42,7 @@ impl SourceKind {
SourceKind::SparseRegistry => None,
SourceKind::LocalRegistry => Some("local-registry"),
SourceKind::Directory => Some("directory"),
SourceKind::Builtin => Some("builtin"),
}
}
}
Expand Down Expand Up @@ -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,
}
}
}
Expand Down
77 changes: 77 additions & 0 deletions crates/resolver-tests/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
use std::collections::BTreeMap;
use std::fmt::Debug;
use std::path::Path;
use std::sync::OnceLock;

use cargo::GlobalContext;
use cargo::compiler::standard_lib::detect_sysroot_src_path;
use cargo::util::IntoUrl;
use cargo::util::data_structures::HashMap;
use cargo::workspace::dependency::DepKind;
use cargo::workspace::{Dependency, GitReference, PackageId, SourceId, Summary};

Expand Down Expand Up @@ -87,6 +91,29 @@ impl<T: AsRef<str>, U: AsRef<str>> ToPkgId for (T, U) {
}
}

#[derive(Copy, Clone)]
pub struct BuiltinPid {
pub name: &'static str,
}

impl ToPkgId for BuiltinPid {
fn to_pkgid(&self) -> PackageId {
PackageId::try_new(self.name, "0.0.0", builtin_loc()).unwrap()
}
}

#[derive(Copy, Clone)]
pub struct BuiltinPidWithGctx<'a> {
pub name: &'static str,
pub gctx: &'a GlobalContext,
}

impl<'a> ToPkgId for BuiltinPidWithGctx<'a> {
fn to_pkgid(&self) -> PackageId {
PackageId::try_new(self.name, "0.0.0", builtin_loc_sysroot(self.gctx)).unwrap()
}
}

#[macro_export]
macro_rules! pkg {
($pkgid:expr => [$($deps:expr),* $(,)? ]) => ({
Expand All @@ -108,6 +135,46 @@ fn registry_loc() -> SourceId {
*example_dot
}

fn builtin_loc() -> SourceId {
static LOCAL_PATH: OnceLock<SourceId> = OnceLock::new();
let local_path = LOCAL_PATH.get_or_init(|| {
SourceId::for_builtin(Path::new(&std::env::current_dir().unwrap())).unwrap()
});
*local_path
}

fn builtin_loc_sysroot(gctx: &GlobalContext) -> SourceId {
static LOCAL_PATH: OnceLock<SourceId> = OnceLock::new();
let local_path = LOCAL_PATH.get_or_init(|| {
SourceId::for_builtin(&detect_sysroot_src_path(gctx, None).unwrap()).unwrap()
});
*local_path
}

pub fn gctx_for_build_std() -> GlobalContext {
let mut gctx = GlobalContext::default().unwrap();
gctx.nightly_features_allowed = true;
gctx.configure(
1,
false,
None,
false,
false,
false,
&None,
&["build-std=core".to_owned()],
&[],
)
.unwrap();
let root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../../tests/testsuite/mock-std/library");
let env = HashMap::from_iter([(
"__CARGO_TESTS_ONLY_SRC_ROOT".to_owned(),
root.into_os_string().into_string().unwrap(),
)]);
gctx.set_env(env);
gctx
}

pub fn pkg<T: ToPkgId>(name: T) -> Summary {
pkg_dep(name, Vec::new())
}
Expand Down Expand Up @@ -215,6 +282,10 @@ pub fn dep_loc(name: &str, location: &str) -> Dependency {
Dependency::parse(name, Some("1.0.0"), source_id).unwrap()
}

pub fn dep_builtin(name: &str) -> Dependency {
Dependency::parse(name, None, builtin_loc()).unwrap()
}

pub fn dep_kind(name: &str, kind: DepKind) -> Dependency {
let mut dep = dep(name);
dep.set_kind(kind);
Expand All @@ -235,6 +306,12 @@ pub fn names<P: ToPkgId>(names: &[P]) -> Vec<PackageId> {
names.iter().map(|name| name.to_pkgid()).collect()
}

/// For a set of name specifiers of varying types
#[macro_export]
macro_rules! names {
($($name:expr),* $(,)?) => {&vec![$($name.to_pkgid()),*]};
}

pub fn loc_names(names: &[(&'static str, &'static str)]) -> Vec<PackageId> {
names
.iter()
Expand Down
25 changes: 20 additions & 5 deletions crates/resolver-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,12 @@ pub fn resolve_and_validated_raw(
root_pkg_id: PackageId,
sat_resolver: &mut SatResolver,
) -> CargoResult<Vec<(PackageId, Vec<InternedString>)>> {
let resolve = resolve_with_global_context_raw(
let resolve = resolve_with_raw(
deps.clone(),
registry,
root_pkg_id,
&GlobalContext::default().unwrap(),
ResolveOpts::everything(),
);

match resolve {
Expand Down Expand Up @@ -120,15 +121,31 @@ pub fn resolve_with_global_context(
registry: &[Summary],
gctx: &GlobalContext,
) -> CargoResult<Vec<(PackageId, Vec<InternedString>)>> {
let resolve = resolve_with_global_context_raw(deps, registry, pkg_id("root"), gctx)?;
let resolve = resolve_with_raw(
deps,
registry,
pkg_id("root"),
gctx,
ResolveOpts::everything(),
)?;
Ok(collect_features(&resolve))
}

pub fn resolve_with_global_context_raw(
pub fn resolve_with_gctx_opts(
deps: Vec<Dependency>,
registry: &[Summary],
gctx: &GlobalContext,
opts: ResolveOpts,
) -> CargoResult<Resolve> {
resolve_with_raw(deps, registry, pkg_id("root"), gctx, opts)
}

pub fn resolve_with_raw(
deps: Vec<Dependency>,
registry: &[Summary],
root_pkg_id: PackageId,
gctx: &GlobalContext,
opts: ResolveOpts,
) -> CargoResult<Resolve> {
struct MyRegistry<'a> {
list: &'a [Summary],
Expand Down Expand Up @@ -190,8 +207,6 @@ pub fn resolve_with_global_context_raw(
let root_summary =
Summary::new(root_pkg_id, deps, &BTreeMap::new(), None::<&String>, None).unwrap();

let opts = ResolveOpts::everything();

let start = Instant::now();
let mut version_prefs = VersionPreferences::default();
if gctx.cli_unstable().minimal_versions {
Expand Down
60 changes: 57 additions & 3 deletions crates/resolver-tests/tests/resolve.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use cargo::resolver::ResolveOpts;
use cargo::util::GlobalContext;
use cargo::workspace::Dependency;
use cargo::workspace::dependency::DepKind;
use resolver_tests::helpers::gctx_for_build_std;
use resolver_tests::helpers::{BuiltinPidWithGctx, dep_builtin};
use snapbox::assert_data_eq;
use snapbox::str;

use resolver_tests::{
helpers::{
ToDep, ToPkgId, assert_contains, assert_same, dep, dep_kind, dep_loc, dep_req, loc_names,
names, pkg, pkg_dep, pkg_dep_with, pkg_id, pkg_loc, registry,
BuiltinPid, ToDep, ToPkgId, assert_contains, assert_same, dep, dep_kind, dep_loc, dep_req,
loc_names, names, pkg, pkg_dep, pkg_dep_with, pkg_id, pkg_loc, registry,
},
pkg, resolve, resolve_with_global_context,
names, pkg, resolve, resolve_with_gctx_opts, resolve_with_global_context,
};

#[test]
Expand Down Expand Up @@ -1036,3 +1039,54 @@ failed to select a version for `F` which could resolve this conflict
"#]]
);
}

#[test]
fn test_builtin_dependency() {
let core = BuiltinPid { name: "core" };
let reg = registry(vec![pkg!(core)]);

let builtin_dep = dep_builtin("core");
// All dependencies on Builtins are opaque
assert!(builtin_dep.is_opaque());
let res = resolve(vec![builtin_dep], &reg).unwrap();

assert_same(&res, &names!("root", core));
}

#[test]
fn normal_dependency_is_not_satisfied_by_builtin_package() {
let core = BuiltinPid { name: "core" };
let reg = registry(vec![pkg!(core)]);

assert!(resolve(vec![dep("core")], &reg).is_err());
}

#[test]
fn missing_builtin_dependency_errors() {
assert!(resolve(vec![dep_builtin("core")], &registry(vec![])).is_err());
}

#[test]
fn injects_builtins_when_required() {
let gctx = gctx_for_build_std();

let core = BuiltinPidWithGctx {
name: "core",
gctx: &gctx,
};
let compiler_builtins = BuiltinPidWithGctx {
name: "compiler_builtins",
gctx: &gctx,
};
let reg = registry(vec![pkg!(core), pkg!(compiler_builtins)]);

let mut opts = ResolveOpts::everything();
opts.inject_builtins = true;
let resolve = resolve_with_gctx_opts(Vec::new(), &reg, &gctx, opts).unwrap();

let root_deps = resolve
.deps(pkg_id("root"))
.map(|(pkg_id, _)| pkg_id)
.collect::<Vec<_>>();
assert_same(&root_deps, names!(core, compiler_builtins))
}
2 changes: 0 additions & 2 deletions src/compiler/build_context/target_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,8 +168,6 @@ impl TargetInfo {
/// invocation is cached by [`Rustc::cached_output`].
///
/// Search `Tricky` to learn why querying `rustc` several times is needed.
///
/// When a Workspace is provided,
#[tracing::instrument(skip_all)]
pub fn new(
gctx: &GlobalContext,
Expand Down
25 changes: 16 additions & 9 deletions src/compiler/standard_lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::ops::{self, Packages};
use crate::resolver::HasDevUnits;
use crate::resolver::Resolve;
use crate::resolver::features::{CliFeatures, FeaturesFor, ResolvedFeatures};
use crate::util::errors::CargoResult;
use crate::util::{CargoResult, GlobalContext};
use crate::workspace::profiles::{Profiles, UnitFor};
use crate::workspace::{PackageId, PackageSet, Workspace};

Expand All @@ -16,7 +16,11 @@ use std::path::PathBuf;

use super::BuildConfig;

fn std_crates<'a>(crates: &'a [String], default: &'static str, units: &[Unit]) -> HashSet<&'a str> {
pub fn std_crates<'a>(
crates: &'a [String],
default: &'static str,
units: &[Unit],
) -> HashSet<&'a str> {
let mut crates = HashSet::from_iter(crates.iter().map(|s| s.as_str()));
// This is a temporary hack until there is a more principled way to
// declare dependencies in Cargo.toml.
Expand Down Expand Up @@ -54,12 +58,13 @@ pub fn resolve_std<'gctx>(
crates: &[String],
kinds: &[CompileKind],
) -> CargoResult<(PackageSet<'gctx>, Resolve, ResolvedFeatures)> {
let src_path = detect_sysroot_src_path(ws)?;
let src_path = detect_sysroot_src_path(ws.gctx(), Some(ws))?;
let std_ws_manifest_path = src_path.join("Cargo.toml");
let gctx = ws.gctx();
// TODO: Consider doing something to enforce --locked? Or to prevent the
// lock file from being written, such as setting ephemeral.
let mut std_ws = Workspace::new(&std_ws_manifest_path, gctx)?;
std_ws.set_is_std(true);
// Don't require optional dependencies in this workspace, aka std's own
// `[dev-dependencies]`. No need for us to generate a `Resolve` which has
// those included because we'll never use them anyway.
Expand Down Expand Up @@ -217,15 +222,17 @@ fn generate_roots(
Ok(())
}

fn detect_sysroot_src_path(ws: &Workspace<'_>) -> CargoResult<PathBuf> {
if let Some(s) = ws.gctx().get_env_os("__CARGO_TESTS_ONLY_SRC_ROOT") {
pub fn detect_sysroot_src_path(
gctx: &GlobalContext,
ws: Option<&Workspace<'_>>,
) -> CargoResult<PathBuf> {
if let Some(s) = gctx.get_env_os("__CARGO_TESTS_ONLY_SRC_ROOT") {
return Ok(s.into());
}

// NOTE: This is temporary until we figure out how to acquire the source.
let rustc = ws.gctx().load_global_rustc(Some(ws))?;
let src_path = ws
.gctx()
let rustc = gctx.load_global_rustc(ws)?;
let src_path = gctx
.get_sysroot(&rustc)
.expect("able to invoke rustc")
.join("lib")
Expand All @@ -240,7 +247,7 @@ fn detect_sysroot_src_path(ws: &Workspace<'_>) -> CargoResult<PathBuf> {
library, try:\n rustup component add rust-src",
lock
);
match ws.gctx().get_env("RUSTUP_TOOLCHAIN") {
match gctx.get_env("RUSTUP_TOOLCHAIN") {
Ok(rustup_toolchain) => {
anyhow::bail!("{} --toolchain {}", msg, rustup_toolchain);
}
Expand Down
Loading