commit 1186a10d4e145f553a4b749042fcc02ba6efe18b (tree)
parent f4ae918684975c191749edec26223b7dba7cd9ed
Author: Andrew Kelley <andrew@ziglang.org>
Date: Fri, 8 May 2026 12:20:21 -0700
configurer: serialize WriteFile
Diffstat:
3 files changed, 181 insertions(+), 109 deletions(-)
diff --git a/lib/compiler/configurer.zig b/lib/compiler/configurer.zig
@@ -891,7 +891,47 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void {
.find_program => @panic("TODO"),
.fmt => @panic("TODO"),
.translate_c => @panic("TODO"),
- .write_file => @panic("TODO"),
+ .write_file => e: {
+ const wf: *Step.WriteFile = @fieldParentPtr("step", step);
+
+ const copies = try arena.alloc(Configuration.Step.WriteFile.Copy, wf.copies.items.len);
+ for (copies, wf.copies.items) |*dest, src| dest.* = .{
+ .sub_path = src.sub_path,
+ .src_file = try s.addLazyPath(src.src_file),
+ };
+
+ const directories = try arena.alloc(
+ Configuration.Step.WriteFile.Directory,
+ wf.directories.items.len,
+ );
+ for (directories, wf.directories.items) |*dest, src| dest.* = .{
+ .sub_path = src.sub_path,
+ .src_path = try s.addLazyPath(src.src_path),
+ .exclude_extensions = src.exclude_extensions,
+ .include_extensions = src.include_extensions,
+ };
+
+ break :e @enumFromInt(try wc.addExtra(@as(Configuration.Step.WriteFile, .{
+ .flags = .{
+ .embeds = wf.embeds.items.len != 0,
+ .copies = copies.len != 0,
+ .directories = directories.len != 0,
+ .mode = switch (wf.mode) {
+ .whole_cached => .whole_cached,
+ .tmp => .tmp,
+ .mutate => .mutate,
+ },
+ },
+ .generated_directory = wf.generated_directory,
+ .embeds = .{ .slice = wf.embeds.items },
+ .copies = .{ .slice = copies },
+ .directories = .{ .slice = directories },
+ .mutate_path = .{ .value = switch (wf.mode) {
+ .mutate => |lp| try s.addLazyPath(lp),
+ .whole_cached, .tmp => null,
+ } },
+ })));
+ },
.update_source_files => @panic("TODO"),
.run => e: {
const run: *Step.Run = @fieldParentPtr("step", step);
diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig
@@ -1251,10 +1251,42 @@ pub const Step = extern struct {
pub const WriteFile = struct {
flags: @This().Flags,
+ generated_directory: GeneratedFileIndex,
+ embeds: Storage.FlagLengthPrefixedList(.flags, .embeds, Embed),
+ copies: Storage.FlagLengthPrefixedList(.flags, .copies, Copy),
+ directories: Storage.FlagLengthPrefixedList(.flags, .directories, Directory),
+ mutate_path: Storage.EnumOptional(.flags, .mode, .mutate, LazyPath.Index),
+
+ pub const Embed = extern struct {
+ sub_path: String,
+ contents: Bytes,
+ };
+
+ pub const Copy = extern struct {
+ sub_path: String,
+ src_file: LazyPath.Index,
+ };
+
+ pub const Directory = extern struct {
+ sub_path: String,
+ src_path: LazyPath.Index,
+ exclude_extensions: OptionalStringList,
+ include_extensions: OptionalStringList,
+ };
+
+ pub const Mode = enum(u2) {
+ whole_cached,
+ tmp,
+ mutate,
+ };
pub const Flags = packed struct(u32) {
tag: Tag = .write_file,
- _: u27 = 0,
+ embeds: bool,
+ copies: bool,
+ directories: bool,
+ mode: Mode,
+ _: u22 = 0,
};
};
@@ -2370,8 +2402,11 @@ pub const Storage = enum {
};
}
- /// A field in flags determines whether the length is zero or nonzero. If the length is
- /// nonzero, then there is a length field followed by the list.
+ /// A field in flags determines whether the length is zero or nonzero. If
+ /// the length is nonzero, then there is a length field followed by the
+ /// list. The elements need well-defined memory layout but can otherwise be
+ /// any multiple of u32 length. The length is the number of elements, not
+ /// the number of u32s.
pub fn FlagLengthPrefixedList(
comptime flags_arg: @EnumLiteral(),
comptime flag_arg: @EnumLiteral(),
@@ -2767,14 +2802,16 @@ pub const Storage = enum {
const len: u32 = @intCast(value.slice.len);
if (len == 0) return 0; // Flag bit hides the length prefix.
buffer[i] = len;
- @memcpy(buffer[i + 1 ..][0..len], @as([]const u32, @ptrCast(value.slice)));
- return len + 1;
+ const buf_len = len * @divExact(@sizeOf(Field.Elem), @sizeOf(u32));
+ @memcpy(buffer[i + 1 ..][0..buf_len], @as([]const u32, @ptrCast(value.slice)));
+ return 1 + buf_len;
},
.length_prefixed_list => {
const len: u32 = @intCast(value.slice.len);
buffer[i] = len;
- @memcpy(buffer[i + 1 ..][0..len], @as([]const u32, @ptrCast(value.slice)));
- return len + 1;
+ const buf_len = len * @divExact(@sizeOf(Field.Elem), @sizeOf(u32));
+ @memcpy(buffer[i + 1 ..][0..buf_len], @as([]const u32, @ptrCast(value.slice)));
+ return 1 + buf_len;
},
.flag_list => {
const len: u32 = @intCast(value.slice.len);
diff --git a/lib/std/Build/Step/WriteFile.zig b/lib/std/Build/Step/WriteFile.zig
@@ -13,8 +13,9 @@ const Configuration = std.Build.Configuration;
step: Step,
-files: std.ArrayList(File),
-directories: std.ArrayList(Directory),
+embeds: std.ArrayList(Embed) = .empty,
+copies: std.ArrayList(Copy) = .empty,
+directories: std.ArrayList(Directory) = .empty,
generated_directory: Configuration.GeneratedFileIndex,
mode: Mode = .whole_cached,
@@ -37,158 +38,152 @@ pub const Mode = union(enum) {
mutate: std.Build.LazyPath,
};
-pub const File = struct {
- sub_path: []const u8,
- contents: Contents,
-};
+pub const Embed = Configuration.Step.WriteFile.Embed;
-pub const Directory = struct {
- source: std.Build.LazyPath,
- sub_path: []const u8,
- options: Options,
-
- pub const Options = struct {
- /// File paths that end in any of these suffixes will be excluded from copying.
- exclude_extensions: []const []const u8 = &.{},
- /// Only file paths that end in any of these suffixes will be included in copying.
- /// `null` means that all suffixes will be included.
- /// `exclude_extensions` takes precedence over `include_extensions`.
- include_extensions: ?[]const []const u8 = null,
-
- pub fn dupe(opts: Options, graph: *std.Build.Graph) Options {
- return .{
- .exclude_extensions = graph.dupeStrings(opts.exclude_extensions),
- .include_extensions = if (opts.include_extensions) |incs| graph.dupeStrings(incs) else null,
- };
- }
-
- pub fn pathIncluded(opts: Options, path: []const u8) bool {
- for (opts.exclude_extensions) |ext| {
- if (std.mem.endsWith(u8, path, ext))
- return false;
- }
- if (opts.include_extensions) |incs| {
- for (incs) |inc| {
- if (std.mem.endsWith(u8, path, inc))
- return true;
- } else {
- return false;
- }
- }
- return true;
- }
- };
+pub const Copy = struct {
+ sub_path: Configuration.String,
+ src_file: std.Build.LazyPath,
};
-pub const Contents = union(enum) {
- bytes: []const u8,
- copy: std.Build.LazyPath,
+pub const Directory = struct {
+ sub_path: Configuration.String,
+ src_path: std.Build.LazyPath,
+ exclude_extensions: Configuration.OptionalStringList,
+ include_extensions: Configuration.OptionalStringList,
};
pub fn create(owner: *std.Build) *WriteFile {
const graph = owner.graph;
- const arena = graph.arena;
- const write_file = arena.create(WriteFile) catch @panic("OOM");
- write_file.* = .{
- .step = Step.init(.{
+ const wf = graph.create(WriteFile);
+ wf.* = .{
+ .step = .init(.{
.tag = base_tag,
.name = "WriteFile",
.owner = owner,
}),
- .files = .empty,
- .directories = .empty,
- .generated_directory = graph.addGeneratedFile(&write_file.step),
+ .generated_directory = graph.addGeneratedFile(&wf.step),
};
- return write_file;
+ return wf;
}
-pub fn add(write_file: *WriteFile, sub_path: []const u8, bytes: []const u8) std.Build.LazyPath {
- const graph = write_file.step.owner.graph;
+/// Writes `contents` to a file at `sub_path` relative to the output
+/// directory.
+///
+/// `sub_path` may be a basename, or it may include subdirectories, which are
+/// created as needed.
+pub fn add(wf: *WriteFile, sub_path: []const u8, contents: []const u8) std.Build.LazyPath {
+ const graph = wf.step.owner.graph;
+ const wc = &graph.wip_configuration;
const arena = graph.arena;
- const file: File = .{
- .sub_path = graph.dupePath(sub_path),
- .contents = .{ .bytes = graph.dupeString(bytes) },
- };
- write_file.files.append(arena, file) catch @panic("OOM");
- write_file.maybeUpdateName();
+
+ wf.embeds.append(arena, .{
+ .sub_path = wc.addString(sub_path) catch @panic("OOM"),
+ .contents = wc.addBytes(contents) catch @panic("OOM"),
+ }) catch @panic("OOM");
+
+ wf.maybeUpdateName();
+
return .{
.generated = .{
- .index = write_file.generated_directory,
- .sub_path = file.sub_path,
+ .index = wf.generated_directory,
+ .sub_path = graph.dupeString(sub_path),
},
};
}
-/// Copies the provided file into the generated directory within the local
-/// cache, along with all the rest of the files added to this step.
+/// Copies the provided file to `sub_path` relative to the output directory.
///
-/// `sub_path` is the destination path relative to the local cache directory
-/// associated with this WriteFile. It may be a basename, or it may include
-/// subdirectories, which are created as needed.
-pub fn addCopyFile(write_file: *WriteFile, source: std.Build.LazyPath, sub_path: []const u8) std.Build.LazyPath {
- const graph = write_file.step.owner.graph;
- const duped_path = graph.dupePath(sub_path);
+/// `sub_path` may be a basename, or it may include subdirectories, which are
+/// created as needed.
+pub fn addCopyFile(wf: *WriteFile, src_file: std.Build.LazyPath, sub_path: []const u8) std.Build.LazyPath {
+ const graph = wf.step.owner.graph;
+ const wc = &graph.wip_configuration;
const arena = graph.arena;
- write_file.files.append(arena, .{
- .sub_path = duped_path,
- .contents = .{ .copy = source },
+ wf.copies.append(arena, .{
+ .sub_path = wc.addString(sub_path) catch @panic("OOM"),
+ .src_file = src_file.dupe(graph),
}) catch @panic("OOM");
- write_file.maybeUpdateName();
- source.addStepDependencies(&write_file.step);
+ wf.maybeUpdateName();
+
+ src_file.addStepDependencies(&wf.step);
return .{ .generated = .{
- .index = write_file.generated_directory,
- .sub_path = duped_path,
+ .index = wf.generated_directory,
+ .sub_path = graph.dupePath(sub_path),
} };
}
+pub const CopyDirectoryOptions = struct {
+ /// File paths that end in any of these suffixes will be excluded from copying.
+ exclude_extensions: []const []const u8 = &.{},
+ /// Only file paths that end in any of these suffixes will be included in copying.
+ /// `null` means that all suffixes will be included.
+ /// `exclude_extensions` takes precedence over `include_extensions`.
+ include_extensions: ?[]const []const u8 = null,
+};
+
/// Copy files matching the specified exclude/include patterns to the specified
/// subdirectory relative to this step's generated directory.
///
/// The returned value is a lazy path to the generated subdirectory.
pub fn addCopyDirectory(
- write_file: *WriteFile,
- source: std.Build.LazyPath,
+ wf: *WriteFile,
+ src_path: std.Build.LazyPath,
sub_path: []const u8,
- options: Directory.Options,
+ options: CopyDirectoryOptions,
) std.Build.LazyPath {
- const graph = write_file.step.owner.graph;
+ const graph = wf.step.owner.graph;
+ const wc = &graph.wip_configuration;
const arena = graph.arena;
- const dir = Directory{
- .source = source.dupe(graph),
- .sub_path = graph.dupePath(sub_path),
- .options = options.dupe(graph),
- };
- write_file.directories.append(arena, dir) catch @panic("OOM");
- write_file.maybeUpdateName();
- source.addStepDependencies(&write_file.step);
+ wf.directories.append(arena, .{
+ .sub_path = wc.addString(sub_path) catch @panic("OOM"),
+ .src_path = src_path.dupe(graph),
+ .exclude_extensions = if (options.exclude_extensions.len != 0)
+ .init(wc.addStringList(options.exclude_extensions) catch @panic("OOM"))
+ else
+ .none,
+ .include_extensions = if (options.include_extensions) |list|
+ .init(wc.addStringList(list) catch @panic("OOM"))
+ else
+ .none,
+ }) catch @panic("OOM");
+
+ wf.maybeUpdateName();
+
+ src_path.addStepDependencies(&wf.step);
+
return .{
.generated = .{
- .index = write_file.generated_directory,
- .sub_path = dir.sub_path,
+ .index = wf.generated_directory,
+ .sub_path = graph.dupePath(sub_path),
},
};
}
/// Returns a `LazyPath` representing the base directory that contains all the
/// files from this `WriteFile`.
-pub fn getDirectory(write_file: *WriteFile) std.Build.LazyPath {
- return .{ .generated = .{ .index = write_file.generated_directory } };
+pub fn getDirectory(wf: *WriteFile) std.Build.LazyPath {
+ return .{ .generated = .{ .index = wf.generated_directory } };
}
-fn maybeUpdateName(write_file: *WriteFile) void {
- if (write_file.files.items.len == 1 and write_file.directories.items.len == 0) {
+fn maybeUpdateName(wf: *WriteFile) void {
+ const graph = wf.step.owner.graph;
+ const wc = &graph.wip_configuration;
+ const files_count = wf.embeds.items.len + wf.copies.items.len;
+ if (files_count == 1 and wf.directories.items.len == 0) {
// First time adding a file; update name.
- if (std.mem.eql(u8, write_file.step.name, "WriteFile")) {
- write_file.step.name = write_file.step.owner.fmt("WriteFile {s}", .{write_file.files.items[0].sub_path});
+ const sub_path = if (wf.embeds.items.len == 1) wf.embeds.items[0].sub_path else wf.copies.items[0].sub_path;
+ if (std.mem.eql(u8, wf.step.name, "WriteFile")) {
+ wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{wc.stringSlice(sub_path)});
}
- } else if (write_file.directories.items.len == 1 and write_file.files.items.len == 0) {
+ } else if (wf.directories.items.len == 1 and files_count == 0) {
// First time adding a directory; update name.
- if (std.mem.eql(u8, write_file.step.name, "WriteFile")) {
- write_file.step.name = write_file.step.owner.fmt("WriteFile {s}", .{write_file.directories.items[0].sub_path});
+ const dir_name = wc.stringSlice(wf.directories.items[0].sub_path);
+ if (std.mem.eql(u8, wf.step.name, "WriteFile")) {
+ wf.step.name = wf.step.owner.fmt("WriteFile {s}", .{dir_name});
}
}
}