@@ -112,7 +112,7 @@ pub fn main() !void {
112
112
var steps_menu = false ;
113
113
var output_tmp_nonce : ? [16 ]u8 = null ;
114
114
var watch = false ;
115
- var fuzz = false ;
115
+ var fuzz : ? std.Build.Fuzz.Mode = null ;
116
116
var debounce_interval_ms : u16 = 50 ;
117
117
var webui_listen : ? std.net.Address = null ;
118
118
@@ -274,10 +274,44 @@ pub fn main() !void {
274
274
webui_listen = std .net .Address .parseIp ("::1" , 0 ) catch unreachable ;
275
275
}
276
276
} else if (mem .eql (u8 , arg , "--fuzz" )) {
277
- fuzz = true ;
277
+ fuzz = .{ . forever = undefined } ;
278
278
if (webui_listen == null ) {
279
279
webui_listen = std .net .Address .parseIp ("::1" , 0 ) catch unreachable ;
280
280
}
281
+ } else if (mem .startsWith (u8 , arg , "--fuzz=" )) {
282
+ const value = arg ["--fuzz=" .len .. ];
283
+ if (value .len == 0 ) fatal ("missing argument to --fuzz" , .{});
284
+
285
+ const unit : u8 = value [value .len - 1 ];
286
+ const digits = switch (unit ) {
287
+ '0' ... '9' = > value ,
288
+ 'K' , 'M' , 'G' = > value [0 .. value .len - 1 ],
289
+ else = > fatal (
290
+ "invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]" ,
291
+ .{},
292
+ ),
293
+ };
294
+
295
+ const amount = std .fmt .parseInt (u64 , digits , 10 ) catch {
296
+ fatal (
297
+ "invalid argument to --fuzz, expected a positive number optionally suffixed by one of: [KMG]" ,
298
+ .{},
299
+ );
300
+ };
301
+
302
+ const normalized_amount = std .math .mul (u64 , amount , switch (unit ) {
303
+ else = > unreachable ,
304
+ '0' ... '9' = > 1 ,
305
+ 'K' = > 1000 ,
306
+ 'M' = > 1_000_000 ,
307
+ 'G' = > 1_000_000_000 ,
308
+ }) catch fatal ("fuzzing limit amount overflows u64" , .{});
309
+
310
+ fuzz = .{
311
+ .limit = .{
312
+ .amount = normalized_amount ,
313
+ },
314
+ };
281
315
} else if (mem .eql (u8 , arg , "-fincremental" )) {
282
316
graph .incremental = true ;
283
317
} else if (mem .eql (u8 , arg , "-fno-incremental" )) {
@@ -476,6 +510,7 @@ pub fn main() !void {
476
510
targets .items ,
477
511
main_progress_node ,
478
512
& run ,
513
+ fuzz ,
479
514
) catch | err | switch (err ) {
480
515
error .UncleanExit = > {
481
516
assert (! run .watch and run .web_server == null );
@@ -485,7 +520,12 @@ pub fn main() !void {
485
520
};
486
521
487
522
if (run .web_server ) | * web_server | {
488
- web_server .finishBuild (.{ .fuzz = fuzz });
523
+ if (fuzz ) | mode | if (mode != .forever ) fatal (
524
+ "error: limited fuzzing is not implemented yet for --webui" ,
525
+ .{},
526
+ );
527
+
528
+ web_server .finishBuild (.{ .fuzz = fuzz != null });
489
529
}
490
530
491
531
if (! watch and run .web_server == null ) {
@@ -651,6 +691,7 @@ fn runStepNames(
651
691
step_names : []const []const u8 ,
652
692
parent_prog_node : std.Progress.Node ,
653
693
run : * Run ,
694
+ fuzz : ? std.Build.Fuzz.Mode ,
654
695
) ! void {
655
696
const gpa = run .gpa ;
656
697
const step_stack = & run .step_stack ;
@@ -676,6 +717,7 @@ fn runStepNames(
676
717
});
677
718
}
678
719
}
720
+
679
721
assert (run .memory_blocked_steps .items .len == 0 );
680
722
681
723
var test_skip_count : usize = 0 ;
@@ -724,6 +766,45 @@ fn runStepNames(
724
766
}
725
767
}
726
768
769
+ const ttyconf = run .ttyconf ;
770
+
771
+ if (fuzz ) | mode | blk : {
772
+ switch (builtin .os .tag ) {
773
+ // Current implementation depends on two things that need to be ported to Windows:
774
+ // * Memory-mapping to share data between the fuzzer and build runner.
775
+ // * COFF/PE support added to `std.debug.Info` (it needs a batching API for resolving
776
+ // many addresses to source locations).
777
+ .windows = > fatal ("--fuzz not yet implemented for {s}" , .{@tagName (builtin .os .tag )}),
778
+ else = > {},
779
+ }
780
+ if (@bitSizeOf (usize ) != 64 ) {
781
+ // Current implementation depends on posix.mmap()'s second parameter, `length: usize`,
782
+ // being compatible with `std.fs.getEndPos() u64`'s return value. This is not the case
783
+ // on 32-bit platforms.
784
+ // Affects or affected by issues #5185, #22523, and #22464.
785
+ fatal ("--fuzz not yet implemented on {d}-bit platforms" , .{@bitSizeOf (usize )});
786
+ }
787
+
788
+ switch (mode ) {
789
+ .forever = > break :blk ,
790
+ .limit = > {},
791
+ }
792
+
793
+ assert (mode == .limit );
794
+ var f = std .Build .Fuzz .init (
795
+ gpa ,
796
+ thread_pool ,
797
+ step_stack .keys (),
798
+ parent_prog_node ,
799
+ ttyconf ,
800
+ mode ,
801
+ ) catch | err | fatal ("failed to start fuzzer: {s}" , .{@errorName (err )});
802
+ defer f .deinit ();
803
+
804
+ f .start ();
805
+ f .waitAndPrintReport ();
806
+ }
807
+
727
808
// A proper command line application defaults to silently succeeding.
728
809
// The user may request verbose mode if they have a different preference.
729
810
const failures_only = switch (run .summary ) {
@@ -737,8 +818,6 @@ fn runStepNames(
737
818
std .Progress .setStatus (.failure );
738
819
}
739
820
740
- const ttyconf = run .ttyconf ;
741
-
742
821
if (run .summary != .none ) {
743
822
const w = std .debug .lockStderrWriter (& stdio_buffer_allocation );
744
823
defer std .debug .unlockStderrWriter ();
@@ -1366,7 +1445,10 @@ fn printUsage(b: *std.Build, w: *Writer) !void {
1366
1445
\\ --watch Continuously rebuild when source files are modified
1367
1446
\\ --debounce <ms> Delay before rebuilding after changed file detected
1368
1447
\\ --webui[=ip] Enable the web interface on the given IP address
1369
- \\ --fuzz Continuously search for unit test failures (implies '--webui')
1448
+ \\ --fuzz[=limit] Continuously search for unit test failures with an optional
1449
+ \\ limit to the max number of iterations. The argument supports
1450
+ \\ an optional 'K', 'M', or 'G' suffix (e.g. '10K'). Implies
1451
+ \\ '--webui' when no limit is specified.
1370
1452
\\ --time-report Force full rebuild and provide detailed information on
1371
1453
\\ compilation time of Zig source code (implies '--webui')
1372
1454
\\ -fincremental Enable incremental compilation
0 commit comments