Skip to content

--{example,bin,bench,test} with no argument now lists all available targets #6505

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 7, 2019
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/bin/cargo/commands/bench.rs
Original file line number Diff line number Diff line change
@@ -72,7 +72,8 @@ Compilation can be customized with the `bench` profile in the manifest.

pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
let ws = args.workspace(config)?;
let mut compile_opts = args.compile_options(config, CompileMode::Bench)?;
let mut compile_opts = args.compile_options(config, CompileMode::Bench, Some(&ws))?;

compile_opts.build_config.release = true;

let ops = TestOptions {
3 changes: 2 additions & 1 deletion src/bin/cargo/commands/build.rs
Original file line number Diff line number Diff line change
@@ -48,7 +48,8 @@ the --release flag will use the `release` profile instead.

pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
let ws = args.workspace(config)?;
let mut compile_opts = args.compile_options(config, CompileMode::Build)?;
let mut compile_opts = args.compile_options(config, CompileMode::Build, Some(&ws))?;

compile_opts.export_dir = args.value_of_path("out-dir", config);
if compile_opts.export_dir.is_some() && !config.cli_unstable().unstable_options {
Err(failure::format_err!(
3 changes: 2 additions & 1 deletion src/bin/cargo/commands/check.rs
Original file line number Diff line number Diff line change
@@ -68,7 +68,8 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
}
};
let mode = CompileMode::Check { test };
let compile_opts = args.compile_options(config, mode)?;
let compile_opts = args.compile_options(config, mode, Some(&ws))?;

ops::compile(&ws, &compile_opts)?;
Ok(())
}
2 changes: 1 addition & 1 deletion src/bin/cargo/commands/doc.rs
Original file line number Diff line number Diff line change
@@ -50,7 +50,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
let mode = CompileMode::Doc {
deps: !args.is_present("no-deps"),
};
let mut compile_opts = args.compile_options(config, mode)?;
let mut compile_opts = args.compile_options(config, mode, Some(&ws))?;
compile_opts.local_rustdoc_args = if args.is_present("document-private-items") {
Some(vec!["--document-private-items".to_string()])
} else {
3 changes: 2 additions & 1 deletion src/bin/cargo/commands/fix.rs
Original file line number Diff line number Diff line change
@@ -122,7 +122,8 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {

// Unlike other commands default `cargo fix` to all targets to fix as much
// code as we can.
let mut opts = args.compile_options(config, mode)?;
let mut opts = args.compile_options(config, mode, Some(&ws))?;

if let CompileFilter::Default { .. } = opts.filter {
opts.filter = CompileFilter::Only {
all_targets: true,
4 changes: 3 additions & 1 deletion src/bin/cargo/commands/install.rs
Original file line number Diff line number Diff line change
@@ -78,7 +78,9 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
let registry = args.registry(config)?;

config.reload_rooted_at_cargo_home()?;
let mut compile_opts = args.compile_options(config, CompileMode::Build)?;

let workspace = args.workspace(config).ok();
let mut compile_opts = args.compile_options(config, CompileMode::Build, workspace.as_ref())?;

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

3 changes: 2 additions & 1 deletion src/bin/cargo/commands/run.rs
Original file line number Diff line number Diff line change
@@ -39,7 +39,8 @@ run. If you're passing arguments to both Cargo and the binary, the ones after
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
let ws = args.workspace(config)?;

let mut compile_opts = args.compile_options(config, CompileMode::Build)?;
let mut compile_opts = args.compile_options(config, CompileMode::Build, Some(&ws))?;

if !args.is_present("example") && !args.is_present("bin") {
let default_runs: Vec<_> = compile_opts
.spec
2 changes: 1 addition & 1 deletion src/bin/cargo/commands/rustc.rs
Original file line number Diff line number Diff line change
@@ -62,7 +62,7 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
return Err(CliError::new(err, 101));
}
};
let mut compile_opts = args.compile_options_for_single_package(config, mode)?;
let mut compile_opts = args.compile_options_for_single_package(config, mode, Some(&ws))?;
let target_args = values(args, "args");
compile_opts.target_rustc_args = if target_args.is_empty() {
None
2 changes: 1 addition & 1 deletion src/bin/cargo/commands/rustdoc.rs
Original file line number Diff line number Diff line change
@@ -51,7 +51,7 @@ the `cargo help pkgid` command.
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
let ws = args.workspace(config)?;
let mut compile_opts =
args.compile_options_for_single_package(config, CompileMode::Doc { deps: false })?;
args.compile_options_for_single_package(config, CompileMode::Doc { deps: false }, Some(&ws))?;
let target_args = values(args, "args");
compile_opts.target_rustdoc_args = if target_args.is_empty() {
None
2 changes: 1 addition & 1 deletion src/bin/cargo/commands/test.rs
Original file line number Diff line number Diff line change
@@ -92,7 +92,7 @@ To get the list of all options available for the test binaries use this:
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
let ws = args.workspace(config)?;

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

let doc = args.is_present("doc");
if doc {
69 changes: 60 additions & 9 deletions src/cargo/util/command_prelude.rs
Original file line number Diff line number Diff line change
@@ -7,6 +7,10 @@ use crate::ops::{CompileFilter, CompileOptions, NewOptions, Packages, VersionCon
use crate::sources::CRATES_IO_REGISTRY;
use crate::util::important_paths::find_root_manifest_for_wd;
use crate::util::{paths, validate_package_name};
use crate::util::{
print_available_benches, print_available_binaries, print_available_examples,
print_available_tests,
};
use crate::CargoResult;
use clap::{self, SubCommand};

@@ -60,18 +64,18 @@ pub trait AppExt: Sized {
all: &'static str,
) -> Self {
self.arg_targets_lib_bin(lib, bin, bins)
._arg(multi_opt("example", "NAME", example))
._arg(optional_multi_opt("example", "NAME", example))
._arg(opt("examples", examples))
._arg(multi_opt("test", "NAME", test))
._arg(optional_multi_opt("test", "NAME", test))
._arg(opt("tests", tests))
._arg(multi_opt("bench", "NAME", bench))
._arg(optional_multi_opt("bench", "NAME", bench))
._arg(opt("benches", benches))
._arg(opt("all-targets", all))
}

fn arg_targets_lib_bin(self, lib: &'static str, bin: &'static str, bins: &'static str) -> Self {
self._arg(opt("lib", lib))
._arg(multi_opt("bin", "NAME", bin))
._arg(optional_multi_opt("bin", "NAME", bin))
._arg(opt("bins", bins))
}

@@ -82,15 +86,15 @@ pub trait AppExt: Sized {
example: &'static str,
examples: &'static str,
) -> Self {
self._arg(multi_opt("bin", "NAME", bin))
self._arg(optional_multi_opt("bin", "NAME", bin))
._arg(opt("bins", bins))
._arg(multi_opt("example", "NAME", example))
._arg(optional_multi_opt("example", "NAME", example))
._arg(opt("examples", examples))
}

fn arg_targets_bin_example(self, bin: &'static str, example: &'static str) -> Self {
self._arg(multi_opt("bin", "NAME", bin))
._arg(multi_opt("example", "NAME", example))
self._arg(optional_multi_opt("bin", "NAME", bin))
._arg(optional_multi_opt("example", "NAME", example))
}

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

pub fn optional_multi_opt(
name: &'static str,
value_name: &'static str,
help: &'static str,
) -> Arg<'static, 'static> {
opt(name, help)
.value_name(value_name)
.multiple(true)
.min_values(0)
.number_of_values(1)
}

pub fn multi_opt(
name: &'static str,
value_name: &'static str,
@@ -272,6 +288,7 @@ pub trait ArgMatchesExt {
&self,
config: &'a Config,
mode: CompileMode,
workspace: Option<&Workspace<'a>>,
) -> CargoResult<CompileOptions<'a>> {
let spec = Packages::from_flags(
self._is_present("all"),
@@ -328,15 +345,21 @@ pub trait ArgMatchesExt {
local_rustdoc_args: None,
export_dir: None,
};

if let Some(ws) = workspace {
self.check_optional_opts(ws, &opts)?;
}

Ok(opts)
}

fn compile_options_for_single_package<'a>(
&self,
config: &'a Config,
mode: CompileMode,
workspace: Option<&Workspace<'a>>,
) -> CargoResult<CompileOptions<'a>> {
let mut compile_opts = self.compile_options(config, mode)?;
let mut compile_opts = self.compile_options(config, mode, workspace)?;
compile_opts.spec = Packages::Packages(self._values_of("package"));
Ok(compile_opts)
}
@@ -413,6 +436,34 @@ about this warning.";
Ok(index)
}

fn check_optional_opts(
&self,
workspace: &Workspace<'_>,
compile_opts: &CompileOptions<'_>,
) -> CargoResult<()> {
if self.is_present_with_zero_values("example") {
print_available_examples(&workspace, &compile_opts)?;
}

if self.is_present_with_zero_values("bin") {
print_available_binaries(&workspace, &compile_opts)?;
}

if self.is_present_with_zero_values("bench") {
print_available_benches(&workspace, &compile_opts)?;
}

if self.is_present_with_zero_values("test") {
print_available_tests(&workspace, &compile_opts)?;
}

Ok(())
}

fn is_present_with_zero_values(&self, name: &str) -> bool {
self._is_present(name) && self._value_of(name).is_none()
}

fn _value_of(&self, name: &str) -> Option<&str>;

fn _values_of(&self, name: &str) -> Vec<String>;
5 changes: 5 additions & 0 deletions src/cargo/util/mod.rs
Original file line number Diff line number Diff line change
@@ -22,6 +22,10 @@ pub use self::sha256::Sha256;
pub use self::to_semver::ToSemver;
pub use self::to_url::ToUrl;
pub use self::vcs::{existing_vcs_repo, FossilRepo, GitRepo, HgRepo, PijulRepo};
pub use self::workspace::{
print_available_benches, print_available_binaries, print_available_examples,
print_available_tests,
};

mod cfg;
pub mod command_prelude;
@@ -49,6 +53,7 @@ pub mod to_semver;
pub mod to_url;
pub mod toml;
mod vcs;
mod workspace;

pub fn elapsed(duration: Duration) -> String {
let secs = duration.as_secs();
75 changes: 75 additions & 0 deletions src/cargo/util/workspace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
use crate::core::{Target, Workspace};
use crate::ops::CompileOptions;
use crate::util::CargoResult;

use std::fmt::Write;

fn get_available_targets<'a>(
filter_fn: fn(&Target) -> bool,
ws: &'a Workspace<'_>,
options: &'a CompileOptions<'_>,
) -> CargoResult<Vec<&'a Target>> {
let packages = options.spec.get_packages(ws)?;

let mut targets: Vec<_> = packages
.into_iter()
.flat_map(|pkg| {
pkg.manifest()
.targets()
.into_iter()
.filter(|target| filter_fn(target))
})
.collect();

targets.sort();

Ok(targets)
}

fn print_available(
filter_fn: fn(&Target) -> bool,
ws: &Workspace<'_>,
options: &CompileOptions<'_>,
option_name: &str,
plural_name: &str,
) -> CargoResult<()> {
let targets = get_available_targets(filter_fn, ws, options)?;

let mut output = String::new();
writeln!(output, "\"{}\" takes one argument.", option_name)?;

if targets.is_empty() {
writeln!(output, "No {} available.", plural_name)?;
} else {
writeln!(output, "Available {}:", plural_name)?;
for target in targets {
writeln!(output, " {}", target.name())?;
}
}
Err(failure::err_msg(output))?
}

pub fn print_available_examples(
ws: &Workspace<'_>,
options: &CompileOptions<'_>,
) -> CargoResult<()> {
print_available(Target::is_example, ws, options, "--example", "examples")
}

pub fn print_available_binaries(
ws: &Workspace<'_>,
options: &CompileOptions<'_>,
) -> CargoResult<()> {
print_available(Target::is_bin, ws, options, "--bin", "binaries")
}

pub fn print_available_benches(
ws: &Workspace<'_>,
options: &CompileOptions<'_>,
) -> CargoResult<()> {
print_available(Target::is_bench, ws, options, "--bench", "benches")
}

pub fn print_available_tests(ws: &Workspace<'_>, options: &CompileOptions<'_>) -> CargoResult<()> {
print_available(Target::is_test, ws, options, "--test", "tests")
}
189 changes: 189 additions & 0 deletions tests/testsuite/list_targets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
use crate::support::project;

const EXAMPLE: u8 = 0x1;
const BIN: u8 = 0x2;
const TEST: u8 = 0x4;
const BENCH: u8 = 0x8;

fn list_targets_test(command: &str, targets: u8) {
let full_project = project()
.file("examples/a.rs", "fn main() { }")
.file("examples/b.rs", "fn main() { }")
.file("benches/bench1.rs", "")
.file("benches/bench2.rs", "")
.file("tests/test1.rs", "")
.file("tests/test2.rs", "")
.file("src/main.rs", "fn main() { }")
.build();

if targets & EXAMPLE != 0 {
full_project
.cargo(&format!("{} --example", command))
.with_stderr(
"\
error: \"--example\" takes one argument.
Available examples:
a
b
",
)
.with_status(101)
.run();
}

if targets & BIN != 0 {
full_project
.cargo(&format!("{} --bin", command))
.with_stderr(
"\
error: \"--bin\" takes one argument.
Available binaries:
foo
",
)
.with_status(101)
.run();
}

if targets & BENCH != 0 {
full_project
.cargo(&format!("{} --bench", command))
.with_stderr(
"\
error: \"--bench\" takes one argument.
Available benches:
bench1
bench2
",
)
.with_status(101)
.run();
}

if targets & TEST != 0 {
full_project
.cargo(&format!("{} --test", command))
.with_stderr(
"\
error: \"--test\" takes one argument.
Available tests:
test1
test2
",
)
.with_status(101)
.run();
}

let empty_project = project().file("src/lib.rs", "").build();

if targets & EXAMPLE != 0 {
empty_project
.cargo(&format!("{} --example", command))
.with_stderr(
"\
error: \"--example\" takes one argument.
No examples available.
",
)
.with_status(101)
.run();
}

if targets & BIN != 0 {
empty_project
.cargo(&format!("{} --bin", command))
.with_stderr(
"\
error: \"--bin\" takes one argument.
No binaries available.
",
)
.with_status(101)
.run();
}

if targets & BENCH != 0 {
empty_project
.cargo(&format!("{} --bench", command))
.with_stderr(
"\
error: \"--bench\" takes one argument.
No benches available.
",
)
.with_status(101)
.run();
}

if targets & TEST != 0 {
empty_project
.cargo(&format!("{} --test", command))
.with_stderr(
"\
error: \"--test\" takes one argument.
No tests available.
",
)
.with_status(101)
.run();
}
}

#[test]
fn build_list_targets() {
list_targets_test("build", EXAMPLE | BIN | TEST | BENCH);
}

#[test]
fn check_list_targets() {
list_targets_test("check", EXAMPLE | BIN | TEST | BENCH);
}

#[test]
fn doc_list_targets() {
list_targets_test("doc", BIN);
}

#[test]
fn fix_list_targets() {
list_targets_test("fix", EXAMPLE | BIN | TEST | BENCH);
}

#[test]
fn run_list_targets() {
list_targets_test("run", EXAMPLE | BIN);
}

#[test]
fn test_list_targets() {
list_targets_test("test", EXAMPLE | BIN | TEST | BENCH);
}

#[test]
fn bench_list_targets() {
list_targets_test("bench", EXAMPLE | BIN | TEST | BENCH);
}

#[test]
fn install_list_targets() {
list_targets_test("install", EXAMPLE | BIN);
}

#[test]
fn rustdoc_list_targets() {
list_targets_test("rustdoc", EXAMPLE | BIN | TEST | BENCH);
}

#[test]
fn rustc_list_targets() {
list_targets_test("rustc", EXAMPLE | BIN | TEST | BENCH);
}
1 change: 1 addition & 0 deletions tests/testsuite/main.rs
Original file line number Diff line number Diff line change
@@ -43,6 +43,7 @@ mod git;
mod init;
mod install;
mod jobserver;
mod list_targets;
mod local_registry;
mod lockfile_compat;
mod login;