1+ use std:: collections:: HashMap ;
2+
3+ use tinyjson:: JsonValue ;
4+
15use crate :: prelude:: * ;
26// NOTE: cargo-fuzz (libFuzzer) does not support Windows yet (coming soon?)
37
8+ /// Enumerate fuzz targets by scanning `fuzz/fuzz_targets/*.rs`.
9+ ///
10+ /// The fuzz targets directory is the single source of truth: each `.rs` file
11+ /// there is a libFuzzer binary registered in `fuzz/Cargo.toml`. Discovering
12+ /// them dynamically means the CI matrix picks up new targets automatically.
13+ pub fn discover_targets ( ) -> anyhow:: Result < Vec < String > > {
14+ let dir = std:: path:: Path :: new ( env ! ( "CARGO_MANIFEST_DIR" ) )
15+ . ancestors ( )
16+ . nth ( 1 )
17+ . context ( "retrieve project root path" ) ?
18+ . join ( "fuzz" )
19+ . join ( "fuzz_targets" ) ;
20+
21+ let mut targets: Vec < String > = std:: fs:: read_dir ( & dir)
22+ . with_context ( || format ! ( "read fuzz targets directory: {}" , dir. display( ) ) ) ?
23+ . map ( |entry| {
24+ let entry = entry. with_context ( || format ! ( "read entry in {}" , dir. display( ) ) ) ?;
25+ Ok ( entry. path ( ) )
26+ } )
27+ . collect :: < anyhow:: Result < Vec < _ > > > ( ) ?
28+ . into_iter ( )
29+ . filter_map ( |path| {
30+ if path. extension ( ) . and_then ( |ext| ext. to_str ( ) ) != Some ( "rs" ) {
31+ return None ;
32+ }
33+ path. file_stem ( ) . and_then ( |stem| stem. to_str ( ) ) . map ( str:: to_owned)
34+ } )
35+ . collect ( ) ;
36+
37+ targets. sort ( ) ;
38+ Ok ( targets)
39+ }
40+
441pub fn corpus_minify ( sh : & Shell , target : Option < String > ) -> anyhow:: Result < ( ) > {
542 let _s = Section :: new ( "FUZZ-CORPUS-MINIFY" ) ;
643 windows_skip ! ( ) ;
744
845 let _guard = sh. push_dir ( "./fuzz" ) ;
946
10- let target_from_user = target. as_deref ( ) . map ( |value| [ value] ) ;
11-
12- let targets = if let Some ( targets) = & target_from_user {
13- targets
14- } else {
15- FUZZ_TARGETS
47+ let targets = match target {
48+ Some ( value) => vec ! [ value] ,
49+ None => discover_targets ( ) ?,
1650 } ;
1751
18- for target in targets {
52+ for target in & targets {
1953 cmd ! ( sh, "rustup run {NIGHTLY_TOOLCHAIN} cargo fuzz cmin {target}" ) . run ( ) ?;
2054 }
2155
@@ -72,15 +106,12 @@ pub fn run(sh: &Shell, duration: Option<u32>, target: Option<String>) -> anyhow:
72106 let _guard = sh. push_dir ( "./fuzz" ) ;
73107
74108 let duration = duration. unwrap_or ( 5 ) . to_string ( ) ;
75- let target_from_user = target. as_deref ( ) . map ( |value| [ value] ) ;
76-
77- let targets = if let Some ( targets) = & target_from_user {
78- targets
79- } else {
80- FUZZ_TARGETS
109+ let targets = match target {
110+ Some ( value) => vec ! [ value] ,
111+ None => discover_targets ( ) ?,
81112 } ;
82113
83- for target in targets {
114+ for target in & targets {
84115 cmd ! (
85116 sh,
86117 "rustup run {NIGHTLY_TOOLCHAIN} cargo fuzz run {target} -- -max_total_time={duration} -timeout=10"
@@ -92,3 +123,38 @@ pub fn run(sh: &Shell, duration: Option<u32>, target: Option<String>) -> anyhow:
92123
93124 Ok ( ( ) )
94125}
126+
127+ /// Print each fuzz target, one per line. Useful for local discovery.
128+ pub fn list_human ( ) -> anyhow:: Result < ( ) > {
129+ for target in discover_targets ( ) ? {
130+ println ! ( "{target}" ) ;
131+ }
132+ Ok ( ( ) )
133+ }
134+
135+ /// Emit a `matrix.include`-compatible JSON array on stdout, one entry per
136+ /// discovered fuzz target. Suitable for piping into a GitHub Actions matrix:
137+ ///
138+ /// ```yaml
139+ /// - id: setup
140+ /// run: echo "fuzz-matrix=$(cargo xtask fuzz list --format github-matrix)" >> "$GITHUB_OUTPUT"
141+ /// ```
142+ ///
143+ /// Each entry has the shape `{ "target": "<name>" }`.
144+ pub fn list_github_matrix ( ) -> anyhow:: Result < ( ) > {
145+ let items: Vec < JsonValue > = discover_targets ( ) ?
146+ . into_iter ( )
147+ . map ( |name| {
148+ let mut obj = HashMap :: new ( ) ;
149+ obj. insert ( "target" . to_owned ( ) , JsonValue :: String ( name) ) ;
150+ JsonValue :: Object ( obj)
151+ } )
152+ . collect ( ) ;
153+
154+ let json = JsonValue :: Array ( items) ;
155+ let stringified = json
156+ . stringify ( )
157+ . context ( "serialize fuzz matrix include array as JSON" ) ?;
158+ println ! ( "{stringified}" ) ;
159+ Ok ( ( ) )
160+ }
0 commit comments