Skip to content

Commit fe24df2

Browse files
committed
Auto merge of #2482 - ryanq:issue2266, r=alexcrichton
Suggest the best matching target for cargo run Targets passed to cargo compile are validated against the package. If the target exists, it is compiled. If not, cargo will bail and offer a suggested target name if there is a close match. The tests create and build/run binaries and examples using filenames that are close (or not so close) to the target names to verify that close matching names are suggested to the user. Sample output attached. <img width="838" alt="screen shot 2016-03-14 at 12 32 59 pm" src="https://cloud.githubusercontent.com/assets/124247/13754913/28490e42-e9ef-11e5-9617-8d3c01106527.png"> Closes #2266
2 parents 75ef4d8 + 68f993a commit fe24df2

File tree

5 files changed

+98
-4
lines changed

5 files changed

+98
-4
lines changed

Cargo.lock

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cargo/core/package.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ use std::path::{Path, PathBuf};
66

77
use semver::Version;
88

9-
use core::{Dependency, Manifest, PackageId, SourceId, Target};
9+
use core::{Dependency, Manifest, PackageId, SourceId, Target, TargetKind};
1010
use core::{Summary, Metadata, SourceMap};
1111
use ops;
12-
use util::{CargoResult, Config, LazyCell, ChainError, internal, human};
12+
use util::{CargoResult, Config, LazyCell, ChainError, internal, human, lev_distance};
1313
use rustc_serialize::{Encoder,Encodable};
1414

1515
/// Information about a package that is available somewhere in the file system.
@@ -89,6 +89,15 @@ impl Package {
8989
pub fn generate_metadata(&self) -> Metadata {
9090
self.package_id().generate_metadata()
9191
}
92+
93+
pub fn find_closest_target(&self, target: &str, kind: TargetKind) -> Option<&Target> {
94+
let targets = self.targets();
95+
96+
let matches = targets.iter().filter(|t| *t.kind() == kind)
97+
.map(|t| (lev_distance(target, t.name()), t))
98+
.filter(|&(d, _)| d < 4);
99+
matches.min_by_key(|t| t.0).map(|t| t.1)
100+
}
92101
}
93102

94103
impl fmt::Display for Package {

src/cargo/ops/cargo_compile.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,17 @@ fn generate_targets<'a>(pkg: &'a Package,
363363
});
364364
let t = match target {
365365
Some(t) => t,
366-
None => bail!("no {} target named `{}`", desc, name),
366+
None => {
367+
let suggestion = pkg.find_closest_target(name, kind);
368+
match suggestion {
369+
Some(s) => {
370+
let suggested_name = s.name();
371+
bail!("no {} target named `{}`\n\nDid you mean `{}`?",
372+
desc, name, suggested_name)
373+
}
374+
None => bail!("no {} target named `{}`", desc, name),
375+
}
376+
}
367377
};
368378
debug!("found {} `{}`", desc, name);
369379
targets.push((t, profile));

tests/test_cargo_compile.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -621,6 +621,44 @@ version required: *
621621
"#, error = ERROR, proj_dir = p.url())));
622622
});
623623

624+
test!(cargo_compile_with_filename{
625+
let p = project("foo")
626+
.file("Cargo.toml", r#"
627+
[project]
628+
name = "foo"
629+
version = "0.0.1"
630+
authors = []
631+
"#)
632+
.file("src/lib.rs", "")
633+
.file("src/bin/a.rs", r#"
634+
extern crate foo;
635+
fn main() { println!("hello a.rs"); }
636+
"#)
637+
.file("examples/a.rs", r#"
638+
fn main() { println!("example"); }
639+
"#);
640+
641+
assert_that(p.cargo_process("build").arg("--bin").arg("bin.rs"),
642+
execs().with_status(101).with_stderr(&format!("\
643+
{error} no bin target named `bin.rs`", error = ERROR)));
644+
645+
assert_that(p.cargo_process("build").arg("--bin").arg("a.rs"),
646+
execs().with_status(101).with_stderr(&format!("\
647+
{error} no bin target named `a.rs`
648+
649+
Did you mean `a`?", error = ERROR)));
650+
651+
assert_that(p.cargo_process("build").arg("--example").arg("example.rs"),
652+
execs().with_status(101).with_stderr(&format!("\
653+
{error} no example target named `example.rs`", error = ERROR)));
654+
655+
assert_that(p.cargo_process("build").arg("--example").arg("a.rs"),
656+
execs().with_status(101).with_stderr(&format!("\
657+
{error} no example target named `a.rs`
658+
659+
Did you mean `a`?", error = ERROR)));
660+
});
661+
624662
test!(compile_path_dep_then_change_version {
625663
let p = project("foo")
626664
.file("Cargo.toml", r#"

tests/test_cargo_run.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,44 @@ example
234234
sep = SEP)));
235235
});
236236

237+
test!(run_with_filename {
238+
let p = project("foo")
239+
.file("Cargo.toml", r#"
240+
[project]
241+
name = "foo"
242+
version = "0.0.1"
243+
authors = []
244+
"#)
245+
.file("src/lib.rs", "")
246+
.file("src/bin/a.rs", r#"
247+
extern crate foo;
248+
fn main() { println!("hello a.rs"); }
249+
"#)
250+
.file("examples/a.rs", r#"
251+
fn main() { println!("example"); }
252+
"#);
253+
254+
assert_that(p.cargo_process("run").arg("--bin").arg("bin.rs"),
255+
execs().with_status(101).with_stderr(&format!("\
256+
{error} no bin target named `bin.rs`", error = ERROR)));
257+
258+
assert_that(p.cargo_process("run").arg("--bin").arg("a.rs"),
259+
execs().with_status(101).with_stderr(&format!("\
260+
{error} no bin target named `a.rs`
261+
262+
Did you mean `a`?", error = ERROR)));
263+
264+
assert_that(p.cargo_process("run").arg("--example").arg("example.rs"),
265+
execs().with_status(101).with_stderr(&format!("\
266+
{error} no example target named `example.rs`", error = ERROR)));
267+
268+
assert_that(p.cargo_process("run").arg("--example").arg("a.rs"),
269+
execs().with_status(101).with_stderr(&format!("\
270+
{error} no example target named `a.rs`
271+
272+
Did you mean `a`?", error = ERROR)));
273+
});
274+
237275
test!(either_name_or_example {
238276
let p = project("foo")
239277
.file("Cargo.toml", r#"

0 commit comments

Comments
 (0)