authorgravatar for mail@linusgroh.deLinus Groh <mail@linusgroh.de> 2023-05-21 14:27:28+01:00
committergravatar for mail@linusgroh.deLinus Groh <mail@linusgroh.de> 2023-05-24 10:15:02+01:00
log0f6fa3f20b3b28958921bd63a9a9d96468455e9c
treeb725c368b43701903b4fe145eb240bfffbdc79e6
parent39c2eee285f820282dedba4404cac1009a5ae2d6

std: Move std.debug.{TTY.Config,detectTTYConfig} to std.io.tty

Also get rid of the TTY wrapper struct, which was exlusively used as a namespace - this is done by the tty.zig root struct now. detectTTYConfig has been renamed to just detectConfig, which is enough given the new namespace. Additionally, a doc comment had been added.

11 files changed, 152 insertions(+), 144 deletions(-)

lib/build_runner.zig+6-6
......@@ -333,7 +333,7 @@ const Run = struct {
333333
334334 claimed_rss: usize,
335335 enable_summary: ?bool,
336 ttyconf: std.debug.TTY.Config,
336 ttyconf: std.io.tty.Config,
337337 stderr: std.fs.File,
338338};
339339
......@@ -535,7 +535,7 @@ const PrintNode = struct {
535535 last: bool = false,
536536};
537537
538fn printPrefix(node: *PrintNode, stderr: std.fs.File, ttyconf: std.debug.TTY.Config) !void {
538fn printPrefix(node: *PrintNode, stderr: std.fs.File, ttyconf: std.io.tty.Config) !void {
539539 const parent = node.parent orelse return;
540540 if (parent.parent == null) return;
541541 try printPrefix(parent, stderr, ttyconf);
......@@ -553,7 +553,7 @@ fn printTreeStep(
553553 b: *std.Build,
554554 s: *Step,
555555 stderr: std.fs.File,
556 ttyconf: std.debug.TTY.Config,
556 ttyconf: std.io.tty.Config,
557557 parent_node: *PrintNode,
558558 step_stack: *std.AutoArrayHashMapUnmanaged(*Step, void),
559559) !void {
......@@ -1026,15 +1026,15 @@ fn cleanExit() void {
10261026
10271027const Color = enum { auto, off, on };
10281028
1029fn get_tty_conf(color: Color, stderr: std.fs.File) std.debug.TTY.Config {
1029fn get_tty_conf(color: Color, stderr: std.fs.File) std.io.tty.Config {
10301030 return switch (color) {
1031 .auto => std.debug.detectTTYConfig(stderr),
1031 .auto => std.io.tty.detectConfig(stderr),
10321032 .on => .escape_codes,
10331033 .off => .no_color,
10341034 };
10351035}
10361036
1037fn renderOptions(ttyconf: std.debug.TTY.Config) std.zig.ErrorBundle.RenderOptions {
1037fn renderOptions(ttyconf: std.io.tty.Config) std.zig.ErrorBundle.RenderOptions {
10381038 return .{
10391039 .ttyconf = ttyconf,
10401040 .include_source_line = ttyconf != .no_color,
lib/std/Build.zig+1-1
......@@ -1712,7 +1712,7 @@ fn dumpBadGetPathHelp(
17121712 s.name,
17131713 });
17141714
1715 const tty_config = std.debug.detectTTYConfig(stderr);
1715 const tty_config = std.io.tty.detectConfig(stderr);
17161716 tty_config.setColor(w, .red) catch {};
17171717 try stderr.writeAll(" The step was created by this stack trace:\n");
17181718 tty_config.setColor(w, .reset) catch {};
lib/std/Build/Step.zig+1-1
......@@ -237,7 +237,7 @@ pub fn dump(step: *Step) void {
237237
238238 const stderr = std.io.getStdErr();
239239 const w = stderr.writer();
240 const tty_config = std.debug.detectTTYConfig(stderr);
240 const tty_config = std.io.tty.detectConfig(stderr);
241241 const debug_info = std.debug.getSelfDebugInfo() catch |err| {
242242 w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{
243243 @errorName(err),
lib/std/builtin.zig+1-1
......@@ -51,7 +51,7 @@ pub const StackTrace = struct {
5151 const debug_info = std.debug.getSelfDebugInfo() catch |err| {
5252 return writer.print("\nUnable to print stack trace: Unable to open debug info: {s}\n", .{@errorName(err)});
5353 };
54 const tty_config = std.debug.detectTTYConfig(std.io.getStdErr());
54 const tty_config = std.io.tty.detectConfig(std.io.getStdErr());
5555 try writer.writeAll("\n");
5656 std.debug.writeStackTrace(self, writer, arena.allocator(), debug_info, tty_config) catch |err| {
5757 try writer.print("Unable to print stack trace: {s}\n", .{@errorName(err)});
lib/std/debug.zig+11-126
......@@ -5,7 +5,6 @@ const mem = std.mem;
55const io = std.io;
66const os = std.os;
77const fs = std.fs;
8const process = std.process;
98const testing = std.testing;
109const elf = std.elf;
1110const DW = std.dwarf;
......@@ -109,31 +108,6 @@ pub fn getSelfDebugInfo() !*DebugInfo {
109108 }
110109}
111110
112pub fn detectTTYConfig(file: std.fs.File) TTY.Config {
113 if (builtin.os.tag == .wasi) {
114 // Per https://github.com/WebAssembly/WASI/issues/162 ANSI codes
115 // aren't currently supported.
116 return .no_color;
117 } else if (process.hasEnvVarConstant("ZIG_DEBUG_COLOR")) {
118 return .escape_codes;
119 } else if (process.hasEnvVarConstant("NO_COLOR")) {
120 return .no_color;
121 } else if (file.supportsAnsiEscapeCodes()) {
122 return .escape_codes;
123 } else if (native_os == .windows and file.isTty()) {
124 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
125 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) {
126 // TODO: Should this return an error instead?
127 return .no_color;
128 }
129 return .{ .windows_api = .{
130 .handle = file.handle,
131 .reset_attributes = info.wAttributes,
132 } };
133 }
134 return .no_color;
135}
136
137111/// Tries to print the current stack trace to stderr, unbuffered, and ignores any error returned.
138112/// TODO multithreaded awareness
139113pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
......@@ -154,7 +128,7 @@ pub fn dumpCurrentStackTrace(start_addr: ?usize) void {
154128 stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
155129 return;
156130 };
157 writeCurrentStackTrace(stderr, debug_info, detectTTYConfig(io.getStdErr()), start_addr) catch |err| {
131 writeCurrentStackTrace(stderr, debug_info, io.tty.detectConfig(io.getStdErr()), start_addr) catch |err| {
158132 stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
159133 return;
160134 };
......@@ -182,7 +156,7 @@ pub fn dumpStackTraceFromBase(bp: usize, ip: usize) void {
182156 stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
183157 return;
184158 };
185 const tty_config = detectTTYConfig(io.getStdErr());
159 const tty_config = io.tty.detectConfig(io.getStdErr());
186160 if (native_os == .windows) {
187161 writeCurrentStackTraceWindows(stderr, debug_info, tty_config, ip) catch return;
188162 return;
......@@ -265,7 +239,7 @@ pub fn dumpStackTrace(stack_trace: std.builtin.StackTrace) void {
265239 stderr.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}) catch return;
266240 return;
267241 };
268 writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, detectTTYConfig(io.getStdErr())) catch |err| {
242 writeStackTrace(stack_trace, stderr, getDebugInfoAllocator(), debug_info, io.tty.detectConfig(io.getStdErr())) catch |err| {
269243 stderr.print("Unable to dump stack trace: {s}\n", .{@errorName(err)}) catch return;
270244 return;
271245 };
......@@ -403,7 +377,7 @@ pub fn writeStackTrace(
403377 out_stream: anytype,
404378 allocator: mem.Allocator,
405379 debug_info: *DebugInfo,
406 tty_config: TTY.Config,
380 tty_config: io.tty.Config,
407381) !void {
408382 _ = allocator;
409383 if (builtin.strip_debug_info) return error.MissingDebugInfo;
......@@ -562,7 +536,7 @@ pub const StackIterator = struct {
562536pub fn writeCurrentStackTrace(
563537 out_stream: anytype,
564538 debug_info: *DebugInfo,
565 tty_config: TTY.Config,
539 tty_config: io.tty.Config,
566540 start_addr: ?usize,
567541) !void {
568542 if (native_os == .windows) {
......@@ -634,7 +608,7 @@ pub noinline fn walkStackWindows(addresses: []usize) usize {
634608pub fn writeCurrentStackTraceWindows(
635609 out_stream: anytype,
636610 debug_info: *DebugInfo,
637 tty_config: TTY.Config,
611 tty_config: io.tty.Config,
638612 start_addr: ?usize,
639613) !void {
640614 var addr_buf: [1024]usize = undefined;
......@@ -651,95 +625,6 @@ pub fn writeCurrentStackTraceWindows(
651625 }
652626}
653627
654/// Provides simple functionality for manipulating the terminal in some way,
655/// for debugging purposes, such as coloring text, etc.
656pub const TTY = struct {
657 pub const Color = enum {
658 red,
659 green,
660 yellow,
661 cyan,
662 white,
663 dim,
664 bold,
665 reset,
666 };
667
668 pub const Config = union(enum) {
669 no_color,
670 escape_codes,
671 windows_api: if (native_os == .windows) WindowsContext else void,
672
673 pub const WindowsContext = struct {
674 handle: File.Handle,
675 reset_attributes: u16,
676 };
677
678 pub fn setColor(conf: Config, out_stream: anytype, color: Color) !void {
679 nosuspend switch (conf) {
680 .no_color => return,
681 .escape_codes => {
682 const color_string = switch (color) {
683 .red => "\x1b[31;1m",
684 .green => "\x1b[32;1m",
685 .yellow => "\x1b[33;1m",
686 .cyan => "\x1b[36;1m",
687 .white => "\x1b[37;1m",
688 .bold => "\x1b[1m",
689 .dim => "\x1b[2m",
690 .reset => "\x1b[0m",
691 };
692 try out_stream.writeAll(color_string);
693 },
694 .windows_api => |ctx| if (native_os == .windows) {
695 const attributes = switch (color) {
696 .red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY,
697 .green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY,
698 .yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY,
699 .cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY,
700 .white, .bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY,
701 .dim => windows.FOREGROUND_INTENSITY,
702 .reset => ctx.reset_attributes,
703 };
704 try windows.SetConsoleTextAttribute(ctx.handle, attributes);
705 } else {
706 unreachable;
707 },
708 };
709 }
710
711 pub fn writeDEC(conf: Config, writer: anytype, codepoint: u8) !void {
712 const bytes = switch (conf) {
713 .no_color, .windows_api => switch (codepoint) {
714 0x50...0x5e => @as(*const [1]u8, &codepoint),
715 0x6a => "+", // ┘
716 0x6b => "+", // ┐
717 0x6c => "+", // ┌
718 0x6d => "+", // └
719 0x6e => "+", // ┼
720 0x71 => "-", // ─
721 0x74 => "+", // ├
722 0x75 => "+", // ┤
723 0x76 => "+", // ┴
724 0x77 => "+", // ┬
725 0x78 => "|", // │
726 else => " ", // TODO
727 },
728 .escape_codes => switch (codepoint) {
729 // Here we avoid writing the DEC beginning sequence and
730 // ending sequence in separate syscalls by putting the
731 // beginning and ending sequence into the same string
732 // literals, to prevent terminals ending up in bad states
733 // in case a crash happens between syscalls.
734 inline 0x50...0x7f => |x| "\x1B\x28\x30" ++ [1]u8{x} ++ "\x1B\x28\x42",
735 else => unreachable,
736 },
737 };
738 return writer.writeAll(bytes);
739 }
740 };
741};
742
743628fn machoSearchSymbols(symbols: []const MachoSymbol, address: usize) ?*const MachoSymbol {
744629 var min: usize = 0;
745630 var max: usize = symbols.len - 1;
......@@ -785,7 +670,7 @@ test "machoSearchSymbols" {
785670 try testing.expectEqual(&symbols[2], machoSearchSymbols(&symbols, 5000).?);
786671}
787672
788fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usize, tty_config: TTY.Config) !void {
673fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usize, tty_config: io.tty.Config) !void {
789674 const module_name = debug_info.getModuleNameForAddress(address);
790675 return printLineInfo(
791676 out_stream,
......@@ -798,7 +683,7 @@ fn printUnknownSource(debug_info: *DebugInfo, out_stream: anytype, address: usiz
798683 );
799684}
800685
801pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: anytype, address: usize, tty_config: TTY.Config) !void {
686pub fn printSourceAtAddress(debug_info: *DebugInfo, out_stream: anytype, address: usize, tty_config: io.tty.Config) !void {
802687 const module = debug_info.getModuleForAddress(address) catch |err| switch (err) {
803688 error.MissingDebugInfo, error.InvalidDebugInfo => return printUnknownSource(debug_info, out_stream, address, tty_config),
804689 else => return err,
......@@ -827,7 +712,7 @@ fn printLineInfo(
827712 address: usize,
828713 symbol_name: []const u8,
829714 compile_unit_name: []const u8,
830 tty_config: TTY.Config,
715 tty_config: io.tty.Config,
831716 comptime printLineFromFile: anytype,
832717) !void {
833718 nosuspend {
......@@ -2193,7 +2078,7 @@ test "manage resources correctly" {
21932078 const writer = std.io.null_writer;
21942079 var di = try openSelfDebugInfo(testing.allocator);
21952080 defer di.deinit();
2196 try printSourceAtAddress(&di, writer, showMyTrace(), detectTTYConfig(std.io.getStdErr()));
2081 try printSourceAtAddress(&di, writer, showMyTrace(), io.tty.detectConfig(std.io.getStdErr()));
21972082}
21982083
21992084noinline fn showMyTrace() usize {
......@@ -2253,7 +2138,7 @@ pub fn ConfigurableTrace(comptime size: usize, comptime stack_frame_count: usize
22532138 pub fn dump(t: @This()) void {
22542139 if (!enabled) return;
22552140
2256 const tty_config = detectTTYConfig(std.io.getStdErr());
2141 const tty_config = io.tty.detectConfig(std.io.getStdErr());
22572142 const stderr = io.getStdErr().writer();
22582143 const end = @min(t.index, size);
22592144 const debug_info = getSelfDebugInfo() catch |err| {
lib/std/io.zig+2
......@@ -155,6 +155,8 @@ pub const BufferedAtomicFile = @import("io/buffered_atomic_file.zig").BufferedAt
155155
156156pub const StreamSource = @import("io/stream_source.zig").StreamSource;
157157
158pub const tty = @import("io/tty.zig");
159
158160/// A Writer that doesn't write to anything.
159161pub const null_writer = @as(NullWriter, .{ .context = {} });
160162
lib/std/io/tty.zig created+121
......@@ -0,0 +1,121 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const File = std.fs.File;
4const process = std.process;
5const windows = std.os.windows;
6const native_os = builtin.os.tag;
7
8/// Detect suitable TTY configuration options for the given file (commonly stdout/stderr).
9/// This includes feature checks for ANSI escape codes and the Windows console API, as well as
10/// respecting the `NO_COLOR` environment variable.
11pub fn detectConfig(file: File) Config {
12 if (builtin.os.tag == .wasi) {
13 // Per https://github.com/WebAssembly/WASI/issues/162 ANSI codes
14 // aren't currently supported.
15 return .no_color;
16 } else if (process.hasEnvVarConstant("ZIG_DEBUG_COLOR")) {
17 return .escape_codes;
18 } else if (process.hasEnvVarConstant("NO_COLOR")) {
19 return .no_color;
20 } else if (file.supportsAnsiEscapeCodes()) {
21 return .escape_codes;
22 } else if (native_os == .windows and file.isTty()) {
23 var info: windows.CONSOLE_SCREEN_BUFFER_INFO = undefined;
24 if (windows.kernel32.GetConsoleScreenBufferInfo(file.handle, &info) != windows.TRUE) {
25 // TODO: Should this return an error instead?
26 return .no_color;
27 }
28 return .{ .windows_api = .{
29 .handle = file.handle,
30 .reset_attributes = info.wAttributes,
31 } };
32 }
33 return .no_color;
34}
35
36pub const Color = enum {
37 red,
38 green,
39 yellow,
40 cyan,
41 white,
42 dim,
43 bold,
44 reset,
45};
46
47/// Provides simple functionality for manipulating the terminal in some way,
48/// such as coloring text, etc.
49pub const Config = union(enum) {
50 no_color,
51 escape_codes,
52 windows_api: if (native_os == .windows) WindowsContext else void,
53
54 pub const WindowsContext = struct {
55 handle: File.Handle,
56 reset_attributes: u16,
57 };
58
59 pub fn setColor(conf: Config, out_stream: anytype, color: Color) !void {
60 nosuspend switch (conf) {
61 .no_color => return,
62 .escape_codes => {
63 const color_string = switch (color) {
64 .red => "\x1b[31;1m",
65 .green => "\x1b[32;1m",
66 .yellow => "\x1b[33;1m",
67 .cyan => "\x1b[36;1m",
68 .white => "\x1b[37;1m",
69 .bold => "\x1b[1m",
70 .dim => "\x1b[2m",
71 .reset => "\x1b[0m",
72 };
73 try out_stream.writeAll(color_string);
74 },
75 .windows_api => |ctx| if (native_os == .windows) {
76 const attributes = switch (color) {
77 .red => windows.FOREGROUND_RED | windows.FOREGROUND_INTENSITY,
78 .green => windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY,
79 .yellow => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_INTENSITY,
80 .cyan => windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY,
81 .white, .bold => windows.FOREGROUND_RED | windows.FOREGROUND_GREEN | windows.FOREGROUND_BLUE | windows.FOREGROUND_INTENSITY,
82 .dim => windows.FOREGROUND_INTENSITY,
83 .reset => ctx.reset_attributes,
84 };
85 try windows.SetConsoleTextAttribute(ctx.handle, attributes);
86 } else {
87 unreachable;
88 },
89 };
90 }
91
92 pub fn writeDEC(conf: Config, writer: anytype, codepoint: u8) !void {
93 const bytes = switch (conf) {
94 .no_color, .windows_api => switch (codepoint) {
95 0x50...0x5e => @as(*const [1]u8, &codepoint),
96 0x6a => "+", // ┘
97 0x6b => "+", // ┐
98 0x6c => "+", // ┌
99 0x6d => "+", // └
100 0x6e => "+", // ┼
101 0x71 => "-", // ─
102 0x74 => "+", // ├
103 0x75 => "+", // ┤
104 0x76 => "+", // ┴
105 0x77 => "+", // ┬
106 0x78 => "|", // │
107 else => " ", // TODO
108 },
109 .escape_codes => switch (codepoint) {
110 // Here we avoid writing the DEC beginning sequence and
111 // ending sequence in separate syscalls by putting the
112 // beginning and ending sequence into the same string
113 // literals, to prevent terminals ending up in bad states
114 // in case a crash happens between syscalls.
115 inline 0x50...0x7f => |x| "\x1B\x28\x30" ++ [1]u8{x} ++ "\x1B\x28\x42",
116 else => unreachable,
117 },
118 };
119 return writer.writeAll(bytes);
120 }
121};
lib/std/testing.zig+4-4
......@@ -279,7 +279,7 @@ test "expectApproxEqRel" {
279279/// This function is intended to be used only in tests. When the two slices are not
280280/// equal, prints diagnostics to stderr to show exactly how they are not equal (with
281281/// the differences highlighted in red), then returns a test failure error.
282/// The colorized output is optional and controlled by the return of `std.debug.detectTTYConfig()`.
282/// The colorized output is optional and controlled by the return of `std.io.tty.detectConfig()`.
283283/// If your inputs are UTF-8 encoded strings, consider calling `expectEqualStrings` instead.
284284pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const T) !void {
285285 if (expected.ptr == actual.ptr and expected.len == actual.len) {
......@@ -312,7 +312,7 @@ pub fn expectEqualSlices(comptime T: type, expected: []const T, actual: []const
312312 const actual_window = actual[window_start..@min(actual.len, window_start + max_window_size)];
313313 const actual_truncated = window_start + actual_window.len < actual.len;
314314
315 const ttyconf = std.debug.detectTTYConfig(std.io.getStdErr());
315 const ttyconf = std.io.tty.detectConfig(std.io.getStdErr());
316316 var differ = if (T == u8) BytesDiffer{
317317 .expected = expected_window,
318318 .actual = actual_window,
......@@ -379,7 +379,7 @@ fn SliceDiffer(comptime T: type) type {
379379 start_index: usize,
380380 expected: []const T,
381381 actual: []const T,
382 ttyconf: std.debug.TTY.Config,
382 ttyconf: std.io.tty.Config,
383383
384384 const Self = @This();
385385
......@@ -398,7 +398,7 @@ fn SliceDiffer(comptime T: type) type {
398398const BytesDiffer = struct {
399399 expected: []const u8,
400400 actual: []const u8,
401 ttyconf: std.debug.TTY.Config,
401 ttyconf: std.io.tty.Config,
402402
403403 pub fn write(self: BytesDiffer, writer: anytype) !void {
404404 var expected_iterator = ChunkIterator{ .bytes = self.expected };
lib/std/zig/ErrorBundle.zig+2-2
......@@ -148,7 +148,7 @@ pub fn nullTerminatedString(eb: ErrorBundle, index: usize) [:0]const u8 {
148148}
149149
150150pub const RenderOptions = struct {
151 ttyconf: std.debug.TTY.Config,
151 ttyconf: std.io.tty.Config,
152152 include_reference_trace: bool = true,
153153 include_source_line: bool = true,
154154 include_log_text: bool = true,
......@@ -181,7 +181,7 @@ fn renderErrorMessageToWriter(
181181 err_msg_index: MessageIndex,
182182 stderr: anytype,
183183 kind: []const u8,
184 color: std.debug.TTY.Color,
184 color: std.io.tty.Color,
185185 indent: usize,
186186) anyerror!void {
187187 const ttyconf = options.ttyconf;
src/main.zig+2-2
......@@ -6044,9 +6044,9 @@ const ClangSearchSanitizer = struct {
60446044 };
60456045};
60466046
6047fn get_tty_conf(color: Color) std.debug.TTY.Config {
6047fn get_tty_conf(color: Color) std.io.tty.Config {
60486048 return switch (color) {
6049 .auto => std.debug.detectTTYConfig(std.io.getStdErr()),
6049 .auto => std.io.tty.detectConfig(std.io.getStdErr()),
60506050 .on => .escape_codes,
60516051 .off => .no_color,
60526052 };
test/src/Cases.zig+1-1
......@@ -1354,7 +1354,7 @@ fn runOneCase(
13541354 defer all_errors.deinit(allocator);
13551355 if (all_errors.errorMessageCount() > 0) {
13561356 all_errors.renderToStdErr(.{
1357 .ttyconf = std.debug.detectTTYConfig(std.io.getStdErr()),
1357 .ttyconf = std.io.tty.detectConfig(std.io.getStdErr()),
13581358 });
13591359 // TODO print generated C code
13601360 return error.UnexpectedCompileErrors;