Skip to content

Commit

Permalink
fix: Don't generate empty elements in ENCODED_RUSTFLAGS (#399)
Browse files Browse the repository at this point in the history
  • Loading branch information
sourcefrog authored Aug 20, 2024
2 parents b213788 + 8d0b9ba commit d010293
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 36 deletions.
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- Fixed: Avoid generating empty string elements in `ENCODED_RUSTFLAGS` when `--cap-lints` is set. In some situations these could cause a compiler error complaining about the empty argument.

- New: `--iterate` option skips mutants that were previously caught or unviable.

- New: cargo-mutants starts a GNU jobserver, shared across all children, so that running multiple `--jobs` does not spawn an excessive number of compiler processes. The jobserver is on by default and can be turned off with `--jobserver false`.
Expand Down
93 changes: 60 additions & 33 deletions src/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

//! Run Cargo as a subprocess, including timeouts and propagating signals.

use std::iter::once;
use std::time::{Duration, Instant};

use itertools::Itertools;
Expand All @@ -27,14 +28,17 @@ pub fn run_cargo(
let _span = debug_span!("run", ?phase).entered();
let start = Instant::now();
let argv = cargo_argv(build_dir.path(), packages, phase, options);
let env = vec![
("CARGO_ENCODED_RUSTFLAGS".to_owned(), rustflags(options)),
let mut env = vec![
// The tests might use Insta <https://insta.rs>, and we don't want it to write
// updates to the source tree, and we *certainly* don't want it to write
// updates and then let the test pass.
("INSTA_UPDATE".to_owned(), "no".to_owned()),
("INSTA_FORCE_PASS".to_owned(), "0".to_owned()),
];
if let Some(encoded_rustflags) = encoded_rustflags(options) {
debug!(?encoded_rustflags);
env.push(("CARGO_ENCODED_RUSTFLAGS".to_owned(), encoded_rustflags));
}
let process_status = Process::run(
&argv,
&env,
Expand Down Expand Up @@ -151,34 +155,33 @@ fn cargo_argv(
///
/// See <https://doc.rust-lang.org/cargo/reference/environment-variables.html>
/// <https://doc.rust-lang.org/rustc/lints/levels.html#capping-lints>
fn rustflags(options: &Options) -> String {
let mut rustflags: Vec<String> = if let Some(rustflags) = env::var_os("CARGO_ENCODED_RUSTFLAGS")
{
rustflags
.to_str()
.expect("CARGO_ENCODED_RUSTFLAGS is not valid UTF-8")
.split('\x1f')
.map(|s| s.to_owned())
.collect()
} else if let Some(rustflags) = env::var_os("RUSTFLAGS") {
rustflags
.to_str()
.expect("RUSTFLAGS is not valid UTF-8")
.split(' ')
.map(|s| s.to_owned())
.collect()
fn encoded_rustflags(options: &Options) -> Option<String> {
let cap_lints_arg = "--cap-lints=warn";
let separator = "\x1f";
if !options.cap_lints {
None
} else if let Ok(encoded) = env::var("CARGO_ENCODED_RUSTFLAGS") {
if encoded.is_empty() {
Some(cap_lints_arg.to_owned())
} else {
Some(encoded + separator + cap_lints_arg)
}
} else if let Ok(rustflags) = env::var("RUSTFLAGS") {
if rustflags.is_empty() {
Some(cap_lints_arg.to_owned())
} else {
Some(
rustflags
.split(' ')
.filter(|s| !s.is_empty())
.chain(once("--cap-lints=warn"))
.collect::<Vec<&str>>()
.join(separator),
)
}
} else {
// TODO: We could read the config files, but working out the right target and config seems complicated
// given the information available here.
// TODO: All matching target.<triple>.rustflags and target.<cfg>.rustflags config entries joined together.
// TODO: build.rustflags config value.
Vec::new()
};
if options.cap_lints {
rustflags.push("--cap-lints=warn".to_owned());
Some(cap_lints_arg.to_owned())
}
// debug!("adjusted rustflags: {:?}", rustflags);
rustflags.join("\x1f")
}

#[cfg(test)]
Expand Down Expand Up @@ -354,14 +357,38 @@ mod test {

rusty_fork_test! {
#[test]
fn rustflags_with_no_environment_variables() {
fn rustflags_without_cap_lints_and_no_environment_variables() {
env::remove_var("RUSTFLAGS");
env::remove_var("CARGO_ENCODED_RUSTFLAGS");
assert_eq!(
encoded_rustflags(&Options {
..Default::default()
}),
None
);
}
#[test]
fn rustflags_with_cap_lints_and_no_environment_variables() {
env::remove_var("RUSTFLAGS");
env::remove_var("CARGO_ENCODED_RUSTFLAGS");
assert_eq!(
rustflags(&Options {
encoded_rustflags(&Options {
cap_lints: true,
..Default::default()
}),
Some("--cap-lints=warn".into())
);
}

// Don't generate an empty argument if the encoded rustflags is empty.
#[test]
fn rustflags_with_empty_encoded_rustflags() {
env::set_var("CARGO_ENCODED_RUSTFLAGS", "");
assert_eq!(
encoded_rustflags(&Options {
cap_lints: true,
..Default::default()
}).unwrap(),
"--cap-lints=warn"
);
}
Expand All @@ -374,17 +401,17 @@ mod test {
cap_lints: true,
..Default::default()
};
assert_eq!(rustflags(&options), "--something\x1f--else\x1f--cap-lints=warn");
assert_eq!(encoded_rustflags(&options).unwrap(), "--something\x1f--else\x1f--cap-lints=warn");
}

#[test]
fn rustflags_added_to_existing_rustflags() {
env::set_var("RUSTFLAGS", "-Dwarnings");
env::remove_var("CARGO_ENCODED_RUSTFLAGS");
assert_eq!(rustflags(&Options {
assert_eq!(encoded_rustflags(&Options {
cap_lints: true,
..Default::default()
}), "-Dwarnings\x1f--cap-lints=warn");
}).unwrap(), "-Dwarnings\x1f--cap-lints=warn");
}
}
}
10 changes: 7 additions & 3 deletions tests/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,7 +785,7 @@ fn mutants_causing_tests_to_hang_are_stopped_by_manual_timeout() {
#[test]
fn hang_avoided_by_build_timeout_with_cap_lints() {
let tmp_src_dir = copy_of_testdata("hang_when_mutated");
run()
let out = run()
.arg("mutants")
.args([
"--build-timeout-multiplier=4",
Expand All @@ -795,8 +795,12 @@ fn hang_avoided_by_build_timeout_with_cap_lints() {
.current_dir(tmp_src_dir.path())
.env_remove("RUST_BACKTRACE")
.timeout(OUTER_TIMEOUT)
.assert()
.code(3); // exit_code::TIMEOUT
.assert();
println!(
"debug log:\n===\n{}\n===",
read_to_string(tmp_src_dir.path().join("mutants.out/debug.log")).unwrap_or_default()
);
out.code(3); // exit_code::TIMEOUT
let timeout_txt = read_to_string(tmp_src_dir.path().join("mutants.out/timeout.txt"))
.expect("read timeout.txt");
assert!(
Expand Down

0 comments on commit d010293

Please sign in to comment.