The ABI version of the build system protocol. Will be bumped whenever a backwards incompatible changes to the protocol is made.
Does not apply to the internal compiler protocol or test runner.
See version in Message.Handshake.
pub fn receiveMessage(s: *Server) !InMessage.Headerpub fn receiveBody_u8(s: *Server) !u8pub fn receiveBody_u32(s: *Server) !u32pub fn receiveBody_u64(s: *Server) !u64pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !voidpub fn serveMessageHeader(s: *const Server, header: OutMessage.Header) !voidDon't forget to flush!
pub fn serveBodylessMessage(s: *const Server, tag: OutMessage.Tag) Writer.Error!voidpub fn serveU32Message(s: *const Server, tag: OutMessage.Tag, int: u32) !voidpub fn serveU64Message(s: *const Server, tag: OutMessage.Tag, int: u64) !voidpub fn serveCoverageIdMessage(s: *const Server, id: u64, runs: u64, unique: u64, cov: u64) !voidpub fn serveBroadcastFuzzInputMessage(s: *const Server, test_i: u32, bytes: []const u8) !voidpub fn serveEmitDigest(s: *Server, digest: *const [Cache.bin_digest_len]u8, header: OutMessage.EmitDigest) !voidpub fn serveTestResults(s: *Server, msg: OutMessage.TestResults) !voidpub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !voidpub fn allocErrorBundle(gpa: Allocator, body: []const u8) errorpub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370const Server = @This();
const std = @import("std");
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
const Cache = std.Build.Cache;
const OutMessage = std.zig.Server.Message;
const InMessage = std.zig.Client.Message;
const Reader = std.Io.Reader;
const Writer = std.Io.Writer;
in: *Reader,
out: *Writer,
/// The ABI version of the build system protocol. Will be bumped whenever a
/// backwards incompatible changes to the protocol is made.
///
/// Does not apply to the internal compiler protocol or test runner.
///
/// See `version` in `Message.Handshake`.
pub const build_system_version: u32 = 1;
pub const Message = struct {
pub const Header = extern struct {
tag: Tag,
/// Size of the body only; does not include this Header.
bytes_len: u32,
};
pub const Tag = enum(u32) {
/// Body is a UTF-8 string.
zig_version,
/// Body is an ErrorBundle.
error_bundle,
/// Body is a EmitDigest.
emit_digest,
/// Body is a TestMetadata
test_metadata,
/// Body is a TestResults
test_results,
/// Does not have a body.
/// Notifies the build runner that the next test (requested by `Client.Message.Tag.run_test`)
/// is starting execution. This message helps to ensure that the timestamp used by the build
/// runner to enforce unit test time limits is relatively accurate under extreme system load
/// (where there may be a non-trivial delay before the test process is scheduled).
test_started,
/// Body is a series of strings, delimited by null bytes.
/// Each string is a prefixed file path.
/// The first byte indicates the file prefix path (see prefixes fields
/// of Cache). This byte is sent over the wire incremented so that null
/// bytes are not confused with string terminators.
/// The remaining bytes is the file path relative to that prefix.
/// The prefixes are hard-coded in Compilation.create (cwd, zig lib dir, local cache dir)
file_system_inputs,
/// Body is:
/// - a u64le that indicates the file path within the cache used
/// to store coverage information. The integer is a hash of the PCs
/// stored within that file.
/// - u64le of total runs accumulated
/// - u64le of unique runs accumulated
/// - u64le of coverage accumulated
coverage_id,
/// Body is a u64le that indicates the function pointer virtual memory
/// address of the fuzz unit test. This is used to provide a starting
/// point to view coverage.
fuzz_start_addr,
/// Body is:
/// - u32le test index.
fuzz_test_change,
/// Body is:
/// - u32le test index
/// - input in remaining bytes
broadcast_fuzz_input,
/// Body is a TimeReport.
time_report,
/// The first message sent by the server over the build system protocol.
/// Body is a `Handshake`.
/// This message only applies to the build system protocol.
bsp_handshake = 0x80000000,
/// Notifies that a new configuration file is available.
/// Body is a cwd relative path to the configuration file.
/// This message only applies to the build system protocol.
bsp_configuration,
/// Does not have a body.
/// This message only applies to the build system protocol.
bsp_build_started,
/// Does not have a body.
/// This message only applies to the build system protocol.
bsp_build_completed,
/// Body is a `Configuration.Step.Index`.
/// This message only applies to the build system protocol.
bsp_step_started,
/// Body is a `BuildStepCompleted`.
/// This message only applies to the build system protocol.
bsp_step_completed,
_,
};
/// Trailing:
/// * base_paths: BasePaths,
pub const Handshake = extern struct {
/// See `build_system_version`.
version: u32,
flags: Flags,
pub const Flags = packed struct(u32) {
file_system_watch_supported: bool,
_: u31 = 0,
};
};
/// Trailing:
/// * error_bundle: ErrorBundle,
pub const BuildStepCompleted = extern struct {
step_index: std.Build.Configuration.Step.Index,
status: Status,
error_bundle: ErrorBundle,
// TODO result_error_msgs
// TODO result_stderr
// TODO result_peak_rss
// TODO result_duration_ns
pub const Status = enum(u32) {
success,
failure,
skipped,
skipped_oom,
};
};
pub const PathPrefix = enum(u8) {
cwd,
zig_lib,
local_cache,
global_cache,
build_root,
};
/// Trailing:
/// * extra: [extra_len]u32,
/// * string_bytes: [string_bytes_len]u8,
/// See `std.zig.ErrorBundle`.
pub const ErrorBundle = extern struct {
extra_len: u32,
string_bytes_len: u32,
};
/// Trailing:
/// * name: [tests_len]u32
/// - null-terminated string_bytes index
/// * expected_panic_msg: [tests_len]u32,
/// - null-terminated string_bytes index
/// - 0 means does not expect panic
/// * string_bytes: [string_bytes_len]u8,
pub const TestMetadata = extern struct {
string_bytes_len: u32,
tests_len: u32,
};
pub const TestResults = extern struct {
index: u32,
flags: Flags align(4),
pub const Flags = packed struct(u64) {
status: Status,
fuzz: bool,
log_err_count: u30,
leak_count: u31,
};
pub const Status = enum(u2) { pass, fail, skip };
};
/// Trailing is the same as in `std.Build.abi.time_report.CompileResult`, excluding `step_name`.
pub const TimeReport = extern struct {
stats: std.Build.abi.time_report.CompileResult.Stats align(4),
llvm_pass_timings_len: u32,
files_len: u32,
decls_len: u32,
flags: Flags,
pub const Flags = packed struct(u32) {
use_llvm: bool,
_: u31 = 0,
};
};
/// Trailing:
/// * the hex digest of the cache directory within the /o/ subdirectory.
pub const EmitDigest = extern struct {
flags: Flags,
pub const Flags = packed struct(u8) {
cache_hit: bool,
reserved: u7 = 0,
};
};
};
pub fn receiveMessage(s: *Server) !InMessage.Header {
return s.in.takeStruct(InMessage.Header, .little);
}
pub fn receiveBody_u8(s: *Server) !u8 {
return s.in.takeInt(u8, .little);
}
pub fn receiveBody_u32(s: *Server) !u32 {
return s.in.takeInt(u32, .little);
}
pub fn receiveBody_u64(s: *Server) !u64 {
return s.in.takeInt(u64, .little);
}
pub fn serveStringMessage(s: *Server, tag: OutMessage.Tag, msg: []const u8) !void {
try s.serveMessageHeader(.{
.tag = tag,
.bytes_len = @intCast(msg.len),
});
try s.out.writeAll(msg);
try s.out.flush();
}
/// Don't forget to flush!
pub fn serveMessageHeader(s: *const Server, header: OutMessage.Header) !void {
try s.out.writeStruct(header, .little);
}
pub fn serveBodylessMessage(s: *const Server, tag: OutMessage.Tag) Writer.Error!void {
try s.serveMessageHeader(.{ .tag = tag, .bytes_len = 0 });
try s.out.flush();
}
pub fn serveU32Message(s: *const Server, tag: OutMessage.Tag, int: u32) !void {
try serveMessageHeader(s, .{
.tag = tag,
.bytes_len = @sizeOf(u32),
});
try s.out.writeInt(u32, int, .little);
try s.out.flush();
}
pub fn serveU64Message(s: *const Server, tag: OutMessage.Tag, int: u64) !void {
assert(tag != .coverage_id);
try serveMessageHeader(s, .{
.tag = tag,
.bytes_len = @sizeOf(u64),
});
try s.out.writeInt(u64, int, .little);
try s.out.flush();
}
pub fn serveCoverageIdMessage(s: *const Server, id: u64, runs: u64, unique: u64, cov: u64) !void {
try serveMessageHeader(s, .{
.tag = .coverage_id,
.bytes_len = @sizeOf(u64) + @sizeOf(u64) + @sizeOf(u64) + @sizeOf(u64),
});
try s.out.writeInt(u64, id, .little);
try s.out.writeInt(u64, runs, .little);
try s.out.writeInt(u64, unique, .little);
try s.out.writeInt(u64, cov, .little);
try s.out.flush();
}
pub fn serveBroadcastFuzzInputMessage(s: *const Server, test_i: u32, bytes: []const u8) !void {
try s.serveMessageHeader(.{
.tag = .broadcast_fuzz_input,
.bytes_len = @sizeOf(u32) + @as(u32, @intCast(bytes.len)),
});
try s.out.writeInt(u32, test_i, .little);
try s.out.writeAll(bytes);
try s.out.flush();
}
pub fn serveEmitDigest(
s: *Server,
digest: *const [Cache.bin_digest_len]u8,
header: OutMessage.EmitDigest,
) !void {
try s.serveMessageHeader(.{
.tag = .emit_digest,
.bytes_len = @intCast(digest.len + @sizeOf(OutMessage.EmitDigest)),
});
try s.out.writeStruct(header, .little);
try s.out.writeAll(digest);
try s.out.flush();
}
pub fn serveTestResults(s: *Server, msg: OutMessage.TestResults) !void {
try s.serveMessageHeader(.{
.tag = .test_results,
.bytes_len = @intCast(@sizeOf(OutMessage.TestResults)),
});
try s.out.writeStruct(msg, .little);
try s.out.flush();
}
pub fn serveErrorBundle(s: *Server, error_bundle: std.zig.ErrorBundle) !void {
const eb_hdr: OutMessage.ErrorBundle = .{
.extra_len = @intCast(error_bundle.extra.len),
.string_bytes_len = @intCast(error_bundle.string_bytes.len),
};
const bytes_len = @sizeOf(OutMessage.ErrorBundle) +
4 * error_bundle.extra.len + error_bundle.string_bytes.len;
try s.serveMessageHeader(.{
.tag = .error_bundle,
.bytes_len = @intCast(bytes_len),
});
try s.out.writeStruct(eb_hdr, .little);
try s.out.writeSliceEndian(u32, error_bundle.extra, .little);
try s.out.writeAll(error_bundle.string_bytes);
try s.out.flush();
}
pub fn allocErrorBundle(gpa: Allocator, body: []const u8) error{ OutOfMemory, EndOfStream }!std.zig.ErrorBundle {
var r: Reader = .fixed(body);
const hdr = r.takeStruct(OutMessage.ErrorBundle, .little) catch |err| switch (err) {
error.EndOfStream => |e| return e,
error.ReadFailed => unreachable,
};
var eb: std.zig.ErrorBundle = .{
.string_bytes = &.{},
.extra = &.{},
};
errdefer eb.deinit(gpa);
const extra = try gpa.alloc(u32, hdr.extra_len);
eb.extra = extra;
const string_bytes = try gpa.alloc(u8, hdr.string_bytes_len);
eb.string_bytes = string_bytes;
r.readSliceEndian(u32, extra, .little) catch |err| switch (err) {
error.EndOfStream => |e| return e,
error.ReadFailed => unreachable,
};
r.readSliceAll(string_bytes) catch |err| switch (err) {
error.EndOfStream => |e| return e,
error.ReadFailed => unreachable,
};
return eb;
}
pub const TestMetadata = struct {
names: []const u32,
expected_panic_msgs: []const u32,
string_bytes: []const u8,
};
pub fn serveTestMetadata(s: *Server, test_metadata: TestMetadata) !void {
const header: OutMessage.TestMetadata = .{
.tests_len = @intCast(test_metadata.names.len),
.string_bytes_len = @intCast(test_metadata.string_bytes.len),
};
const trailing = 2;
const bytes_len = @sizeOf(OutMessage.TestMetadata) +
trailing * @sizeOf(u32) * test_metadata.names.len + test_metadata.string_bytes.len;
try s.serveMessageHeader(.{
.tag = .test_metadata,
.bytes_len = @intCast(bytes_len),
});
try s.out.writeStruct(header, .little);
try s.out.writeSliceEndian(u32, test_metadata.names, .little);
try s.out.writeSliceEndian(u32, test_metadata.expected_panic_msgs, .little);
try s.out.writeAll(test_metadata.string_bytes);
try s.out.flush();
}