Skip to content

Commit 8cf5455

Browse files
scootermonNobodyXu
andauthored
Add cargo_warnings config to control the use of the cargo warning instruction (#917)
* fix some instances of broken cargo:warning instruction * allow disabling cargo warnings for compilation * comply with MSRV * allow dead_code to make ci pass * make print thread optional * simplify warnings using macros * add a test case * this line in the docstring is no longer true * apply review suggestions * write warnings to buffered stdout * add proper output tests * reduce import diff and always print stdout in tests * correct println * add an output abstraction * hopefully make the tests work on win32 * msvc-compatible warnings * turns out we can't capture warnings for msvc... * fix unconditional metadata output * skip warnings_on test for msvc * Update src/lib.rs Co-authored-by: Jiahao XU <[email protected]> * Update src/lib.rs Co-authored-by: Jiahao XU <[email protected]> * Update src/lib.rs Co-authored-by: Jiahao XU <[email protected]> * Update src/lib.rs Co-authored-by: Jiahao XU <[email protected]> --------- Co-authored-by: Jiahao XU <[email protected]>
1 parent 385b43f commit 8cf5455

File tree

5 files changed

+306
-82
lines changed

5 files changed

+306
-82
lines changed

cc-test/build.rs

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,35 @@
11
use std::env;
22
use std::fs;
3-
use std::path::PathBuf;
3+
use std::path::{Path, PathBuf};
4+
use std::process::Command;
45

56
fn main() {
7+
// if we are being executed from a `fork_run_action` call (i.e. this is a
8+
// "fork"), perform the requested action and then return.
9+
if run_action_if_forked() {
10+
return;
11+
}
12+
613
let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
714
fs::remove_dir_all(&out).unwrap();
815
fs::create_dir(&out).unwrap();
916

17+
// The following are builds where we want to capture the output (i.e. stdout and
18+
// stderr). We do that by re-running _this_ executable and passing in the
19+
// action as the first argument.
20+
run_forked_capture_output(&out, "metadata-on");
21+
run_forked_capture_output(&out, "metadata-off");
22+
23+
run_forked_capture_output(&out, "warnings-off");
24+
if cc::Build::new().get_compiler().is_like_msvc() {
25+
// MSVC doesn't output warnings to stderr, so we can't capture them.
26+
// the test will use this env var to know whether to run the test.
27+
println!("cargo:rustc-env=TEST_WARNINGS_ON=0");
28+
} else {
29+
println!("cargo:rustc-env=TEST_WARNINGS_ON=1");
30+
run_forked_capture_output(&out, "warnings-on");
31+
}
32+
1033
cc::Build::new()
1134
.file("src/foo.c")
1235
.flag_if_supported("-Wall")
@@ -104,3 +127,48 @@ fn main() {
104127
let out = String::from_utf8(out).unwrap();
105128
assert!(out.contains("hello world"));
106129
}
130+
131+
#[track_caller]
132+
fn run_forked_capture_output(out: &Path, action: &str) {
133+
let program = env::current_exe().unwrap();
134+
let output = Command::new(&program).arg(action).output().unwrap();
135+
assert!(output.status.success(), "output: {:#?}", output);
136+
// we've captured the output and now we write it to a dedicated directory in the
137+
// build output so our tests can access the output.
138+
let action_dir = out.join(action);
139+
fs::create_dir_all(&action_dir).unwrap();
140+
fs::write(action_dir.join("stdout"), output.stdout).unwrap();
141+
fs::write(action_dir.join("stderr"), output.stderr).unwrap();
142+
}
143+
144+
fn run_action_if_forked() -> bool {
145+
let mut args = env::args();
146+
let _program = args.next().unwrap();
147+
let action = args.next();
148+
match action.as_deref() {
149+
Some("metadata-on") => build_cargo_metadata(true),
150+
Some("metadata-off") => build_cargo_metadata(false),
151+
Some("warnings-on") => build_cargo_warnings(true),
152+
Some("warnings-off") => build_cargo_warnings(false),
153+
// No action requested, we're being called from cargo. Proceed with build.
154+
_ => return false,
155+
}
156+
true
157+
}
158+
159+
fn build_cargo_warnings(warnings: bool) {
160+
cc::Build::new()
161+
.cargo_metadata(false)
162+
.cargo_warnings(warnings)
163+
.file("src/compile_error.c")
164+
.try_compile("compile_error")
165+
.unwrap_err();
166+
}
167+
168+
fn build_cargo_metadata(metadata: bool) {
169+
cc::Build::new()
170+
.cargo_metadata(metadata)
171+
.file("src/dummy.c")
172+
.try_compile("dummy")
173+
.unwrap();
174+
}

cc-test/src/compile_error.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
#error "if you see this, cargo_warnings(false) didn't do its job"

cc-test/src/dummy.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
/* just an empty file */
2+
3+
void dummy(void) {}

cc-test/tests/output.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
use std::fs;
2+
use std::path::PathBuf;
3+
4+
#[test]
5+
fn cargo_warnings_on() {
6+
if env!("TEST_WARNINGS_ON") == "0" {
7+
// in some cases we don't catch compiler warnings and turn them into cargo
8+
// instructions.
9+
return;
10+
}
11+
let (stdout, stderr) = load_output("warnings-on");
12+
assert!(stderr.is_empty());
13+
assert!(stdout.contains("cargo:warning="));
14+
}
15+
16+
#[test]
17+
fn cargo_warnings_off() {
18+
let (stdout, stderr) = load_output("warnings-off");
19+
assert!(stderr.is_empty());
20+
assert!(!stdout.contains("cargo:warning="));
21+
}
22+
23+
#[test]
24+
fn cargo_metadata_on() {
25+
let (stdout, stderr) = load_output("metadata-on");
26+
assert!(stderr.is_empty());
27+
assert!(stdout.contains("cargo:rustc-link-lib="));
28+
assert!(stdout.contains("cargo:rustc-link-search="));
29+
}
30+
31+
#[test]
32+
fn cargo_metadata_off() {
33+
let (stdout, stderr) = load_output("metadata-off");
34+
assert!(stderr.is_empty());
35+
36+
// most of the instructions aren't currently used
37+
const INSTRUCTIONS: &[&str] = &[
38+
"cargo:rerun-if-changed=",
39+
"cargo:rerun-if-env-changed=",
40+
"cargo:rustc-cdylib-link-arg=",
41+
"cargo:rustc-cfg=",
42+
"cargo:rustc-env=",
43+
"cargo:rustc-flags=",
44+
"cargo:rustc-link-arg-benches=",
45+
"cargo:rustc-link-arg-bin=",
46+
"cargo:rustc-link-arg-bins=",
47+
"cargo:rustc-link-arg-examples=",
48+
"cargo:rustc-link-arg-tests=",
49+
"cargo:rustc-link-arg=",
50+
"cargo:rustc-link-lib=",
51+
"cargo:rustc-link-search=",
52+
];
53+
for instr in INSTRUCTIONS {
54+
assert!(!stdout.contains(instr), "instruction present: {}", instr);
55+
}
56+
}
57+
58+
#[track_caller]
59+
fn load_output(action: &str) -> (String, String) {
60+
// these files are written by the `run_forked_capture_output` function in the
61+
// build script.
62+
let action_dir = PathBuf::from(env!("OUT_DIR")).join(action);
63+
let stdout = fs::read_to_string(action_dir.join("stdout")).unwrap();
64+
let stderr = fs::read_to_string(action_dir.join("stderr")).unwrap();
65+
println!("compile stdout: {:?}", stdout);
66+
println!("compile stderr: {:?}", stderr);
67+
(stdout, stderr)
68+
}

0 commit comments

Comments
 (0)