From 3cf913ef93fa690e6eb2d2612e60d71654fcc7e1 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 1 Apr 2019 16:03:35 -0700 Subject: [PATCH] Include proc-macros in `build-override`. --- src/cargo/core/profiles.rs | 29 +++--- src/doc/src/reference/unstable.md | 5 +- tests/testsuite/profile_overrides.rs | 68 ++++++++++++++ tests/testsuite/support/mod.rs | 130 ++++++++++++++++++++++----- 4 files changed, 194 insertions(+), 38 deletions(-) diff --git a/src/cargo/core/profiles.rs b/src/cargo/core/profiles.rs index be97a54ef..8cf142981 100644 --- a/src/cargo/core/profiles.rs +++ b/src/cargo/core/profiles.rs @@ -328,7 +328,7 @@ fn merge_toml( toml: &TomlProfile, ) { merge_profile(profile, toml); - if unit_for.is_custom_build() { + if unit_for.is_build() { if let Some(ref build_override) = toml.build_override { merge_profile(profile, build_override); } @@ -590,9 +590,10 @@ impl fmt::Display for PanicStrategy { /// to ensure the target's dependencies have the correct settings. #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)] pub struct UnitFor { - /// A target for `build.rs` or any of its dependencies. This enables - /// `build-override` profiles for these targets. - custom_build: bool, + /// A target for `build.rs` or any of its dependencies, or a proc-macro or + /// any of its dependencies. This enables `build-override` profiles for + /// these targets. + build: bool, /// This is true if it is *allowed* to set the `panic=abort` flag. Currently /// this is false for test/bench targets and all their dependencies, and /// "for_host" units such as proc macro and custom build scripts and their @@ -605,7 +606,7 @@ impl UnitFor { /// proc macro/plugin, or test/bench). pub fn new_normal() -> UnitFor { UnitFor { - custom_build: false, + build: false, panic_abort_ok: true, } } @@ -613,7 +614,7 @@ impl UnitFor { /// A unit for a custom build script or its dependencies. pub fn new_build() -> UnitFor { UnitFor { - custom_build: true, + build: true, panic_abort_ok: false, } } @@ -621,7 +622,7 @@ impl UnitFor { /// A unit for a proc macro or compiler plugin or their dependencies. pub fn new_compiler() -> UnitFor { UnitFor { - custom_build: false, + build: false, panic_abort_ok: false, } } @@ -629,7 +630,7 @@ impl UnitFor { /// A unit for a test/bench target or their dependencies. pub fn new_test() -> UnitFor { UnitFor { - custom_build: false, + build: false, panic_abort_ok: false, } } @@ -640,15 +641,15 @@ impl UnitFor { /// that all its dependencies also have `panic_abort_ok=false`. pub fn with_for_host(self, for_host: bool) -> UnitFor { UnitFor { - custom_build: self.custom_build, + build: self.build || for_host, panic_abort_ok: self.panic_abort_ok && !for_host, } } /// Returns `true` if this unit is for a custom build script or one of its /// dependencies. - pub fn is_custom_build(self) -> bool { - self.custom_build + pub fn is_build(self) -> bool { + self.build } /// Returns `true` if this unit is allowed to set the `panic` compiler flag. @@ -660,15 +661,15 @@ impl UnitFor { pub fn all_values() -> &'static [UnitFor] { static ALL: [UnitFor; 3] = [ UnitFor { - custom_build: false, + build: false, panic_abort_ok: true, }, UnitFor { - custom_build: true, + build: true, panic_abort_ok: false, }, UnitFor { - custom_build: false, + build: false, panic_abort_ok: false, }, ]; diff --git a/src/doc/src/reference/unstable.md b/src/doc/src/reference/unstable.md index e1546b832..8322ab8aa 100644 --- a/src/doc/src/reference/unstable.md +++ b/src/doc/src/reference/unstable.md @@ -118,8 +118,9 @@ opt-level = 3 [profile.dev.overrides."*"] opt-level = 2 -# Build scripts and their dependencies will be compiled with -Copt-level=3 -# By default, build scripts use the same rules as the rest of the profile +# Build scripts or proc-macros and their dependencies will be compiled with +# `-Copt-level=3`. By default, they use the same rules as the rest of the +# profile. [profile.dev.build-override] opt-level = 3 ``` diff --git a/tests/testsuite/profile_overrides.rs b/tests/testsuite/profile_overrides.rs index 7a2ff9451..09bfd7dba 100644 --- a/tests/testsuite/profile_overrides.rs +++ b/tests/testsuite/profile_overrides.rs @@ -1,3 +1,4 @@ +use crate::support::registry::Package; use crate::support::{basic_lib_manifest, basic_manifest, project}; #[test] @@ -439,3 +440,70 @@ fn profile_override_spec() { .with_stderr_contains("[RUNNING] `rustc [..]dep2/src/lib.rs [..] -C codegen-units=2 [..]") .run(); } + +#[test] +fn override_proc_macro() { + Package::new("shared", "1.0.0").publish(); + let p = project() + .file( + "Cargo.toml", + r#" + cargo-features = ["profile-overrides"] + [package] + name = "foo" + version = "0.1.0" + edition = "2018" + + [dependencies] + shared = "1.0" + pm = {path = "pm"} + + [profile.dev.build-override] + codegen-units = 4 + "#, + ) + .file("src/lib.rs", r#"pm::eat!{}"#) + .file( + "pm/Cargo.toml", + r#" + [package] + name = "pm" + version = "0.1.0" + + [lib] + proc-macro = true + + [dependencies] + shared = "1.0" + "#, + ) + .file( + "pm/src/lib.rs", + r#" + extern crate proc_macro; + use proc_macro::TokenStream; + + #[proc_macro] + pub fn eat(_item: TokenStream) -> TokenStream { + "".parse().unwrap() + } + "#, + ) + .build(); + + p.cargo("build -v") + .masquerade_as_nightly_cargo() + // Shared built for the proc-macro. + .with_stderr_contains("[RUNNING] `rustc [..]--crate-name shared [..]-C codegen-units=4[..]") + // Shared built for the library. + .with_stderr_line_without( + &["[RUNNING] `rustc --crate-name shared"], + &["-C codegen-units"], + ) + .with_stderr_contains("[RUNNING] `rustc [..]--crate-name pm [..]-C codegen-units=4[..]") + .with_stderr_line_without( + &["[RUNNING] `rustc [..]--crate-name foo"], + &["-C codegen-units"], + ) + .run(); +} diff --git a/tests/testsuite/support/mod.rs b/tests/testsuite/support/mod.rs index 305d66420..9e9beaac4 100644 --- a/tests/testsuite/support/mod.rs +++ b/tests/testsuite/support/mod.rs @@ -579,6 +579,7 @@ pub struct Execs { expect_stderr_not_contains: Vec, expect_stderr_unordered: Vec, expect_neither_contains: Vec, + expect_stderr_with_without: Vec<(Vec, Vec)>, expect_json: Option>, expect_json_contains_unordered: Vec, stream_output: bool, @@ -696,6 +697,37 @@ impl Execs { self } + /// Verify that a particular line appears in stderr with and without the + /// given substrings. Exactly one line must match. + /// + /// The substrings are matched as `contains`. Example: + /// + /// ```no_run + /// execs.with_stderr_line_without( + /// &[ + /// "[RUNNING] `rustc --crate-name build_script_build", + /// "-C opt-level=3", + /// ], + /// &["-C debuginfo", "-C incremental"], + /// ) + /// ``` + /// + /// This will check that a build line includes `-C opt-level=3` but does + /// not contain `-C debuginfo` or `-C incremental`. + /// + /// Be careful writing the `without` fragments, see note in + /// `with_stderr_does_not_contain`. + pub fn with_stderr_line_without( + &mut self, + with: &[S], + without: &[S], + ) -> &mut Self { + let with = with.iter().map(|s| s.to_string()).collect(); + let without = without.iter().map(|s| s.to_string()).collect(); + self.expect_stderr_with_without.push((with, without)); + self + } + /// Verifies the JSON output matches the given JSON. /// Typically used when testing cargo commands that emit JSON. /// Each separate JSON object should be separated by a blank line. @@ -830,6 +862,7 @@ impl Execs { && self.expect_stderr_not_contains.is_empty() && self.expect_stderr_unordered.is_empty() && self.expect_neither_contains.is_empty() + && self.expect_stderr_with_without.is_empty() && self.expect_json.is_none() && self.expect_json_contains_unordered.is_empty() { @@ -1004,6 +1037,10 @@ impl Execs { } } + for (with, without) in self.expect_stderr_with_without.iter() { + self.match_with_without(&actual.stderr, with, without)?; + } + if let Some(ref objects) = self.expect_json { let stdout = str::from_utf8(&actual.stdout) .map_err(|_| "stdout was not utf8 encoded".to_owned())?; @@ -1063,6 +1100,32 @@ impl Execs { ) } + fn normalize_actual(&self, description: &str, actual: &[u8]) -> Result { + let actual = match str::from_utf8(actual) { + Err(..) => return Err(format!("{} was not utf8 encoded", description)), + Ok(actual) => actual, + }; + // Let's not deal with \r\n vs \n on windows... + let actual = actual.replace("\r", ""); + let actual = actual.replace("\t", ""); + Ok(actual) + } + + fn replace_expected(&self, expected: &str) -> String { + // Do the template replacements on the expected string. + let replaced = match self.process_builder { + None => expected.to_string(), + Some(ref p) => match p.get_cwd() { + None => expected.to_string(), + Some(cwd) => expected.replace("[CWD]", &cwd.display().to_string()), + }, + }; + + // On Windows, we need to use a wildcard for the drive, + // because we don't actually know what it will be. + replaced.replace("[ROOT]", if cfg!(windows) { r#"[..]:\"# } else { "/" }) + } + fn match_std( &self, expected: Option<&String>, @@ -1072,30 +1135,11 @@ impl Execs { kind: MatchKind, ) -> MatchResult { let out = match expected { - Some(out) => { - // Do the template replacements on the expected string. - let replaced = match self.process_builder { - None => out.to_string(), - Some(ref p) => match p.get_cwd() { - None => out.to_string(), - Some(cwd) => out.replace("[CWD]", &cwd.display().to_string()), - }, - }; - - // On Windows, we need to use a wildcard for the drive, - // because we don't actually know what it will be. - replaced.replace("[ROOT]", if cfg!(windows) { r#"[..]:\"# } else { "/" }) - } + Some(out) => self.replace_expected(out), None => return Ok(()), }; - let actual = match str::from_utf8(actual) { - Err(..) => return Err(format!("{} was not utf8 encoded", description)), - Ok(actual) => actual, - }; - // Let's not deal with `\r\n` vs `\n` on Windows. - let actual = actual.replace("\r", ""); - let actual = actual.replace("\t", ""); + let actual = self.normalize_actual(description, actual)?; match kind { MatchKind::Exact => { @@ -1219,6 +1263,47 @@ impl Execs { } } + fn match_with_without( + &self, + actual: &[u8], + with: &[String], + without: &[String], + ) -> MatchResult { + let actual = self.normalize_actual("stderr", actual)?; + let contains = |s, line| { + let mut s = self.replace_expected(s); + s.insert_str(0, "[..]"); + s.push_str("[..]"); + lines_match(&s, line) + }; + let matches: Vec<&str> = actual + .lines() + .filter(|line| with.iter().all(|with| contains(with, line))) + .filter(|line| !without.iter().any(|without| contains(without, line))) + .collect(); + match matches.len() { + 0 => Err(format!( + "Could not find expected line in output.\n\ + With contents: {:?}\n\ + Without contents: {:?}\n\ + Actual stderr:\n\ + {}\n", + with, without, actual + )), + 1 => Ok(()), + _ => Err(format!( + "Found multiple matching lines, but only expected one.\n\ + With contents: {:?}\n\ + Without contents: {:?}\n\ + Matching lines:\n\ + {}\n", + with, + without, + matches.join("\n") + )), + } + } + fn match_json(&self, expected: &Value, line: &str) -> MatchResult { let actual = match line.parse() { Err(e) => return Err(format!("invalid json, {}:\n`{}`", e, line)), @@ -1436,6 +1521,7 @@ pub fn execs() -> Execs { expect_stderr_not_contains: Vec::new(), expect_stderr_unordered: Vec::new(), expect_neither_contains: Vec::new(), + expect_stderr_with_without: Vec::new(), expect_json: None, expect_json_contains_unordered: Vec::new(), stream_output: false, @@ -1529,7 +1615,7 @@ fn substitute_macros(input: &str) -> String { ("[UNPACKING]", " Unpacking"), ("[SUMMARY]", " Summary"), ("[FIXING]", " Fixing"), - ("[EXE]", env::consts::EXE_SUFFIX), + ("[EXE]", env::consts::EXE_SUFFIX), ]; let mut result = input.to_owned(); for &(pat, subst) in ¯os {