Commit 405bf1b091

Andrew Kelley <andrew@ziglang.org>
2023-03-04 01:29:59
std.Build.ConfigHeaderStep: integrate with the cache system
1 parent 2cc33f5
Changed files (2)
lib/std/Build/ConfigHeaderStep.zig
@@ -1,9 +1,3 @@
-const std = @import("../std.zig");
-const ConfigHeaderStep = @This();
-const Step = std.Build.Step;
-
-pub const base_id: Step.Id = .config_header;
-
 pub const Style = union(enum) {
     /// The configure format supported by autotools. It uses `#undef foo` to
     /// mark lines that can be substituted with different values.
@@ -41,6 +35,8 @@ style: Style,
 max_bytes: usize,
 include_path: []const u8,
 
+pub const base_id: Step.Id = .config_header;
+
 pub const Options = struct {
     style: Style = .blank,
     max_bytes: usize = 2 * 1024 * 1024,
@@ -162,23 +158,15 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
     const b = step.owner;
     const self = @fieldParentPtr(ConfigHeaderStep, "step", step);
     const gpa = b.allocator;
+    const arena = b.allocator;
 
-    // The cache is used here not really as a way to speed things up - because writing
-    // the data to a file would probably be very fast - but as a way to find a canonical
-    // location to put build artifacts.
+    var man = b.cache.obtain();
+    defer man.deinit();
 
-    // If, for example, a hard-coded path was used as the location to put ConfigHeaderStep
-    // files, then two ConfigHeaderStep executing in parallel might clobber each other.
-
-    // TODO port the cache system from the compiler to zig std lib. Until then
-    // we construct the path directly, and no "cache hit" detection happens;
-    // the files are always written.
-    // Note there is very similar code over in WriteFileStep
-    const Hasher = std.crypto.auth.siphash.SipHash128(1, 3);
     // Random bytes to make ConfigHeaderStep unique. Refresh this with new
     // random bytes when ConfigHeaderStep implementation is modified in a
     // non-backwards-compatible way.
-    var hash = Hasher.init("PGuDTpidxyMqnkGM");
+    man.hash.add(@as(u32, 0xdef08d23));
 
     var output = std.ArrayList(u8).init(gpa);
     defer output.deinit();
@@ -191,13 +179,13 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
         .autoconf => |file_source| {
             try output.appendSlice(c_generated_line);
             const src_path = file_source.getPath(b);
-            const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
+            const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes);
             try render_autoconf(step, contents, &output, self.values, src_path);
         },
         .cmake => |file_source| {
             try output.appendSlice(c_generated_line);
             const src_path = file_source.getPath(b);
-            const contents = try std.fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes);
+            const contents = try std.fs.cwd().readFileAlloc(arena, src_path, self.max_bytes);
             try render_cmake(step, contents, &output, self.values, src_path);
         },
         .blank => {
@@ -210,39 +198,40 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
         },
     }
 
-    hash.update(output.items);
+    man.hash.addBytes(output.items);
 
-    var digest: [16]u8 = undefined;
-    hash.final(&digest);
-    var hash_basename: [digest.len * 2]u8 = undefined;
-    _ = std.fmt.bufPrint(
-        &hash_basename,
-        "{s}",
-        .{std.fmt.fmtSliceHexLower(&digest)},
-    ) catch unreachable;
+    if (try step.cacheHit(&man)) {
+        const digest = man.final();
+        self.output_file.path = try b.cache_root.join(arena, &.{
+            "o", &digest, self.include_path,
+        });
+        return;
+    }
 
-    const output_dir = try b.cache_root.join(gpa, &.{ "o", &hash_basename });
+    const digest = man.final();
 
     // If output_path has directory parts, deal with them.  Example:
     // output_dir is zig-cache/o/HASH
     // output_path is libavutil/avconfig.h
     // We want to open directory zig-cache/o/HASH/libavutil/
     // but keep output_dir as zig-cache/o/HASH for -I include
-    const sub_dir_path = if (std.fs.path.dirname(self.include_path)) |d|
-        try std.fs.path.join(gpa, &.{ output_dir, d })
-    else
-        output_dir;
+    const sub_path = try std.fs.path.join(arena, &.{ "o", &digest, self.include_path });
+    const sub_path_dirname = std.fs.path.dirname(sub_path).?;
 
-    var dir = std.fs.cwd().makeOpenPath(sub_dir_path, .{}) catch |err| {
-        return step.fail("unable to make path '{s}': {s}", .{ output_dir, @errorName(err) });
+    b.cache_root.handle.makePath(sub_path_dirname) catch |err| {
+        return step.fail("unable to make path '{}{s}': {s}", .{
+            b.cache_root, sub_path_dirname, @errorName(err),
+        });
     };
-    defer dir.close();
 
-    try dir.writeFile(std.fs.path.basename(self.include_path), output.items);
+    b.cache_root.handle.writeFile(sub_path, output.items) catch |err| {
+        return step.fail("unable to write file '{}{s}': {s}", .{
+            b.cache_root, sub_path, @errorName(err),
+        });
+    };
 
-    self.output_file.path = try std.fs.path.join(b.allocator, &.{
-        output_dir, self.include_path,
-    });
+    self.output_file.path = try b.cache_root.join(arena, &.{sub_path});
+    try man.writeManifest();
 }
 
 fn render_autoconf(
@@ -442,3 +431,7 @@ fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) !
         },
     }
 }
+
+const std = @import("../std.zig");
+const ConfigHeaderStep = @This();
+const Step = std.Build.Step;
lib/std/Build/WriteFileStep.zig
@@ -187,7 +187,6 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
     }
 
     if (try step.cacheHit(&man)) {
-        // Cache hit, skip writing file data.
         const digest = man.final();
         for (wf.files.items) |file| {
             file.generated_file.path = try b.cache_root.join(