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(...@@ -93,6 +93,32 @@ pub fn make(
93 else => |e| return e,93 else => |e| return e,
94 };94 };
95 },95 },
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 },
96 .blank => {122 .blank => {
97 renderBlank(conf, &aw.writer, value_pairs, &value_map, include_path, include_guard_override) catch |err| switch (err) {123 renderBlank(conf, &aw.writer, value_pairs, &value_map, include_path, include_guard_override) catch |err| switch (err) {
98 error.WriteFailed => return error.OutOfMemory,124 error.WriteFailed => return error.OutOfMemory,
...@@ -370,6 +396,62 @@ fn renderCmake(...@@ -370,6 +396,62 @@ fn renderCmake(
370 if (any_errors) return error.MakeFailed;396 if (any_errors) return error.MakeFailed;
371}397}
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
373fn renderBlank(455fn renderBlank(
374 conf: *const Configuration,456 conf: *const Configuration,
375 w: *Writer,457 w: *Writer,
...@@ -432,6 +514,28 @@ fn renderValueC(conf: *const Configuration, w: *Writer, newline: []const u8, nam...@@ -432,6 +514,28 @@ fn renderValueC(conf: *const Configuration, w: *Writer, newline: []const u8, nam
432 }514 }
433}515}
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
435fn renderValueCIdent(w: *Writer, newline: []const u8, name: []const u8, ident: []const u8) Writer.Error!void {539fn renderValueCIdent(w: *Writer, newline: []const u8, name: []const u8, ident: []const u8) Writer.Error!void {
436 try w.print("#define {s}", .{name});540 try w.print("#define {s}", .{name});
437 if (ident.len > 0) {541 if (ident.len > 0) {
...@@ -628,3 +732,36 @@ fn expandVariablesCmake(...@@ -628,3 +732,36 @@ fn expandVariablesCmake(
628732
629 return result.toOwnedSliceAssert();733 return result.toOwnedSliceAssert();
630}734}
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 {...@@ -1076,6 +1076,7 @@ pub const Step = extern struct {
1076 autoconf_undef,1076 autoconf_undef,
1077 autoconf_at,1077 autoconf_at,
1078 cmake,1078 cmake,
1079 meson,
1079 blank,1080 blank,
1080 nasm,1081 nasm,
10811082
...@@ -1084,6 +1085,7 @@ pub const Step = extern struct {...@@ -1084,6 +1085,7 @@ pub const Step = extern struct {
1084 .autoconf_undef => .autoconf_undef,1085 .autoconf_undef => .autoconf_undef,
1085 .autoconf_at => .autoconf_at,1086 .autoconf_at => .autoconf_at,
1086 .cmake => .cmake,1087 .cmake => .cmake,
1088 .meson => .meson,
1087 .blank => .blank,1089 .blank => .blank,
1088 .nasm => .nasm,1090 .nasm => .nasm,
1089 };1091 };
lib/std/Build/Step/ConfigHeader.zig+4-1
...@@ -27,6 +27,9 @@ pub const Style = union(enum) {...@@ -27,6 +27,9 @@ pub const Style = union(enum) {
27 /// The configure format supported by CMake. It uses `@FOO@`, `${}` and27 /// The configure format supported by CMake. It uses `@FOO@`, `${}` and
28 /// `#cmakedefine` for template substitution.28 /// `#cmakedefine` for template substitution.
29 cmake: std.Build.LazyPath,29 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,
30 /// Instead of starting with an input file, start with nothing.33 /// Instead of starting with an input file, start with nothing.
31 blank,34 blank,
32 /// Start with nothing, like blank, and output a nasm .asm file.35 /// Start with nothing, like blank, and output a nasm .asm file.
...@@ -34,7 +37,7 @@ pub const Style = union(enum) {...@@ -34,7 +37,7 @@ pub const Style = union(enum) {
3437
35 pub fn getPath(style: Style) ?std.Build.LazyPath {38 pub fn getPath(style: Style) ?std.Build.LazyPath {
36 switch (style) {39 switch (style) {
37 .autoconf_undef, .autoconf_at, .cmake => |s| return s,40 .autoconf_undef, .autoconf_at, .cmake, .meson => |s| return s,
38 .blank, .nasm => return null,41 .blank, .nasm => return null,
39 }42 }
40 }43 }
test/standalone/config_header/build.zig+21
...@@ -51,6 +51,27 @@ pub fn build(b: *std.Build) void {...@@ -51,6 +51,27 @@ pub fn build(b: *std.Build) void {
51 });51 });
52 test_step.dependOn(&check_config_header_autoconf_at.step);52 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
54 const config_header_blank = b.addConfigHeader(75 const config_header_blank = b.addConfigHeader(
55 .{76 .{
56 .style = .blank,77 .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