From 2e429697865ebcadc001a7d167ef964b3f1393a2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 10 Jul 2024 17:14:54 -0700 Subject: [PATCH] std.Build.Step.Run: integrate with --watch --- lib/std/Build/Step.zig | 40 ++++++++++++++++++++++++++++++++++++++ lib/std/Build/Step/Run.zig | 8 ++++---- 2 files changed, 44 insertions(+), 4 deletions(-) diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 5c77bd3367..13cd47981b 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -582,11 +582,26 @@ pub fn allocPrintCmd2( return buf.toOwnedSlice(arena); } +/// Prefer `cacheHitAndWatch` unless you already added watch inputs +/// separately from using the cache system. pub fn cacheHit(s: *Step, man: *Build.Cache.Manifest) !bool { s.result_cached = man.hit() catch |err| return failWithCacheError(s, man, err); return s.result_cached; } +/// Clears previous watch inputs, if any, and then populates watch inputs from +/// the full set of files picked up by the cache manifest. +/// +/// Must be accompanied with `writeManifestAndWatch`. +pub fn cacheHitAndWatch(s: *Step, man: *Build.Cache.Manifest) !bool { + const is_hit = man.hit() catch |err| return failWithCacheError(s, man, err); + s.result_cached = is_hit; + // The above call to hit() populates the manifest with files, so in case of + // a hit, we need to populate watch inputs. + if (is_hit) try setWatchInputsFromManifest(s, man); + return is_hit; +} + fn failWithCacheError(s: *Step, man: *const Build.Cache.Manifest, err: anyerror) anyerror { const i = man.failed_file_index orelse return err; const pp = man.files.keys()[i].prefixed_path; @@ -594,6 +609,8 @@ fn failWithCacheError(s: *Step, man: *const Build.Cache.Manifest, err: anyerror) return s.fail("{s}: {s}/{s}", .{ @errorName(err), prefix, pp.sub_path }); } +/// Prefer `writeManifestAndWatch` unless you already added watch inputs +/// separately from using the cache system. pub fn writeManifest(s: *Step, man: *Build.Cache.Manifest) !void { if (s.test_results.isSuccess()) { man.writeManifest() catch |err| { @@ -602,6 +619,29 @@ pub fn writeManifest(s: *Step, man: *Build.Cache.Manifest) !void { } } +/// Clears previous watch inputs, if any, and then populates watch inputs from +/// the full set of files picked up by the cache manifest. +/// +/// Must be accompanied with `cacheHitAndWatch`. +pub fn writeManifestAndWatch(s: *Step, man: *Build.Cache.Manifest) !void { + try writeManifest(s, man); + try setWatchInputsFromManifest(s, man); +} + +fn setWatchInputsFromManifest(s: *Step, man: *Build.Cache.Manifest) !void { + const arena = s.owner.allocator; + const prefixes = man.cache.prefixes(); + clearWatchInputs(s); + for (man.files.keys()) |file| { + // The file path data is freed when the cache manifest is cleaned up at the end of `make`. + const sub_path = try arena.dupe(u8, file.prefixed_path.sub_path); + try addWatchInputFromPath(s, .{ + .root_dir = prefixes[file.prefixed_path.prefix], + .sub_path = std.fs.path.dirname(sub_path) orelse "", + }, std.fs.path.basename(sub_path)); + } +} + /// For steps that have a single input that never changes when re-running `make`. pub fn singleUnchangingWatchInput(step: *Step, lazy_path: Build.LazyPath) Allocator.Error!void { if (!step.inputs.populated()) try step.addWatchInput(lazy_path); diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 69d6b393fd..b1e7060f3e 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -615,7 +615,7 @@ fn make(step: *Step, prog_node: std.Progress.Node) !void { // On Windows we don't have rpaths so we have to add .dll search paths to PATH run.addPathForDynLibs(artifact); } - const file_path = artifact.installed_path orelse artifact.generated_bin.?.path.?; // the path is guaranteed to be set + const file_path = artifact.installed_path orelse artifact.generated_bin.?.path.?; try argv_list.append(file_path); @@ -665,7 +665,7 @@ fn make(step: *Step, prog_node: std.Progress.Node) !void { _ = try man.addFile(lazy_path.getPath2(b, step), null); } - if (!has_side_effects and try step.cacheHit(&man)) { + if (!has_side_effects and try step.cacheHitAndWatch(&man)) { // cache hit, skip running command const digest = man.final(); @@ -719,7 +719,7 @@ fn make(step: *Step, prog_node: std.Progress.Node) !void { } try runCommand(run, argv_list.items, has_side_effects, output_dir_path, prog_node); - if (!has_side_effects) try step.writeManifest(&man); + if (!has_side_effects) try step.writeManifestAndWatch(&man); return; }; @@ -795,7 +795,7 @@ fn make(step: *Step, prog_node: std.Progress.Node) !void { }; } - if (!has_side_effects) try step.writeManifest(&man); + if (!has_side_effects) try step.writeManifestAndWatch(&man); try populateGeneratedPaths( arena,