Skip to content

Commit

Permalink
Infer targets from subdirectories
Browse files Browse the repository at this point in the history
  • Loading branch information
rwakulszowa committed Sep 18, 2017
1 parent 525323f commit 6b94ed2
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 43 deletions.
86 changes: 45 additions & 41 deletions src/cargo/util/toml/targets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
//! * `src/bin/*.rs` are binaries
//! * `examples/*.rs` are examples
//! * `tests/*.rs` are integration tests
//! * `benches/*.rs` are benchmarks
//!
//! It is a bit tricky because we need match explicit information from `Cargo.toml`
//! with implicit info in directory layout.
Expand Down Expand Up @@ -218,8 +219,11 @@ fn clean_examples(toml_examples: Option<&Vec<TomlExampleTarget>>,
package_root: &Path,
errors: &mut Vec<String>)
-> CargoResult<Vec<Target>> {

let inferred = infer_from_directory(&package_root.join("examples"));

let targets = clean_targets("example", "example",
toml_examples, inferred_examples(package_root),
toml_examples, inferred,
package_root, errors)?;

let mut result = Vec::new();
Expand All @@ -241,8 +245,11 @@ fn clean_examples(toml_examples: Option<&Vec<TomlExampleTarget>>,
fn clean_tests(toml_tests: Option<&Vec<TomlTestTarget>>,
package_root: &Path,
errors: &mut Vec<String>) -> CargoResult<Vec<Target>> {

let inferred = infer_from_directory(&package_root.join("tests"));

let targets = clean_targets("test", "test",
toml_tests, inferred_tests(package_root),
toml_tests, inferred,
package_root, errors)?;

let mut result = Vec::new();
Expand Down Expand Up @@ -272,8 +279,10 @@ fn clean_benches(toml_benches: Option<&Vec<TomlBenchTarget>>,
Some(legacy_path)
};

let inferred = infer_from_directory(&package_root.join("benches"));

let targets = clean_targets_with_legacy_path("benchmark", "bench",
toml_benches, inferred_benches(package_root),
toml_benches, inferred,
package_root,
errors,
&mut legacy_bench_path)?;
Expand Down Expand Up @@ -359,41 +368,9 @@ fn inferred_bins(package_root: &Path, package_name: &str) -> Vec<(String, PathBu
}
result.extend(infer_from_directory(&package_root.join("src").join("bin")));

if let Ok(entries) = fs::read_dir(&package_root.join("src").join("bin")) {
let multifile_bins = entries
.filter_map(|e| e.ok())
.filter(is_not_dotfile)
.filter(|e| match e.file_type() {
Ok(t) if t.is_dir() => true,
_ => false
})
.filter_map(|entry| {
let dir = entry.path();
let main = dir.join("main.rs");
let name = dir.file_name().and_then(|n| n.to_str());
match (main.exists(), name) {
(true, Some(name)) => Some((name.to_owned(), main)),
_ => None
}
});
result.extend(multifile_bins);
}

result
}

fn inferred_examples(package_root: &Path) -> Vec<(String, PathBuf)> {
infer_from_directory(&package_root.join("examples"))
}

fn inferred_tests(package_root: &Path) -> Vec<(String, PathBuf)> {
infer_from_directory(&package_root.join("tests"))
}

fn inferred_benches(package_root: &Path) -> Vec<(String, PathBuf)> {
infer_from_directory(&package_root.join("benches"))
}

fn infer_from_directory(directory: &Path) -> Vec<(String, PathBuf)> {
let entries = match fs::read_dir(directory) {
Err(_) => return Vec::new(),
Expand All @@ -403,15 +380,42 @@ fn infer_from_directory(directory: &Path) -> Vec<(String, PathBuf)> {
entries
.filter_map(|e| e.ok())
.filter(is_not_dotfile)
.map(|e| e.path())
.filter(|f| f.extension().and_then(|s| s.to_str()) == Some("rs"))
.filter_map(|f| {
f.file_stem().and_then(|s| s.to_str())
.map(|s| (s.to_owned(), f.clone()))
})
.filter_map(infer_any)
.collect()
}


fn infer_any(entry: DirEntry) -> Option<(String, PathBuf)> {
if entry.path().extension().and_then(|p| p.to_str()) == Some("rs") {
infer_file(entry)
} else if entry.file_type().map(|t| t.is_dir()).ok() == Some(true) {
infer_subdirectory(entry)
} else {
None
}
}


fn infer_file(entry: DirEntry) -> Option<(String, PathBuf)> {
let path = entry.path();
path
.file_stem()
.and_then(|p| p.to_str())
.map(|p| (p.to_owned(), path.clone()))
}


fn infer_subdirectory(entry: DirEntry) -> Option<(String, PathBuf)> {
let path = entry.path();
let main = path.join("main.rs");
let name = path.file_name().and_then(|n| n.to_str());
match (name, main.exists()) {
(Some(name), true) => Some((name.to_owned(), main)),
_ => None
}
}


fn is_not_dotfile(entry: &DirEntry) -> bool {
entry.file_name().to_str().map(|s| s.starts_with('.')) == Some(false)
}
Expand Down
9 changes: 8 additions & 1 deletion src/doc/manifest.md
Original file line number Diff line number Diff line change
Expand Up @@ -532,7 +532,8 @@ each file you want to build.

Your project can optionally contain folders named `examples`, `tests`, and
`benches`, which Cargo will treat as containing examples,
integration tests, and benchmarks respectively.
integration tests, and benchmarks respectively. Analogous to `bin` targets, they
may be composed of single files or directories with a `main.rs` file.

```notrust
▾ src/ # directory containing source files
Expand All @@ -544,10 +545,16 @@ integration tests, and benchmarks respectively.
main.rs
▾ examples/ # (optional) examples
*.rs
▾ */ # (optional) directories containing multi-file examples
main.rs
▾ tests/ # (optional) integration tests
*.rs
▾ */ # (optional) directories containing multi-file tests
main.rs
▾ benches/ # (optional) benchmarks
*.rs
▾ */ # (optional) directories containing multi-file benchmarks
main.rs
```

To structure your code after you've created the files and folders for your
Expand Down
73 changes: 72 additions & 1 deletion tests/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3495,7 +3495,7 @@ fn dir_and_file_with_same_name_in_bin() {
.file("src/bin/foo.rs", "fn main() {}")
.file("src/bin/foo/main.rs", "fn main() {}");

assert_that(p.cargo_process("build"),
assert_that(p.cargo_process("build"),
execs().with_status(101)
.with_stderr_contains("\
[..]found duplicate binary name foo, but all binary targets must have a unique name[..]
Expand All @@ -3521,6 +3521,77 @@ fn inferred_path_in_src_bin_foo() {
assert_that(&p.bin("bar"), existing_file());
}

#[test]
fn inferred_examples() {
let p = project("foo")
.file("Cargo.toml", r#"
[package]
name = "foo"
version = "0.1.0"
authors = []
"#)
.file("src/lib.rs", "fn main() {}")
.file("examples/bar.rs", "fn main() {}")
.file("examples/baz/main.rs", "fn main() {}");

assert_that(p.cargo_process("test"), execs().with_status(0));
assert_that(&p.bin("examples/bar"), existing_file());
assert_that(&p.bin("examples/baz"), existing_file());
}

#[test]
fn inferred_tests() {
let p = project("foo")
.file("Cargo.toml", r#"
[package]
name = "foo"
version = "0.1.0"
authors = []
"#)
.file("src/lib.rs", "fn main() {}")
.file("tests/bar.rs", "fn main() {}")
.file("tests/baz/main.rs", "fn main() {}");

assert_that(
p.cargo_process("test").arg("--test=bar").arg("--test=baz"),
execs().with_status(0));
}

#[test]
fn inferred_benchmark() {
let p = project("foo")
.file("Cargo.toml", r#"
[package]
name = "foo"
version = "0.1.0"
authors = []
"#)
.file("src/lib.rs", "fn main() {}")
.file("benches/bar.rs", "fn main() {}");

assert_that(
p.cargo_process("bench").arg("--bench=bar"),
execs().with_status(0));
}

#[test]
fn inferred_benchmark_from_directory() {
//FIXME: merge with `inferred_benchmark` after fixing #4504
let p = project("foo")
.file("Cargo.toml", r#"
[package]
name = "foo"
version = "0.1.0"
authors = []
"#)
.file("src/lib.rs", "fn main() {}")
.file("benches/bar/main.rs", "fn main() {}");

assert_that(
p.cargo_process("bench").arg("--bench=bar"),
execs().with_status(0));
}

#[test]
fn same_metadata_different_directory() {
// A top-level crate built in two different workspaces should have the
Expand Down

0 comments on commit 6b94ed2

Please sign in to comment.