authorgravatar for mega.alpha100@gmail.comBernard Assan <mega.alpha100@gmail.com> 2026-06-29 19:43:51+00:00
committergravatar for squeek502@hotmail.comRyan Liptak <squeek502@hotmail.com> 2026-08-02 06:32:19+02:00
log8523ee09ae1f03d93d8029263c7bb7fe0ec282e7
tree157ba33a5fd5cae19c889817787ccbe71d019a11
parent6db520a4cd1ce2391c79d0d55b2b2d5297e133a3

Implement Meson ConfigHeader support

updated PR for the new build system changes add standalone tests for meson config_header Signed-off-by: Bernard Assan <mega.alpha100@gmail.com>

6 files changed, 207 insertions(+), 1 deletions(-)

lib/compiler/Maker/Step/ConfigHeader.zig+137
......@@ -93,6 +93,32 @@ pub fn make(
9393 else => |e| return e,
9494 };
9595 },
96 .meson => {
97 const tf = template_file.?;
98 const contents = tf.root_dir.handle.readFileAlloc(
99 io,
100 tf.sub_path,
101 arena,
102 input_size_limit,
103 ) catch |err| return step.fail(
104 maker,
105 "unable to read meson input file {f}: {t}",
106 .{ tf, err },
107 );
108
109 renderMeson(
110 maker,
111 step,
112 contents,
113 &aw,
114 value_pairs,
115 &value_map,
116 tf,
117 ) catch |err| switch (err) {
118 error.WriteFailed => return error.OutOfMemory,
119 else => |e| return e,
120 };
121 },
96122 .blank => {
97123 renderBlank(conf, &aw.writer, value_pairs, &value_map, include_path, include_guard_override) catch |err| switch (err) {
98124 error.WriteFailed => return error.OutOfMemory,
......@@ -370,6 +396,62 @@ fn renderCmake(
370396 if (any_errors) return error.MakeFailed;
371397}
372398
399fn renderMeson(
400 maker: *Maker,
401 step: *Step,
402 contents: []const u8,
403 aw: *Writer.Allocating,
404 value_pairs: []const Value.Pair,
405 value_map: *const ValueMap,
406 src_path: Path,
407) !void {
408 const w = &aw.writer;
409 const conf = &maker.scanned_config.configuration;
410 const newline = detectNewline(contents);
411
412 try w.writeAll(c_generated_line);
413 try w.writeAll(newline);
414
415 var any_errors = false;
416 var line_index: u32 = 0;
417 var line_it = std.mem.splitScalar(u8, contents, '\n');
418 // https://mesonbuild.com/Configuration.html
419 while (line_it.next()) |raw_line| : (line_index += 1) {
420 const last_line = line_it.index == line_it.buffer.len;
421 const line = std.mem.trimEnd(u8, raw_line, "\r");
422
423 const old_len = aw.written().len;
424 expandVariablesMeson(w, conf, line, value_pairs, value_map) catch |err| switch (err) {
425 error.MissingToken => {
426 try step.addError(maker, "{f}:{d}: error: missing define name", .{ src_path, line_index + 1 });
427 any_errors = true;
428 continue;
429 },
430 error.MissingValue => {
431 const name = aw.written()[old_len..];
432 defer aw.shrinkRetainingCapacity(old_len);
433
434 try step.addError(maker, "{f}:{d}: error: unspecified config header value: {q}", .{
435 src_path, line_index + 1, name,
436 });
437 any_errors = true;
438 continue;
439 },
440 else => {
441 try step.addError(maker, "{f}:{d}: unable to substitute variable: error: {t}", .{
442 src_path, line_index + 1, err,
443 });
444 any_errors = true;
445 continue;
446 },
447 };
448 if (!last_line) try w.writeAll(newline);
449 }
450
451 try ensureAllValuesUsed(maker, step, value_map, src_path);
452 if (any_errors) return error.MakeFailed;
453}
454
373455fn renderBlank(
374456 conf: *const Configuration,
375457 w: *Writer,
......@@ -432,6 +514,28 @@ fn renderValueC(conf: *const Configuration, w: *Writer, newline: []const u8, nam
432514 }
433515}
434516
517fn renderValueMeson(
518 conf: *const Configuration,
519 w: *Writer,
520 name: []const u8,
521 value: Value.Index,
522) !void {
523 switch (value.unpack(conf)) {
524 .undef => try w.print("/* #undef {s} */", .{name}),
525 .defined => try w.print("#define {s}", .{name}),
526 .bool => |b| {
527 if (b) {
528 try w.print("#define {s}", .{name});
529 } else {
530 try w.print("#undef {s}", .{name});
531 }
532 },
533 inline .u64, .i64 => |int| try w.print("#define {s} {d}", .{ name, int }),
534 .ident => |ident| try w.print("#define {s} {s}", .{ name, ident }),
535 .string => |string| try w.print("#define {s} \"{f}\"", .{ name, std.zig.fmtString(string) }),
536 }
537}
538
435539fn renderValueCIdent(w: *Writer, newline: []const u8, name: []const u8, ident: []const u8) Writer.Error!void {
436540 try w.print("#define {s}", .{name});
437541 if (ident.len > 0) {
......@@ -628,3 +732,36 @@ fn expandVariablesCmake(
628732
629733 return result.toOwnedSliceAssert();
630734}
735
736fn expandVariablesMeson(
737 w: *Writer,
738 conf: *const Configuration,
739 line: []const u8,
740 value_pairs: []const Value.Pair,
741 value_map: *const ValueMap,
742) !void {
743 const mesondefine = "#mesondefine";
744 if (std.mem.startsWith(u8, line, mesondefine)) {
745 const line_offset = mesondefine.len + 1;
746 if (line_offset > line.len) return error.MissingToken;
747
748 var it = std.mem.tokenizeAny(u8, line[line_offset..], " \t\r");
749 const name = it.next() orelse return error.MissingToken;
750
751 const index = value_map.getIndex(name) orelse {
752 // Report the missing key to the caller.
753 try w.writeAll(name);
754 return error.MissingValue;
755 };
756
757 const value = value_pairs[index].index;
758 value_map.values()[index] = true; // Mark as used.
759
760 try renderValueMeson(conf, w, name, value);
761
762 // comments/any other text passthrough unaffected
763 return try w.writeAll(line[line_offset + name.len ..]);
764 }
765
766 try expandVariablesAutoconfAt(w, line, conf, value_pairs, value_map);
767}
lib/std/Build/Configuration.zig+2
......@@ -1076,6 +1076,7 @@ pub const Step = extern struct {
10761076 autoconf_undef,
10771077 autoconf_at,
10781078 cmake,
1079 meson,
10791080 blank,
10801081 nasm,
10811082
......@@ -1084,6 +1085,7 @@ pub const Step = extern struct {
10841085 .autoconf_undef => .autoconf_undef,
10851086 .autoconf_at => .autoconf_at,
10861087 .cmake => .cmake,
1088 .meson => .meson,
10871089 .blank => .blank,
10881090 .nasm => .nasm,
10891091 };
lib/std/Build/Step/ConfigHeader.zig+4-1
......@@ -27,6 +27,9 @@ pub const Style = union(enum) {
2727 /// The configure format supported by CMake. It uses `@FOO@`, `${}` and
2828 /// `#cmakedefine` for template substitution.
2929 cmake: std.Build.LazyPath,
30 /// The configure format supported by Meson. It uses `@FOO@`, and
31 /// `#mesondefine` for template substitution.
32 meson: std.Build.LazyPath,
3033 /// Instead of starting with an input file, start with nothing.
3134 blank,
3235 /// Start with nothing, like blank, and output a nasm .asm file.
......@@ -34,7 +37,7 @@ pub const Style = union(enum) {
3437
3538 pub fn getPath(style: Style) ?std.Build.LazyPath {
3639 switch (style) {
37 .autoconf_undef, .autoconf_at, .cmake => |s| return s,
40 .autoconf_undef, .autoconf_at, .cmake, .meson => |s| return s,
3841 .blank, .nasm => return null,
3942 }
4043 }
test/standalone/config_header/build.zig+21
......@@ -51,6 +51,27 @@ pub fn build(b: *std.Build) void {
5151 });
5252 test_step.dependOn(&check_config_header_autoconf_at.step);
5353
54 const config_header_meson = b.addConfigHeader(
55 .{ .style = .{
56 .meson = b.path("meson/mesondefine.h.in"),
57 } },
58 .{
59 .version = "1.2.3",
60 .boolean_true = true,
61 .boolean_false = false,
62 .uint_64 = 42,
63 .int_64 = -42,
64 .string = "meson",
65 .ident = .meson,
66 .not_defined = null,
67 .is_defined = {},
68 },
69 );
70 const check_config_header_meson = b.addCheckFile(config_header_meson.getOutputFile(), .{
71 .expected_exact = @embedFile("meson/mesondefine.h"),
72 });
73 test_step.dependOn(&check_config_header_meson.step);
74
5475 const config_header_blank = b.addConfigHeader(
5576 .{
5677 .style = .blank,
test/standalone/config_header/meson/mesondefine.h created+22
......@@ -0,0 +1,22 @@
1/* This file was generated by ConfigHeader using the Zig Build System. */
2// comments are preserved
3
4// empty lines are preserved
5
6#define VERSION_STR "1.2.3"
7
8#define boolean_true /* comment after define is okay */
9
10#undef boolean_false // same for line comment
11
12#define uint_64 42
13
14#define int_64 -42
15
16#define string "meson"
17
18#define ident meson
19
20/* #undef not_defined */
21
22#define is_defined
test/standalone/config_header/meson/mesondefine.h.in created+21
......@@ -0,0 +1,21 @@
1// comments are preserved
2
3// empty lines are preserved
4
5#define VERSION_STR "@version@"
6
7#mesondefine boolean_true /* comment after define is okay */
8
9#mesondefine boolean_false // same for line comment
10
11#mesondefine uint_64
12
13#mesondefine int_64
14
15#mesondefine string
16
17#mesondefine ident
18
19#mesondefine not_defined
20
21#mesondefine is_defined