Skip to content

Commit 986a30e

Browse files
committed
integrate the build runner and the compiler server
The compiler now provides a server protocol for an interactive session with another process. The build runner uses this protocol to communicate compilation errors semantically from zig compiler subprocesses to the build runner. The protocol is exposed via stdin/stdout, or on a network socket, depending on whether the CLI flag `--listen=-` or e.g. `--listen=127.0.0.1:1337` is used. Additionally: * add the zig version string to the build runner cache prefix * remove --prominent-compile-errors CLI flag because it no longer does anything. Compilation errors are now unconditionally displayed at the bottom of the build summary output when using the terminal-based build runner. * Remove the color field from std.Build. The build steps are no longer supposed to interact with stderr directly. Instead they communicate semantically back to the build runner, which has its own logic about TTY configuration. * Use the cleanExit() pattern in the build runner. * Build steps can now use error.MakeFailed when they have already properly reported an error, or they can fail with any other error code in which case the build runner will create a simple message based on this error code.
1 parent c583d14 commit 986a30e

File tree

9 files changed

+524
-221
lines changed

9 files changed

+524
-221
lines changed

lib/build_runner.zig

Lines changed: 81 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const root = @import("@build");
22
const std = @import("std");
33
const builtin = @import("builtin");
4+
const assert = std.debug.assert;
45
const io = std.io;
56
const fmt = std.fmt;
67
const mem = std.mem;
@@ -71,8 +72,7 @@ pub fn main() !void {
7172
cache.addPrefix(build_root_directory);
7273
cache.addPrefix(local_cache_directory);
7374
cache.addPrefix(global_cache_directory);
74-
75-
//cache.hash.addBytes(builtin.zig_version);
75+
cache.hash.addBytes(builtin.zig_version_string);
7676

7777
const builder = try std.Build.create(
7878
allocator,
@@ -95,10 +95,8 @@ pub fn main() !void {
9595
var install_prefix: ?[]const u8 = null;
9696
var dir_list = std.Build.DirList{};
9797

98-
// before arg parsing, check for the NO_COLOR environment variable
99-
// if it exists, default the color setting to .off
100-
// explicit --color arguments will still override this setting.
101-
builder.color = if (process.hasEnvVarConstant("NO_COLOR")) .off else .auto;
98+
const Color = enum { auto, off, on };
99+
var color: Color = .auto;
102100

103101
while (nextArg(args, &arg_idx)) |arg| {
104102
if (mem.startsWith(u8, arg, "-D")) {
@@ -166,7 +164,7 @@ pub fn main() !void {
166164
std.debug.print("expected [auto|on|off] after --color", .{});
167165
usageAndErr(builder, false, stderr_stream);
168166
};
169-
builder.color = std.meta.stringToEnum(@TypeOf(builder.color), next_arg) orelse {
167+
color = std.meta.stringToEnum(Color, next_arg) orelse {
170168
std.debug.print("expected [auto|on|off] after --color, found '{s}'", .{next_arg});
171169
usageAndErr(builder, false, stderr_stream);
172170
};
@@ -200,8 +198,6 @@ pub fn main() !void {
200198
builder.verbose_cc = true;
201199
} else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) {
202200
builder.verbose_llvm_cpu_features = true;
203-
} else if (mem.eql(u8, arg, "--prominent-compile-errors")) {
204-
builder.prominent_compile_errors = true;
205201
} else if (mem.eql(u8, arg, "-fwine")) {
206202
builder.enable_wine = true;
207203
} else if (mem.eql(u8, arg, "-fno-wine")) {
@@ -257,6 +253,12 @@ pub fn main() !void {
257253
}
258254
}
259255

256+
const ttyconf: std.debug.TTY.Config = switch (color) {
257+
.auto => std.debug.detectTTYConfig(std.io.getStdErr()),
258+
.on => .escape_codes,
259+
.off => .no_color,
260+
};
261+
260262
var progress: std.Progress = .{};
261263
const main_progress_node = progress.start("", 0);
262264
defer main_progress_node.end();
@@ -272,11 +274,15 @@ pub fn main() !void {
272274
if (builder.validateUserInputDidItFail())
273275
usageAndErr(builder, true, stderr_stream);
274276

275-
runStepNames(builder, targets.items, main_progress_node, thread_pool_options) catch |err| {
276-
switch (err) {
277-
error.UncleanExit => process.exit(1),
278-
else => return err,
279-
}
277+
runStepNames(
278+
builder,
279+
targets.items,
280+
main_progress_node,
281+
thread_pool_options,
282+
ttyconf,
283+
) catch |err| switch (err) {
284+
error.UncleanExit => process.exit(1),
285+
else => return err,
280286
};
281287
}
282288

@@ -285,6 +291,7 @@ fn runStepNames(
285291
step_names: []const []const u8,
286292
parent_prog_node: *std.Progress.Node,
287293
thread_pool_options: std.Thread.Pool.Options,
294+
ttyconf: std.debug.TTY.Config,
288295
) !void {
289296
var step_stack = ArrayList(*Step).init(b.allocator);
290297
defer step_stack.deinit();
@@ -332,12 +339,14 @@ fn runStepNames(
332339

333340
wait_group.start();
334341
thread_pool.spawn(workerMakeOneStep, .{
335-
&wait_group, &thread_pool, b, step, &step_prog,
342+
&wait_group, &thread_pool, b, step, &step_prog, ttyconf,
336343
}) catch @panic("OOM");
337344
}
338345
}
339346

340-
var any_failed = false;
347+
var success_count: usize = 0;
348+
var failure_count: usize = 0;
349+
var pending_count: usize = 0;
341350

342351
for (step_stack.items) |s| {
343352
switch (s.state) {
@@ -349,20 +358,42 @@ fn runStepNames(
349358
// A -> B -> C (failure)
350359
// B will be marked as dependency_failure, while A may never be queued, and thus
351360
// remain in the initial state of precheck_done.
352-
.dependency_failure, .precheck_done => continue,
353-
.success => continue,
354-
.failure => {
355-
any_failed = true;
356-
std.debug.print("{s}: {s}\n", .{
357-
s.name, @errorName(s.result.err_code),
358-
});
359-
},
361+
.dependency_failure, .precheck_done => pending_count += 1,
362+
.success => success_count += 1,
363+
.failure => failure_count += 1,
360364
}
361365
}
362366

363-
if (any_failed) {
364-
process.exit(1);
365-
}
367+
const stderr = std.io.getStdErr();
368+
369+
const total_count = success_count + failure_count + pending_count;
370+
stderr.writer().print("build summary: {d}/{d} steps succeeded; {d} failed\n", .{
371+
success_count, total_count, failure_count,
372+
}) catch {};
373+
if (failure_count == 0) return cleanExit();
374+
375+
for (step_stack.items) |s| switch (s.state) {
376+
.failure => {
377+
// TODO print the dep prefix too
378+
ttyconf.setColor(stderr, .Bold) catch break;
379+
stderr.writeAll(s.name) catch break;
380+
ttyconf.setColor(stderr, .Reset) catch break;
381+
382+
if (s.result_error_bundle.errorMessageCount() > 0) {
383+
stderr.writer().print(": {d} compilation errors:\n", .{
384+
s.result_error_bundle.errorMessageCount(),
385+
}) catch break;
386+
s.result_error_bundle.renderToStdErr(ttyconf);
387+
} else {
388+
stderr.writer().print(": {d} error messages (printed above)\n", .{
389+
s.result_error_msgs.items.len,
390+
}) catch break;
391+
}
392+
},
393+
else => continue,
394+
};
395+
396+
process.exit(1);
366397
}
367398

368399
fn checkForDependencyLoop(
@@ -407,6 +438,7 @@ fn workerMakeOneStep(
407438
b: *std.Build,
408439
s: *Step,
409440
prog_node: *std.Progress.Node,
441+
ttyconf: std.debug.TTY.Config,
410442
) void {
411443
defer wg.finish();
412444

@@ -446,17 +478,26 @@ fn workerMakeOneStep(
446478
const make_result = s.make();
447479

448480
// No matter the result, we want to display error/warning messages.
449-
if (s.result.error_msgs.items.len > 0) {
481+
if (s.result_error_msgs.items.len > 0) {
450482
sub_prog_node.context.lock_stderr();
451483
defer sub_prog_node.context.unlock_stderr();
452484

453-
for (s.result.error_msgs.items) |msg| {
454-
std.io.getStdErr().writeAll(msg) catch break;
485+
const stderr = std.io.getStdErr();
486+
487+
for (s.result_error_msgs.items) |msg| {
488+
// TODO print the dep prefix too
489+
ttyconf.setColor(stderr, .Bold) catch break;
490+
stderr.writeAll(s.name) catch break;
491+
stderr.writeAll(": ") catch break;
492+
ttyconf.setColor(stderr, .Red) catch break;
493+
stderr.writeAll("error: ") catch break;
494+
ttyconf.setColor(stderr, .Reset) catch break;
495+
stderr.writeAll(msg) catch break;
455496
}
456497
}
457498

458499
make_result catch |err| {
459-
s.result.err_code = err;
500+
assert(err == error.MakeFailed);
460501
@atomicStore(Step.State, &s.state, .failure, .SeqCst);
461502
return;
462503
};
@@ -467,7 +508,7 @@ fn workerMakeOneStep(
467508
for (s.dependants.items) |dep| {
468509
wg.start();
469510
thread_pool.spawn(workerMakeOneStep, .{
470-
wg, thread_pool, b, dep, prog_node,
511+
wg, thread_pool, b, dep, prog_node, ttyconf,
471512
}) catch @panic("OOM");
472513
}
473514
}
@@ -601,3 +642,11 @@ fn argsRest(args: [][]const u8, idx: usize) ?[][]const u8 {
601642
if (idx >= args.len) return null;
602643
return args[idx..];
603644
}
645+
646+
fn cleanExit() void {
647+
if (builtin.mode == .Debug) {
648+
return;
649+
} else {
650+
process.exit(0);
651+
}
652+
}

0 commit comments

Comments
 (0)