diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2024-07-08 23:42:20 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2024-07-12 00:14:07 -0700 |
| commit | bbd90a562efd6e802ed41df2649a05fad763a4de (patch) | |
| tree | c5276879ad6ade65af80393019ac649f9e6c1734 /lib/std/Build/Step.zig | |
| parent | deea36250ffe458d92b32b1ad090b8a958ba8082 (diff) | |
| download | zig-bbd90a562efd6e802ed41df2649a05fad763a4de.tar.gz zig-bbd90a562efd6e802ed41df2649a05fad763a4de.zip | |
build runner: implement --watch (work-in-progress)
I'm still learning how the fanotify API works but I think after playing
with it in this commit, I finally know how to implement it, at least on
Linux. This commit does not accomplish the goal but I want to take the
code in a different direction and still be able to reference this point
in time by viewing a source control diff.
I think the move is going to be saving the file_handle for the parent
directory, which combined with the dirent names is how we can correlate
the events back to the Step instances that have registered file system
inputs. I predict this to be similar to implementations on other
operating systems.
Diffstat (limited to 'lib/std/Build/Step.zig')
| -rw-r--r-- | lib/std/Build/Step.zig | 73 |
1 files changed, 51 insertions, 22 deletions
diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 91fe298666..fcab831f67 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -7,6 +7,16 @@ dependencies: std.ArrayList(*Step), /// This field is empty during execution of the user's build script, and /// then populated during dependency loop checking in the build runner. dependants: std.ArrayListUnmanaged(*Step), +/// Collects the set of files that retrigger this step to run. +/// +/// This is used by the build system's implementation of `--watch` but it can +/// also be potentially useful for IDEs to know what effects editing a +/// particular file has. +/// +/// Populated within `make`. Implementation may choose to clear and repopulate, +/// retain previous value, or update. +inputs: Inputs, + state: State, /// Set this field to declare an upper bound on the amount of bytes of memory it will /// take to run the step. Zero means no limit. @@ -63,6 +73,11 @@ pub const MakeFn = *const fn (step: *Step, prog_node: std.Progress.Node) anyerro pub const State = enum { precheck_unstarted, precheck_started, + /// This is also used to indicate "dirty" steps that have been modified + /// after a previous build completed, in which case, the step may or may + /// not have been completed before. Either way, one or more of its direct + /// file system inputs have been modified, meaning that the step needs to + /// be re-evaluated. precheck_done, running, dependency_failure, @@ -134,6 +149,26 @@ pub const Run = @import("Step/Run.zig"); pub const TranslateC = @import("Step/TranslateC.zig"); pub const WriteFile = @import("Step/WriteFile.zig"); +pub const Inputs = struct { + table: Table, + + pub const init: Inputs = .{ + .table = .{}, + }; + + pub const Table = std.ArrayHashMapUnmanaged(Build.Cache.Path, Files, Build.Cache.Path.TableAdapter, false); + pub const Files = std.ArrayListUnmanaged([]const u8); + + pub fn populated(inputs: *Inputs) bool { + return inputs.table.count() != 0; + } + + pub fn clear(inputs: *Inputs, gpa: Allocator) void { + for (inputs.table.values()) |*files| files.deinit(gpa); + inputs.table.clearRetainingCapacity(); + } +}; + pub const StepOptions = struct { id: Id, name: []const u8, @@ -153,6 +188,7 @@ pub fn init(options: StepOptions) Step { .makeFn = options.makeFn, .dependencies = std.ArrayList(*Step).init(arena), .dependants = .{}, + .inputs = Inputs.init, .state = .precheck_unstarted, .max_rss = options.max_rss, .debug_stack_trace = blk: { @@ -542,19 +578,19 @@ pub fn allocPrintCmd2( return buf.toOwnedSlice(arena); } -pub fn cacheHit(s: *Step, man: *std.Build.Cache.Manifest) !bool { +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; } -fn failWithCacheError(s: *Step, man: *const std.Build.Cache.Manifest, err: anyerror) anyerror { +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; const prefix = man.cache.prefixes()[pp.prefix].path orelse ""; return s.fail("{s}: {s}/{s}", .{ @errorName(err), prefix, pp.sub_path }); } -pub fn writeManifest(s: *Step, man: *std.Build.Cache.Manifest) !void { +pub fn writeManifest(s: *Step, man: *Build.Cache.Manifest) !void { if (s.test_results.isSuccess()) { man.writeManifest() catch |err| { try s.addError("unable to write cache manifest: {s}", .{@errorName(err)}); @@ -568,44 +604,37 @@ fn oom(err: anytype) noreturn { } } -pub fn addWatchInput(step: *Step, lazy_path: std.Build.LazyPath) void { +pub fn addWatchInput(step: *Step, lazy_path: Build.LazyPath) void { errdefer |err| oom(err); - const w = step.owner.graph.watch orelse return; switch (lazy_path) { - .src_path => |src_path| try addWatchInputFromBuilder(step, w, src_path.owner, src_path.sub_path), - .dependency => |d| try addWatchInputFromBuilder(step, w, d.dependency.builder, d.sub_path), + .src_path => |src_path| try addWatchInputFromBuilder(step, src_path.owner, src_path.sub_path), + .dependency => |d| try addWatchInputFromBuilder(step, d.dependency.builder, d.sub_path), .cwd_relative => |path_string| { - try addWatchInputFromPath(w, .{ + try addWatchInputFromPath(step, .{ .root_dir = .{ .path = null, .handle = std.fs.cwd(), }, .sub_path = std.fs.path.dirname(path_string) orelse "", - }, .{ - .step = step, - .basename = std.fs.path.basename(path_string), - }); + }, std.fs.path.basename(path_string)); }, // Nothing to watch because this dependency edge is modeled instead via `dependants`. .generated => {}, } } -fn addWatchInputFromBuilder(step: *Step, w: *std.Build.Watch, builder: *std.Build, sub_path: []const u8) !void { - return addWatchInputFromPath(w, .{ +fn addWatchInputFromBuilder(step: *Step, builder: *Build, sub_path: []const u8) !void { + return addWatchInputFromPath(step, .{ .root_dir = builder.build_root, .sub_path = std.fs.path.dirname(sub_path) orelse "", - }, .{ - .step = step, - .basename = std.fs.path.basename(sub_path), - }); + }, std.fs.path.basename(sub_path)); } -fn addWatchInputFromPath(w: *std.Build.Watch, path: std.Build.Cache.Path, match: std.Build.Watch.Match) !void { - const gpa = match.step.owner.allocator; - const gop = try w.table.getOrPut(gpa, path); +fn addWatchInputFromPath(step: *Step, path: Build.Cache.Path, basename: []const u8) !void { + const gpa = step.owner.allocator; + const gop = try step.inputs.table.getOrPut(gpa, path); if (!gop.found_existing) gop.value_ptr.* = .{}; - try gop.value_ptr.put(gpa, match, {}); + try gop.value_ptr.append(gpa, basename); } test { |
