Stabilize default-run

This commit is contained in:
Eric Huss 2019-06-21 11:36:53 -07:00
parent 142603ae22
commit 7d7fe6797a
20 changed files with 131 additions and 225 deletions

View File

@ -10,7 +10,7 @@ use std::fs;
use std::path::{Path, PathBuf};
use cargo::core::shell::Shell;
use cargo::util::{self, command_prelude, lev_distance, CargoResult, CliResult, Config};
use cargo::util::{self, closest_msg, command_prelude, CargoResult, CliResult, Config};
use cargo::util::{CliError, ProcessError};
mod cli;
@ -113,18 +113,6 @@ fn list_commands(config: &Config) -> BTreeSet<CommandInfo> {
commands
}
fn find_closest(config: &Config, cmd: &str) -> Option<String> {
let cmds = list_commands(config);
// Only consider candidates with a lev_distance of 3 or less so we don't
// suggest out-of-the-blue options.
cmds.into_iter()
.map(|c| c.name())
.map(|c| (lev_distance(&c, cmd), c))
.filter(|&(d, _)| d < 4)
.min_by_key(|a| a.0)
.map(|slot| slot.1)
}
fn execute_external_subcommand(config: &Config, cmd: &str, args: &[&str]) -> CliResult {
let command_exe = format!("cargo-{}{}", cmd, env::consts::EXE_SUFFIX);
let path = search_directories(config)
@ -134,14 +122,9 @@ fn execute_external_subcommand(config: &Config, cmd: &str, args: &[&str]) -> Cli
let command = match path {
Some(command) => command,
None => {
let err = match find_closest(config, cmd) {
Some(closest) => failure::format_err!(
"no such subcommand: `{}`\n\n\tDid you mean `{}`?\n",
cmd,
closest
),
None => failure::format_err!("no such subcommand: `{}`", cmd),
};
let cmds = list_commands(config);
let did_you_mean = closest_msg(cmd, cmds.iter(), |c| c.name());
let err = failure::format_err!("no such subcommand: `{}`{}", cmd, did_you_mean);
return Err(CliError::new(err, 101));
}
};

View File

@ -196,7 +196,7 @@ features! {
[unstable] namespaced_features: bool,
// "default-run" manifest option,
[unstable] default_run: bool,
[stable] default_run: bool,
// Declarative build scripts.
[unstable] metabuild: bool,

View File

@ -518,12 +518,6 @@ impl Manifest {
})?;
}
if self.default_run.is_some() {
self.features
.require(Feature::default_run())
.chain_err(|| failure::format_err!("the `default-run` manifest key is unstable"))?;
}
Ok(())
}

View File

@ -25,7 +25,7 @@ use crate::ops;
use crate::util::config::PackageCacheLock;
use crate::util::errors::{CargoResult, CargoResultExt, HttpNot200};
use crate::util::network::Retry;
use crate::util::{self, internal, lev_distance, Config, Progress, ProgressStyle};
use crate::util::{self, internal, Config, Progress, ProgressStyle};
/// Information about a package that is available somewhere in the file system.
///
@ -193,21 +193,6 @@ impl Package {
self.targets().iter().any(|t| t.is_custom_build())
}
pub fn find_closest_target(
&self,
target: &str,
is_expected_kind: fn(&Target) -> bool,
) -> Option<&Target> {
let targets = self.targets();
let matches = targets
.iter()
.filter(|t| is_expected_kind(t))
.map(|t| (lev_distance(target, t.name()), t))
.filter(|&(d, _)| d < 4);
matches.min_by_key(|t| t.0).map(|t| t.1)
}
pub fn map_source(self, to_replace: SourceId, replace_with: SourceId) -> Package {
Package {
manifest: self.manifest.map_source(to_replace, replace_with),

View File

@ -7,9 +7,8 @@ use crate::core::compiler::CompileMode;
use crate::core::interning::InternedString;
use crate::core::{Features, PackageId, PackageIdSpec, PackageSet, Shell};
use crate::util::errors::CargoResultExt;
use crate::util::lev_distance::lev_distance;
use crate::util::toml::{ProfilePackageSpec, StringOrBool, TomlProfile, TomlProfiles, U32OrBool};
use crate::util::{CargoResult, Config};
use crate::util::{closest_msg, CargoResult, Config};
/// Collection of all user profiles.
#[derive(Clone, Debug)]
@ -290,23 +289,12 @@ impl ProfileMaker {
})
.collect();
if name_matches.is_empty() {
let suggestion = packages
.package_ids()
.map(|p| (lev_distance(&*spec.name(), &p.name()), p.name()))
.filter(|&(d, _)| d < 4)
.min_by_key(|p| p.0)
.map(|p| p.1);
match suggestion {
Some(p) => shell.warn(format!(
"profile override spec `{}` did not match any packages\n\n\
Did you mean `{}`?",
spec, p
))?,
None => shell.warn(format!(
"profile override spec `{}` did not match any packages",
spec
))?,
}
let suggestion =
closest_msg(&spec.name(), packages.package_ids(), |p| p.name().as_str());
shell.warn(format!(
"profile override spec `{}` did not match any packages{}",
spec, suggestion
))?;
} else {
shell.warn(format!(
"version or URL in profile override spec `{}` does not \

View File

@ -35,7 +35,7 @@ use crate::core::{Package, Target};
use crate::core::{PackageId, PackageIdSpec, TargetKind, Workspace};
use crate::ops;
use crate::util::config::Config;
use crate::util::{lev_distance, profile, CargoResult};
use crate::util::{closest_msg, profile, CargoResult};
/// Contains information about how a package should be compiled.
#[derive(Debug)]
@ -906,26 +906,18 @@ fn find_named_targets<'a>(
let filter = |t: &Target| t.name() == target_name && is_expected_kind(t);
let proposals = filter_targets(packages, filter, true, mode);
if proposals.is_empty() {
let suggestion = packages
.iter()
.flat_map(|pkg| {
pkg.targets()
.iter()
.filter(|target| is_expected_kind(target))
})
.map(|target| (lev_distance(target_name, target.name()), target))
.filter(|&(d, _)| d < 4)
.min_by_key(|t| t.0)
.map(|t| t.1);
match suggestion {
Some(s) => failure::bail!(
"no {} target named `{}`\n\nDid you mean `{}`?",
target_desc,
target_name,
s.name()
),
None => failure::bail!("no {} target named `{}`", target_desc, target_name),
}
let targets = packages.iter().flat_map(|pkg| {
pkg.targets()
.iter()
.filter(|target| is_expected_kind(target))
});
let suggestion = closest_msg(target_name, targets, |t| t.name());
failure::bail!(
"no {} target named `{}`{}",
target_desc,
target_name,
suggestion
);
}
Ok(proposals)
}

View File

@ -2,7 +2,7 @@ use std::ffi::OsString;
use std::iter;
use std::path::Path;
use crate::core::{nightly_features_allowed, TargetKind, Workspace};
use crate::core::{TargetKind, Workspace};
use crate::ops;
use crate::util::{CargoResult, ProcessError};
@ -55,22 +55,13 @@ pub fn run(
.into_iter()
.map(|(_pkg, target)| target.name())
.collect();
if nightly_features_allowed() {
failure::bail!(
"`cargo run` could not determine which binary to run. \
Use the `--bin` option to specify a binary, \
or (on nightly) the `default-run` manifest key.\n\
available binaries: {}",
names.join(", ")
)
} else {
failure::bail!(
"`cargo run` requires that a package only have one \
executable; use the `--bin` option to specify which one \
to run\navailable binaries: {}",
names.join(", ")
)
}
failure::bail!(
"`cargo run` could not determine which binary to run. \
Use the `--bin` option to specify a binary, \
or the `default-run` manifest key.\n\
available binaries: {}",
names.join(", ")
)
} else {
failure::bail!(
"`cargo run` can run at most one executable, but \

View File

@ -514,10 +514,10 @@ pub enum CommandInfo {
}
impl CommandInfo {
pub fn name(&self) -> String {
pub fn name(&self) -> &str {
match self {
CommandInfo::BuiltIn { name, .. } => name.to_string(),
CommandInfo::External { name, .. } => name.to_string(),
CommandInfo::BuiltIn { name, .. } => &name,
CommandInfo::External { name, .. } => &name,
}
}
}

View File

@ -33,6 +33,34 @@ pub fn lev_distance(me: &str, t: &str) -> usize {
dcol[t_last + 1]
}
/// Find the closest element from `iter` matching `choice`. The `key` callback
/// is used to select a `&str` from the iterator to compare against `choice`.
pub fn closest<'a, T>(
choice: &str,
iter: impl Iterator<Item = T>,
key: impl Fn(&T) -> &'a str,
) -> Option<T> {
// Only consider candidates with a lev_distance of 3 or less so we don't
// suggest out-of-the-blue options.
iter.map(|e| (lev_distance(choice, key(&e)), e))
.filter(|&(d, _)| d < 4)
.min_by_key(|t| t.0)
.map(|t| t.1)
}
/// Version of `closest` that returns a common "suggestion" that can be tacked
/// onto the end of an error message.
pub fn closest_msg<'a, T>(
choice: &str,
iter: impl Iterator<Item = T>,
key: impl Fn(&T) -> &'a str,
) -> String {
match closest(choice, iter, &key) {
Some(e) => format!("\n\n\tDid you mean `{}`?", key(&e)),
None => String::new(),
}
}
#[test]
fn test_lev_distance() {
use std::char::{from_u32, MAX};

View File

@ -12,7 +12,7 @@ pub use self::graph::Graph;
pub use self::hex::{hash_u64, short_hash, to_hex};
pub use self::into_url::IntoUrl;
pub use self::into_url_with_base::IntoUrlWithBase;
pub use self::lev_distance::lev_distance;
pub use self::lev_distance::{closest, closest_msg, lev_distance};
pub use self::lockserver::{LockServer, LockServerClient, LockServerStarted};
pub use self::paths::{bytes2path, dylib_path, join_paths, path2bytes};
pub use self::paths::{dylib_path_envvar, normalize_path};

View File

@ -21,8 +21,7 @@ use crate::core::{Edition, EitherManifest, Feature, Features, VirtualManifest};
use crate::core::{GitReference, PackageIdSpec, SourceId, WorkspaceConfig, WorkspaceRootConfig};
use crate::sources::{CRATES_IO_INDEX, CRATES_IO_REGISTRY};
use crate::util::errors::{CargoResult, CargoResultExt, ManifestError};
use crate::util::paths;
use crate::util::{self, validate_package_name, Config, IntoUrl};
use crate::util::{self, paths, validate_package_name, Config, IntoUrl};
mod targets;
use self::targets::targets;
@ -1052,6 +1051,18 @@ impl TomlManifest {
)
}
if let Some(run) = &project.default_run {
if !targets
.iter()
.filter(|t| t.is_bin())
.any(|t| t.name() == run)
{
let suggestion =
util::closest_msg(&run, targets.iter().filter(|t| t.is_bin()), |t| t.name());
bail!("default-run target `{}` not found{}", run, suggestion);
}
}
let custom_metadata = project.metadata.clone();
let mut manifest = Manifest::new(
summary,

View File

@ -29,7 +29,8 @@ include::options-package.adoc[]
When no target selection options are given, `cargo run` will run the binary
target. If there are multiple binary targets, you must pass a target flag to
choose one.
choose one. Or, the `default-run` field may be specified in the `[package]`
section of `Cargo.toml` to choose the name of the binary to run by default.
*--bin* _NAME_::
Run the specified binary.

View File

@ -48,7 +48,8 @@ the SPEC format.</p>
<div class="paragraph">
<p>When no target selection options are given, <code>cargo run</code> will run the binary
target. If there are multiple binary targets, you must pass a target flag to
choose one.</p>
choose one. Or, the <code>default-run</code> field may be specified in the <code>[package]</code>
section of <code>Cargo.toml</code> to choose the name of the binary to run by default.</p>
</div>
<div class="dlist">
<dl>

View File

@ -21,7 +21,7 @@ when listed as a dependency in another package, and as the default name of
inferred lib and bin targets.
The name must not be empty, use only [alphanumeric] characters or `-` or `_`.
Note that `cargo new` and `cargo init` impose some additional restrictions on
Note that [`cargo new`] and [`cargo init`] impose some additional restrictions on
the package name, such as enforcing that it is a valid Rust identifier and not
a keyword. [crates.io][cratesio] imposes even more restrictions, such as
enforcing only ASCII characters, not a reserved name, not a special Windows
@ -66,7 +66,7 @@ edition = '2018'
```
The `edition` key affects which edition your package is compiled with. Cargo
will always generate packages via `cargo new` with the `edition` key set to the
will always generate packages via [`cargo new`] with the `edition` key set to the
latest edition. Setting the `edition` key in `[package]` will affect all
targets/crates in the package, including test suites, benchmarks, binaries,
examples, etc.
@ -364,6 +364,17 @@ package-name = "my-awesome-android-app"
assets = "path/to/static"
```
#### The `default-run` field
The `default-run` field in the `[package]` 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
[package]
default-run = "a"
```
### Dependency sections
See the [specifying dependencies page](reference/specifying-dependencies.html) for
@ -629,8 +640,8 @@ A crate may either specify `package.workspace` or specify `[workspace]`. That
is, a crate cannot both be a root crate in a workspace (contain `[workspace]`)
and also be a member crate of another workspace (contain `package.workspace`).
Most of the time workspaces will not need to be dealt with as `cargo new` and
`cargo init` will handle workspace configuration automatically.
Most of the time workspaces will not need to be dealt with as [`cargo new`] and
[`cargo init`] will handle workspace configuration automatically.
[globs]: https://docs.rs/glob/0.2.11/glob/struct.Pattern.html
@ -643,7 +654,7 @@ manifest*.
#### Package selection
In a workspace, package-related cargo commands like `cargo build` apply to
In a workspace, package-related cargo commands like [`cargo build`] apply to
packages selected by `-p` / `--package` or `--all` command-line parameters.
When neither is specified, the optional `default-members` configuration is used:
@ -733,7 +744,7 @@ You can build individual library examples with the command `cargo build
### Tests
When you run `cargo test`, Cargo will:
When you run [`cargo test`], Cargo will:
* compile and run your librarys unit tests, which are in the files reachable
from `lib.rs` (naturally, any sections marked with `#[cfg(test)]` will be
@ -745,7 +756,7 @@ When you run `cargo test`, Cargo will:
#### Integration tests
Each file in `tests/*.rs` is an integration test. When you run `cargo test`,
Each file in `tests/*.rs` is an integration test. When you run [`cargo test`],
Cargo will compile each of these files as a separate crate. The crate can link
to your library by using `extern crate <library-name>`, like any other code that
depends on it.
@ -966,6 +977,11 @@ source (e.g., git or a local path).
More information about overriding dependencies can be found in the [overriding
dependencies][replace] section of the documentation.
[`cargo build`]: commands/cargo-build.html
[`cargo init`]: commands/cargo-init.html
[`cargo new`]: commands/cargo-new.html
[`cargo run`]: commands/cargo-run.html
[`cargo test`]: commands/cargo-test.html
[spdx-2.1-license-expressions]: https://spdx.org/spdx-specification-21-web-version#h.jxpfx0ykyb60
[spdx-license-list]: https://spdx.org/licenses/
[spdx-license-list-2.4]: https://github.com/spdx/license-list-data/tree/v2.4
[spdx-license-list]: https://spdx.org/licenses/

View File

@ -155,18 +155,6 @@ Example:
cargo +nightly build --build-plan -Z unstable-options
```
### default-run
* Original issue: [#2200](https://github.com/rust-lang/cargo/issues/2200)
The `default-run` option in the `[package]` 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
[package]
default-run = "a"
```
### Metabuild
* Tracking Issue: [rust-lang/rust#49803](https://github.com/rust-lang/rust/issues/49803)
* RFC: [#2196](https://github.com/rust-lang/rfcs/blob/master/text/2196-metabuild.md)

View File

@ -2,12 +2,12 @@
.\" Title: cargo-run
.\" Author: [see the "AUTHOR(S)" section]
.\" Generator: Asciidoctor 1.5.8
.\" Date: 2019-05-08
.\" Date: 2019-06-21
.\" Manual: \ \&
.\" Source: \ \&
.\" Language: English
.\"
.TH "CARGO\-RUN" "1" "2019-05-08" "\ \&" "\ \&"
.TH "CARGO\-RUN" "1" "2019-06-21" "\ \&" "\ \&"
.ie \n(.g .ds Aq \(aq
.el .ds Aq '
.ss \n[.ss] 0
@ -54,7 +54,8 @@ the SPEC format.
.sp
When no target selection options are given, \fBcargo run\fP will run the binary
target. If there are multiple binary targets, you must pass a target flag to
choose one.
choose one. Or, the \fBdefault\-run\fP field may be specified in the \fB[package]\fP
section of \fBCargo.toml\fP to choose the name of the binary to run by default.
.sp
\fB\-\-bin\fP \fINAME\fP
.RS 4

View File

@ -936,7 +936,7 @@ fn cargo_compile_with_filename() {
"\
[ERROR] no bin target named `a.rs`
Did you mean `a`?",
<tab>Did you mean `a`?",
)
.run();
@ -951,7 +951,7 @@ Did you mean `a`?",
"\
[ERROR] no example target named `a.rs`
Did you mean `a`?",
<tab>Did you mean `a`?",
)
.run();
}
@ -4417,7 +4417,7 @@ fn target_filters_workspace() {
"\
[ERROR] no example target named `ex`
Did you mean `ex1`?",
<tab>Did you mean `ex1`?",
)
.run();

View File

@ -141,7 +141,7 @@ fn profile_override_warnings() {
[WARNING] version or URL in profile override spec `bar:1.2.3` does not match any of the packages: bar v0.5.0 ([..])
[WARNING] profile override spec `bart` did not match any packages
Did you mean `bar`?
<tab>Did you mean `bar`?
[WARNING] profile override spec `no-suggestion` did not match any packages
[COMPILING] [..]
",

View File

@ -1154,8 +1154,8 @@ fn run_default_multiple_required_features() {
.with_status(101)
.with_stderr(
"\
error: `cargo run` requires that a package 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[..]
available binaries: foo1, foo2",
)
.run();
}

View File

@ -168,22 +168,11 @@ fn too_many_bins() {
// Using [..] here because the order is not stable
p.cargo("run")
.with_status(101)
.with_stderr(
"[ERROR] `cargo run` requires that a package only \
have one executable; use the `--bin` option \
to specify which one to run\navailable binaries: [..]\n",
)
.run();
// Using [..] here because the order is not stable
p.cargo("run")
.masquerade_as_nightly_cargo()
.with_status(101)
.with_stderr(
"[ERROR] `cargo run` could not determine which binary to run. \
Use the `--bin` option to specify a binary, or (on \
nightly) the `default-run` manifest key.\
Use the `--bin` option to specify a binary, or the \
`default-run` manifest key.\
\navailable binaries: [..]\n",
)
.run();
@ -241,8 +230,6 @@ fn specify_default_run() {
.file(
"Cargo.toml",
r#"
cargo-features = ["default-run"]
[project]
name = "foo"
version = "0.0.1"
@ -255,18 +242,9 @@ fn specify_default_run() {
.file("src/bin/b.rs", r#"fn main() { println!("hello B"); }"#)
.build();
p.cargo("run")
.masquerade_as_nightly_cargo()
.with_stdout("hello A")
.run();
p.cargo("run --bin a")
.masquerade_as_nightly_cargo()
.with_stdout("hello A")
.run();
p.cargo("run --bin b")
.masquerade_as_nightly_cargo()
.with_stdout("hello B")
.run();
p.cargo("run").with_stdout("hello A").run();
p.cargo("run --bin a").with_stdout("hello A").run();
p.cargo("run --bin b").with_stdout("hello B").run();
}
#[cargo_test]
@ -275,8 +253,6 @@ fn bogus_default_run() {
.file(
"Cargo.toml",
r#"
cargo-features = ["default-run"]
[project]
name = "foo"
version = "0.0.1"
@ -288,61 +264,17 @@ fn bogus_default_run() {
.file("src/bin/a.rs", r#"fn main() { println!("hello A"); }"#)
.build();
p.cargo("run")
.masquerade_as_nightly_cargo()
.with_status(101)
.with_stderr("error: no bin target named `b`\n\nDid you mean [..]?")
.run();
}
#[cargo_test]
fn default_run_unstable() {
let p = project()
.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();
p.cargo("run")
.with_status(101)
.with_stderr(
r#"error: failed to parse manifest at [..]
"\
[ERROR] failed to parse manifest at `[..]/foo/Cargo.toml`
Caused by:
the `default-run` manifest key is unstable
default-run target `b` not found
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
"#,
)
.run();
p.cargo("run")
.masquerade_as_nightly_cargo()
.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
"#,
<tab>Did you mean `a`?
",
)
.run();
}
@ -586,7 +518,7 @@ fn run_with_filename() {
"\
[ERROR] no bin target named `a.rs`
Did you mean `a`?",
<tab>Did you mean `a`?",
)
.run();
@ -601,7 +533,7 @@ Did you mean `a`?",
"\
[ERROR] no example target named `a.rs`
Did you mean `a`?",
<tab>Did you mean `a`?",
)
.run();
}
@ -1103,7 +1035,7 @@ fn run_workspace() {
.with_status(101)
.with_stderr(
"\
[ERROR] `cargo run` requires that a package only have one executable[..]
[ERROR] `cargo run` could not determine which binary to run[..]
available binaries: a, b",
)
.run();
@ -1123,8 +1055,6 @@ fn default_run_workspace() {
.file(
"a/Cargo.toml",
r#"
cargo-features = ["default-run"]
[project]
name = "a"
version = "0.0.1"
@ -1136,10 +1066,7 @@ fn default_run_workspace() {
.file("b/src/main.rs", r#"fn main() {println!("run-b");}"#)
.build();
p.cargo("run")
.masquerade_as_nightly_cargo()
.with_stdout("run-a")
.run();
p.cargo("run").with_stdout("run-a").run();
}
#[cargo_test]