diff --git a/src/cargo/util/toml/targets.rs b/src/cargo/util/toml/targets.rs index 70b84f815e9..9fdc9caa753 100644 --- a/src/cargo/util/toml/targets.rs +++ b/src/cargo/util/toml/targets.rs @@ -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. @@ -218,8 +219,11 @@ fn clean_examples(toml_examples: Option<&Vec>, package_root: &Path, errors: &mut Vec) -> CargoResult> { + + 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(); @@ -241,8 +245,11 @@ fn clean_examples(toml_examples: Option<&Vec>, fn clean_tests(toml_tests: Option<&Vec>, package_root: &Path, errors: &mut Vec) -> CargoResult> { + + 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(); @@ -272,8 +279,10 @@ fn clean_benches(toml_benches: Option<&Vec>, 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)?; @@ -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(), @@ -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) } diff --git a/src/doc/manifest.md b/src/doc/manifest.md index 7531f04548d..bf2cf4d2354 100644 --- a/src/doc/manifest.md +++ b/src/doc/manifest.md @@ -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 @@ -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 diff --git a/tests/build.rs b/tests/build.rs index 0aacd6cf1b6..23deb6e331f 100644 --- a/tests/build.rs +++ b/tests/build.rs @@ -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[..] @@ -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