Skip to content

Commit 0279e8e

Browse files
committed
Fix using global options before an alias.
1 parent 2a0f0c8 commit 0279e8e

File tree

5 files changed

+104
-40
lines changed

5 files changed

+104
-40
lines changed

crates/resolver-tests/tests/resolve.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ proptest! {
6161
config
6262
.configure(
6363
1,
64-
None,
64+
false,
6565
None,
6666
false,
6767
false,
@@ -569,7 +569,7 @@ fn test_resolving_minimum_version_with_transitive_deps() {
569569
config
570570
.configure(
571571
1,
572-
None,
572+
false,
573573
None,
574574
false,
575575
false,

src/bin/cargo/cli.rs

+76-21
Original file line numberDiff line numberDiff line change
@@ -92,16 +92,19 @@ Run with 'cargo -Z [FLAG] [SUBCOMMAND]'"
9292
return Ok(());
9393
}
9494

95-
let args = expand_aliases(config, args)?;
96-
let (cmd, subcommand_args) = match args.subcommand() {
95+
// Global args need to be extracted before expanding aliases because the
96+
// clap code for extracting a subcommand discards global options
97+
// (appearing before the subcommand).
98+
let (expanded_args, global_args) = expand_aliases(config, args)?;
99+
let (cmd, subcommand_args) = match expanded_args.subcommand() {
97100
(cmd, Some(args)) => (cmd, args),
98101
_ => {
99102
// No subcommand provided.
100103
cli().print_help()?;
101104
return Ok(());
102105
}
103106
};
104-
config_configure(config, &args, subcommand_args)?;
107+
config_configure(config, &expanded_args, subcommand_args, global_args)?;
105108
super::init_git_transports(config);
106109

107110
execute_subcommand(config, cmd, subcommand_args)
@@ -129,7 +132,7 @@ pub fn get_version_string(is_verbose: bool) -> String {
129132
fn expand_aliases(
130133
config: &mut Config,
131134
args: ArgMatches<'static>,
132-
) -> Result<ArgMatches<'static>, CliError> {
135+
) -> Result<(ArgMatches<'static>, GlobalArgs), CliError> {
133136
if let (cmd, Some(args)) = args.subcommand() {
134137
match (
135138
commands::builtin_exec(cmd),
@@ -148,41 +151,60 @@ fn expand_aliases(
148151
.unwrap_or_default()
149152
.map(|s| s.to_string()),
150153
);
151-
let args = cli()
154+
// new_args strips out everything before the subcommand, so
155+
// capture those global options now.
156+
// Note that an alias to an external command will not receive
157+
// these arguments. That may be confusing, but such is life.
158+
let global_args = GlobalArgs::new(&args);
159+
let new_args = cli()
152160
.setting(AppSettings::NoBinaryName)
153161
.get_matches_from_safe(alias)?;
154-
return expand_aliases(config, args);
162+
let (expanded_args, _) = expand_aliases(config, new_args)?;
163+
return Ok((expanded_args, global_args));
155164
}
156165
(_, None) => {}
157166
}
158167
};
159168

160-
Ok(args)
169+
Ok((args, GlobalArgs::default()))
161170
}
162171

163172
fn config_configure(
164173
config: &mut Config,
165174
args: &ArgMatches<'_>,
166175
subcommand_args: &ArgMatches<'_>,
176+
global_args: GlobalArgs,
167177
) -> CliResult {
168178
let arg_target_dir = &subcommand_args.value_of_path("target-dir", config);
169-
let config_args: Vec<&str> = args.values_of("config").unwrap_or_default().collect();
170-
let quiet = if args.is_present("quiet") || subcommand_args.is_present("quiet") {
171-
Some(true)
172-
} else {
173-
None
174-
};
179+
let verbose = global_args.verbose + args.occurrences_of("verbose") as u32;
180+
// quiet is unusual because it is redefined in some subcommands in order
181+
// to provide custom help text.
182+
let quiet =
183+
args.is_present("quiet") || subcommand_args.is_present("quiet") || global_args.quiet;
184+
let global_color = global_args.color; // Extract so it can take reference.
185+
let color = args
186+
.value_of("color")
187+
.or_else(|| global_color.as_ref().map(|s| s.as_ref()));
188+
let frozen = args.is_present("frozen") || global_args.frozen;
189+
let locked = args.is_present("locked") || global_args.locked;
190+
let offline = args.is_present("offline") || global_args.offline;
191+
let mut unstable_flags = global_args.unstable_flags;
192+
if let Some(values) = args.values_of("unstable-features") {
193+
unstable_flags.extend(values.map(|s| s.to_string()));
194+
}
195+
let mut config_args = global_args.config_args;
196+
if let Some(values) = args.values_of("config") {
197+
config_args.extend(values.map(|s| s.to_string()));
198+
}
175199
config.configure(
176-
args.occurrences_of("verbose") as u32,
200+
verbose,
177201
quiet,
178-
args.value_of("color"),
179-
args.is_present("frozen"),
180-
args.is_present("locked"),
181-
args.is_present("offline"),
202+
color,
203+
frozen,
204+
locked,
205+
offline,
182206
arg_target_dir,
183-
&args
184-
.values_of_lossy("unstable-features")
185-
.unwrap_or_default(),
207+
&unstable_flags,
186208
&config_args,
187209
)?;
188210
Ok(())
@@ -202,6 +224,39 @@ fn execute_subcommand(
202224
super::execute_external_subcommand(config, cmd, &ext_args)
203225
}
204226

227+
#[derive(Default)]
228+
struct GlobalArgs {
229+
verbose: u32,
230+
quiet: bool,
231+
color: Option<String>,
232+
frozen: bool,
233+
locked: bool,
234+
offline: bool,
235+
unstable_flags: Vec<String>,
236+
config_args: Vec<String>,
237+
}
238+
239+
impl GlobalArgs {
240+
fn new(args: &ArgMatches<'_>) -> GlobalArgs {
241+
GlobalArgs {
242+
verbose: args.occurrences_of("verbose") as u32,
243+
quiet: args.is_present("quiet"),
244+
color: args.value_of("color").map(|s| s.to_string()),
245+
frozen: args.is_present("frozen"),
246+
locked: args.is_present("locked"),
247+
offline: args.is_present("offline"),
248+
unstable_flags: args
249+
.values_of_lossy("unstable-features")
250+
.unwrap_or_default(),
251+
config_args: args
252+
.values_of("config")
253+
.unwrap_or_default()
254+
.map(|s| s.to_string())
255+
.collect(),
256+
}
257+
}
258+
}
259+
205260
fn cli() -> App {
206261
App::new("cargo")
207262
.settings(&[

src/cargo/util/config/mod.rs

+8-14
Original file line numberDiff line numberDiff line change
@@ -602,14 +602,14 @@ impl Config {
602602
pub fn configure(
603603
&mut self,
604604
verbose: u32,
605-
quiet: Option<bool>,
605+
quiet: bool,
606606
color: Option<&str>,
607607
frozen: bool,
608608
locked: bool,
609609
offline: bool,
610610
target_dir: &Option<PathBuf>,
611611
unstable_flags: &[String],
612-
cli_config: &[&str],
612+
cli_config: &[String],
613613
) -> CargoResult<()> {
614614
self.unstable_flags.parse(unstable_flags)?;
615615
if !cli_config.is_empty() {
@@ -618,7 +618,7 @@ impl Config {
618618
self.merge_cli_args()?;
619619
}
620620
let extra_verbose = verbose >= 2;
621-
let verbose = if verbose == 0 { None } else { Some(true) };
621+
let verbose = verbose != 0;
622622

623623
#[derive(Deserialize, Default)]
624624
struct TermConfig {
@@ -632,25 +632,19 @@ impl Config {
632632
let color = color.or_else(|| term.color.as_ref().map(|s| s.as_ref()));
633633

634634
let verbosity = match (verbose, term.verbose, quiet) {
635-
(Some(true), _, None) | (None, Some(true), None) => Verbosity::Verbose,
635+
(true, _, false) | (_, Some(true), false) => Verbosity::Verbose,
636636

637637
// Command line takes precedence over configuration, so ignore the
638638
// configuration..
639-
(None, _, Some(true)) => Verbosity::Quiet,
639+
(false, _, true) => Verbosity::Quiet,
640640

641641
// Can't pass both at the same time on the command line regardless
642642
// of configuration.
643-
(Some(true), _, Some(true)) => {
643+
(true, _, true) => {
644644
bail!("cannot set both --verbose and --quiet");
645645
}
646646

647-
// Can't actually get `Some(false)` as a value from the command
648-
// line, so just ignore them here to appease exhaustiveness checking
649-
// in match statements.
650-
(Some(false), _, _)
651-
| (_, _, Some(false))
652-
| (None, Some(false), None)
653-
| (None, None, None) => Verbosity::Normal,
647+
(false, _, false) => Verbosity::Normal,
654648
};
655649

656650
let cli_target_dir = match target_dir.as_ref() {
@@ -659,7 +653,7 @@ impl Config {
659653
};
660654

661655
self.shell().set_verbosity(verbosity);
662-
self.shell().set_color_choice(color.map(|s| &s[..]))?;
656+
self.shell().set_color_choice(color)?;
663657
self.extra_verbose = extra_verbose;
664658
self.frozen = frozen;
665659
self.locked = locked;

tests/testsuite/cargo_alias_config.rs

+16
Original file line numberDiff line numberDiff line change
@@ -176,3 +176,19 @@ fn builtin_alias_takes_options() {
176176

177177
p.cargo("r --example ex1 -- asdf").with_stdout("asdf").run();
178178
}
179+
180+
#[cargo_test]
181+
fn global_options_with_alias() {
182+
// Check that global options are passed through.
183+
let p = project().file("src/lib.rs", "").build();
184+
185+
p.cargo("-v c")
186+
.with_stderr(
187+
"\
188+
[CHECKING] foo [..]
189+
[RUNNING] `rustc [..]
190+
[FINISHED] dev [..]
191+
",
192+
)
193+
.run();
194+
}

tests/testsuite/config.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -76,17 +76,16 @@ impl ConfigBuilder {
7676
let homedir = paths::home();
7777
let mut config = Config::new(shell, cwd, homedir);
7878
config.set_env(self.env.clone());
79-
let config_args: Vec<&str> = self.config_args.iter().map(AsRef::as_ref).collect();
8079
config.configure(
8180
0,
82-
None,
81+
false,
8382
None,
8483
false,
8584
false,
8685
false,
8786
&None,
8887
&self.unstable,
89-
&config_args,
88+
&self.config_args,
9089
)?;
9190
Ok(config)
9291
}

0 commit comments

Comments
 (0)