diff --git a/src/compiler/build_context/mod.rs b/src/compiler/build_context/mod.rs index 67292e523a1..ce58b53d019 100644 --- a/src/compiler/build_context/mod.rs +++ b/src/compiler/build_context/mod.rs @@ -1,5 +1,7 @@ //! [`BuildContext`] is a (mostly) static information about a build task. +use std::path::Path; + use crate::compiler::BuildConfig; use crate::compiler::CompileKind; use crate::compiler::Unit; @@ -162,6 +164,18 @@ impl<'a, 'gctx> BuildContext<'a, 'gctx> { pub fn extra_args_for(&self, unit: &Unit) -> Option<&Vec> { self.extra_compiler_args.get(unit) } + + /// Gets the path to the sysroot. + /// + /// Helper function that uses GlobalContext. + pub fn get_sysroot(&self) -> &'gctx Path { + // cfg::bad_cfg_discovery tests that these panics aren't reachable + let rustc = self + .gctx + .load_global_rustc(Some(self.ws)) + .expect("rustc load ok"); + self.gctx.get_sysroot(&rustc).expect("sysroot fetch ok") + } } #[derive(Copy, Clone, Default, Debug)] diff --git a/src/compiler/build_context/target_info.rs b/src/compiler/build_context/target_info.rs index c4c1bb6b64e..fc9268e88df 100644 --- a/src/compiler/build_context/target_info.rs +++ b/src/compiler/build_context/target_info.rs @@ -53,8 +53,6 @@ pub struct TargetInfo { pub supports_std: Option, /// Supported values for `-Csplit-debuginfo=` flag, queried from rustc support_split_debuginfo: Vec, - /// Path to the sysroot. - pub sysroot: PathBuf, /// Path to the "lib" directory in the sysroot which rustc uses for linking /// target libraries. pub sysroot_target_libdir: PathBuf, @@ -165,6 +163,8 @@ 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, @@ -174,15 +174,25 @@ impl TargetInfo { ) -> CargoResult { let mut rustflags = extra_args(gctx, requested_kinds, &rustc.host, None, kind, Flags::Rust)?; + + let sysroot = gctx.get_sysroot(rustc)?; + let sysroot_target_libdir = sysroot + .join("lib") + .join("rustlib") + .join(match &kind { + CompileKind::Host => rustc.host.as_str(), + CompileKind::Target(target) => target.short_name(), + }) + .join("lib"); + let mut turn = 0; loop { let extra_fingerprint = kind.fingerprint_hash(); // Query rustc for several kinds of info from each line of output: // 0) file-names (to determine output file prefix/suffix for given crate type) - // 1) sysroot - // 2) split-debuginfo - // 3) cfg + // 1) split-debuginfo + // 2) cfg // // Search `--print` to see what we query so far. let mut process = rustc.workspace_process(); @@ -216,7 +226,6 @@ impl TargetInfo { process.arg("--crate-type").arg(crate_type.as_str()); } - process.arg("--print=sysroot"); process.arg("--print=split-debuginfo"); process.arg("--print=crate-name"); // `___` as a delimiter. process.arg("--print=cfg"); @@ -238,22 +247,6 @@ impl TargetInfo { map.insert(crate_type.clone(), out); } - let Some(line) = lines.next() else { - return error_missing_print_output("sysroot", &process, &output, &error); - }; - let sysroot = PathBuf::from(line); - let sysroot_target_libdir = { - let mut libdir = sysroot.clone(); - libdir.push("lib"); - libdir.push("rustlib"); - libdir.push(match &kind { - CompileKind::Host => rustc.host.as_str(), - CompileKind::Target(target) => target.short_name(), - }); - libdir.push("lib"); - libdir - }; - let support_split_debuginfo = { // HACK: abuse `--print=crate-name` to use `___` as a delimiter. let mut res = Vec::new(); @@ -352,7 +345,6 @@ impl TargetInfo { return Ok(TargetInfo { crate_type_process, crate_types: RefCell::new(map), - sysroot, sysroot_target_libdir, rustflags: rustflags.into(), rustdocflags: extra_args( diff --git a/src/compiler/build_runner/compilation_files.rs b/src/compiler/build_runner/compilation_files.rs index 3285455325d..e2de7212543 100644 --- a/src/compiler/build_runner/compilation_files.rs +++ b/src/compiler/build_runner/compilation_files.rs @@ -735,7 +735,7 @@ fn compute_metadata( // SourceId for stdlib crates is an absolute path inside the sysroot. // Pass the sysroot as workspace root so that we hash a relative path. // This avoids the metadata hash changing depending on where the user installed rustc. - &bcx.target_data.get_info(unit.kind).unwrap().sysroot + &bcx.get_sysroot() } else { bcx.ws.root() }; diff --git a/src/compiler/rustdoc.rs b/src/compiler/rustdoc.rs index 3337e2b37cc..3dbe4aa8171 100644 --- a/src/compiler/rustdoc.rs +++ b/src/compiler/rustdoc.rs @@ -1,8 +1,8 @@ //! Utilities for building with rustdoc. +use crate::compiler::BuildContext; use crate::compiler::build_runner::BuildRunner; use crate::compiler::unit::Unit; -use crate::compiler::{BuildContext, CompileKind}; use crate::sources::CRATES_IO_REGISTRY; use crate::util::data_structures::HashMap; use crate::util::data_structures::HashSet; @@ -208,7 +208,7 @@ pub fn add_root_urls( let std_url = match &map.std { None | Some(RustdocExternMode::Remote) => None, Some(RustdocExternMode::Local) => { - let sysroot = &build_runner.bcx.target_data.info(CompileKind::Host).sysroot; + let sysroot = build_runner.bcx.get_sysroot(); let html_root = sysroot.join("share").join("doc").join("rust").join("html"); if html_root.exists() { let url = Url::from_file_path(&html_root).map_err(|()| { diff --git a/src/compiler/standard_lib.rs b/src/compiler/standard_lib.rs index ba5e1bc12f9..3af824e2cb0 100644 --- a/src/compiler/standard_lib.rs +++ b/src/compiler/standard_lib.rs @@ -54,7 +54,7 @@ pub fn resolve_std<'gctx>( crates: &[String], kinds: &[CompileKind], ) -> CargoResult<(PackageSet<'gctx>, Resolve, ResolvedFeatures)> { - let src_path = detect_sysroot_src_path(target_data)?; + let src_path = detect_sysroot_src_path(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 @@ -217,15 +217,17 @@ fn generate_roots( Ok(()) } -fn detect_sysroot_src_path(target_data: &RustcTargetData<'_>) -> CargoResult { - if let Some(s) = target_data.gctx.get_env_os("__CARGO_TESTS_ONLY_SRC_ROOT") { +fn detect_sysroot_src_path(ws: &Workspace<'_>) -> CargoResult { + if let Some(s) = ws.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 src_path = target_data - .info(CompileKind::Host) - .sysroot + let rustc = ws.gctx().load_global_rustc(Some(ws))?; + let src_path = ws + .gctx() + .get_sysroot(&rustc) + .expect("able to invoke rustc") .join("lib") .join("rustlib") .join("src") @@ -238,7 +240,7 @@ fn detect_sysroot_src_path(target_data: &RustcTargetData<'_>) -> CargoResult { anyhow::bail!("{} --toolchain {}", msg, rustup_toolchain); } diff --git a/src/compiler/trim_paths.rs b/src/compiler/trim_paths.rs index 227b98f0415..0864ba39ffe 100644 --- a/src/compiler/trim_paths.rs +++ b/src/compiler/trim_paths.rs @@ -85,7 +85,7 @@ pub(crate) fn trim_paths_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) [ package_remap(build_runner, unit), build_dir_remap(build_runner), - sysroot_remap(build_runner, unit), + sysroot_remap(build_runner), ] } @@ -93,16 +93,16 @@ pub(crate) fn trim_paths_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) /// /// This remap logic aligns with rustc: /// -fn sysroot_remap(build_runner: &BuildRunner<'_, '_>, unit: &Unit) -> OsString { +fn sysroot_remap(build_runner: &BuildRunner<'_, '_>) -> OsString { let mut remap = OsString::new(); remap.push({ - // See also `detect_sysroot_src_path()`. - let mut sysroot = build_runner.bcx.target_data.info(unit.kind).sysroot.clone(); - sysroot.push("lib"); - sysroot.push("rustlib"); - sysroot.push("src"); - sysroot.push("rust"); - sysroot + build_runner + .bcx + .get_sysroot() + .join("lib") + .join("rustlib") + .join("src") + .join("rust") }); remap.push("="); remap.push("/rustc/"); diff --git a/src/context/mod.rs b/src/context/mod.rs index 00131cfba97..b743ea47168 100644 --- a/src/context/mod.rs +++ b/src/context/mod.rs @@ -225,6 +225,8 @@ pub struct GlobalContext { cargo_exe: OnceLock, /// The location of the rustdoc executable rustdoc: OnceLock, + /// The path to the sysroot + sysroot: OnceLock, /// Whether we are printing extra verbose messages extra_verbose: bool, /// `frozen` is the same as `locked`, but additionally will not access the @@ -379,6 +381,7 @@ impl GlobalContext { cli_config: None, cargo_exe: Default::default(), rustdoc: Default::default(), + sysroot: Default::default(), extra_verbose: false, frozen: false, locked: false, @@ -610,6 +613,13 @@ impl GlobalContext { .map(AsRef::as_ref) } + /// Get the sysroot path. + pub fn get_sysroot<'gctx>(&'gctx self, rustc: &Rustc) -> CargoResult<&'gctx Path> { + self.sysroot + .try_borrow_with(|| rustc.sysroot(self)) + .map(AsRef::as_ref) + } + /// Which package sources have been updated, used to ensure it is only done once. pub fn updated_sources(&self) -> MutexGuard<'_, HashSet> { self.updated_sources.lock().unwrap() diff --git a/src/ops/cargo_fix/mod.rs b/src/ops/cargo_fix/mod.rs index 90aeb7a4b21..4df6c69502d 100644 --- a/src/ops/cargo_fix/mod.rs +++ b/src/ops/cargo_fix/mod.rs @@ -53,7 +53,6 @@ use semver::Version; use tracing::{debug, trace, warn}; pub use self::fix_edition::fix_edition; -use crate::compiler::CompileKind; use crate::compiler::RustcTargetData; use crate::ops::resolve::WorkspaceResolve; use crate::ops::{self, CompileOptions}; @@ -185,7 +184,8 @@ pub fn fix( wrapper.env(IDIOMS_ENV_INTERNAL, "1"); } - let sysroot = &target_data.info(CompileKind::Host).sysroot; + let rustc = gctx.load_global_rustc(Some(original_ws))?; + let sysroot = gctx.get_sysroot(&rustc).expect("able to invoke rustc"); if sysroot.is_dir() { wrapper.env(SYSROOT_INTERNAL, sysroot); } diff --git a/src/util/rustc.rs b/src/util/rustc.rs index 97ff06db7a9..1ab62ae0134 100644 --- a/src/util/rustc.rs +++ b/src/util/rustc.rs @@ -4,7 +4,7 @@ use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; use std::sync::Mutex; -use anyhow::Context as _; +use anyhow::{Context as _, bail}; use cargo_util::{ProcessBuilder, ProcessError, paths}; use filetime::FileTime; use serde::{Deserialize, Serialize}; @@ -64,6 +64,7 @@ impl Rustc { .wrapped(wrapper.as_deref()); apply_env_config(gctx, &mut cmd)?; cmd.env(crate::CARGO_ENV, gctx.cargo_exe()?); + cmd.arg("-vV"); let verbose_version = cache.cached_output(&cmd, 0)?.0; @@ -159,6 +160,21 @@ impl Rustc { .unwrap() .cached_output(cmd, extra_fingerprint) } + + /// Use the rustc executable to fetch the sysroot path. + pub fn sysroot(&self, gctx: &GlobalContext) -> CargoResult { + let mut cmd = self.workspace_process(); + apply_env_config(gctx, &mut cmd)?; + cmd.env(crate::CARGO_ENV, gctx.cargo_exe()?); + cmd.arg("--print=sysroot"); + + let (stdout, _) = self.cached_output(&cmd, 0)?; + let path: PathBuf = stdout.trim().into(); + if !path.exists() { + bail!("sysroot path \"{}\" does not exist", path.display()); + } + Ok(path) + } } /// It is a well known fact that `rustc` is not the fastest compiler in the diff --git a/tests/testsuite/cfg.rs b/tests/testsuite/cfg.rs index c185e3c4a50..a850c652765 100644 --- a/tests/testsuite/cfg.rs +++ b/tests/testsuite/cfg.rs @@ -347,6 +347,13 @@ fn bad_cfg_discovery() { print!("{}", run_rustc()); return; } + if mode == "no-sysroot" { + return; + } + if std::env::args_os().any(|a| a == "--print=sysroot") { + print!("{}", run_rustc()); + return; + } if mode == "no-crate-types" { return; } @@ -356,24 +363,19 @@ fn bad_cfg_discovery() { } let output = run_rustc(); let mut lines = output.lines(); - let sysroot = loop { + let mut line = loop { let line = lines.next().unwrap(); if line.contains("___") { - println!("{}", line); + println!("{line}"); } else { break line; } }; - if mode == "no-sysroot" { - return; - } - println!("{}", sysroot); if mode == "no-split-debuginfo" { return; } loop { - let line = lines.next().unwrap(); if line == "___" { println!("\n{line}"); break; @@ -382,6 +384,7 @@ fn bad_cfg_discovery() { // concat them into one line. print!("{line},"); } + line = lines.next().unwrap(); }; if mode != "bad-cfg" { @@ -411,32 +414,22 @@ foo p.cargo("check") .env("RUSTC", &funky_rustc) - .env("FUNKY_MODE", "no-crate-types") + .env("FUNKY_MODE", "no-sysroot") .with_status(101) .with_stderr_data(str![[r#" -[ERROR] malformed output when learning about crate-type bin information -command was: `[ROOT]/compiler/target/debug/compiler[..] --crate-name ___ [..]` -(no output received) +[ERROR] sysroot path "" does not exist "#]]) .run(); p.cargo("check") .env("RUSTC", &funky_rustc) - .env("FUNKY_MODE", "no-sysroot") + .env("FUNKY_MODE", "no-crate-types") .with_status(101) .with_stderr_data(str![[r#" -[ERROR] output of --print=sysroot missing when learning about target-specific information from rustc -command was: `[ROOT]/compiler/target/debug/compiler[..]--crate-type [..]` - ---- stdout -___[EXE] -lib___.rlib -[..]___.[..] -[..]___.[..] -[..]___.[..] -[..]___.[..] - +[ERROR] malformed output when learning about crate-type bin information +command was: `[ROOT]/compiler/target/debug/compiler[..] --crate-name ___ [..]` +(no output received) "#]]) .run(); @@ -456,7 +449,6 @@ lib___.rlib [..]___.[..] [..]___.[..] [..]___.[..] -[..] "#]]) @@ -474,7 +466,6 @@ lib___.rlib [..]___.[..] [..]___.[..] [..]___.[..] -[..] [..],[..] ___ 123