Skip to content

Commit e325bff

Browse files
committed
Auto merge of #5710 - RalfJung:default-run, r=alexcrichton
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
2 parents fefbb68 + 34a5cfb commit e325bff

File tree

8 files changed

+199
-21
lines changed

8 files changed

+199
-21
lines changed

src/bin/cargo/commands/run.rs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,26 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
4040

4141
let mut compile_opts = args.compile_options_for_single_package(config, CompileMode::Build)?;
4242
if !args.is_present("example") && !args.is_present("bin") {
43-
compile_opts.filter = CompileFilter::Default {
44-
required_features_filterable: false,
45-
};
43+
if let Some(default_run) = compile_opts.get_package(&ws)?
44+
.and_then(|pkg| pkg.manifest().default_run())
45+
{
46+
compile_opts.filter = CompileFilter::new(
47+
false,
48+
vec![default_run.to_owned()],
49+
false,
50+
vec![],
51+
false,
52+
vec![],
53+
false,
54+
vec![],
55+
false,
56+
false,
57+
);
58+
} else {
59+
compile_opts.filter = CompileFilter::Default {
60+
required_features_filterable: false,
61+
};
62+
}
4663
};
4764
match ops::run(&ws, &compile_opts, &values(args, "args"))? {
4865
None => Ok(()),

src/cargo/core/features.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,9 @@ features! {
186186

187187
// Separating the namespaces for features and dependencies
188188
[unstable] namespaced_features: bool,
189+
190+
// "default-run" manifest option,
191+
[unstable] default_run: bool,
189192
}
190193
}
191194

src/cargo/core/manifest.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ pub struct Manifest {
4343
features: Features,
4444
edition: Edition,
4545
im_a_teapot: Option<bool>,
46+
default_run: Option<String>,
4647
}
4748

4849
/// When parsing `Cargo.toml`, some warnings should silenced
@@ -297,6 +298,7 @@ impl Manifest {
297298
features: Features,
298299
edition: Edition,
299300
im_a_teapot: Option<bool>,
301+
default_run: Option<String>,
300302
original: Rc<TomlManifest>,
301303
) -> Manifest {
302304
Manifest {
@@ -317,6 +319,7 @@ impl Manifest {
317319
edition,
318320
original,
319321
im_a_teapot,
322+
default_run,
320323
publish_lockfile,
321324
}
322325
}
@@ -407,6 +410,16 @@ impl Manifest {
407410
})?;
408411
}
409412

413+
if self.default_run.is_some() {
414+
self.features
415+
.require(Feature::default_run())
416+
.chain_err(|| {
417+
format_err!(
418+
"the `default-run` manifest key is unstable"
419+
)
420+
})?;
421+
}
422+
410423
Ok(())
411424
}
412425

@@ -426,6 +439,10 @@ impl Manifest {
426439
pub fn custom_metadata(&self) -> Option<&toml::Value> {
427440
self.custom_metadata.as_ref()
428441
}
442+
443+
pub fn default_run(&self) -> Option<&str> {
444+
self.default_run.as_ref().map(|s| &s[..])
445+
}
429446
}
430447

431448
impl VirtualManifest {

src/cargo/ops/cargo_compile.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,24 @@ impl<'a> CompileOptions<'a> {
8383
export_dir: None,
8484
})
8585
}
86+
87+
// Returns the unique specified package, or None
88+
pub fn get_package<'b>(&self, ws: &'b Workspace) -> CargoResult<Option<&'b Package>> {
89+
Ok(match self.spec {
90+
Packages::All | Packages::Default | Packages::OptOut(_) => {
91+
None
92+
}
93+
Packages::Packages(ref xs) => match xs.len() {
94+
0 => Some(ws.current()?),
95+
1 => Some(ws.members()
96+
.find(|pkg| *pkg.name() == xs[0])
97+
.ok_or_else(|| {
98+
format_err!("package `{}` is not a member of the workspace", xs[0])
99+
})?),
100+
_ => None,
101+
},
102+
})
103+
}
86104
}
87105

88106
#[derive(Clone, PartialEq, Eq, Debug)]

src/cargo/ops/cargo_run.rs

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use std::path::Path;
22

3-
use ops::{self, Packages};
3+
use ops;
44
use util::{self, CargoResult, ProcessError};
55
use core::{TargetKind, Workspace};
66

@@ -11,21 +11,11 @@ pub fn run(
1111
) -> CargoResult<Option<ProcessError>> {
1212
let config = ws.config();
1313

14-
let pkg = match options.spec {
15-
Packages::All | Packages::Default | Packages::OptOut(_) => {
16-
unreachable!("cargo run supports single package only")
17-
}
18-
Packages::Packages(ref xs) => match xs.len() {
19-
0 => ws.current()?,
20-
1 => ws.members()
21-
.find(|pkg| *pkg.name() == xs[0])
22-
.ok_or_else(|| {
23-
format_err!("package `{}` is not a member of the workspace", xs[0])
24-
})?,
25-
_ => unreachable!("cargo run supports single package only"),
26-
},
27-
};
14+
let pkg = options.get_package(ws)?
15+
.unwrap_or_else(|| unreachable!("cargo run supports single package only"));
2816

17+
// We compute the `bins` here *just for diagnosis*. The actual set of packages to be run
18+
// is determined by the `ops::compile` call below.
2919
let bins: Vec<_> = pkg.manifest()
3020
.targets()
3121
.iter()
@@ -66,9 +56,8 @@ pub fn run(
6656
bail!(
6757
"`cargo run` requires that a project only have one \
6858
executable; use the `--bin` option to specify which one \
69-
to run\navailable binaries: {}",
59+
to run\navailable binaries: {}",
7060
names.join(", ")
71-
7261
)
7362
} else {
7463
bail!(
@@ -102,4 +91,4 @@ pub fn run(
10291
Ok(Some(err))
10392
}
10493
}
105-
}
94+
}

src/cargo/util/toml/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -586,6 +586,8 @@ pub struct TomlProject {
586586
autobenches: Option<bool>,
587587
#[serde(rename = "namespaced-features")]
588588
namespaced_features: Option<bool>,
589+
#[serde(rename = "default-run")]
590+
default_run: Option<String>,
589591

590592
// package metadata
591593
description: Option<String>,
@@ -970,6 +972,7 @@ impl TomlManifest {
970972
features,
971973
edition,
972974
project.im_a_teapot,
975+
project.default_run.clone(),
973976
Rc::clone(me),
974977
);
975978
if project.license_file.is_some() && project.license.is_some() {

src/doc/src/reference/unstable.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -308,3 +308,15 @@ $ cargo +nightly build -Z compile-progress
308308
Compiling utf8-ranges v1.0.0
309309
Building [=======> ] 2/14: libc, regex, uc...
310310
```
311+
312+
### default-run
313+
* Original issue: [#2200](https://github.com/rust-lang/cargo/issues/2200)
314+
315+
The `default-run` option in the `[project]` section of the manifest can be used
316+
to specify a default binary picked by `cargo run`. For example, when there is
317+
both `src/bin/a.rs` and `src/bin/b.rs`:
318+
319+
```toml
320+
[project]
321+
default-run = "a"
322+
```

tests/testsuite/run.rs

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,7 @@ fn too_many_bins() {
275275

276276
assert_that(
277277
p.cargo("run"),
278+
// Using [..] here because the order is not stable
278279
execs().with_status(101).with_stderr(
279280
"[ERROR] `cargo run` requires that a project only \
280281
have one executable; use the `--bin` option \
@@ -345,6 +346,124 @@ fn specify_name() {
345346
);
346347
}
347348

349+
#[test]
350+
fn specify_default_run() {
351+
let p = project("foo")
352+
.file(
353+
"Cargo.toml",
354+
r#"
355+
cargo-features = ["default-run"]
356+
357+
[project]
358+
name = "foo"
359+
version = "0.0.1"
360+
authors = []
361+
default-run = "a"
362+
"#,
363+
)
364+
.file("src/lib.rs", "")
365+
.file("src/bin/a.rs", r#"fn main() { println!("hello A"); }"#)
366+
.file("src/bin/b.rs", r#"fn main() { println!("hello B"); }"#)
367+
.build();
368+
369+
assert_that(
370+
p.cargo("run").masquerade_as_nightly_cargo(),
371+
execs()
372+
.with_status(0)
373+
.with_stdout("hello A"),
374+
);
375+
assert_that(
376+
p.cargo("run").masquerade_as_nightly_cargo().arg("--bin").arg("a"),
377+
execs()
378+
.with_status(0)
379+
.with_stdout("hello A"),
380+
);
381+
assert_that(
382+
p.cargo("run").masquerade_as_nightly_cargo().arg("--bin").arg("b"),
383+
execs()
384+
.with_status(0)
385+
.with_stdout("hello B"),
386+
);
387+
}
388+
389+
#[test]
390+
fn bogus_default_run() {
391+
let p = project("foo")
392+
.file(
393+
"Cargo.toml",
394+
r#"
395+
cargo-features = ["default-run"]
396+
397+
[project]
398+
name = "foo"
399+
version = "0.0.1"
400+
authors = []
401+
default-run = "b"
402+
"#,
403+
)
404+
.file("src/lib.rs", "")
405+
.file("src/bin/a.rs", r#"fn main() { println!("hello A"); }"#)
406+
.build();
407+
408+
assert_that(
409+
p.cargo("run").masquerade_as_nightly_cargo(),
410+
execs().with_status(101).with_stderr(
411+
"error: no bin target named `b`\n\nDid you mean [..]?",
412+
),
413+
);
414+
}
415+
416+
#[test]
417+
fn default_run_unstable() {
418+
let p = project("foo")
419+
.file(
420+
"Cargo.toml",
421+
r#"
422+
[project]
423+
name = "foo"
424+
version = "0.0.1"
425+
authors = []
426+
default-run = "a"
427+
"#,
428+
)
429+
.file("src/bin/a.rs", r#"fn main() { println!("hello A"); }"#)
430+
.build();
431+
432+
assert_that(
433+
p.cargo("run"),
434+
execs().with_status(101).with_stderr(
435+
r#"error: failed to parse manifest at [..]
436+
437+
Caused by:
438+
the `default-run` manifest key is unstable
439+
440+
Caused by:
441+
feature `default-run` is required
442+
443+
this Cargo does not support nightly features, but if you
444+
switch to nightly channel you can add
445+
`cargo-features = ["default-run"]` to enable this feature
446+
"#,
447+
),
448+
);
449+
450+
assert_that(
451+
p.cargo("run").masquerade_as_nightly_cargo(),
452+
execs().with_status(101).with_stderr(
453+
r#"error: failed to parse manifest at [..]
454+
455+
Caused by:
456+
the `default-run` manifest key is unstable
457+
458+
Caused by:
459+
feature `default-run` is required
460+
461+
consider adding `cargo-features = ["default-run"]` to the manifest
462+
"#,
463+
),
464+
);
465+
}
466+
348467
#[test]
349468
fn run_example() {
350469
let p = project("foo")

0 commit comments

Comments
 (0)