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/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 36077e8a029..1f49bddc902 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, } } @@ -407,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(()) } @@ -426,6 +439,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..aea9a9b1986 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() @@ -66,9 +56,8 @@ pub fn run( bail!( "`cargo run` requires that a project only have one \ executable; use the `--bin` option to specify which one \ - to run\navailable binaries: {}", + to run\navailable binaries: {}", names.join(", ") - ) } else { bail!( @@ -102,4 +91,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/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" +``` diff --git a/tests/testsuite/run.rs b/tests/testsuite/run.rs index aff305c8f11..08736f569d6 100644 --- a/tests/testsuite/run.rs +++ b/tests/testsuite/run.rs @@ -275,6 +275,7 @@ 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` requires that a project only \ have one executable; use the `--bin` option \ @@ -345,6 +346,124 @@ fn specify_name() { ); } +#[test] +fn specify_default_run() { + let p = project("foo") + .file( + "Cargo.toml", + r#" + cargo-features = ["default-run"] + + [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").masquerade_as_nightly_cargo(), + execs() + .with_status(0) + .with_stdout("hello A"), + ); + assert_that( + p.cargo("run").masquerade_as_nightly_cargo().arg("--bin").arg("a"), + execs() + .with_status(0) + .with_stdout("hello A"), + ); + assert_that( + p.cargo("run").masquerade_as_nightly_cargo().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#" + cargo-features = ["default-run"] + + [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").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")