From 65878c16ee3e6677912343328e39c87c8f89c784 Mon Sep 17 00:00:00 2001 From: Loris Cro Date: Mon, 11 Dec 2023 23:08:22 +0100 Subject: [PATCH] std.Build.Step.Run: fix depfile support --- lib/std/Build/Step/Run.zig | 119 +++++++++++++++++++++++++++++-------- 1 file changed, 93 insertions(+), 26 deletions(-) diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 5555b03e94..83d94f00c6 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -249,7 +249,7 @@ pub fn addDepFileOutputArg(self: *Run, basename: []const u8) std.Build.LazyPath /// Add a prefixed path argument to a dep file (.d) for the child process to /// write its discovered additional dependencies. /// Only one dep file argument is allowed by instance. -pub fn addPrefixedDepFileOutputArg(self: *Run, prefix: []const u8, basename: []const u8) void { +pub fn addPrefixedDepFileOutputArg(self: *Run, prefix: []const u8, basename: []const u8) std.Build.LazyPath { assert(self.dep_output_file == null); const b = self.step.owner; @@ -264,6 +264,8 @@ pub fn addPrefixedDepFileOutputArg(self: *Run, prefix: []const u8, basename: []c self.dep_output_file = dep_file; self.argv.append(.{ .output = dep_file }) catch @panic("OOM"); + + return .{ .generated = &dep_file.generated_file }; } pub fn addArg(self: *Run, arg: []const u8) void { @@ -454,6 +456,10 @@ fn checksContainStderr(checks: []const StdIo.Check) bool { return false; } +const IndexedOutput = struct { + index: usize, + output: *Output, +}; fn make(step: *Step, prog_node: *std.Progress.Node) !void { const b = step.owner; const arena = b.allocator; @@ -461,10 +467,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { const has_side_effects = self.hasSideEffects(); var argv_list = ArrayList([]const u8).init(arena); - var output_placeholders = ArrayList(struct { - index: usize, - output: *Output, - }).init(arena); + var output_placeholders = ArrayList(IndexedOutput).init(arena); var man = b.cache.obtain(); defer man.deinit(); @@ -546,32 +549,25 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (try step.cacheHit(&man)) { // cache hit, skip running command const digest = man.final(); - for (output_placeholders.items) |placeholder| { - placeholder.output.generated_file.path = try b.cache_root.join(arena, &.{ - "o", &digest, placeholder.output.basename, - }); - } - if (self.captured_stdout) |output| { - output.generated_file.path = try b.cache_root.join(arena, &.{ - "o", &digest, output.basename, - }); - } - - if (self.captured_stderr) |output| { - output.generated_file.path = try b.cache_root.join(arena, &.{ - "o", &digest, output.basename, - }); - } + try populateGeneratedPaths( + arena, + output_placeholders.items, + self.captured_stdout, + self.captured_stderr, + b.cache_root, + &digest, + ); step.result_cached = true; return; } - const digest = man.final(); + const rand_int = std.crypto.random.int(u64); + const tmp_dir_path = "tmp" ++ fs.path.sep_str ++ std.Build.hex64(rand_int); for (output_placeholders.items) |placeholder| { - const output_components = .{ "o", &digest, placeholder.output.basename }; + const output_components = .{ tmp_dir_path, placeholder.output.basename }; const output_sub_path = try fs.path.join(arena, &output_components); const output_sub_dir_path = fs.path.dirname(output_sub_path).?; b.cache_root.handle.makePath(output_sub_dir_path) catch |err| { @@ -588,12 +584,83 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { argv_list.items[placeholder.index] = cli_arg; } - try runCommand(self, argv_list.items, has_side_effects, &digest, prog_node); + try runCommand(self, argv_list.items, has_side_effects, tmp_dir_path, prog_node); if (self.dep_output_file) |dep_output_file| try man.addDepFilePost(std.fs.cwd(), dep_output_file.generated_file.getPath()); + const digest = man.final(); + + const any_output = output_placeholders.items.len > 0 or + self.captured_stdout != null or self.captured_stderr != null; + + // Rename into place + if (any_output) { + const o_sub_path = "o" ++ fs.path.sep_str ++ &digest; + + b.cache_root.handle.rename(tmp_dir_path, o_sub_path) catch |err| { + if (err == error.PathAlreadyExists) { + b.cache_root.handle.deleteTree(o_sub_path) catch |del_err| { + return step.fail("unable to remove dir '{}'{s}: {s}", .{ + b.cache_root, + tmp_dir_path, + @errorName(del_err), + }); + }; + b.cache_root.handle.rename(tmp_dir_path, o_sub_path) catch |retry_err| { + return step.fail("unable to rename dir '{}{s}' to '{}{s}': {s}", .{ + b.cache_root, tmp_dir_path, + b.cache_root, o_sub_path, + @errorName(retry_err), + }); + }; + } else { + return step.fail("unable to rename dir '{}{s}' to '{}{s}': {s}", .{ + b.cache_root, tmp_dir_path, + b.cache_root, o_sub_path, + @errorName(err), + }); + } + }; + } + try step.writeManifest(&man); + + try populateGeneratedPaths( + arena, + output_placeholders.items, + self.captured_stdout, + self.captured_stderr, + b.cache_root, + &digest, + ); +} + +fn populateGeneratedPaths( + arena: std.mem.Allocator, + output_placeholders: []const IndexedOutput, + captured_stdout: ?*Output, + captured_stderr: ?*Output, + cache_root: Build.Cache.Directory, + digest: *const Build.Cache.HexDigest, +) !void { + for (output_placeholders) |placeholder| { + placeholder.output.generated_file.path = try cache_root.join(arena, &.{ + "o", digest, placeholder.output.basename, + }); + } + + if (captured_stdout) |output| { + output.generated_file.path = try cache_root.join(arena, &.{ + "o", digest, output.basename, + }); + } + + if (captured_stderr) |output| { + output.generated_file.path = try cache_root.join(arena, &.{ + "o", digest, output.basename, + }); + } } fn formatTerm( @@ -645,7 +712,7 @@ fn runCommand( self: *Run, argv: []const []const u8, has_side_effects: bool, - digest: ?*const [std.Build.Cache.hex_digest_len]u8, + tmp_dir_path: ?[]const u8, prog_node: *std.Progress.Node, ) !void { const step = &self.step; @@ -816,7 +883,7 @@ fn runCommand( }, }) |stream| { if (stream.captured) |output| { - const output_components = .{ "o", digest.?, output.basename }; + const output_components = .{ tmp_dir_path.?, output.basename }; const output_path = try b.cache_root.join(arena, &output_components); output.generated_file.path = output_path;