@@ -68,7 +68,13 @@ pub const TestResults = struct {
68
68
}
69
69
};
70
70
71
- pub const MakeFn = * const fn (step : * Step , prog_node : std.Progress.Node ) anyerror ! void ;
71
+ pub const MakeOptions = struct {
72
+ progress_node : std.Progress.Node ,
73
+ thread_pool : * std.Thread.Pool ,
74
+ watch : bool ,
75
+ };
76
+
77
+ pub const MakeFn = * const fn (step : * Step , options : MakeOptions ) anyerror ! void ;
72
78
73
79
pub const State = enum {
74
80
precheck_unstarted ,
@@ -219,10 +225,10 @@ pub fn init(options: StepOptions) Step {
219
225
/// If the Step's `make` function reports `error.MakeFailed`, it indicates they
220
226
/// have already reported the error. Otherwise, we add a simple error report
221
227
/// here.
222
- pub fn make (s : * Step , prog_node : std.Progress.Node ) error { MakeFailed , MakeSkipped }! void {
228
+ pub fn make (s : * Step , options : MakeOptions ) error { MakeFailed , MakeSkipped }! void {
223
229
const arena = s .owner .allocator ;
224
230
225
- s .makeFn (s , prog_node ) catch | err | switch (err ) {
231
+ s .makeFn (s , options ) catch | err | switch (err ) {
226
232
error .MakeFailed = > return error .MakeFailed ,
227
233
error .MakeSkipped = > return error .MakeSkipped ,
228
234
else = > {
@@ -260,8 +266,8 @@ pub fn getStackTrace(s: *Step) ?std.builtin.StackTrace {
260
266
};
261
267
}
262
268
263
- fn makeNoOp (step : * Step , prog_node : std.Progress.Node ) anyerror ! void {
264
- _ = prog_node ;
269
+ fn makeNoOp (step : * Step , options : MakeOptions ) anyerror ! void {
270
+ _ = options ;
265
271
266
272
var all_cached = true ;
267
273
@@ -352,13 +358,54 @@ pub fn addError(step: *Step, comptime fmt: []const u8, args: anytype) error{OutO
352
358
try step .result_error_msgs .append (arena , msg );
353
359
}
354
360
361
+ pub const ZigProcess = struct {
362
+ child : std.process.Child ,
363
+ poller : std .io .Poller (StreamEnum ),
364
+ progress_ipc_fd : if (std .Progress .have_ipc ) ? std .posix .fd_t else void ,
365
+
366
+ pub const StreamEnum = enum { stdout , stderr };
367
+ };
368
+
355
369
/// Assumes that argv contains `--listen=-` and that the process being spawned
356
370
/// is the zig compiler - the same version that compiled the build runner.
357
371
pub fn evalZigProcess (
358
372
s : * Step ,
359
373
argv : []const []const u8 ,
360
374
prog_node : std.Progress.Node ,
375
+ watch : bool ,
361
376
) ! ? []const u8 {
377
+ if (s .getZigProcess ()) | zp | update : {
378
+ assert (watch );
379
+ if (std .Progress .have_ipc ) if (zp .progress_ipc_fd ) | fd | prog_node .setIpcFd (fd );
380
+ const result = zigProcessUpdate (s , zp , watch ) catch | err | switch (err ) {
381
+ error .BrokenPipe = > {
382
+ // Process restart required.
383
+ const term = zp .child .wait () catch | e | {
384
+ return s .fail ("unable to wait for {s}: {s}" , .{ argv [0 ], @errorName (e ) });
385
+ };
386
+ _ = term ;
387
+ s .clearZigProcess ();
388
+ break :update ;
389
+ },
390
+ else = > | e | return e ,
391
+ };
392
+
393
+ if (s .result_error_bundle .errorMessageCount () > 0 )
394
+ return s .fail ("{d} compilation errors" , .{s .result_error_bundle .errorMessageCount ()});
395
+
396
+ if (s .result_error_msgs .items .len > 0 and result == null ) {
397
+ // Crash detected.
398
+ const term = zp .child .wait () catch | e | {
399
+ return s .fail ("unable to wait for {s}: {s}" , .{ argv [0 ], @errorName (e ) });
400
+ };
401
+ s .result_peak_rss = zp .child .resource_usage_statistics .getMaxRss () orelse 0 ;
402
+ s .clearZigProcess ();
403
+ try handleChildProcessTerm (s , term , null , argv );
404
+ return error .MakeFailed ;
405
+ }
406
+
407
+ return result ;
408
+ }
362
409
assert (argv .len != 0 );
363
410
const b = s .owner ;
364
411
const arena = b .allocator ;
@@ -378,29 +425,79 @@ pub fn evalZigProcess(
378
425
child .spawn () catch | err | return s .fail ("unable to spawn {s}: {s}" , .{
379
426
argv [0 ], @errorName (err ),
380
427
});
381
- var timer = try std .time .Timer .start ();
382
428
383
- var poller = std .io .poll (gpa , enum { stdout , stderr }, .{
384
- .stdout = child .stdout .? ,
385
- .stderr = child .stderr .? ,
386
- });
387
- defer poller .deinit ();
429
+ const zp = try gpa .create (ZigProcess );
430
+ zp .* = .{
431
+ .child = child ,
432
+ .poller = std .io .poll (gpa , ZigProcess .StreamEnum , .{
433
+ .stdout = child .stdout .? ,
434
+ .stderr = child .stderr .? ,
435
+ }),
436
+ .progress_ipc_fd = if (std .Progress .have_ipc ) child .progress_node .getIpcFd () else {},
437
+ };
438
+ if (watch ) s .setZigProcess (zp );
439
+ defer if (! watch ) zp .poller .deinit ();
440
+
441
+ const result = try zigProcessUpdate (s , zp , watch );
442
+
443
+ if (! watch ) {
444
+ // Send EOF to stdin.
445
+ zp .child .stdin .? .close ();
446
+ zp .child .stdin = null ;
447
+
448
+ const term = zp .child .wait () catch | err | {
449
+ return s .fail ("unable to wait for {s}: {s}" , .{ argv [0 ], @errorName (err ) });
450
+ };
451
+ s .result_peak_rss = zp .child .resource_usage_statistics .getMaxRss () orelse 0 ;
452
+
453
+ // Special handling for Compile step that is expecting compile errors.
454
+ if (s .cast (Compile )) | compile | switch (term ) {
455
+ .Exited = > {
456
+ // Note that the exit code may be 0 in this case due to the
457
+ // compiler server protocol.
458
+ if (compile .expect_errors != null ) {
459
+ return error .NeedCompileErrorCheck ;
460
+ }
461
+ },
462
+ else = > {},
463
+ };
464
+
465
+ try handleChildProcessTerm (s , term , null , argv );
466
+ }
467
+
468
+ // This is intentionally printed for failure on the first build but not for
469
+ // subsequent rebuilds.
470
+ if (s .result_error_bundle .errorMessageCount () > 0 ) {
471
+ return s .fail ("the following command failed with {d} compilation errors:\n {s}" , .{
472
+ s .result_error_bundle .errorMessageCount (),
473
+ try allocPrintCmd (arena , null , argv ),
474
+ });
475
+ }
388
476
389
- try sendMessage (child .stdin .? , .update );
390
- try sendMessage (child .stdin .? , .exit );
477
+ return result ;
478
+ }
479
+
480
+ fn zigProcessUpdate (s : * Step , zp : * ZigProcess , watch : bool ) ! ? []const u8 {
481
+ const b = s .owner ;
482
+ const arena = b .allocator ;
483
+
484
+ var timer = try std .time .Timer .start ();
485
+
486
+ try sendMessage (zp .child .stdin .? , .update );
487
+ if (! watch ) try sendMessage (zp .child .stdin .? , .exit );
391
488
392
489
const Header = std .zig .Server .Message .Header ;
393
490
var result : ? []const u8 = null ;
394
491
395
- const stdout = poller .fifo (.stdout );
492
+ const stdout = zp . poller .fifo (.stdout );
396
493
397
494
poll : while (true ) {
398
495
while (stdout .readableLength () < @sizeOf (Header )) {
399
- if (! (try poller .poll ())) break :poll ;
496
+ if (! (try zp . poller .poll ())) break :poll ;
400
497
}
401
498
const header = stdout .reader ().readStruct (Header ) catch unreachable ;
402
499
while (stdout .readableLength () < header .bytes_len ) {
403
- if (! (try poller .poll ())) break :poll ;
500
+ if (! (try zp . poller .poll ())) break :poll ;
404
501
}
405
502
const body = stdout .readableSliceOfLen (header .bytes_len );
406
503
@@ -428,12 +525,22 @@ pub fn evalZigProcess(
428
525
.string_bytes = try arena .dupe (u8 , string_bytes ),
429
526
.extra = extra_array ,
430
527
};
528
+ if (watch ) {
529
+ // This message indicates the end of the update.
530
+ stdout .discard (body .len );
531
+ break ;
532
+ }
431
533
},
432
534
.emit_bin_path = > {
433
535
const EbpHdr = std .zig .Server .Message .EmitBinPath ;
434
536
const ebp_hdr = @as (* align (1 ) const EbpHdr , @ptrCast (body ));
435
537
s .result_cached = ebp_hdr .flags .cache_hit ;
436
538
result = try arena .dupe (u8 , body [@sizeOf (EbpHdr ).. ]);
539
+ if (watch ) {
540
+ // This message indicates the end of the update.
541
+ stdout .discard (body .len );
542
+ break ;
543
+ }
437
544
},
438
545
.file_system_inputs = > {
439
546
s .clearWatchInputs ();
@@ -470,6 +577,13 @@ pub fn evalZigProcess(
470
577
};
471
578
try addWatchInputFromPath (s , path , std .fs .path .basename (sub_path ));
472
579
},
580
+ .global_cache = > {
581
+ const path : Build.Cache.Path = .{
582
+ .root_dir = s .owner .graph .global_cache_root ,
583
+ .sub_path = sub_path_dirname ,
584
+ };
585
+ try addWatchInputFromPath (s , path , std .fs .path .basename (sub_path ));
586
+ },
473
587
}
474
588
}
475
589
},
@@ -479,43 +593,42 @@ pub fn evalZigProcess(
479
593
stdout .discard (body .len );
480
594
}
481
595
482
- const stderr = poller .fifo (.stderr );
596
+ s .result_duration_ns = timer .read ();
597
+
598
+ const stderr = zp .poller .fifo (.stderr );
483
599
if (stderr .readableLength () > 0 ) {
484
600
try s .result_error_msgs .append (arena , try stderr .toOwnedSlice ());
485
601
}
486
602
487
- // Send EOF to stdin.
488
- child .stdin .? .close ();
489
- child .stdin = null ;
603
+ return result ;
604
+ }
490
605
491
- const term = child .wait () catch | err | {
492
- return s .fail ("unable to wait for {s}: {s}" , .{ argv [0 ], @errorName (err ) });
493
- };
494
- s .result_duration_ns = timer .read ();
495
- s .result_peak_rss = child .resource_usage_statistics .getMaxRss () orelse 0 ;
496
-
497
- // Special handling for Compile step that is expecting compile errors.
498
- if (s .cast (Compile )) | compile | switch (term ) {
499
- .Exited = > {
500
- // Note that the exit code may be 0 in this case due to the
501
- // compiler server protocol.
502
- if (compile .expect_errors != null ) {
503
- return error .NeedCompileErrorCheck ;
504
- }
505
- },
506
- else = > {},
606
+ pub fn getZigProcess (s : * Step ) ? * ZigProcess {
607
+ return switch (s .id ) {
608
+ .compile = > s .cast (Compile ).? .zig_process ,
609
+ else = > null ,
507
610
};
611
+ }
508
612
509
- try handleChildProcessTerm (s , term , null , argv );
510
-
511
- if (s .result_error_bundle .errorMessageCount () > 0 ) {
512
- return s .fail ("the following command failed with {d} compilation errors:\n {s}" , .{
513
- s .result_error_bundle .errorMessageCount (),
514
- try allocPrintCmd (arena , null , argv ),
515
- });
613
+ fn setZigProcess (s : * Step , zp : * ZigProcess ) void {
614
+ switch (s .id ) {
615
+ .compile = > s .cast (Compile ).? .zig_process = zp ,
616
+ else = > unreachable ,
516
617
}
618
+ }
517
619
518
- return result ;
620
+ fn clearZigProcess (s : * Step ) void {
621
+ const gpa = s .owner .allocator ;
622
+ switch (s .id ) {
623
+ .compile = > {
624
+ const compile = s .cast (Compile ).? ;
625
+ if (compile .zig_process ) | zp | {
626
+ gpa .destroy (zp );
627
+ compile .zig_process = null ;
628
+ }
629
+ },
630
+ else = > unreachable ,
631
+ }
519
632
}
520
633
521
634
fn sendMessage (file : std.fs.File , tag : std.zig.Client.Message.Tag ) ! void {
0 commit comments