| author | |
| committer | |
| log | 7bccef3e4e4d48c2e1d34ec28754d8c33902f2bb |
| tree | cc708ebeb8f8885c3ca4cfaa273a1eaee5101f87 |
| parent | 26bdc836d2d9b2654f7f95fec34c6276070f2a59 |
And use it to implement InstallDir Step watch integration.
I'm not seeing any events triggered when I run `mkdir` in the watched
directory, however, and I have not yet figured out why.3 files changed, 75 insertions(+), 21 deletions(-)
lib/std/Build/Step.zig+39-2| ... | @@ -160,6 +160,7 @@ pub const Inputs = struct { | ... | @@ -160,6 +160,7 @@ pub const Inputs = struct { |
| 160 | }; | 160 | }; |
| 161 | 161 | ||
| 162 | pub const Table = std.ArrayHashMapUnmanaged(Build.Cache.Path, Files, Build.Cache.Path.TableAdapter, false); | 162 | pub const Table = std.ArrayHashMapUnmanaged(Build.Cache.Path, Files, Build.Cache.Path.TableAdapter, false); |
| 163 | /// The special file name "." means any changes inside the directory. | ||
| 163 | pub const Files = std.ArrayListUnmanaged([]const u8); | 164 | pub const Files = std.ArrayListUnmanaged([]const u8); |
| 164 | 165 | ||
| 165 | pub fn populated(inputs: *Inputs) bool { | 166 | pub fn populated(inputs: *Inputs) bool { |
| ... | @@ -611,8 +612,9 @@ pub fn clearWatchInputs(step: *Step) void { | ... | @@ -611,8 +612,9 @@ pub fn clearWatchInputs(step: *Step) void { |
| 611 | step.inputs.clear(gpa); | 612 | step.inputs.clear(gpa); |
| 612 | } | 613 | } |
| 613 | 614 | ||
| 614 | pub fn addWatchInput(step: *Step, lazy_path: Build.LazyPath) Allocator.Error!void { | 615 | /// Places a *file* dependency on the path. |
| 615 | switch (lazy_path) { | 616 | pub fn addWatchInput(step: *Step, lazy_file: Build.LazyPath) Allocator.Error!void { |
| 617 | switch (lazy_file) { | ||
| 616 | .src_path => |src_path| try addWatchInputFromBuilder(step, src_path.owner, src_path.sub_path), | 618 | .src_path => |src_path| try addWatchInputFromBuilder(step, src_path.owner, src_path.sub_path), |
| 617 | .dependency => |d| try addWatchInputFromBuilder(step, d.dependency.builder, d.sub_path), | 619 | .dependency => |d| try addWatchInputFromBuilder(step, d.dependency.builder, d.sub_path), |
| 618 | .cwd_relative => |path_string| { | 620 | .cwd_relative => |path_string| { |
| ... | @@ -629,6 +631,34 @@ pub fn addWatchInput(step: *Step, lazy_path: Build.LazyPath) Allocator.Error!voi | ... | @@ -629,6 +631,34 @@ pub fn addWatchInput(step: *Step, lazy_path: Build.LazyPath) Allocator.Error!voi |
| 629 | } | 631 | } |
| 630 | } | 632 | } |
| 631 | 633 | ||
| 634 | /// Any changes inside the directory will trigger invalidation. | ||
| 635 | /// | ||
| 636 | /// See also `addDirectoryWatchInputFromPath` which takes a `Build.Cache.Path` instead. | ||
| 637 | pub fn addDirectoryWatchInput(step: *Step, lazy_directory: Build.LazyPath) Allocator.Error!void { | ||
| 638 | switch (lazy_directory) { | ||
| 639 | .src_path => |src_path| try addDirectoryWatchInputFromBuilder(step, src_path.owner, src_path.sub_path), | ||
| 640 | .dependency => |d| try addDirectoryWatchInputFromBuilder(step, d.dependency.builder, d.sub_path), | ||
| 641 | .cwd_relative => |path_string| { | ||
| 642 | try addDirectoryWatchInputFromPath(step, .{ | ||
| 643 | .root_dir = .{ | ||
| 644 | .path = null, | ||
| 645 | .handle = std.fs.cwd(), | ||
| 646 | }, | ||
| 647 | .sub_path = path_string, | ||
| 648 | }); | ||
| 649 | }, | ||
| 650 | // Nothing to watch because this dependency edge is modeled instead via `dependants`. | ||
| 651 | .generated => {}, | ||
| 652 | } | ||
| 653 | } | ||
| 654 | |||
| 655 | /// Any changes inside the directory will trigger invalidation. | ||
| 656 | /// | ||
| 657 | /// See also `addDirectoryWatchInput` which takes a `Build.LazyPath` instead. | ||
| 658 | pub fn addDirectoryWatchInputFromPath(step: *Step, path: Build.Cache.Path) !void { | ||
| 659 | return addWatchInputFromPath(step, path, "."); | ||
| 660 | } | ||
| 661 | |||
| 632 | fn addWatchInputFromBuilder(step: *Step, builder: *Build, sub_path: []const u8) !void { | 662 | fn addWatchInputFromBuilder(step: *Step, builder: *Build, sub_path: []const u8) !void { |
| 633 | return addWatchInputFromPath(step, .{ | 663 | return addWatchInputFromPath(step, .{ |
| 634 | .root_dir = builder.build_root, | 664 | .root_dir = builder.build_root, |
| ... | @@ -636,6 +666,13 @@ fn addWatchInputFromBuilder(step: *Step, builder: *Build, sub_path: []const u8) | ... | @@ -636,6 +666,13 @@ fn addWatchInputFromBuilder(step: *Step, builder: *Build, sub_path: []const u8) |
| 636 | }, std.fs.path.basename(sub_path)); | 666 | }, std.fs.path.basename(sub_path)); |
| 637 | } | 667 | } |
| 638 | 668 | ||
| 669 | fn addDirectoryWatchInputFromBuilder(step: *Step, builder: *Build, sub_path: []const u8) !void { | ||
| 670 | return addDirectoryWatchInputFromPath(step, .{ | ||
| 671 | .root_dir = builder.build_root, | ||
| 672 | .sub_path = sub_path, | ||
| 673 | }); | ||
| 674 | } | ||
| 675 | |||
| 639 | fn addWatchInputFromPath(step: *Step, path: Build.Cache.Path, basename: []const u8) !void { | 676 | fn addWatchInputFromPath(step: *Step, path: Build.Cache.Path, basename: []const u8) !void { |
| 640 | const gpa = step.owner.allocator; | 677 | const gpa = step.owner.allocator; |
| 641 | const gop = try step.inputs.table.getOrPut(gpa, path); | 678 | const gop = try step.inputs.table.getOrPut(gpa, path); |
lib/std/Build/Step/InstallDir.zig+16-10| ... | @@ -59,12 +59,14 @@ fn make(step: *Step, prog_node: std.Progress.Node) !void { | ... | @@ -59,12 +59,14 @@ fn make(step: *Step, prog_node: std.Progress.Node) !void { |
| 59 | _ = prog_node; | 59 | _ = prog_node; |
| 60 | const b = step.owner; | 60 | const b = step.owner; |
| 61 | const install_dir: *InstallDir = @fieldParentPtr("step", step); | 61 | const install_dir: *InstallDir = @fieldParentPtr("step", step); |
| 62 | step.clearWatchInputs(); | ||
| 62 | const arena = b.allocator; | 63 | const arena = b.allocator; |
| 63 | const dest_prefix = b.getInstallPath(install_dir.options.install_dir, install_dir.options.install_subdir); | 64 | const dest_prefix = b.getInstallPath(install_dir.options.install_dir, install_dir.options.install_subdir); |
| 64 | const src_dir_path = install_dir.options.source_dir.getPath2(b, step); | 65 | const src_dir_path = install_dir.options.source_dir.getPath3(b, step); |
| 65 | var src_dir = b.build_root.handle.openDir(src_dir_path, .{ .iterate = true }) catch |err| { | 66 | try step.addDirectoryWatchInput(install_dir.options.source_dir); |
| 66 | return step.fail("unable to open source directory '{}{s}': {s}", .{ | 67 | var src_dir = src_dir_path.root_dir.handle.openDir(src_dir_path.subPathOpt() orelse ".", .{ .iterate = true }) catch |err| { |
| 67 | b.build_root, src_dir_path, @errorName(err), | 68 | return step.fail("unable to open source directory '{}': {s}", .{ |
| 69 | src_dir_path, @errorName(err), | ||
| 68 | }); | 70 | }); |
| 69 | }; | 71 | }; |
| 70 | defer src_dir.close(); | 72 | defer src_dir.close(); |
| ... | @@ -88,12 +90,16 @@ fn make(step: *Step, prog_node: std.Progress.Node) !void { | ... | @@ -88,12 +90,16 @@ fn make(step: *Step, prog_node: std.Progress.Node) !void { |
| 88 | } | 90 | } |
| 89 | 91 | ||
| 90 | // relative to src build root | 92 | // relative to src build root |
| 91 | const src_sub_path = b.pathJoin(&.{ src_dir_path, entry.path }); | 93 | const src_sub_path = try src_dir_path.join(arena, entry.path); |
| 92 | const dest_path = b.pathJoin(&.{ dest_prefix, entry.path }); | 94 | const dest_path = b.pathJoin(&.{ dest_prefix, entry.path }); |
| 93 | const cwd = fs.cwd(); | 95 | const cwd = fs.cwd(); |
| 94 | 96 | ||
| 95 | switch (entry.kind) { | 97 | switch (entry.kind) { |
| 96 | .directory => try cwd.makePath(dest_path), | 98 | .directory => { |
| 99 | const subdir_path = try src_dir_path.join(arena, entry.path); | ||
| 100 | try step.addDirectoryWatchInputFromPath(subdir_path); | ||
| 101 | try cwd.makePath(dest_path); | ||
| 102 | }, | ||
| 97 | .file => { | 103 | .file => { |
| 98 | for (install_dir.options.blank_extensions) |ext| { | 104 | for (install_dir.options.blank_extensions) |ext| { |
| 99 | if (mem.endsWith(u8, entry.path, ext)) { | 105 | if (mem.endsWith(u8, entry.path, ext)) { |
| ... | @@ -103,14 +109,14 @@ fn make(step: *Step, prog_node: std.Progress.Node) !void { | ... | @@ -103,14 +109,14 @@ fn make(step: *Step, prog_node: std.Progress.Node) !void { |
| 103 | } | 109 | } |
| 104 | 110 | ||
| 105 | const prev_status = fs.Dir.updateFile( | 111 | const prev_status = fs.Dir.updateFile( |
| 106 | b.build_root.handle, | 112 | src_sub_path.root_dir.handle, |
| 107 | src_sub_path, | 113 | src_sub_path.sub_path, |
| 108 | cwd, | 114 | cwd, |
| 109 | dest_path, | 115 | dest_path, |
| 110 | .{}, | 116 | .{}, |
| 111 | ) catch |err| { | 117 | ) catch |err| { |
| 112 | return step.fail("unable to update file from '{}{s}' to '{s}': {s}", .{ | 118 | return step.fail("unable to update file from '{}' to '{s}': {s}", .{ |
| 113 | b.build_root, src_sub_path, dest_path, @errorName(err), | 119 | src_sub_path, dest_path, @errorName(err), |
| 114 | }); | 120 | }); |
| 115 | }; | 121 | }; |
| 116 | all_cached = all_cached and prev_status == .fresh; | 122 | all_cached = all_cached and prev_status == .fresh; |
lib/std/Build/Watch.zig+20-9| ... | @@ -12,10 +12,13 @@ generation: Generation, | ... | @@ -12,10 +12,13 @@ generation: Generation, |
| 12 | 12 | ||
| 13 | pub const fan_mask: std.os.linux.fanotify.MarkMask = .{ | 13 | pub const fan_mask: std.os.linux.fanotify.MarkMask = .{ |
| 14 | .CLOSE_WRITE = true, | 14 | .CLOSE_WRITE = true, |
| 15 | .CREATE = true, | ||
| 15 | .DELETE = true, | 16 | .DELETE = true, |
| 17 | .DELETE_SELF = true, | ||
| 18 | .EVENT_ON_CHILD = true, | ||
| 16 | .MOVED_FROM = true, | 19 | .MOVED_FROM = true, |
| 17 | .MOVED_TO = true, | 20 | .MOVED_TO = true, |
| 18 | .EVENT_ON_CHILD = true, | 21 | .MOVE_SELF = true, |
| 19 | }; | 22 | }; |
| 20 | 23 | ||
| 21 | pub const init: Watch = .{ | 24 | pub const init: Watch = .{ |
| ... | @@ -32,6 +35,7 @@ pub const init: Watch = .{ | ... | @@ -32,6 +35,7 @@ pub const init: Watch = .{ |
| 32 | const DirTable = std.ArrayHashMapUnmanaged(Cache.Path, void, Cache.Path.TableAdapter, false); | 35 | const DirTable = std.ArrayHashMapUnmanaged(Cache.Path, void, Cache.Path.TableAdapter, false); |
| 33 | 36 | ||
| 34 | const HandleTable = std.ArrayHashMapUnmanaged(LinuxFileHandle, ReactionSet, LinuxFileHandle.Adapter, false); | 37 | const HandleTable = std.ArrayHashMapUnmanaged(LinuxFileHandle, ReactionSet, LinuxFileHandle.Adapter, false); |
| 38 | /// Special key of "." means any changes in this directory trigger the steps. | ||
| 35 | const ReactionSet = std.StringArrayHashMapUnmanaged(StepSet); | 39 | const ReactionSet = std.StringArrayHashMapUnmanaged(StepSet); |
| 36 | const StepSet = std.AutoArrayHashMapUnmanaged(*Step, Generation); | 40 | const StepSet = std.AutoArrayHashMapUnmanaged(*Step, Generation); |
| 37 | 41 | ||
| ... | @@ -149,14 +153,10 @@ pub fn markDirtySteps(w: *Watch, gpa: Allocator) !bool { | ... | @@ -149,14 +153,10 @@ pub fn markDirtySteps(w: *Watch, gpa: Allocator) !bool { |
| 149 | const file_name = std.mem.span(file_name_z); | 153 | const file_name = std.mem.span(file_name_z); |
| 150 | const lfh: Watch.LinuxFileHandle = .{ .handle = file_handle }; | 154 | const lfh: Watch.LinuxFileHandle = .{ .handle = file_handle }; |
| 151 | if (w.handle_table.getPtr(lfh)) |reaction_set| { | 155 | if (w.handle_table.getPtr(lfh)) |reaction_set| { |
| 152 | if (reaction_set.getPtr(file_name)) |step_set| { | 156 | if (reaction_set.getPtr(".")) |glob_set| |
| 153 | for (step_set.keys()) |step| { | 157 | any_dirty = markStepSetDirty(gpa, glob_set, any_dirty); |
| 154 | if (step.state != .precheck_done) { | 158 | if (reaction_set.getPtr(file_name)) |step_set| |
| 155 | step.recursiveReset(gpa); | 159 | any_dirty = markStepSetDirty(gpa, step_set, any_dirty); |
| 156 | any_dirty = true; | ||
| 157 | } | ||
| 158 | } | ||
| 159 | } | ||
| 160 | } | 160 | } |
| 161 | }, | 161 | }, |
| 162 | else => |t| std.log.warn("unexpected fanotify event '{s}'", .{@tagName(t)}), | 162 | else => |t| std.log.warn("unexpected fanotify event '{s}'", .{@tagName(t)}), |
| ... | @@ -187,3 +187,14 @@ fn markAllFilesDirty(w: *Watch, gpa: Allocator) void { | ... | @@ -187,3 +187,14 @@ fn markAllFilesDirty(w: *Watch, gpa: Allocator) void { |
| 187 | } | 187 | } |
| 188 | } | 188 | } |
| 189 | } | 189 | } |
| 190 | |||
| 191 | fn markStepSetDirty(gpa: Allocator, step_set: *StepSet, any_dirty: bool) bool { | ||
| 192 | var this_any_dirty = false; | ||
| 193 | for (step_set.keys()) |step| { | ||
| 194 | if (step.state != .precheck_done) { | ||
| 195 | step.recursiveReset(gpa); | ||
| 196 | this_any_dirty = true; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | return any_dirty or this_any_dirty; | ||
| 200 | } |