authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-18 18:32:43-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-03-19 00:39:29-04:00
log8f481dfc3c4f12327499485e3bf10fbbb1023186
tree1b197c065a39b459ddb9b7d2cfae10dd8023cc28
parent2ac8d90df05fdbb59a0ee9ff6609a058185f66ff

fix std.Build.OptionsStep

* use the same hash function as the rest of the steps * fix race condition due to a macOS oddity. * fix race condition due to file truncation (rename into place instead) * integrate with marking Step.result_cached. check if the file already exists with fs.access before doing anything else. * use a directory so that the file basename can be "options.zig" instead of a hash digest. * better error reporting in case of file system failures.

4 files changed, 78 insertions(+), 34 deletions(-)

lib/std/Build.zig+2-2
...@@ -1737,7 +1737,7 @@ pub fn makeTempPath(b: *Build) []const u8 {...@@ -1737,7 +1737,7 @@ pub fn makeTempPath(b: *Build) []const u8 {
1737 const rand_int = std.crypto.random.int(u64);1737 const rand_int = std.crypto.random.int(u64);
1738 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ hex64(rand_int);1738 const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ hex64(rand_int);
1739 const result_path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch @panic("OOM");1739 const result_path = b.cache_root.join(b.allocator, &.{tmp_dir_sub_path}) catch @panic("OOM");
1740 fs.cwd().makePath(result_path) catch |err| {1740 b.cache_root.handle.makePath(tmp_dir_sub_path) catch |err| {
1741 std.debug.print("unable to make tmp path '{s}': {s}\n", .{1741 std.debug.print("unable to make tmp path '{s}': {s}\n", .{
1742 result_path, @errorName(err),1742 result_path, @errorName(err),
1743 });1743 });
...@@ -1747,7 +1747,7 @@ pub fn makeTempPath(b: *Build) []const u8 {...@@ -1747,7 +1747,7 @@ pub fn makeTempPath(b: *Build) []const u8 {
17471747
1748/// There are a few copies of this function in miscellaneous places. Would be nice to find1748/// There are a few copies of this function in miscellaneous places. Would be nice to find
1749/// a home for them.1749/// a home for them.
1750fn hex64(x: u64) [16]u8 {1750pub fn hex64(x: u64) [16]u8 {
1751 const hex_charset = "0123456789abcdef";1751 const hex_charset = "0123456789abcdef";
1752 var result: [16]u8 = undefined;1752 var result: [16]u8 = undefined;
1753 var i: usize = 0;1753 var i: usize = 0;
lib/std/Build/OptionsStep.zig+69-27
...@@ -241,33 +241,75 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -241,33 +241,75 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
241 );241 );
242 }242 }
243243
244 var options_dir = try b.cache_root.handle.makeOpenPath("options", .{});244 const basename = "options.zig";
245 defer options_dir.close();245
246246 // Hash contents to file name.
247 const basename = self.hashContentsToFileName();247 var hash = b.cache.hash;
248248 // Random bytes to make unique. Refresh this with new random bytes when
249 try options_dir.writeFile(&basename, self.contents.items);249 // implementation is modified in a non-backwards-compatible way.
250250 hash.add(@as(u32, 0x38845ef8));
251 self.generated_file.path = try b.cache_root.join(b.allocator, &.{ "options", &basename });251 hash.addBytes(self.contents.items);
252}252 const sub_path = "c" ++ fs.path.sep_str ++ hash.final() ++ fs.path.sep_str ++ basename;
253253
254fn hashContentsToFileName(self: *OptionsStep) [64]u8 {254 self.generated_file.path = try b.cache_root.join(b.allocator, &.{sub_path});
255 // TODO update to use the cache system instead of this255
256 // This implementation is copied from `WriteFileStep.make`256 // Optimize for the hot path. Stat the file, and if it already exists,
257257 // cache hit.
258 var hash = std.crypto.hash.blake2.Blake2b384.init(.{});258 if (b.cache_root.handle.access(sub_path, .{})) |_| {
259259 // This is the hot path, success.
260 // Random bytes to make OptionsStep unique. Refresh this with260 step.result_cached = true;
261 // new random bytes when OptionsStep implementation is modified261 return;
262 // in a non-backwards-compatible way.262 } else |outer_err| switch (outer_err) {
263 hash.update("yL0Ya4KkmcCjBlP8");263 error.FileNotFound => {
264 hash.update(self.contents.items);264 const sub_dirname = fs.path.dirname(sub_path).?;
265265 b.cache_root.handle.makePath(sub_dirname) catch |e| {
266 var digest: [48]u8 = undefined;266 return step.fail("unable to make path '{}{s}': {s}", .{
267 hash.final(&digest);267 b.cache_root, sub_dirname, @errorName(e),
268 var hash_basename: [64]u8 = undefined;268 });
269 _ = fs.base64_encoder.encode(&hash_basename, &digest);269 };
270 return hash_basename;270
271 const rand_int = std.crypto.random.int(u64);
272 const tmp_sub_path = "tmp" ++ fs.path.sep_str ++
273 std.Build.hex64(rand_int) ++ fs.path.sep_str ++
274 basename;
275 const tmp_sub_path_dirname = fs.path.dirname(tmp_sub_path).?;
276
277 b.cache_root.handle.makePath(tmp_sub_path_dirname) catch |err| {
278 return step.fail("unable to make temporary directory '{}{s}': {s}", .{
279 b.cache_root, tmp_sub_path_dirname, @errorName(err),
280 });
281 };
282
283 b.cache_root.handle.writeFile(tmp_sub_path, self.contents.items) catch |err| {
284 return step.fail("unable to write options to '{}{s}': {s}", .{
285 b.cache_root, tmp_sub_path, @errorName(err),
286 });
287 };
288
289 b.cache_root.handle.rename(tmp_sub_path, sub_path) catch |err| switch (err) {
290 error.PathAlreadyExists => {
291 // Other process beat us to it. Clean up the temp file.
292 b.cache_root.handle.deleteFile(tmp_sub_path) catch |e| {
293 try step.addError("warning: unable to delete temp file '{}{s}': {s}", .{
294 b.cache_root, tmp_sub_path, @errorName(e),
295 });
296 };
297 step.result_cached = true;
298 return;
299 },
300 else => {
301 return step.fail("unable to rename options from '{}{s}' to '{}{s}': {s}", .{
302 b.cache_root, tmp_sub_path,
303 b.cache_root, sub_path,
304 @errorName(err),
305 });
306 },
307 };
308 },
309 else => |e| return step.fail("unable to access options file '{}{s}': {s}", .{
310 b.cache_root, sub_path, @errorName(e),
311 }),
312 }
271}313}
272314
273const OptionArtifactArg = struct {315const OptionArtifactArg = struct {
test/standalone.zig+5
...@@ -213,6 +213,11 @@ pub const build_cases = [_]BuildCase{...@@ -213,6 +213,11 @@ pub const build_cases = [_]BuildCase{
213 .build_root = "test/standalone/issue_13030",213 .build_root = "test/standalone/issue_13030",
214 .import = @import("standalone/issue_13030/build.zig"),214 .import = @import("standalone/issue_13030/build.zig"),
215 },215 },
216 // TODO restore this test
217 //.{
218 // .build_root = "test/standalone/options",
219 // .import = @import("standalone/options/build.zig"),
220 //},
216};221};
217222
218const std = @import("std");223const std = @import("std");
test/standalone/options/build.zig+2-5
...@@ -1,13 +1,10 @@...@@ -1,13 +1,10 @@
1const std = @import("std");1const std = @import("std");
22
3pub fn build(b: *std.Build) void {3pub fn build(b: *std.Build) void {
4 const target = b.standardTargetOptions(.{});
5 const optimize = b.standardOptimizeOption(.{});
6
7 const main = b.addTest(.{4 const main = b.addTest(.{
8 .root_source_file = .{ .path = "src/main.zig" },5 .root_source_file = .{ .path = "src/main.zig" },
9 .target = target,6 .target = .{},
10 .optimize = optimize,7 .optimize = .Debug,
11 });8 });
129
13 const options = b.addOptions();10 const options = b.addOptions();