diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 056a7ec639..5b974bb816 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -1737,7 +1737,7 @@ pub fn makeTempPath(b: *Build) []const u8 { const rand_int = std.crypto.random.int(u64); const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ hex64(rand_int); const result_path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch @panic("OOM"); - fs.cwd().makePath(result_path) catch |err| { + b.cache_root.handle.makePath(tmp_dir_sub_path) catch |err| { std.debug.print("unable to make tmp path '{s}': {s}\n", .{ result_path, @errorName(err), }); @@ -1747,7 +1747,7 @@ pub fn makeTempPath(b: *Build) []const u8 { /// There are a few copies of this function in miscellaneous places. Would be nice to find /// a home for them. -fn hex64(x: u64) [16]u8 { +pub fn hex64(x: u64) [16]u8 { const hex_charset = "0123456789abcdef"; var result: [16]u8 = undefined; var i: usize = 0; diff --git a/lib/std/Build/OptionsStep.zig b/lib/std/Build/OptionsStep.zig index 859d0b68c9..a0e72e3695 100644 --- a/lib/std/Build/OptionsStep.zig +++ b/lib/std/Build/OptionsStep.zig @@ -241,33 +241,75 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { ); } - var options_dir = try b.cache_root.handle.makeOpenPath("options", .{}); - defer options_dir.close(); + const basename = "options.zig"; - const basename = self.hashContentsToFileName(); + // Hash contents to file name. + var hash = b.cache.hash; + // Random bytes to make unique. Refresh this with new random bytes when + // implementation is modified in a non-backwards-compatible way. + hash.add(@as(u32, 0x38845ef8)); + hash.addBytes(self.contents.items); + const sub_path = "c" ++ fs.path.sep_str ++ hash.final() ++ fs.path.sep_str ++ basename; - try options_dir.writeFile(&basename, self.contents.items); + self.generated_file.path = try b.cache_root.join(b.allocator, &.{sub_path}); - self.generated_file.path = try b.cache_root.join(b.allocator, &.{ "options", &basename }); -} + // Optimize for the hot path. Stat the file, and if it already exists, + // cache hit. + if (b.cache_root.handle.access(sub_path, .{})) |_| { + // This is the hot path, success. + step.result_cached = true; + return; + } else |outer_err| switch (outer_err) { + error.FileNotFound => { + const sub_dirname = fs.path.dirname(sub_path).?; + b.cache_root.handle.makePath(sub_dirname) catch |e| { + return step.fail("unable to make path '{}{s}': {s}", .{ + b.cache_root, sub_dirname, @errorName(e), + }); + }; -fn hashContentsToFileName(self: *OptionsStep) [64]u8 { - // TODO update to use the cache system instead of this - // This implementation is copied from `WriteFileStep.make` + const rand_int = std.crypto.random.int(u64); + const tmp_sub_path = "tmp" ++ fs.path.sep_str ++ + std.Build.hex64(rand_int) ++ fs.path.sep_str ++ + basename; + const tmp_sub_path_dirname = fs.path.dirname(tmp_sub_path).?; - var hash = std.crypto.hash.blake2.Blake2b384.init(.{}); + b.cache_root.handle.makePath(tmp_sub_path_dirname) catch |err| { + return step.fail("unable to make temporary directory '{}{s}': {s}", .{ + b.cache_root, tmp_sub_path_dirname, @errorName(err), + }); + }; - // Random bytes to make OptionsStep unique. Refresh this with - // new random bytes when OptionsStep implementation is modified - // in a non-backwards-compatible way. - hash.update("yL0Ya4KkmcCjBlP8"); - hash.update(self.contents.items); + b.cache_root.handle.writeFile(tmp_sub_path, self.contents.items) catch |err| { + return step.fail("unable to write options to '{}{s}': {s}", .{ + b.cache_root, tmp_sub_path, @errorName(err), + }); + }; - var digest: [48]u8 = undefined; - hash.final(&digest); - var hash_basename: [64]u8 = undefined; - _ = fs.base64_encoder.encode(&hash_basename, &digest); - return hash_basename; + b.cache_root.handle.rename(tmp_sub_path, sub_path) catch |err| switch (err) { + error.PathAlreadyExists => { + // Other process beat us to it. Clean up the temp file. + b.cache_root.handle.deleteFile(tmp_sub_path) catch |e| { + try step.addError("warning: unable to delete temp file '{}{s}': {s}", .{ + b.cache_root, tmp_sub_path, @errorName(e), + }); + }; + step.result_cached = true; + return; + }, + else => { + return step.fail("unable to rename options from '{}{s}' to '{}{s}': {s}", .{ + b.cache_root, tmp_sub_path, + b.cache_root, sub_path, + @errorName(err), + }); + }, + }; + }, + else => |e| return step.fail("unable to access options file '{}{s}': {s}", .{ + b.cache_root, sub_path, @errorName(e), + }), + } } const OptionArtifactArg = struct { diff --git a/test/standalone.zig b/test/standalone.zig index 4cf795a85f..98297e9578 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -213,6 +213,11 @@ pub const build_cases = [_]BuildCase{ .build_root = "test/standalone/issue_13030", .import = @import("standalone/issue_13030/build.zig"), }, + // TODO restore this test + //.{ + // .build_root = "test/standalone/options", + // .import = @import("standalone/options/build.zig"), + //}, }; const std = @import("std"); diff --git a/test/standalone/options/build.zig b/test/standalone/options/build.zig index 5e894102a7..28e7e31eb7 100644 --- a/test/standalone/options/build.zig +++ b/test/standalone/options/build.zig @@ -1,13 +1,10 @@ const std = @import("std"); pub fn build(b: *std.Build) void { - const target = b.standardTargetOptions(.{}); - const optimize = b.standardOptimizeOption(.{}); - const main = b.addTest(.{ .root_source_file = .{ .path = "src/main.zig" }, - .target = target, - .optimize = optimize, + .target = .{}, + .optimize = .Debug, }); const options = b.addOptions();