commit e435299cfa6a6bd446483e6c95a56bf1cdca8cfd (tree)
parent ed1f00582670df44c15875914a1cc8fc0ff6329f
Author: Andrew Kelley <andrew@ziglang.org>
Date: Fri, 22 May 2026 21:51:11 -0700
Maker: progress towards ConfigHeader
however... why is this done in the make phase anyway? making a header
like this is typically done by the configure phase...
Diffstat:
3 files changed, 139 insertions(+), 120 deletions(-)
diff --git a/lib/compiler/Maker/Step.zig b/lib/compiler/Maker/Step.zig
@@ -20,6 +20,7 @@ const Maker = @import("../Maker.zig");
pub const CheckFile = @import("Step/CheckFile.zig");
pub const Compile = @import("Step/Compile.zig");
+pub const ConfigHeader = @import("Step/ConfigHeader.zig");
pub const FindProgram = @import("Step/FindProgram.zig");
pub const Fmt = @import("Step/Fmt.zig");
pub const InstallArtifact = @import("Step/InstallArtifact.zig");
@@ -78,7 +79,7 @@ comptime {
pub const Extended = union(enum) {
check_file: CheckFile,
compile: Compile,
- config_header: Todo,
+ config_header: ConfigHeader,
fail: Fail,
find_program: FindProgram,
fmt: Fmt,
@@ -114,21 +115,6 @@ pub const Extended = union(enum) {
};
}
- pub const Todo = struct {
- pub fn make(
- todo: *Todo,
- step_index: Configuration.Step.Index,
- maker: *Maker,
- progress_node: std.Progress.Node,
- ) Step.ExtendedMakeError!void {
- _ = todo;
- _ = progress_node;
- const conf = &maker.scanned_config.configuration;
- const conf_step = step_index.ptr(conf);
- std.debug.panic("TODO implement another step type: {s}", .{conf_step.name.slice(conf)});
- }
- };
-
pub const TopLevel = struct {
pub fn make(
top_level: *TopLevel,
@@ -750,8 +736,9 @@ fn failWithCacheError(
/// separately from using the cache system.
pub fn writeManifest(s: *Step, maker: *Maker, man: *Cache.Manifest) !void {
if (s.test_results.isSuccess()) {
- man.writeManifest() catch |err| {
- try s.addError(maker, "failed writing cache manifest: {t}", .{err});
+ man.writeManifest() catch |err| switch (err) {
+ error.Canceled => |e| return e,
+ else => |e| try s.addError(maker, "failed writing cache manifest: {t}", .{e}),
};
}
}
diff --git a/lib/compiler/Maker/Step/ConfigHeader.zig b/lib/compiler/Maker/Step/ConfigHeader.zig
@@ -4,22 +4,35 @@ const std = @import("std");
const Io = std.Io;
const Configuration = std.Build.Configuration;
const Writer = std.Io.Writer;
+const Path = std.Build.Cache.Path;
+const Allocator = std.mem.Allocator;
const Step = @import("../Step.zig");
const Maker = @import("../../Maker.zig");
+ const header_text = "This file was generated by ConfigHeader using the Zig Build System.";
+ const c_generated_line = "/* " ++ header_text ++ " */\n";
+ const asm_generated_line = "; " ++ header_text ++ "\n";
+
pub fn make(
config_header: *ConfigHeader,
step_index: Configuration.Step.Index,
maker: *Maker,
progress_node: std.Progress.Node,
) Step.ExtendedMakeError!void {
+ _ = config_header;
+ _ = progress_node;
const graph = maker.graph;
- const arena = maker.graph.arena; // TODO don't leak into process arena
+ const gpa = maker.gpa;
const step = maker.stepByIndex(step_index);
const io = graph.io;
+ const arena = graph.arena; // TODO don't leak into the process arena
+ const conf = &maker.scanned_config.configuration;
+ const conf_step = step_index.ptr(conf);
+ const conf_ch = conf_step.extended.get(conf.extra).config_header;
+ const cache_root = graph.local_cache_root;
- if (config_header.style.getPath()) |lp|
+ if (conf_ch.style.getPath()) |lp|
try step.singleUnchangingWatchInput(maker, arena, lp);
var man = graph.cache.obtain();
@@ -29,45 +42,51 @@ pub fn make(
// random bytes when ConfigHeader implementation is modified in a
// non-backwards-compatible way.
man.hash.add(@as(u32, 0xdef08d23));
- man.hash.addBytes(config_header.include_path);
- man.hash.addOptionalBytes(config_header.include_guard_override);
+ man.hash.addBytes(conf_ch.include_path);
+ man.hash.addOptionalBytes(conf_ch.include_guard_override);
var aw: Writer.Allocating = .init(arena);
defer aw.deinit();
- const bw = &aw.writer;
- const header_text = "This file was generated by ConfigHeader using the Zig Build System.";
- const c_generated_line = "/* " ++ header_text ++ " */\n";
- const asm_generated_line = "; " ++ header_text ++ "\n";
-
- switch (config_header.style) {
- .autoconf_undef, .autoconf_at => |file_source| {
- try bw.writeAll(c_generated_line);
- const src_path = file_source.getPath2(b, step);
- const contents = Io.Dir.cwd().readFileAlloc(io, src_path, arena, .limited(config_header.max_bytes)) catch |err| {
+ switch (conf_ch.flags.style) {
+ .autoconf_undef => {
+ const src_path = try maker.resolveLazyPathIndex(arena, conf_ch.template_file.value.?, step_index);
+ const contents = Io.Dir.cwd().readFileAlloc(io, src_path, arena, .limited(conf_ch.max_bytes)) catch |err|
return step.fail("unable to read autoconf input file {s}: {t}", .{ src_path, err });
+ renderAutoConfUndef(step, contents, &aw.writer, &conf_ch.values, src_path) catch |err| switch (err) {
+ error.WriteFailed => return error.OutOfMemory,
+ else => |e| return e,
+ };
+ },
+ .autoconf_at => {
+ const src_path = try maker.resolveLazyPathIndex(arena, conf_ch.template_file.value.?, step_index);
+ const contents = Io.Dir.cwd().readFileAlloc(io, src_path, arena, .limited(conf_ch.max_bytes)) catch |err|
+ return step.fail("unable to read autoconf input file {s}: {t}", .{ src_path, err });
+ renderAutoconfAt(step, contents, &aw, &conf_ch.values, src_path) catch |err| switch (err) {
+ error.WriteFailed => return error.OutOfMemory,
+ else => |e| return e,
};
- switch (config_header.style) {
- .autoconf_undef => try render_autoconf_undef(step, contents, bw, &config_header.values, src_path),
- .autoconf_at => try render_autoconf_at(step, contents, &aw, &config_header.values, src_path),
- else => unreachable,
- }
},
- .cmake => |file_source| {
- try bw.writeAll(c_generated_line);
- const src_path = file_source.getPath2(b, step);
- const contents = Io.Dir.cwd().readFileAlloc(io, src_path, arena, .limited(config_header.max_bytes)) catch |err| {
+ .cmake => {
+ const src_path = try maker.resolveLazyPathIndex(arena, conf_ch.template_file.value.?, step_index);
+ const contents = Io.Dir.cwd().readFileAlloc(io, src_path, arena, .limited(conf_ch.max_bytes)) catch |err|
return step.fail("unable to read cmake input file {s}: {t}", .{ src_path, err });
+ renderCmake(step, contents, &aw.writer, conf_ch.values, src_path) catch |err| switch (err) {
+ error.WriteFailed => return error.OutOfMemory,
+ else => |e| return e,
};
- try render_cmake(step, contents, bw, config_header.values, src_path);
},
.blank => {
- try bw.writeAll(c_generated_line);
- try render_blank(gpa, bw, config_header.values, config_header.include_path, config_header.include_guard_override);
+ renderBlank(gpa, &aw.writer, conf_ch.values, conf_ch.include_path, conf_ch.include_guard_override) catch |err| switch (err) {
+ error.WriteFailed => return error.OutOfMemory,
+ else => |e| return e,
+ };
},
.nasm => {
- try bw.writeAll(asm_generated_line);
- try render_nasm(bw, config_header.values);
+ renderNasm(&aw.writer, conf_ch.values) catch |err| switch (err) {
+ error.WriteFailed => return error.OutOfMemory,
+ else => |e| return e,
+ };
},
}
@@ -76,7 +95,10 @@ pub fn make(
if (try step.cacheHit(&man)) {
const digest = man.final();
- config_header.generated_dir.path = try b.cache_root.join(arena, &.{ "o", &digest });
+ maker.generatedPath().* = .{
+ .root_dir = cache_root,
+ .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest }),
+ };
return;
}
@@ -87,35 +109,38 @@ pub fn make(
// output_path is libavutil/avconfig.h
// We want to open directory zig-cache/o/HASH/libavutil/
// but keep output_dir as zig-cache/o/HASH for -I include
- const sub_path = b.pathJoin(&.{ "o", &digest, config_header.include_path });
- const sub_path_dirname = std.fs.path.dirname(sub_path).?;
-
- b.cache_root.handle.createDirPath(io, sub_path_dirname) catch |err| {
- return step.fail("unable to make path '{f}{s}': {s}", .{
- b.cache_root, sub_path_dirname, @errorName(err),
- });
+ const out_path: Path = .{
+ .root_dir = cache_root,
+ .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest, conf_ch.include_path.slice(conf) }),
};
+ const out_path_dirname = out_path.dirname().?;
+
+ out_path_dirname.root_dir.handle.createDirPath(io, out_path_dirname.sub_path) catch |err|
+ return step.fail("unable to make path {f}: {t}", .{ out_path_dirname, err });
- b.cache_root.handle.writeFile(io, .{ .sub_path = sub_path, .data = output }) catch |err| {
- return step.fail("unable to write file '{f}{s}': {s}", .{
- b.cache_root, sub_path, @errorName(err),
- });
+ out_path.root_dir.handle.writeFile(io, .{ .sub_path = out_path.sub_path, .data = output }) catch |err|
+ return step.fail("unable to write file {f}: {t}", .{ out_path, err });
+
+ maker.generatedPath().* = .{
+ .root_dir = cache_root,
+ .sub_path = try Io.Dir.path.join(arena, &.{ "o", &digest }),
};
- config_header.generated_dir.path = try b.cache_root.join(arena, &.{ "o", &digest });
- try man.writeManifest();
+ try step.writeManifest(maker, &man);
}
-fn render_autoconf_undef(
+fn renderAutoConfUndef(
step: *Step,
contents: []const u8,
- bw: *Writer,
+ w: *Writer,
values: *const std.array_hash_map.String(Value),
src_path: []const u8,
) !void {
const build = step.owner;
const allocator = build.allocator;
+ try w.writeAll(c_generated_line);
+
var is_used: std.bit_set.Dynamic = try .initEmpty(allocator, values.count());
defer is_used.deinit(allocator);
@@ -124,15 +149,15 @@ fn render_autoconf_undef(
var line_it = std.mem.splitScalar(u8, contents, '\n');
while (line_it.next()) |line| : (line_index += 1) {
if (!std.mem.startsWith(u8, line, "#")) {
- try bw.writeAll(line);
- try bw.writeByte('\n');
+ try w.writeAll(line);
+ try w.writeByte('\n');
continue;
}
var it = std.mem.tokenizeAny(u8, line[1..], " \t\r");
const undef = it.next().?;
if (!std.mem.eql(u8, undef, "undef")) {
- try bw.writeAll(line);
- try bw.writeByte('\n');
+ try w.writeAll(line);
+ try w.writeByte('\n');
continue;
}
const name = it.next().?;
@@ -144,7 +169,7 @@ fn render_autoconf_undef(
continue;
};
is_used.set(index);
- try renderValueC(bw, name, values.values()[index]);
+ try renderValueC(w, name, values.values()[index]);
}
var unused_value_it = is_used.iterator(.{ .kind = .unset });
@@ -158,7 +183,7 @@ fn render_autoconf_undef(
}
}
-fn render_autoconf_at(
+fn renderAutoconfAt(
step: *Step,
contents: []const u8,
aw: *Writer.Allocating,
@@ -167,7 +192,9 @@ fn render_autoconf_at(
) !void {
const build = step.owner;
const allocator = build.allocator;
- const bw = &aw.writer;
+ const w = &aw.writer;
+
+ try w.writeAll(c_generated_line);
const used = allocator.alloc(bool, values.count()) catch @panic("OOM");
for (used) |*u| u.* = false;
@@ -180,7 +207,7 @@ fn render_autoconf_at(
const last_line = line_it.index == line_it.buffer.len;
const old_len = aw.written().len;
- expand_variables_autoconf_at(bw, line, values, used) catch |err| switch (err) {
+ expandVariablesAutoconfAt(w, line, values, used) catch |err| switch (err) {
error.MissingValue => {
const name = aw.written()[old_len..];
defer aw.shrinkRetainingCapacity(old_len);
@@ -198,7 +225,7 @@ fn render_autoconf_at(
continue;
},
};
- if (!last_line) try bw.writeByte('\n');
+ if (!last_line) try w.writeByte('\n');
}
for (values.entries.slice().items(.key), used) |name, u| {
@@ -211,16 +238,18 @@ fn render_autoconf_at(
if (any_errors) return error.MakeFailed;
}
-fn render_cmake(
+fn renderCmake(
step: *Step,
contents: []const u8,
- bw: *Writer,
+ w: *Writer,
values: std.array_hash_map.String(Value),
src_path: []const u8,
) !void {
const build = step.owner;
const allocator = build.allocator;
+ try w.writeAll(c_generated_line);
+
var values_copy = try values.clone(allocator);
defer values_copy.deinit(allocator);
@@ -230,7 +259,7 @@ fn render_cmake(
while (line_it.next()) |raw_line| : (line_index += 1) {
const last_line = line_it.index == line_it.buffer.len;
- const line = expand_variables_cmake(allocator, raw_line, values) catch |err| switch (err) {
+ const line = expandVariablesCmake(allocator, raw_line, values) catch |err| switch (err) {
error.InvalidCharacter => {
try step.addError("{s}:{d}: error: invalid character in a variable name", .{
src_path, line_index + 1,
@@ -249,16 +278,16 @@ fn render_cmake(
defer allocator.free(line);
const line_start = std.mem.findNone(u8, line, " \t\r") orelse {
- try bw.writeAll(line);
- if (!last_line) try bw.writeByte('\n');
+ try w.writeAll(line);
+ if (!last_line) try w.writeByte('\n');
continue;
};
const whitespace_prefix = line[0..line_start];
const trimmed_line = line[line_start..];
if (!std.mem.startsWith(u8, trimmed_line, "#")) {
- try bw.writeAll(line);
- if (!last_line) try bw.writeByte('\n');
+ try w.writeAll(line);
+ if (!last_line) try w.writeByte('\n');
continue;
}
@@ -267,8 +296,8 @@ fn render_cmake(
if (!std.mem.eql(u8, cmakedefine, "cmakedefine") and
!std.mem.eql(u8, cmakedefine, "cmakedefine01"))
{
- try bw.writeAll(line);
- if (!last_line) try bw.writeByte('\n');
+ try w.writeAll(line);
+ if (!last_line) try w.writeByte('\n');
continue;
}
@@ -339,8 +368,8 @@ fn render_cmake(
value = Value{ .ident = it.rest() };
}
- try bw.writeAll(whitespace_prefix);
- try renderValueC(bw, name, value);
+ try w.writeAll(whitespace_prefix);
+ try renderValueC(w, name, value);
}
if (any_errors) {
@@ -348,13 +377,15 @@ fn render_cmake(
}
}
-fn render_blank(
+fn renderBlank(
gpa: std.mem.Allocator,
- bw: *Writer,
+ w: *Writer,
defines: std.array_hash_map.String(Value),
include_path: []const u8,
include_guard_override: ?[]const u8,
) !void {
+ try w.writeAll(c_generated_line);
+
const include_guard_name = include_guard_override orelse blk: {
const name = try gpa.dupe(u8, include_path);
for (name) |*byte| {
@@ -368,51 +399,52 @@ fn render_blank(
};
defer if (include_guard_override == null) gpa.free(include_guard_name);
- try bw.print(
+ try w.print(
\\#ifndef {[0]s}
\\#define {[0]s}
\\
, .{include_guard_name});
const values = defines.values();
- for (defines.keys(), 0..) |name, i| try renderValueC(bw, name, values[i]);
+ for (defines.keys(), 0..) |name, i| try renderValueC(w, name, values[i]);
- try bw.print(
+ try w.print(
\\#endif /* {s} */
\\
, .{include_guard_name});
}
-fn render_nasm(bw: *Writer, defines: std.array_hash_map.String(Value)) !void {
- for (defines.keys(), defines.values()) |name, value| try renderValueNasm(bw, name, value);
+fn renderNasm(w: *Writer, defines: std.array_hash_map.String(Value)) !void {
+ try w.writeAll(asm_generated_line);
+ for (defines.keys(), defines.values()) |name, value| try renderValueNasm(w, name, value);
}
-fn renderValueC(bw: *Writer, name: []const u8, value: Value) !void {
+fn renderValueC(w: *Writer, name: []const u8, value: Value) !void {
switch (value) {
- .undef => try bw.print("/* #undef {s} */\n", .{name}),
- .defined => try bw.print("#define {s}\n", .{name}),
- .boolean => |b| try bw.print("#define {s} {c}\n", .{ name, @as(u8, '0') + @intFromBool(b) }),
- .int => |i| try bw.print("#define {s} {d}\n", .{ name, i }),
- .ident => |ident| try bw.print("#define {s} {s}\n", .{ name, ident }),
+ .undef => try w.print("/* #undef {s} */\n", .{name}),
+ .defined => try w.print("#define {s}\n", .{name}),
+ .boolean => |b| try w.print("#define {s} {c}\n", .{ name, @as(u8, '0') + @intFromBool(b) }),
+ .int => |i| try w.print("#define {s} {d}\n", .{ name, i }),
+ .ident => |ident| try w.print("#define {s} {s}\n", .{ name, ident }),
// TODO: use C-specific escaping instead of zig string literals
- .string => |string| try bw.print("#define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }),
+ .string => |string| try w.print("#define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }),
}
}
-fn renderValueNasm(bw: *Writer, name: []const u8, value: Value) !void {
+fn renderValueNasm(w: *Writer, name: []const u8, value: Value) !void {
switch (value) {
- .undef => try bw.print("; %undef {s}\n", .{name}),
- .defined => try bw.print("%define {s}\n", .{name}),
- .boolean => |b| try bw.print("%define {s} {c}\n", .{ name, @as(u8, '0') + @intFromBool(b) }),
- .int => |i| try bw.print("%define {s} {d}\n", .{ name, i }),
- .ident => |ident| try bw.print("%define {s} {s}\n", .{ name, ident }),
+ .undef => try w.print("; %undef {s}\n", .{name}),
+ .defined => try w.print("%define {s}\n", .{name}),
+ .boolean => |b| try w.print("%define {s} {c}\n", .{ name, @as(u8, '0') + @intFromBool(b) }),
+ .int => |i| try w.print("%define {s} {d}\n", .{ name, i }),
+ .ident => |ident| try w.print("%define {s} {s}\n", .{ name, ident }),
// TODO: use nasm-specific escaping instead of zig string literals
- .string => |string| try bw.print("%define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }),
+ .string => |string| try w.print("%define {s} \"{f}\"\n", .{ name, std.zig.fmtString(string) }),
}
}
-fn expand_variables_autoconf_at(
- bw: *Writer,
+fn expandVariablesAutoconfAt(
+ w: *Writer,
contents: []const u8,
values: *const std.array_hash_map.String(Value),
used: []bool,
@@ -437,17 +469,17 @@ fn expand_variables_autoconf_at(
const key = contents[curr + 1 .. close_pos];
const index = values.getIndex(key) orelse {
// Report the missing key to the caller.
- try bw.writeAll(key);
+ try w.writeAll(key);
return error.MissingValue;
};
const value = values.entries.slice().items(.value)[index];
used[index] = true;
- try bw.writeAll(contents[source_offset..curr]);
+ try w.writeAll(contents[source_offset..curr]);
switch (value) {
.undef, .defined => {},
- .boolean => |b| try bw.writeByte(@as(u8, '0') + @intFromBool(b)),
- .int => |i| try bw.print("{d}", .{i}),
- .ident, .string => |s| try bw.writeAll(s),
+ .boolean => |b| try w.writeByte(@as(u8, '0') + @intFromBool(b)),
+ .int => |i| try w.print("{d}", .{i}),
+ .ident, .string => |s| try w.writeAll(s),
}
curr = close_pos;
@@ -455,10 +487,10 @@ fn expand_variables_autoconf_at(
}
}
- try bw.writeAll(contents[source_offset..]);
+ try w.writeAll(contents[source_offset..]);
}
-fn expand_variables_cmake(
+fn expandVariablesCmake(
allocator: Allocator,
contents: []const u8,
values: std.array_hash_map.String(Value),
@@ -602,7 +634,7 @@ fn testReplaceVariablesAutoconfAt(
for (used) |*u| u.* = false;
defer allocator.free(used);
- try expand_variables_autoconf_at(&aw.writer, contents, values, used);
+ try expandVariablesAutoconfAt(&aw.writer, contents, values, used);
for (used) |u| if (!u) return error.UnusedValue;
try std.testing.expectEqualStrings(expected, aw.written());
@@ -614,13 +646,13 @@ fn testReplaceVariablesCMake(
expected: []const u8,
values: std.array_hash_map.String(Value),
) !void {
- const actual = try expand_variables_cmake(allocator, contents, values);
+ const actual = try expandVariablesCmake(allocator, contents, values);
defer allocator.free(actual);
try std.testing.expectEqualStrings(expected, actual);
}
-test "expand_variables_autoconf_at simple cases" {
+test "expandVariablesAutoconfAt simple cases" {
const allocator = std.testing.allocator;
var values: std.array_hash_map.String(Value) = .init(allocator);
defer values.deinit();
@@ -716,7 +748,7 @@ test "expand_variables_autoconf_at simple cases" {
values.clearRetainingCapacity();
}
-test "expand_variables_autoconf_at edge cases" {
+test "expandVariablesAutoconfAt edge cases" {
const allocator = std.testing.allocator;
var values: std.array_hash_map.String(Value) = .init(allocator);
defer values.deinit();
@@ -732,7 +764,7 @@ test "expand_variables_autoconf_at edge cases" {
values.clearRetainingCapacity();
}
-test "expand_variables_cmake simple cases" {
+test "expandVariablesCmake simple cases" {
const allocator = std.testing.allocator;
var values: std.array_hash_map.String(Value) = .init(allocator);
defer values.deinit();
@@ -820,7 +852,7 @@ test "expand_variables_cmake simple cases" {
try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${bad}", "", values));
}
-test "expand_variables_cmake edge cases" {
+test "expandVariablesCmake edge cases" {
const allocator = std.testing.allocator;
var values: std.array_hash_map.String(Value) = .init(allocator);
defer values.deinit();
@@ -881,7 +913,7 @@ test "expand_variables_cmake edge cases" {
try std.testing.expectError(error.InvalidCharacter, testReplaceVariablesCMake(allocator, "${str@ing}", "", values));
}
-test "expand_variables_cmake escaped characters" {
+test "expandVariablesCmake escaped characters" {
const allocator = std.testing.allocator;
var values: std.array_hash_map.String(Value) = .init(allocator);
defer values.deinit();
diff --git a/lib/compiler/Maker/Step/ObjCopy.zig b/lib/compiler/Maker/Step/ObjCopy.zig
@@ -166,7 +166,7 @@ pub fn make(
maker.generatedPath(conf_oc.output_file).* = dest_path;
- man.writeManifest() catch |err| switch (err) {
+ step.writeManifest(maker, &man) catch |err| switch (err) {
error.Canceled => |e| return e,
else => |e| try step.addError(maker, "failed writing cache manifest: {t}", .{e}),
};