From 5936f6ae9ef210b849550640a1e8ecae8526394c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 11 Jul 2018 17:25:41 +0200 Subject: [PATCH 1/5] implement default-run option to set default binary for cargo run --- src/bin/cargo/commands/run.rs | 23 ++++++++-- src/cargo/core/manifest.rs | 7 +++ src/cargo/ops/cargo_compile.rs | 18 ++++++++ src/cargo/ops/cargo_run.rs | 30 ++++-------- src/cargo/util/toml/mod.rs | 3 ++ tests/testsuite/required_features.rs | 5 +- tests/testsuite/run.rs | 68 ++++++++++++++++++++++++++-- 7 files changed, 125 insertions(+), 29 deletions(-) diff --git a/src/bin/cargo/commands/run.rs b/src/bin/cargo/commands/run.rs index 683b47b885c..264c421e6e1 100644 --- a/src/bin/cargo/commands/run.rs +++ b/src/bin/cargo/commands/run.rs @@ -40,9 +40,26 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult { let mut compile_opts = args.compile_options_for_single_package(config, CompileMode::Build)?; if !args.is_present("example") && !args.is_present("bin") { - compile_opts.filter = CompileFilter::Default { - required_features_filterable: false, - }; + if let Some(default_run) = compile_opts.get_package(&ws)? + .and_then(|pkg| pkg.manifest().default_run()) + { + compile_opts.filter = CompileFilter::new( + false, + vec![default_run.to_owned()], + false, + vec![], + false, + vec![], + false, + vec![], + false, + false, + ); + } else { + compile_opts.filter = CompileFilter::Default { + required_features_filterable: false, + }; + } }; match ops::run(&ws, &compile_opts, &values(args, "args"))? { None => Ok(()), diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index 36077e8a029..bb67414bb30 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -43,6 +43,7 @@ pub struct Manifest { features: Features, edition: Edition, im_a_teapot: Option, + default_run: Option, } /// When parsing `Cargo.toml`, some warnings should silenced @@ -297,6 +298,7 @@ impl Manifest { features: Features, edition: Edition, im_a_teapot: Option, + default_run: Option, original: Rc, ) -> Manifest { Manifest { @@ -317,6 +319,7 @@ impl Manifest { edition, original, im_a_teapot, + default_run, publish_lockfile, } } @@ -426,6 +429,10 @@ impl Manifest { pub fn custom_metadata(&self) -> Option<&toml::Value> { self.custom_metadata.as_ref() } + + pub fn default_run(&self) -> Option<&str> { + self.default_run.as_ref().map(|s| &s[..]) + } } impl VirtualManifest { diff --git a/src/cargo/ops/cargo_compile.rs b/src/cargo/ops/cargo_compile.rs index 5197becc34e..f7abf5e668b 100644 --- a/src/cargo/ops/cargo_compile.rs +++ b/src/cargo/ops/cargo_compile.rs @@ -83,6 +83,24 @@ impl<'a> CompileOptions<'a> { export_dir: None, }) } + + // Returns the unique specified package, or None + pub fn get_package<'b>(&self, ws: &'b Workspace) -> CargoResult> { + Ok(match self.spec { + Packages::All | Packages::Default | Packages::OptOut(_) => { + None + } + Packages::Packages(ref xs) => match xs.len() { + 0 => Some(ws.current()?), + 1 => Some(ws.members() + .find(|pkg| *pkg.name() == xs[0]) + .ok_or_else(|| { + format_err!("package `{}` is not a member of the workspace", xs[0]) + })?), + _ => None, + }, + }) + } } #[derive(Clone, PartialEq, Eq, Debug)] diff --git a/src/cargo/ops/cargo_run.rs b/src/cargo/ops/cargo_run.rs index a2e35c3d53e..f0745d148b7 100644 --- a/src/cargo/ops/cargo_run.rs +++ b/src/cargo/ops/cargo_run.rs @@ -1,6 +1,6 @@ use std::path::Path; -use ops::{self, Packages}; +use ops; use util::{self, CargoResult, ProcessError}; use core::{TargetKind, Workspace}; @@ -11,21 +11,11 @@ pub fn run( ) -> CargoResult> { let config = ws.config(); - let pkg = match options.spec { - Packages::All | Packages::Default | Packages::OptOut(_) => { - unreachable!("cargo run supports single package only") - } - Packages::Packages(ref xs) => match xs.len() { - 0 => ws.current()?, - 1 => ws.members() - .find(|pkg| *pkg.name() == xs[0]) - .ok_or_else(|| { - format_err!("package `{}` is not a member of the workspace", xs[0]) - })?, - _ => unreachable!("cargo run supports single package only"), - }, - }; + let pkg = options.get_package(ws)? + .unwrap_or_else(|| unreachable!("cargo run supports single package only")); + // We compute the `bins` here *just for diagnosis*. The actual set of packages to be run + // is determined by the `ops::compile` call below. let bins: Vec<_> = pkg.manifest() .targets() .iter() @@ -42,7 +32,8 @@ pub fn run( if bins.is_empty() { if !options.filter.is_specific() { bail!("a bin target must be available for `cargo run`") - } else { + } + else { // this will be verified in cargo_compile } } @@ -64,9 +55,8 @@ pub fn run( if !options.filter.is_specific() { let names: Vec<&str> = bins.into_iter().map(|bin| bin.0).collect(); bail!( - "`cargo run` requires that a project only have one \ - executable; use the `--bin` option to specify which one \ - to run\navailable binaries: {}", + "`cargo run` could not determine which binary to run; set `default-run` \ + in the manifest or use the `--bin` option to specify\navailable binaries: {}", names.join(", ") ) @@ -102,4 +92,4 @@ pub fn run( Ok(Some(err)) } } -} \ No newline at end of file +} diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index 5a7521bca9e..4c29512fc41 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -586,6 +586,8 @@ pub struct TomlProject { autobenches: Option, #[serde(rename = "namespaced-features")] namespaced_features: Option, + #[serde(rename = "default-run")] + default_run: Option, // package metadata description: Option, @@ -970,6 +972,7 @@ impl TomlManifest { features, edition, project.im_a_teapot, + project.default_run.clone(), Rc::clone(me), ); if project.license_file.is_some() && project.license.is_some() { diff --git a/tests/testsuite/required_features.rs b/tests/testsuite/required_features.rs index 6dba1ad70ca..ed7cb228165 100644 --- a/tests/testsuite/required_features.rs +++ b/tests/testsuite/required_features.rs @@ -1347,9 +1347,8 @@ fn run_default_multiple_required_features() { assert_that( p.cargo("run"), execs().with_status(101).with_stderr( - "\ - error: `cargo run` requires that a project only have one executable; \ - use the `--bin` option to specify which one to run\navailable binaries: foo1, foo2", + "error: `cargo run` could not determine which binary to run; set `default-run` \ + in the manifest or use the `--bin` option to specify\navailable binaries: foo1, foo2", ), ); } diff --git a/tests/testsuite/run.rs b/tests/testsuite/run.rs index aff305c8f11..9d1fde90687 100644 --- a/tests/testsuite/run.rs +++ b/tests/testsuite/run.rs @@ -276,9 +276,8 @@ fn too_many_bins() { assert_that( p.cargo("run"), execs().with_status(101).with_stderr( - "[ERROR] `cargo run` requires that a project only \ - have one executable; use the `--bin` option \ - to specify which one to run\navailable binaries: [..]\n", + "[ERROR] `cargo run` could not determine which binary to run; set `default-run` \ + in the manifest or use the `--bin` option to specify\navailable binaries: a, b\n", ), ); } @@ -345,6 +344,69 @@ fn specify_name() { ); } +#[test] +fn specify_default_run() { + let p = project("foo") + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + default-run = "a" + "#, + ) + .file("src/lib.rs", "") + .file("src/bin/a.rs", r#"fn main() { println!("hello A"); }"#) + .file("src/bin/b.rs", r#"fn main() { println!("hello B"); }"#) + .build(); + + assert_that( + p.cargo("run"), + execs() + .with_status(0) + .with_stdout("hello A"), + ); + assert_that( + p.cargo("run").arg("--bin").arg("a"), + execs() + .with_status(0) + .with_stdout("hello A"), + ); + assert_that( + p.cargo("run").arg("--bin").arg("b"), + execs() + .with_status(0) + .with_stdout("hello B"), + ); +} + +#[test] +fn bogus_default_run() { + let p = project("foo") + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + default-run = "b" + "#, + ) + .file("src/lib.rs", "") + .file("src/bin/a.rs", r#"fn main() { println!("hello A"); }"#) + .build(); + + assert_that( + p.cargo("run"), + execs().with_status(101).with_stderr( + "error: no bin target named `b`\n\nDid you mean [..]?", + ), + ); +} + #[test] fn run_example() { let p = project("foo") From c955c60e6b2c9b0facbdc3028384d8da79d2edc1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 16 Jul 2018 11:28:32 +0200 Subject: [PATCH 2/5] feature-gate default-run --- src/cargo/core/features.rs | 3 ++ src/cargo/core/manifest.rs | 10 ++++++ tests/testsuite/run.rs | 63 +++++++++++++++++++++++++++++++++++--- 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/cargo/core/features.rs b/src/cargo/core/features.rs index fdbc7d7d4b2..06f9d5741f4 100644 --- a/src/cargo/core/features.rs +++ b/src/cargo/core/features.rs @@ -186,6 +186,9 @@ features! { // Separating the namespaces for features and dependencies [unstable] namespaced_features: bool, + + // "default-run" manifest option, + [unstable] default_run: bool, } } diff --git a/src/cargo/core/manifest.rs b/src/cargo/core/manifest.rs index bb67414bb30..1f49bddc902 100644 --- a/src/cargo/core/manifest.rs +++ b/src/cargo/core/manifest.rs @@ -410,6 +410,16 @@ impl Manifest { })?; } + if self.default_run.is_some() { + self.features + .require(Feature::default_run()) + .chain_err(|| { + format_err!( + "the `default-run` manifest key is unstable" + ) + })?; + } + Ok(()) } diff --git a/tests/testsuite/run.rs b/tests/testsuite/run.rs index 9d1fde90687..2c2e51f9bc4 100644 --- a/tests/testsuite/run.rs +++ b/tests/testsuite/run.rs @@ -350,6 +350,8 @@ fn specify_default_run() { .file( "Cargo.toml", r#" + cargo-features = ["default-run"] + [project] name = "foo" version = "0.0.1" @@ -363,19 +365,19 @@ fn specify_default_run() { .build(); assert_that( - p.cargo("run"), + p.cargo("run").masquerade_as_nightly_cargo(), execs() .with_status(0) .with_stdout("hello A"), ); assert_that( - p.cargo("run").arg("--bin").arg("a"), + p.cargo("run").masquerade_as_nightly_cargo().arg("--bin").arg("a"), execs() .with_status(0) .with_stdout("hello A"), ); assert_that( - p.cargo("run").arg("--bin").arg("b"), + p.cargo("run").masquerade_as_nightly_cargo().arg("--bin").arg("b"), execs() .with_status(0) .with_stdout("hello B"), @@ -388,6 +390,8 @@ fn bogus_default_run() { .file( "Cargo.toml", r#" + cargo-features = ["default-run"] + [project] name = "foo" version = "0.0.1" @@ -400,13 +404,64 @@ fn bogus_default_run() { .build(); assert_that( - p.cargo("run"), + p.cargo("run").masquerade_as_nightly_cargo(), execs().with_status(101).with_stderr( "error: no bin target named `b`\n\nDid you mean [..]?", ), ); } +#[test] +fn default_run_unstable() { + let p = project("foo") + .file( + "Cargo.toml", + r#" + [project] + name = "foo" + version = "0.0.1" + authors = [] + default-run = "a" + "#, + ) + .file("src/bin/a.rs", r#"fn main() { println!("hello A"); }"#) + .build(); + + assert_that( + p.cargo("run"), + execs().with_status(101).with_stderr( +r#"error: failed to parse manifest at [..] + +Caused by: + the `default-run` manifest key is unstable + +Caused by: + feature `default-run` is required + +this Cargo does not support nightly features, but if you +switch to nightly channel you can add +`cargo-features = ["default-run"]` to enable this feature +"#, + ), + ); + + assert_that( + p.cargo("run").masquerade_as_nightly_cargo(), + execs().with_status(101).with_stderr( +r#"error: failed to parse manifest at [..] + +Caused by: + the `default-run` manifest key is unstable + +Caused by: + feature `default-run` is required + +consider adding `cargo-features = ["default-run"]` to the manifest +"#, + ), + ); +} + #[test] fn run_example() { let p = project("foo") From 579e0348d792baf536951ed211de1980469658be Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 16 Jul 2018 11:31:14 +0200 Subject: [PATCH 3/5] order of lits of binaries is not stable --- tests/testsuite/run.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/testsuite/run.rs b/tests/testsuite/run.rs index 2c2e51f9bc4..052b3aeda0b 100644 --- a/tests/testsuite/run.rs +++ b/tests/testsuite/run.rs @@ -275,9 +275,10 @@ fn too_many_bins() { assert_that( p.cargo("run"), + // Using [..] here because the order is not stable execs().with_status(101).with_stderr( "[ERROR] `cargo run` could not determine which binary to run; set `default-run` \ - in the manifest or use the `--bin` option to specify\navailable binaries: a, b\n", + in the manifest or use the `--bin` option to specify\navailable binaries: [..]\n", ), ); } From 04abd5ef2fe84761e0ff7ed44bd201caef927e03 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 16 Jul 2018 19:45:11 +0200 Subject: [PATCH 4/5] fix code nits --- src/cargo/ops/cargo_run.rs | 9 ++++----- tests/testsuite/required_features.rs | 5 +++-- tests/testsuite/run.rs | 5 +++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/cargo/ops/cargo_run.rs b/src/cargo/ops/cargo_run.rs index f0745d148b7..aea9a9b1986 100644 --- a/src/cargo/ops/cargo_run.rs +++ b/src/cargo/ops/cargo_run.rs @@ -32,8 +32,7 @@ pub fn run( if bins.is_empty() { if !options.filter.is_specific() { bail!("a bin target must be available for `cargo run`") - } - else { + } else { // this will be verified in cargo_compile } } @@ -55,10 +54,10 @@ pub fn run( if !options.filter.is_specific() { let names: Vec<&str> = bins.into_iter().map(|bin| bin.0).collect(); bail!( - "`cargo run` could not determine which binary to run; set `default-run` \ - in the manifest or use the `--bin` option to specify\navailable binaries: {}", + "`cargo run` requires that a project only have one \ + executable; use the `--bin` option to specify which one \ + to run\navailable binaries: {}", names.join(", ") - ) } else { bail!( diff --git a/tests/testsuite/required_features.rs b/tests/testsuite/required_features.rs index ed7cb228165..6dba1ad70ca 100644 --- a/tests/testsuite/required_features.rs +++ b/tests/testsuite/required_features.rs @@ -1347,8 +1347,9 @@ fn run_default_multiple_required_features() { assert_that( p.cargo("run"), execs().with_status(101).with_stderr( - "error: `cargo run` could not determine which binary to run; set `default-run` \ - in the manifest or use the `--bin` option to specify\navailable binaries: foo1, foo2", + "\ + error: `cargo run` requires that a project only have one executable; \ + use the `--bin` option to specify which one to run\navailable binaries: foo1, foo2", ), ); } diff --git a/tests/testsuite/run.rs b/tests/testsuite/run.rs index 052b3aeda0b..08736f569d6 100644 --- a/tests/testsuite/run.rs +++ b/tests/testsuite/run.rs @@ -277,8 +277,9 @@ fn too_many_bins() { p.cargo("run"), // Using [..] here because the order is not stable execs().with_status(101).with_stderr( - "[ERROR] `cargo run` could not determine which binary to run; set `default-run` \ - in the manifest or use the `--bin` option to specify\navailable binaries: [..]\n", + "[ERROR] `cargo run` requires that a project only \ + have one executable; use the `--bin` option \ + to specify which one to run\navailable binaries: [..]\n", ), ); } From 34a5cfb0840161b3d2593bf435cf9de5a6f545b1 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 16 Jul 2018 21:46:42 +0200 Subject: [PATCH 5/5] document new feature --- src/doc/src/reference/unstable.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index c3a29869a23..fe137b84201 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -308,3 +308,15 @@ $ cargo +nightly build -Z compile-progress Compiling utf8-ranges v1.0.0 Building [=======> ] 2/14: libc, regex, uc... ``` + +### default-run +* Original issue: [#2200](https://github.com/rust-lang/cargo/issues/2200) + +The `default-run` option in the `[project]` section of the manifest can be used +to specify a default binary picked by `cargo run`. For example, when there is +both `src/bin/a.rs` and `src/bin/b.rs`: + +```toml +[project] +default-run = "a" +```