diff --git a/src/main.rs b/src/main.rs index e9b7eb0..ca99e28 100644 --- a/src/main.rs +++ b/src/main.rs @@ -116,6 +116,10 @@ enum Command { /// Build fuzz targets Build(options::Build), + #[structopt(template(LONG_ABOUT_TEMPLATE))] + /// Type-check the fuzz targets + Check(options::Check), + /// Print the `std::fmt::Debug` output for an input Fmt(options::Fmt), @@ -146,6 +150,7 @@ impl RunCommand for Command { Command::Init(x) => x.run_command(), Command::Add(x) => x.run_command(), Command::Build(x) => x.run_command(), + Command::Check(x) => x.run_command(), Command::List(x) => x.run_command(), Command::Fmt(x) => x.run_command(), Command::Run(x) => x.run_command(), diff --git a/src/options.rs b/src/options.rs index a0ea806..9be0240 100644 --- a/src/options.rs +++ b/src/options.rs @@ -1,5 +1,6 @@ mod add; mod build; +mod check; mod cmin; mod coverage; mod fmt; @@ -9,8 +10,8 @@ mod run; mod tmin; pub use self::{ - add::Add, build::Build, cmin::Cmin, coverage::Coverage, fmt::Fmt, init::Init, list::List, - run::Run, tmin::Tmin, + add::Add, build::Build, check::Check, cmin::Cmin, coverage::Coverage, fmt::Fmt, init::Init, + list::List, run::Run, tmin::Tmin, }; use std::str::FromStr; @@ -57,6 +58,12 @@ impl FromStr for Sanitizer { } } +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum BuildMode { + Build, + Check, +} + #[derive(Clone, Debug, StructOpt, PartialEq)] pub struct BuildOptions { #[structopt(short = "D", long = "dev", conflicts_with = "release")] diff --git a/src/options/build.rs b/src/options/build.rs index a54ad70..df43074 100644 --- a/src/options/build.rs +++ b/src/options/build.rs @@ -1,5 +1,5 @@ use crate::{ - options::{BuildOptions, FuzzDirWrapper}, + options::{BuildMode, BuildOptions, FuzzDirWrapper}, project::FuzzProject, RunCommand, }; @@ -21,6 +21,10 @@ pub struct Build { impl RunCommand for Build { fn run_command(&mut self) -> Result<()> { let project = FuzzProject::new(self.fuzz_dir_wrapper.fuzz_dir.to_owned())?; - project.exec_build(&self.build, self.target.as_deref().map(|s| s)) + project.exec_build( + BuildMode::Build, + &self.build, + self.target.as_deref().map(|s| s), + ) } } diff --git a/src/options/check.rs b/src/options/check.rs new file mode 100644 index 0000000..6142bd8 --- /dev/null +++ b/src/options/check.rs @@ -0,0 +1,30 @@ +use crate::{ + options::{BuildMode, BuildOptions, FuzzDirWrapper}, + project::FuzzProject, + RunCommand, +}; +use anyhow::Result; +use structopt::StructOpt; + +#[derive(Clone, Debug, StructOpt)] +pub struct Check { + #[structopt(flatten)] + pub build: BuildOptions, + + #[structopt(flatten)] + pub fuzz_dir_wrapper: FuzzDirWrapper, + + /// Name of the fuzz target to check, or check all targets if not supplied + pub target: Option, +} + +impl RunCommand for Check { + fn run_command(&mut self) -> Result<()> { + let project = FuzzProject::new(self.fuzz_dir_wrapper.fuzz_dir.to_owned())?; + project.exec_build( + BuildMode::Check, + &self.build, + self.target.as_deref().map(|s| s), + ) + } +} diff --git a/src/project.rs b/src/project.rs index fcdf21f..1b2f3b9 100644 --- a/src/project.rs +++ b/src/project.rs @@ -1,4 +1,4 @@ -use crate::options::{self, BuildOptions, Sanitizer}; +use crate::options::{self, BuildMode, BuildOptions, Sanitizer}; use crate::utils::default_target; use anyhow::{anyhow, bail, Context, Result}; use std::collections::HashSet; @@ -268,10 +268,15 @@ impl FuzzProject { pub fn exec_build( &self, + mode: options::BuildMode, build: &options::BuildOptions, fuzz_target: Option<&str>, ) -> Result<()> { - let mut cmd = self.cargo("build", build)?; + let cargo_subcommand = match mode { + options::BuildMode::Build => "build", + options::BuildMode::Check => "check", + }; + let mut cmd = self.cargo(cargo_subcommand, build)?; if let Some(fuzz_target) = fuzz_target { cmd.arg("--bin").arg(fuzz_target); @@ -408,7 +413,7 @@ impl FuzzProject { /// Fuzz a given fuzz target pub fn exec_fuzz(&self, run: &options::Run) -> Result<()> { - self.exec_build(&run.build, Some(&run.target))?; + self.exec_build(BuildMode::Build, &run.build, Some(&run.target))?; let mut cmd = self.cargo_run(&run.build, &run.target)?; for arg in &run.args { @@ -497,7 +502,7 @@ impl FuzzProject { } pub fn exec_tmin(&self, tmin: &options::Tmin) -> Result<()> { - self.exec_build(&tmin.build, Some(&tmin.target))?; + self.exec_build(BuildMode::Build, &tmin.build, Some(&tmin.target))?; let mut cmd = self.cargo_run(&tmin.build, &tmin.target)?; cmd.arg("-minimize_crash=1") .arg(format!("-runs={}", tmin.runs)) @@ -572,7 +577,7 @@ impl FuzzProject { } pub fn exec_cmin(&self, cmin: &options::Cmin) -> Result<()> { - self.exec_build(&cmin.build, Some(&cmin.target))?; + self.exec_build(BuildMode::Build, &cmin.build, Some(&cmin.target))?; let mut cmd = self.cargo_run(&cmin.build, &cmin.target)?; for arg in &cmin.args { @@ -613,7 +618,7 @@ impl FuzzProject { /// Produce coverage information for a given corpus pub fn exec_coverage(self, coverage: &options::Coverage) -> Result<()> { // Build project with source-based coverage generation enabled. - self.exec_build(&coverage.build, Some(&coverage.target))?; + self.exec_build(BuildMode::Build, &coverage.build, Some(&coverage.target))?; // Retrieve corpus directories. let corpora = if coverage.corpus.is_empty() {