Skip to content

Commit

Permalink
Auto merge of #5710 - RalfJung:default-run, r=alexcrichton
Browse files Browse the repository at this point in the history
implement default-run option to set default binary for cargo run

The implementation is not pretty but as good as I could make it. The fact that all this logic in `cargo_run` is for diagnosis only and essentially just re-implements the filtering done elsewhere really threw me off.

Fixes #2200
  • Loading branch information
bors committed Jul 16, 2018
2 parents fefbb68 + 34a5cfb commit e325bff
Show file tree
Hide file tree
Showing 8 changed files with 199 additions and 21 deletions.
23 changes: 20 additions & 3 deletions src/bin/cargo/commands/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(()),
Expand Down
3 changes: 3 additions & 0 deletions src/cargo/core/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,9 @@ features! {

// Separating the namespaces for features and dependencies
[unstable] namespaced_features: bool,

// "default-run" manifest option,
[unstable] default_run: bool,
}
}

Expand Down
17 changes: 17 additions & 0 deletions src/cargo/core/manifest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub struct Manifest {
features: Features,
edition: Edition,
im_a_teapot: Option<bool>,
default_run: Option<String>,
}

/// When parsing `Cargo.toml`, some warnings should silenced
Expand Down Expand Up @@ -297,6 +298,7 @@ impl Manifest {
features: Features,
edition: Edition,
im_a_teapot: Option<bool>,
default_run: Option<String>,
original: Rc<TomlManifest>,
) -> Manifest {
Manifest {
Expand All @@ -317,6 +319,7 @@ impl Manifest {
edition,
original,
im_a_teapot,
default_run,
publish_lockfile,
}
}
Expand Down Expand Up @@ -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(())
}

Expand All @@ -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 {
Expand Down
18 changes: 18 additions & 0 deletions src/cargo/ops/cargo_compile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<&'b Package>> {
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)]
Expand Down
25 changes: 7 additions & 18 deletions src/cargo/ops/cargo_run.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::path::Path;

use ops::{self, Packages};
use ops;
use util::{self, CargoResult, ProcessError};
use core::{TargetKind, Workspace};

Expand All @@ -11,21 +11,11 @@ pub fn run(
) -> CargoResult<Option<ProcessError>> {
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()
Expand Down Expand Up @@ -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!(
Expand Down Expand Up @@ -102,4 +91,4 @@ pub fn run(
Ok(Some(err))
}
}
}
}
3 changes: 3 additions & 0 deletions src/cargo/util/toml/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,8 @@ pub struct TomlProject {
autobenches: Option<bool>,
#[serde(rename = "namespaced-features")]
namespaced_features: Option<bool>,
#[serde(rename = "default-run")]
default_run: Option<String>,

// package metadata
description: Option<String>,
Expand Down Expand Up @@ -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() {
Expand Down
12 changes: 12 additions & 0 deletions src/doc/src/reference/unstable.md
Original file line number Diff line number Diff line change
Expand Up @@ -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"
```
119 changes: 119 additions & 0 deletions tests/testsuite/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -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")
Expand Down

0 comments on commit e325bff

Please sign in to comment.