diff --git a/lib/compiler/Maker/Step/ConfigHeader.zig b/lib/compiler/Maker/Step/ConfigHeader.zig index 2863ced63f5f96def9a22d8870f35e999a021a7f..20152955fe469b751648032e54152a7aa2b53f67 100644 --- a/lib/compiler/Maker/Step/ConfigHeader.zig +++ b/lib/compiler/Maker/Step/ConfigHeader.zig @@ -93,6 +93,32 @@ pub fn make( else => |e| return e, }; }, + .meson => { + const tf = template_file.?; + const contents = tf.root_dir.handle.readFileAlloc( + io, + tf.sub_path, + arena, + input_size_limit, + ) catch |err| return step.fail( + maker, + "unable to read meson input file {f}: {t}", + .{ tf, err }, + ); + + renderMeson( + maker, + step, + contents, + &aw, + value_pairs, + &value_map, + tf, + ) catch |err| switch (err) { + error.WriteFailed => return error.OutOfMemory, + else => |e| return e, + }; + }, .blank => { renderBlank(conf, &aw.writer, value_pairs, &value_map, include_path, include_guard_override) catch |err| switch (err) { error.WriteFailed => return error.OutOfMemory, @@ -370,6 +396,62 @@ fn renderCmake( if (any_errors) return error.MakeFailed; } +fn renderMeson( + maker: *Maker, + step: *Step, + contents: []const u8, + aw: *Writer.Allocating, + value_pairs: []const Value.Pair, + value_map: *const ValueMap, + src_path: Path, +) !void { + const w = &aw.writer; + const conf = &maker.scanned_config.configuration; + const newline = detectNewline(contents); + + try w.writeAll(c_generated_line); + try w.writeAll(newline); + + var any_errors = false; + var line_index: u32 = 0; + var line_it = std.mem.splitScalar(u8, contents, '\n'); + // https://mesonbuild.com/Configuration.html + while (line_it.next()) |raw_line| : (line_index += 1) { + const last_line = line_it.index == line_it.buffer.len; + const line = std.mem.trimEnd(u8, raw_line, "\r"); + + const old_len = aw.written().len; + expandVariablesMeson(w, conf, line, value_pairs, value_map) catch |err| switch (err) { + error.MissingToken => { + try step.addError(maker, "{f}:{d}: error: missing define name", .{ src_path, line_index + 1 }); + any_errors = true; + continue; + }, + error.MissingValue => { + const name = aw.written()[old_len..]; + defer aw.shrinkRetainingCapacity(old_len); + + try step.addError(maker, "{f}:{d}: error: unspecified config header value: {q}", .{ + src_path, line_index + 1, name, + }); + any_errors = true; + continue; + }, + else => { + try step.addError(maker, "{f}:{d}: unable to substitute variable: error: {t}", .{ + src_path, line_index + 1, err, + }); + any_errors = true; + continue; + }, + }; + if (!last_line) try w.writeAll(newline); + } + + try ensureAllValuesUsed(maker, step, value_map, src_path); + if (any_errors) return error.MakeFailed; +} + fn renderBlank( conf: *const Configuration, w: *Writer, @@ -432,6 +514,28 @@ fn renderValueC(conf: *const Configuration, w: *Writer, newline: []const u8, nam } } +fn renderValueMeson( + conf: *const Configuration, + w: *Writer, + name: []const u8, + value: Value.Index, +) !void { + switch (value.unpack(conf)) { + .undef => try w.print("/* #undef {s} */", .{name}), + .defined => try w.print("#define {s}", .{name}), + .bool => |b| { + if (b) { + try w.print("#define {s}", .{name}); + } else { + try w.print("#undef {s}", .{name}); + } + }, + inline .u64, .i64 => |int| try w.print("#define {s} {d}", .{ name, int }), + .ident => |ident| try w.print("#define {s} {s}", .{ name, ident }), + .string => |string| try w.print("#define {s} \"{f}\"", .{ name, std.zig.fmtString(string) }), + } +} + fn renderValueCIdent(w: *Writer, newline: []const u8, name: []const u8, ident: []const u8) Writer.Error!void { try w.print("#define {s}", .{name}); if (ident.len > 0) { @@ -628,3 +732,36 @@ fn expandVariablesCmake( return result.toOwnedSliceAssert(); } + +fn expandVariablesMeson( + w: *Writer, + conf: *const Configuration, + line: []const u8, + value_pairs: []const Value.Pair, + value_map: *const ValueMap, +) !void { + const mesondefine = "#mesondefine"; + if (std.mem.startsWith(u8, line, mesondefine)) { + const line_offset = mesondefine.len + 1; + if (line_offset > line.len) return error.MissingToken; + + var it = std.mem.tokenizeAny(u8, line[line_offset..], " \t\r"); + const name = it.next() orelse return error.MissingToken; + + const index = value_map.getIndex(name) orelse { + // Report the missing key to the caller. + try w.writeAll(name); + return error.MissingValue; + }; + + const value = value_pairs[index].index; + value_map.values()[index] = true; // Mark as used. + + try renderValueMeson(conf, w, name, value); + + // comments/any other text passthrough unaffected + return try w.writeAll(line[line_offset + name.len ..]); + } + + try expandVariablesAutoconfAt(w, line, conf, value_pairs, value_map); +} diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig index 31da18abfd88368efb7ded2112d3343bdcf6201a..b04a7c592460b1db60c80b412edcbf973c5d29e2 100644 --- a/lib/std/Build/Configuration.zig +++ b/lib/std/Build/Configuration.zig @@ -1076,6 +1076,7 @@ pub const Step = extern struct { autoconf_undef, autoconf_at, cmake, + meson, blank, nasm, @@ -1084,6 +1085,7 @@ pub const Step = extern struct { .autoconf_undef => .autoconf_undef, .autoconf_at => .autoconf_at, .cmake => .cmake, + .meson => .meson, .blank => .blank, .nasm => .nasm, }; diff --git a/lib/std/Build/Step/ConfigHeader.zig b/lib/std/Build/Step/ConfigHeader.zig index 9d036dcf2db7b1417c60af507461d8ec647aeb9e..9aeebf1a1096fae7bfb4589e386c953839824f9a 100644 --- a/lib/std/Build/Step/ConfigHeader.zig +++ b/lib/std/Build/Step/ConfigHeader.zig @@ -27,6 +27,9 @@ pub const Style = union(enum) { /// The configure format supported by CMake. It uses `@FOO@`, `${}` and /// `#cmakedefine` for template substitution. cmake: std.Build.LazyPath, + /// The configure format supported by Meson. It uses `@FOO@`, and + /// `#mesondefine` for template substitution. + meson: std.Build.LazyPath, /// Instead of starting with an input file, start with nothing. blank, /// Start with nothing, like blank, and output a nasm .asm file. @@ -34,7 +37,7 @@ pub const Style = union(enum) { pub fn getPath(style: Style) ?std.Build.LazyPath { switch (style) { - .autoconf_undef, .autoconf_at, .cmake => |s| return s, + .autoconf_undef, .autoconf_at, .cmake, .meson => |s| return s, .blank, .nasm => return null, } } diff --git a/test/standalone/config_header/build.zig b/test/standalone/config_header/build.zig index 88078ca2fae1eea50cffda5dff641d1566771e08..120c65c8532459a9cfcb3702c2d9b33492090e3f 100644 --- a/test/standalone/config_header/build.zig +++ b/test/standalone/config_header/build.zig @@ -51,6 +51,27 @@ pub fn build(b: *std.Build) void { }); test_step.dependOn(&check_config_header_autoconf_at.step); + const config_header_meson = b.addConfigHeader( + .{ .style = .{ + .meson = b.path("meson/mesondefine.h.in"), + } }, + .{ + .version = "1.2.3", + .boolean_true = true, + .boolean_false = false, + .uint_64 = 42, + .int_64 = -42, + .string = "meson", + .ident = .meson, + .not_defined = null, + .is_defined = {}, + }, + ); + const check_config_header_meson = b.addCheckFile(config_header_meson.getOutputFile(), .{ + .expected_exact = @embedFile("meson/mesondefine.h"), + }); + test_step.dependOn(&check_config_header_meson.step); + const config_header_blank = b.addConfigHeader( .{ .style = .blank, diff --git a/test/standalone/config_header/meson/mesondefine.h b/test/standalone/config_header/meson/mesondefine.h new file mode 100644 index 0000000000000000000000000000000000000000..f70ffe0ef1c08bfb04071699300d4404b7905f62 --- /dev/null +++ b/test/standalone/config_header/meson/mesondefine.h @@ -0,0 +1,22 @@ +/* This file was generated by ConfigHeader using the Zig Build System. */ +// comments are preserved + +// empty lines are preserved + +#define VERSION_STR "1.2.3" + +#define boolean_true /* comment after define is okay */ + +#undef boolean_false // same for line comment + +#define uint_64 42 + +#define int_64 -42 + +#define string "meson" + +#define ident meson + +/* #undef not_defined */ + +#define is_defined diff --git a/test/standalone/config_header/meson/mesondefine.h.in b/test/standalone/config_header/meson/mesondefine.h.in new file mode 100644 index 0000000000000000000000000000000000000000..b90f3054c29c4476da7d44f05afd20e69adc4acf --- /dev/null +++ b/test/standalone/config_header/meson/mesondefine.h.in @@ -0,0 +1,21 @@ +// comments are preserved + +// empty lines are preserved + +#define VERSION_STR "@version@" + +#mesondefine boolean_true /* comment after define is okay */ + +#mesondefine boolean_false // same for line comment + +#mesondefine uint_64 + +#mesondefine int_64 + +#mesondefine string + +#mesondefine ident + +#mesondefine not_defined + +#mesondefine is_defined