Infer targets from subdirectories
authorrwakulszowa <rwakulszowa1@gmail.com>
Fri, 15 Sep 2017 13:37:34 +0000 (14:37 +0100)
committerrwakulszowa <rwakulszowa1@gmail.com>
Mon, 18 Sep 2017 11:26:15 +0000 (12:26 +0100)
src/cargo/util/toml/targets.rs
src/doc/manifest.md
tests/build.rs

index 70b84f815e96519971554b14a4a346501864dff8..9fdc9caa75347b7575a827620c7140759524d36c 100644 (file)
@@ -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<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();
@@ -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();
@@ -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)?;
@@ -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)
 }
index 7531f04548d520a12f69148859e56ac433ff2297..bf2cf4d23543133ce60deace29120e2c2dee59b6 100644 (file)
@@ -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
index 0aacd6cf1b648762bb4875b041c11a17e9c62b1a..23deb6e331f690d4cfc9cc481d3f45f934328f42 100644 (file)
@@ -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