diff --git a/src/bin/rustup-init.rs b/src/bin/rustup-init.rs index 248198f469..42e1d88d95 100644 --- a/src/bin/rustup-init.rs +++ b/src/bin/rustup-init.rs @@ -27,7 +27,7 @@ use rustup::cli::rustup_mode; #[cfg(windows)] use rustup::cli::self_update; use rustup::cli::setup_mode; -use rustup::currentprocess::{process, with_runtime, Process}; +use rustup::currentprocess::{with_runtime, Process}; use rustup::env_var::RUST_RECURSION_COUNT_MAX; use rustup::errors::RustupError; use rustup::is_proxyable_tools; @@ -40,11 +40,11 @@ fn main() { let process = Process::os(); let mut builder = Builder::new_multi_thread(); builder.enable_all(); - with_runtime(process, builder, { - async { - match maybe_trace_rustup().await { + with_runtime(process.clone(), builder, { + async move { + match maybe_trace_rustup(&process).await { Err(e) => { - common::report_error(&e); + common::report_error(&e, &process); std::process::exit(1); } Ok(utils::ExitCode(c)) => std::process::exit(c), @@ -53,14 +53,14 @@ fn main() { }); } -async fn maybe_trace_rustup() -> Result { +async fn maybe_trace_rustup(process: &Process) -> Result { #[cfg(feature = "otel")] opentelemetry::global::set_text_map_propagator( opentelemetry_sdk::propagation::TraceContextPropagator::new(), ); - let subscriber = rustup::cli::log::tracing_subscriber(process()); + let subscriber = rustup::cli::log::tracing_subscriber(process); tracing::subscriber::set_global_default(subscriber)?; - let result = run_rustup().await; + let result = run_rustup(process).await; // We're tracing, so block until all spans are exported. #[cfg(feature = "otel")] opentelemetry::global::shutdown_tracer_provider(); @@ -68,45 +68,45 @@ async fn maybe_trace_rustup() -> Result { } #[cfg_attr(feature = "otel", tracing::instrument)] -async fn run_rustup() -> Result { - if let Ok(dir) = process().var("RUSTUP_TRACE_DIR") { +async fn run_rustup(process: &Process) -> Result { + if let Ok(dir) = process.var("RUSTUP_TRACE_DIR") { open_trace_file!(dir)?; } - let result = run_rustup_inner().await; - if process().var("RUSTUP_TRACE_DIR").is_ok() { + let result = run_rustup_inner(process).await; + if process.var("RUSTUP_TRACE_DIR").is_ok() { close_trace_file!(); } result } #[cfg_attr(feature = "otel", tracing::instrument(err))] -async fn run_rustup_inner() -> Result { +async fn run_rustup_inner(process: &Process) -> Result { // Guard against infinite proxy recursion. This mostly happens due to // bugs in rustup. - do_recursion_guard()?; + do_recursion_guard(process)?; // Before we do anything else, ensure we know where we are and who we // are because otherwise we cannot proceed usefully. - let current_dir = process() + let current_dir = process .current_dir() .context(RustupError::LocatingWorkingDir)?; utils::current_exe()?; - match process().name().as_deref() { - Some("rustup") => rustup_mode::main(current_dir).await, + match process.name().as_deref() { + Some("rustup") => rustup_mode::main(current_dir, process).await, Some(n) if n.starts_with("rustup-setup") || n.starts_with("rustup-init") => { // NB: The above check is only for the prefix of the file // name. Browsers rename duplicates to // e.g. rustup-setup(2), and this allows all variations // to work. - setup_mode::main(current_dir).await + setup_mode::main(current_dir, process).await } Some(n) if n.starts_with("rustup-gc-") => { // This is the final uninstallation stage on windows where // rustup deletes its own exe cfg_if! { if #[cfg(windows)] { - self_update::complete_windows_uninstall() + self_update::complete_windows_uninstall(process) } else { unreachable!("Attempted to use Windows-specific code on a non-Windows platform. Aborting.") } @@ -114,7 +114,9 @@ async fn run_rustup_inner() -> Result { } Some(n) => { is_proxyable_tools(n)?; - proxy_mode::main(n, current_dir).await.map(ExitCode::from) + proxy_mode::main(n, current_dir, process) + .await + .map(ExitCode::from) } None => { // Weird case. No arg0, or it's unparsable. @@ -123,8 +125,8 @@ async fn run_rustup_inner() -> Result { } } -fn do_recursion_guard() -> Result<()> { - let recursion_count = process() +fn do_recursion_guard(process: &Process) -> Result<()> { + let recursion_count = process .var("RUST_RECURSION_COUNT") .ok() .and_then(|s| s.parse().ok()) diff --git a/src/cli/common.rs b/src/cli/common.rs index bb0000d663..b827b7cda1 100644 --- a/src/cli/common.rs +++ b/src/cli/common.rs @@ -14,7 +14,7 @@ use once_cell::sync::Lazy; use super::self_update; use crate::cli::download_tracker::DownloadTracker; -use crate::currentprocess::{process, terminalsource}; +use crate::currentprocess::{terminalsource, Process}; use crate::dist::dist::{TargetTriple, ToolchainDesc}; use crate::dist::manifest::ComponentStatus; use crate::install::UpdateStatus; @@ -30,10 +30,10 @@ use crate::{ pub(crate) const WARN_COMPLETE_PROFILE: &str = "downloading with complete profile isn't recommended unless you are a developer of the rust language"; -pub(crate) fn confirm(question: &str, default: bool) -> Result { - write!(process().stdout().lock(), "{question} ")?; +pub(crate) fn confirm(question: &str, default: bool, process: &Process) -> Result { + write!(process.stdout().lock(), "{question} ")?; let _ = std::io::stdout().flush(); - let input = read_line()?; + let input = read_line(process)?; let r = match &*input.to_lowercase() { "y" | "yes" => true, @@ -42,7 +42,7 @@ pub(crate) fn confirm(question: &str, default: bool) -> Result { _ => false, }; - writeln!(process().stdout().lock())?; + writeln!(process.stdout().lock())?; Ok(r) } @@ -53,19 +53,19 @@ pub(crate) enum Confirm { Advanced, } -pub(crate) fn confirm_advanced(customized_install: bool) -> Result { - writeln!(process().stdout().lock())?; +pub(crate) fn confirm_advanced(customized_install: bool, process: &Process) -> Result { + writeln!(process.stdout().lock())?; let first_option = match customized_install { true => "1) Proceed with selected options (default - just press enter)", false => "1) Proceed with standard installation (default - just press enter)", }; - writeln!(process().stdout().lock(), "{first_option}")?; - writeln!(process().stdout().lock(), "2) Customize installation")?; - writeln!(process().stdout().lock(), "3) Cancel installation")?; - write!(process().stdout().lock(), ">")?; + writeln!(process.stdout().lock(), "{first_option}")?; + writeln!(process.stdout().lock(), "2) Customize installation")?; + writeln!(process.stdout().lock(), "3) Cancel installation")?; + write!(process.stdout().lock(), ">")?; let _ = std::io::stdout().flush(); - let input = read_line()?; + let input = read_line(process)?; let r = match &*input { "1" | "" => Confirm::Yes, @@ -73,17 +73,17 @@ pub(crate) fn confirm_advanced(customized_install: bool) -> Result { _ => Confirm::No, }; - writeln!(process().stdout().lock())?; + writeln!(process.stdout().lock())?; Ok(r) } -pub(crate) fn question_str(question: &str, default: &str) -> Result { - writeln!(process().stdout().lock(), "{question} [{default}]")?; +pub(crate) fn question_str(question: &str, default: &str, process: &Process) -> Result { + writeln!(process.stdout().lock(), "{question} [{default}]")?; let _ = std::io::stdout().flush(); - let input = read_line()?; + let input = read_line(process)?; - writeln!(process().stdout().lock())?; + writeln!(process.stdout().lock())?; if input.is_empty() { Ok(default.to_string()) @@ -92,14 +92,14 @@ pub(crate) fn question_str(question: &str, default: &str) -> Result { } } -pub(crate) fn question_bool(question: &str, default: bool) -> Result { +pub(crate) fn question_bool(question: &str, default: bool, process: &Process) -> Result { let default_text = if default { "(Y/n)" } else { "(y/N)" }; - writeln!(process().stdout().lock(), "{question} {default_text}")?; + writeln!(process.stdout().lock(), "{question} {default_text}")?; let _ = std::io::stdout().flush(); - let input = read_line()?; + let input = read_line(process)?; - writeln!(process().stdout().lock())?; + writeln!(process.stdout().lock())?; if input.is_empty() { Ok(default) @@ -112,8 +112,8 @@ pub(crate) fn question_bool(question: &str, default: bool) -> Result { } } -pub(crate) fn read_line() -> Result { - let stdin = process().stdin(); +pub(crate) fn read_line(process: &Process) -> Result { + let stdin = process.stdin(); let stdin = stdin.lock(); let mut lines = stdin.lines(); let lines = lines.next().transpose()?; @@ -131,9 +131,9 @@ pub(super) struct Notifier { } impl Notifier { - pub(super) fn new(verbose: bool, quiet: bool) -> Self { + pub(super) fn new(verbose: bool, quiet: bool, process: &Process) -> Self { Self { - tracker: Mutex::new(DownloadTracker::new_with_display_progress(!quiet)), + tracker: Mutex::new(DownloadTracker::new_with_display_progress(!quiet, process)), ram_notice_shown: RefCell::new(false), verbose, } @@ -180,13 +180,18 @@ impl Notifier { } #[cfg_attr(feature = "otel", tracing::instrument)] -pub(crate) fn set_globals(current_dir: PathBuf, verbose: bool, quiet: bool) -> Result { - let notifier = Notifier::new(verbose, quiet); - Cfg::from_env(current_dir, Arc::new(move |n| notifier.handle(n))) +pub(crate) fn set_globals( + current_dir: PathBuf, + verbose: bool, + quiet: bool, + process: &Process, +) -> Result> { + let notifier = Notifier::new(verbose, quiet, process); + Cfg::from_env(current_dir, Arc::new(move |n| notifier.handle(n)), process) } pub(crate) fn show_channel_update( - cfg: &Cfg, + cfg: &Cfg<'_>, name: PackageUpdate, updated: Result, ) -> Result<()> { @@ -208,7 +213,7 @@ impl Display for PackageUpdate { } fn show_channel_updates( - cfg: &Cfg, + cfg: &Cfg<'_>, updates: Vec<(PackageUpdate, Result)>, ) -> Result<()> { let data = updates.into_iter().map(|(pkg, result)| { @@ -254,7 +259,7 @@ fn show_channel_updates( Ok((pkg, banner, width, color, version, previous_version)) }); - let mut t = process().stdout().terminal(); + let mut t = cfg.process.stdout().terminal(cfg.process); let data: Vec<_> = data.collect::>()?; let max_width = data @@ -283,7 +288,7 @@ fn show_channel_updates( } pub(crate) async fn update_all_channels( - cfg: &Cfg, + cfg: &Cfg<'_>, do_self_update: bool, force_update: bool, ) -> Result { @@ -295,7 +300,7 @@ pub(crate) async fn update_all_channels( let show_channel_updates = || { if !toolchains.is_empty() { - writeln!(process().stdout().lock())?; + writeln!(cfg.process.stdout().lock())?; let t = toolchains .into_iter() @@ -307,7 +312,7 @@ pub(crate) async fn update_all_channels( }; if do_self_update { - self_update(show_channel_updates).await + self_update(show_channel_updates, cfg.process).await } else { show_channel_updates() } @@ -348,7 +353,7 @@ pub(crate) fn self_update_permitted(explicit: bool) -> Result(before_restart: F) -> Result +pub(crate) async fn self_update(before_restart: F, process: &Process) -> Result where F: FnOnce() -> Result, { @@ -361,7 +366,7 @@ where SelfUpdatePermission::Permit => {} } - let setup_path = self_update::prepare_update().await?; + let setup_path = self_update::prepare_update(process).await?; before_restart()?; @@ -369,7 +374,7 @@ where return self_update::run_update(setup_path); } else { // Try again in case we emitted "tool `{}` is already installed" last time. - self_update::install_proxies()?; + self_update::install_proxies(process)?; } Ok(utils::ExitCode(0)) @@ -380,8 +385,9 @@ pub(super) fn list_items( f: impl Fn(&ComponentStatus) -> Option<&str>, installed_only: bool, quiet: bool, + process: &Process, ) -> Result { - let mut t = process().stdout().terminal(); + let mut t = process.stdout().terminal(process); for component in distributable.components()? { let Some(name) = f(&component) else { continue }; match (component.available, component.installed, installed_only) { @@ -400,10 +406,14 @@ pub(super) fn list_items( Ok(utils::ExitCode(0)) } -pub(crate) fn list_toolchains(cfg: &Cfg, verbose: bool, quiet: bool) -> Result { +pub(crate) fn list_toolchains( + cfg: &Cfg<'_>, + verbose: bool, + quiet: bool, +) -> Result { let toolchains = cfg.list_toolchains()?; if toolchains.is_empty() { - writeln!(process().stdout().lock(), "no installed toolchains")?; + writeln!(cfg.process.stdout().lock(), "no installed toolchains")?; } else { let default_toolchain_name = cfg.get_default()?; let active_toolchain_name: Option = @@ -432,7 +442,7 @@ pub(crate) fn list_toolchains(cfg: &Cfg, verbose: bool, quiet: bool) -> Result, toolchain: &str, is_default: bool, is_active: bool, @@ -440,7 +450,7 @@ pub(crate) fn list_toolchains(cfg: &Cfg, verbose: bool, quiet: bool) -> Result Result<()> { if quiet { - writeln!(process().stdout().lock(), "{toolchain}")?; + writeln!(cfg.process.stdout().lock(), "{toolchain}")?; return Ok(()); } @@ -463,7 +473,7 @@ pub(crate) fn list_toolchains(cfg: &Cfg, verbose: bool, quiet: bool) -> Result Result Result { +pub(crate) fn list_overrides(cfg: &Cfg<'_>) -> Result { let overrides = cfg.settings_file.with(|s| Ok(s.overrides.clone()))?; if overrides.is_empty() { - writeln!(process().stdout().lock(), "no overrides")?; + writeln!(cfg.process.stdout().lock(), "no overrides")?; } else { let mut any_not_exist = false; for (k, v) in overrides { @@ -488,7 +498,7 @@ pub(crate) fn list_overrides(cfg: &Cfg) -> Result { any_not_exist = true; } writeln!( - process().stdout().lock(), + cfg.process.stdout().lock(), "{:<40}\t{:<20}", utils::format_path_for_display(&k) + if dir_exists { "" } else { " (not a directory)" }, @@ -496,7 +506,7 @@ pub(crate) fn list_overrides(cfg: &Cfg) -> Result { )? } if any_not_exist { - writeln!(process().stdout().lock())?; + writeln!(cfg.process.stdout().lock())?; info!( "you may remove overrides for non-existent directories with `rustup override unset --nonexistent`" @@ -516,54 +526,50 @@ pub(crate) fn version() -> &'static str { &RENDERED } -pub(crate) fn dump_testament() -> Result { +pub(crate) fn dump_testament(process: &Process) -> Result { use git_testament::GitModification::*; writeln!( - process().stdout().lock(), + process.stdout().lock(), "Rustup version renders as: {}", version() )?; writeln!( - process().stdout().lock(), + process.stdout().lock(), "Current crate version: {}", env!("CARGO_PKG_VERSION") )?; if TESTAMENT.branch_name.is_some() { writeln!( - process().stdout().lock(), + process.stdout().lock(), "Built from branch: {}", TESTAMENT.branch_name.unwrap() )?; } else { - writeln!(process().stdout().lock(), "Branch information missing")?; + writeln!(process.stdout().lock(), "Branch information missing")?; } - writeln!( - process().stdout().lock(), - "Commit info: {}", - TESTAMENT.commit - )?; + writeln!(process.stdout().lock(), "Commit info: {}", TESTAMENT.commit)?; if TESTAMENT.modifications.is_empty() { - writeln!(process().stdout().lock(), "Working tree is clean")?; + writeln!(process.stdout().lock(), "Working tree is clean")?; } else { for fmod in TESTAMENT.modifications { match fmod { Added(f) => writeln!( - process().stdout().lock(), + process.stdout().lock(), "Added: {}", String::from_utf8_lossy(f) )?, Removed(f) => writeln!( - process().stdout().lock(), + process.stdout().lock(), "Removed: {}", String::from_utf8_lossy(f) )?, Modified(f) => writeln!( - process().stdout().lock(), + process.stdout().lock(), "Modified: {}", String::from_utf8_lossy(f) )?, Untracked(f) => writeln!( - process().stdout().lock(), + process.stdout().lock(), "Untracked: {}", String::from_utf8_lossy(f) )?, @@ -573,16 +579,16 @@ pub(crate) fn dump_testament() -> Result { Ok(utils::ExitCode(0)) } -fn show_backtrace() -> bool { - if let Ok(true) = process().var("RUSTUP_NO_BACKTRACE").map(|s| s == "1") { +fn show_backtrace(process: &Process) -> bool { + if let Ok(true) = process.var("RUSTUP_NO_BACKTRACE").map(|s| s == "1") { return false; } - if let Ok(true) = process().var("RUST_BACKTRACE").map(|s| s == "1") { + if let Ok(true) = process.var("RUST_BACKTRACE").map(|s| s == "1") { return true; } - for arg in process().args() { + for arg in process.args() { if arg == "-v" || arg == "--verbose" { return true; } @@ -591,26 +597,30 @@ fn show_backtrace() -> bool { false } -pub fn report_error(e: &anyhow::Error) { +pub fn report_error(e: &anyhow::Error, process: &Process) { // NB: This shows one error: even for multiple causes and backtraces etc, // rather than one per cause, and one for the backtrace. This seems like a // reasonable tradeoff, but if we want to do differently, this is the code // hunk to revisit, that and a similar build.rs auto-detect glue as anyhow // has to detect when backtrace is available. - if show_backtrace() { + if show_backtrace(process) { err!("{:?}", e); } else { err!("{:#}", e); } } -pub(crate) fn ignorable_error(error: &'static str, no_prompt: bool) -> Result<()> { +pub(crate) fn ignorable_error( + error: &'static str, + no_prompt: bool, + process: &Process, +) -> Result<()> { let error = anyhow!(error); - report_error(&error); + report_error(&error, process); if no_prompt { warn!("continuing (because the -y flag is set and the error is ignorable)"); Ok(()) - } else if confirm("\nContinue? (y/N)", false).unwrap_or(false) { + } else if confirm("\nContinue? (y/N)", false, process).unwrap_or(false) { Ok(()) } else { Err(error) @@ -618,11 +628,11 @@ pub(crate) fn ignorable_error(error: &'static str, no_prompt: bool) -> Result<() } /// Warns if rustup is running under emulation, such as macOS Rosetta -pub(crate) fn warn_if_host_is_emulated() { +pub(crate) fn warn_if_host_is_emulated(process: &Process) { if TargetTriple::is_host_emulated() { warn!( "Rustup is not running natively. It's running under emulation of {}.", - TargetTriple::from_host_or_build() + TargetTriple::from_host_or_build(process) ); warn!("For best compatibility and performance you should reinstall rustup for your native CPU."); } diff --git a/src/cli/download_tracker.rs b/src/cli/download_tracker.rs index 89e0aeebd5..6c66b95d91 100644 --- a/src/cli/download_tracker.rs +++ b/src/cli/download_tracker.rs @@ -3,7 +3,7 @@ use std::fmt; use std::io::Write; use std::time::{Duration, Instant}; -use crate::currentprocess::{process, terminalsource}; +use crate::currentprocess::{terminalsource, Process}; use crate::dist::Notification as In; use crate::notifications::Notification; use crate::utils::units::{Size, Unit, UnitMode}; @@ -45,11 +45,12 @@ pub(crate) struct DownloadTracker { units: Vec, /// Whether we display progress display_progress: bool, + stdout_is_a_tty: bool, } impl DownloadTracker { /// Creates a new DownloadTracker. - pub(crate) fn new_with_display_progress(display_progress: bool) -> Self { + pub(crate) fn new_with_display_progress(display_progress: bool, process: &Process) -> Self { Self { content_len: None, total_downloaded: 0, @@ -57,10 +58,11 @@ impl DownloadTracker { downloaded_last_few_secs: VecDeque::with_capacity(DOWNLOAD_TRACK_COUNT), start_sec: None, last_sec: None, - term: process().stdout().terminal(), + term: process.stdout().terminal(process), displayed_charcount: None, units: vec![Unit::B], display_progress, + stdout_is_a_tty: process.stdout().is_a_tty(process), } } @@ -72,7 +74,7 @@ impl DownloadTracker { true } Notification::Install(In::Utils(Un::DownloadDataReceived(data))) => { - if process().stdout().is_a_tty() { + if self.stdout_is_a_tty { self.data_received(data.len()); } true diff --git a/src/cli/log.rs b/src/cli/log.rs index 84d7b0b3c2..dc8cbc7559 100644 --- a/src/cli/log.rs +++ b/src/cli/log.rs @@ -38,11 +38,11 @@ macro_rules! err { ( $ ( $ arg : tt ) * ) => ( ::tracing::error ! ( $ ( $ arg ) * ) ) } -pub fn tracing_subscriber(process: Process) -> impl tracing::Subscriber { +pub fn tracing_subscriber(process: &Process) -> impl tracing::Subscriber { use tracing_subscriber::{layer::SubscriberExt, Registry}; #[cfg(feature = "otel")] - let telemetry = telemetry(&process); + let telemetry = telemetry(process); let console_logger = console_logger(process); #[cfg(feature = "otel")] { @@ -60,7 +60,7 @@ pub fn tracing_subscriber(process: Process) -> impl tracing::Subscriber { /// When the `RUST_LOG` environment variable is present, a standard [`tracing_subscriber`] /// formatter will be used according to the filtering directives set in its value. /// Otherwise, this logger will use [`EventFormatter`] to mimic "classic" Rustup `stderr` output. -fn console_logger(process: Process) -> impl Layer +fn console_logger(process: &Process) -> impl Layer where S: Subscriber + for<'span> LookupSpan<'span>, { @@ -69,9 +69,10 @@ where Ok(s) if s.eq_ignore_ascii_case("never") => false, // `RUSTUP_TERM_COLOR` is prioritized over `NO_COLOR`. _ if process.var("NO_COLOR").is_ok() => false, - _ => process.stderr().is_a_tty(), + _ => process.stderr().is_a_tty(process), }; let maybe_rust_log_directives = process.var("RUST_LOG"); + let process = process.clone(); let logger = tracing_subscriber::fmt::layer() .with_writer(move || process.stderr()) .with_ansi(has_ansi); diff --git a/src/cli/proxy_mode.rs b/src/cli/proxy_mode.rs index 1a81999e5f..8e12e906f1 100644 --- a/src/cli/proxy_mode.rs +++ b/src/cli/proxy_mode.rs @@ -6,17 +6,15 @@ use crate::toolchain::toolchain::Toolchain; use crate::{ cli::{common::set_globals, job, self_update}, command::run_command_for_dir, - currentprocess::process, + currentprocess::Process, toolchain::names::ResolvableLocalToolchainName, }; #[cfg_attr(feature = "otel", tracing::instrument)] -pub async fn main(arg0: &str, current_dir: PathBuf) -> Result { - self_update::cleanup_self_updater()?; +pub async fn main(arg0: &str, current_dir: PathBuf, process: &Process) -> Result { + self_update::cleanup_self_updater(process)?; let _setup = job::setup(); - - let process = process(); let mut args = process.args_os().skip(1); // Check for a + toolchain specifier @@ -29,12 +27,12 @@ pub async fn main(arg0: &str, current_dir: PathBuf) -> Result { .transpose()?; // Build command args now while we know whether or not to skip arg 1. - let cmd_args: Vec<_> = crate::currentprocess::process() + let cmd_args: Vec<_> = process .args_os() .skip(1 + toolchain.is_some() as usize) .collect(); - let cfg = set_globals(current_dir, false, true)?; + let cfg = set_globals(current_dir, false, true, process)?; cfg.check_metadata_version()?; let toolchain = toolchain .map(|t| t.resolve(&cfg.get_default_host_triple()?)) diff --git a/src/cli/rustup_mode.rs b/src/cli/rustup_mode.rs index d7abbae46c..99fa3719e3 100644 --- a/src/cli/rustup_mode.rs +++ b/src/cli/rustup_mode.rs @@ -20,8 +20,8 @@ use crate::{ command, config::{ActiveReason, Cfg}, currentprocess::{ - process, terminalsource::{self, ColorableTerminal}, + Process, }, dist::{ dist::{PartialToolchainDesc, Profile, TargetTriple}, @@ -525,28 +525,29 @@ enum SetSubcmd { }, } -#[cfg_attr(feature = "otel", tracing::instrument(fields(args = format!("{:?}", process().args_os().collect::>()))))] -pub async fn main(current_dir: PathBuf) -> Result { - self_update::cleanup_self_updater()?; +#[cfg_attr(feature = "otel", tracing::instrument(fields(args = format!("{:?}", process.args_os().collect::>()))))] +pub async fn main(current_dir: PathBuf, process: &Process) -> Result { + self_update::cleanup_self_updater(process)?; use clap::error::ErrorKind::*; - let matches = match Rustup::try_parse_from(process().args_os()) { + let matches = match Rustup::try_parse_from(process.args_os()) { Ok(matches) => Ok(matches), Err(err) if err.kind() == DisplayHelp => { - write!(process().stdout().lock(), "{err}")?; + write!(process.stdout().lock(), "{err}")?; return Ok(utils::ExitCode(0)); } Err(err) if err.kind() == DisplayVersion => { - write!(process().stdout().lock(), "{err}")?; + write!(process.stdout().lock(), "{err}")?; info!("This is the version for the rustup toolchain manager, not the rustc compiler."); #[cfg_attr(feature = "otel", tracing::instrument)] async fn rustc_version( current_dir: PathBuf, + process: &Process, ) -> std::result::Result> { - let cfg = &mut common::set_globals(current_dir, false, true)?; + let cfg = &mut common::set_globals(current_dir, false, true, process)?; - if let Some(t) = process().args().find(|x| x.starts_with('+')) { + if let Some(t) = process.args().find(|x| x.starts_with('+')) { debug!("Fetching rustc version from toolchain `{}`", t); cfg.set_toolchain_override(&ResolvableToolchainName::try_from(&t[1..])?); } @@ -556,7 +557,7 @@ pub async fn main(current_dir: PathBuf) -> Result { Ok(toolchain.rustc_version()) } - match rustc_version(current_dir).await { + match rustc_version(current_dir, process).await { Ok(version) => info!("The currently active `rustc` version is `{}`", version), Err(err) => debug!("Wanted to tell you the current rustc version, too, but ran into this error: {}", err), } @@ -571,17 +572,17 @@ pub async fn main(current_dir: PathBuf) -> Result { ] .contains(&err.kind()) { - write!(process().stdout().lock(), "{err}")?; + write!(process.stdout().lock(), "{err}")?; return Ok(utils::ExitCode(1)); } if err.kind() == ValueValidation && err.to_string().contains(TOOLCHAIN_OVERRIDE_ERROR) { - write!(process().stderr().lock(), "{err}")?; + write!(process.stderr().lock(), "{err}")?; return Ok(utils::ExitCode(1)); } Err(err) } }?; - let cfg = &mut common::set_globals(current_dir, matches.verbose, matches.quiet)?; + let cfg = &mut common::set_globals(current_dir, matches.verbose, matches.quiet, process)?; if let Some(t) = &matches.plus_toolchain { cfg.set_toolchain_override(t); @@ -595,7 +596,7 @@ pub async fn main(current_dir: PathBuf) -> Result { }; match subcmd { - RustupSubcmd::DumpTestament => common::dump_testament(), + RustupSubcmd::DumpTestament => common::dump_testament(process), RustupSubcmd::Install { opts } => update(cfg, opts).await, RustupSubcmd::Uninstall { opts } => toolchain_remove(cfg, opts), RustupSubcmd::Show { verbose, subcmd } => handle_epipe(match subcmd { @@ -603,7 +604,7 @@ pub async fn main(current_dir: PathBuf) -> Result { Some(ShowSubcmd::ActiveToolchain { verbose }) => show_active_toolchain(cfg, verbose), Some(ShowSubcmd::Home) => show_rustup_home(cfg), Some(ShowSubcmd::Profile) => { - writeln!(process().stdout().lock(), "{}", cfg.get_profile()?)?; + writeln!(process.stdout().lock(), "{}", cfg.get_profile()?)?; Ok(ExitCode(0)) } }), @@ -692,7 +693,7 @@ pub async fn main(current_dir: PathBuf) -> Result { RustupSubcmd::Man { command, toolchain } => man(cfg, &command, toolchain).await, RustupSubcmd::Self_ { subcmd } => match subcmd { SelfSubcmd::Update => self_update::update(cfg).await, - SelfSubcmd::Uninstall { no_prompt } => self_update::uninstall(no_prompt), + SelfSubcmd::Uninstall { no_prompt } => self_update::uninstall(no_prompt, process), SelfSubcmd::UpgradeData => cfg.upgrade_data().map(|_| ExitCode(0)), }, RustupSubcmd::Set { subcmd } => match subcmd { @@ -706,15 +707,17 @@ pub async fn main(current_dir: PathBuf) -> Result { auto_self_update_mode, } => set_auto_self_update(cfg, auto_self_update_mode), }, - RustupSubcmd::Completions { shell, command } => output_completion_script(shell, command), + RustupSubcmd::Completions { shell, command } => { + output_completion_script(shell, command, process) + } } } async fn default_( - cfg: &Cfg, + cfg: &Cfg<'_>, toolchain: Option, ) -> Result { - common::warn_if_host_is_emulated(); + common::warn_if_host_is_emulated(cfg.process); if let Some(toolchain) = toolchain { match toolchain.to_owned() { @@ -731,7 +734,7 @@ async fn default_( cfg.set_default(Some(&(&desc).into()))?; - writeln!(process().stdout().lock())?; + writeln!(cfg.process.stdout().lock())?; common::show_channel_update(cfg, PackageUpdate::Toolchain(desc), Ok(status))?; } @@ -745,10 +748,10 @@ async fn default_( } else { match cfg.get_default()? { Some(default_toolchain) => { - writeln!(process().stdout().lock(), "{default_toolchain} (default)")?; + writeln!(cfg.process.stdout().lock(), "{default_toolchain} (default)")?; } None => writeln!( - process().stdout().lock(), + cfg.process.stdout().lock(), "no default toolchain is configured" )?, } @@ -757,8 +760,8 @@ async fn default_( Ok(utils::ExitCode(0)) } -async fn check_updates(cfg: &Cfg) -> Result { - let mut t = process().stdout().terminal(); +async fn check_updates(cfg: &Cfg<'_>) -> Result { + let mut t = cfg.process.stdout().terminal(cfg.process); let channels = cfg.list_channels()?; for channel in channels { @@ -793,13 +796,13 @@ async fn check_updates(cfg: &Cfg) -> Result { } } - check_rustup_update().await?; + check_rustup_update(cfg.process).await?; Ok(utils::ExitCode(0)) } -async fn update(cfg: &mut Cfg, opts: UpdateOpts) -> Result { - common::warn_if_host_is_emulated(); +async fn update(cfg: &mut Cfg<'_>, opts: UpdateOpts) -> Result { + common::warn_if_host_is_emulated(cfg.process); let self_update_mode = cfg.get_self_update_mode()?; // Priority: no-self-update feature > self_update_mode > no-self-update args. // Update only if rustup does **not** have the no-self-update feature, @@ -821,7 +824,7 @@ async fn update(cfg: &mut Cfg, opts: UpdateOpts) -> Result { for name in names { // This needs another pass to fix it all up if name.has_triple() { - let host_arch = TargetTriple::from_host_or_build(); + let host_arch = TargetTriple::from_host_or_build(cfg.process); let target_triple = name.clone().resolve(&host_arch)?.target; if !forced && !host_arch.can_run(&target_triple)? { @@ -864,7 +867,7 @@ async fn update(cfg: &mut Cfg, opts: UpdateOpts) -> Result { Err(e) => Err(e)?, }; - writeln!(process().stdout().lock())?; + writeln!(cfg.process.stdout().lock())?; common::show_channel_update( cfg, PackageUpdate::Toolchain(desc.clone()), @@ -875,7 +878,7 @@ async fn update(cfg: &mut Cfg, opts: UpdateOpts) -> Result { } } if self_update { - common::self_update(|| Ok(utils::ExitCode(0))).await?; + common::self_update(|| Ok(utils::ExitCode(0)), cfg.process).await?; } } else { common::update_all_channels(cfg, self_update, opts.force).await?; @@ -885,7 +888,7 @@ async fn update(cfg: &mut Cfg, opts: UpdateOpts) -> Result { } if !self_update::NEVER_SELF_UPDATE && self_update_mode == SelfUpdateMode::CheckOnly { - check_rustup_update().await?; + check_rustup_update(cfg.process).await?; } if self_update::NEVER_SELF_UPDATE { @@ -897,7 +900,7 @@ async fn update(cfg: &mut Cfg, opts: UpdateOpts) -> Result { } async fn run( - cfg: &Cfg, + cfg: &Cfg<'_>, toolchain: ResolvableLocalToolchainName, command: Vec, install: bool, @@ -909,7 +912,7 @@ async fn run( } async fn which( - cfg: &Cfg, + cfg: &Cfg<'_>, binary: &str, toolchain: Option, ) -> Result { @@ -923,17 +926,17 @@ async fn which( utils::assert_is_file(&binary_path)?; - writeln!(process().stdout().lock(), "{}", binary_path.display())?; + writeln!(cfg.process.stdout().lock(), "{}", binary_path.display())?; Ok(utils::ExitCode(0)) } #[cfg_attr(feature = "otel", tracing::instrument(skip_all))] -fn show(cfg: &Cfg, verbose: bool) -> Result { - common::warn_if_host_is_emulated(); +fn show(cfg: &Cfg<'_>, verbose: bool) -> Result { + common::warn_if_host_is_emulated(cfg.process); // Print host triple { - let mut t = process().stdout().terminal(); + let mut t = cfg.process.stdout().terminal(cfg.process); t.attr(terminalsource::Attr::Bold)?; write!(t.lock(), "Default host: ")?; t.reset()?; @@ -942,7 +945,7 @@ fn show(cfg: &Cfg, verbose: bool) -> Result { // Print rustup home directory { - let mut t = process().stdout().terminal(); + let mut t = cfg.process.stdout().terminal(cfg.process); t.attr(terminalsource::Attr::Bold)?; write!(t.lock(), "rustup home: ")?; t.reset()?; @@ -983,7 +986,7 @@ fn show(cfg: &Cfg, verbose: bool) -> Result { // show installed toolchains { - let mut t = process().stdout().terminal(); + let mut t = cfg.process.stdout().terminal(cfg.process); print_header::(&mut t, "installed toolchains")?; @@ -1005,11 +1008,15 @@ fn show(cfg: &Cfg, verbose: bool) -> Result { if verbose { let toolchain = Toolchain::new(cfg, toolchain_name.into())?; - writeln!(process().stdout().lock(), " {}", toolchain.rustc_version())?; + writeln!( + cfg.process.stdout().lock(), + " {}", + toolchain.rustc_version() + )?; // To make it easy to see which rustc belongs to which // toolchain, we separate each pair with an extra newline. if n != last_index { - writeln!(process().stdout().lock())?; + writeln!(cfg.process.stdout().lock())?; } } } @@ -1017,7 +1024,7 @@ fn show(cfg: &Cfg, verbose: bool) -> Result { // show active toolchain { - let mut t = process().stdout().terminal(); + let mut t = cfg.process.stdout().terminal(cfg.process); writeln!(t.lock())?; @@ -1065,37 +1072,40 @@ fn show(cfg: &Cfg, verbose: bool) -> Result { } #[cfg_attr(feature = "otel", tracing::instrument(skip_all))] -fn show_active_toolchain(cfg: &Cfg, verbose: bool) -> Result { +fn show_active_toolchain(cfg: &Cfg<'_>, verbose: bool) -> Result { match cfg.find_active_toolchain()? { Some((toolchain_name, reason)) => { let toolchain = Toolchain::with_reason(cfg, toolchain_name.clone(), &reason)?; writeln!( - process().stdout().lock(), + cfg.process.stdout().lock(), "{}\nactive because: {}", toolchain.name(), reason )?; if verbose { writeln!( - process().stdout().lock(), + cfg.process.stdout().lock(), "compiler: {}", toolchain.rustc_version() )?; } } - None => writeln!(process().stdout().lock(), "There isn't an active toolchain")?, + None => writeln!( + cfg.process.stdout().lock(), + "There isn't an active toolchain" + )?, } Ok(utils::ExitCode(0)) } #[cfg_attr(feature = "otel", tracing::instrument(skip_all))] -fn show_rustup_home(cfg: &Cfg) -> Result { - writeln!(process().stdout().lock(), "{}", cfg.rustup_dir.display())?; +fn show_rustup_home(cfg: &Cfg<'_>) -> Result { + writeln!(cfg.process.stdout().lock(), "{}", cfg.rustup_dir.display())?; Ok(utils::ExitCode(0)) } async fn target_list( - cfg: &Cfg, + cfg: &Cfg<'_>, toolchain: Option, installed_only: bool, quiet: bool, @@ -1114,11 +1124,12 @@ async fn target_list( }, installed_only, quiet, + cfg.process, ) } async fn target_add( - cfg: &Cfg, + cfg: &Cfg<'_>, mut targets: Vec, toolchain: Option, ) -> Result { @@ -1167,7 +1178,7 @@ async fn target_add( } async fn target_remove( - cfg: &Cfg, + cfg: &Cfg<'_>, targets: Vec, toolchain: Option, ) -> Result { @@ -1201,19 +1212,25 @@ async fn target_remove( } async fn component_list( - cfg: &Cfg, + cfg: &Cfg<'_>, toolchain: Option, installed_only: bool, quiet: bool, ) -> Result { // downcasting required because the toolchain files can name any toolchain let distributable = DistributableToolchain::from_partial(toolchain, cfg).await?; - common::list_items(distributable, |c| Some(&c.name), installed_only, quiet)?; + common::list_items( + distributable, + |c| Some(&c.name), + installed_only, + quiet, + cfg.process, + )?; Ok(utils::ExitCode(0)) } async fn component_add( - cfg: &Cfg, + cfg: &Cfg<'_>, components: Vec, toolchain: Option, target: Option, @@ -1239,7 +1256,7 @@ fn get_target( } async fn component_remove( - cfg: &Cfg, + cfg: &Cfg<'_>, components: Vec, toolchain: Option, target: Option, @@ -1256,7 +1273,7 @@ async fn component_remove( } async fn toolchain_link( - cfg: &Cfg, + cfg: &Cfg<'_>, dest: &CustomToolchainName, src: &Path, ) -> Result { @@ -1286,7 +1303,7 @@ async fn toolchain_link( Ok(utils::ExitCode(0)) } -fn toolchain_remove(cfg: &mut Cfg, opts: UninstallOpts) -> Result { +fn toolchain_remove(cfg: &mut Cfg<'_>, opts: UninstallOpts) -> Result { for toolchain_name in &opts.toolchain { let toolchain_name = toolchain_name.resolve(&cfg.get_default_host_triple()?)?; Toolchain::ensure_removed(cfg, (&toolchain_name).into())?; @@ -1295,7 +1312,7 @@ fn toolchain_remove(cfg: &mut Cfg, opts: UninstallOpts) -> Result, toolchain: ResolvableToolchainName, path: Option<&Path>, ) -> Result { @@ -1309,7 +1326,7 @@ async fn override_add( DistributableToolchain::install(cfg, desc, &[], &[], cfg.get_profile()?, false) .await? .0; - writeln!(process().stdout().lock())?; + writeln!(cfg.process.stdout().lock())?; common::show_channel_update( cfg, PackageUpdate::Toolchain(desc.clone()), @@ -1324,7 +1341,11 @@ async fn override_add( Ok(utils::ExitCode(0)) } -fn override_remove(cfg: &Cfg, path: Option<&Path>, nonexistent: bool) -> Result { +fn override_remove( + cfg: &Cfg<'_>, + path: Option<&Path>, + nonexistent: bool, +) -> Result { let paths = if nonexistent { let list: Vec<_> = cfg.settings_file.with(|s| { Ok(s.overrides @@ -1418,7 +1439,7 @@ docs_data![ ]; async fn doc( - cfg: &Cfg, + cfg: &Cfg<'_>, path_only: bool, toolchain: Option, mut topic: Option<&str>, @@ -1463,16 +1484,16 @@ async fn doc( if path_only { let doc_path = toolchain.doc_path(doc_url)?; - writeln!(process().stdout().lock(), "{}", doc_path.display())?; + writeln!(cfg.process.stdout().lock(), "{}", doc_path.display())?; Ok(utils::ExitCode(0)) } else { if let Some(name) = topic { writeln!( - process().stderr().lock(), + cfg.process.stderr().lock(), "Opening docs named `{name}` in your browser" )?; } else { - writeln!(process().stderr().lock(), "Opening docs in your browser")?; + writeln!(cfg.process.stderr().lock(), "Opening docs in your browser")?; } toolchain.open_docs(doc_url)?; Ok(utils::ExitCode(0)) @@ -1481,7 +1502,7 @@ async fn doc( #[cfg(not(windows))] async fn man( - cfg: &Cfg, + cfg: &Cfg<'_>, command: &str, toolchain: Option, ) -> Result { @@ -1493,7 +1514,7 @@ async fn man( let mut manpaths = std::ffi::OsString::from(path); manpaths.push(":"); // prepend to the default MANPATH list - if let Some(path) = process().var_os("MANPATH") { + if let Some(path) = cfg.process.var_os("MANPATH") { manpaths.push(path); } std::process::Command::new("man") @@ -1505,12 +1526,11 @@ async fn man( } fn set_auto_self_update( - cfg: &mut Cfg, + cfg: &mut Cfg<'_>, auto_self_update_mode: SelfUpdateMode, ) -> Result { if self_update::NEVER_SELF_UPDATE { - let process = process(); - let mut args = process.args_os(); + let mut args = cfg.process.args_os(); let arg0 = args.next().map(PathBuf::from); let arg0 = arg0 .as_ref() @@ -1550,19 +1570,23 @@ impl fmt::Display for CompletionCommand { } } -fn output_completion_script(shell: Shell, command: CompletionCommand) -> Result { +fn output_completion_script( + shell: Shell, + command: CompletionCommand, + process: &Process, +) -> Result { match command { CompletionCommand::Rustup => { clap_complete::generate( shell, &mut Rustup::command(), "rustup", - &mut process().stdout().lock(), + &mut process.stdout().lock(), ); } CompletionCommand::Cargo => { if let Shell::Zsh = shell { - writeln!(process().stdout().lock(), "#compdef cargo")?; + writeln!(process.stdout().lock(), "#compdef cargo")?; } let script = match shell { @@ -1578,7 +1602,7 @@ fn output_completion_script(shell: Shell, command: CompletionCommand) -> Result< }; writeln!( - process().stdout().lock(), + process.stdout().lock(), "if command -v rustc >/dev/null 2>&1; then\n\ \tsource \"$(rustc --print sysroot)\"{script}\n\ fi", diff --git a/src/cli/self_update.rs b/src/cli/self_update.rs index 3763181ab7..f91cdd36c6 100644 --- a/src/cli/self_update.rs +++ b/src/cli/self_update.rs @@ -71,7 +71,7 @@ use crate::{ markdown::md, }, config::Cfg, - currentprocess::process, + currentprocess::Process, dist::dist::{self, PartialToolchainDesc, Profile, TargetTriple, ToolchainDesc}, install::UpdateStatus, toolchain::{ @@ -99,7 +99,7 @@ pub(crate) struct InstallOpts<'a> { } impl<'a> InstallOpts<'a> { - fn install(self, cfg: &mut Cfg) -> Result> { + fn install(self, cfg: &mut Cfg<'_>) -> Result> { let Self { default_host_triple, default_toolchain, @@ -146,7 +146,7 @@ impl<'a> InstallOpts<'a> { targets.join(", ") ); } - writeln!(process().stdout().lock())?; + writeln!(cfg.process.stdout().lock())?; Ok(None) } else if user_specified_something || (!no_update_toolchain && cfg.find_default()?.is_none()) @@ -173,27 +173,28 @@ impl<'a> InstallOpts<'a> { }) } else { info!("updating existing rustup installation - leaving toolchains alone"); - writeln!(process().stdout().lock())?; + writeln!(cfg.process.stdout().lock())?; Ok(None) } } // Interactive editing of the install options - fn customize(&mut self) -> Result<()> { + fn customize(&mut self, process: &Process) -> Result<()> { writeln!( - process().stdout().lock(), + process.stdout().lock(), "I'm going to ask you the value of each of these installation options.\n\ You may simply press the Enter key to leave unchanged." )?; - writeln!(process().stdout().lock())?; + writeln!(process.stdout().lock())?; self.default_host_triple = Some(common::question_str( "Default host triple?", &self .default_host_triple .take() - .unwrap_or_else(|| TargetTriple::from_host_or_build().to_string()), + .unwrap_or_else(|| TargetTriple::from_host_or_build(process).to_string()), + process, )?); self.default_toolchain = Some(MaybeOfficialToolchainName::try_from(common::question_str( @@ -203,6 +204,7 @@ impl<'a> InstallOpts<'a> { .as_ref() .map(ToString::to_string) .unwrap_or("stable".into()), + process, )?)?); self.profile = ::from_str(&common::question_str( @@ -211,22 +213,23 @@ impl<'a> InstallOpts<'a> { Profile::value_variants().iter().join("/"), ), self.profile.as_str(), + process, )?)?; self.no_modify_path = - !common::question_bool("Modify PATH variable?", !self.no_modify_path)?; + !common::question_bool("Modify PATH variable?", !self.no_modify_path, process)?; Ok(()) } - fn validate(&self) -> Result<()> { - common::warn_if_host_is_emulated(); + fn validate(&self, process: &Process) -> Result<()> { + common::warn_if_host_is_emulated(process); let host_triple = self .default_host_triple .as_ref() .map(dist::TargetTriple::new) - .unwrap_or_else(TargetTriple::from_host_or_build); + .unwrap_or_else(|| TargetTriple::from_host_or_build(process)); let partial_channel = match &self.default_toolchain { None | Some(MaybeOfficialToolchainName::None) => { ResolvableToolchainName::try_from("stable")? @@ -491,8 +494,8 @@ These components can be acquired through a Visual Studio installer. static DEFAULT_UPDATE_ROOT: &str = "https://static.rust-lang.org/rustup"; -fn update_root() -> String { - process() +fn update_root(process: &Process) -> String { + process .var("RUSTUP_UPDATE_ROOT") .inspect(|url| debug!("`RUSTUP_UPDATE_ROOT` has been set to `{url}`")) .unwrap_or_else(|_| String::from(DEFAULT_UPDATE_ROOT)) @@ -500,10 +503,10 @@ fn update_root() -> String { /// `CARGO_HOME` suitable for display, possibly with $HOME /// substituted for the directory prefix -fn canonical_cargo_home() -> Result> { - let path = utils::cargo_home()?; +fn canonical_cargo_home(process: &Process) -> Result> { + let path = utils::cargo_home(process)?; - let default_cargo_home = utils::home_dir() + let default_cargo_home = utils::home_dir(process) .unwrap_or_else(|| PathBuf::from(".")) .join(".cargo"); Ok(if default_cargo_home == path { @@ -528,55 +531,56 @@ pub(crate) async fn install( verbose: bool, quiet: bool, mut opts: InstallOpts<'_>, + process: &Process, ) -> Result { - if !process() + if !process .var_os("RUSTUP_INIT_SKIP_EXISTENCE_CHECKS") .map_or(false, |s| s == "yes") { - do_pre_install_sanity_checks(no_prompt)?; + do_pre_install_sanity_checks(no_prompt, process)?; } - opts.validate().map_err(|e| { + opts.validate(process).map_err(|e| { anyhow!( "Pre-checks for host and toolchain failed: {e}\n\ If you are unsure of suitable values, the 'stable' toolchain is the default.\n\ Valid host triples look something like: {}", - TargetTriple::from_host_or_build() + TargetTriple::from_host_or_build(process) ) })?; - if !process() + if !process .var_os("RUSTUP_INIT_SKIP_EXISTENCE_CHECKS") .map_or(false, |s| s == "yes") { - check_existence_of_rustc_or_cargo_in_path(no_prompt)?; + check_existence_of_rustc_or_cargo_in_path(no_prompt, process)?; } #[cfg(unix)] - do_anti_sudo_check(no_prompt)?; + do_anti_sudo_check(no_prompt, process)?; - let mut term = process().stdout().terminal(); + let mut term = process.stdout().terminal(process); #[cfg(windows)] - if let Some(plan) = do_msvc_check(&opts) { + if let Some(plan) = do_msvc_check(&opts, process) { if no_prompt { warn!("installing msvc toolchain without its prerequisites"); } else if !quiet && plan == VsInstallPlan::Automatic { md(&mut term, MSVC_AUTO_INSTALL_MESSAGE); - match windows::choose_vs_install()? { + match windows::choose_vs_install(process)? { Some(VsInstallPlan::Automatic) => { - match try_install_msvc(&opts).await { + match try_install_msvc(&opts, process).await { Err(e) => { // Make sure the console doesn't exit before the user can // see the error and give the option to continue anyway. - report_error(&e); - if !common::question_bool("\nContinue?", false)? { + report_error(&e, process); + if !common::question_bool("\nContinue?", false, process)? { info!("aborting installation"); return Ok(utils::ExitCode(0)); } } Ok(ContinueInstall::No) => { - ensure_prompt()?; + ensure_prompt(process)?; return Ok(utils::ExitCode(0)); } _ => {} @@ -584,7 +588,7 @@ pub(crate) async fn install( } Some(VsInstallPlan::Manual) => { md(&mut term, MSVC_MANUAL_INSTALL_MESSAGE); - if !common::question_bool("\nContinue?", false)? { + if !common::question_bool("\nContinue?", false, process)? { info!("aborting installation"); return Ok(utils::ExitCode(0)); } @@ -594,7 +598,7 @@ pub(crate) async fn install( } else { md(&mut term, MSVC_MESSAGE); md(&mut term, MSVC_MANUAL_INSTALL_MESSAGE); - if !common::question_bool("\nContinue?", false)? { + if !common::question_bool("\nContinue?", false, process)? { info!("aborting installation"); return Ok(utils::ExitCode(0)); } @@ -602,13 +606,13 @@ pub(crate) async fn install( } if !no_prompt { - let msg = pre_install_msg(opts.no_modify_path)?; + let msg = pre_install_msg(opts.no_modify_path, process)?; md(&mut term, msg); let mut customized_install = false; loop { - md(&mut term, current_install_opts(&opts)); - match common::confirm_advanced(customized_install)? { + md(&mut term, current_install_opts(&opts, process)); + match common::confirm_advanced(customized_install, process)? { Confirm::No => { info!("aborting installation"); return Ok(utils::ExitCode(0)); @@ -618,15 +622,15 @@ pub(crate) async fn install( } Confirm::Advanced => { customized_install = true; - opts.customize()?; + opts.customize(process)?; } } } } let no_modify_path = opts.no_modify_path; - if let Err(e) = maybe_install_rust(current_dir, verbose, quiet, opts).await { - report_error(&e); + if let Err(e) = maybe_install_rust(current_dir, verbose, quiet, opts, process).await { + report_error(&e, process); // On windows, where installation happens in a console // that may have opened just for this purpose, give @@ -634,13 +638,13 @@ pub(crate) async fn install( // window closes. #[cfg(windows)] if !no_prompt { - ensure_prompt()?; + ensure_prompt(process)?; } return Ok(utils::ExitCode(1)); } - let cargo_home = canonical_cargo_home()?; + let cargo_home = canonical_cargo_home(process)?; #[cfg(windows)] let cargo_home = cargo_home.replace('\\', r"\\"); #[cfg(windows)] @@ -668,13 +672,13 @@ pub(crate) async fn install( // On windows, where installation happens in a console // that may have opened just for this purpose, require // the user to press a key to continue. - ensure_prompt()?; + ensure_prompt(process)?; } Ok(utils::ExitCode(0)) } -fn rustc_or_cargo_exists_in_path() -> Result<()> { +fn rustc_or_cargo_exists_in_path(process: &Process) -> Result<()> { // Ignore rustc and cargo if present in $HOME/.cargo/bin or a few other directories #[allow(clippy::ptr_arg)] fn ignore_paths(path: &PathBuf) -> bool { @@ -683,7 +687,7 @@ fn rustc_or_cargo_exists_in_path() -> Result<()> { .any(|c| c == Component::Normal(".cargo".as_ref())) } - if let Some(paths) = process().var_os("PATH") { + if let Some(paths) = process.var_os("PATH") { let paths = env::split_paths(&paths).filter(ignore_paths); for path in paths { @@ -698,16 +702,16 @@ fn rustc_or_cargo_exists_in_path() -> Result<()> { Ok(()) } -fn check_existence_of_rustc_or_cargo_in_path(no_prompt: bool) -> Result<()> { +fn check_existence_of_rustc_or_cargo_in_path(no_prompt: bool, process: &Process) -> Result<()> { // Only the test runner should set this - let skip_check = process().var_os("RUSTUP_INIT_SKIP_PATH_CHECK"); + let skip_check = process.var_os("RUSTUP_INIT_SKIP_PATH_CHECK"); // Skip this if the environment variable is set if skip_check == Some("yes".into()) { return Ok(()); } - if let Err(path) = rustc_or_cargo_exists_in_path() { + if let Err(path) = rustc_or_cargo_exists_in_path(process) { warn!("it looks like you have an existing installation of Rust at:"); warn!("{}", path); warn!("It is recommended that rustup be the primary Rust installation."); @@ -715,15 +719,15 @@ fn check_existence_of_rustc_or_cargo_in_path(no_prompt: bool) -> Result<()> { warn!("If you are sure that you want both rustup and your already installed Rust"); warn!("then please reply `y' or `yes' or set RUSTUP_INIT_SKIP_PATH_CHECK to yes"); warn!("or pass `-y' to ignore all ignorable checks."); - ignorable_error("cannot install while Rust is installed", no_prompt)?; + ignorable_error("cannot install while Rust is installed", no_prompt, process)?; } Ok(()) } -fn do_pre_install_sanity_checks(no_prompt: bool) -> Result<()> { +fn do_pre_install_sanity_checks(no_prompt: bool, process: &Process) -> Result<()> { let rustc_manifest_path = PathBuf::from("/usr/local/lib/rustlib/manifest-rustc"); let uninstaller_path = PathBuf::from("/usr/local/lib/rustlib/uninstall.sh"); - let rustup_sh_path = utils::home_dir().unwrap().join(".rustup"); + let rustup_sh_path = utils::home_dir(process).unwrap().join(".rustup"); let rustup_sh_version_path = rustup_sh_path.join("rustup-version"); let rustc_exists = rustc_manifest_path.exists() && uninstaller_path.exists(); @@ -736,7 +740,7 @@ fn do_pre_install_sanity_checks(no_prompt: bool) -> Result<()> { "run `{}` as root to uninstall Rust", uninstaller_path.display() ); - ignorable_error("cannot install while Rust is installed", no_prompt)?; + ignorable_error("cannot install while Rust is installed", no_prompt, process)?; } if rustup_sh_exists { @@ -746,14 +750,18 @@ fn do_pre_install_sanity_checks(no_prompt: bool) -> Result<()> { warn!("or, if you already have rustup installed, you can run"); warn!("`rustup self update` and `rustup toolchain list` to upgrade"); warn!("your directory structure"); - ignorable_error("cannot install while rustup.sh is installed", no_prompt)?; + ignorable_error( + "cannot install while rustup.sh is installed", + no_prompt, + process, + )?; } Ok(()) } -fn pre_install_msg(no_modify_path: bool) -> Result { - let cargo_home = utils::cargo_home()?; +fn pre_install_msg(no_modify_path: bool, process: &Process) -> Result { + let cargo_home = utils::cargo_home(process)?; let cargo_home_bin = cargo_home.join("bin"); let rustup_home = home::rustup_home()?; @@ -761,8 +769,8 @@ fn pre_install_msg(no_modify_path: bool) -> Result { // Brittle code warning: some duplication in unix::do_add_to_path #[cfg(not(windows))] { - let rcfiles = shell::get_available_shells() - .flat_map(|sh| sh.update_rcs().into_iter()) + let rcfiles = shell::get_available_shells(process) + .flat_map(|sh| sh.update_rcs(process).into_iter()) .map(|rc| format!(" {}", rc.display())) .collect::>(); let plural = if rcfiles.len() > 1 { "s" } else { "" }; @@ -793,7 +801,7 @@ fn pre_install_msg(no_modify_path: bool) -> Result { } } -fn current_install_opts(opts: &InstallOpts<'_>) -> String { +fn current_install_opts(opts: &InstallOpts<'_>, process: &Process) -> String { format!( r"Current installation options: @@ -805,7 +813,7 @@ fn current_install_opts(opts: &InstallOpts<'_>) -> String { opts.default_host_triple .as_ref() .map(TargetTriple::new) - .unwrap_or_else(TargetTriple::from_host_or_build), + .unwrap_or_else(|| TargetTriple::from_host_or_build(process)), opts.default_toolchain .as_ref() .map(ToString::to_string) @@ -815,8 +823,8 @@ fn current_install_opts(opts: &InstallOpts<'_>) -> String { ) } -fn install_bins() -> Result<()> { - let bin_path = utils::cargo_home()?.join("bin"); +fn install_bins(process: &Process) -> Result<()> { + let bin_path = utils::cargo_home(process)?.join("bin"); let this_exe_path = utils::current_exe()?; let rustup_path = bin_path.join(format!("rustup{EXE_SUFFIX}")); @@ -828,11 +836,11 @@ fn install_bins() -> Result<()> { } utils::copy_file(&this_exe_path, &rustup_path)?; utils::make_executable(&rustup_path)?; - install_proxies() + install_proxies(process) } -pub(crate) fn install_proxies() -> Result<()> { - let bin_path = utils::cargo_home()?.join("bin"); +pub(crate) fn install_proxies(process: &Process) -> Result<()> { + let bin_path = utils::cargo_home(process)?.join("bin"); let rustup_path = bin_path.join(format!("rustup{EXE_SUFFIX}")); let rustup = Handle::from_path(&rustup_path)?; @@ -915,28 +923,29 @@ async fn maybe_install_rust( verbose: bool, quiet: bool, opts: InstallOpts<'_>, + process: &Process, ) -> Result<()> { - install_bins()?; + install_bins(process)?; #[cfg(unix)] - do_write_env_files()?; + do_write_env_files(process)?; if !opts.no_modify_path { #[cfg(windows)] - do_add_to_programs()?; - do_add_to_path()?; + do_add_to_programs(process)?; + do_add_to_path(process)?; } // If RUSTUP_HOME is not set, make sure it exists - if process().var_os("RUSTUP_HOME").is_none() { - let home = utils::home_dir() + if process.var_os("RUSTUP_HOME").is_none() { + let home = utils::home_dir(process) .map(|p| p.join(".rustup")) .ok_or_else(|| anyhow::anyhow!("could not find home dir to put .rustup in"))?; fs::create_dir_all(home).context("unable to create ~/.rustup")?; } - let mut cfg = common::set_globals(current_dir, verbose, quiet)?; + let mut cfg = common::set_globals(current_dir, verbose, quiet, process)?; let (components, targets) = (opts.components, opts.targets); let toolchain = opts.install(&mut cfg)?; @@ -966,30 +975,33 @@ async fn maybe_install_rust( }; cfg.set_default(Some(&desc.into()))?; - writeln!(process().stdout().lock())?; + writeln!(process.stdout().lock())?; common::show_channel_update(&cfg, PackageUpdate::Toolchain(desc.clone()), Ok(status))?; } Ok(()) } -pub(crate) fn uninstall(no_prompt: bool) -> Result { +pub(crate) fn uninstall(no_prompt: bool, process: &Process) -> Result { if NEVER_SELF_UPDATE { err!("self-uninstall is disabled for this build of rustup"); err!("you should probably use your system package manager to uninstall rustup"); return Ok(utils::ExitCode(1)); } - let cargo_home = utils::cargo_home()?; + let cargo_home = utils::cargo_home(process)?; if !cargo_home.join(format!("bin/rustup{EXE_SUFFIX}")).exists() { return Err(CLIError::NotSelfInstalled { p: cargo_home }.into()); } if !no_prompt { - writeln!(process().stdout().lock())?; - let msg = format!(pre_uninstall_msg!(), cargo_home = canonical_cargo_home()?); - md(&mut process().stdout().terminal(), msg); - if !common::confirm("\nContinue? (y/N)", false)? { + writeln!(process.stdout().lock())?; + let msg = format!( + pre_uninstall_msg!(), + cargo_home = canonical_cargo_home(process)? + ); + md(&mut process.stdout().terminal(process), msg); + if !common::confirm("\nContinue? (y/N)", false, process)? { info!("aborting uninstallation"); return Ok(utils::ExitCode(0)); } @@ -1006,7 +1018,7 @@ pub(crate) fn uninstall(no_prompt: bool) -> Result { info!("removing cargo home"); // Remove CARGO_HOME/bin from PATH - do_remove_from_path()?; + do_remove_from_path(process)?; #[cfg(windows)] do_remove_from_programs()?; @@ -1064,7 +1076,7 @@ pub(crate) fn uninstall(no_prompt: bool) -> Result { // Delete rustup. This is tricky because this is *probably* // the running executable and on Windows can't be unlinked until // the process exits. - delete_rustup_and_cargo_home()?; + delete_rustup_and_cargo_home(process)?; info!("rustup is uninstalled"); @@ -1086,8 +1098,8 @@ pub(crate) fn uninstall(no_prompt: bool) -> Result { /// (and on windows this process will not be running to do it), /// rustup-init is stored in `CARGO_HOME`/bin, and then deleted next /// time rustup runs. -pub(crate) async fn update(cfg: &Cfg) -> Result { - common::warn_if_host_is_emulated(); +pub(crate) async fn update(cfg: &Cfg<'_>) -> Result { + common::warn_if_host_is_emulated(cfg.process); use common::SelfUpdatePermission::*; let update_permitted = if NEVER_SELF_UPDATE { @@ -1109,7 +1121,7 @@ pub(crate) async fn update(cfg: &Cfg) -> Result { Permit => {} } - match prepare_update().await? { + match prepare_update(cfg.process).await? { Some(setup_path) => { let Some(version) = get_and_parse_new_rustup_version(&setup_path) else { err!("failed to get rustup version"); @@ -1130,7 +1142,7 @@ pub(crate) async fn update(cfg: &Cfg) -> Result { Ok(UpdateStatus::Unchanged), ); // Try again in case we emitted "tool `{}` is already installed" last time. - install_proxies()? + install_proxies(cfg.process)? } } @@ -1165,8 +1177,8 @@ fn parse_new_rustup_version(version: String) -> String { String::from(matched_version) } -pub(crate) async fn prepare_update() -> Result> { - let cargo_home = utils::cargo_home()?; +pub(crate) async fn prepare_update(process: &Process) -> Result> { + let cargo_home = utils::cargo_home(process)?; let rustup_path = cargo_home.join(format!("bin{MAIN_SEPARATOR}rustup{EXE_SUFFIX}")); let setup_path = cargo_home.join(format!("bin{MAIN_SEPARATOR}rustup-init{EXE_SUFFIX}")); @@ -1188,17 +1200,17 @@ pub(crate) async fn prepare_update() -> Result> { // If someone really wants to use another version, they still can enforce // that using the environment variable RUSTUP_OVERRIDE_HOST_TRIPLE. #[cfg(windows)] - let triple = dist::TargetTriple::from_host().unwrap_or(triple); + let triple = dist::TargetTriple::from_host(process).unwrap_or(triple); // Get update root. - let update_root = update_root(); + let update_root = update_root(process); // Get current version let current_version = env!("CARGO_PKG_VERSION"); // Get available version info!("checking for self-update"); - let available_version = get_available_rustup_version().await?; + let available_version = get_available_rustup_version(process).await?; // If up-to-date if available_version == current_version { @@ -1213,7 +1225,7 @@ pub(crate) async fn prepare_update() -> Result> { // Download new version info!("downloading self-update"); - utils::download_file(&download_url, &setup_path, None, &|_| ()).await?; + utils::download_file(&download_url, &setup_path, None, &|_| (), process).await?; // Mark as executable utils::make_executable(&setup_path)?; @@ -1221,8 +1233,8 @@ pub(crate) async fn prepare_update() -> Result> { Ok(Some(setup_path)) } -async fn get_available_rustup_version() -> Result { - let update_root = update_root(); +async fn get_available_rustup_version(process: &Process) -> Result { + let update_root = update_root(process); let tempdir = tempfile::Builder::new() .prefix("rustup-update") .tempdir() @@ -1232,7 +1244,7 @@ async fn get_available_rustup_version() -> Result { let release_file_url = format!("{update_root}/release-stable.toml"); let release_file_url = utils::parse_url(&release_file_url)?; let release_file = tempdir.path().join("release-stable.toml"); - utils::download_file(&release_file_url, &release_file, None, &|_| ()).await?; + utils::download_file(&release_file_url, &release_file, None, &|_| (), process).await?; let release_toml_str = utils::read_file("rustup release", &release_file)?; let release_toml = toml::from_str::(&release_toml_str) .context("unable to parse rustup release file")?; @@ -1279,13 +1291,13 @@ impl fmt::Display for SchemaVersion { } } -pub(crate) async fn check_rustup_update() -> Result<()> { - let mut t = process().stdout().terminal(); +pub(crate) async fn check_rustup_update(process: &Process) -> Result<()> { + let mut t = process.stdout().terminal(process); // Get current rustup version let current_version = env!("CARGO_PKG_VERSION"); // Get available rustup version - let available_version = get_available_rustup_version().await?; + let available_version = get_available_rustup_version(process).await?; let _ = t.attr(terminalsource::Attr::Bold); write!(t.lock(), "rustup - ")?; @@ -1306,8 +1318,8 @@ pub(crate) async fn check_rustup_update() -> Result<()> { } #[cfg_attr(feature = "otel", tracing::instrument)] -pub(crate) fn cleanup_self_updater() -> Result<()> { - let cargo_home = utils::cargo_home()?; +pub(crate) fn cleanup_self_updater(process: &Process) -> Result<()> { + let cargo_home = utils::cargo_home(process)?; let setup = cargo_home.join(format!("bin/rustup-init{EXE_SUFFIX}")); if setup.exists() { @@ -1321,25 +1333,23 @@ pub(crate) fn cleanup_self_updater() -> Result<()> { mod tests { use std::collections::HashMap; - use anyhow::Result; - use rustup_macros::unit_test as test; use crate::cli::common; use crate::cli::self_update::InstallOpts; use crate::dist::dist::{PartialToolchainDesc, Profile}; use crate::test::{test_dir, with_rustup_home, Env}; - use crate::{currentprocess, for_host}; + use crate::{currentprocess::TestProcess, for_host}; #[test] fn default_toolchain_is_stable() { with_rustup_home(|home| { let mut vars = HashMap::new(); home.apply(&mut vars); - let tp = currentprocess::TestProcess { - vars, - ..Default::default() - }; + let tp = TestProcess::with_vars(vars); + let mut cfg = + common::set_globals(tp.process.current_dir().unwrap(), false, false, &tp.process) + .unwrap(); let opts = InstallOpts { default_host_triple: None, @@ -1351,29 +1361,23 @@ mod tests { no_update_toolchain: false, }; - currentprocess::with(tp.clone().into(), || -> Result<()> { - // TODO: we could pass in a custom cfg to get notification - // callbacks rather than output to the tp sink. - let mut cfg = common::set_globals(tp.cwd.clone(), false, false).unwrap(); - assert_eq!( - "stable" - .parse::() - .unwrap() - .resolve(&cfg.get_default_host_triple().unwrap()) - .unwrap(), - opts.install(&mut cfg) - .unwrap() // result - .unwrap() // option - ); - Ok(()) - })?; + assert_eq!( + "stable" + .parse::() + .unwrap() + .resolve(&cfg.get_default_host_triple().unwrap()) + .unwrap(), + opts.install(&mut cfg) + .unwrap() // result + .unwrap() // option + ); assert_eq!( for_host!( r"info: profile set to 'default' info: default host triple is {0} " ), - &String::from_utf8(tp.get_stderr()).unwrap() + &String::from_utf8(tp.stderr()).unwrap() ); Ok(()) }) @@ -1386,15 +1390,8 @@ info: default host triple is {0} let cargo_home = root_dir.path().join("cargo"); let mut vars = HashMap::new(); vars.env("CARGO_HOME", cargo_home.to_string_lossy().to_string()); - let tp = currentprocess::TestProcess { - vars, - ..Default::default() - }; - currentprocess::with(tp.into(), || -> Result<()> { - super::install_bins().unwrap(); - Ok(()) - }) - .unwrap(); + let tp = TestProcess::with_vars(vars); + super::install_bins(&tp.process).unwrap(); assert!(cargo_home.exists()); } } diff --git a/src/cli/self_update/shell.rs b/src/cli/self_update/shell.rs index ca3a88488e..b59325257b 100644 --- a/src/cli/self_update/shell.rs +++ b/src/cli/self_update/shell.rs @@ -29,7 +29,7 @@ use std::path::PathBuf; use anyhow::{bail, Result}; use super::utils; -use crate::currentprocess::process; +use crate::currentprocess::Process; pub(crate) type Shell = Box; @@ -40,9 +40,9 @@ pub(crate) struct ShellScript { } impl ShellScript { - pub(crate) fn write(&self) -> Result<()> { - let home = utils::cargo_home()?; - let cargo_bin = format!("{}/bin", cargo_home_str()?); + pub(crate) fn write(&self, process: &Process) -> Result<()> { + let home = utils::cargo_home(process)?; + let cargo_bin = format!("{}/bin", cargo_home_str(process)?); let env_name = home.join(self.name); let env_file = self.content.replace("{cargo_bin}", &cargo_bin); utils::write_file(self.name, &env_name, &env_file)?; @@ -51,10 +51,10 @@ impl ShellScript { } // TODO: Update into a bytestring. -pub(crate) fn cargo_home_str() -> Result> { - let path = utils::cargo_home()?; +pub(crate) fn cargo_home_str(process: &Process) -> Result> { + let path = utils::cargo_home(process)?; - let default_cargo_home = utils::home_dir() + let default_cargo_home = utils::home_dir(process) .unwrap_or_else(|| PathBuf::from(".")) .join(".cargo"); Ok(if default_cargo_home == path { @@ -79,21 +79,23 @@ fn enumerate_shells() -> Vec { ] } -pub(crate) fn get_available_shells() -> impl Iterator { - enumerate_shells().into_iter().filter(|sh| sh.does_exist()) +pub(crate) fn get_available_shells(process: &Process) -> impl Iterator + '_ { + enumerate_shells() + .into_iter() + .filter(|sh| sh.does_exist(process)) } pub(crate) trait UnixShell { // Detects if a shell "exists". Users have multiple shells, so an "eager" // heuristic should be used, assuming shells exist if any traces do. - fn does_exist(&self) -> bool; + fn does_exist(&self, process: &Process) -> bool; // Gives all rcfiles of a given shell that Rustup is concerned with. // Used primarily in checking rcfiles for cleanup. - fn rcfiles(&self) -> Vec; + fn rcfiles(&self, process: &Process) -> Vec; // Gives rcs that should be written to. - fn update_rcs(&self) -> Vec; + fn update_rcs(&self, process: &Process) -> Vec; // Writes the relevant env file. fn env_script(&self) -> ShellScript { @@ -103,49 +105,49 @@ pub(crate) trait UnixShell { } } - fn source_string(&self) -> Result { - Ok(format!(r#". "{}/env""#, cargo_home_str()?)) + fn source_string(&self, process: &Process) -> Result { + Ok(format!(r#". "{}/env""#, cargo_home_str(process)?)) } } struct Posix; impl UnixShell for Posix { - fn does_exist(&self) -> bool { + fn does_exist(&self, _: &Process) -> bool { true } - fn rcfiles(&self) -> Vec { - match utils::home_dir() { + fn rcfiles(&self, process: &Process) -> Vec { + match utils::home_dir(process) { Some(dir) => vec![dir.join(".profile")], _ => vec![], } } - fn update_rcs(&self) -> Vec { + fn update_rcs(&self, process: &Process) -> Vec { // Write to .profile even if it doesn't exist. It's the only rc in the // POSIX spec so it should always be set up. - self.rcfiles() + self.rcfiles(process) } } struct Bash; impl UnixShell for Bash { - fn does_exist(&self) -> bool { - !self.update_rcs().is_empty() + fn does_exist(&self, process: &Process) -> bool { + !self.update_rcs(process).is_empty() } - fn rcfiles(&self) -> Vec { + fn rcfiles(&self, process: &Process) -> Vec { // Bash also may read .profile, however Rustup already includes handling // .profile as part of POSIX and always does setup for POSIX shells. [".bash_profile", ".bash_login", ".bashrc"] .iter() - .filter_map(|rc| utils::home_dir().map(|dir| dir.join(rc))) + .filter_map(|rc| utils::home_dir(process).map(|dir| dir.join(rc))) .collect() } - fn update_rcs(&self) -> Vec { - self.rcfiles() + fn update_rcs(&self, process: &Process) -> Vec { + self.rcfiles(process) .into_iter() .filter(|rc| rc.is_file()) .collect() @@ -155,12 +157,12 @@ impl UnixShell for Bash { struct Zsh; impl Zsh { - fn zdotdir() -> Result { + fn zdotdir(process: &Process) -> Result { use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; - if matches!(process().var("SHELL"), Ok(sh) if sh.contains("zsh")) { - match process().var("ZDOTDIR") { + if matches!(process.var("SHELL"), Ok(sh) if sh.contains("zsh")) { + match process.var("ZDOTDIR") { Ok(dir) if !dir.is_empty() => Ok(PathBuf::from(dir)), _ => bail!("Zsh setup failed."), } @@ -177,30 +179,30 @@ impl Zsh { } impl UnixShell for Zsh { - fn does_exist(&self) -> bool { + fn does_exist(&self, process: &Process) -> bool { // zsh has to either be the shell or be callable for zsh setup. - matches!(process().var("SHELL"), Ok(sh) if sh.contains("zsh")) - || utils::find_cmd(&["zsh"]).is_some() + matches!(process.var("SHELL"), Ok(sh) if sh.contains("zsh")) + || utils::find_cmd(&["zsh"], process).is_some() } - fn rcfiles(&self) -> Vec { - [Zsh::zdotdir().ok(), utils::home_dir()] + fn rcfiles(&self, process: &Process) -> Vec { + [Zsh::zdotdir(process).ok(), utils::home_dir(process)] .iter() .filter_map(|dir| dir.as_ref().map(|p| p.join(".zshenv"))) .collect() } - fn update_rcs(&self) -> Vec { + fn update_rcs(&self, process: &Process) -> Vec { // zsh can change $ZDOTDIR both _before_ AND _during_ reading .zshenv, // so we: write to $ZDOTDIR/.zshenv if-exists ($ZDOTDIR changes before) // OR write to $HOME/.zshenv if it exists (change-during) // if neither exist, we create it ourselves, but using the same logic, // because we must still respond to whether $ZDOTDIR is set or unset. // In any case we only write once. - self.rcfiles() + self.rcfiles(process) .into_iter() .filter(|env| env.is_file()) - .chain(self.rcfiles()) + .chain(self.rcfiles(process)) .take(1) .collect() } @@ -209,22 +211,22 @@ impl UnixShell for Zsh { struct Fish; impl UnixShell for Fish { - fn does_exist(&self) -> bool { + fn does_exist(&self, process: &Process) -> bool { // fish has to either be the shell or be callable for fish setup. - matches!(process().var("SHELL"), Ok(sh) if sh.contains("fish")) - || utils::find_cmd(&["fish"]).is_some() + matches!(process.var("SHELL"), Ok(sh) if sh.contains("fish")) + || utils::find_cmd(&["fish"], process).is_some() } // > "$XDG_CONFIG_HOME/fish/conf.d" (or "~/.config/fish/conf.d" if that variable is unset) for the user // from - fn rcfiles(&self) -> Vec { - let p0 = process().var("XDG_CONFIG_HOME").ok().map(|p| { + fn rcfiles(&self, process: &Process) -> Vec { + let p0 = process.var("XDG_CONFIG_HOME").ok().map(|p| { let mut path = PathBuf::from(p); path.push("fish/conf.d/rustup.fish"); path }); - let p1 = utils::home_dir().map(|mut path| { + let p1 = utils::home_dir(process).map(|mut path| { path.push(".config/fish/conf.d/rustup.fish"); path }); @@ -232,9 +234,9 @@ impl UnixShell for Fish { p0.into_iter().chain(p1).collect() } - fn update_rcs(&self) -> Vec { + fn update_rcs(&self, process: &Process) -> Vec { // The first rcfile takes precedence. - match self.rcfiles().into_iter().next() { + match self.rcfiles(process).into_iter().next() { Some(path) => vec![path], None => vec![], } @@ -247,19 +249,19 @@ impl UnixShell for Fish { } } - fn source_string(&self) -> Result { - Ok(format!(r#"source "{}/env.fish""#, cargo_home_str()?)) + fn source_string(&self, process: &Process) -> Result { + Ok(format!(r#"source "{}/env.fish""#, cargo_home_str(process)?)) } } -pub(crate) fn legacy_paths() -> impl Iterator { - let zprofiles = Zsh::zdotdir() +pub(crate) fn legacy_paths(process: &Process) -> impl Iterator + '_ { + let zprofiles = Zsh::zdotdir(process) .into_iter() - .chain(utils::home_dir()) + .chain(utils::home_dir(process)) .map(|d| d.join(".zprofile")); let profiles = [".bash_profile", ".profile"] .iter() - .filter_map(|rc| utils::home_dir().map(|d| d.join(rc))); + .filter_map(|rc| utils::home_dir(process).map(|d| d.join(rc))); profiles.chain(zprofiles) } diff --git a/src/cli/self_update/unix.rs b/src/cli/self_update/unix.rs index 6e96f5cf2d..d58593978a 100644 --- a/src/cli/self_update/unix.rs +++ b/src/cli/self_update/unix.rs @@ -5,25 +5,25 @@ use anyhow::{bail, Context, Result}; use super::install_bins; use super::shell; -use crate::currentprocess::process; +use crate::currentprocess::Process; use crate::utils::utils; use crate::utils::Notification; // If the user is trying to install with sudo, on some systems this will // result in writing root-owned files to the user's home directory, because // sudo is configured not to change $HOME. Don't let that bogosity happen. -pub(crate) fn do_anti_sudo_check(no_prompt: bool) -> Result { - pub(crate) fn home_mismatch() -> (bool, PathBuf, PathBuf) { +pub(crate) fn do_anti_sudo_check(no_prompt: bool, process: &Process) -> Result { + pub(crate) fn home_mismatch(process: &Process) -> (bool, PathBuf, PathBuf) { let fallback = || (false, PathBuf::new(), PathBuf::new()); // test runner should set this, nothing else - if process() + if process .var_os("RUSTUP_INIT_SKIP_SUDO_CHECK") .map_or(false, |s| s == "yes") { return fallback(); } - match (utils::home_dir_from_passwd(), process().var_os("HOME")) { + match (utils::home_dir_from_passwd(), process.var_os("HOME")) { (Some(pw), Some(eh)) if eh != pw => return (true, PathBuf::from(eh), pw), (None, _) => warn!("getpwuid_r: couldn't get user data"), _ => {} @@ -31,7 +31,7 @@ pub(crate) fn do_anti_sudo_check(no_prompt: bool) -> Result { fallback() } - match home_mismatch() { + match home_mismatch(process) { (false, _, _) => {} (true, env_home, euid_home) => { err!("$HOME differs from euid-obtained home directory: you may be using sudo"); @@ -47,17 +47,17 @@ pub(crate) fn do_anti_sudo_check(no_prompt: bool) -> Result { Ok(utils::ExitCode(0)) } -pub(crate) fn delete_rustup_and_cargo_home() -> Result<()> { - let cargo_home = utils::cargo_home()?; +pub(crate) fn delete_rustup_and_cargo_home(process: &Process) -> Result<()> { + let cargo_home = utils::cargo_home(process)?; utils::remove_dir("cargo_home", &cargo_home, &|_: Notification<'_>| ()) } -pub(crate) fn do_remove_from_path() -> Result<()> { - for sh in shell::get_available_shells() { - let source_bytes = format!("{}\n", sh.source_string()?).into_bytes(); +pub(crate) fn do_remove_from_path(process: &Process) -> Result<()> { + for sh in shell::get_available_shells(process) { + let source_bytes = format!("{}\n", sh.source_string(process)?).into_bytes(); // Check more files for cleanup than normally are updated. - for rc in sh.rcfiles().iter().filter(|rc| rc.is_file()) { + for rc in sh.rcfiles(process).iter().filter(|rc| rc.is_file()) { let file = utils::read_file("rcfile", rc)?; let file_bytes = file.into_bytes(); // FIXME: This is whitespace sensitive where it should not be. @@ -74,17 +74,17 @@ pub(crate) fn do_remove_from_path() -> Result<()> { } } - remove_legacy_paths()?; + remove_legacy_paths(process)?; Ok(()) } -pub(crate) fn do_add_to_path() -> Result<()> { - for sh in shell::get_available_shells() { - let source_cmd = sh.source_string()?; +pub(crate) fn do_add_to_path(process: &Process) -> Result<()> { + for sh in shell::get_available_shells(process) { + let source_cmd = sh.source_string(process)?; let source_cmd_with_newline = format!("\n{}", &source_cmd); - for rc in sh.update_rcs() { + for rc in sh.update_rcs(process) { let cmd_to_write = match utils::read_file("rcfile", &rc) { Ok(contents) if contents.contains(&source_cmd) => continue, Ok(contents) if !contents.ends_with('\n') => &source_cmd_with_newline, @@ -103,19 +103,19 @@ pub(crate) fn do_add_to_path() -> Result<()> { } } - remove_legacy_paths()?; + remove_legacy_paths(process)?; Ok(()) } -pub(crate) fn do_write_env_files() -> Result<()> { +pub(crate) fn do_write_env_files(process: &Process) -> Result<()> { let mut written = vec![]; - for sh in shell::get_available_shells() { + for sh in shell::get_available_shells(process) { let script = sh.env_script(); // Only write each possible script once. if !written.contains(&script) { - script.write()?; + script.write(process)?; written.push(script); } } @@ -141,15 +141,15 @@ pub(crate) fn run_update(setup_path: &Path) -> Result { /// This function is as the final step of a self-upgrade. It replaces /// `CARGO_HOME`/bin/rustup with the running exe, and updates the /// links to it. -pub(crate) fn self_replace() -> Result { - install_bins()?; +pub(crate) fn self_replace(process: &Process) -> Result { + install_bins(process)?; Ok(utils::ExitCode(0)) } -fn remove_legacy_source_command(source_cmd: String) -> Result<()> { +fn remove_legacy_source_command(source_cmd: String, process: &Process) -> Result<()> { let cmd_bytes = source_cmd.into_bytes(); - for rc in shell::legacy_paths().filter(|rc| rc.is_file()) { + for rc in shell::legacy_paths(process).filter(|rc| rc.is_file()) { let file = utils::read_file("rcfile", &rc)?; let file_bytes = file.into_bytes(); // FIXME: This is whitespace sensitive where it should not be. @@ -167,18 +167,24 @@ fn remove_legacy_source_command(source_cmd: String) -> Result<()> { Ok(()) } -fn remove_legacy_paths() -> Result<()> { +fn remove_legacy_paths(process: &Process) -> Result<()> { // Before the work to support more kinds of shells, which was released in // version 1.23.0 of Rustup, we always inserted this line instead, which is // now considered legacy - remove_legacy_source_command(format!( - "export PATH=\"{}/bin:$PATH\"\n", - shell::cargo_home_str()? - ))?; + remove_legacy_source_command( + format!( + "export PATH=\"{}/bin:$PATH\"\n", + shell::cargo_home_str(process)? + ), + process, + )?; // Unfortunately in 1.23, we accidentally used `source` rather than `.` // which, while widely supported, isn't actually POSIX, so we also // clean that up here. This issue was filed as #2623. - remove_legacy_source_command(format!("source \"{}/env\"\n", shell::cargo_home_str()?))?; + remove_legacy_source_command( + format!("source \"{}/env\"\n", shell::cargo_home_str(process)?), + process, + )?; Ok(()) } diff --git a/src/cli/self_update/windows.rs b/src/cli/self_update/windows.rs index cd046bb6d4..c0dacb200b 100644 --- a/src/cli/self_update/windows.rs +++ b/src/cli/self_update/windows.rs @@ -13,7 +13,7 @@ use super::super::errors::*; use super::common; use super::{install_bins, InstallOpts}; use crate::cli::download_tracker::DownloadTracker; -use crate::currentprocess::process; +use crate::currentprocess::Process; use crate::dist::dist::TargetTriple; use crate::utils::utils; use crate::utils::Notification; @@ -21,62 +21,59 @@ use crate::utils::Notification; use winreg::enums::{RegType, HKEY_CURRENT_USER, KEY_READ, KEY_WRITE}; use winreg::{RegKey, RegValue}; -pub(crate) fn ensure_prompt() -> Result<()> { - writeln!(process().stdout().lock(),)?; - writeln!( - process().stdout().lock(), - "Press the Enter key to continue." - )?; - common::read_line()?; +pub(crate) fn ensure_prompt(process: &Process) -> Result<()> { + writeln!(process.stdout().lock(),)?; + writeln!(process.stdout().lock(), "Press the Enter key to continue.")?; + common::read_line(process)?; Ok(()) } -fn choice(max: u8) -> Result> { - write!(process().stdout().lock(), ">")?; +fn choice(max: u8, process: &Process) -> Result> { + write!(process.stdout().lock(), ">")?; let _ = std::io::stdout().flush(); - let input = common::read_line()?; + let input = common::read_line(process)?; let r = match str::parse(&input) { Ok(n) if n <= max => Some(n), _ => None, }; - writeln!(process().stdout().lock())?; + writeln!(process.stdout().lock())?; Ok(r) } -pub(crate) fn choose_vs_install() -> Result> { +pub(crate) fn choose_vs_install(process: &Process) -> Result> { writeln!( - process().stdout().lock(), + process.stdout().lock(), "\n1) Quick install via the Visual Studio Community installer" )?; writeln!( - process().stdout().lock(), + process.stdout().lock(), " (free for individuals, academic uses, and open source)." )?; writeln!( - process().stdout().lock(), + process.stdout().lock(), "\n2) Manually install the prerequisites" )?; writeln!( - process().stdout().lock(), + process.stdout().lock(), " (for enterprise and advanced users)." )?; writeln!( - process().stdout().lock(), + process.stdout().lock(), "\n3) Don't install the prerequisites" )?; writeln!( - process().stdout().lock(), + process.stdout().lock(), " (if you're targeting the GNU ABI).\n" )?; let choice = loop { - if let Some(n) = choice(3)? { + if let Some(n) = choice(3, process)? { break n; } - writeln!(process().stdout().lock(), "Select option 1, 2 or 3")?; + writeln!(process.stdout().lock(), "Select option 1, 2 or 3")?; }; let plan = match choice { 1 => Some(VsInstallPlan::Automatic), @@ -94,9 +91,9 @@ pub(crate) enum VsInstallPlan { // Provide guidance about setting up MSVC if it doesn't appear to be // installed -pub(crate) fn do_msvc_check(opts: &InstallOpts<'_>) -> Option { +pub(crate) fn do_msvc_check(opts: &InstallOpts<'_>, process: &Process) -> Option { // Test suite skips this since it's env dependent - if process().var("RUSTUP_INIT_SKIP_MSVC_CHECK").is_ok() { + if process.var("RUSTUP_INIT_SKIP_MSVC_CHECK").is_ok() { return None; } @@ -104,7 +101,7 @@ pub(crate) fn do_msvc_check(opts: &InstallOpts<'_>) -> Option { let host_triple = if let Some(trip) = opts.default_host_triple.as_ref() { trip.to_owned() } else { - TargetTriple::from_host_or_build().to_string() + TargetTriple::from_host_or_build(process).to_string() }; let installing_msvc = host_triple.contains("msvc"); let have_msvc = windows_registry::find_tool(&host_triple, "cl.exe").is_some(); @@ -170,7 +167,10 @@ pub(crate) enum ContinueInstall { /// /// Returns `Ok(ContinueInstall::No)` if installing Visual Studio was successful /// but the rustup install should not be continued at this time. -pub(crate) async fn try_install_msvc(opts: &InstallOpts<'_>) -> Result { +pub(crate) async fn try_install_msvc( + opts: &InstallOpts<'_>, + process: &Process, +) -> Result { // download the installer let visual_studio_url = utils::parse_url("https://aka.ms/vs/17/release/vs_community.exe")?; @@ -180,15 +180,23 @@ pub(crate) async fn try_install_msvc(opts: &InstallOpts<'_>) -> Result) -> Result) -> Result) -> Result bool { - if let Some(paths) = process().var_os("lib") { +fn has_windows_sdk_libs(process: &Process) -> bool { + if let Some(paths) = process.var_os("lib") { for mut path in split_paths(&paths) { path.push("kernel32.lib"); if path.exists() { @@ -265,13 +273,13 @@ fn has_windows_sdk_libs() -> bool { /// Run by rustup-gc-$num.exe to delete CARGO_HOME #[cfg_attr(feature = "otel", tracing::instrument)] -pub fn complete_windows_uninstall() -> Result { +pub fn complete_windows_uninstall(process: &Process) -> Result { use std::process::Stdio; wait_for_parent()?; // Now that the parent has exited there are hopefully no more files open in CARGO_HOME - let cargo_home = utils::cargo_home()?; + let cargo_home = utils::cargo_home(process)?; utils::remove_dir("cargo_home", &cargo_home, &|_: Notification<'_>| ())?; // Now, run a *system* binary to inherit the DELETE_ON_CLOSE @@ -361,8 +369,8 @@ pub(crate) fn wait_for_parent() -> Result<()> { Ok(()) } -pub(crate) fn do_add_to_path() -> Result<()> { - let new_path = _with_path_cargo_home_bin(_add_to_path)?; +pub(crate) fn do_add_to_path(process: &Process) -> Result<()> { + let new_path = _with_path_cargo_home_bin(_add_to_path, process)?; _apply_new_path(new_path) } @@ -479,19 +487,19 @@ fn _remove_from_path(old_path: Vec, path_str: Vec) -> Option> Some(new_path) } -fn _with_path_cargo_home_bin(f: F) -> Result>> +fn _with_path_cargo_home_bin(f: F, process: &Process) -> Result>> where F: FnOnce(Vec, Vec) -> Option>, { let windows_path = get_windows_path_var()?; - let mut path_str = utils::cargo_home()?; + let mut path_str = utils::cargo_home(process)?; path_str.push("bin"); Ok(windows_path .and_then(|old_path| f(old_path, OsString::from(path_str).encode_wide().collect()))) } -pub(crate) fn do_remove_from_path() -> Result<()> { - let new_path = _with_path_cargo_home_bin(_remove_from_path)?; +pub(crate) fn do_remove_from_path(process: &Process) -> Result<()> { + let new_path = _with_path_cargo_home_bin(_remove_from_path, process)?; _apply_new_path(new_path) } @@ -510,7 +518,7 @@ pub(crate) fn do_update_programs_display_version(version: &str) -> Result<()> { .context("Failed to set `DisplayVersion`") } -pub(crate) fn do_add_to_programs() -> Result<()> { +pub(crate) fn do_add_to_programs(process: &Process) -> Result<()> { use std::path::PathBuf; let key = rustup_uninstall_reg_key()?; @@ -527,7 +535,7 @@ pub(crate) fn do_add_to_programs() -> Result<()> { } } - let mut path = utils::cargo_home()?; + let mut path = utils::cargo_home(process)?; path.push("bin\\rustup.exe"); let mut uninstall_cmd = OsString::from("\""); uninstall_cmd.push(path); @@ -599,9 +607,9 @@ pub(crate) fn run_update(setup_path: &Path) -> Result { Ok(utils::ExitCode(0)) } -pub(crate) fn self_replace() -> Result { +pub(crate) fn self_replace(process: &Process) -> Result { wait_for_parent()?; - install_bins()?; + install_bins(process)?; Ok(utils::ExitCode(0)) } @@ -636,7 +644,7 @@ pub(crate) fn self_replace() -> Result { // // .. augmented with this SO answer // https://stackoverflow.com/questions/10319526/understanding-a-self-deleting-program-in-c -pub(crate) fn delete_rustup_and_cargo_home() -> Result<()> { +pub(crate) fn delete_rustup_and_cargo_home(process: &Process) -> Result<()> { use std::io; use std::mem; use std::ptr; @@ -649,7 +657,7 @@ pub(crate) fn delete_rustup_and_cargo_home() -> Result<()> { }; // CARGO_HOME, hopefully empty except for bin/rustup.exe - let cargo_home = utils::cargo_home()?; + let cargo_home = utils::cargo_home(process)?; // The rustup.exe bin let rustup_path = cargo_home.join(format!("bin/rustup{EXE_SUFFIX}")); @@ -722,7 +730,7 @@ mod tests { use rustup_macros::unit_test as test; - use crate::currentprocess; + use crate::currentprocess::TestProcess; use crate::test::with_saved_path; fn wide(str: &str) -> Vec { @@ -763,28 +771,25 @@ mod tests { #[test] fn windows_path_regkey_type() { // per issue #261, setting PATH should use REG_EXPAND_SZ. - let tp = currentprocess::TestProcess::default(); with_saved_path(&mut || { - currentprocess::with(tp.clone().into(), || { - let root = RegKey::predef(HKEY_CURRENT_USER); - let environment = root - .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE) - .unwrap(); - environment.delete_value("PATH").unwrap(); - - { - // Can't compare the Results as Eq isn't derived; thanks error-chain. - #![allow(clippy::unit_cmp)] - assert_eq!((), super::_apply_new_path(Some(wide("foo"))).unwrap()); - } - let root = RegKey::predef(HKEY_CURRENT_USER); - let environment = root - .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE) - .unwrap(); - let path = environment.get_raw_value("PATH").unwrap(); - assert_eq!(path.vtype, RegType::REG_EXPAND_SZ); - assert_eq!(super::to_winreg_bytes(wide("foo")), &path.bytes[..]); - }) + let root = RegKey::predef(HKEY_CURRENT_USER); + let environment = root + .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE) + .unwrap(); + environment.delete_value("PATH").unwrap(); + + { + // Can't compare the Results as Eq isn't derived; thanks error-chain. + #![allow(clippy::unit_cmp)] + assert_eq!((), super::_apply_new_path(Some(wide("foo"))).unwrap()); + } + let root = RegKey::predef(HKEY_CURRENT_USER); + let environment = root + .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE) + .unwrap(); + let path = environment.get_raw_value("PATH").unwrap(); + assert_eq!(path.vtype, RegType::REG_EXPAND_SZ); + assert_eq!(super::to_winreg_bytes(wide("foo")), &path.bytes[..]); }); } @@ -793,87 +798,78 @@ mod tests { use std::io; // during uninstall the PATH key may end up empty; if so we should // delete it. - let tp = currentprocess::TestProcess::default(); with_saved_path(&mut || { - currentprocess::with(tp.clone().into(), || { - let root = RegKey::predef(HKEY_CURRENT_USER); - let environment = root - .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE) - .unwrap(); - environment - .set_raw_value( - "PATH", - &RegValue { - bytes: super::to_winreg_bytes(wide("foo")), - vtype: RegType::REG_EXPAND_SZ, - }, - ) - .unwrap(); - - { - // Can't compare the Results as Eq isn't derived; thanks error-chain. - #![allow(clippy::unit_cmp)] - assert_eq!((), super::_apply_new_path(Some(Vec::new())).unwrap()); - } - let reg_value = environment.get_raw_value("PATH"); - match reg_value { - Ok(_) => panic!("key not deleted"), - Err(ref e) if e.kind() == io::ErrorKind::NotFound => {} - Err(ref e) => panic!("error {e}"), - } - }) + let root = RegKey::predef(HKEY_CURRENT_USER); + let environment = root + .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE) + .unwrap(); + environment + .set_raw_value( + "PATH", + &RegValue { + bytes: super::to_winreg_bytes(wide("foo")), + vtype: RegType::REG_EXPAND_SZ, + }, + ) + .unwrap(); + + { + // Can't compare the Results as Eq isn't derived; thanks error-chain. + #![allow(clippy::unit_cmp)] + assert_eq!((), super::_apply_new_path(Some(Vec::new())).unwrap()); + } + let reg_value = environment.get_raw_value("PATH"); + match reg_value { + Ok(_) => panic!("key not deleted"), + Err(ref e) if e.kind() == io::ErrorKind::NotFound => {} + Err(ref e) => panic!("error {e}"), + } }); } #[test] fn windows_doesnt_mess_with_a_non_string_path() { // This writes an error, so we want a sink for it. - let tp = currentprocess::TestProcess { - vars: [("HOME".to_string(), "/unused".to_string())] + let tp = TestProcess::with_vars( + [("HOME".to_string(), "/unused".to_string())] .iter() .cloned() .collect(), - ..Default::default() - }; + ); with_saved_path(&mut || { - currentprocess::with(tp.clone().into(), || { - let root = RegKey::predef(HKEY_CURRENT_USER); - let environment = root - .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE) - .unwrap(); - let reg_value = RegValue { - bytes: vec![0x12, 0x34], - vtype: RegType::REG_BINARY, - }; - environment.set_raw_value("PATH", ®_value).unwrap(); - // Ok(None) signals no change to the PATH setting layer - assert_eq!( - None, - super::_with_path_cargo_home_bin(|_, _| panic!("called")).unwrap() - ); - }) + let root = RegKey::predef(HKEY_CURRENT_USER); + let environment = root + .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE) + .unwrap(); + let reg_value = RegValue { + bytes: vec![0x12, 0x34], + vtype: RegType::REG_BINARY, + }; + environment.set_raw_value("PATH", ®_value).unwrap(); + // Ok(None) signals no change to the PATH setting layer + assert_eq!( + None, + super::_with_path_cargo_home_bin(|_, _| panic!("called"), &tp.process).unwrap() + ); }); assert_eq!( r"warning: the registry key HKEY_CURRENT_USER\Environment\PATH is not a string. Not modifying the PATH variable ", - String::from_utf8(tp.get_stderr()).unwrap() + String::from_utf8(tp.stderr()).unwrap() ); } #[test] fn windows_treat_missing_path_as_empty() { // during install the PATH key may be missing; treat it as empty - let tp = currentprocess::TestProcess::default(); with_saved_path(&mut || { - currentprocess::with(tp.clone().into(), || { - let root = RegKey::predef(HKEY_CURRENT_USER); - let environment = root - .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE) - .unwrap(); - environment.delete_value("PATH").unwrap(); - - assert_eq!(Some(Vec::new()), super::get_windows_path_var().unwrap()); - }) + let root = RegKey::predef(HKEY_CURRENT_USER); + let environment = root + .open_subkey_with_flags("Environment", KEY_READ | KEY_WRITE) + .unwrap(); + environment.delete_value("PATH").unwrap(); + + assert_eq!(Some(Vec::new()), super::get_windows_path_var().unwrap()); }); } diff --git a/src/cli/setup_mode.rs b/src/cli/setup_mode.rs index c30112ac5b..6777785a01 100644 --- a/src/cli/setup_mode.rs +++ b/src/cli/setup_mode.rs @@ -8,7 +8,7 @@ use crate::{ common, self_update::{self, InstallOpts}, }, - currentprocess::process, + currentprocess::Process, dist::dist::Profile, toolchain::names::MaybeOfficialToolchainName, utils::utils, @@ -72,7 +72,7 @@ struct RustupInit { } #[cfg_attr(feature = "otel", tracing::instrument)] -pub async fn main(current_dir: PathBuf) -> Result { +pub async fn main(current_dir: PathBuf, process: &Process) -> Result { use clap::error::ErrorKind; let RustupInit { @@ -91,18 +91,18 @@ pub async fn main(current_dir: PathBuf) -> Result { } = match RustupInit::try_parse() { Ok(args) => args, Err(e) if [ErrorKind::DisplayHelp, ErrorKind::DisplayVersion].contains(&e.kind()) => { - write!(process().stdout().lock(), "{e}")?; + write!(process.stdout().lock(), "{e}")?; return Ok(utils::ExitCode(0)); } Err(e) => return Err(e.into()), }; if self_replace { - return self_update::self_replace(); + return self_update::self_replace(process); } if dump_testament { - common::dump_testament()?; + common::dump_testament(process)?; return Ok(utils::ExitCode(0)); } @@ -120,5 +120,5 @@ pub async fn main(current_dir: PathBuf) -> Result { targets: &target.iter().map(|s| &**s).collect::>(), }; - self_update::install(current_dir, no_prompt, verbose, quiet, opts).await + self_update::install(current_dir, no_prompt, verbose, quiet, opts, process).await } diff --git a/src/config.rs b/src/config.rs index 50eeba2277..0cec386662 100644 --- a/src/config.rs +++ b/src/config.rs @@ -13,7 +13,7 @@ use tokio_stream::StreamExt; use crate::settings::MetadataVersion; use crate::{ cli::self_update::SelfUpdateMode, - currentprocess::process, + currentprocess::Process, dist::{ dist::{self, Profile, ToolchainDesc}, download::DownloadCfg, @@ -134,7 +134,7 @@ enum OverrideCfg { } impl OverrideCfg { - fn from_file(cfg: &Cfg, file: OverrideFile) -> Result { + fn from_file(cfg: &Cfg<'_>, file: OverrideFile) -> Result { let toolchain_name = match (file.toolchain.channel, file.toolchain.path) { (Some(name), None) => { ResolvableToolchainName::try_from(name)?.resolve(&cfg.get_default_host_triple()?)? @@ -165,7 +165,9 @@ impl OverrideCfg { path.display() ) } - (None, None) => cfg.get_default()?.ok_or_else(no_toolchain_error)?, + (None, None) => cfg + .get_default()? + .ok_or_else(|| no_toolchain_error(cfg.process))?, }; Ok(match toolchain_name { ToolchainName::Official(desc) => { @@ -232,7 +234,7 @@ impl From for OverrideCfg { pub(crate) const UNIX_FALLBACK_SETTINGS: &str = "/etc/rustup/settings.toml"; -pub(crate) struct Cfg { +pub(crate) struct Cfg<'a> { profile_override: Option, pub rustup_dir: PathBuf, pub settings_file: SettingsFile, @@ -246,15 +248,17 @@ pub(crate) struct Cfg { pub dist_root_url: String, pub notify_handler: Arc)>, pub current_dir: PathBuf, + pub process: &'a Process, } -impl Cfg { +impl<'a> Cfg<'a> { pub(crate) fn from_env( current_dir: PathBuf, notify_handler: Arc)>, + process: &'a Process, ) -> Result { // Set up the rustup home directory - let rustup_dir = utils::rustup_home()?; + let rustup_dir = utils::rustup_home(process)?; utils::ensure_dir_exists("home", &rustup_dir, notify_handler.as_ref())?; @@ -265,7 +269,7 @@ impl Cfg { // If present, use the RUSTUP_OVERRIDE_UNIX_FALLBACK_SETTINGS environment // variable as settings path, or UNIX_FALLBACK_SETTINGS otherwise FallbackSettings::new( - match process().var("RUSTUP_OVERRIDE_UNIX_FALLBACK_SETTINGS") { + match process.var("RUSTUP_OVERRIDE_UNIX_FALLBACK_SETTINGS") { Ok(s) => PathBuf::from(s), Err(_) => PathBuf::from(UNIX_FALLBACK_SETTINGS), }, @@ -279,9 +283,10 @@ impl Cfg { let download_dir = rustup_dir.join("downloads"); // Figure out get_default_host_triple before Config is populated - let default_host_triple = settings_file.with(|s| Ok(get_default_host_triple(s)))?; + let default_host_triple = + settings_file.with(|s| Ok(get_default_host_triple(s, process)))?; // Environment override - let env_override = process() + let env_override = process .var("RUSTUP_TOOLCHAIN") .ok() .and_then(utils::if_not_empty) @@ -290,14 +295,14 @@ impl Cfg { .map(|t| t.resolve(&default_host_triple)) .transpose()?; - let dist_root_server = match process().var("RUSTUP_DIST_SERVER") { + let dist_root_server = match process.var("RUSTUP_DIST_SERVER") { Ok(s) if !s.is_empty() => { debug!("`RUSTUP_DIST_SERVER` has been set to `{s}`"); s } _ => { // For backward compatibility - process() + process .var("RUSTUP_DIST_ROOT") .ok() .and_then(utils::if_not_empty) @@ -331,6 +336,7 @@ impl Cfg { env_override, dist_root_url: dist_root, current_dir, + process, }; // Run some basic checks against the constructed configuration @@ -345,7 +351,7 @@ impl Cfg { } /// construct a download configuration - pub(crate) fn download_cfg<'a>( + pub(crate) fn download_cfg( &'a self, notify_handler: &'a dyn Fn(crate::dist::Notification<'_>), ) -> DownloadCfg<'a> { @@ -354,6 +360,7 @@ impl Cfg { tmp_cx: &self.tmp_cx, download_dir: &self.download_dir, notify_handler, + process: self.process, } } @@ -550,7 +557,7 @@ impl Cfg { // have an unresolved name. I'm just preserving pre-existing // behaviour by choosing ResolvableToolchainName here. let toolchain_name = ResolvableToolchainName::try_from(name)? - .resolve(&get_default_host_triple(settings))?; + .resolve(&get_default_host_triple(settings, self.process))?; let override_cfg = toolchain_name.into(); return Ok(Some((override_cfg, reason))); } @@ -595,7 +602,7 @@ impl Cfg { })?; if let Some(toolchain_name_str) = &override_file.toolchain.channel { let toolchain_name = ResolvableToolchainName::try_from(toolchain_name_str)?; - let default_host_triple = get_default_host_triple(settings); + let default_host_triple = get_default_host_triple(settings, self.process); // Do not permit architecture/os selection in channels as // these are host specific and toolchain files are portable. if let ResolvableToolchainName::Official(ref name) = toolchain_name { @@ -670,18 +677,18 @@ impl Cfg { } pub(crate) async fn find_or_install_active_toolchain( - &self, - ) -> Result<(Toolchain<'_>, ActiveReason)> { + &'a self, + ) -> Result<(Toolchain<'a>, ActiveReason)> { self.maybe_find_or_install_active_toolchain(&self.current_dir) .await? - .ok_or_else(no_toolchain_error) + .ok_or_else(|| no_toolchain_error(self.process)) } #[cfg_attr(feature = "otel", tracing::instrument(skip_all))] pub(crate) async fn maybe_find_or_install_active_toolchain( - &self, + &'a self, path: &Path, - ) -> Result, ActiveReason)>> { + ) -> Result, ActiveReason)>> { match self.find_override_config(path)? { Some((override_config, reason)) => match override_config { OverrideCfg::PathBased(path_based_name) => { @@ -885,7 +892,8 @@ impl Cfg { #[cfg_attr(feature = "otel", tracing::instrument(skip_all))] pub(crate) fn get_default_host_triple(&self) -> Result { - self.settings_file.with(|s| Ok(get_default_host_triple(s))) + self.settings_file + .with(|s| Ok(get_default_host_triple(s, self.process))) } /// The path on disk of any concrete toolchain @@ -897,7 +905,7 @@ impl Cfg { } } -impl Debug for Cfg { +impl<'a> Debug for Cfg<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let Self { profile_override, @@ -913,6 +921,7 @@ impl Debug for Cfg { dist_root_url, notify_handler: _, current_dir, + process: _, } = self; f.debug_struct("Cfg") @@ -932,15 +941,15 @@ impl Debug for Cfg { } } -fn get_default_host_triple(s: &Settings) -> dist::TargetTriple { +fn get_default_host_triple(s: &Settings, process: &Process) -> dist::TargetTriple { s.default_host_triple .as_ref() .map(dist::TargetTriple::new) - .unwrap_or_else(dist::TargetTriple::from_host_or_build) + .unwrap_or_else(|| dist::TargetTriple::from_host_or_build(process)) } -fn no_toolchain_error() -> anyhow::Error { - RustupError::ToolchainNotSelected(process().name().unwrap_or_else(|| "Rust".into())).into() +fn no_toolchain_error(process: &Process) -> anyhow::Error { + RustupError::ToolchainNotSelected(process.name().unwrap_or_else(|| "Rust".into())).into() } /// Specifies how a `rust-toolchain`/`rust-toolchain.toml` configuration file should be parsed. diff --git a/src/currentprocess.rs b/src/currentprocess.rs index 24d7987bed..22a1a5203c 100644 --- a/src/currentprocess.rs +++ b/src/currentprocess.rs @@ -16,7 +16,7 @@ use std::{ }; #[cfg(feature = "test")] -use rand::{thread_rng, Rng}; +use tracing::subscriber::DefaultGuard; use tracing_subscriber::util::SubscriberInitExt; pub mod filesource; @@ -27,7 +27,7 @@ pub mod terminalsource; pub enum Process { OSProcess(OSProcess), #[cfg(feature = "test")] - TestProcess(TestProcess), + TestProcess(TestContext), } impl Process { @@ -114,15 +114,6 @@ impl Process { Process::TestProcess(p) => Ok(p.cwd.clone()), } } - - #[cfg(test)] - fn id(&self) -> u64 { - match self { - Process::OSProcess(_) => std::process::id() as u64, - #[cfg(feature = "test")] - Process::TestProcess(p) => p.id, - } - } } impl home::env::Env for Process { @@ -151,45 +142,8 @@ impl home::env::Env for Process { } } -#[cfg(feature = "test")] -impl From for Process { - fn from(p: TestProcess) -> Self { - Self::TestProcess(p) - } -} - -/// Obtain the current instance of CurrentProcess -pub fn process() -> Process { - match PROCESS.with(|p| p.borrow().clone()) { - None => panic!("No process instance"), - Some(p) => p, - } -} - static HOOK_INSTALLED: Once = Once::new(); -/// Run a function in the context of a process definition. -/// -/// If the function panics, the process definition *in that thread* is cleared -/// by an implicitly installed global panic hook. -pub fn with(process: Process, f: F) -> R -where - F: FnOnce() -> R, -{ - ensure_hook(); - - PROCESS.with(|p| { - if let Some(old_p) = &*p.borrow() { - panic!("current process already set {old_p:?}"); - } - *p.borrow_mut() = Some(process.clone()); - let _guard = crate::cli::log::tracing_subscriber(process).set_default(); - let result = f(); - *p.borrow_mut() = None; - result - }) -} - fn ensure_hook() { HOOK_INSTALLED.call_once(|| { let orig_hook = panic::take_hook(); @@ -257,7 +211,7 @@ pub fn with_runtime<'a, R>( } *p.borrow_mut() = Some(process.clone()); let result = runtime.block_on(async { - let _guard = crate::cli::log::tracing_subscriber(process).set_default(); + let _guard = crate::cli::log::tracing_subscriber(&process).set_default(); fut.await }); *p.borrow_mut() = None; @@ -298,16 +252,12 @@ impl Default for OSProcess { } // ------------ test process ---------------- + #[cfg(feature = "test")] -#[derive(Clone, Debug, Default)] pub struct TestProcess { - pub cwd: PathBuf, - pub args: Vec, - pub vars: HashMap, - pub id: u64, - pub stdin: filesource::TestStdinInner, - pub stdout: filesource::TestWriterInner, - pub stderr: filesource::TestWriterInner, + pub process: Process, + #[allow(dead_code)] // guard is dropped at the end of the test + guard: DefaultGuard, } #[cfg(feature = "test")] @@ -318,60 +268,60 @@ impl TestProcess { vars: HashMap, stdin: &str, ) -> Self { - TestProcess { + Self::from(TestContext { cwd: cwd.as_ref().to_path_buf(), args: args.iter().map(|s| s.as_ref().to_string()).collect(), vars, - id: TestProcess::new_id(), stdin: Arc::new(Mutex::new(Cursor::new(stdin.to_string()))), stdout: Arc::new(Mutex::new(Vec::new())), stderr: Arc::new(Mutex::new(Vec::new())), - } + }) } - fn new_id() -> u64 { - let low_bits: u64 = std::process::id() as u64; - let mut rng = thread_rng(); - let high_bits = rng.gen_range(0..u32::MAX) as u64; - high_bits << 32 | low_bits + pub fn with_vars(vars: HashMap) -> Self { + Self::from(TestContext { + vars, + ..Default::default() + }) } - /// Extracts the stdout from the process - pub fn get_stdout(&self) -> Vec { - self.stdout - .lock() - .unwrap_or_else(|e| e.into_inner()) - .clone() + /// Extracts the stderr from the process + pub fn stderr(&self) -> Vec { + let tp = match &self.process { + Process::TestProcess(tp) => tp, + _ => unreachable!(), + }; + + tp.stderr.lock().unwrap_or_else(|e| e.into_inner()).clone() } +} - /// Extracts the stderr from the process - pub fn get_stderr(&self) -> Vec { - self.stderr - .lock() - .unwrap_or_else(|e| e.into_inner()) - .clone() +#[cfg(feature = "test")] +impl From for TestProcess { + fn from(inner: TestContext) -> Self { + let inner = Process::TestProcess(inner); + let guard = crate::cli::log::tracing_subscriber(&inner).set_default(); + Self { + process: inner, + guard, + } } } -#[cfg(test)] -mod tests { - use std::collections::HashMap; - use std::env; - - use rustup_macros::unit_test as test; - - use super::{process, with, TestProcess}; - - #[test] - fn test_instance() { - let proc = TestProcess::new( - env::current_dir().unwrap(), - &["foo", "bar", "baz"], - HashMap::default(), - "", - ); - with(proc.clone().into(), || { - assert_eq!(proc.id, process().id(), "{:?} != {:?}", proc, process()) - }); +#[cfg(feature = "test")] +impl Default for TestProcess { + fn default() -> Self { + Self::from(TestContext::default()) } } + +#[cfg(feature = "test")] +#[derive(Clone, Debug, Default)] +pub struct TestContext { + pub cwd: PathBuf, + args: Vec, + vars: HashMap, + stdin: filesource::TestStdinInner, + stdout: filesource::TestWriterInner, + stderr: filesource::TestWriterInner, +} diff --git a/src/currentprocess/filesource.rs b/src/currentprocess/filesource.rs index bf46a54f4e..a98b5837e7 100644 --- a/src/currentprocess/filesource.rs +++ b/src/currentprocess/filesource.rs @@ -1,7 +1,7 @@ use std::io::{self, BufRead, Read, Result, Write}; use super::terminalsource::{ColorableTerminal, StreamSelector}; -use crate::currentprocess::process; +use crate::currentprocess::Process; /// Stand-in for std::io::Stdin pub trait Stdin { @@ -40,10 +40,10 @@ pub trait Writer: Write + Send + Sync { /// Query whether a TTY is present. Used in download_tracker - we may want /// to remove this entirely with a better progress bar system (in favour of /// filtering in the Terminal layer?) - fn is_a_tty(&self) -> bool; + fn is_a_tty(&self, process: &Process) -> bool; /// Construct a terminal on this writer. - fn terminal(&self) -> ColorableTerminal; + fn terminal(&self, process: &Process) -> ColorableTerminal; } // ----------------- OS support for writers ----------------- @@ -51,8 +51,8 @@ pub trait Writer: Write + Send + Sync { impl WriterLock for io::StdoutLock<'_> {} impl Writer for io::Stdout { - fn is_a_tty(&self) -> bool { - match process() { + fn is_a_tty(&self, process: &Process) -> bool { + match process { crate::currentprocess::Process::OSProcess(p) => p.stdout_is_a_tty, #[cfg(feature = "test")] crate::currentprocess::Process::TestProcess(_) => unreachable!(), @@ -63,16 +63,16 @@ impl Writer for io::Stdout { Box::new(io::Stdout::lock(self)) } - fn terminal(&self) -> ColorableTerminal { - ColorableTerminal::new(StreamSelector::Stdout) + fn terminal(&self, process: &Process) -> ColorableTerminal { + ColorableTerminal::new(StreamSelector::Stdout, process) } } impl WriterLock for io::StderrLock<'_> {} impl Writer for io::Stderr { - fn is_a_tty(&self) -> bool { - match process() { + fn is_a_tty(&self, process: &Process) -> bool { + match process { crate::currentprocess::Process::OSProcess(p) => p.stderr_is_a_tty, #[cfg(feature = "test")] crate::currentprocess::Process::TestProcess(_) => unreachable!(), @@ -83,8 +83,8 @@ impl Writer for io::Stderr { Box::new(io::Stderr::lock(self)) } - fn terminal(&self) -> ColorableTerminal { - ColorableTerminal::new(StreamSelector::Stderr) + fn terminal(&self, process: &Process) -> ColorableTerminal { + ColorableTerminal::new(StreamSelector::Stderr, process) } } @@ -173,7 +173,7 @@ mod test_support { } impl Writer for TestWriter { - fn is_a_tty(&self) -> bool { + fn is_a_tty(&self, _: &Process) -> bool { false } @@ -181,8 +181,8 @@ mod test_support { Box::new(self.lock()) } - fn terminal(&self) -> ColorableTerminal { - ColorableTerminal::new(StreamSelector::TestWriter(self.clone())) + fn terminal(&self, process: &Process) -> ColorableTerminal { + ColorableTerminal::new(StreamSelector::TestWriter(self.clone()), process) } } diff --git a/src/currentprocess/terminalsource.rs b/src/currentprocess/terminalsource.rs index 962e92ca8e..a6d81d03bc 100644 --- a/src/currentprocess/terminalsource.rs +++ b/src/currentprocess/terminalsource.rs @@ -11,7 +11,7 @@ use termcolor::{ColorChoice, ColorSpec, StandardStream, StandardStreamLock, Writ #[cfg(feature = "test")] use super::filesource::{TestWriter, TestWriterLock}; -use super::process; +use super::Process; /// Select what stream to make a terminal on pub(super) enum StreamSelector { @@ -24,17 +24,17 @@ pub(super) enum StreamSelector { } impl StreamSelector { - fn is_a_tty(&self) -> bool { + fn is_a_tty(&self, process: &Process) -> bool { match self { - StreamSelector::Stdout => match process() { - super::Process::OSProcess(p) => p.stdout_is_a_tty, + StreamSelector::Stdout => match process { + Process::OSProcess(p) => p.stdout_is_a_tty, #[cfg(feature = "test")] - super::Process::TestProcess(_) => unreachable!(), + Process::TestProcess(_) => unreachable!(), }, - StreamSelector::Stderr => match process() { - super::Process::OSProcess(p) => p.stderr_is_a_tty, + StreamSelector::Stderr => match process { + Process::OSProcess(p) => p.stderr_is_a_tty, #[cfg(feature = "test")] - super::Process::TestProcess(_) => unreachable!(), + Process::TestProcess(_) => unreachable!(), }, #[cfg(feature = "test")] StreamSelector::TestWriter(_) => false, @@ -83,11 +83,11 @@ impl ColorableTerminal { /// `RUSTUP_TERM_COLOR` either unset or set to `auto`, /// then color commands will be sent to the stream. /// Otherwise color commands are discarded. - pub(super) fn new(stream: StreamSelector) -> Self { - let choice = match process().var("RUSTUP_TERM_COLOR") { + pub(super) fn new(stream: StreamSelector, process: &Process) -> Self { + let choice = match process.var("RUSTUP_TERM_COLOR") { Ok(s) if s.eq_ignore_ascii_case("always") => ColorChoice::Always, Ok(s) if s.eq_ignore_ascii_case("never") => ColorChoice::Never, - _ if stream.is_a_tty() => ColorChoice::Auto, + _ if stream.is_a_tty(process) => ColorChoice::Auto, _ => ColorChoice::Never, }; let inner = match stream { @@ -241,25 +241,22 @@ mod tests { use rustup_macros::unit_test as test; use super::*; - use crate::{currentprocess, test::Env}; + use crate::currentprocess::TestProcess; + use crate::test::Env; #[test] fn term_color_choice() { fn assert_color_choice(env_val: &str, stream: StreamSelector, color_choice: ColorChoice) { let mut vars = HashMap::new(); vars.env("RUSTUP_TERM_COLOR", env_val); - let tp = currentprocess::TestProcess { - vars, - ..Default::default() - }; - currentprocess::with(tp.into(), || { - let term = ColorableTerminal::new(stream); - let inner = term.inner.lock().unwrap(); - assert!(matches!( - &*inner, - &TerminalInner::TestWriter(_, choice) if choice == color_choice - )); - }); + let tp = TestProcess::with_vars(vars); + + let term = ColorableTerminal::new(stream, &tp.process); + let inner = term.inner.lock().unwrap(); + assert!(matches!( + &*inner, + &TerminalInner::TestWriter(_, choice) if choice == color_choice + )); } assert_color_choice( diff --git a/src/diskio/mod.rs b/src/diskio/mod.rs index f59b7b0da1..d96ecb5fad 100644 --- a/src/diskio/mod.rs +++ b/src/diskio/mod.rs @@ -66,7 +66,7 @@ use std::{fmt::Debug, fs::OpenOptions}; use anyhow::{Context, Result}; -use crate::currentprocess::process; +use crate::currentprocess::Process; use crate::utils::notifications::Notification; use threaded::PoolReference; @@ -450,9 +450,10 @@ pub(crate) fn create_dir>(path: P) -> io::Result<()> { pub(crate) fn get_executor<'a>( notify_handler: Option<&'a dyn Fn(Notification<'_>)>, ram_budget: usize, + process: &Process, ) -> Result> { // If this gets lots of use, consider exposing via the config file. - let thread_count = match process().var("RUSTUP_IO_THREADS") { + let thread_count = match process.var("RUSTUP_IO_THREADS") { Err(_) => available_parallelism().map(|p| p.get()).unwrap_or(1), Ok(n) => n .parse::() diff --git a/src/diskio/test.rs b/src/diskio/test.rs index 14c5fcb3bc..4425975513 100644 --- a/src/diskio/test.rs +++ b/src/diskio/test.rs @@ -7,7 +7,7 @@ use rustup_macros::unit_test as test; use crate::test::test_dir; use super::{get_executor, Executor, Item, Kind}; -use crate::currentprocess; +use crate::currentprocess::TestProcess; impl Item { /// The length of the file, for files (for stats) @@ -23,67 +23,62 @@ fn test_incremental_file(io_threads: &str) -> Result<()> { let work_dir = test_dir()?; let mut vars = HashMap::new(); vars.insert("RUSTUP_IO_THREADS".to_string(), io_threads.to_string()); - let tp = currentprocess::TestProcess { - vars, - ..Default::default() - }; - currentprocess::with(tp.into(), || -> Result<()> { - let mut written = 0; - let mut file_finished = false; - let mut io_executor: Box = get_executor(None, 32 * 1024 * 1024)?; - let (item, mut sender) = Item::write_file_segmented( - work_dir.path().join("scratch"), - 0o666, - io_executor.incremental_file_state(), - )?; - - // The file should be open and incomplete, and no completed chunks - assert!(io_executor.execute(item).collect::>().is_empty()); - - let mut chunk = io_executor.get_buffer(super::IO_CHUNK_SIZE); - chunk.extend(b"0123456789"); - chunk = chunk.finished(); - sender(chunk); - let mut chunk = io_executor.get_buffer(super::IO_CHUNK_SIZE); - chunk.extend(b"0123456789"); - chunk = chunk.finished(); - sender(chunk); - loop { - for work in io_executor.completed().collect::>() { - match work { - super::CompletedIo::Chunk(size) => written += size, - super::CompletedIo::Item(item) => unreachable!("{:?}", item), - } - } - if written == 20 { - break; + let tp = TestProcess::with_vars(vars); + + let mut written = 0; + let mut file_finished = false; + let mut io_executor: Box = get_executor(None, 32 * 1024 * 1024, &tp.process)?; + let (item, mut sender) = Item::write_file_segmented( + work_dir.path().join("scratch"), + 0o666, + io_executor.incremental_file_state(), + )?; + + // The file should be open and incomplete, and no completed chunks + assert!(io_executor.execute(item).collect::>().is_empty()); + + let mut chunk = io_executor.get_buffer(super::IO_CHUNK_SIZE); + chunk.extend(b"0123456789"); + chunk = chunk.finished(); + sender(chunk); + let mut chunk = io_executor.get_buffer(super::IO_CHUNK_SIZE); + chunk.extend(b"0123456789"); + chunk = chunk.finished(); + sender(chunk); + loop { + for work in io_executor.completed().collect::>() { + match work { + super::CompletedIo::Chunk(size) => written += size, + super::CompletedIo::Item(item) => unreachable!("{:?}", item), } } - // sending a zero length chunk closes the file - let mut chunk = io_executor.get_buffer(super::IO_CHUNK_SIZE); - chunk = chunk.finished(); - sender(chunk); - loop { - for work in io_executor.completed().collect::>() { - match work { - super::CompletedIo::Chunk(_) => {} - super::CompletedIo::Item(_) => { - file_finished = true; - } + if written == 20 { + break; + } + } + // sending a zero length chunk closes the file + let mut chunk = io_executor.get_buffer(super::IO_CHUNK_SIZE); + chunk = chunk.finished(); + sender(chunk); + loop { + for work in io_executor.completed().collect::>() { + match work { + super::CompletedIo::Chunk(_) => {} + super::CompletedIo::Item(_) => { + file_finished = true; } } - if file_finished { - break; - } } + if file_finished { + break; + } + } - // no more work should be outstanding - assert!(file_finished); - assert!(io_executor.join().collect::>().is_empty()); - assert_eq!(io_executor.buffer_used(), 0); + // no more work should be outstanding + assert!(file_finished); + assert!(io_executor.join().collect::>().is_empty()); + assert_eq!(io_executor.buffer_used(), 0); - Ok(()) - })?; // We should be able to read back the file assert_eq!( std::fs::read_to_string(work_dir.path().join("scratch"))?, @@ -96,57 +91,52 @@ fn test_complete_file(io_threads: &str) -> Result<()> { let work_dir = test_dir()?; let mut vars = HashMap::new(); vars.insert("RUSTUP_IO_THREADS".to_string(), io_threads.to_string()); - let tp = currentprocess::TestProcess { - vars, - ..Default::default() - }; - currentprocess::with(tp.into(), || -> Result<()> { - let mut io_executor: Box = get_executor(None, 32 * 1024 * 1024)?; - let mut chunk = io_executor.get_buffer(10); - chunk.extend(b"0123456789"); - assert_eq!(chunk.len(), 10); - chunk = chunk.finished(); - let item = Item::write_file(work_dir.path().join("scratch"), 0o666, chunk); + let tp = TestProcess::with_vars(vars); + + let mut io_executor: Box = get_executor(None, 32 * 1024 * 1024, &tp.process)?; + let mut chunk = io_executor.get_buffer(10); + chunk.extend(b"0123456789"); + assert_eq!(chunk.len(), 10); + chunk = chunk.finished(); + let item = Item::write_file(work_dir.path().join("scratch"), 0o666, chunk); + assert_eq!(item.size(), Some(10)); + let mut items = 0; + let mut check_item = |item: Item| { assert_eq!(item.size(), Some(10)); - let mut items = 0; - let mut check_item = |item: Item| { - assert_eq!(item.size(), Some(10)); - items += 1; - assert_eq!(1, items); - }; - let mut finished = false; - for work in io_executor.execute(item).collect::>() { - // The file might complete immediately - match work { - super::CompletedIo::Chunk(size) => unreachable!("{:?}", size), - super::CompletedIo::Item(item) => { - check_item(item); - finished = true; - } + items += 1; + assert_eq!(1, items); + }; + let mut finished = false; + for work in io_executor.execute(item).collect::>() { + // The file might complete immediately + match work { + super::CompletedIo::Chunk(size) => unreachable!("{:?}", size), + super::CompletedIo::Item(item) => { + check_item(item); + finished = true; } } - if !finished { - loop { - for work in io_executor.completed().collect::>() { - match work { - super::CompletedIo::Chunk(size) => unreachable!("{:?}", size), - super::CompletedIo::Item(item) => { - check_item(item); - finished = true; - } + } + if !finished { + loop { + for work in io_executor.completed().collect::>() { + match work { + super::CompletedIo::Chunk(size) => unreachable!("{:?}", size), + super::CompletedIo::Item(item) => { + check_item(item); + finished = true; } } - if finished { - break; - } + } + if finished { + break; } } - assert!(items > 0); - // no more work should be outstanding - assert!(io_executor.join().collect::>().is_empty()); + } + assert!(items > 0); + // no more work should be outstanding + assert!(io_executor.join().collect::>().is_empty()); - Ok(()) - })?; // We should be able to read back the file with correct content assert_eq!( std::fs::read_to_string(work_dir.path().join("scratch"))?, diff --git a/src/dist/component/components.rs b/src/dist/component/components.rs index 46b1709d45..920c80c05c 100644 --- a/src/dist/component/components.rs +++ b/src/dist/component/components.rs @@ -7,6 +7,7 @@ use std::path::{Path, PathBuf}; use anyhow::{bail, Result}; +use crate::currentprocess::Process; use crate::dist::component::package::{INSTALLER_VERSION, VERSION_FILE}; use crate::dist::component::transaction::Transaction; use crate::dist::prefix::InstallPrefix; @@ -187,14 +188,18 @@ impl Component { } Ok(result) } - pub fn uninstall<'a>(&self, mut tx: Transaction<'a>) -> Result> { + pub fn uninstall<'a>( + &self, + mut tx: Transaction<'a>, + process: &Process, + ) -> Result> { // Update components file let path = self.components.rel_components_file(); let abs_path = self.components.prefix.abs_path(&path); let temp = tx.temp().new_file()?; utils::filter_file("components", &abs_path, &temp, |l| l != self.name)?; tx.modify_file(path)?; - utils::rename("components", &temp, &abs_path, tx.notify_handler())?; + utils::rename("components", &temp, &abs_path, tx.notify_handler(), process)?; // TODO: If this is the last component remove the components file // and the version file. diff --git a/src/dist/component/package.rs b/src/dist/component/package.rs index 40b4630baa..05b26be015 100644 --- a/src/dist/component/package.rs +++ b/src/dist/component/package.rs @@ -11,7 +11,7 @@ use std::path::{Path, PathBuf}; use anyhow::{anyhow, bail, Context, Result}; use tar::EntryType; -use crate::currentprocess::process; +use crate::currentprocess::Process; use crate::diskio::{get_executor, CompletedIo, Executor, FileBuffer, Item, Kind, IO_CHUNK_SIZE}; use crate::dist::component::components::*; use crate::dist::component::transaction::*; @@ -144,13 +144,14 @@ impl<'a> TarPackage<'a> { stream: R, tmp_cx: &'a temp::Context, notify_handler: Option<&'a dyn Fn(Notification<'_>)>, + process: &Process, ) -> Result { let temp_dir = tmp_cx.new_directory()?; let mut archive = tar::Archive::new(stream); // The rust-installer packages unpack to a directory called // $pkgname-$version-$target. Skip that directory when // unpacking. - unpack_without_first_dir(&mut archive, &temp_dir, notify_handler) + unpack_without_first_dir(&mut archive, &temp_dir, notify_handler, process) .context("failed to extract package")?; Ok(TarPackage( @@ -165,6 +166,7 @@ fn unpack_ram( io_chunk_size: usize, effective_max_ram: Option, notify_handler: Option<&dyn Fn(Notification<'_>)>, + process: &Process, ) -> usize { const RAM_ALLOWANCE_FOR_RUSTUP_AND_BUFFERS: usize = 200 * 1024 * 1024; let minimum_ram = io_chunk_size * 2; @@ -178,7 +180,7 @@ fn unpack_ram( // Rustup does not know how much RAM the machine has: use the minimum minimum_ram }; - let unpack_ram = match process() + let unpack_ram = match process .var("RUSTUP_UNPACK_RAM") .ok() .and_then(|budget_str| budget_str.parse::().ok()) @@ -289,6 +291,7 @@ fn unpack_without_first_dir( archive: &mut tar::Archive, path: &Path, notify_handler: Option<&dyn Fn(Notification<'_>)>, + process: &Process, ) -> Result<()> { let entries = archive.entries()?; let effective_max_ram = match effective_limits::memory_limit() { @@ -300,8 +303,8 @@ fn unpack_without_first_dir( None } }; - let unpack_ram = unpack_ram(IO_CHUNK_SIZE, effective_max_ram, notify_handler); - let mut io_executor: Box = get_executor(notify_handler, unpack_ram)?; + let unpack_ram = unpack_ram(IO_CHUNK_SIZE, effective_max_ram, notify_handler, process); + let mut io_executor: Box = get_executor(notify_handler, unpack_ram, process)?; let mut directories: HashMap = HashMap::new(); // Path is presumed to exist. Call it a precondition. @@ -457,7 +460,7 @@ fn unpack_without_first_dir( // Tar has item before containing directory // Complain about this so we can see if these exist. writeln!( - process().stderr().lock(), + process.stderr().lock(), "Unexpected: missing parent '{}' for '{}'", parent.display(), entry.path()?.display() @@ -556,12 +559,14 @@ impl<'a> TarGzPackage<'a> { stream: R, tmp_cx: &'a temp::Context, notify_handler: Option<&'a dyn Fn(Notification<'_>)>, + process: &Process, ) -> Result { let stream = flate2::read::GzDecoder::new(stream); Ok(TarGzPackage(TarPackage::new( stream, tmp_cx, notify_handler, + process, )?)) } } @@ -592,12 +597,14 @@ impl<'a> TarXzPackage<'a> { stream: R, tmp_cx: &'a temp::Context, notify_handler: Option<&'a dyn Fn(Notification<'_>)>, + process: &Process, ) -> Result { let stream = xz2::read::XzDecoder::new(stream); Ok(TarXzPackage(TarPackage::new( stream, tmp_cx, notify_handler, + process, )?)) } } @@ -628,12 +635,14 @@ impl<'a> TarZStdPackage<'a> { stream: R, tmp_cx: &'a temp::Context, notify_handler: Option<&'a dyn Fn(Notification<'_>)>, + process: &Process, ) -> Result { let stream = zstd::stream::read::Decoder::new(stream)?; Ok(TarZStdPackage(TarPackage::new( stream, tmp_cx, notify_handler, + process, )?)) } } diff --git a/src/dist/component/tests.rs b/src/dist/component/tests.rs index 3ea97e939a..42d394d4ea 100644 --- a/src/dist/component/tests.rs +++ b/src/dist/component/tests.rs @@ -4,6 +4,7 @@ use std::path::PathBuf; use rustup_macros::unit_test as test; +use crate::currentprocess::TestProcess; use crate::dist::component::Transaction; use crate::dist::dist::DEFAULT_DIST_SERVER; use crate::dist::prefix::InstallPrefix; @@ -27,7 +28,8 @@ fn add_file() { ); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let mut file = tx.add_file("c", PathBuf::from("foo/bar")).unwrap(); write!(file, "test").unwrap(); @@ -55,7 +57,8 @@ fn add_file_then_rollback() { ); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); tx.add_file("c", PathBuf::from("foo/bar")).unwrap(); drop(tx); @@ -77,7 +80,8 @@ fn add_file_that_exists() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix, &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); fs::create_dir_all(prefixdir.path().join("foo")).unwrap(); utils::write_file("", &prefixdir.path().join("foo/bar"), "").unwrap(); @@ -108,7 +112,8 @@ fn copy_file() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let srcpath = srcdir.path().join("bar"); utils::write_file("", &srcpath, "").unwrap(); @@ -135,7 +140,8 @@ fn copy_file_then_rollback() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let srcpath = srcdir.path().join("bar"); utils::write_file("", &srcpath, "").unwrap(); @@ -162,7 +168,8 @@ fn copy_file_that_exists() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix, &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let srcpath = srcdir.path().join("bar"); utils::write_file("", &srcpath, "").unwrap(); @@ -198,7 +205,8 @@ fn copy_dir() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let srcpath1 = srcdir.path().join("foo"); let srcpath2 = srcdir.path().join("bar/baz"); @@ -232,7 +240,8 @@ fn copy_dir_then_rollback() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let srcpath1 = srcdir.path().join("foo"); let srcpath2 = srcdir.path().join("bar/baz"); @@ -266,7 +275,8 @@ fn copy_dir_that_exists() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); fs::create_dir_all(prefix.path().join("a")).unwrap(); @@ -297,7 +307,8 @@ fn remove_file() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix, &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let filepath = prefixdir.path().join("foo"); utils::write_file("", &filepath, "").unwrap(); @@ -322,7 +333,8 @@ fn remove_file_then_rollback() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix, &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let filepath = prefixdir.path().join("foo"); utils::write_file("", &filepath, "").unwrap(); @@ -347,7 +359,8 @@ fn remove_file_that_not_exists() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix, &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let err = tx.remove_file("c", PathBuf::from("foo")).unwrap_err(); @@ -374,7 +387,8 @@ fn remove_dir() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix, &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let filepath = prefixdir.path().join("foo/bar"); fs::create_dir_all(filepath.parent().unwrap()).unwrap(); @@ -400,7 +414,8 @@ fn remove_dir_then_rollback() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix, &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let filepath = prefixdir.path().join("foo/bar"); fs::create_dir_all(filepath.parent().unwrap()).unwrap(); @@ -426,7 +441,8 @@ fn remove_dir_that_not_exists() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix, &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let err = tx.remove_dir("c", PathBuf::from("foo")).unwrap_err(); @@ -453,7 +469,8 @@ fn write_file() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let content = "hi".to_string(); tx.write_file("c", PathBuf::from("foo/bar"), content.clone()) @@ -480,7 +497,8 @@ fn write_file_then_rollback() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let content = "hi".to_string(); tx.write_file("c", PathBuf::from("foo/bar"), content) @@ -504,7 +522,8 @@ fn write_file_that_exists() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let content = "hi".to_string(); utils_raw::write_file(&prefix.path().join("a"), &content).unwrap(); @@ -535,7 +554,8 @@ fn modify_file_that_not_exists() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); tx.modify_file(PathBuf::from("foo/bar")).unwrap(); tx.commit(); @@ -559,7 +579,8 @@ fn modify_file_that_exists() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let path = prefix.path().join("foo"); utils_raw::write_file(&path, "wow").unwrap(); @@ -583,7 +604,8 @@ fn modify_file_that_not_exists_then_rollback() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); tx.modify_file(PathBuf::from("foo/bar")).unwrap(); drop(tx); @@ -605,7 +627,8 @@ fn modify_file_that_exists_then_rollback() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let path = prefix.path().join("foo"); utils_raw::write_file(&path, "wow").unwrap(); @@ -632,7 +655,8 @@ fn modify_twice_then_rollback() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let path = prefix.path().join("foo"); utils_raw::write_file(&path, "wow").unwrap(); @@ -659,7 +683,8 @@ fn do_multiple_op_transaction(rollback: bool) { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); // copy_file let relpath1 = PathBuf::from("bin/rustc"); @@ -760,7 +785,8 @@ fn rollback_failure_keeps_going() { let prefix = InstallPrefix::from(prefixdir.path()); let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); write!(tx.add_file("", PathBuf::from("foo")).unwrap(), "").unwrap(); write!(tx.add_file("", PathBuf::from("bar")).unwrap(), "").unwrap(); diff --git a/src/dist/component/transaction.rs b/src/dist/component/transaction.rs index aa0b0ffbcf..9e1fe1402d 100644 --- a/src/dist/component/transaction.rs +++ b/src/dist/component/transaction.rs @@ -14,6 +14,7 @@ use std::path::{Path, PathBuf}; use anyhow::{anyhow, Context, Result}; +use crate::currentprocess::Process; use crate::dist::notifications::*; use crate::dist::prefix::InstallPrefix; use crate::dist::temp; @@ -39,6 +40,7 @@ pub struct Transaction<'a> { tmp_cx: &'a temp::Context, notify_handler: &'a dyn Fn(Notification<'_>), committed: bool, + process: &'a Process, } impl<'a> Transaction<'a> { @@ -46,6 +48,7 @@ impl<'a> Transaction<'a> { prefix: InstallPrefix, tmp_cx: &'a temp::Context, notify_handler: &'a dyn Fn(Notification<'_>), + process: &'a Process, ) -> Self { Transaction { prefix, @@ -53,6 +56,7 @@ impl<'a> Transaction<'a> { tmp_cx, notify_handler, committed: false, + process, } } @@ -101,6 +105,7 @@ impl<'a> Transaction<'a> { relpath, self.tmp_cx, self.notify_handler(), + self.process, )?; self.change(item); Ok(()) @@ -116,6 +121,7 @@ impl<'a> Transaction<'a> { relpath, self.tmp_cx, self.notify_handler(), + self.process, )?; self.change(item); Ok(()) @@ -155,8 +161,14 @@ impl<'a> Transaction<'a> { src: &Path, ) -> Result<()> { assert!(relpath.is_relative()); - let item = - ChangedItem::move_file(&self.prefix, component, relpath, src, self.notify_handler())?; + let item = ChangedItem::move_file( + &self.prefix, + component, + relpath, + src, + self.notify_handler(), + self.process, + )?; self.change(item); Ok(()) } @@ -164,8 +176,14 @@ impl<'a> Transaction<'a> { /// Recursively move a directory to a relative path of the install prefix. pub(crate) fn move_dir(&mut self, component: &str, relpath: PathBuf, src: &Path) -> Result<()> { assert!(relpath.is_relative()); - let item = - ChangedItem::move_dir(&self.prefix, component, relpath, src, self.notify_handler())?; + let item = ChangedItem::move_dir( + &self.prefix, + component, + relpath, + src, + self.notify_handler(), + self.process, + )?; self.change(item); Ok(()) } @@ -187,7 +205,7 @@ impl<'a> Drop for Transaction<'a> { for item in self.changes.iter().rev() { // ok_ntfy!(self.notify_handler, // Notification::NonFatalError, - match item.roll_back(&self.prefix, self.notify_handler()) { + match item.roll_back(&self.prefix, self.notify_handler(), self.process) { Ok(()) => {} Err(e) => { (self.notify_handler)(Notification::NonFatalError(&e)); @@ -216,17 +234,22 @@ impl<'a> ChangedItem<'a> { &self, prefix: &InstallPrefix, notify: &'a dyn Fn(Notification<'_>), + process: &Process, ) -> Result<()> { use self::ChangedItem::*; match self { AddedFile(path) => utils::remove_file("component", &prefix.abs_path(path))?, AddedDir(path) => utils::remove_dir("component", &prefix.abs_path(path), notify)?, RemovedFile(path, tmp) | ModifiedFile(path, Some(tmp)) => { - utils::rename("component", tmp, &prefix.abs_path(path), notify)? - } - RemovedDir(path, tmp) => { - utils::rename("component", &tmp.join("bk"), &prefix.abs_path(path), notify)? + utils::rename("component", tmp, &prefix.abs_path(path), notify, process)? } + RemovedDir(path, tmp) => utils::rename( + "component", + &tmp.join("bk"), + &prefix.abs_path(path), + notify, + process, + )?, ModifiedFile(path, None) => { let abs_path = prefix.abs_path(path); if utils::is_file(&abs_path) { @@ -282,6 +305,7 @@ impl<'a> ChangedItem<'a> { relpath: PathBuf, tmp_cx: &'a temp::Context, notify: &'a dyn Fn(Notification<'_>), + process: &Process, ) -> Result { let abs_path = prefix.abs_path(&relpath); let backup = tmp_cx.new_file()?; @@ -292,7 +316,7 @@ impl<'a> ChangedItem<'a> { } .into()) } else { - utils::rename("component", &abs_path, &backup, notify)?; + utils::rename("component", &abs_path, &backup, notify, process)?; Ok(ChangedItem::RemovedFile(relpath, backup)) } } @@ -302,6 +326,7 @@ impl<'a> ChangedItem<'a> { relpath: PathBuf, tmp_cx: &'a temp::Context, notify: &'a dyn Fn(Notification<'_>), + process: &Process, ) -> Result { let abs_path = prefix.abs_path(&relpath); let backup = tmp_cx.new_directory()?; @@ -312,7 +337,7 @@ impl<'a> ChangedItem<'a> { } .into()) } else { - utils::rename("component", &abs_path, &backup.join("bk"), notify)?; + utils::rename("component", &abs_path, &backup.join("bk"), notify, process)?; Ok(ChangedItem::RemovedDir(relpath, backup)) } } @@ -340,9 +365,10 @@ impl<'a> ChangedItem<'a> { relpath: PathBuf, src: &Path, notify: &'a dyn Fn(Notification<'_>), + process: &Process, ) -> Result { let abs_path = ChangedItem::dest_abs_path(prefix, component, &relpath)?; - utils::rename("component", src, &abs_path, notify)?; + utils::rename("component", src, &abs_path, notify, process)?; Ok(ChangedItem::AddedFile(relpath)) } fn move_dir( @@ -351,9 +377,10 @@ impl<'a> ChangedItem<'a> { relpath: PathBuf, src: &Path, notify: &'a dyn Fn(Notification<'_>), + process: &Process, ) -> Result { let abs_path = ChangedItem::dest_abs_path(prefix, component, &relpath)?; - utils::rename("component", src, &abs_path, notify)?; + utils::rename("component", src, &abs_path, notify, process)?; Ok(ChangedItem::AddedDir(relpath)) } } diff --git a/src/dist/dist.rs b/src/dist/dist.rs index 76b856a79f..57fc5dcdd0 100644 --- a/src/dist/dist.rs +++ b/src/dist/dist.rs @@ -18,7 +18,7 @@ use thiserror::Error as ThisError; pub(crate) use crate::dist::triple::*; use crate::{ - currentprocess::process, + currentprocess::Process, dist::{ download::DownloadCfg, manifest::{Component, Manifest as ManifestV2}, @@ -299,7 +299,7 @@ impl TargetTriple { } } - pub(crate) fn from_host() -> Option { + pub(crate) fn from_host(process: &Process) -> Option { #[cfg(windows)] fn inner() -> Option { use std::mem; @@ -429,15 +429,15 @@ impl TargetTriple { host_triple.map(TargetTriple::new) } - if let Ok(triple) = process().var("RUSTUP_OVERRIDE_HOST_TRIPLE") { + if let Ok(triple) = process.var("RUSTUP_OVERRIDE_HOST_TRIPLE") { Some(Self(triple)) } else { inner() } } - pub(crate) fn from_host_or_build() -> Self { - Self::from_host().unwrap_or_else(Self::from_build) + pub(crate) fn from_host_or_build(process: &Process) -> Self { + Self::from_host(process).unwrap_or_else(Self::from_build) } pub(crate) fn can_run(&self, other: &TargetTriple) -> Result { @@ -554,8 +554,8 @@ impl FromStr for ToolchainDesc { } impl ToolchainDesc { - pub(crate) fn manifest_v1_url(&self, dist_root: &str) -> String { - let do_manifest_staging = process().var("RUSTUP_STAGED_MANIFEST").is_ok(); + pub(crate) fn manifest_v1_url(&self, dist_root: &str, process: &Process) -> String { + let do_manifest_staging = process.var("RUSTUP_STAGED_MANIFEST").is_ok(); match (self.date.as_ref(), do_manifest_staging) { (None, false) => format!("{}/channel-rust-{}", dist_root, self.channel), (Some(date), false) => format!("{}/{}/channel-rust-{}", dist_root, date, self.channel), @@ -564,8 +564,8 @@ impl ToolchainDesc { } } - pub(crate) fn manifest_v2_url(&self, dist_root: &str) -> String { - format!("{}.toml", self.manifest_v1_url(dist_root)) + pub(crate) fn manifest_v2_url(&self, dist_root: &str, process: &Process) -> String { + format!("{}.toml", self.manifest_v1_url(dist_root, process)) } /// Either "$channel" or "channel-$date" pub fn manifest_name(&self) -> String { @@ -774,7 +774,8 @@ async fn update_from_dist_( // We limit the backtracking to 21 days by default (half a release cycle). // The limit of 21 days is an arbitrary selection, so we let the user override it. const BACKTRACK_LIMIT_DEFAULT: i32 = 21; - let provided = process() + let provided = download + .process .var("RUSTUP_BACKTRACK_LIMIT") .ok() .and_then(|v| v.parse().ok()) @@ -1061,6 +1062,7 @@ async fn try_update_from_dist_( update_hash, download.tmp_cx, &download.notify_handler, + download.process, ) .await; // inspect, determine what context to add, then process afterwards. @@ -1087,7 +1089,7 @@ pub(crate) async fn dl_v2_manifest( update_hash: Option<&Path>, toolchain: &ToolchainDesc, ) -> Result> { - let manifest_url = toolchain.manifest_v2_url(download.dist_root); + let manifest_url = toolchain.manifest_v2_url(download.dist_root, download.process); match download .download_and_check(&manifest_url, update_hash, ".toml") .await @@ -1134,7 +1136,7 @@ async fn dl_v1_manifest( return Ok(vec![installer_name]); } - let manifest_url = toolchain.manifest_v1_url(download.dist_root); + let manifest_url = toolchain.manifest_v1_url(download.dist_root, download.process); let manifest_dl = download.download_and_check(&manifest_url, None, "").await?; let (manifest_file, _) = manifest_dl.unwrap(); let manifest_str = utils::read_file("manifest", &manifest_file)?; diff --git a/src/dist/download.rs b/src/dist/download.rs index 89413755ae..51ce354a84 100644 --- a/src/dist/download.rs +++ b/src/dist/download.rs @@ -6,6 +6,7 @@ use anyhow::{anyhow, Context, Result}; use sha2::{Digest, Sha256}; use url::Url; +use crate::currentprocess::Process; use crate::dist::notifications::*; use crate::dist::temp; use crate::errors::*; @@ -19,6 +20,7 @@ pub struct DownloadCfg<'a> { pub tmp_cx: &'a temp::Context, pub download_dir: &'a PathBuf, pub notify_handler: &'a dyn Fn(Notification<'_>), + pub process: &'a Process, } pub(crate) struct File { @@ -77,6 +79,7 @@ impl<'a> DownloadCfg<'a> { Some(&mut hasher), true, &|n| (self.notify_handler)(n.into()), + self.process, ) .await { @@ -111,6 +114,7 @@ impl<'a> DownloadCfg<'a> { &partial_file_path, &target_file, self.notify_handler, + self.process, )?; Ok(File { path: target_file }) } @@ -130,9 +134,13 @@ impl<'a> DownloadCfg<'a> { let hash_url = utils::parse_url(&(url.to_owned() + ".sha256"))?; let hash_file = self.tmp_cx.new_file()?; - utils::download_file(&hash_url, &hash_file, None, &|n| { - (self.notify_handler)(n.into()) - }) + utils::download_file( + &hash_url, + &hash_file, + None, + &|n| (self.notify_handler)(n.into()), + self.process, + ) .await?; utils::read_file("hash", &hash_file).map(|s| s[0..64].to_owned()) @@ -171,9 +179,13 @@ impl<'a> DownloadCfg<'a> { let file = self.tmp_cx.new_file_with_ext("", ext)?; let mut hasher = Sha256::new(); - utils::download_file(&url, &file, Some(&mut hasher), &|n| { - (self.notify_handler)(n.into()) - }) + utils::download_file( + &url, + &file, + Some(&mut hasher), + &|n| (self.notify_handler)(n.into()), + self.process, + ) .await?; let actual_hash = format!("{:x}", hasher.finalize()); diff --git a/src/dist/manifestation.rs b/src/dist/manifestation.rs index 6abf92eb82..b6a4aa045f 100644 --- a/src/dist/manifestation.rs +++ b/src/dist/manifestation.rs @@ -9,7 +9,7 @@ use std::path::Path; use anyhow::{anyhow, bail, Context, Result}; use tokio_retry::{strategy::FixedInterval, RetryIf}; -use crate::currentprocess::process; +use crate::currentprocess::Process; use crate::dist::component::{ Components, Package, TarGzPackage, TarXzPackage, TarZStdPackage, Transaction, }; @@ -155,7 +155,8 @@ impl Manifestation { let components = update.components_urls_and_hashes(new_manifest)?; const DEFAULT_MAX_RETRIES: usize = 3; - let max_retries: usize = process() + let max_retries: usize = download_cfg + .process .var("RUSTUP_MAX_RETRIES") .ok() .and_then(|s| s.parse().ok()) @@ -199,11 +200,16 @@ impl Manifestation { } // Begin transaction - let mut tx = Transaction::new(prefix.clone(), tmp_cx, download_cfg.notify_handler); + let mut tx = Transaction::new( + prefix.clone(), + tmp_cx, + download_cfg.notify_handler, + download_cfg.process, + ); // If the previous installation was from a v1 manifest we need // to uninstall it first. - tx = self.maybe_handle_v2_upgrade(&config, tx)?; + tx = self.maybe_handle_v2_upgrade(&config, tx, download_cfg.process)?; // Uninstall components for component in &update.components_to_uninstall { @@ -223,6 +229,7 @@ impl Manifestation { new_manifest, tx, &download_cfg.notify_handler, + download_cfg.process, )?; } @@ -252,15 +259,30 @@ impl Manifestation { utils::FileReaderWithProgress::new_file(&installer_file, ¬ification_converter)?; let package: &dyn Package = match format { CompressionKind::GZip => { - gz = TarGzPackage::new(reader, tmp_cx, Some(¬ification_converter))?; + gz = TarGzPackage::new( + reader, + tmp_cx, + Some(¬ification_converter), + download_cfg.process, + )?; &gz } CompressionKind::XZ => { - xz = TarXzPackage::new(reader, tmp_cx, Some(¬ification_converter))?; + xz = TarXzPackage::new( + reader, + tmp_cx, + Some(¬ification_converter), + download_cfg.process, + )?; &xz } CompressionKind::ZStd => { - zst = TarZStdPackage::new(reader, tmp_cx, Some(¬ification_converter))?; + zst = TarZStdPackage::new( + reader, + tmp_cx, + Some(¬ification_converter), + download_cfg.process, + )?; &zst } }; @@ -308,10 +330,11 @@ impl Manifestation { manifest: &Manifest, tmp_cx: &temp::Context, notify_handler: &dyn Fn(Notification<'_>), + process: &Process, ) -> Result<()> { let prefix = self.installation.prefix(); - let mut tx = Transaction::new(prefix.clone(), tmp_cx, notify_handler); + let mut tx = Transaction::new(prefix.clone(), tmp_cx, notify_handler, process); // Read configuration and delete it let rel_config_path = prefix.rel_manifest_file(CONFIG_FILE); @@ -324,7 +347,7 @@ impl Manifestation { tx.remove_file("dist config", rel_config_path)?; for component in config.components { - tx = self.uninstall_component(&component, manifest, tx, notify_handler)?; + tx = self.uninstall_component(&component, manifest, tx, notify_handler, process)?; } tx.commit(); @@ -337,6 +360,7 @@ impl Manifestation { manifest: &Manifest, mut tx: Transaction<'a>, notify_handler: &dyn Fn(Notification<'_>), + process: &Process, ) -> Result> { // For historical reasons, the rust-installer component // names are not the same as the dist manifest component @@ -345,9 +369,9 @@ impl Manifestation { let name = component.name_in_manifest(); let short_name = component.short_name_in_manifest(); if let Some(c) = self.installation.find(&name)? { - tx = c.uninstall(tx)?; + tx = c.uninstall(tx, process)?; } else if let Some(c) = self.installation.find(short_name)? { - tx = c.uninstall(tx)?; + tx = c.uninstall(tx, process)?; } else { notify_handler(Notification::MissingInstalledComponent( &component.short_name(manifest), @@ -400,6 +424,7 @@ impl Manifestation { update_hash: Option<&Path>, tmp_cx: &temp::Context, notify_handler: &dyn Fn(Notification<'_>), + process: &Process, ) -> Result> { // If there's already a v2 installation then something has gone wrong if self.read_config()?.is_some() { @@ -435,6 +460,7 @@ impl Manifestation { download_dir: &dld_dir, tmp_cx, notify_handler, + process, }; let dl = dlcfg @@ -454,12 +480,12 @@ impl Manifestation { )); // Begin transaction - let mut tx = Transaction::new(prefix, tmp_cx, notify_handler); + let mut tx = Transaction::new(prefix, tmp_cx, notify_handler, process); // Uninstall components let components = self.installation.list()?; for component in components { - tx = component.uninstall(tx)?; + tx = component.uninstall(tx, process)?; } // Install all the components in the installer @@ -469,7 +495,7 @@ impl Manifestation { let reader = utils::FileReaderWithProgress::new_file(&installer_file, ¬ification_converter)?; let package: &dyn Package = - &TarGzPackage::new(reader, tmp_cx, Some(¬ification_converter))?; + &TarGzPackage::new(reader, tmp_cx, Some(¬ification_converter), process)?; for component in package.components() { tx = package.install(&self.installation, &component, None, tx)?; @@ -489,6 +515,7 @@ impl Manifestation { &self, config: &Option, mut tx: Transaction<'a>, + process: &Process, ) -> Result> { let installed_components = self.installation.list()?; let looks_like_v1 = config.is_none() && !installed_components.is_empty(); @@ -498,7 +525,7 @@ impl Manifestation { } for component in installed_components { - tx = component.uninstall(tx)?; + tx = component.uninstall(tx, process)?; } Ok(tx) diff --git a/src/dist/manifestation/tests.rs b/src/dist/manifestation/tests.rs index 4e6b6ce5f9..cba55e722b 100644 --- a/src/dist/manifestation/tests.rs +++ b/src/dist/manifestation/tests.rs @@ -17,7 +17,7 @@ use url::Url; use rustup_macros::unit_test as test; use crate::{ - currentprocess, + currentprocess::{Process, TestProcess}, dist::{ dist::{Profile, TargetTriple, ToolchainDesc, DEFAULT_DIST_SERVER}, download::DownloadCfg, @@ -462,7 +462,14 @@ async fn update_from_dist( // Download the dist manifest and place it into the installation prefix let manifest_url = make_manifest_url(dist_server, toolchain)?; let manifest_file = tmp_cx.new_file()?; - utils::download_file(&manifest_url, &manifest_file, None, &|_| {}).await?; + utils::download_file( + &manifest_url, + &manifest_file, + None, + &|_| {}, + download_cfg.process, + ) + .await?; let manifest_str = utils::read_file("manifest", &manifest_file)?; let manifest = Manifest::parse(&manifest_str)?; @@ -506,12 +513,13 @@ fn uninstall( prefix: &InstallPrefix, tmp_cx: &temp::Context, notify_handler: &dyn Fn(Notification<'_>), + process: &Process, ) -> Result<()> { let trip = toolchain.target.clone(); let manifestation = Manifestation::open(prefix.clone(), trip)?; let manifest = manifestation.load_manifest()?.unwrap(); - manifestation.uninstall(&manifest, tmp_cx, notify_handler)?; + manifestation.uninstall(&manifest, tmp_cx, notify_handler, process)?; Ok(()) } @@ -568,6 +576,13 @@ fn setup_from_dist_server( let toolchain = ToolchainDesc::from_str("nightly-x86_64-apple-darwin").unwrap(); let prefix = InstallPrefix::from(prefix_tempdir.path()); + let tp = TestProcess::new( + env::current_dir().unwrap(), + &["rustup"], + HashMap::default(), + "", + ); + let download_cfg = DownloadCfg { dist_root: "phony", tmp_cx: &tmp_cx, @@ -575,18 +590,10 @@ fn setup_from_dist_server( notify_handler: &|event| { println!("{event}"); }, + process: &tp.process, }; - currentprocess::with( - currentprocess::TestProcess::new( - env::current_dir().unwrap(), - &["rustup"], - HashMap::default(), - "", - ) - .into(), - || f(url, &toolchain, &prefix, &download_cfg, &tmp_cx), - ); + f(url, &toolchain, &prefix, &download_cfg, &tmp_cx) } fn initial_install(comps: Compressions) { @@ -645,7 +652,7 @@ fn test_uninstall() { false, )) .unwrap(); - uninstall(toolchain, prefix, tmp_cx, &|_| ()).unwrap(); + uninstall(toolchain, prefix, tmp_cx, &|_| (), download_cfg.process).unwrap(); assert!(!utils::path_exists(prefix.path().join("bin/rustc"))); assert!(!utils::path_exists(prefix.path().join("lib/libstd.rlib"))); @@ -673,7 +680,7 @@ fn uninstall_removes_config_file() { assert!(utils::path_exists( prefix.manifest_file("multirust-config.toml") )); - uninstall(toolchain, prefix, tmp_cx, &|_| ()).unwrap(); + uninstall(toolchain, prefix, tmp_cx, &|_| (), download_cfg.process).unwrap(); assert!(!utils::path_exists( prefix.manifest_file("multirust-config.toml") )); @@ -2158,6 +2165,7 @@ fn reuse_downloaded_file() { reuse_notification_fired.set(true); } }, + process: download_cfg.process, }; run_future(update_from_dist( @@ -2225,6 +2233,7 @@ fn checks_files_hashes_before_reuse() { noticed_bad_checksum.set(true); } }, + process: download_cfg.process, }; run_future(update_from_dist( diff --git a/src/env_var.rs b/src/env_var.rs index 86d9035924..d6b54e380c 100644 --- a/src/env_var.rs +++ b/src/env_var.rs @@ -3,12 +3,17 @@ use std::env; use std::path::PathBuf; use std::process::Command; -use crate::currentprocess::process; +use crate::currentprocess::Process; pub const RUST_RECURSION_COUNT_MAX: u32 = 20; -pub(crate) fn prepend_path(name: &str, prepend: Vec, cmd: &mut Command) { - let old_value = process().var_os(name); +pub(crate) fn prepend_path( + name: &str, + prepend: Vec, + cmd: &mut Command, + process: &Process, +) { + let old_value = process.var_os(name); let parts = if let Some(ref v) = old_value { let mut tail = env::split_paths(v).collect::>(); for path in prepend.into_iter().rev() { @@ -26,8 +31,8 @@ pub(crate) fn prepend_path(name: &str, prepend: Vec, cmd: &mut Command) } } -pub(crate) fn inc(name: &str, cmd: &mut Command) { - let old_value = process() +pub(crate) fn inc(name: &str, cmd: &mut Command, process: &Process) { + let old_value = process .var(name) .ok() .and_then(|v| v.parse().ok()) @@ -44,7 +49,7 @@ mod tests { use rustup_macros::unit_test as test; use super::*; - use crate::currentprocess; + use crate::currentprocess::TestProcess; use crate::test::{with_saved_path, Env}; #[test] @@ -54,49 +59,44 @@ mod tests { "PATH", env::join_paths(["/home/a/.cargo/bin", "/home/b/.cargo/bin"].iter()).unwrap(), ); - let tp = currentprocess::TestProcess { - vars, - ..Default::default() - }; + let tp = TestProcess::with_vars(vars); with_saved_path(&mut || { - currentprocess::with(tp.clone().into(), || { - let mut path_entries = vec![]; - let mut cmd = Command::new("test"); + let mut path_entries = vec![]; + let mut cmd = Command::new("test"); - let a = OsString::from("/home/a/.cargo/bin"); - let path_a = PathBuf::from(a); - path_entries.push(path_a); + let a = OsString::from("/home/a/.cargo/bin"); + let path_a = PathBuf::from(a); + path_entries.push(path_a); - let _a = OsString::from("/home/a/.cargo/bin"); - let _path_a = PathBuf::from(_a); - path_entries.push(_path_a); + let _a = OsString::from("/home/a/.cargo/bin"); + let _path_a = PathBuf::from(_a); + path_entries.push(_path_a); - let z = OsString::from("/home/z/.cargo/bin"); - let path_z = PathBuf::from(z); - path_entries.push(path_z); + let z = OsString::from("/home/z/.cargo/bin"); + let path_z = PathBuf::from(z); + path_entries.push(path_z); - prepend_path("PATH", path_entries, &mut cmd); - let envs: Vec<_> = cmd.get_envs().collect(); + prepend_path("PATH", path_entries, &mut cmd, &tp.process); + let envs: Vec<_> = cmd.get_envs().collect(); - assert_eq!( - envs, - &[( - OsStr::new("PATH"), - Some( - env::join_paths( - [ - "/home/z/.cargo/bin", - "/home/a/.cargo/bin", - "/home/b/.cargo/bin" - ] - .iter() - ) - .unwrap() - .as_os_str() + assert_eq!( + envs, + &[( + OsStr::new("PATH"), + Some( + env::join_paths( + [ + "/home/z/.cargo/bin", + "/home/a/.cargo/bin", + "/home/b/.cargo/bin" + ] + .iter() ) - ),] - ); - }); + .unwrap() + .as_os_str() + ) + ),] + ); }); } } diff --git a/src/errors.rs b/src/errors.rs index b9067488f7..88af152b6e 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -10,8 +10,8 @@ use std::path::PathBuf; use thiserror::Error as ThisError; use url::Url; -use crate::dist::dist::{TargetTriple, ToolchainDesc}; use crate::{ + dist::dist::{TargetTriple, ToolchainDesc}, dist::manifest::{Component, Manifest}, toolchain::names::{PathBasedToolchainName, ToolchainName}, }; diff --git a/src/install.rs b/src/install.rs index 922570ede7..f2dc07a03a 100644 --- a/src/install.rs +++ b/src/install.rs @@ -28,15 +28,15 @@ pub(crate) enum InstallMethod<'a> { Copy { src: &'a Path, dest: &'a CustomToolchainName, - cfg: &'a Cfg, + cfg: &'a Cfg<'a>, }, Link { src: &'a Path, dest: &'a CustomToolchainName, - cfg: &'a Cfg, + cfg: &'a Cfg<'a>, }, Dist { - cfg: &'a Cfg, + cfg: &'a Cfg<'a>, desc: &'a dist::ToolchainDesc, profile: dist::Profile, update_hash: Option<&'a Path>, @@ -165,7 +165,7 @@ impl<'a> InstallMethod<'a> { } } - fn cfg(&self) -> &Cfg { + fn cfg(&self) -> &Cfg<'_> { match self { InstallMethod::Copy { cfg, .. } => cfg, InstallMethod::Link { cfg, .. } => cfg, diff --git a/src/test.rs b/src/test.rs index 2fbd47d972..630810f406 100644 --- a/src/test.rs +++ b/src/test.rs @@ -16,7 +16,7 @@ use std::process::Command; use anyhow::Result; pub use crate::cli::self_update::test::{with_saved_global_state, with_saved_path}; -use crate::currentprocess; +use crate::currentprocess::TestProcess; use crate::dist::dist::TargetTriple; #[cfg(windows)] @@ -127,8 +127,8 @@ pub fn this_host_triple() -> String { // For windows, this host may be different to the target: we may be // building with i686 toolchain, but on an x86_64 host, so run the // actual detection logic and trust it. - let tp = currentprocess::TestProcess::default(); - return currentprocess::with(tp.into(), || TargetTriple::from_host().unwrap().to_string()); + let tp = TestProcess::default(); + return TargetTriple::from_host(&tp.process).unwrap().to_string(); } let arch = if cfg!(target_arch = "x86") { "i686" diff --git a/src/toolchain/distributable.rs b/src/toolchain/distributable.rs index 63b9d34fd9..3c60bde3bd 100644 --- a/src/toolchain/distributable.rs +++ b/src/toolchain/distributable.rs @@ -28,21 +28,21 @@ use super::{ #[derive(Debug)] pub(crate) struct DistributableToolchain<'a> { toolchain: Toolchain<'a>, - cfg: &'a Cfg, + cfg: &'a Cfg<'a>, desc: ToolchainDesc, } impl<'a> DistributableToolchain<'a> { pub(crate) async fn from_partial( toolchain: Option, - cfg: &'a Cfg, + cfg: &'a Cfg<'a>, ) -> anyhow::Result { Ok(Self::try_from( &Toolchain::from_partial(toolchain, cfg).await?, )?) } - pub(crate) fn new(cfg: &'a Cfg, desc: ToolchainDesc) -> Result { + pub(crate) fn new(cfg: &'a Cfg<'a>, desc: ToolchainDesc) -> Result { Toolchain::new(cfg, (&desc).into()).map(|toolchain| Self { toolchain, cfg, @@ -331,8 +331,8 @@ impl<'a> DistributableToolchain<'a> { #[cfg_attr(feature = "otel", tracing::instrument(err, skip_all))] pub(crate) async fn install( - cfg: &'a Cfg, - desc: &'_ ToolchainDesc, + cfg: &'a Cfg<'a>, + desc: &ToolchainDesc, components: &[&str], targets: &[&str], profile: Profile, @@ -361,8 +361,8 @@ impl<'a> DistributableToolchain<'a> { #[cfg_attr(feature = "otel", tracing::instrument(err, skip_all))] pub async fn install_if_not_installed( - cfg: &'a Cfg, - desc: &'a ToolchainDesc, + cfg: &'a Cfg<'a>, + desc: &ToolchainDesc, ) -> anyhow::Result { (cfg.notify_handler)(Notification::LookingForToolchain(desc)); if Toolchain::exists(cfg, &desc.into())? { @@ -555,7 +555,7 @@ impl<'a> DistributableToolchain<'a> { } pub(crate) fn installed_paths<'b>( - cfg: &'b Cfg, + cfg: &Cfg<'_>, desc: &ToolchainDesc, path: &'b Path, ) -> anyhow::Result>> { @@ -576,7 +576,7 @@ impl<'a> TryFrom<&Toolchain<'a>> for DistributableToolchain<'a> { match value.name() { LocalToolchainName::Named(ToolchainName::Official(desc)) => Ok(Self { toolchain: value.clone(), - cfg: value.cfg(), + cfg: value.cfg, desc: desc.clone(), }), n => Err(RustupError::ComponentsUnsupported(n.to_string())), diff --git a/src/toolchain/toolchain.rs b/src/toolchain/toolchain.rs index b6260080e1..d902877a3b 100644 --- a/src/toolchain/toolchain.rs +++ b/src/toolchain/toolchain.rs @@ -16,7 +16,6 @@ use wait_timeout::ChildExt; use crate::{ config::{ActiveReason, Cfg}, - currentprocess::process, dist::dist::PartialToolchainDesc, env_var, install, notifications::Notification, @@ -32,7 +31,7 @@ use super::{ /// A toolchain installed on the local disk #[derive(Clone, Debug)] pub(crate) struct Toolchain<'a> { - cfg: &'a Cfg, + pub(super) cfg: &'a Cfg<'a>, name: LocalToolchainName, path: PathBuf, } @@ -41,7 +40,7 @@ impl<'a> Toolchain<'a> { pub(crate) async fn from_local( toolchain_name: &LocalToolchainName, install_if_missing: bool, - cfg: &'a Cfg, + cfg: &'a Cfg<'a>, ) -> anyhow::Result> { match toolchain_name { LocalToolchainName::Named(ToolchainName::Official(desc)) => { @@ -76,7 +75,7 @@ impl<'a> Toolchain<'a> { pub(crate) async fn from_partial( toolchain: Option, - cfg: &'a Cfg, + cfg: &'a Cfg<'a>, ) -> anyhow::Result { match toolchain.map(|it| ResolvableToolchainName::from(&it)) { Some(toolchain) => { @@ -90,7 +89,7 @@ impl<'a> Toolchain<'a> { /// Calls Toolchain::new(), but augments the error message with more context /// from the ActiveReason if the toolchain isn't installed. pub(crate) fn with_reason( - cfg: &'a Cfg, + cfg: &'a Cfg<'a>, name: LocalToolchainName, reason: &ActiveReason, ) -> anyhow::Result { @@ -125,7 +124,7 @@ impl<'a> Toolchain<'a> { Err(anyhow!(reason_err).context(format!("override toolchain '{name}' is not installed"))) } - pub(crate) fn new(cfg: &'a Cfg, name: LocalToolchainName) -> Result { + pub(crate) fn new(cfg: &'a Cfg<'a>, name: LocalToolchainName) -> Result { let path = cfg.toolchain_path(&name); if !Toolchain::exists(cfg, &name)? { return Err(match name { @@ -138,7 +137,7 @@ impl<'a> Toolchain<'a> { /// Ok(True) if the toolchain exists. Ok(False) if the toolchain or its /// containing directory don't exist. Err otherwise. - pub(crate) fn exists(cfg: &'a Cfg, name: &LocalToolchainName) -> Result { + pub(crate) fn exists(cfg: &Cfg<'_>, name: &LocalToolchainName) -> Result { let path = cfg.toolchain_path(name); // toolchain validation should have prevented a situation where there is // no base dir, but defensive programming is defensive. @@ -160,10 +159,6 @@ impl<'a> Toolchain<'a> { Ok(opened.is_ok()) } - pub(crate) fn cfg(&self) -> &'a Cfg { - self.cfg - } - pub(crate) fn name(&self) -> &LocalToolchainName { &self.name } @@ -190,14 +185,14 @@ impl<'a> Toolchain<'a> { // versions of Cargo did. Rustup and Cargo should be in sync now (both // using the same `home` crate), but this is retained to ensure cargo // and rustup agree in older versions. - if let Ok(cargo_home) = utils::cargo_home() { + if let Ok(cargo_home) = utils::cargo_home(self.cfg.process) { cmd.env("CARGO_HOME", &cargo_home); } - env_var::inc("RUST_RECURSION_COUNT", cmd); + env_var::inc("RUST_RECURSION_COUNT", cmd, self.cfg.process); cmd.env("RUSTUP_TOOLCHAIN", format!("{}", self.name)); - cmd.env("RUSTUP_HOME", &self.cfg().rustup_dir); + cmd.env("RUSTUP_HOME", &self.cfg.rustup_dir); } /// Apply the appropriate LD path for a command being run from a toolchain. @@ -224,7 +219,9 @@ impl<'a> Toolchain<'a> { pub const LOADER_PATH: &str = "DYLD_FALLBACK_LIBRARY_PATH"; } if cfg!(target_os = "macos") - && process() + && self + .cfg + .process .var_os(sysenv::LOADER_PATH) .filter(|x| x.len() > 0) .is_none() @@ -232,21 +229,21 @@ impl<'a> Toolchain<'a> { // These are the defaults when DYLD_FALLBACK_LIBRARY_PATH isn't // set or set to an empty string. Since we are explicitly setting // the value, make sure the defaults still work. - if let Some(home) = process().var_os("HOME") { + if let Some(home) = self.cfg.process.var_os("HOME") { new_path.push(PathBuf::from(home).join("lib")); } new_path.push(PathBuf::from("/usr/local/lib")); new_path.push(PathBuf::from("/usr/lib")); } - env_var::prepend_path(sysenv::LOADER_PATH, new_path, cmd); + env_var::prepend_path(sysenv::LOADER_PATH, new_path, cmd, self.cfg.process); // Prepend CARGO_HOME/bin to the PATH variable so that we're sure to run // cargo/rustc via the proxy bins. There is no fallback case for if the // proxy bins don't exist. We'll just be running whatever happens to // be on the PATH. let mut path_entries = vec![]; - if let Ok(cargo_home) = utils::cargo_home() { + if let Ok(cargo_home) = utils::cargo_home(self.cfg.process) { path_entries.push(cargo_home.join("bin")); } @@ -264,7 +261,9 @@ impl<'a> Toolchain<'a> { // testing the fix. The default is now off, but this is left here // just in case there are problems. Consider removing in the // future if it doesn't seem necessary. - if process() + if self + .cfg + .process .var_os("RUSTUP_WINDOWS_PATH_ADD_BIN") .map_or(false, |s| s == "1") { @@ -272,7 +271,7 @@ impl<'a> Toolchain<'a> { } } - env_var::prepend_path("PATH", path_entries, cmd); + env_var::prepend_path("PATH", path_entries, cmd, self.cfg.process); } /// Infallible function that describes the version of rustc in an installed distribution @@ -373,7 +372,7 @@ impl<'a> Toolchain<'a> { Ok(None) } - #[cfg_attr(feature="otel", tracing::instrument(err,fields(binary, recursion=process().var("RUST_RECURSION_COUNT").ok())))] + #[cfg_attr(feature="otel", tracing::instrument(err,fields(binary, recursion=self.cfg.process.var("RUST_RECURSION_COUNT").ok())))] fn create_command + Debug>(&self, binary: T) -> Result { // Create the path to this binary within the current toolchain sysroot let binary = if let Some(binary_str) = binary.as_ref().to_str() { @@ -391,7 +390,9 @@ impl<'a> Toolchain<'a> { let path = if utils::is_file(&bin_path) { &bin_path } else { - let recursion_count = process() + let recursion_count = self + .cfg + .process .var("RUST_RECURSION_COUNT") .ok() .and_then(|s| s.parse().ok()) @@ -441,7 +442,7 @@ impl<'a> Toolchain<'a> { /// Remove the toolchain from disk /// /// - pub fn ensure_removed(cfg: &'a Cfg, name: LocalToolchainName) -> anyhow::Result<()> { + pub fn ensure_removed(cfg: &Cfg<'_>, name: LocalToolchainName) -> anyhow::Result<()> { let path = cfg.toolchain_path(&name); let name = match name { LocalToolchainName::Named(t) => t, diff --git a/src/utils/raw.rs b/src/utils/raw.rs index 2820878997..c93296784c 100644 --- a/src/utils/raw.rs +++ b/src/utils/raw.rs @@ -8,7 +8,7 @@ use std::path::Path; use std::str; #[cfg(not(windows))] -use crate::currentprocess::process; +use crate::currentprocess::Process; pub(crate) fn ensure_dir_exists, F: FnOnce(&Path)>( path: P, @@ -272,17 +272,17 @@ pub(crate) fn copy_dir(src: &Path, dest: &Path) -> io::Result<()> { } #[cfg(not(windows))] -fn has_cmd(cmd: &str) -> bool { +fn has_cmd(cmd: &str, process: &Process) -> bool { let cmd = format!("{}{}", cmd, env::consts::EXE_SUFFIX); - let path = process().var_os("PATH").unwrap_or_default(); + let path = process.var_os("PATH").unwrap_or_default(); env::split_paths(&path) .map(|p| p.join(&cmd)) .any(|p| p.exists()) } #[cfg(not(windows))] -pub(crate) fn find_cmd<'a>(cmds: &[&'a str]) -> Option<&'a str> { - cmds.iter().cloned().find(|&s| has_cmd(s)) +pub(crate) fn find_cmd<'a>(cmds: &[&'a str], process: &Process) -> Option<&'a str> { + cmds.iter().cloned().find(|&s| has_cmd(s, process)) } #[cfg(windows)] diff --git a/src/utils/utils.rs b/src/utils/utils.rs index 1f07f309cb..05f85415be 100644 --- a/src/utils/utils.rs +++ b/src/utils/utils.rs @@ -11,7 +11,7 @@ use retry::{retry, OperationResult}; use sha2::Sha256; use url::Url; -use crate::currentprocess::process; +use crate::currentprocess::Process; use crate::errors::*; use crate::utils::notifications::Notification; use crate::utils::raw; @@ -121,8 +121,9 @@ pub async fn download_file( path: &Path, hasher: Option<&mut Sha256>, notify_handler: &dyn Fn(Notification<'_>), + process: &Process, ) -> Result<()> { - download_file_with_resume(url, path, hasher, false, ¬ify_handler).await + download_file_with_resume(url, path, hasher, false, ¬ify_handler, process).await } pub(crate) async fn download_file_with_resume( @@ -131,9 +132,19 @@ pub(crate) async fn download_file_with_resume( hasher: Option<&mut Sha256>, resume_from_partial: bool, notify_handler: &dyn Fn(Notification<'_>), + process: &Process, ) -> Result<()> { use download::DownloadError as DEK; - match download_file_(url, path, hasher, resume_from_partial, notify_handler).await { + match download_file_( + url, + path, + hasher, + resume_from_partial, + notify_handler, + process, + ) + .await + { Ok(_) => Ok(()), Err(e) => { if e.downcast_ref::().is_some() { @@ -169,6 +180,7 @@ async fn download_file_( hasher: Option<&mut Sha256>, resume_from_partial: bool, notify_handler: &dyn Fn(Notification<'_>), + process: &Process, ) -> Result<()> { use download::download_to_path_with_backend; use download::{Backend, Event, TlsBackend}; @@ -206,10 +218,10 @@ async fn download_file_( // Download the file // Keep the curl env var around for a bit - let use_curl_backend = process() + let use_curl_backend = process .var_os("RUSTUP_USE_CURL") .map_or(false, |it| it != "0"); - let use_rustls = process() + let use_rustls = process .var_os("RUSTUP_USE_RUSTLS") .map_or(true, |it| it != "0"); let (backend, notification) = if use_curl_backend { @@ -471,16 +483,16 @@ pub fn current_exe() -> Result { env::current_exe().context(RustupError::LocatingWorkingDir) } -pub(crate) fn home_dir() -> Option { - home::home_dir_with_env(&process()) +pub(crate) fn home_dir(process: &Process) -> Option { + home::home_dir_with_env(process) } -pub(crate) fn cargo_home() -> Result { - home::cargo_home_with_env(&process()).context("failed to determine cargo home") +pub(crate) fn cargo_home(process: &Process) -> Result { + home::cargo_home_with_env(process).context("failed to determine cargo home") } -pub(crate) fn rustup_home() -> Result { - home::rustup_home_with_env(&process()).context("failed to determine rustup home dir") +pub(crate) fn rustup_home(process: &Process) -> Result { + home::rustup_home_with_env(process).context("failed to determine rustup home dir") } pub(crate) fn format_path_for_display(path: &str) -> String { @@ -522,6 +534,8 @@ pub fn rename<'a, N>( src: &'a Path, dest: &'a Path, notify_handler: &'a dyn Fn(N), + #[allow(unused_variables)] // Only used on Linux + process: &Process, ) -> Result<()> where N: From>, @@ -542,7 +556,7 @@ where OperationResult::Retry(e) } #[cfg(target_os = "linux")] - _ if process().var_os("RUSTUP_PERMIT_COPY_RENAME").is_some() + _ if process.var_os("RUSTUP_PERMIT_COPY_RENAME").is_some() && Some(EXDEV) == e.raw_os_error() => { match copy_and_delete(name, src, dest, notify_handler) { diff --git a/tests/suite/dist_install.rs b/tests/suite/dist_install.rs index 8e0f07a878..cf80fc363e 100644 --- a/tests/suite/dist_install.rs +++ b/tests/suite/dist_install.rs @@ -1,6 +1,7 @@ use std::fs::File; use std::io::Write; +use rustup::currentprocess::TestProcess; use rustup::dist::component::Components; use rustup::dist::component::Transaction; use rustup::dist::component::{DirectoryPackage, Package}; @@ -117,7 +118,8 @@ fn basic_install() { Box::new(|_| ()), ); let notify = |_: Notification<'_>| (); - let tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let components = Components::open(prefix).unwrap(); @@ -163,7 +165,8 @@ fn multiple_component_install() { Box::new(|_| ()), ); let notify = |_: Notification<'_>| (); - let tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let components = Components::open(prefix).unwrap(); @@ -213,7 +216,8 @@ fn uninstall() { Box::new(|_| ()), ); let notify = |_: Notification<'_>| (); - let tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let components = Components::open(prefix.clone()).unwrap(); @@ -225,9 +229,10 @@ fn uninstall() { // Now uninstall let notify = |_: Notification<'_>| (); - let mut tx = Transaction::new(prefix, &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let mut tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); for component in components.list().unwrap() { - tx = component.uninstall(tx).unwrap(); + tx = component.uninstall(tx, &tp.process).unwrap(); } tx.commit(); @@ -270,7 +275,8 @@ fn component_bad_version() { Box::new(|_| ()), ); let notify = |_: Notification<'_>| (); - let tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let components = Components::open(prefix.clone()).unwrap(); @@ -316,7 +322,8 @@ fn install_to_prefix_that_does_not_exist() { Box::new(|_| ()), ); let notify = |_: Notification<'_>| (); - let tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify); + let tp = TestProcess::default(); + let tx = Transaction::new(prefix.clone(), &tmp_cx, ¬ify, &tp.process); let components = Components::open(prefix).unwrap();