Skip to content

Commit

Permalink
refactor(cli): Lazily do first-pass config loading
Browse files Browse the repository at this point in the history
This will be a help for cases like #10952 which I would expect would
assert that the config is not loaded before changing the current_dir.
  • Loading branch information
epage committed Sep 1, 2022
1 parent ddc21ea commit eda1652
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 13 deletions.
51 changes: 48 additions & 3 deletions src/bin/cargo/cli.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use anyhow::anyhow;
use cargo::core::shell::Shell;
use cargo::core::{features, CliUnstable};
use cargo::{self, drop_print, drop_println, CliResult, Config};
use clap::{AppSettings, Arg, ArgMatches};
Expand All @@ -21,12 +22,13 @@ lazy_static::lazy_static! {
]);
}

pub fn main(config: &mut Config) -> CliResult {
pub fn main(config: &mut LazyConfig) -> CliResult {
let args = cli().try_get_matches()?;

// CAUTION: Be careful with using `config` until it is configured below.
// In general, try to avoid loading config values unless necessary (like
// the [alias] table).

let args = cli().try_get_matches()?;
let config = config.get_mut();

// Global args need to be extracted before expanding aliases because the
// clap code for extracting a subcommand discards global options
Expand Down Expand Up @@ -463,6 +465,49 @@ See 'cargo help <command>' for more information on a specific command.\n",
.subcommands(commands::builtin())
}

/// Delay loading [`Config`] until access.
///
/// In the common path, the [`Config`] is dependent on CLI parsing and shouldn't be loaded until
/// after that is done but some other paths (like fix or earlier errors) might need access to it,
/// so this provides a way to share the instance and the implementation across these different
/// accesses.
pub struct LazyConfig {
config: Option<Config>,
}

impl LazyConfig {
pub fn new() -> Self {
Self { config: None }
}

/// Check whether the config is loaded
///
/// This is useful for asserts in case the environment needs to be setup before loading
pub fn is_init(&self) -> bool {
self.config.is_some()
}

/// Get the config, loading it if needed
///
/// On error, the process is terminated
pub fn get(&mut self) -> &Config {
self.get_mut()
}

/// Get the config, loading it if needed
///
/// On error, the process is terminated
pub fn get_mut(&mut self) -> &mut Config {
self.config.get_or_insert_with(|| match Config::default() {
Ok(cfg) => cfg,
Err(e) => {
let mut shell = Shell::new();
cargo::exit_with_error(e.into(), &mut shell)
}
})
}
}

#[test]
fn verify_cli() {
cli().debug_assert();
Expand Down
13 changes: 3 additions & 10 deletions src/bin/cargo/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#![warn(rust_2018_idioms)] // while we're getting used to 2018
#![allow(clippy::all)]

use cargo::core::shell::Shell;
use cargo::util::toml::StringOrVec;
use cargo::util::CliError;
use cargo::util::{self, closest_msg, command_prelude, CargoResult, CliResult, Config};
Expand All @@ -22,23 +21,17 @@ fn main() {
#[cfg(not(feature = "pretty-env-logger"))]
env_logger::init_from_env("CARGO_LOG");

let mut config = match Config::default() {
Ok(cfg) => cfg,
Err(e) => {
let mut shell = Shell::new();
cargo::exit_with_error(e.into(), &mut shell)
}
};
let mut config = cli::LazyConfig::new();

let result = if let Some(lock_addr) = cargo::ops::fix_get_proxy_lock_addr() {
cargo::ops::fix_exec_rustc(&config, &lock_addr).map_err(|e| CliError::from(e))
cargo::ops::fix_exec_rustc(config.get(), &lock_addr).map_err(|e| CliError::from(e))
} else {
let _token = cargo::util::job::setup();
cli::main(&mut config)
};

match result {
Err(e) => cargo::exit_with_error(e, &mut *config.shell()),
Err(e) => cargo::exit_with_error(e, &mut config.get_mut().shell()),
Ok(()) => {}
}
}
Expand Down

0 comments on commit eda1652

Please sign in to comment.