From: Dale Wijnand Date: Mon, 16 Apr 2018 22:19:42 +0000 (+0200) Subject: Introduce autoXXX keys for target auto-discovery X-Git-Tag: archive/raspbian/0.35.0-2+rpi1~3^2^2^2^2^2^2^2~22^2~1^2~52^2~4 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=e471745e475e592c8a76e004f035869bdfd6de88;p=cargo.git Introduce autoXXX keys for target auto-discovery In Rust 2015 absence of the configuration makes it default to not include auto-discovered targets (i.e false), with a warnings message. In Rust 2018 absence makes it default to include auto-discovered targets (i.e true). Fixes #5330 --- diff --git a/src/cargo/util/toml/mod.rs b/src/cargo/util/toml/mod.rs index bb9fb7b4d..c1daaf909 100644 --- a/src/cargo/util/toml/mod.rs +++ b/src/cargo/util/toml/mod.rs @@ -459,6 +459,10 @@ pub struct TomlProject { workspace: Option, #[serde(rename = "im-a-teapot")] im_a_teapot: Option, + autobins: Option, + autoexamples: Option, + autotests: Option, + autobenches: Option, // package metadata description: Option, @@ -653,6 +657,7 @@ impl TomlManifest { me, package_name, package_root, + edition, &project.build, &mut warnings, &mut errors, diff --git a/src/cargo/util/toml/targets.rs b/src/cargo/util/toml/targets.rs index 182154ce6..2384dc28c 100644 --- a/src/cargo/util/toml/targets.rs +++ b/src/cargo/util/toml/targets.rs @@ -14,7 +14,7 @@ use std::path::{Path, PathBuf}; use std::fs::{self, DirEntry}; use std::collections::HashSet; -use core::{compiler, Target}; +use core::{compiler, Edition, Target}; use util::errors::CargoResult; use super::{LibKind, PathValue, StringOrBool, TomlBenchTarget, TomlBinTarget, TomlExampleTarget, TomlLibTarget, TomlManifest, TomlTarget, TomlTestTarget}; @@ -23,6 +23,7 @@ pub fn targets( manifest: &TomlManifest, package_name: &str, package_root: &Path, + edition: Edition, custom_build: &Option, warnings: &mut Vec, errors: &mut Vec, @@ -38,10 +39,18 @@ pub fn targets( has_lib = false; } + let package = manifest + .package + .as_ref() + .or_else(|| manifest.project.as_ref()) + .ok_or_else(|| format_err!("manifest has no `package` (or `project`)"))?; + targets.extend(clean_bins( manifest.bin.as_ref(), package_root, package_name, + edition, + package.autobins, warnings, errors, has_lib, @@ -50,14 +59,26 @@ pub fn targets( targets.extend(clean_examples( manifest.example.as_ref(), package_root, + edition, + package.autoexamples, + warnings, errors, )?); - targets.extend(clean_tests(manifest.test.as_ref(), package_root, errors)?); + targets.extend(clean_tests( + manifest.test.as_ref(), + package_root, + edition, + package.autotests, + warnings, + errors, + )?); targets.extend(clean_benches( manifest.bench.as_ref(), package_root, + edition, + package.autobenches, warnings, errors, )?); @@ -163,6 +184,8 @@ fn clean_bins( toml_bins: Option<&Vec>, package_root: &Path, package_name: &str, + edition: Edition, + autodiscover: Option, warnings: &mut Vec, errors: &mut Vec, has_lib: bool, @@ -172,6 +195,13 @@ fn clean_bins( let bins = toml_targets_and_inferred( toml_bins, &inferred, + package_root, + autodiscover, + edition, + warnings, + "binary", + "bin", + "autobins", ); for bin in &bins { @@ -254,6 +284,9 @@ fn clean_bins( fn clean_examples( toml_examples: Option<&Vec>, package_root: &Path, + edition: Edition, + autodiscover: Option, + warnings: &mut Vec, errors: &mut Vec, ) -> CargoResult> { let inferred = infer_from_directory(&package_root.join("examples")); @@ -264,7 +297,11 @@ fn clean_examples( toml_examples, &inferred, package_root, + edition, + autodiscover, + warnings, errors, + "autoexamples", )?; let mut result = Vec::new(); @@ -290,11 +327,25 @@ fn clean_examples( fn clean_tests( toml_tests: Option<&Vec>, package_root: &Path, + edition: Edition, + autodiscover: Option, + warnings: &mut Vec, errors: &mut Vec, ) -> CargoResult> { let inferred = infer_from_directory(&package_root.join("tests")); - let targets = clean_targets("test", "test", toml_tests, &inferred, package_root, errors)?; + let targets = clean_targets( + "test", + "test", + toml_tests, + &inferred, + package_root, + edition, + autodiscover, + warnings, + errors, + "autotests", + )?; let mut result = Vec::new(); for (path, toml) in targets { @@ -308,6 +359,8 @@ fn clean_tests( fn clean_benches( toml_benches: Option<&Vec>, package_root: &Path, + edition: Edition, + autodiscover: Option, warnings: &mut Vec, errors: &mut Vec, ) -> CargoResult> { @@ -336,8 +389,12 @@ fn clean_benches( toml_benches, &inferred, package_root, + edition, + autodiscover, + warnings, errors, &mut legacy_bench_path, + "autobenches", )? }; @@ -359,7 +416,11 @@ fn clean_targets( toml_targets: Option<&Vec>, inferred: &[(String, PathBuf)], package_root: &Path, + edition: Edition, + autodiscover: Option, + warnings: &mut Vec, errors: &mut Vec, + autodiscover_flag_name: &str, ) -> CargoResult> { clean_targets_with_legacy_path( target_kind_human, @@ -367,8 +428,12 @@ fn clean_targets( toml_targets, inferred, package_root, + edition, + autodiscover, + warnings, errors, &mut |_| None, + autodiscover_flag_name, ) } @@ -378,12 +443,23 @@ fn clean_targets_with_legacy_path( toml_targets: Option<&Vec>, inferred: &[(String, PathBuf)], package_root: &Path, + edition: Edition, + autodiscover: Option, + warnings: &mut Vec, errors: &mut Vec, legacy_path: &mut FnMut(&TomlTarget) -> Option, + autodiscover_flag_name: &str, ) -> CargoResult> { let toml_targets = toml_targets_and_inferred( toml_targets, inferred, + package_root, + autodiscover, + edition, + warnings, + target_kind_human, + target_kind, + autodiscover_flag_name, ); for target in &toml_targets { @@ -473,10 +549,81 @@ fn is_not_dotfile(entry: &DirEntry) -> bool { fn toml_targets_and_inferred( toml_targets: Option<&Vec>, inferred: &[(String, PathBuf)], + package_root: &Path, + autodiscover: Option, + edition: Edition, + warnings: &mut Vec, + target_kind_human: &str, + target_kind: &str, + autodiscover_flag_name: &str, ) -> Vec { + let inferred_targets = inferred_to_toml_targets(inferred); match toml_targets { - None => inferred_to_toml_targets(inferred), - Some(targets) => targets.clone(), + None => inferred_targets, + Some(targets) => { + let mut targets = targets.clone(); + + let target_path = + |target: &TomlTarget| target.path.clone().map(|p| package_root.join(p.0)); + + let mut seen_names = HashSet::new(); + let mut seen_paths = HashSet::new(); + for target in targets.iter() { + seen_names.insert(target.name.clone()); + seen_paths.insert(target_path(target)); + } + + let mut rem_targets = vec![]; + for target in inferred_targets { + if !seen_names.contains(&target.name) && !seen_paths.contains(&target_path(&target)) + { + rem_targets.push(target); + } + } + + let autodiscover = match autodiscover { + Some(autodiscover) => autodiscover, + None => match edition { + Edition::Edition2018 => true, + Edition::Edition2015 => { + if !rem_targets.is_empty() { + let mut rem_targets_str = String::new(); + for t in rem_targets.iter() { + if let Some(p) = t.path.clone() { + rem_targets_str.push_str(&format!("* {:?}\n", p.0)) + } + } + warnings.push(format!( + "\ +An explicit [[{section}]] section is specified in Cargo.toml which currently disables Cargo from \ +automatically inferring other {target_kind_human} targets. This inference behavior will change in \ +the Rust 2018 edition and the following files will be included as a {target_kind_human} target: + +{rem_targets_str} +This is likely to break cargo build or cargo test as these files may not be ready to be compiled \ +as a {target_kind_human} target today. You can future-proof yourself and disable this warning by \ +adding {autodiscover_flag_name} = false to your [package] section. You may also move the files to \ +a location where Cargo would not automatically infer them to be a target, such as in subfolders. + +For more information on this warning you can consult https://github.com/rust-lang/cargo/issues/5330\ + ", + section = target_kind, + target_kind_human = target_kind_human, + rem_targets_str = rem_targets_str, + autodiscover_flag_name = autodiscover_flag_name, + )); + }; + false + } + }, + }; + + if autodiscover { + targets.append(&mut rem_targets); + } + + targets + } } } diff --git a/src/doc/src/reference/manifest.md b/src/doc/src/reference/manifest.md index 334230d42..0e62242a0 100644 --- a/src/doc/src/reference/manifest.md +++ b/src/doc/src/reference/manifest.md @@ -728,6 +728,10 @@ proc-macro = false harness = true ``` +The `[package]` also includes the optional `autobins`, `autoexamples`, +`autotests`, and `autobenches` keys to explicitly opt-in or opt-out of +auto-discovering specific target kinds. + #### The `required-features` field (optional) The `required-features` field specifies which features the target needs in order diff --git a/tests/testsuite/bench.rs b/tests/testsuite/bench.rs index 7347795f0..770faf9d3 100644 --- a/tests/testsuite/bench.rs +++ b/tests/testsuite/bench.rs @@ -1,7 +1,7 @@ use std::str; use cargo::util::process; -use cargotest::is_nightly; +use cargotest::{is_nightly, ChannelChanger}; use cargotest::support::paths::CargoPathExt; use cargotest::support::{basic_bin_manifest, basic_lib_manifest, execs, project}; use hamcrest::{assert_that, existing_file}; @@ -697,6 +697,83 @@ fn external_bench_implicit() { ); } +#[test] +fn bench_autodiscover_2015() { + if !is_nightly() { + return; + } + + let p = project("foo") + .file( + "Cargo.toml", + r#" + cargo-features = ["edition"] + + [project] + name = "foo" + version = "0.0.1" + authors = [] + rust = "2015" + + [[bench]] + name = "bench_magic" + required-features = ["magic"] + "#, + ) + .file("src/lib.rs", "") + .file( + "benches/bench_basic.rs", + r#" + #![feature(test)] + #[allow(unused_extern_crates)] + extern crate foo; + extern crate test; + + #[bench] + fn bench_basic(_b: &mut test::Bencher) {} + "#, + ) + .file( + "benches/bench_magic.rs", + r#" + #![feature(test)] + #[allow(unused_extern_crates)] + extern crate foo; + extern crate test; + + #[bench] + fn bench_magic(_b: &mut test::Bencher) {} + "#, + ) + .build(); + + assert_that( + p.cargo("bench") + .arg("bench_basic") + .masquerade_as_nightly_cargo(), + execs().with_stderr(&format!( + "warning: \ +An explicit [[bench]] section is specified in Cargo.toml which currently disables Cargo from \ +automatically inferring other benchmark targets. This inference behavior will change in \ +the Rust 2018 edition and the following files will be included as a benchmark target: + +* \"[..]foo[/]benches[/]bench_basic.rs\" + +This is likely to break cargo build or cargo test as these files may not be ready to be compiled \ +as a benchmark target today. You can future-proof yourself and disable this warning by \ +adding autobenches = false to your [package] section. You may also move the files to \ +a location where Cargo would not automatically infer them to be a target, such as in subfolders. + +For more information on this warning you can consult https://github.com/rust-lang/cargo/issues/5330 +[COMPILING] foo v0.0.1 ({}) +[FINISHED] release [optimized] target(s) in [..] +[RUNNING] target[/]release[/]deps[/]foo-[..][EXE] +", + p.url() + )), + ); +} + #[test] fn dont_run_examples() { if !is_nightly() { diff --git a/tests/testsuite/build.rs b/tests/testsuite/build.rs index 80c1404d9..2cb04e1bb 100644 --- a/tests/testsuite/build.rs +++ b/tests/testsuite/build.rs @@ -2264,7 +2264,6 @@ fn non_existing_example() { "#, ) .file("src/lib.rs", "") - .file("examples/ehlo.rs", "") .build(); assert_that( diff --git a/tests/testsuite/run.rs b/tests/testsuite/run.rs index 742f37d04..5fdac77c7 100644 --- a/tests/testsuite/run.rs +++ b/tests/testsuite/run.rs @@ -1,5 +1,6 @@ use cargo::util::paths::dylib_path_envvar; -use cargotest::support::{execs, project, path2url}; +use cargotest::{self, ChannelChanger}; +use cargotest::support::{execs, project, Project, path2url}; use hamcrest::{assert_that, existing_file}; #[test] @@ -386,6 +387,151 @@ fn run_example() { ); } +fn autodiscover_examples_project(rust_edition: &str, autoexamples: Option) -> Project { + let autoexamples = match autoexamples { + None => "".to_string(), + Some(bool) => format!("autoexamples = {}", bool), + }; + project("foo") + .file( + "Cargo.toml", + &format!( + r#" + cargo-features = ["edition"] + + [project] + name = "foo" + version = "0.0.1" + authors = [] + rust = "{rust_edition}" + {autoexamples} + + [features] + magic = [] + + [[example]] + name = "do_magic" + required-features = ["magic"] + "#, + rust_edition = rust_edition, + autoexamples = autoexamples + ), + ) + .file( + "examples/a.rs", + r#" + fn main() { println!("example"); } + "#, + ) + .file( + "examples/do_magic.rs", + r#" + fn main() { println!("magic example"); } + "#, + ) + .build() +} + +#[test] +fn run_example_autodiscover_2015() { + if !cargotest::is_nightly() { + return; + } + + let p = autodiscover_examples_project("2015", None); + assert_that( + p.cargo("run") + .arg("--example") + .arg("a") + .masquerade_as_nightly_cargo(), + execs().with_status(101).with_stderr( + "warning: \ +An explicit [[example]] section is specified in Cargo.toml which currently disables Cargo from \ +automatically inferring other example targets. This inference behavior will change in \ +the Rust 2018 edition and the following files will be included as a example target: + +* \"[..]foo[/]examples[/]a.rs\" + +This is likely to break cargo build or cargo test as these files may not be ready to be compiled \ +as a example target today. You can future-proof yourself and disable this warning by \ +adding autoexamples = false to your [package] section. You may also move the files to \ +a location where Cargo would not automatically infer them to be a target, such as in subfolders. + +For more information on this warning you can consult https://github.com/rust-lang/cargo/issues/5330 +error: no example target named `a` +", + ), + ); +} + +#[test] +fn run_example_autodiscover_2015_with_autoexamples_enabled() { + if !cargotest::is_nightly() { + return; + } + + let p = autodiscover_examples_project("2015", Some(true)); + assert_that( + p.cargo("run") + .arg("--example") + .arg("a") + .masquerade_as_nightly_cargo(), + execs() + .with_status(0) + .with_stderr(&format!( + "\ +[COMPILING] foo v0.0.1 ({dir}) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target[/]debug[/]examples[/]a[EXE]`", + dir = path2url(p.root()) + )) + .with_stdout("example"), + ); +} + +#[test] +fn run_example_autodiscover_2015_with_autoexamples_disabled() { + if !cargotest::is_nightly() { + return; + } + + let p = autodiscover_examples_project("2015", Some(false)); + assert_that( + p.cargo("run") + .arg("--example") + .arg("a") + .masquerade_as_nightly_cargo(), + execs() + .with_status(101) + .with_stderr("error: no example target named `a`\n"), + ); +} + +#[test] +fn run_example_autodiscover_2018() { + if !cargotest::is_nightly() { + return; + } + + let p = autodiscover_examples_project("2018", None); + assert_that( + p.cargo("run") + .arg("--example") + .arg("a") + .masquerade_as_nightly_cargo(), + execs() + .with_status(0) + .with_stderr(&format!( + "\ +[COMPILING] foo v0.0.1 ({dir}) +[FINISHED] dev [unoptimized + debuginfo] target(s) in [..] +[RUNNING] `target[/]debug[/]examples[/]a[EXE]`", + dir = path2url(p.root()) + )) + .with_stdout("example"), + ); +} + #[test] fn run_bins() { let p = project("foo")