From 2d04dca0c7ca534678120dd9ca92d1f791d8938e Mon Sep 17 00:00:00 2001 From: Darkhan Kubigenov Date: Sat, 6 Jan 2024 11:23:42 +0000 Subject: [PATCH] Enable use of cargo-fuzz without a separate workspace This addresses https://github.com/rust-fuzz/cargo-fuzz/issues/345. Currently, `cargo-fuzz` defines an independent workspace ``` [workspace] members = ["."] ``` which introduces some issues like caching setup, separate linting configs, etc. The main reason for this seems to be a custom config for `profile.release.debug` which is allowed to be changed only at the root Cargo.toml of the workspace. This commit adds a workaround for that by supplying `--config profile.release.debug=true`. If users need to preserve the old behavior they can provide the flag `--fuzzing-workspace=true` during `cargo fuzz init` --- README.md | 6 ++++++ src/options/init.rs | 4 ++++ src/project.rs | 8 ++++++-- src/templates.rs | 21 ++++++++++++--------- tests/tests/main.rs | 33 +++++++++++++++++++++++++++++++-- tests/tests/project.rs | 20 ++++++++++++++++++++ 6 files changed, 79 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 85d0544..72a70f8 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,12 @@ $ cargo install -f cargo-fuzz Initialize a `cargo fuzz` project for your crate! +### Add `fuzz` directory to `workspace.members` in root `Cargo.toml` + +`fuzz` directory can be either a part of an existing workspace (default) +or use an independent workspace. If latter is desired, you can use +`cargo fuzz init --fuzzing-workspace=true`. + ### `cargo fuzz add ` Create a new fuzzing target! diff --git a/src/options/init.rs b/src/options/init.rs index 35250a5..565ac53 100644 --- a/src/options/init.rs +++ b/src/options/init.rs @@ -8,6 +8,10 @@ pub struct Init { /// Name of the first fuzz target to create pub target: String, + #[arg(long, value_parser = clap::builder::BoolishValueParser::new(), default_value = "false")] + /// Whether to create a separate workspace for fuzz targets crate + pub fuzzing_workspace: Option, + #[command(flatten)] pub fuzz_dir_wrapper: FuzzDirWrapper, } diff --git a/src/project.rs b/src/project.rs index 4150673..c58afc2 100644 --- a/src/project.rs +++ b/src/project.rs @@ -66,7 +66,11 @@ impl FuzzProject { let mut cargo = fs::File::create(&cargo_toml) .with_context(|| format!("failed to create {}", cargo_toml.display()))?; cargo - .write_fmt(toml_template!(manifest.crate_name, manifest.edition)) + .write_fmt(toml_template!( + manifest.crate_name, + manifest.edition, + init.fuzzing_workspace + )) .with_context(|| format!("failed to write to {}", cargo_toml.display()))?; let gitignore = fuzz_project.join(".gitignore"); @@ -137,7 +141,7 @@ impl FuzzProject { .arg(&build.triple); // we default to release mode unless debug mode is explicitly requested if !build.dev { - cmd.arg("--release"); + cmd.args(["--release", "--config", "profile.release.debug=true"]); } if build.verbose { cmd.arg("--verbose"); diff --git a/src/templates.rs b/src/templates.rs index 11601fe..5f09e33 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -1,5 +1,5 @@ macro_rules! toml_template { - ($name:expr, $edition:expr) => { + ($name:expr, $edition:expr, $fuzzing_workspace:expr) => { format_args!( r##"[package] name = "{name}-fuzz" @@ -14,20 +14,22 @@ libfuzzer-sys = "0.4" [dependencies.{name}] path = ".." - -# Prevent this from interfering with workspaces -[workspace] -members = ["."] - -[profile.release] -debug = 1 -"##, +{workspace}"##, name = $name, edition = if let Some(edition) = &$edition { format!("edition = \"{}\"\n", edition) } else { String::new() }, + workspace = if let Some(true) = $fuzzing_workspace { + r##" +# Use independent workspace for fuzzers +[workspace] +members = ["."] +"## + } else { + "" + } ) }; } @@ -41,6 +43,7 @@ name = "{0}" path = "fuzz_targets/{0}.rs" test = false doc = false +bench = false "#, $name ) diff --git a/tests/tests/main.rs b/tests/tests/main.rs index d779369..d621f48 100644 --- a/tests/tests/main.rs +++ b/tests/tests/main.rs @@ -17,9 +17,35 @@ fn help() { } #[test] -fn init() { +fn init_with_workspace() { let project = project("init").build(); + project + .cargo_fuzz() + .arg("init") + .arg("--fuzzing-workspace=true") + .assert() + .success(); + assert!(project.fuzz_dir().is_dir()); + assert!(project.fuzz_cargo_toml().is_file()); + assert!(project.fuzz_targets_dir().is_dir()); + assert!(project.fuzz_target_path("fuzz_target_1").is_file()); + project + .cargo_fuzz() + .arg("run") + .arg("fuzz_target_1") + .arg("--") + .arg("-runs=1") + .assert() + .success(); +} + +#[test] +fn init_no_workspace() { + let mut project_builder = project("init_no_workspace"); + let project = project_builder.build(); project.cargo_fuzz().arg("init").assert().success(); + project_builder.set_workspace_members(&["fuzz"]); + assert!(project.fuzz_dir().is_dir()); assert!(project.fuzz_cargo_toml().is_file()); assert!(project.fuzz_targets_dir().is_dir()); @@ -28,6 +54,8 @@ fn init() { .cargo_fuzz() .arg("run") .arg("fuzz_target_1") + .arg("--fuzz-dir") + .arg(project.fuzz_dir().to_str().unwrap()) .arg("--") .arg("-runs=1") .assert() @@ -35,13 +63,14 @@ fn init() { } #[test] -fn init_with_target() { +fn init_with_target_and_workspace() { let project = project("init_with_target").build(); project .cargo_fuzz() .arg("init") .arg("-t") .arg("custom_target_name") + .arg("--fuzzing-workspace=true") .assert() .success(); assert!(project.fuzz_dir().is_dir()); diff --git a/tests/tests/project.rs b/tests/tests/project.rs index 5652df6..3491d72 100644 --- a/tests/tests/project.rs +++ b/tests/tests/project.rs @@ -118,6 +118,26 @@ impl ProjectBuilder { self.file(path, body) } + pub fn set_workspace_members(&mut self, members: &[&str]) -> &mut Self { + let cargo_toml = self.root().join("Cargo.toml"); + let manifest = fs::read_to_string(cargo_toml.clone()).unwrap(); + + let with_members = manifest.replace( + "[workspace]", + &format!( + "[workspace]\nmembers=[{}]", + members + .iter() + .map(|&v| format!("\"{}\"", v)) + .collect::>() + .join(", ") + ), + ); + + fs::write(cargo_toml, with_members).unwrap(); + self + } + pub fn file>(&mut self, path: B, body: &str) -> &mut Self { self._file(path.as_ref(), body); self