diff --git a/bin/cargo-bolero/src/build_clusterfuzz.rs b/bin/cargo-bolero/src/build_clusterfuzz.rs index d816d8f6..1361e15e 100644 --- a/bin/cargo-bolero/src/build_clusterfuzz.rs +++ b/bin/cargo-bolero/src/build_clusterfuzz.rs @@ -1,4 +1,4 @@ -use crate::{list::List, project::Project}; +use crate::list::List; use anyhow::{Context, Result}; use std::{ collections::{hash_map::DefaultHasher, HashMap}, @@ -12,7 +12,7 @@ use structopt::StructOpt; #[derive(Debug, StructOpt)] pub struct BuildClusterfuzz { #[structopt(flatten)] - project: Project, + list: List, } impl BuildClusterfuzz { @@ -34,9 +34,7 @@ impl BuildClusterfuzz { ); // Figure out the list of fuzz targets, grouped by which test executable they use - let targets = List::new(self.project.clone()) - .list() - .context("listing fuzz targets")?; + let targets = self.list.list().context("listing fuzz targets")?; let mut targets_per_exe = HashMap::new(); for t in targets { targets_per_exe @@ -54,7 +52,7 @@ impl BuildClusterfuzz { let dir = PathBuf::from(format!("{}-{:x}", list_bin, hash)); let fuzz_exe = - crate::libfuzzer::build(self.project.clone(), tests[0].test_name.clone()) + crate::libfuzzer::build(self.list.project().clone(), tests[0].test_name.clone()) .context("building to-be-fuzzed executable")?; // .cargo extension is not an ALLOWED_FUZZ_TARGET_EXTENSIONS for clusterfuzz, so it doesn’t get picked up as a fuzzer let fuzz_bin = format!("{}.cargo", fuzz_exe.file_name().unwrap().to_string_lossy()); diff --git a/bin/cargo-bolero/src/list.rs b/bin/cargo-bolero/src/list.rs index 1c06395b..86ae29e7 100644 --- a/bin/cargo-bolero/src/list.rs +++ b/bin/cargo-bolero/src/list.rs @@ -11,22 +11,16 @@ pub struct List { } impl List { - pub fn new(project: Project) -> Self { - Self { project } + pub fn project(&self) -> &Project { + &self.project } pub fn list(&self) -> Result> { - let mut build_command = self.cmd("test", &[], None)?; - build_command.arg("--no-run"); + let build_command = self.cmd("test", false, &["--no-run"], &[], None)?; exec(build_command)?; - let output = self - .cmd("test", &[], None)? - .arg("--no-fail-fast") - .arg("--") - .arg("--nocapture") - .env("CARGO_BOLERO_SELECT", "all") - .output()?; + let mut cmd = self.cmd("test", true, &["--no-fail-fast"], &[], None)?; + let output = cmd.env("CARGO_BOLERO_SELECT", "all").output()?; // ignore the status in case any tests failed TestTarget::all_from_stdout(&output.stdout) diff --git a/bin/cargo-bolero/src/project.rs b/bin/cargo-bolero/src/project.rs index 59e3ee69..81eff7d0 100644 --- a/bin/cargo-bolero/src/project.rs +++ b/bin/cargo-bolero/src/project.rs @@ -41,6 +41,10 @@ pub struct Project { #[structopt(short, long)] package: Option, + /// Whether to run tests for the whole workspace + #[structopt(long)] + workspace: bool, + /// Path to Cargo.toml #[structopt(long)] manifest_path: Option, @@ -60,6 +64,14 @@ pub struct Project { /// Fake using a nightly toolchain while using the default toolchain by using RUSTC_BOOTSTRAP #[structopt(long)] rustc_bootstrap: bool, + + /// Use cargo nextest instead of cargo test + #[structopt(long)] + use_nextest: bool, + + /// Set a specific nextest profile, eg. to set a one second timeout for listing fuzzers + #[structopt(long)] + nextest_profile: Option, } impl Project { @@ -86,11 +98,38 @@ impl Project { } } - pub fn cmd(&self, call: &str, flags: &[&str], fuzzer: Option<&str>) -> Result { + pub fn cmd( + &self, + call: &str, + no_capture: bool, + cargoflags: &[&str], + rustflags: &[&str], + fuzzer: Option<&str>, + ) -> Result { let mut cmd = self.cargo(); - cmd.arg(call).arg("--target").arg(&self.target); - cmd.arg("--profile").arg(&self.profile); + let use_nextest = self.use_nextest && call == "test"; + + if use_nextest { + cmd.arg("nextest").arg("run"); + } else { + cmd.arg(call); + } + + cmd.arg("--target").arg(&self.target); + + if use_nextest { + cmd.arg("--cargo-profile").arg(&self.profile); + if let Some(nextest_profile) = &self.nextest_profile { + cmd.arg("--profile").arg(nextest_profile); + } + } else { + anyhow::ensure!( + self.nextest_profile.is_none(), + "Requested nextest profile without using nextest" + ); + cmd.arg("--profile").arg(&self.profile); + } if self.no_default_features { cmd.arg("--no-default-features"); @@ -104,6 +143,10 @@ impl Project { cmd.arg("--features").arg(value); } + if self.workspace { + cmd.arg("--workspace"); + } + if let Some(value) = self.package.as_ref() { cmd.arg("--package").arg(value); } @@ -113,13 +156,13 @@ impl Project { } if let Some(fuzzer) = fuzzer { - let rustflags = self.rustflags("RUSTFLAGS", flags)?; + let final_rustflags = self.rustflags("RUSTFLAGS", rustflags)?; if let Some(value) = self.target_dir.as_ref() { cmd.arg("--target-dir").arg(value); } else { let mut hasher = DefaultHasher::new(); - rustflags.hash(&mut hasher); + final_rustflags.hash(&mut hasher); cmd.arg("--target-dir") .arg(format!("target/fuzz/build_{:x}", hasher.finish())); } @@ -128,11 +171,23 @@ impl Project { cmd.arg("-Zbuild-std"); } - cmd.env("RUSTFLAGS", rustflags) - .env("RUSTDOCFLAGS", self.rustflags("RUSTDOCFLAGS", flags)?) + cmd.env("RUSTFLAGS", final_rustflags) + .env("RUSTDOCFLAGS", self.rustflags("RUSTDOCFLAGS", rustflags)?) .env("BOLERO_FUZZER", fuzzer); } + for f in cargoflags { + cmd.arg(f); + } + + if no_capture { + if use_nextest { + cmd.arg("--no-capture"); + } else { + cmd.arg("--").arg("--nocapture"); + } + } + Ok(cmd) } diff --git a/bin/cargo-bolero/src/selection.rs b/bin/cargo-bolero/src/selection.rs index 87e682a8..03ac899a 100644 --- a/bin/cargo-bolero/src/selection.rs +++ b/bin/cargo-bolero/src/selection.rs @@ -18,7 +18,7 @@ impl Selection { } pub fn test_target(&self, flags: &[&str], fuzzer: &str) -> Result { - let mut build_command = self.cmd("test", flags, Some(fuzzer))?; + let mut build_command = self.cmd("test", false, &[], flags, Some(fuzzer))?; build_command .arg(&self.test) .arg("--no-run") @@ -26,7 +26,7 @@ impl Selection { .arg("--exact"); exec(build_command)?; - let mut output_command = self.cmd("test", flags, Some(fuzzer))?; + let mut output_command = self.cmd("test", false, &[], flags, Some(fuzzer))?; output_command .arg(&self.test) .arg("--")