Skip to content

Commit

Permalink
Include proc-macros in build-override.
Browse files Browse the repository at this point in the history
  • Loading branch information
ehuss committed Apr 1, 2019
1 parent 025b01e commit 3cf913e
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 38 deletions.
29 changes: 15 additions & 14 deletions src/cargo/core/profiles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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
Expand All @@ -605,31 +606,31 @@ impl UnitFor {
/// proc macro/plugin, or test/bench).
pub fn new_normal() -> UnitFor {
UnitFor {
custom_build: false,
build: false,
panic_abort_ok: true,
}
}

/// 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,
}
}

/// 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,
}
}

/// 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,
}
}
Expand All @@ -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.
Expand All @@ -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,
},
];
Expand Down
5 changes: 3 additions & 2 deletions src/doc/src/reference/unstable.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```
Expand Down
68 changes: 68 additions & 0 deletions tests/testsuite/profile_overrides.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::support::registry::Package;
use crate::support::{basic_lib_manifest, basic_manifest, project};

#[test]
Expand Down Expand Up @@ -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();
}
130 changes: 108 additions & 22 deletions tests/testsuite/support/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,6 +579,7 @@ pub struct Execs {
expect_stderr_not_contains: Vec<String>,
expect_stderr_unordered: Vec<String>,
expect_neither_contains: Vec<String>,
expect_stderr_with_without: Vec<(Vec<String>, Vec<String>)>,
expect_json: Option<Vec<Value>>,
expect_json_contains_unordered: Vec<Value>,
stream_output: bool,
Expand Down Expand Up @@ -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<S: ToString>(
&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.
Expand Down Expand Up @@ -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()
{
Expand Down Expand Up @@ -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())?;
Expand Down Expand Up @@ -1063,6 +1100,32 @@ impl Execs {
)
}

fn normalize_actual(&self, description: &str, actual: &[u8]) -> Result<String, String> {
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", "<tab>");
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>,
Expand All @@ -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", "<tab>");
let actual = self.normalize_actual(description, actual)?;

match kind {
MatchKind::Exact => {
Expand Down Expand Up @@ -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)),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 &macros {
Expand Down

0 comments on commit 3cf913e

Please sign in to comment.