diff --git a/src/bin/cargo/main.rs b/src/bin/cargo/main.rs index ff691095187..28cd18d248b 100644 --- a/src/bin/cargo/main.rs +++ b/src/bin/cargo/main.rs @@ -235,7 +235,7 @@ fn is_executable>(path: P) -> bool { } fn search_directories(config: &Config) -> Vec { - let mut path_dirs = if let Some(val) = env::var_os("PATH") { + let mut path_dirs = if let Some(val) = config.get_env_os("PATH") { env::split_paths(&val).collect() } else { vec![] diff --git a/src/cargo/core/compiler/build_context/target_info.rs b/src/cargo/core/compiler/build_context/target_info.rs index 31702bc94d0..f8f92683b65 100644 --- a/src/cargo/core/compiler/build_context/target_info.rs +++ b/src/cargo/core/compiler/build_context/target_info.rs @@ -20,7 +20,6 @@ use cargo_util::{paths, ProcessBuilder}; use serde::{Deserialize, Serialize}; use std::cell::RefCell; use std::collections::hash_map::{Entry, HashMap}; -use std::env; use std::path::{Path, PathBuf}; use std::str::{self, FromStr}; @@ -731,7 +730,7 @@ fn extra_args( // NOTE: It is impossible to have a [host] section and reach this logic with kind.is_host(), // since [host] implies `target-applies-to-host = false`, which always early-returns above. - if let Some(rustflags) = rustflags_from_env(flags) { + if let Some(rustflags) = rustflags_from_env(config, flags) { Ok(rustflags) } else if let Some(rustflags) = rustflags_from_target(config, host_triple, target_cfg, kind, flags)? @@ -746,10 +745,10 @@ fn extra_args( /// Gets compiler flags from environment variables. /// See [`extra_args`] for more. -fn rustflags_from_env(flags: Flags) -> Option> { +fn rustflags_from_env(config: &Config, flags: Flags) -> Option> { // First try CARGO_ENCODED_RUSTFLAGS from the environment. // Prefer this over RUSTFLAGS since it's less prone to encoding errors. - if let Ok(a) = env::var(format!("CARGO_ENCODED_{}", flags.as_env())) { + if let Ok(a) = config.get_env(format!("CARGO_ENCODED_{}", flags.as_env())) { if a.is_empty() { return Some(Vec::new()); } @@ -757,7 +756,7 @@ fn rustflags_from_env(flags: Flags) -> Option> { } // Then try RUSTFLAGS from the environment - if let Ok(a) = env::var(flags.as_env()) { + if let Ok(a) = config.get_env(flags.as_env()) { let args = a .split(' ') .map(str::trim) @@ -855,7 +854,7 @@ pub struct RustcTargetData<'cfg> { pub rustc: Rustc, /// Config - config: &'cfg Config, + pub config: &'cfg Config, requested_kinds: Vec, /// Build information for the "host", which is information about when diff --git a/src/cargo/core/compiler/compilation.rs b/src/cargo/core/compiler/compilation.rs index 3e50095a49f..8fe5d939082 100644 --- a/src/cargo/core/compiler/compilation.rs +++ b/src/cargo/core/compiler/compilation.rs @@ -1,7 +1,6 @@ //! Type definitions for the result of a compilation. use std::collections::{BTreeSet, HashMap}; -use std::env; use std::ffi::{OsStr, OsString}; use std::path::PathBuf; @@ -295,7 +294,7 @@ impl<'cfg> Compilation<'cfg> { // These are the defaults when DYLD_FALLBACK_LIBRARY_PATH isn't // set or set to an empty string. Since Cargo is explicitly setting // the value, make sure the defaults still work. - if let Some(home) = env::var_os("HOME") { + if let Some(home) = self.config.get_env_os("HOME") { search_path.push(PathBuf::from(home).join("lib")); } search_path.push(PathBuf::from("/usr/local/lib")); @@ -362,7 +361,7 @@ impl<'cfg> Compilation<'cfg> { continue; } - if value.is_force() || env::var_os(key).is_none() { + if value.is_force() || self.config.get_env_os(key).is_none() { cmd.env(key, value.resolve(self.config)); } } diff --git a/src/cargo/core/compiler/context/compilation_files.rs b/src/cargo/core/compiler/context/compilation_files.rs index b025a6d53d4..1c9d28461f9 100644 --- a/src/cargo/core/compiler/context/compilation_files.rs +++ b/src/cargo/core/compiler/context/compilation_files.rs @@ -1,7 +1,6 @@ //! See [`CompilationFiles`]. use std::collections::HashMap; -use std::env; use std::fmt; use std::hash::{Hash, Hasher}; use std::path::{Path, PathBuf}; @@ -628,7 +627,7 @@ fn compute_metadata( // Seed the contents of `__CARGO_DEFAULT_LIB_METADATA` to the hasher if present. // This should be the release channel, to get a different hash for each channel. - if let Ok(ref channel) = env::var("__CARGO_DEFAULT_LIB_METADATA") { + if let Ok(ref channel) = cx.bcx.config.get_env("__CARGO_DEFAULT_LIB_METADATA") { channel.hash(&mut hasher); } @@ -717,7 +716,7 @@ fn should_use_metadata(bcx: &BuildContext<'_, '_>, unit: &Unit) -> bool { || (unit.target.is_executable() && short_name == "wasm32-unknown-emscripten") || (unit.target.is_executable() && short_name.contains("msvc"))) && unit.pkg.package_id().source_id().is_path() - && env::var("__CARGO_DEFAULT_LIB_METADATA").is_err() + && bcx.config.get_env("__CARGO_DEFAULT_LIB_METADATA").is_err() { return false; } diff --git a/src/cargo/core/compiler/standard_lib.rs b/src/cargo/core/compiler/standard_lib.rs index 2955dce05c5..b5ac0c997d1 100644 --- a/src/cargo/core/compiler/standard_lib.rs +++ b/src/cargo/core/compiler/standard_lib.rs @@ -11,7 +11,6 @@ use crate::ops::{self, Packages}; use crate::util::errors::CargoResult; use crate::Config; use std::collections::{HashMap, HashSet}; -use std::env; use std::path::PathBuf; use super::BuildConfig; @@ -222,7 +221,7 @@ pub fn generate_std_roots( } fn detect_sysroot_src_path(target_data: &RustcTargetData<'_>) -> CargoResult { - if let Some(s) = env::var_os("__CARGO_TESTS_ONLY_SRC_ROOT") { + if let Some(s) = target_data.config.get_env_os("__CARGO_TESTS_ONLY_SRC_ROOT") { return Ok(s.into()); } @@ -241,7 +240,7 @@ fn detect_sysroot_src_path(target_data: &RustcTargetData<'_>) -> CargoResult { anyhow::bail!("{} --toolchain {}", msg, rustup_toolchain); } diff --git a/src/cargo/core/profiles.rs b/src/cargo/core/profiles.rs index 80bc2e7ce95..51d19e32e62 100644 --- a/src/cargo/core/profiles.rs +++ b/src/cargo/core/profiles.rs @@ -31,7 +31,7 @@ use crate::util::{closest_msg, config, CargoResult, Config}; use anyhow::{bail, Context as _}; use std::collections::{BTreeMap, HashMap, HashSet}; use std::hash::Hash; -use std::{cmp, env, fmt, hash}; +use std::{cmp, fmt, hash}; /// Collection of all profiles. /// @@ -62,7 +62,7 @@ pub struct Profiles { impl Profiles { pub fn new(ws: &Workspace<'_>, requested_profile: InternedString) -> CargoResult { let config = ws.config(); - let incremental = match env::var_os("CARGO_INCREMENTAL") { + let incremental = match config.get_env_os("CARGO_INCREMENTAL") { Some(v) => Some(v == "1"), None => config.build_config()?.incremental, }; diff --git a/src/cargo/ops/cargo_compile/mod.rs b/src/cargo/ops/cargo_compile/mod.rs index b0aba266d2e..2113422b3ac 100644 --- a/src/cargo/ops/cargo_compile/mod.rs +++ b/src/cargo/ops/cargo_compile/mod.rs @@ -215,14 +215,14 @@ pub fn create_bcx<'a, 'cfg>( | CompileMode::Check { .. } | CompileMode::Bench | CompileMode::RunCustomBuild => { - if std::env::var("RUST_FLAGS").is_ok() { + if ws.config().get_env("RUST_FLAGS").is_ok() { config.shell().warn( "Cargo does not read `RUST_FLAGS` environment variable. Did you mean `RUSTFLAGS`?", )?; } } CompileMode::Doc { .. } | CompileMode::Doctest | CompileMode::Docscrape => { - if std::env::var("RUSTDOC_FLAGS").is_ok() { + if ws.config().get_env("RUSTDOC_FLAGS").is_ok() { config.shell().warn( "Cargo does not read `RUSTDOC_FLAGS` environment variable. Did you mean `RUSTDOCFLAGS`?" )?; diff --git a/src/cargo/ops/cargo_config.rs b/src/cargo/ops/cargo_config.rs index f27ed22da92..2277bd6f836 100644 --- a/src/cargo/ops/cargo_config.rs +++ b/src/cargo/ops/cargo_config.rs @@ -93,7 +93,7 @@ fn maybe_env<'config>( config: &'config Config, key: &ConfigKey, cv: &CV, -) -> Option> { +) -> Option> { // Only fetching a table is unable to load env values. Leaf entries should // work properly. match cv { @@ -102,7 +102,6 @@ fn maybe_env<'config>( } let mut env: Vec<_> = config .env() - .iter() .filter(|(env_key, _val)| env_key.starts_with(&format!("{}_", key.as_env_key()))) .collect(); env.sort_by_key(|x| x.0); @@ -162,7 +161,7 @@ fn print_toml(config: &Config, opts: &GetOptions<'_>, key: &ConfigKey, cv: &CV) } } -fn print_toml_env(config: &Config, env: &[(&String, &String)]) { +fn print_toml_env(config: &Config, env: &[(&str, &str)]) { drop_println!( config, "# The following environment variables may affect the loaded values." @@ -173,7 +172,7 @@ fn print_toml_env(config: &Config, env: &[(&String, &String)]) { } } -fn print_json_env(config: &Config, env: &[(&String, &String)]) { +fn print_json_env(config: &Config, env: &[(&str, &str)]) { drop_eprintln!( config, "note: The following environment variables may affect the loaded values." @@ -287,7 +286,6 @@ fn print_toml_unmerged(config: &Config, opts: &GetOptions<'_>, key: &ConfigKey) // special, and will just naturally get loaded as part of the config. let mut env: Vec<_> = config .env() - .iter() .filter(|(env_key, _val)| env_key.starts_with(key.as_env_key())) .collect(); if !env.is_empty() { diff --git a/src/cargo/ops/cargo_doc.rs b/src/cargo/ops/cargo_doc.rs index b4f2b7d1714..afa6ac327d8 100644 --- a/src/cargo/ops/cargo_doc.rs +++ b/src/cargo/ops/cargo_doc.rs @@ -1,6 +1,6 @@ use crate::core::{Shell, Workspace}; use crate::ops; -use crate::util::config::PathAndArgs; +use crate::util::config::{Config, PathAndArgs}; use crate::util::CargoResult; use std::path::Path; use std::path::PathBuf; @@ -37,7 +37,7 @@ pub fn doc(ws: &Workspace<'_>, options: &DocOptions) -> CargoResult<()> { let mut shell = ws.config().shell(); shell.status("Opening", path.display())?; - open_docs(&path, &mut shell, config_browser)?; + open_docs(&path, &mut shell, config_browser, ws.config())?; } } @@ -48,9 +48,10 @@ fn open_docs( path: &Path, shell: &mut Shell, config_browser: Option<(PathBuf, Vec)>, + config: &Config, ) -> CargoResult<()> { let browser = - config_browser.or_else(|| Some((PathBuf::from(std::env::var_os("BROWSER")?), Vec::new()))); + config_browser.or_else(|| Some((PathBuf::from(config.get_env_os("BROWSER")?), Vec::new()))); match browser { Some((browser, initial_args)) => { diff --git a/src/cargo/ops/cargo_install.rs b/src/cargo/ops/cargo_install.rs index c9fcbc40574..f3aebd7c9e8 100644 --- a/src/cargo/ops/cargo_install.rs +++ b/src/cargo/ops/cargo_install.rs @@ -704,7 +704,7 @@ pub fn install( if installed_anything { // Print a warning that if this directory isn't in PATH that they won't be // able to run these commands. - let path = env::var_os("PATH").unwrap_or_default(); + let path = config.get_env_os("PATH").unwrap_or_default(); let dst_in_path = env::split_paths(&path).any(|path| path == dst); if !dst_in_path { diff --git a/src/cargo/ops/cargo_new.rs b/src/cargo/ops/cargo_new.rs index e2d49379615..7fa66736474 100644 --- a/src/cargo/ops/cargo_new.rs +++ b/src/cargo/ops/cargo_new.rs @@ -463,7 +463,7 @@ pub fn new(opts: &NewOptions, config: &Config) -> CargoResult<()> { pub fn init(opts: &NewOptions, config: &Config) -> CargoResult { // This is here just as a random location to exercise the internal error handling. - if std::env::var_os("__CARGO_TEST_INTERNAL_ERROR").is_some() { + if config.get_env_os("__CARGO_TEST_INTERNAL_ERROR").is_some() { return Err(crate::util::internal("internal error test")); } diff --git a/src/cargo/ops/common_for_install_and_uninstall.rs b/src/cargo/ops/common_for_install_and_uninstall.rs index 8da70ce1cdb..81083c7fbf8 100644 --- a/src/cargo/ops/common_for_install_and_uninstall.rs +++ b/src/cargo/ops/common_for_install_and_uninstall.rs @@ -506,7 +506,7 @@ pub fn resolve_root(flag: Option<&str>, config: &Config) -> CargoResult CargoResult<()> { let args = FixArgs::get()?; trace!("cargo-fix as rustc got file {:?}", args.file); - let workspace_rustc = std::env::var("RUSTC_WORKSPACE_WRAPPER") + let workspace_rustc = config + .get_env("RUSTC_WORKSPACE_WRAPPER") .map(PathBuf::from) .ok(); let mut rustc = ProcessBuilder::new(&args.rustc).wrapped(workspace_rustc.as_ref()); @@ -388,7 +389,7 @@ pub fn fix_exec_rustc(config: &Config, lock_addr: &str) -> CargoResult<()> { file: path.clone(), fixes: file.fixes_applied, } - .post()?; + .post(config)?; } } @@ -403,7 +404,7 @@ pub fn fix_exec_rustc(config: &Config, lock_addr: &str) -> CargoResult<()> { // user's code with our changes. Back out everything and fall through // below to recompile again. if !output.status.success() { - if env::var_os(BROKEN_CODE_ENV).is_none() { + if config.get_env_os(BROKEN_CODE_ENV).is_none() { for (path, file) in fixes.files.iter() { debug!("reverting {:?} due to errors", path); paths::write(path, &file.original_code)?; @@ -420,7 +421,7 @@ pub fn fix_exec_rustc(config: &Config, lock_addr: &str) -> CargoResult<()> { } krate }; - log_failed_fix(krate, &output.stderr, output.status)?; + log_failed_fix(config, krate, &output.stderr, output.status)?; } } @@ -510,7 +511,8 @@ fn rustfix_crate( // definitely can't make progress, so bail out. let mut fixes = FixedCrate::default(); let mut last_fix_counts = HashMap::new(); - let iterations = env::var("CARGO_FIX_MAX_RETRIES") + let iterations = config + .get_env("CARGO_FIX_MAX_RETRIES") .ok() .and_then(|n| n.parse().ok()) .unwrap_or(4); @@ -547,7 +549,7 @@ fn rustfix_crate( file: path.clone(), message: error, } - .post()?; + .post(config)?; } } @@ -576,7 +578,7 @@ fn rustfix_and_fix( // worse by applying fixes where a bug could cause *more* broken code. // Instead, punt upwards which will reexec rustc over the original code, // displaying pretty versions of the diagnostics we just read out. - if !output.status.success() && env::var_os(BROKEN_CODE_ENV).is_none() { + if !output.status.success() && config.get_env_os(BROKEN_CODE_ENV).is_none() { debug!( "rustfixing `{:?}` failed, rustc exited with {:?}", filename, @@ -585,7 +587,8 @@ fn rustfix_and_fix( return Ok(()); } - let fix_mode = env::var_os("__CARGO_FIX_YOLO") + let fix_mode = config + .get_env_os("__CARGO_FIX_YOLO") .map(|_| rustfix::Filter::Everything) .unwrap_or(rustfix::Filter::MachineApplicableOnly); @@ -710,7 +713,12 @@ fn exit_with(status: ExitStatus) -> ! { process::exit(status.code().unwrap_or(3)); } -fn log_failed_fix(krate: Option, stderr: &[u8], status: ExitStatus) -> CargoResult<()> { +fn log_failed_fix( + config: &Config, + krate: Option, + stderr: &[u8], + status: ExitStatus, +) -> CargoResult<()> { let stderr = str::from_utf8(stderr).context("failed to parse rustc stderr as utf-8")?; let diagnostics = stderr @@ -745,7 +753,7 @@ fn log_failed_fix(krate: Option, stderr: &[u8], status: ExitStatus) -> C errors, abnormal_exit, } - .post()?; + .post(config)?; Ok(()) } @@ -895,7 +903,7 @@ impl FixArgs { return Message::Fixing { file: self.file.display().to_string(), } - .post() + .post(config) .and(Ok(true)); } }; @@ -922,7 +930,7 @@ impl FixArgs { message, edition: to_edition.previous().unwrap(), } - .post() + .post(config) .and(Ok(false)); // Do not run rustfix for this the edition. } let from_edition = self.enabled_edition.unwrap_or(Edition::Edition2015); @@ -937,14 +945,14 @@ impl FixArgs { message, edition: to_edition, } - .post() + .post(config) } else { Message::Migrating { file: self.file.display().to_string(), from_edition, to_edition, } - .post() + .post(config) } .and(Ok(true)) } diff --git a/src/cargo/ops/registry.rs b/src/cargo/ops/registry.rs index a94617511fa..fca1342bab5 100644 --- a/src/cargo/ops/registry.rs +++ b/src/cargo/ops/registry.rs @@ -1,3 +1,4 @@ +use std::cmp; use std::collections::{BTreeMap, HashSet}; use std::fs::File; use std::io::{self, BufRead}; @@ -6,7 +7,6 @@ use std::path::PathBuf; use std::str; use std::task::Poll; use std::time::Duration; -use std::{cmp, env}; use anyhow::{anyhow, bail, format_err, Context as _}; use cargo_util::paths; @@ -596,7 +596,7 @@ pub fn http_handle_and_timeout(config: &Config) -> CargoResult<(Easy, HttpTimeou pub fn needs_custom_http_transport(config: &Config) -> CargoResult { Ok(http_proxy_exists(config)? || *config.http_config()? != Default::default() - || env::var_os("HTTP_TIMEOUT").is_some()) + || config.get_env_os("HTTP_TIMEOUT").is_some()) } /// Configure a libcurl http handle with the defaults options for Cargo @@ -721,11 +721,16 @@ pub struct HttpTimeout { impl HttpTimeout { pub fn new(config: &Config) -> CargoResult { - let config = config.http_config()?; - let low_speed_limit = config.low_speed_limit.unwrap_or(10); - let seconds = config + let http_config = config.http_config()?; + let low_speed_limit = http_config.low_speed_limit.unwrap_or(10); + let seconds = http_config .timeout - .or_else(|| env::var("HTTP_TIMEOUT").ok().and_then(|s| s.parse().ok())) + .or_else(|| { + config + .get_env("HTTP_TIMEOUT") + .ok() + .and_then(|s| s.parse().ok()) + }) .unwrap_or(30); Ok(HttpTimeout { dur: Duration::new(seconds, 0), @@ -779,7 +784,7 @@ fn http_proxy_exists(config: &Config) -> CargoResult { } else { Ok(["http_proxy", "HTTP_PROXY", "https_proxy", "HTTPS_PROXY"] .iter() - .any(|v| env::var(v).is_ok())) + .any(|v| config.get_env(v).is_ok())) } } diff --git a/src/cargo/sources/config.rs b/src/cargo/sources/config.rs index 880831bf808..97a23a0b41c 100644 --- a/src/cargo/sources/config.rs +++ b/src/cargo/sources/config.rs @@ -100,7 +100,7 @@ impl<'cfg> SourceConfigMap<'cfg> { }, )?; } - if let Ok(url) = std::env::var("__CARGO_TEST_CRATES_IO_URL_DO_NOT_USE_THIS") { + if let Ok(url) = config.get_env("__CARGO_TEST_CRATES_IO_URL_DO_NOT_USE_THIS") { base.add( CRATES_IO_REGISTRY, SourceConfig { diff --git a/src/cargo/sources/git/known_hosts.rs b/src/cargo/sources/git/known_hosts.rs index e50b7a5043a..7916b07f595 100644 --- a/src/cargo/sources/git/known_hosts.rs +++ b/src/cargo/sources/git/known_hosts.rs @@ -19,7 +19,7 @@ //! added (it just adds a little complexity). For example, hostname patterns, //! and revoked markers. See "FIXME" comments littered in this file. -use crate::util::config::{Definition, Value}; +use crate::util::config::{Config, Definition, Value}; use git2::cert::{Cert, SshHostKeyType}; use git2::CertificateCheckStatus; use hmac::Mac; @@ -111,6 +111,7 @@ impl Display for KnownHostLocation { /// The git2 callback used to validate a certificate (only ssh known hosts are validated). pub fn certificate_check( + config: &Config, cert: &Cert<'_>, host: &str, port: Option, @@ -129,7 +130,12 @@ pub fn certificate_check( _ => host.to_string(), }; // The error message must be constructed as a string to pass through the libgit2 C API. - let err_msg = match check_ssh_known_hosts(host_key, &host_maybe_port, config_known_hosts) { + let err_msg = match check_ssh_known_hosts( + config, + host_key, + &host_maybe_port, + config_known_hosts, + ) { Ok(()) => { return Ok(CertificateCheckStatus::CertificateOk); } @@ -146,7 +152,7 @@ pub fn certificate_check( // Try checking without the port. if port.is_some() && !matches!(port, Some(22)) - && check_ssh_known_hosts(host_key, host, config_known_hosts).is_ok() + && check_ssh_known_hosts(config, host_key, host, config_known_hosts).is_ok() { return Ok(CertificateCheckStatus::CertificateOk); } @@ -288,6 +294,7 @@ pub fn certificate_check( /// Checks if the given host/host key pair is known. fn check_ssh_known_hosts( + config: &Config, cert_host_key: &git2::cert::CertHostkey<'_>, host: &str, config_known_hosts: Option<&Vec>>, @@ -299,7 +306,7 @@ fn check_ssh_known_hosts( // Collect all the known host entries from disk. let mut known_hosts = Vec::new(); - for path in known_host_files() { + for path in known_host_files(config) { if !path.exists() { continue; } @@ -464,9 +471,12 @@ fn check_ssh_known_hosts_loaded( } /// Returns a list of files to try loading OpenSSH-formatted known hosts. -fn known_host_files() -> Vec { +fn known_host_files(config: &Config) -> Vec { let mut result = Vec::new(); - if std::env::var_os("__CARGO_TEST_DISABLE_GLOBAL_KNOWN_HOST").is_some() { + if config + .get_env_os("__CARGO_TEST_DISABLE_GLOBAL_KNOWN_HOST") + .is_some() + { } else if cfg!(unix) { result.push(PathBuf::from("/etc/ssh/ssh_known_hosts")); } else if cfg!(windows) { @@ -475,7 +485,7 @@ fn known_host_files() -> Vec { // However, I do not know of a way to obtain that location from // Windows-land. The ProgramData version here is what the PowerShell // port of OpenSSH does. - if let Some(progdata) = std::env::var_os("ProgramData") { + if let Some(progdata) = config.get_env_os("ProgramData") { let mut progdata = PathBuf::from(progdata); progdata.push("ssh"); progdata.push("ssh_known_hosts"); diff --git a/src/cargo/sources/git/utils.rs b/src/cargo/sources/git/utils.rs index da899ad178a..1979cf8a3a0 100644 --- a/src/cargo/sources/git/utils.rs +++ b/src/cargo/sources/git/utils.rs @@ -12,7 +12,6 @@ use log::{debug, info}; use serde::ser; use serde::Serialize; use std::borrow::Cow; -use std::env; use std::fmt; use std::path::{Path, PathBuf}; use std::process::Command; @@ -472,7 +471,12 @@ impl<'a> GitCheckout<'a> { /// credentials until we give it a reason to not do so. To ensure we don't /// just sit here looping forever we keep track of authentications we've /// attempted and we don't try the same ones again. -fn with_authentication(url: &str, cfg: &git2::Config, mut f: F) -> CargoResult +fn with_authentication( + cargo_config: &Config, + url: &str, + cfg: &git2::Config, + mut f: F, +) -> CargoResult where F: FnMut(&mut git2::Credentials<'_>) -> CargoResult, { @@ -577,7 +581,10 @@ where if ssh_username_requested { debug_assert!(res.is_err()); let mut attempts = vec![String::from("git")]; - if let Ok(s) = env::var("USER").or_else(|_| env::var("USERNAME")) { + if let Ok(s) = cargo_config + .get_env("USER") + .or_else(|_| cargo_config.get_env("USERNAME")) + { attempts.push(s); } if let Some(ref s) = cred_helper.username { @@ -730,7 +737,7 @@ pub fn with_fetch_options( let config_known_hosts = ssh_config.and_then(|ssh| ssh.known_hosts.as_ref()); let diagnostic_home_config = config.diagnostic_home_config(); network::with_retry(config, || { - with_authentication(url, git_config, |f| { + with_authentication(config, url, git_config, |f| { let port = Url::parse(url).ok().and_then(|url| url.port()); let mut last_update = Instant::now(); let mut rcb = git2::RemoteCallbacks::new(); @@ -740,6 +747,7 @@ pub fn with_fetch_options( rcb.credentials(f); rcb.certificate_check(|cert, host| { super::known_hosts::certificate_check( + config, cert, host, port, @@ -824,7 +832,7 @@ pub fn fetch( // repo check to see if it's a little too old and could benefit from a gc. // In theory this shouldn't be too expensive compared to the network // request we're about to issue. - maybe_gc_repo(repo)?; + maybe_gc_repo(repo, config)?; clean_repo_temp_files(repo); @@ -978,7 +986,7 @@ fn fetch_with_cli( /// we may not even have `git` installed on the system! As a result we /// opportunistically try a `git gc` when the pack directory looks too big, and /// failing that we just blow away the repository and start over. -fn maybe_gc_repo(repo: &mut git2::Repository) -> CargoResult<()> { +fn maybe_gc_repo(repo: &mut git2::Repository, config: &Config) -> CargoResult<()> { // Here we arbitrarily declare that if you have more than 100 files in your // `pack` folder that we need to do a gc. let entries = match repo.path().join("objects/pack").read_dir() { @@ -988,7 +996,8 @@ fn maybe_gc_repo(repo: &mut git2::Repository) -> CargoResult<()> { return Ok(()); } }; - let max = env::var("__CARGO_PACKFILE_LIMIT") + let max = config + .get_env("__CARGO_PACKFILE_LIMIT") .ok() .and_then(|s| s.parse::().ok()) .unwrap_or(100); diff --git a/src/cargo/sources/registry/mod.rs b/src/cargo/sources/registry/mod.rs index 0edd0ab8f07..5e84a1165cc 100644 --- a/src/cargo/sources/registry/mod.rs +++ b/src/cargo/sources/registry/mod.rs @@ -636,7 +636,7 @@ impl<'cfg> RegistrySource<'cfg> { } dst.create_dir()?; let mut tar = { - let size_limit = max_unpack_size(tarball.metadata()?.len()); + let size_limit = max_unpack_size(self.config, tarball.metadata()?.len()); let gz = GzDecoder::new(tarball); let gz = LimitErrorReader::new(gz, size_limit); Archive::new(gz) @@ -880,21 +880,23 @@ impl<'cfg> Source for RegistrySource<'cfg> { /// * /// * /// * -fn max_unpack_size(size: u64) -> u64 { +fn max_unpack_size(config: &Config, size: u64) -> u64 { const SIZE_VAR: &str = "__CARGO_TEST_MAX_UNPACK_SIZE"; const RATIO_VAR: &str = "__CARGO_TEST_MAX_UNPACK_RATIO"; - let max_unpack_size = if cfg!(debug_assertions) && std::env::var(SIZE_VAR).is_ok() { + let max_unpack_size = if cfg!(debug_assertions) && config.get_env(SIZE_VAR).is_ok() { // For integration test only. - std::env::var(SIZE_VAR) + config + .get_env(SIZE_VAR) .unwrap() .parse() .expect("a max unpack size in bytes") } else { MAX_UNPACK_SIZE }; - let max_compression_ratio = if cfg!(debug_assertions) && std::env::var(RATIO_VAR).is_ok() { + let max_compression_ratio = if cfg!(debug_assertions) && config.get_env(RATIO_VAR).is_ok() { // For integration test only. - std::env::var(RATIO_VAR) + config + .get_env(RATIO_VAR) .unwrap() .parse() .expect("a max compression ratio in bytes") diff --git a/src/cargo/util/auth.rs b/src/cargo/util/auth.rs index 2a70ea37e27..f7d0fc8c4a2 100644 --- a/src/cargo/util/auth.rs +++ b/src/cargo/util/auth.rs @@ -182,7 +182,6 @@ pub fn registry_credential_config( let index = sid.canonical_url(); let mut names: Vec<_> = config .env() - .iter() .filter_map(|(k, v)| { Some(( k.strip_prefix("CARGO_REGISTRIES_")? diff --git a/src/cargo/util/config/de.rs b/src/cargo/util/config/de.rs index 1408f15b573..ab4dd93b36a 100644 --- a/src/cargo/util/config/de.rs +++ b/src/cargo/util/config/de.rs @@ -215,7 +215,7 @@ impl<'config> ConfigMapAccess<'config> { if de.config.cli_unstable().advanced_env { // `CARGO_PROFILE_DEV_PACKAGE_` let env_prefix = format!("{}_", de.key.as_env_key()); - for env_key in de.config.env.keys() { + for env_key in de.config.env_keys() { if env_key.starts_with(&env_prefix) { // `CARGO_PROFILE_DEV_PACKAGE_bar_OPT_LEVEL = 3` let rest = &env_key[env_prefix.len()..]; @@ -265,7 +265,7 @@ impl<'config> ConfigMapAccess<'config> { for field in given_fields { let mut field_key = de.key.clone(); field_key.push(field); - for env_key in de.config.env.keys() { + for env_key in de.config.env_keys() { if env_key.starts_with(field_key.as_env_key()) { fields.insert(KeyKind::Normal(field.to_string())); } @@ -424,7 +424,7 @@ impl<'config> ValueDeserializer<'config> { let definition = { let env = de.key.as_env_key(); let env_def = Definition::Environment(env.to_string()); - match (de.config.env.contains_key(env), de.config.get_cv(&de.key)?) { + match (de.config.env_has_key(env), de.config.get_cv(&de.key)?) { (true, Some(cv)) => { // Both, pick highest priority. if env_def.is_higher_priority(cv.definition()) { diff --git a/src/cargo/util/config/mod.rs b/src/cargo/util/config/mod.rs index 6ac5e47741d..abf3b56bbb7 100644 --- a/src/cargo/util/config/mod.rs +++ b/src/cargo/util/config/mod.rs @@ -54,7 +54,7 @@ use std::cell::{RefCell, RefMut}; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::{HashMap, HashSet}; use std::env; -use std::ffi::OsStr; +use std::ffi::{OsStr, OsString}; use std::fmt; use std::fs::{self, File}; use std::io::prelude::*; @@ -106,7 +106,7 @@ macro_rules! get_value_typed { /// Low-level private method for getting a config value as an OptValue. fn $name(&self, key: &ConfigKey) -> Result, ConfigError> { let cv = self.get_cv(key)?; - let env = self.get_env::<$ty>(key)?; + let env = self.get_config_env::<$ty>(key)?; match (cv, env) { (Some(CV::$variant(val, definition)), Some(env)) => { if definition.is_higher_priority(&env.definition) { @@ -200,7 +200,7 @@ pub struct Config { /// Target Directory via resolved Cli parameter target_dir: Option, /// Environment variables, separated to assist testing. - env: HashMap, + env: HashMap, /// Environment variables, converted to uppercase to check for case mismatch upper_case_env: HashMap, /// Tracks which sources have been updated to avoid multiple updates. @@ -260,23 +260,16 @@ impl Config { } }); - let env: HashMap<_, _> = env::vars_os() - .filter_map(|(k, v)| { - // Ignore any key/values that are not valid Unicode. - match (k.into_string(), v.into_string()) { - (Ok(k), Ok(v)) => Some((k, v)), - _ => None, - } - }) - .collect(); + let env: HashMap<_, _> = env::vars_os().collect(); let upper_case_env = env - .clone() - .into_iter() - .map(|(k, _)| (k.to_uppercase().replace("-", "_"), k)) + .iter() + .filter_map(|(k, _)| k.to_str()) // Only keep valid UTF-8 + .map(|k| (k.to_uppercase().replace("-", "_"), k.to_owned())) .collect(); - let cache_rustc_info = match env.get("CARGO_CACHE_RUSTC_INFO") { + let cache_key: &OsStr = "CARGO_CACHE_RUSTC_INFO".as_ref(); + let cache_rustc_info = match env.get(cache_key) { Some(cache) => cache != "0", _ => true, }; @@ -440,17 +433,18 @@ impl Config { pub fn cargo_exe(&self) -> CargoResult<&Path> { self.cargo_exe .try_borrow_with(|| { - fn from_env() -> CargoResult { + let from_env = || -> CargoResult { // Try re-using the `cargo` set in the environment already. This allows // commands that use Cargo as a library to inherit (via `cargo `) // or set (by setting `$CARGO`) a correct path to `cargo` when the current exe // is not actually cargo (e.g., `cargo-*` binaries, Valgrind, `ld.so`, etc.). - let exe = env::var_os(crate::CARGO_ENV) + let exe = self + .get_env_os(crate::CARGO_ENV) .map(PathBuf::from) .ok_or_else(|| anyhow!("$CARGO not set"))? .canonicalize()?; Ok(exe) - } + }; fn from_current_exe() -> CargoResult { // Try fetching the path to `cargo` using `env::current_exe()`. @@ -565,7 +559,7 @@ impl Config { pub fn target_dir(&self) -> CargoResult> { if let Some(dir) = &self.target_dir { Ok(Some(dir.clone())) - } else if let Some(dir) = self.env.get("CARGO_TARGET_DIR") { + } else if let Some(dir) = self.get_env_os("CARGO_TARGET_DIR") { // Check if the CARGO_TARGET_DIR environment variable is set to an empty string. if dir.is_empty() { bail!( @@ -663,7 +657,7 @@ impl Config { // Root table can't have env value. return Ok(cv); } - let env = self.env.get(key.as_env_key()); + let env = self.get_env_str(key.as_env_key()); let env_def = Definition::Environment(key.as_env_key().to_string()); let use_env = match (&cv, env) { // Lists are always merged. @@ -734,20 +728,28 @@ impl Config { /// Helper primarily for testing. pub fn set_env(&mut self, env: HashMap) { - self.env = env; + self.env = env.into_iter().map(|(k, v)| (k.into(), v.into())).collect(); } - /// Returns all environment variables. - pub(crate) fn env(&self) -> &HashMap { - &self.env + /// Returns all environment variables as an iterator, filtering out entries + /// that are not valid UTF-8. + pub(crate) fn env(&self) -> impl Iterator { + self.env + .iter() + .filter_map(|(k, v)| Some((k.to_str()?, v.to_str()?))) } - fn get_env(&self, key: &ConfigKey) -> Result, ConfigError> + /// Returns all environment variable keys, filtering out entries that are not valid UTF-8. + fn env_keys(&self) -> impl Iterator { + self.env.iter().filter_map(|(k, _)| k.to_str()) + } + + fn get_config_env(&self, key: &ConfigKey) -> Result, ConfigError> where T: FromStr, ::Err: fmt::Display, { - match self.env.get(key.as_env_key()) { + match self.get_env_str(key.as_env_key()) { Some(value) => { let definition = Definition::Environment(key.as_env_key().to_string()); Ok(Some(Value { @@ -764,16 +766,48 @@ impl Config { } } + /// Get the value of environment variable `key` through the `Config` snapshot. + /// + /// This can be used similarly to `std::env::var`. + pub fn get_env(&self, key: impl AsRef) -> CargoResult { + let key = key.as_ref(); + let s = match self.env.get(key) { + Some(s) => s, + None => bail!("{key:?} could not be found in the environment snapshot",), + }; + match s.to_str() { + Some(s) => Ok(s.to_owned()), + None => bail!("environment variable value is not valid unicode: {s:?}"), + } + } + + /// Get the value of environment variable `key` through the `Config` snapshot. + /// + /// This can be used similarly to `std::env::var_os`. + pub fn get_env_os(&self, key: impl AsRef) -> Option { + self.env.get(key.as_ref()).cloned() + } + + /// Get the value of environment variable `key`. + /// Returns `None` if `key` is not in `self.env` or if the value is not valid UTF-8. + fn get_env_str(&self, key: impl AsRef) -> Option<&str> { + self.env.get(key.as_ref()).and_then(|s| s.to_str()) + } + + fn env_has_key(&self, key: impl AsRef) -> bool { + self.env.contains_key(key.as_ref()) + } + /// Check if the [`Config`] contains a given [`ConfigKey`]. /// /// See `ConfigMapAccess` for a description of `env_prefix_ok`. fn has_key(&self, key: &ConfigKey, env_prefix_ok: bool) -> CargoResult { - if self.env.contains_key(key.as_env_key()) { + if self.env_has_key(key.as_env_key()) { return Ok(true); } if env_prefix_ok { let env_prefix = format!("{}_", key.as_env_key()); - if self.env.keys().any(|k| k.starts_with(&env_prefix)) { + if self.env_keys().any(|k| k.starts_with(&env_prefix)) { return Ok(true); } } @@ -885,7 +919,7 @@ impl Config { key: &ConfigKey, output: &mut Vec<(String, Definition)>, ) -> CargoResult<()> { - let env_val = match self.env.get(key.as_env_key()) { + let env_val = match self.get_env_str(key.as_env_key()) { Some(v) => v, None => { self.check_environment_key_case_mismatch(key); @@ -1616,12 +1650,9 @@ impl Config { ) -> Option { let var = tool.to_uppercase(); - match env::var_os(&var) { + match self.get_env_os(&var).as_ref().and_then(|s| s.to_str()) { Some(tool_path) => { - let maybe_relative = match tool_path.to_str() { - Some(s) => s.contains('/') || s.contains('\\'), - None => false, - }; + let maybe_relative = tool_path.contains('/') || tool_path.contains('\\'); let path = if maybe_relative { self.cwd.join(tool_path) } else { diff --git a/src/cargo/util/diagnostic_server.rs b/src/cargo/util/diagnostic_server.rs index 1c18f994a88..cc531426090 100644 --- a/src/cargo/util/diagnostic_server.rs +++ b/src/cargo/util/diagnostic_server.rs @@ -2,7 +2,6 @@ //! cross-platform way for the `cargo fix` command. use std::collections::HashSet; -use std::env; use std::io::{BufReader, Read, Write}; use std::net::{Shutdown, SocketAddr, TcpListener, TcpStream}; use std::sync::atomic::{AtomicBool, Ordering}; @@ -61,9 +60,10 @@ pub enum Message { } impl Message { - pub fn post(&self) -> Result<(), Error> { - let addr = - env::var(DIAGNOSTICS_SERVER_VAR).context("diagnostics collector misconfigured")?; + pub fn post(&self, config: &Config) -> Result<(), Error> { + let addr = config + .get_env(DIAGNOSTICS_SERVER_VAR) + .context("diagnostics collector misconfigured")?; let mut client = TcpStream::connect(&addr).context("failed to connect to parent diagnostics target")?; diff --git a/src/cargo/util/progress.rs b/src/cargo/util/progress.rs index ac19e874db3..f2758485011 100644 --- a/src/cargo/util/progress.rs +++ b/src/cargo/util/progress.rs @@ -1,5 +1,4 @@ use std::cmp; -use std::env; use std::time::{Duration, Instant}; use crate::core::shell::Verbosity; @@ -44,7 +43,7 @@ impl<'cfg> Progress<'cfg> { // report no progress when -q (for quiet) or TERM=dumb are set // or if running on Continuous Integration service like Travis where the // output logs get mangled. - let dumb = match env::var("TERM") { + let dumb = match cfg.get_env("TERM") { Ok(term) => term == "dumb", Err(_) => false, };