Skip to content

Commit d92bd3a

Browse files
committed
Auto merge of #6505 - DamianX:print_examples, r=ehuss
--{example,bin,bench,test} with no argument now lists all available targets ``` cargo run --bin error: "--bin" takes one argument. Available binaries: cargo error: process didn't exit successfully: `target\debug\cargo.exe run --bin` (exit code: 101) ``` Previous PR: #5062 Closes #2548 Notes: - `optional_opt` is a weird name, can someone come up with a better one? - Should I call clap's `usage()` as well when printing the error message?
2 parents b84e625 + a51759c commit d92bd3a

File tree

15 files changed

+347
-19
lines changed

15 files changed

+347
-19
lines changed

src/bin/cargo/commands/bench.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,8 @@ Compilation can be customized with the `bench` profile in the manifest.
7272

7373
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
7474
let ws = args.workspace(config)?;
75-
let mut compile_opts = args.compile_options(config, CompileMode::Bench)?;
75+
let mut compile_opts = args.compile_options(config, CompileMode::Bench, Some(&ws))?;
76+
7677
compile_opts.build_config.release = true;
7778

7879
let ops = TestOptions {

src/bin/cargo/commands/build.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,8 @@ the --release flag will use the `release` profile instead.
4848

4949
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
5050
let ws = args.workspace(config)?;
51-
let mut compile_opts = args.compile_options(config, CompileMode::Build)?;
51+
let mut compile_opts = args.compile_options(config, CompileMode::Build, Some(&ws))?;
52+
5253
compile_opts.export_dir = args.value_of_path("out-dir", config);
5354
if compile_opts.export_dir.is_some() && !config.cli_unstable().unstable_options {
5455
Err(failure::format_err!(

src/bin/cargo/commands/check.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
6868
}
6969
};
7070
let mode = CompileMode::Check { test };
71-
let compile_opts = args.compile_options(config, mode)?;
71+
let compile_opts = args.compile_options(config, mode, Some(&ws))?;
72+
7273
ops::compile(&ws, &compile_opts)?;
7374
Ok(())
7475
}

src/bin/cargo/commands/doc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
5050
let mode = CompileMode::Doc {
5151
deps: !args.is_present("no-deps"),
5252
};
53-
let mut compile_opts = args.compile_options(config, mode)?;
53+
let mut compile_opts = args.compile_options(config, mode, Some(&ws))?;
5454
compile_opts.local_rustdoc_args = if args.is_present("document-private-items") {
5555
Some(vec!["--document-private-items".to_string()])
5656
} else {

src/bin/cargo/commands/fix.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,8 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
122122

123123
// Unlike other commands default `cargo fix` to all targets to fix as much
124124
// code as we can.
125-
let mut opts = args.compile_options(config, mode)?;
125+
let mut opts = args.compile_options(config, mode, Some(&ws))?;
126+
126127
if let CompileFilter::Default { .. } = opts.filter {
127128
opts.filter = CompileFilter::Only {
128129
all_targets: true,

src/bin/cargo/commands/install.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,9 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
7878
let registry = args.registry(config)?;
7979

8080
config.reload_rooted_at_cargo_home()?;
81-
let mut compile_opts = args.compile_options(config, CompileMode::Build)?;
81+
82+
let workspace = args.workspace(config).ok();
83+
let mut compile_opts = args.compile_options(config, CompileMode::Build, workspace.as_ref())?;
8284

8385
compile_opts.build_config.release = !args.is_present("debug");
8486

src/bin/cargo/commands/run.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ run. If you're passing arguments to both Cargo and the binary, the ones after
3939
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
4040
let ws = args.workspace(config)?;
4141

42-
let mut compile_opts = args.compile_options(config, CompileMode::Build)?;
42+
let mut compile_opts = args.compile_options(config, CompileMode::Build, Some(&ws))?;
43+
4344
if !args.is_present("example") && !args.is_present("bin") {
4445
let default_runs: Vec<_> = compile_opts
4546
.spec

src/bin/cargo/commands/rustc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
6262
return Err(CliError::new(err, 101));
6363
}
6464
};
65-
let mut compile_opts = args.compile_options_for_single_package(config, mode)?;
65+
let mut compile_opts = args.compile_options_for_single_package(config, mode, Some(&ws))?;
6666
let target_args = values(args, "args");
6767
compile_opts.target_rustc_args = if target_args.is_empty() {
6868
None

src/bin/cargo/commands/rustdoc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ the `cargo help pkgid` command.
5151
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
5252
let ws = args.workspace(config)?;
5353
let mut compile_opts =
54-
args.compile_options_for_single_package(config, CompileMode::Doc { deps: false })?;
54+
args.compile_options_for_single_package(config, CompileMode::Doc { deps: false }, Some(&ws))?;
5555
let target_args = values(args, "args");
5656
compile_opts.target_rustdoc_args = if target_args.is_empty() {
5757
None

src/bin/cargo/commands/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ To get the list of all options available for the test binaries use this:
9292
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
9393
let ws = args.workspace(config)?;
9494

95-
let mut compile_opts = args.compile_options(config, CompileMode::Test)?;
95+
let mut compile_opts = args.compile_options(config, CompileMode::Test, Some(&ws))?;
9696

9797
let doc = args.is_present("doc");
9898
if doc {

src/cargo/util/command_prelude.rs

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ use crate::ops::{CompileFilter, CompileOptions, NewOptions, Packages, VersionCon
77
use crate::sources::CRATES_IO_REGISTRY;
88
use crate::util::important_paths::find_root_manifest_for_wd;
99
use crate::util::{paths, validate_package_name};
10+
use crate::util::{
11+
print_available_benches, print_available_binaries, print_available_examples,
12+
print_available_tests,
13+
};
1014
use crate::CargoResult;
1115
use clap::{self, SubCommand};
1216

@@ -60,18 +64,18 @@ pub trait AppExt: Sized {
6064
all: &'static str,
6165
) -> Self {
6266
self.arg_targets_lib_bin(lib, bin, bins)
63-
._arg(multi_opt("example", "NAME", example))
67+
._arg(optional_multi_opt("example", "NAME", example))
6468
._arg(opt("examples", examples))
65-
._arg(multi_opt("test", "NAME", test))
69+
._arg(optional_multi_opt("test", "NAME", test))
6670
._arg(opt("tests", tests))
67-
._arg(multi_opt("bench", "NAME", bench))
71+
._arg(optional_multi_opt("bench", "NAME", bench))
6872
._arg(opt("benches", benches))
6973
._arg(opt("all-targets", all))
7074
}
7175

7276
fn arg_targets_lib_bin(self, lib: &'static str, bin: &'static str, bins: &'static str) -> Self {
7377
self._arg(opt("lib", lib))
74-
._arg(multi_opt("bin", "NAME", bin))
78+
._arg(optional_multi_opt("bin", "NAME", bin))
7579
._arg(opt("bins", bins))
7680
}
7781

@@ -82,15 +86,15 @@ pub trait AppExt: Sized {
8286
example: &'static str,
8387
examples: &'static str,
8488
) -> Self {
85-
self._arg(multi_opt("bin", "NAME", bin))
89+
self._arg(optional_multi_opt("bin", "NAME", bin))
8690
._arg(opt("bins", bins))
87-
._arg(multi_opt("example", "NAME", example))
91+
._arg(optional_multi_opt("example", "NAME", example))
8892
._arg(opt("examples", examples))
8993
}
9094

9195
fn arg_targets_bin_example(self, bin: &'static str, example: &'static str) -> Self {
92-
self._arg(multi_opt("bin", "NAME", bin))
93-
._arg(multi_opt("example", "NAME", example))
96+
self._arg(optional_multi_opt("bin", "NAME", bin))
97+
._arg(optional_multi_opt("example", "NAME", example))
9498
}
9599

96100
fn arg_features(self) -> Self {
@@ -193,6 +197,18 @@ pub fn opt(name: &'static str, help: &'static str) -> Arg<'static, 'static> {
193197
Arg::with_name(name).long(name).help(help)
194198
}
195199

200+
pub fn optional_multi_opt(
201+
name: &'static str,
202+
value_name: &'static str,
203+
help: &'static str,
204+
) -> Arg<'static, 'static> {
205+
opt(name, help)
206+
.value_name(value_name)
207+
.multiple(true)
208+
.min_values(0)
209+
.number_of_values(1)
210+
}
211+
196212
pub fn multi_opt(
197213
name: &'static str,
198214
value_name: &'static str,
@@ -272,6 +288,7 @@ pub trait ArgMatchesExt {
272288
&self,
273289
config: &'a Config,
274290
mode: CompileMode,
291+
workspace: Option<&Workspace<'a>>,
275292
) -> CargoResult<CompileOptions<'a>> {
276293
let spec = Packages::from_flags(
277294
self._is_present("all"),
@@ -328,15 +345,21 @@ pub trait ArgMatchesExt {
328345
local_rustdoc_args: None,
329346
export_dir: None,
330347
};
348+
349+
if let Some(ws) = workspace {
350+
self.check_optional_opts(ws, &opts)?;
351+
}
352+
331353
Ok(opts)
332354
}
333355

334356
fn compile_options_for_single_package<'a>(
335357
&self,
336358
config: &'a Config,
337359
mode: CompileMode,
360+
workspace: Option<&Workspace<'a>>,
338361
) -> CargoResult<CompileOptions<'a>> {
339-
let mut compile_opts = self.compile_options(config, mode)?;
362+
let mut compile_opts = self.compile_options(config, mode, workspace)?;
340363
compile_opts.spec = Packages::Packages(self._values_of("package"));
341364
Ok(compile_opts)
342365
}
@@ -413,6 +436,34 @@ about this warning.";
413436
Ok(index)
414437
}
415438

439+
fn check_optional_opts(
440+
&self,
441+
workspace: &Workspace<'_>,
442+
compile_opts: &CompileOptions<'_>,
443+
) -> CargoResult<()> {
444+
if self.is_present_with_zero_values("example") {
445+
print_available_examples(&workspace, &compile_opts)?;
446+
}
447+
448+
if self.is_present_with_zero_values("bin") {
449+
print_available_binaries(&workspace, &compile_opts)?;
450+
}
451+
452+
if self.is_present_with_zero_values("bench") {
453+
print_available_benches(&workspace, &compile_opts)?;
454+
}
455+
456+
if self.is_present_with_zero_values("test") {
457+
print_available_tests(&workspace, &compile_opts)?;
458+
}
459+
460+
Ok(())
461+
}
462+
463+
fn is_present_with_zero_values(&self, name: &str) -> bool {
464+
self._is_present(name) && self._value_of(name).is_none()
465+
}
466+
416467
fn _value_of(&self, name: &str) -> Option<&str>;
417468

418469
fn _values_of(&self, name: &str) -> Vec<String>;

src/cargo/util/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ pub use self::sha256::Sha256;
2222
pub use self::to_semver::ToSemver;
2323
pub use self::to_url::ToUrl;
2424
pub use self::vcs::{existing_vcs_repo, FossilRepo, GitRepo, HgRepo, PijulRepo};
25+
pub use self::workspace::{
26+
print_available_benches, print_available_binaries, print_available_examples,
27+
print_available_tests,
28+
};
2529

2630
mod cfg;
2731
pub mod command_prelude;
@@ -49,6 +53,7 @@ pub mod to_semver;
4953
pub mod to_url;
5054
pub mod toml;
5155
mod vcs;
56+
mod workspace;
5257

5358
pub fn elapsed(duration: Duration) -> String {
5459
let secs = duration.as_secs();

src/cargo/util/workspace.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use crate::core::{Target, Workspace};
2+
use crate::ops::CompileOptions;
3+
use crate::util::CargoResult;
4+
5+
use std::fmt::Write;
6+
7+
fn get_available_targets<'a>(
8+
filter_fn: fn(&Target) -> bool,
9+
ws: &'a Workspace<'_>,
10+
options: &'a CompileOptions<'_>,
11+
) -> CargoResult<Vec<&'a Target>> {
12+
let packages = options.spec.get_packages(ws)?;
13+
14+
let mut targets: Vec<_> = packages
15+
.into_iter()
16+
.flat_map(|pkg| {
17+
pkg.manifest()
18+
.targets()
19+
.into_iter()
20+
.filter(|target| filter_fn(target))
21+
})
22+
.collect();
23+
24+
targets.sort();
25+
26+
Ok(targets)
27+
}
28+
29+
fn print_available(
30+
filter_fn: fn(&Target) -> bool,
31+
ws: &Workspace<'_>,
32+
options: &CompileOptions<'_>,
33+
option_name: &str,
34+
plural_name: &str,
35+
) -> CargoResult<()> {
36+
let targets = get_available_targets(filter_fn, ws, options)?;
37+
38+
let mut output = String::new();
39+
writeln!(output, "\"{}\" takes one argument.", option_name)?;
40+
41+
if targets.is_empty() {
42+
writeln!(output, "No {} available.", plural_name)?;
43+
} else {
44+
writeln!(output, "Available {}:", plural_name)?;
45+
for target in targets {
46+
writeln!(output, " {}", target.name())?;
47+
}
48+
}
49+
Err(failure::err_msg(output))?
50+
}
51+
52+
pub fn print_available_examples(
53+
ws: &Workspace<'_>,
54+
options: &CompileOptions<'_>,
55+
) -> CargoResult<()> {
56+
print_available(Target::is_example, ws, options, "--example", "examples")
57+
}
58+
59+
pub fn print_available_binaries(
60+
ws: &Workspace<'_>,
61+
options: &CompileOptions<'_>,
62+
) -> CargoResult<()> {
63+
print_available(Target::is_bin, ws, options, "--bin", "binaries")
64+
}
65+
66+
pub fn print_available_benches(
67+
ws: &Workspace<'_>,
68+
options: &CompileOptions<'_>,
69+
) -> CargoResult<()> {
70+
print_available(Target::is_bench, ws, options, "--bench", "benches")
71+
}
72+
73+
pub fn print_available_tests(ws: &Workspace<'_>, options: &CompileOptions<'_>) -> CargoResult<()> {
74+
print_available(Target::is_test, ws, options, "--test", "tests")
75+
}

0 commit comments

Comments
 (0)