From bb1b59ee1feecd5500847d394fc90842efe138ed Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 18 May 2026 21:58:19 -0700 Subject: [PATCH] Maker: implement Step.InstallDir --- BRANCH_TODO | 1 + lib/compiler/Maker.zig | 45 +++++++++++++-- lib/compiler/Maker/Step.zig | 5 +- lib/compiler/Maker/Step/InstallDir.zig | 78 ++++++++++++++++++-------- lib/compiler/configurer.zig | 2 +- lib/std/Build.zig | 17 ------ lib/std/Build/Configuration.zig | 14 +---- 7 files changed, 103 insertions(+), 59 deletions(-) diff --git a/BRANCH_TODO b/BRANCH_TODO index ebc00a3d51..aa3f095f98 100644 --- a/BRANCH_TODO +++ b/BRANCH_TODO @@ -41,6 +41,7 @@ ## Already Filed Followup Issues * build system fmt step with check=false does not acquire a write lock on source files #35204 * enhance CheckFile step output when there is not a match #35208 +* missing truncate functionality #35353 ## Release Notes diff --git a/lib/compiler/Maker.zig b/lib/compiler/Maker.zig index e8dff14a46..52815f7945 100644 --- a/lib/compiler/Maker.zig +++ b/lib/compiler/Maker.zig @@ -1879,13 +1879,50 @@ pub fn installGenerated( return installPath(maker, arena, src_path, dest_path, asking_step_index); } +pub fn truncatePath( + maker: *Maker, + arena: Allocator, + dest_path: Path, + asking_step_index: Configuration.Step.Index, +) Step.ExtendedMakeError!void { + const graph = maker.graph; + const io = graph.io; + if (graph.verbose) try graph.handleVerbose(.inherit, null, &.{ + "truncate", try dest_path.toString(arena), + }); + // https://codeberg.org/ziglang/zig/issues/35353 + const err = e: { + var file = f: { + break :f dest_path.root_dir.handle.createFile(io, dest_path.sub_path, .{}) catch |err| switch (err) { + error.FileNotFound => { + const parent_path = dest_path.dirname() orelse break :e err; + parent_path.root_dir.handle.createDirPath(io, parent_path.sub_path) catch |in| switch (in) { + error.Canceled => |e| return e, + else => |e| { + const s = stepByIndex(maker, asking_step_index); + return s.fail(maker, "failed creating directory {f}: {t}", .{ parent_path, e }); + }, + }; + break :f dest_path.root_dir.handle.createFile(io, dest_path.sub_path, .{}) catch |in| break :e in; + }, + error.Canceled => |e| return e, + else => |e| break :e e, + }; + }; + file.close(io); + return; + }; + const s = stepByIndex(maker, asking_step_index); + return s.fail(maker, "failed truncating file {f}: {t}", .{ dest_path, err }); +} + pub fn installPath( maker: *Maker, arena: Allocator, src_path: Path, dest_path: Path, asking_step_index: Configuration.Step.Index, -) !Dir.PrevStatus { +) Step.ExtendedMakeError!Dir.PrevStatus { const graph = maker.graph; const io = graph.io; if (graph.verbose) try graph.handleVerbose(.inherit, null, &.{ @@ -1900,7 +1937,7 @@ pub fn installPath( .{}, ) catch |err| { const s = stepByIndex(maker, asking_step_index); - return s.fail(maker, "unable to update file from {f} to {f}: {t}", .{ src_path, dest_path, err }); + return s.fail(maker, "failed updating file from {f} to {f}: {t}", .{ src_path, dest_path, err }); }; } @@ -1910,7 +1947,7 @@ pub fn installDir( arena: Allocator, dest_path: Path, asking_step_index: Configuration.Step.Index, -) !Dir.CreatePathStatus { +) Step.ExtendedMakeError!Dir.CreatePathStatus { const graph = maker.graph; const io = graph.io; if (graph.verbose) try graph.handleVerbose(.inherit, null, &.{ @@ -1918,7 +1955,7 @@ pub fn installDir( }); return dest_path.root_dir.handle.createDirPathStatus(io, dest_path.sub_path, .default_dir) catch |err| { const s = stepByIndex(maker, asking_step_index); - return s.fail(maker, "unable to create dir {f}: {t}", .{ dest_path, err }); + return s.fail(maker, "failed creating dir {f}: {t}", .{ dest_path, err }); }; } diff --git a/lib/compiler/Maker/Step.zig b/lib/compiler/Maker/Step.zig index 92cdfdb957..6b74c1469a 100644 --- a/lib/compiler/Maker/Step.zig +++ b/lib/compiler/Maker/Step.zig @@ -21,6 +21,7 @@ const Maker = @import("../Maker.zig"); pub const Compile = @import("Step/Compile.zig"); pub const Fmt = @import("Step/Fmt.zig"); pub const InstallArtifact = @import("Step/InstallArtifact.zig"); +pub const InstallDir = @import("Step/InstallDir.zig"); pub const InstallFile = @import("Step/InstallFile.zig"); pub const ObjCopy = @import("Step/ObjCopy.zig"); pub const Options = @import("Step/Options.zig"); @@ -79,11 +80,10 @@ pub const Extended = union(enum) { find_program: Todo, fmt: Fmt, install_artifact: InstallArtifact, - install_dir: Todo, + install_dir: InstallDir, install_file: InstallFile, obj_copy: ObjCopy, options: Options, - remove_dir: Todo, run: Run, top_level: TopLevel, translate_c: Todo, @@ -103,7 +103,6 @@ pub const Extended = union(enum) { .install_file => .{ .install_file = .{} }, .obj_copy => .{ .obj_copy = .{} }, .options => .{ .options = .{} }, - .remove_dir => .{ .remove_dir = .{} }, .run => .{ .run = .{} }, .top_level => .{ .top_level = .{} }, .translate_c => .{ .translate_c = .{} }, diff --git a/lib/compiler/Maker/Step/InstallDir.zig b/lib/compiler/Maker/Step/InstallDir.zig index 7d079380dd..484bebf329 100644 --- a/lib/compiler/Maker/Step/InstallDir.zig +++ b/lib/compiler/Maker/Step/InstallDir.zig @@ -1,7 +1,10 @@ const InstallDir = @This(); const std = @import("std"); +const Io = std.Io; +const log = std.log; const Configuration = std.Build.Configuration; +const endsWith = std.mem.endsWith; const Step = @import("../Step.zig"); const Maker = @import("../../Maker.zig"); @@ -12,51 +15,82 @@ pub fn make( maker: *Maker, progress_node: std.Progress.Node, ) Step.ExtendedMakeError!void { + _ = install_dir; const graph = maker.graph; + const gpa = maker.gpa; const arena = maker.graph.arena; // TODO don't leak into process arena const io = graph.io; const step = maker.stepByIndex(step_index); + const conf = &maker.scanned_config.configuration; + const conf_step = step_index.ptr(conf); + const conf_id = conf_step.extended.get(conf.extra).install_dir; - step.clearWatchInputs(); - const dest_prefix = b.getInstallPath(install_dir.options.install_dir, install_dir.options.install_subdir); - const src_dir_path = install_dir.options.source_dir.getPath3(b, step); - const need_derived_inputs = try step.addDirectoryWatchInput(install_dir.options.source_dir); - var src_dir = src_dir_path.root_dir.handle.openDir(io, src_dir_path.subPathOrDot(), .{ .iterate = true }) catch |err| { - return step.fail("unable to open source directory '{f}': {t}", .{ src_dir_path, err }); - }; + step.clearWatchInputs(maker); + + const dest_parent_path = try maker.resolveInstallDir(arena, conf_id.dest_dir); + const dest_prefix = if (conf_id.dest_sub_path.value) |s| + try dest_parent_path.join(arena, s.slice(conf)) + else + dest_parent_path; + const src_dir_lazy_path = conf_id.source_dir.get(conf); + const src_dir_path = try maker.resolveLazyPath(arena, src_dir_lazy_path, step_index); + const need_derived_inputs = try step.addDirectoryWatchInput(maker, src_dir_lazy_path); + + var src_dir = src_dir_path.root_dir.handle.openDir( + io, + src_dir_path.subPathOrDot(), + .{ .iterate = true }, + ) catch |err| return step.fail(maker, "failed opening source directory {f}: {t}", .{ src_dir_path, err }); defer src_dir.close(io); - var it = try src_dir.walk(arena); + + const exclude_extensions = conf_id.exclude_extensions.slice; + const include_extensions: ?[]const Configuration.String = if (conf_id.flags.include_extensions_active) + conf_id.include_extensions.slice + else + null; + const blank_extensions = conf_id.blank_extensions.slice; + var all_cached = true; - next_entry: while (try it.next(io)) |entry| { - for (install_dir.options.exclude_extensions) |ext| { - if (std.mem.endsWith(u8, entry.path, ext)) continue :next_entry; + var it = try src_dir.walk(gpa); + defer it.deinit(); + next_entry: while (it.next(io) catch |err| switch (err) { + error.Canceled, error.OutOfMemory => |e| return e, + else => |e| return step.fail(maker, "failed iterating dir {f}: {t}", .{ src_dir_path, e }), + }) |entry| { + for (exclude_extensions) |ext| { + if (endsWith(u8, entry.path, ext.slice(conf))) continue :next_entry; } - if (install_dir.options.include_extensions) |incs| { - for (incs) |inc| { - if (std.mem.endsWith(u8, entry.path, inc)) break; + if (include_extensions) |includes| { + for (includes) |inc| { + if (endsWith(u8, entry.path, inc.slice(conf))) break; } else { continue :next_entry; } } - const src_path = try install_dir.options.source_dir.join(arena, entry.path); - const dest_path = b.pathJoin(&.{ dest_prefix, entry.path }); + const dest_path = try dest_prefix.join(arena, entry.path); switch (entry.kind) { .directory => { - if (need_derived_inputs) _ = try step.addDirectoryWatchInput(src_path); - const p = try step.installDir(dest_path); + if (need_derived_inputs) { + const entry_path = try src_dir_path.join(arena, entry.path); + try step.addDirectoryWatchInputFromPath(maker, entry_path); + } + const p = try maker.installDir(arena, dest_path, step_index); all_cached = all_cached and p == .existed; }, .file => { - for (install_dir.options.blank_extensions) |ext| { - if (std.mem.endsWith(u8, entry.path, ext)) { - try b.truncateFile(dest_path); + for (blank_extensions) |ext| { + if (endsWith(u8, entry.path, ext.slice(conf))) { + // TODO check if the file was already there and length 0 + try maker.truncatePath(arena, dest_path, step_index); continue :next_entry; } } - const p = try step.installFile(src_path, dest_path); + const entry_path = try src_dir_path.join(arena, entry.path); + const p = try maker.installPath(arena, entry_path, dest_path, step_index); all_cached = all_cached and p == .fresh; + progress_node.completeOne(); }, else => continue, } diff --git a/lib/compiler/configurer.zig b/lib/compiler/configurer.zig index a5a35a2d4b..d07f77b118 100644 --- a/lib/compiler/configurer.zig +++ b/lib/compiler/configurer.zig @@ -931,6 +931,7 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { .dest_sub_path = dest_sub_path != null, .exclude_extensions = sid.options.exclude_extensions.len != 0, .include_extensions = include_extensions.len != 0, + .include_extensions_active = sid.options.include_extensions != null, .blank_extensions = sid.options.blank_extensions.len != 0, }, .source_dir = try s.addLazyPath(sid.options.source_dir), @@ -941,7 +942,6 @@ fn serialize(b: *std.Build, wc: *Configuration.Wip, writer: *Io.Writer) !void { .blank_extensions = .{ .slice = try s.initStringList(sid.options.blank_extensions) }, }))); }, - .remove_dir => @panic("TODO"), .fail => e: { const sf: *Step.Fail = @fieldParentPtr("step", step); break :e @enumFromInt(try wc.addExtra(@as(Configuration.Step.Fail, .{ diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 252d65f6cc..92474787c6 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1584,23 +1584,6 @@ pub fn addCheckFile( return Step.CheckFile.create(b, file_source, options); } -pub fn truncateFile(b: *Build, dest_path: []const u8) (Io.Dir.CreateDirError || Io.Dir.StatFileError)!void { - const graph = b.graph; - const io = graph.io; - if (graph.verbose) log.info("truncate {s}", .{dest_path}); - const cwd = Io.Dir.cwd(); - var src_file = cwd.createFile(io, dest_path, .{}) catch |err| switch (err) { - error.FileNotFound => blk: { - if (fs.path.dirname(dest_path)) |dirname| { - try cwd.createDirPath(io, dirname); - } - break :blk try cwd.createFile(io, dest_path, .{}); - }, - else => |e| return e, - }; - src_file.close(io); -} - /// References a file or directory relative to the source root. pub fn path(b: *Build, sub_path: []const u8) LazyPath { if (fs.path.isAbsolute(sub_path)) { diff --git a/lib/std/Build/Configuration.zig b/lib/std/Build/Configuration.zig index 34cd8c62e7..5888aa8abf 100644 --- a/lib/std/Build/Configuration.zig +++ b/lib/std/Build/Configuration.zig @@ -466,7 +466,6 @@ pub const Step = extern struct { install_file: InstallFile, obj_copy: ObjCopy, options: Options, - remove_dir: RemoveDir, run: Run, top_level: TopLevel, translate_c: TranslateC, @@ -501,7 +500,6 @@ pub const Step = extern struct { install_file, obj_copy, options, - remove_dir, run, top_level, translate_c, @@ -1197,8 +1195,9 @@ pub const Step = extern struct { dest_sub_path: bool, exclude_extensions: bool, include_extensions: bool, + include_extensions_active: bool, blank_extensions: bool, - _: u23 = 0, + _: u22 = 0, }; }; @@ -1320,15 +1319,6 @@ pub const Step = extern struct { }; }; - pub const RemoveDir = struct { - flags: @This().Flags, - - pub const Flags = packed struct(u32) { - tag: Tag = .remove_dir, - _: u27 = 0, - }; - }; - pub const TranslateC = struct { flags: @This().Flags, src_path: LazyPath.Index,