Skip to content

Commit

Permalink
Add cargo_warnings config to control the use of the cargo warning i…
Browse files Browse the repository at this point in the history
…nstruction (#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 <Jiahao_XU@outlook.com>

* Update src/lib.rs

Co-authored-by: Jiahao XU <Jiahao_XU@outlook.com>

* Update src/lib.rs

Co-authored-by: Jiahao XU <Jiahao_XU@outlook.com>

* Update src/lib.rs

Co-authored-by: Jiahao XU <Jiahao_XU@outlook.com>

---------

Co-authored-by: Jiahao XU <Jiahao_XU@outlook.com>
  • Loading branch information
scootermon and NobodyXu committed Jan 24, 2024
1 parent 385b43f commit 8cf5455
Show file tree
Hide file tree
Showing 5 changed files with 306 additions and 82 deletions.
70 changes: 69 additions & 1 deletion cc-test/build.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,35 @@
use std::env;
use std::fs;
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
// if we are being executed from a `fork_run_action` call (i.e. this is a
// "fork"), perform the requested action and then return.
if run_action_if_forked() {
return;
}

let out = PathBuf::from(env::var_os("OUT_DIR").unwrap());
fs::remove_dir_all(&out).unwrap();
fs::create_dir(&out).unwrap();

// The following are builds where we want to capture the output (i.e. stdout and
// stderr). We do that by re-running _this_ executable and passing in the
// action as the first argument.
run_forked_capture_output(&out, "metadata-on");
run_forked_capture_output(&out, "metadata-off");

run_forked_capture_output(&out, "warnings-off");
if cc::Build::new().get_compiler().is_like_msvc() {
// MSVC doesn't output warnings to stderr, so we can't capture them.
// the test will use this env var to know whether to run the test.
println!("cargo:rustc-env=TEST_WARNINGS_ON=0");
} else {
println!("cargo:rustc-env=TEST_WARNINGS_ON=1");
run_forked_capture_output(&out, "warnings-on");
}

cc::Build::new()
.file("src/foo.c")
.flag_if_supported("-Wall")
Expand Down Expand Up @@ -104,3 +127,48 @@ fn main() {
let out = String::from_utf8(out).unwrap();
assert!(out.contains("hello world"));
}

#[track_caller]
fn run_forked_capture_output(out: &Path, action: &str) {
let program = env::current_exe().unwrap();
let output = Command::new(&program).arg(action).output().unwrap();
assert!(output.status.success(), "output: {:#?}", output);
// we've captured the output and now we write it to a dedicated directory in the
// build output so our tests can access the output.
let action_dir = out.join(action);
fs::create_dir_all(&action_dir).unwrap();
fs::write(action_dir.join("stdout"), output.stdout).unwrap();
fs::write(action_dir.join("stderr"), output.stderr).unwrap();
}

fn run_action_if_forked() -> bool {
let mut args = env::args();
let _program = args.next().unwrap();
let action = args.next();
match action.as_deref() {
Some("metadata-on") => build_cargo_metadata(true),
Some("metadata-off") => build_cargo_metadata(false),
Some("warnings-on") => build_cargo_warnings(true),
Some("warnings-off") => build_cargo_warnings(false),
// No action requested, we're being called from cargo. Proceed with build.
_ => return false,
}
true
}

fn build_cargo_warnings(warnings: bool) {
cc::Build::new()
.cargo_metadata(false)
.cargo_warnings(warnings)
.file("src/compile_error.c")
.try_compile("compile_error")
.unwrap_err();
}

fn build_cargo_metadata(metadata: bool) {
cc::Build::new()
.cargo_metadata(metadata)
.file("src/dummy.c")
.try_compile("dummy")
.unwrap();
}
1 change: 1 addition & 0 deletions cc-test/src/compile_error.c
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
#error "if you see this, cargo_warnings(false) didn't do its job"
3 changes: 3 additions & 0 deletions cc-test/src/dummy.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/* just an empty file */

void dummy(void) {}
68 changes: 68 additions & 0 deletions cc-test/tests/output.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
use std::fs;
use std::path::PathBuf;

#[test]
fn cargo_warnings_on() {
if env!("TEST_WARNINGS_ON") == "0" {
// in some cases we don't catch compiler warnings and turn them into cargo
// instructions.
return;
}
let (stdout, stderr) = load_output("warnings-on");
assert!(stderr.is_empty());
assert!(stdout.contains("cargo:warning="));
}

#[test]
fn cargo_warnings_off() {
let (stdout, stderr) = load_output("warnings-off");
assert!(stderr.is_empty());
assert!(!stdout.contains("cargo:warning="));
}

#[test]
fn cargo_metadata_on() {
let (stdout, stderr) = load_output("metadata-on");
assert!(stderr.is_empty());
assert!(stdout.contains("cargo:rustc-link-lib="));
assert!(stdout.contains("cargo:rustc-link-search="));
}

#[test]
fn cargo_metadata_off() {
let (stdout, stderr) = load_output("metadata-off");
assert!(stderr.is_empty());

// most of the instructions aren't currently used
const INSTRUCTIONS: &[&str] = &[
"cargo:rerun-if-changed=",
"cargo:rerun-if-env-changed=",
"cargo:rustc-cdylib-link-arg=",
"cargo:rustc-cfg=",
"cargo:rustc-env=",
"cargo:rustc-flags=",
"cargo:rustc-link-arg-benches=",
"cargo:rustc-link-arg-bin=",
"cargo:rustc-link-arg-bins=",
"cargo:rustc-link-arg-examples=",
"cargo:rustc-link-arg-tests=",
"cargo:rustc-link-arg=",
"cargo:rustc-link-lib=",
"cargo:rustc-link-search=",
];
for instr in INSTRUCTIONS {
assert!(!stdout.contains(instr), "instruction present: {}", instr);
}
}

#[track_caller]
fn load_output(action: &str) -> (String, String) {
// these files are written by the `run_forked_capture_output` function in the
// build script.
let action_dir = PathBuf::from(env!("OUT_DIR")).join(action);
let stdout = fs::read_to_string(action_dir.join("stdout")).unwrap();
let stderr = fs::read_to_string(action_dir.join("stderr")).unwrap();
println!("compile stdout: {:?}", stdout);
println!("compile stderr: {:?}", stderr);
(stdout, stderr)
}

0 comments on commit 8cf5455

Please sign in to comment.