Auto merge of #11727 - kylematsuda:config-env, r=ehuss

Read environment variables through `Config` instead of `std::env::var(_os)`

### What does this PR try to resolve?

Adds two new methods `get_env` and `get_env_os` for reading environment variables from the `Config` and uses them to replace the many of the calls to `std::env::var` and `var_os`  (see the discussion in #11588).

Closes #11588
This commit is contained in:
bors 2023-02-21 16:31:51 +00:00
commit 7b98113f6c
23 changed files with 176 additions and 118 deletions

View file

@ -235,7 +235,7 @@ fn is_executable<P: AsRef<Path>>(path: P) -> bool {
}
fn search_directories(config: &Config) -> Vec<PathBuf> {
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![]

View file

@ -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<Vec<String>> {
fn rustflags_from_env(config: &Config, flags: Flags) -> Option<Vec<String>> {
// 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<Vec<String>> {
}
// 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<CompileKind>,
/// Build information for the "host", which is information about when

View file

@ -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));
}
}

View file

@ -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;
}

View file

@ -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<PathBuf> {
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<Pat
library, try:\n rustup component add rust-src",
lock
);
match env::var("RUSTUP_TOOLCHAIN") {
match target_data.config.get_env("RUSTUP_TOOLCHAIN") {
Ok(rustup_toolchain) => {
anyhow::bail!("{} --toolchain {}", msg, rustup_toolchain);
}

View file

@ -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<Profiles> {
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,
};

View file

@ -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`?"
)?;

View file

@ -93,7 +93,7 @@ fn maybe_env<'config>(
config: &'config Config,
key: &ConfigKey,
cv: &CV,
) -> Option<Vec<(&'config String, &'config String)>> {
) -> Option<Vec<(&'config str, &'config str)>> {
// 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() {

View file

@ -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<String>)>,
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)) => {

View file

@ -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 {

View file

@ -463,7 +463,7 @@ pub fn new(opts: &NewOptions, config: &Config) -> CargoResult<()> {
pub fn init(opts: &NewOptions, config: &Config) -> CargoResult<NewProjectKind> {
// 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"));
}

View file

@ -506,7 +506,7 @@ pub fn resolve_root(flag: Option<&str>, config: &Config) -> CargoResult<Filesyst
let config_root = config.get_path("install.root")?;
Ok(flag
.map(PathBuf::from)
.or_else(|| env::var_os("CARGO_INSTALL_ROOT").map(PathBuf::from))
.or_else(|| config.get_env_os("CARGO_INSTALL_ROOT").map(PathBuf::from))
.or_else(move || config_root.map(|v| v.val))
.map(Filesystem::new)
.unwrap_or_else(|| config.home().clone()))

View file

@ -354,7 +354,8 @@ pub fn fix_exec_rustc(config: &Config, lock_addr: &str) -> 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<String>, stderr: &[u8], status: ExitStatus) -> CargoResult<()> {
fn log_failed_fix(
config: &Config,
krate: Option<String>,
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<String>, 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))
}

View file

@ -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<bool> {
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<HttpTimeout> {
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<bool> {
} 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()))
}
}

View file

@ -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 {

View file

@ -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<u16>,
@ -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<Value<String>>>,
@ -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<PathBuf> {
fn known_host_files(config: &Config) -> Vec<PathBuf> {
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<PathBuf> {
// 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");

View file

@ -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<T, F>(url: &str, cfg: &git2::Config, mut f: F) -> CargoResult<T>
fn with_authentication<T, F>(
cargo_config: &Config,
url: &str,
cfg: &git2::Config,
mut f: F,
) -> CargoResult<T>
where
F: FnMut(&mut git2::Credentials<'_>) -> CargoResult<T>,
{
@ -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::<usize>().ok())
.unwrap_or(100);

View file

@ -674,7 +674,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)
@ -918,21 +918,23 @@ impl<'cfg> Source for RegistrySource<'cfg> {
/// * <https://cran.r-project.org/web/packages/brotli/vignettes/brotli-2015-09-22.pdf>
/// * <https://blog.cloudflare.com/results-experimenting-brotli/>
/// * <https://tukaani.org/lzma/benchmarks.html>
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")

View file

@ -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_")?

View file

@ -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()) {

View file

@ -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<OptValue<$ty>, 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<Filesystem>,
/// Environment variables, separated to assist testing.
env: HashMap<String, String>,
env: HashMap<OsString, OsString>,
/// Environment variables, converted to uppercase to check for case mismatch
upper_case_env: HashMap<String, String>,
/// 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<PathBuf> {
let from_env = || -> CargoResult<PathBuf> {
// Try re-using the `cargo` set in the environment already. This allows
// commands that use Cargo as a library to inherit (via `cargo <subcommand>`)
// 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<PathBuf> {
// Try fetching the path to `cargo` using `env::current_exe()`.
@ -565,7 +559,7 @@ impl Config {
pub fn target_dir(&self) -> CargoResult<Option<Filesystem>> {
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<String, String>) {
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<String, String> {
&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<Item = (&str, &str)> {
self.env
.iter()
.filter_map(|(k, v)| Some((k.to_str()?, v.to_str()?)))
}
fn get_env<T>(&self, key: &ConfigKey) -> Result<OptValue<T>, ConfigError>
/// Returns all environment variable keys, filtering out entries that are not valid UTF-8.
fn env_keys(&self) -> impl Iterator<Item = &str> {
self.env.iter().filter_map(|(k, _)| k.to_str())
}
fn get_config_env<T>(&self, key: &ConfigKey) -> Result<OptValue<T>, ConfigError>
where
T: FromStr,
<T as 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<OsStr>) -> CargoResult<String> {
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<OsStr>) -> Option<OsString> {
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<OsStr>) -> Option<&str> {
self.env.get(key.as_ref()).and_then(|s| s.to_str())
}
fn env_has_key(&self, key: impl AsRef<OsStr>) -> 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<bool> {
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<PathBuf> {
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 {

View file

@ -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")?;

View file

@ -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,
};