authorgravatar for arthurcarvalhot@yahoo.com.brArthur Teixeira <arthurcarvalhot@yahoo.com.br> 2026-06-18 00:01:17+02:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2026-06-18 00:01:17+02:00
log0b22111bc94f6fc53f0565c386487ea772ae996c
tree7de4be69dd37cd5b6afb2aac1d97f9cba814dbf1
parent4f72106c859e71cc47bf53241387271ec3203a43

MinGW: remove dependency on a C preprocessor for MinGW .def.in files (#35679)

closes #31955 Reviewed-on: https://codeberg.org/ziglang/zig/pulls/35679 Reviewed-by: Ryan Liptak <squeek502@noreply.codeberg.org>

6 files changed, 1510 insertions(+), 47 deletions(-)

build.zig+20
...@@ -649,6 +649,26 @@ pub fn build(b: *std.Build) !void {...@@ -649,6 +649,26 @@ pub fn build(b: *std.Build) !void {
649 update_mingw_step.dependOn(&b.addFail("The -Dmingw-src=... option is required for this step").step);649 update_mingw_step.dependOn(&b.addFail("The -Dmingw-src=... option is required for this step").step);
650 }650 }
651651
652 const check_mingw_step = b.step("check-mingw", "Checks for mingw preprocessor regressions");
653 const mingw_preprocessor_mod = b.createModule(.{
654 .root_source_file = b.path("src/libs/mingw/Preprocessor.zig"),
655 .target = target,
656 });
657
658 const check_mingw_exe = b.addExecutable(.{
659 .name = "check_mingw",
660 .root_module = b.createModule(.{
661 .target = b.graph.host,
662 .root_source_file = b.path("tools/check_mingw.zig"),
663 .imports = &.{
664 .{ .name = "preprocessor", .module = mingw_preprocessor_mod },
665 },
666 }),
667 });
668 const check_mingw_run = b.addRunArtifact(check_mingw_exe);
669 check_mingw_run.addDirectoryArg(b.path("lib/libc/mingw"));
670 check_mingw_step.dependOn(&check_mingw_run.step);
671
652 const test_incremental_step = b.step("test-incremental", "Run the incremental compilation test cases");672 const test_incremental_step = b.step("test-incremental", "Run the incremental compilation test cases");
653 try tests.addIncrementalTests(b, test_incremental_step, test_filters);673 try tests.addIncrementalTests(b, test_incremental_step, test_filters);
654 if (!skip_test_incremental) test_step.dependOn(test_incremental_step);674 if (!skip_test_incremental) test_step.dependOn(test_incremental_step);
src/libs/mingw.zig+19-47
...@@ -14,9 +14,12 @@ const dev = @import("../dev.zig");...@@ -14,9 +14,12 @@ const dev = @import("../dev.zig");
14const def = @import("mingw/def.zig");14const def = @import("mingw/def.zig");
15const implib = @import("mingw/implib.zig");15const implib = @import("mingw/implib.zig");
1616
17const Preprocessor = @import("mingw/Preprocessor.zig");
18
17test {19test {
18 _ = def;20 _ = def;
19 _ = implib;21 _ = implib;
22 _ = Preprocessor;
20}23}
2124
22pub const CrtFile = enum {25pub const CrtFile = enum {
...@@ -273,22 +276,6 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {...@@ -273,22 +276,6 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
273 var o_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, o_sub_path, .{});276 var o_dir = try comp.dirs.global_cache.handle.createDirPathOpen(io, o_sub_path, .{});
274 defer o_dir.close(io);277 defer o_dir.close(io);
275278
276 const aro = @import("aro");
277 var diagnostics: aro.Diagnostics = .{
278 .output = .{ .to_list = .{ .arena = .init(gpa) } },
279 };
280 defer diagnostics.deinit();
281 var aro_comp = try aro.Compilation.init(.{
282 .gpa = gpa,
283 .arena = arena,
284 .io = io,
285 .diagnostics = &diagnostics,
286 .environ_map = null,
287 });
288 defer aro_comp.deinit();
289
290 aro_comp.target = .fromZigTarget(target.*);
291
292 const include_dir = try comp.dirs.zig_lib.join(arena, &.{ "libc", "mingw", "def-include" });279 const include_dir = try comp.dirs.zig_lib.join(arena, &.{ "libc", "mingw", "def-include" });
293280
294 if (comp.verbose_cc) {281 if (comp.verbose_cc) {
...@@ -304,39 +291,24 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {...@@ -304,39 +291,24 @@ pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void {
304 };291 };
305 }292 }
306293
307 try aro_comp.search_path.append(gpa, .{ .path = include_dir, .kind = .normal });
308
309 const builtin_macros = try aro_comp.generateBuiltinMacros(.include_system_defines);
310 const def_file_source = try aro_comp.addSourceFromPath(def_file_path);
311
312 var pp = try aro.Preprocessor.init(&aro_comp, .{ .base_file = .unused });
313 defer pp.deinit();
314 pp.linemarkers = .none;
315 pp.preserve_whitespace = true;
316
317 try pp.preprocessSources(.{ .main = def_file_source, .builtin = builtin_macros });
318
319 if (aro_comp.diagnostics.output.to_list.messages.items.len != 0) {
320 var buffer: [64]u8 = undefined;
321 const stderr = try io.lockStderr(&buffer, null);
322 defer io.unlockStderr();
323 for (aro_comp.diagnostics.output.to_list.messages.items) |msg| {
324 if (msg.kind == .@"fatal error" or msg.kind == .@"error") {
325 msg.write(stderr.terminal(), true) catch |err| switch (err) {
326 error.WriteFailed => return stderr.file_writer.err.?,
327 error.Canceled, error.Unexpected => |e| return e,
328 };
329 return error.AroPreprocessorFailed;
330 }
331 }
332 }
333
334 const members = members: {294 const members = members: {
335 var aw: Io.Writer.Allocating = .init(gpa);295 const input = pp: {
336 errdefer aw.deinit();296 var aw: Io.Writer.Allocating = .init(gpa);
337 try pp.prettyPrintTokens(&aw.writer, .result_only);297 errdefer aw.deinit();
298
299 var pp_arena = std.heap.ArenaAllocator.init(gpa);
300 defer pp_arena.deinit();
301 var pp: Preprocessor = .{
302 .io = io,
303 .arena = pp_arena.allocator(),
304 .include_dir = include_dir,
305 .target = target,
306 };
307 try pp.preprocess(def_file_path);
308 try pp.prettyPrintTokens(&aw.writer);
338309
339 const input = try aw.toOwnedSliceSentinel(0);310 break :pp try aw.toOwnedSliceSentinel(0);
311 };
340 defer gpa.free(input);312 defer gpa.free(input);
341313
342 const machine_type = target.toCoffMachine();314 const machine_type = target.toCoffMachine();
src/libs/mingw/Preprocessor.zig created+975
...@@ -0,0 +1,975 @@
1const std = @import("std");
2const Tokenizer = @import("./Tokenizer.zig");
3const Allocator = std.mem.Allocator;
4const Token = Tokenizer.Token;
5const mem = std.mem;
6const assert = std.debug.assert;
7
8test {
9 _ = Tokenizer;
10}
11
12const TokenList = std.MultiArrayList(Token);
13const RawTokenList = std.ArrayList(Token);
14
15const ExpandBuf = std.ArrayList(Token);
16
17const Preprocessor = @This();
18const DefineMap = std.StringArrayHashMapUnmanaged(Macro);
19
20const GeneratedTokens = std.ArrayList(u8);
21
22const MacroArgument = []const Token;
23
24pub const Source = struct {
25 pub const generated: Source.Id = std.math.maxInt(usize);
26 pub const Id = usize;
27 id: Id = generated,
28 path: []const u8,
29 buf: []const u8,
30};
31
32sources: std.StringArrayHashMapUnmanaged(Source) = .empty,
33
34arena: Allocator,
35io: std.Io,
36include_dir: []const u8,
37
38top_expansion_buf: ExpandBuf = .empty,
39add_expansion_nl: usize = 0,
40token_buf: RawTokenList = .empty,
41generated_tokens: GeneratedTokens = .empty,
42generated_line: u32 = 1,
43defines: DefineMap = .empty,
44tokens: TokenList = .empty,
45target: *const std.Target,
46
47const Macro = struct {
48 param: []const u8,
49 tokens: []const Token,
50 is_func: bool,
51};
52
53const IfContext = struct {
54 const Backing = u2;
55 const Nesting = enum(Backing) {
56 until_else,
57 until_endif,
58 until_endif_seen_else,
59 };
60
61 const buf_size_bits = @bitSizeOf(Backing) * 256;
62 kind: [buf_size_bits / std.mem.byte_size_in_bits]u8,
63 level: u8,
64
65 fn get(self: *const IfContext) Nesting {
66 return @enumFromInt(std.mem.readPackedInt(Backing, &self.kind, @as(usize, self.level) * 2, .native));
67 }
68
69 fn set(self: *IfContext, context: Nesting) void {
70 std.mem.writePackedInt(Backing, &self.kind, @as(usize, self.level) * 2, @intFromEnum(context), .native);
71 }
72
73 fn increment(self: *IfContext) void {
74 self.level += 1;
75 }
76
77 fn decrement(self: *IfContext) void {
78 self.level -= 1;
79 }
80
81 const default: IfContext = .{ .kind = @splat(0xFF), .level = 0 };
82};
83
84fn addToken(pp: *Preprocessor, tok: Token) !void {
85 try pp.tokens.append(pp.arena, tok);
86}
87
88fn addTokenAssumeCapacity(pp: *Preprocessor, tok: Token) void {
89 pp.tokens.appendAssumeCapacity(tok);
90}
91
92fn defineBuiltins(pp: *Preprocessor) !void {
93 var buf: [5]u8 = undefined;
94 var val = std.fmt.bufPrint(&buf, "{d}", .{pp.target.cTypeBitSize(.longdouble)}) catch unreachable;
95 try pp.defineBuiltinValue("__SIZEOF_LONG_DOUBLE__", val, .pp_num);
96 val = std.fmt.bufPrint(&buf, "{d}", .{pp.target.cTypeBitSize(.double)}) catch unreachable;
97 try pp.defineBuiltinValue("__SIZEOF_DOUBLE__", val, .pp_num);
98
99 if (pp.target.abi.isGnu()) {
100 try pp.defineBuiltinValue("__cdecl", "__attribute__((__cdecl__))", .identifier);
101 }
102
103 const arch = switch (pp.target.cpu.arch) {
104 .aarch64 => "__aarch64__",
105 .x86 => "__i386__",
106 .x86_64 => "__x86_64__",
107 .arm, .thumb => "__arm__",
108 else => return error.ArchitectureNotSupported,
109 };
110 try pp.defineBuiltin(arch);
111}
112
113fn defineBuiltinValue(pp: *Preprocessor, name: []const u8, value: []const u8, id: Token.Id) !void {
114 const start = pp.generated_tokens.items.len;
115 try pp.generated_tokens.appendSlice(pp.arena, value);
116 const end = pp.generated_tokens.items.len;
117
118 const token_list = try pp.arena.alloc(Token, 1);
119 token_list[0] = .{ .source = Source.generated, .id = id, .start = @intCast(start), .end = @intCast(end) };
120 try pp.defines.putNoClobber(pp.arena, name, .{
121 .is_func = false,
122 .param = "",
123 .tokens = token_list,
124 });
125}
126
127fn defineBuiltin(pp: *Preprocessor, name: []const u8) !void {
128 return pp.defines.putNoClobber(pp.arena, name, .{
129 .tokens = &.{},
130 .param = "",
131 .is_func = false,
132 });
133}
134
135pub fn preprocess(pp: *Preprocessor, file_path: []const u8) !void {
136 const source = try pp.addSourceFromPath(file_path);
137 try pp.preprocessFile(source);
138}
139
140fn preprocessFile(pp: *Preprocessor, src: Source) !void {
141 try pp.defineBuiltins();
142 const eof = try pp.preprocessFileExtra(src);
143 try pp.addToken(eof);
144}
145
146fn preprocessFileExtra(pp: *Preprocessor, src: Source) !Token {
147 var tokenizer: Tokenizer = .init(src.buf, src.id);
148 var if_context: IfContext = .default;
149
150 while (true) {
151 var tok = tokenizer.next();
152 switch (tok.id) {
153 .hash => {
154 const directive = tokenizer.nextNoWS();
155 switch (directive.id) {
156 .keyword_define => try pp.define(&tokenizer),
157 .keyword_if => {
158 if_context.increment();
159 if (try pp.expr(&tokenizer)) {
160 if_context.set(.until_endif);
161 } else {
162 if_context.set(.until_else);
163 try pp.skip(&tokenizer, .until_else);
164 }
165 },
166 .keyword_ifdef => {
167 if_context.increment();
168 const macro_name = pp.expectMacroName(&tokenizer);
169 skipToNl(&tokenizer);
170 if (pp.defines.get(macro_name) != null) {
171 if_context.set(.until_endif);
172 } else {
173 if_context.set(.until_else);
174 try pp.skip(&tokenizer, .until_else);
175 }
176 },
177 .keyword_ifndef => {
178 if_context.increment();
179 const macro_name = pp.expectMacroName(&tokenizer);
180 skipToNl(&tokenizer);
181 if (pp.defines.get(macro_name) == null) {
182 if_context.set(.until_endif);
183 } else {
184 if_context.set(.until_else);
185 try pp.skip(&tokenizer, .until_else);
186 }
187 },
188 .keyword_elif => {
189 assert(if_context.level > 0);
190 switch (if_context.get()) {
191 .until_else => if (try pp.expr(&tokenizer)) {
192 if_context.set(.until_endif);
193 } else {
194 try pp.skip(&tokenizer, .until_else);
195 },
196 .until_endif => try pp.skip(&tokenizer, .until_endif),
197 .until_endif_seen_else => unreachable, //elif after endif
198 }
199 },
200 .keyword_else => {
201 skipToNl(&tokenizer);
202 assert(if_context.level > 0);
203 switch (if_context.get()) {
204 .until_else => if_context.set(.until_endif_seen_else),
205 .until_endif => try pp.skip(&tokenizer, .until_endif),
206 .until_endif_seen_else => unreachable, // else after else
207 }
208 },
209 .keyword_endif => {
210 skipToNl(&tokenizer);
211 assert(if_context.level > 0);
212 if_context.decrement();
213 },
214 .keyword_undef => {
215 const macro_name = tokenizer.nextNoWS();
216 assert(macro_name.id == .identifier);
217 pp.undefineMacro(macro_name);
218 skipToNl(&tokenizer);
219 },
220 .keyword_include => {
221 try pp.include(&tokenizer);
222 continue;
223 },
224 .keyword_defined, .keyword_error => {},
225 else => unreachable,
226 }
227 tok.id = .nl;
228 try pp.addToken(tok);
229 },
230 .whitespace, .nl => try pp.addToken(tok),
231 .eof => {
232 assert(if_context.level == 0);
233 return tok;
234 },
235 else => try pp.expandMacro(&tokenizer, tok),
236 }
237 }
238}
239
240fn include(pp: *Preprocessor, tokenizer: *Tokenizer) anyerror!void {
241 const first = tokenizer.nextNoWS();
242 const src = try findIncludeSource(pp, tokenizer, first);
243
244 _ = try pp.preprocessFileExtra(src);
245 if (pp.tokens.items(.id)[pp.tokens.len - 1] != .nl) {
246 try pp.addToken(.{ .id = .nl, .source = Source.generated });
247 }
248}
249
250fn findIncludeSource(
251 pp: *Preprocessor,
252 tokenizer: *Tokenizer,
253 first: Token,
254) !Source {
255 const filename_tok = first;
256 skipToNl(tokenizer);
257 const tok_slice = pp.expandToken(filename_tok);
258 assert(tok_slice.len >= 3);
259 const filename = tok_slice[1 .. tok_slice.len - 1];
260 return (try pp.findInclude(filename, first)) orelse @panic("include not found");
261}
262
263fn expectMacroName(pp: *const Preprocessor, tokenizer: *Tokenizer) []const u8 {
264 const macro_name = tokenizer.nextNoWS();
265 assert(macro_name.id.isMacroIdentifier());
266 return pp.expandToken(macro_name);
267}
268
269fn skipToNl(tokenizer: *Tokenizer) void {
270 while (true) {
271 const tok = tokenizer.next();
272 if (tok.id == .nl or tok.id == .eof) return;
273 if (tok.id == .whitespace) continue;
274 }
275}
276
277fn define(pp: *Preprocessor, tokenizer: *Tokenizer) !void {
278 const macro_name = tokenizer.nextNoWS();
279 assert(macro_name.id == .identifier);
280
281 const first = tokenizer.nextNoWS();
282 switch (first.id) {
283 .nl, .eof => return pp.defineMacro(macro_name, .{
284 .is_func = false,
285 .tokens = &.{},
286 .param = "",
287 }),
288 .l_paren => return pp.defineFn(tokenizer, macro_name),
289 else => {},
290 }
291}
292
293fn defineMacro(pp: *Preprocessor, tok: Token, macro: Macro) !void {
294 const token_value = pp.expandToken(tok);
295 try pp.defines.putNoClobber(pp.arena, token_value, macro);
296}
297
298fn undefineMacro(pp: *Preprocessor, tok: Token) void {
299 const token_value = pp.expandToken(tok);
300 _ = pp.defines.orderedRemove(token_value);
301}
302
303fn defineFn(
304 pp: *Preprocessor,
305 tokenizer: *Tokenizer,
306 macro_name: Token,
307) !void {
308 var tok = tokenizer.nextNoWS();
309 assert(tok.id == .identifier);
310 const param = pp.expandToken(tok);
311 tok = tokenizer.nextNoWS();
312 assert(tok.id == .r_paren);
313
314 pp.token_buf.items.len = 0;
315 var need_ws = false;
316 while (true) {
317 tok = tokenizer.next();
318 switch (tok.id) {
319 .nl, .eof => break,
320 .whitespace => need_ws = pp.token_buf.items.len != 0,
321 .hash => unreachable,
322 .hash_hash => {
323 need_ws = false;
324 try pp.token_buf.append(pp.arena, tok);
325 },
326 else => {
327 if (need_ws) {
328 need_ws = false;
329 try pp.token_buf.append(pp.arena, .{ .id = .whitespace, .source = Source.generated });
330 }
331
332 if (tok.id.isMacroIdentifier()) {
333 tok.id = .identifier;
334 const s = pp.expandToken(tok);
335 if (mem.eql(u8, param, s)) {
336 tok.id = .macro_param;
337 tok.end = 0;
338 }
339 }
340 try pp.token_buf.append(pp.arena, tok);
341 },
342 }
343 }
344
345 const token_list = try pp.arena.dupe(Token, pp.token_buf.items);
346 try pp.defineMacro(macro_name, .{
347 .tokens = token_list,
348 .is_func = true,
349 .param = param,
350 });
351}
352
353fn expandToken(pp: *const Preprocessor, tok: Token) []const u8 {
354 return switch (tok.source) {
355 Source.generated => pp.generated_tokens.items,
356 else => blk: {
357 const src = pp.sources.values()[tok.source];
358 break :blk src.buf;
359 },
360 }[@intCast(tok.start)..@intCast(tok.end)];
361}
362
363fn skip(
364 pp: *Preprocessor,
365 tokenizer: *Tokenizer,
366 cont: IfContext.Nesting,
367) !void {
368 var ifs_seen: u32 = 0;
369 var line_start = true;
370 while (tokenizer.index < tokenizer.buf.len) {
371 if (line_start) {
372 const tokenizer_bkp = tokenizer.*;
373 const hash = tokenizer.nextNoWS();
374 if (hash.id == .nl) continue;
375 line_start = false;
376 if (hash.id != .hash) continue;
377 const directive = tokenizer.nextNoWS();
378 switch (directive.id) {
379 .keyword_else => {
380 if (ifs_seen != 0) continue;
381 assert(cont != .until_endif_seen_else); // else after else;
382 tokenizer.* = tokenizer_bkp;
383 return;
384 },
385 .keyword_elif => {
386 if (ifs_seen != 0 or cont == .until_endif) continue;
387 assert(cont != .until_endif_seen_else); // elif after else;
388 tokenizer.* = tokenizer_bkp;
389 return;
390 },
391 .keyword_endif => {
392 if (ifs_seen == 0) {
393 tokenizer.* = tokenizer_bkp;
394 return;
395 }
396 ifs_seen -= 1;
397 },
398 .keyword_if, .keyword_ifdef, .keyword_ifndef => ifs_seen += 1,
399 else => {},
400 }
401 } else if (tokenizer.buf[tokenizer.index] == '\n') {
402 line_start = true;
403 tokenizer.index += 1;
404 try pp.addToken(.{ .id = .nl, .source = Source.generated });
405 } else {
406 line_start = false;
407 tokenizer.index += 1;
408 }
409 }
410}
411
412fn ensureUnusedTokenCapacity(pp: *Preprocessor, capacity: usize) !void {
413 try pp.tokens.ensureUnusedCapacity(pp.arena, capacity);
414}
415
416fn expr(pp: *Preprocessor, tokenizer: *Tokenizer) !bool {
417 const token_state = pp.tokens.len;
418 defer pp.tokens.len = token_state;
419
420 pp.top_expansion_buf.items.len = 0;
421 while (true) {
422 const tok = tokenizer.next();
423 switch (tok.id) {
424 .nl, .eof => break,
425 .whitespace => if (pp.top_expansion_buf.items.len == 0) continue,
426 else => {},
427 }
428 try pp.top_expansion_buf.append(pp.arena, tok);
429 } else unreachable;
430 if (pp.top_expansion_buf.items.len != 0) {
431 try pp.expandMacroExhaustive(tokenizer, &pp.top_expansion_buf, 0, pp.top_expansion_buf.items.len, false, .expr);
432 }
433 try pp.ensureUnusedTokenCapacity(pp.top_expansion_buf.items.len);
434 var i: usize = 0;
435 const items = pp.top_expansion_buf.items;
436 while (i < items.len) : (i += 1) {
437 var tok = items[i];
438 switch (tok.id) {
439 .string_literal,
440 .semicolon,
441 .hash_hash,
442 => unreachable,
443 .whitespace => continue,
444 else => if (tok.id == .keyword_defined) {
445 i += try pp.handleKeywordDefined(&tok, items[i + 1 ..]);
446 },
447 }
448 pp.addTokenAssumeCapacity(tok);
449 }
450
451 try pp.addToken(.{ .id = .eof, .source = Source.generated });
452 return pp.evalExpression(token_state);
453}
454
455fn handleKeywordDefined(
456 pp: *Preprocessor,
457 macro_tok: *Token,
458 tokens: []const Token,
459) !usize {
460 assert(macro_tok.id == .keyword_defined);
461 var it = TokenIterator.init(tokens);
462
463 _ = it.expectNoWS(.l_paren);
464 const second = it.expectNoWS(.identifier);
465 _ = it.expectNoWS(.r_paren);
466
467 macro_tok.id = if (pp.defines.contains(pp.expandToken(second))) .one else .zero;
468
469 return it.i;
470}
471
472const TokenIterator = struct {
473 toks: []const Token,
474 i: usize,
475
476 fn init(toks: []const Token) TokenIterator {
477 return .{ .toks = toks, .i = 0 };
478 }
479
480 fn nextNoWS(self: *TokenIterator) ?Token {
481 while (self.i < self.toks.len) : (self.i += 1) {
482 const tok = self.toks[self.i];
483 if (tok.id == .whitespace) continue;
484
485 self.i += 1;
486 return tok;
487 }
488 return null;
489 }
490
491 fn expectNext(self: *TokenIterator) Token {
492 assert(self.i < self.toks.len);
493 const t = self.toks[self.i];
494 self.i += 1;
495 return t;
496 }
497
498 fn expectNoWS(self: *TokenIterator, expected: Token.Id) Token {
499 if (self.nextNoWS()) |tok| {
500 if (tok.id != expected) {
501 std.debug.panic("expected token {any} but got {any}\n", .{ expected, tok.id });
502 }
503 return tok;
504 }
505 std.debug.panic("expected token {any} but got null\n", .{expected});
506 }
507};
508
509fn expandMacro(pp: *Preprocessor, tokenizer: *Tokenizer, tok: Token) !void {
510 if (!tok.id.isMacroIdentifier()) {
511 return pp.addToken(tok);
512 }
513 pp.top_expansion_buf.items.len = 0;
514 try pp.top_expansion_buf.append(pp.arena, tok);
515 try pp.expandMacroExhaustive(tokenizer, &pp.top_expansion_buf, 0, 1, true, .non_expr);
516 try pp.addTokensFromExpandBuf(pp.top_expansion_buf.items, .{ .id = .nl, .source = Source.generated });
517}
518
519fn addTokensFromExpandBuf(pp: *Preprocessor, tokens: []Token, tokenizer_nl: Token) !void {
520 try pp.ensureUnusedTokenCapacity(tokens.len);
521 for (tokens) |tok| {
522 pp.addTokenAssumeCapacity(tok);
523 }
524 try pp.ensureUnusedTokenCapacity(pp.add_expansion_nl);
525 while (pp.add_expansion_nl > 0) : (pp.add_expansion_nl -= 1) {
526 pp.addTokenAssumeCapacity(tokenizer_nl);
527 }
528}
529
530const EvalContext = enum {
531 expr,
532 non_expr,
533};
534
535fn expandMacroExhaustive(
536 pp: *Preprocessor,
537 tokenizer: *Tokenizer,
538 buf: *ExpandBuf,
539 start_idx: usize,
540 end_idx: usize,
541 extend_buf: bool,
542 eval_ctx: EvalContext,
543) !void {
544 var moving_end_idx = end_idx;
545 var advance_index: usize = 0;
546 var do_rescan = true;
547 while (do_rescan) {
548 do_rescan = false;
549 var idx: usize = start_idx + advance_index;
550 while (idx < moving_end_idx) {
551 const macro_tok = buf.items[idx];
552 if (macro_tok.id == .keyword_defined and eval_ctx == .expr) {
553 idx += 1;
554 var it = TokenIterator.init(buf.items[idx..moving_end_idx]);
555 if (it.nextNoWS()) |tok| {
556 switch (tok.id) {
557 .l_paren => {
558 _ = it.nextNoWS();
559 _ = it.nextNoWS();
560 },
561 else => {},
562 }
563 }
564 idx += it.i;
565 continue;
566 }
567 if (!macro_tok.id.isMacroIdentifier()) {
568 idx += 1;
569 continue;
570 }
571 const expanded = pp.expandToken(macro_tok);
572 const macro = pp.defines.getPtr(expanded) orelse {
573 idx += 1;
574 continue;
575 };
576
577 if (macro.is_func) {
578 var macro_scan_idx = idx;
579 const arg = try pp.collectMacroArgument(
580 tokenizer,
581 buf,
582 &macro_scan_idx,
583 &moving_end_idx,
584 extend_buf,
585 );
586 const expanded_arg = arg: {
587 var expand_buf: ExpandBuf = .empty;
588 errdefer expand_buf.deinit(pp.arena);
589 try expand_buf.appendSlice(pp.arena, arg);
590 try pp.expandMacroExhaustive(tokenizer, &expand_buf, 0, expand_buf.items.len, false, eval_ctx);
591 break :arg try expand_buf.toOwnedSlice(pp.arena);
592 };
593
594 const res = try pp.expandFuncMacro(macro, arg, expanded_arg);
595 const tokens_added = res.items.len;
596 const tokens_removed = macro_scan_idx - idx + 1;
597 try buf.replaceRange(pp.arena, idx, tokens_removed, res.items);
598
599 moving_end_idx += tokens_added;
600 moving_end_idx -|= tokens_removed;
601 idx += tokens_added;
602 do_rescan = true;
603 } else {
604 var res = try pp.expandObjMacro(macro);
605 defer res.deinit(pp.arena);
606 var increment_idx_by = res.items.len;
607
608 for (res.items, 0..) |*tok, i| {
609 if (i < increment_idx_by and pp.defines.contains(pp.expandToken(tok.*))) {
610 increment_idx_by = i;
611 }
612 }
613 try buf.replaceRange(pp.arena, idx, 1, res.items);
614 idx += res.items.len;
615 moving_end_idx = moving_end_idx + res.items.len - 1;
616 do_rescan = true;
617 }
618 if (idx - start_idx == advance_index + 1 and !do_rescan) {
619 advance_index += 1;
620 }
621 }
622 }
623 buf.items.len = moving_end_idx;
624}
625
626fn collectMacroArgument(
627 pp: *Preprocessor,
628 tokenizer: *Tokenizer,
629 buf: *ExpandBuf,
630 start_idx: *usize,
631 end_idx: *usize,
632 extend_buf: bool,
633) !MacroArgument {
634 var parens: u32 = 0;
635 var argument: std.ArrayList(Token) = .empty;
636 defer argument.deinit(pp.arena);
637
638 while (true) {
639 const tok = try nextBufToken(pp, tokenizer, buf, start_idx, end_idx, extend_buf);
640 switch (tok.id) {
641 .nl, .whitespace => {},
642 .l_paren => break,
643 else => unreachable,
644 }
645 }
646
647 while (true) {
648 const tok = try nextBufToken(pp, tokenizer, buf, start_idx, end_idx, extend_buf);
649 switch (tok.id) {
650 .l_paren => {
651 try argument.append(pp.arena, tok);
652 parens += 1;
653 },
654 .r_paren => {
655 if (parens == 0) {
656 return try argument.toOwnedSlice(pp.arena);
657 } else {
658 try argument.append(pp.arena, tok);
659 parens -= 1;
660 }
661 },
662 .nl, .whitespace => try argument.append(pp.arena, .{ .id = .whitespace, .source = Source.generated }),
663 .eof => unreachable,
664 else => try argument.append(pp.arena, tok),
665 }
666 }
667}
668
669fn expandObjMacro(pp: *Preprocessor, simple_macro: *const Macro) !ExpandBuf {
670 var buf: ExpandBuf = .empty;
671 errdefer buf.deinit(pp.arena);
672 try buf.appendSlice(pp.arena, simple_macro.tokens);
673 return buf;
674}
675
676fn expandFuncMacro(
677 pp: *Preprocessor,
678 func_macro: *const Macro,
679 arg: MacroArgument,
680 expanded_arg: MacroArgument,
681) !ExpandBuf {
682 var buf: ExpandBuf = .empty;
683 errdefer buf.deinit(pp.arena);
684 try buf.ensureTotalCapacity(pp.arena, func_macro.tokens.len);
685
686 var tok_i: usize = 0;
687 while (tok_i < func_macro.tokens.len) : (tok_i += 1) {
688 const tok = func_macro.tokens[tok_i];
689 switch (tok.id) {
690 .hash_hash => while (tok_i + 1 < func_macro.tokens.len) {
691 tok_i += 1;
692 const tok_next = func_macro.tokens[tok_i];
693 const next = switch (tok_next.id) {
694 .whitespace => continue,
695 .hash_hash => continue,
696 .macro_param => arg,
697 else => &[1]Token{tok_next},
698 };
699 try pp.pasteTokens(&buf, next);
700 if (next.len != 0) break;
701 },
702 .macro_param => {
703 try buf.appendSlice(pp.arena, expanded_arg);
704 },
705 else => try buf.append(pp.arena, tok),
706 }
707 }
708
709 return buf;
710}
711
712fn pasteTokens(
713 pp: *Preprocessor,
714 lhs_toks: *ExpandBuf,
715 rhs_toks: []const Token,
716) !void {
717 const lhs = while (lhs_toks.pop()) |lhs| {
718 if (lhs.id != .whitespace) break lhs;
719 } else {
720 return lhs_toks.appendSlice(pp.arena, rhs_toks);
721 };
722
723 var rhs_rest: u32 = 1;
724 const rhs = for (rhs_toks) |rhs| {
725 if (rhs.id != .whitespace) break rhs;
726 rhs_rest += 1;
727 } else {
728 return lhs_toks.appendAssumeCapacity(lhs);
729 };
730
731 const start = pp.generated_tokens.items.len;
732 const end = start + pp.expandToken(lhs).len + pp.expandToken(rhs).len;
733 try pp.generated_tokens.ensureTotalCapacity(pp.arena, end + 1);
734 pp.generated_tokens.appendSliceAssumeCapacity(pp.expandToken(lhs));
735 pp.generated_tokens.appendSliceAssumeCapacity(pp.expandToken(rhs));
736 pp.generated_tokens.appendAssumeCapacity('\n');
737
738 var tmp_tokenizer: Tokenizer = .{
739 .index = @intCast(start),
740 .buf = pp.generated_tokens.items,
741 .source = Source.generated,
742 };
743 const pasted_token = tmp_tokenizer.nextNoWS();
744 const next = tmp_tokenizer.nextNoWS();
745
746 try lhs_toks.append(pp.arena, pp.makeGeneratedToken(start, end, pasted_token.id));
747 assert(next.id == .nl or next.id == .eof);
748
749 return lhs_toks.appendSlice(pp.arena, rhs_toks[rhs_rest..]);
750}
751
752fn nextBufToken(
753 pp: *Preprocessor,
754 tokenizer: *Tokenizer,
755 buf: *ExpandBuf,
756 start_idx: *usize,
757 end_idx: *usize,
758 extend_buf: bool,
759) !Token {
760 start_idx.* += 1;
761 if (start_idx.* == buf.items.len and start_idx.* >= end_idx.*) {
762 if (extend_buf) {
763 const tok = tokenizer.next();
764 if (tok.id == .nl) pp.add_expansion_nl += 1;
765
766 end_idx.* += 1;
767 try buf.append(pp.arena, tok);
768 return tok;
769 }
770 return .{ .id = .eof, .source = Source.generated };
771 }
772
773 return buf.items[start_idx.*];
774}
775
776fn makeGeneratedToken(
777 pp: *Preprocessor,
778 start: usize,
779 end: usize,
780 id: Token.Id,
781) Token {
782 const pasted_token: Token = .{
783 .id = id,
784 .source = Source.generated,
785 .start = @intCast(start),
786 .end = @intCast(end),
787 };
788 pp.generated_line += 1;
789 return pasted_token;
790}
791
792fn findInclude(
793 pp: *Preprocessor,
794 filename: []const u8,
795 includer_token: Token,
796) !?Source {
797 const other_file = pp.sources.values()[includer_token.source].path;
798 const dir = std.fs.path.dirname(other_file) orelse ".";
799 if (try pp.checkIncludeDir(filename, dir)) |res| return res;
800
801 return pp.checkIncludeDir(filename, pp.include_dir);
802}
803
804fn checkIncludeDir(
805 pp: *Preprocessor,
806 include_path: []const u8,
807 include_dir: []const u8,
808) !?Source {
809 const format = "{s}{c}{s}";
810 var bfa_buf: [1024]u8 = undefined;
811 var bfa_state: std.heap.BufferFirstAllocator = .init(&bfa_buf, pp.arena);
812 const bfa = bfa_state.allocator();
813 const header_path = try std.fmt.allocPrint(bfa, format, .{
814 include_dir,
815 std.fs.path.sep,
816 include_path,
817 });
818 defer bfa.free(header_path);
819
820 return pp.addSourceFromPath(header_path) catch |err| switch (err) {
821 error.OutOfMemory => |e| return e,
822 else => return null,
823 };
824}
825
826pub fn addSourceFromPath(pp: *Preprocessor, path: []const u8) !Source {
827 if (pp.sources.get(path)) |src| return src;
828 try pp.sources.ensureUnusedCapacity(pp.arena, 1);
829
830 const contents = try std.Io.Dir.cwd().readFileAlloc(pp.io, path, pp.arena, .limited(std.math.maxInt(u32)));
831 const duped_path = try pp.arena.dupe(u8, path);
832
833 const src: Source = .{
834 .buf = contents,
835 .path = duped_path,
836 .id = pp.sources.count(),
837 };
838
839 pp.sources.putAssumeCapacityNoClobber(duped_path, src);
840 return src;
841}
842
843fn evalExpression(
844 pp: *Preprocessor,
845 start: usize,
846) !bool {
847 const s = pp.tokens.slice();
848 const len = s.len - start;
849 const ss = s.subslice(start, len);
850
851 const ids: []Token.Id = ss.items(.id);
852 const starts: []u32 = ss.items(.start);
853 const ends: []u32 = ss.items(.end);
854 const srcs: []usize = ss.items(.source);
855
856 var toks = try pp.arena.alloc(Token, len);
857 defer pp.arena.free(toks);
858
859 for (0..len) |i| {
860 toks[i] = .{
861 .id = ids[i],
862 .source = srcs[i],
863 .start = starts[i],
864 .end = ends[i],
865 };
866 }
867
868 return pp.evaluateExpressionTokens(toks);
869}
870
871fn evaluateExpressionTokens(
872 pp: *const Preprocessor,
873 toks: []const Token,
874) bool {
875 var it = TokenIterator.init(toks);
876
877 const left = evalToken(&it);
878 assert(!left.id.isInfix());
879
880 const op = evalToken(&it);
881 if (op.id == .eof) return left.id == .one;
882
883 assert(op.id.isInfix());
884 const right = evalToken(&it);
885
886 return pp.evalInfix(left, op, right);
887}
888
889fn evalToken(it: *TokenIterator) Token {
890 const tok = it.expectNext();
891 if (tok.id != .bang) {
892 return tok;
893 }
894
895 var op = it.expectNext();
896 const flipped: Token.Id = switch (op.id) {
897 .one => .zero,
898 .zero => .one,
899 else => unreachable,
900 };
901 op.id = flipped;
902 return op;
903}
904
905fn evalInfix(
906 pp: *const Preprocessor,
907 left: Token,
908 op: Token,
909 right: Token,
910) bool {
911 switch (op.id) {
912 .pipe_pipe => return (left.id == .one) or (right.id == .one),
913 .equal_equal => {
914 switch (left.id) {
915 .one, .zero => {
916 assert(right.id == .one or right.id == .zero);
917 return left.id == right.id;
918 },
919 .pp_num => {
920 assert(right.id == .pp_num);
921 const lval = pp.expandToken(left);
922 const rval = pp.expandToken(right);
923 return std.mem.eql(u8, lval, rval);
924 },
925 else => unreachable,
926 }
927 },
928 else => unreachable,
929 }
930}
931
932pub fn prettyPrintTokens(pp: *Preprocessor, w: *std.Io.Writer) !void {
933 const tok_ids = pp.tokens.items(.id);
934 var i: usize = 0;
935 var last_nl = true;
936 outer: while (true) : (i += 1) {
937 const cur: Token = pp.tokens.get(i);
938 switch (cur.id) {
939 .eof => {
940 if (!last_nl) try w.writeByte('\n');
941 try w.flush();
942 return;
943 },
944 .nl => {
945 var newlines: u32 = 0;
946 for (tok_ids[i..], i..) |id, j| {
947 if (id == .nl) {
948 newlines += 1;
949 } else if (id == .eof) {
950 if (!last_nl) try w.writeByte('\n');
951 try w.flush();
952 return;
953 } else if (id != .whitespace) {
954 if (newlines < 2) break;
955
956 i = @intCast((j - 1) - @intFromBool(tok_ids[j - 1] == .whitespace));
957 if (!last_nl) try w.writeAll("\n");
958 continue :outer;
959 }
960 }
961 last_nl = true;
962 try w.writeAll("\n");
963 },
964 .whitespace => {
965 try w.writeByte(' ');
966 last_nl = false;
967 },
968 else => {
969 const slice = pp.expandToken(cur);
970 try w.writeAll(slice);
971 last_nl = false;
972 },
973 }
974 }
975}
src/libs/mingw/Tokenizer.zig created+365
...@@ -0,0 +1,365 @@
1const std = @import("std");
2const assert = std.debug.assert;
3const Source = @import("Preprocessor.zig").Source;
4
5const Tokenizer = @This();
6
7pub fn init(buf: []const u8, source: Source.Id) Tokenizer {
8 return .{ .buf = buf, .source = source };
9}
10
11buf: []const u8,
12index: u32 = 0,
13source: Source.Id,
14
15pub const Token = struct {
16 pub const Id = enum {
17 bang,
18 eof,
19 equal_equal,
20 hash,
21 hash_hash,
22 macro_param,
23 identifier,
24 keyword_if,
25 keyword_ifndef,
26 keyword_ifdef,
27 keyword_define,
28 keyword_endif,
29 keyword_defined,
30 keyword_include,
31 keyword_elif,
32 keyword_else,
33 keyword_undef,
34 keyword_error,
35 l_paren,
36 nl,
37 pp_num,
38 pipe_pipe,
39 r_paren,
40 semicolon,
41 string_literal,
42 whitespace,
43 one,
44 zero,
45
46 pub fn isInfix(id: Id) bool {
47 switch (id) {
48 .pipe_pipe, .equal_equal => return true,
49 else => return false,
50 }
51 }
52
53 pub fn isMacroIdentifier(id: Id) bool {
54 switch (id) {
55 .keyword_if,
56 .keyword_ifndef,
57 .keyword_ifdef,
58 .keyword_define,
59 .keyword_endif,
60 .keyword_defined,
61 .keyword_include,
62 .keyword_elif,
63 .keyword_else,
64 .keyword_undef,
65 .keyword_error,
66 .identifier,
67 => return true,
68 else => return false,
69 }
70 }
71 };
72
73 const all_kws = std.StaticStringMap(Id).initComptime(.{
74 .{ "define", .keyword_define },
75 .{ "defined", .keyword_defined },
76 .{ "else", .keyword_else },
77 .{ "endif", .keyword_endif },
78 .{ "if", .keyword_if },
79 .{ "elif", .keyword_elif },
80 .{ "ifdef", .keyword_ifdef },
81 .{ "ifndef", .keyword_ifndef },
82 .{ "include", .keyword_include },
83 .{ "undef", .keyword_undef },
84 .{ "error", .keyword_error },
85 });
86
87 id: Id,
88 source: Source.Id,
89 start: u32 = 0,
90 end: u32 = 0,
91
92 fn getTokenId(str: []const u8) Id {
93 return all_kws.get(str) orelse .identifier;
94 }
95};
96
97pub fn next(self: *Tokenizer) Token {
98 var state: enum {
99 start,
100 cr,
101 string_literal,
102 identifier,
103 equal,
104 slash,
105 line_comment,
106 hash,
107 pipe,
108 pp_num,
109 } = .start;
110
111 const start = self.index;
112 var id: Token.Id = .eof;
113
114 while (self.index < self.buf.len) : (self.index += 1) {
115 const c = self.buf[self.index];
116 switch (state) {
117 .start => switch (c) {
118 '\r' => {
119 id = .nl;
120 state = .cr;
121 },
122 '\n' => {
123 id = .nl;
124 self.index += 1;
125 break;
126 },
127 '!' => {
128 id = .bang;
129 self.index += 1;
130 break;
131 },
132 '"' => {
133 id = .string_literal;
134 state = .string_literal;
135 },
136 '|' => state = .pipe,
137 '=' => state = .equal,
138 '(' => {
139 id = .l_paren;
140 self.index += 1;
141 break;
142 },
143 ')' => {
144 id = .r_paren;
145 self.index += 1;
146 break;
147 },
148 ';' => {
149 id = .semicolon;
150 self.index += 1;
151 break;
152 },
153 '/' => state = .slash,
154 '#' => state = .hash,
155 '0'...'9' => state = .pp_num,
156 ' ' => {
157 id = .whitespace;
158 self.index += 1;
159 break;
160 },
161 else => state = .identifier,
162 },
163 .cr => switch (c) {
164 '\n' => {
165 self.index += 1;
166 break;
167 },
168 else => break,
169 },
170 .pipe => switch (c) {
171 '|' => {
172 id = .pipe_pipe;
173 self.index += 1;
174 break;
175 },
176 else => unreachable,
177 },
178 .hash => switch (c) {
179 '#' => {
180 id = .hash_hash;
181 self.index += 1;
182 break;
183 },
184 else => {
185 id = .hash;
186 break;
187 },
188 },
189 .string_literal => switch (c) {
190 '"' => {
191 self.index += 1;
192 break;
193 },
194 else => {},
195 },
196 .identifier => switch (c) {
197 'a'...'z', 'A'...'Z', '_', '0'...'9' => {},
198 else => {
199 id = Token.getTokenId(self.buf[start..self.index]);
200 break;
201 },
202 },
203 .equal => switch (c) {
204 '=' => {
205 id = .equal_equal;
206 self.index += 1;
207 break;
208 },
209 else => unreachable,
210 },
211 .slash => switch (c) {
212 '/' => state = .line_comment,
213 else => {
214 id = .identifier;
215 break;
216 },
217 },
218 .line_comment => switch (c) {
219 '\n' => {
220 self.index -= 1;
221 state = .start;
222 },
223 else => {},
224 },
225 .pp_num => switch (c) {
226 '0'...'9' => {},
227 else => {
228 id = .pp_num;
229 break;
230 },
231 },
232 }
233 } else if (self.index == self.buf.len) {
234 switch (state) {
235 .start, .line_comment, .cr => {},
236 .identifier => id = Token.getTokenId(self.buf[start..self.index]),
237 .hash => id = .hash,
238 .pp_num => id = .pp_num,
239 else => unreachable,
240 }
241 }
242
243 return .{
244 .id = id,
245 .start = start,
246 .end = self.index,
247 .source = self.source,
248 };
249}
250
251pub fn nextNoWS(self: *Tokenizer) Token {
252 var tok = self.next();
253 while (tok.id == .whitespace) tok = self.next();
254 return tok;
255}
256
257pub fn nextNoWSComments(self: *Tokenizer) Token {
258 var tok = self.next();
259 while (tok.id == .whitespace) tok = self.next();
260 return tok;
261}
262
263fn expectToken(expected: Token.Id, actual: Token) !void {
264 try std.testing.expectEqual(expected, actual.id);
265}
266
267fn testToken(buf: []const u8, expected: Token.Id) !void {
268 var tokenizer = Tokenizer.init(buf, Source.generated);
269 const t = tokenizer.next();
270 try expectToken(expected, t);
271 try expectToken(.eof, tokenizer.next());
272}
273
274test "tokens" {
275 try testToken("TEST", .identifier);
276 try testToken("__x86_64__", .identifier);
277 try testToken("122", .pp_num);
278 try testToken("==", .equal_equal);
279 try testToken("#", .hash);
280 try testToken("##", .hash_hash);
281 try testToken("undef", .keyword_undef);
282 try testToken("||", .pipe_pipe);
283 try testToken("!", .bang);
284 try testToken("else", .keyword_else);
285 try testToken("endif", .keyword_endif);
286 try testToken("include", .keyword_include);
287 try testToken("define", .keyword_define);
288 try testToken("defined", .keyword_defined);
289 try testToken("if", .keyword_if);
290 try testToken("ifdef", .keyword_ifdef);
291 try testToken("ifndef", .keyword_ifndef);
292 try testToken("(", .l_paren);
293 try testToken("\n", .nl);
294 try testToken("\r", .nl);
295 try testToken("\r\n", .nl);
296 try testToken("5", .pp_num);
297 try testToken(")", .r_paren);
298 try testToken("\"str\"", .string_literal);
299 try testToken(" ", .whitespace);
300}
301
302fn expectTokens(contents: []const u8, expected_tokens: []const Token.Id) !void {
303 var tokenizer: Tokenizer = .init(contents, Source.generated);
304 var i: usize = 0;
305 while (i < expected_tokens.len) {
306 const token = tokenizer.next();
307 if (token.id == .whitespace) continue;
308 const expected_token_id = expected_tokens[i];
309 i += 1;
310 if (!std.meta.eql(token.id, expected_token_id)) {
311 std.debug.print("expected {s}, found {s}\n", .{ @tagName(expected_token_id), @tagName(token.id) });
312 return error.TokensDoNotEqual;
313 }
314 }
315 const last_token = tokenizer.next();
316 try std.testing.expect(last_token.id == .eof);
317}
318
319test "preprocessor keywords" {
320 try expectTokens(
321 \\#if
322 \\#ifndef
323 \\#ifdef
324 \\#define
325 \\#endif
326 \\defined
327 \\#include
328 \\#elif
329 \\#else
330 \\#undef
331 \\#error
332 , &.{
333 .hash,
334 .keyword_if,
335 .nl,
336 .hash,
337 .keyword_ifndef,
338 .nl,
339 .hash,
340 .keyword_ifdef,
341 .nl,
342 .hash,
343 .keyword_define,
344 .nl,
345 .hash,
346 .keyword_endif,
347 .nl,
348 .keyword_defined,
349 .nl,
350 .hash,
351 .keyword_include,
352 .nl,
353 .hash,
354 .keyword_elif,
355 .nl,
356 .hash,
357 .keyword_else,
358 .nl,
359 .hash,
360 .keyword_undef,
361 .nl,
362 .hash,
363 .keyword_error,
364 });
365}
test/standalone/build.zig+9
...@@ -31,6 +31,7 @@ pub fn build(b: *std.Build) void {...@@ -31,6 +31,7 @@ pub fn build(b: *std.Build) void {
31 const tools_target = b.resolveTargetQuery(.{});31 const tools_target = b.resolveTargetQuery(.{});
32 for ([_][]const u8{32 for ([_][]const u8{
33 // Alphabetically sorted. No need to build `tools/spirv/grammar.zig`.33 // Alphabetically sorted. No need to build `tools/spirv/grammar.zig`.
34 "../../tools/check_mingw.zig",
34 "../../tools/dump-cov.zig",35 "../../tools/dump-cov.zig",
35 "../../tools/fetch_them_macos_headers.zig",36 "../../tools/fetch_them_macos_headers.zig",
36 "../../tools/gen_macos_headers_c.zig",37 "../../tools/gen_macos_headers_c.zig",
...@@ -61,6 +62,14 @@ pub fn build(b: *std.Build) void {...@@ -61,6 +62,14 @@ pub fn build(b: *std.Build) void {
61 .target = tools_target,62 .target = tools_target,
62 }),63 }),
63 });64 });
65 if (std.mem.endsWith(u8, tool_src_path, "check_mingw.zig")) {
66 const mingw_preprocessor_mod = b.createModule(.{
67 .root_source_file = b.path("../../src/libs/mingw/Preprocessor.zig"),
68 .target = tools_target,
69 });
70 tool.root_module.addImport("preprocessor", mingw_preprocessor_mod);
71 }
72
64 tools_tests_step.dependOn(&tool.step);73 tools_tests_step.dependOn(&tool.step);
65 }74 }
66 for ([_][]const u8{75 for ([_][]const u8{
tools/check_mingw.zig created+122
...@@ -0,0 +1,122 @@
1const std = @import("std");
2const Io = std.Io;
3const Dir = Io.Dir;
4const Preprocessor = @import("preprocessor");
5
6pub fn main(init: std.process.Init) !void {
7 const arena = init.arena.allocator();
8 const io = init.io;
9 const args = try init.minimal.args.toSlice(arena);
10
11 const zig_src_mingw_lib_path = args[1];
12
13 const mingw_include_path = try Dir.path.join(arena, &.{
14 zig_src_mingw_lib_path, "def-include",
15 });
16 const mingw_libcommon_path = try Dir.path.join(arena, &.{
17 zig_src_mingw_lib_path, "lib-common",
18 });
19
20 var mingw_libcommon_dir = Dir.cwd().openDir(io, mingw_libcommon_path, .{ .iterate = true }) catch |err| {
21 std.log.err("unable to open directory {s}: {t}", .{ mingw_libcommon_path, err });
22 std.process.exit(1);
23 };
24 defer mingw_libcommon_dir.close(io);
25
26 var walker = try mingw_libcommon_dir.walk(arena);
27 defer walker.deinit();
28
29 while (try walker.next(io)) |entry| {
30 if (entry.kind != .file) continue;
31
32 var fail = false;
33 for (&targets) |*target| {
34 var target_arena: std.heap.ArenaAllocator = .init(init.gpa);
35 defer target_arena.deinit();
36
37 const pp_arena = target_arena.allocator();
38 const file_path = try Dir.path.join(pp_arena, &.{ mingw_libcommon_path, entry.path });
39
40 const aro = pp: {
41 const target_triple = try target.zigTriple(pp_arena);
42 const target_arg = try std.fmt.allocPrint(pp_arena, "--target={s}", .{target_triple});
43 const result = std.process.run(pp_arena, io, .{
44 .argv = &.{
45 "arocc",
46 "-E",
47 target_arg,
48 "--no-line-commands",
49 "-nostdinc",
50 "-I",
51 mingw_include_path,
52 file_path,
53 },
54 }) catch |err| {
55 std.log.err("unable to execute arocc: {t}", .{err});
56 std.process.exit(1);
57 };
58 if (result.term.exited != 0) {
59 std.log.err("error executing arocc: {s}", .{result.stderr});
60 std.process.exit(result.term.exited);
61 }
62 break :pp result.stdout;
63 };
64
65 const native = pp: {
66 var aw: Io.Writer.Allocating = .init(pp_arena);
67 errdefer aw.deinit();
68
69 var pp: Preprocessor = .{
70 .io = io,
71 .arena = pp_arena,
72 .include_dir = mingw_include_path,
73 .target = target,
74 };
75
76 pp.preprocess(file_path) catch |err| {
77 std.log.err("error preprocessing file {s} for target {t}: {t}", .{ entry.path, target.cpu.arch, err });
78 fail = true;
79 continue;
80 };
81 pp.prettyPrintTokens(&aw.writer) catch |err| {
82 std.log.err("error printing tokens for file {s} for target {t}: {t}", .{ entry.path, target.cpu.arch, err });
83 fail = true;
84 continue;
85 };
86
87 break :pp try aw.toOwnedSliceSentinel(0);
88 };
89
90 try std.testing.expectEqualStrings(aro, native);
91 }
92
93 if (fail) std.process.exit(1);
94 }
95}
96
97const targets = [_]std.Target{
98 .{
99 .ofmt = .coff,
100 .abi = .gnu,
101 .os = .{ .tag = .windows, .version_range = .default(.thumb, .windows, .gnu) },
102 .cpu = .{ .arch = .thumb, .model = .generic(.thumb), .features = .empty },
103 },
104 .{
105 .ofmt = .coff,
106 .abi = .gnu,
107 .os = .{ .tag = .windows, .version_range = .default(.aarch64, .windows, .gnu) },
108 .cpu = .{ .arch = .aarch64, .model = .generic(.aarch64), .features = .empty },
109 },
110 .{
111 .ofmt = .coff,
112 .abi = .gnu,
113 .os = .{ .tag = .windows, .version_range = .default(.x86, .windows, .gnu) },
114 .cpu = .{ .arch = .x86, .model = .generic(.x86), .features = .empty },
115 },
116 .{
117 .ofmt = .coff,
118 .abi = .gnu,
119 .os = .{ .tag = .windows, .version_range = .default(.x86_64, .windows, .gnu) },
120 .cpu = .{ .arch = .x86_64, .model = .generic(.x86_64), .features = .empty },
121 },
122};