mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 13:54:21 +00:00
std.Build.Watch: make dirty steps invalidate each other
and make failed steps always be invalidated and make steps that don't need to be reevaluated marked as cached
This commit is contained in:
parent
6f89824c22
commit
001ff7b3b2
3 changed files with 82 additions and 40 deletions
|
|
@ -500,9 +500,10 @@ pub fn main() !void {
|
|||
const events_len = try std.posix.poll(&poll_fds, timeout);
|
||||
if (events_len == 0) {
|
||||
debouncing_node.end();
|
||||
Watch.markFailedStepsDirty(gpa, run.step_stack.keys());
|
||||
continue :rebuild;
|
||||
}
|
||||
if (try markDirtySteps(&w)) {
|
||||
if (try w.markDirtySteps(gpa)) {
|
||||
if (!debouncing) {
|
||||
debouncing = true;
|
||||
debouncing_node.end();
|
||||
|
|
@ -513,44 +514,6 @@ pub fn main() !void {
|
|||
}
|
||||
}
|
||||
|
||||
fn markDirtySteps(w: *Watch) !bool {
|
||||
const fanotify = std.os.linux.fanotify;
|
||||
const M = fanotify.event_metadata;
|
||||
var events_buf: [256 + 4096]u8 = undefined;
|
||||
var any_dirty = false;
|
||||
while (true) {
|
||||
var len = std.posix.read(w.fan_fd, &events_buf) catch |err| switch (err) {
|
||||
error.WouldBlock => return any_dirty,
|
||||
else => |e| return e,
|
||||
};
|
||||
var meta: [*]align(1) M = @ptrCast(&events_buf);
|
||||
while (len >= @sizeOf(M) and meta[0].event_len >= @sizeOf(M) and meta[0].event_len <= len) : ({
|
||||
len -= meta[0].event_len;
|
||||
meta = @ptrCast(@as([*]u8, @ptrCast(meta)) + meta[0].event_len);
|
||||
}) {
|
||||
assert(meta[0].vers == M.VERSION);
|
||||
const fid: *align(1) fanotify.event_info_fid = @ptrCast(meta + 1);
|
||||
switch (fid.hdr.info_type) {
|
||||
.DFID_NAME => {
|
||||
const file_handle: *align(1) std.os.linux.file_handle = @ptrCast(&fid.handle);
|
||||
const file_name_z: [*:0]u8 = @ptrCast((&file_handle.f_handle).ptr + file_handle.handle_bytes);
|
||||
const file_name = mem.span(file_name_z);
|
||||
const lfh: Watch.LinuxFileHandle = .{ .handle = file_handle };
|
||||
if (w.handle_table.getPtr(lfh)) |reaction_set| {
|
||||
if (reaction_set.getPtr(file_name)) |step_set| {
|
||||
for (step_set.keys()) |step| {
|
||||
step.state = .precheck_done;
|
||||
any_dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
else => |t| std.log.warn("unexpected fanotify event '{s}'", .{@tagName(t)}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const Run = struct {
|
||||
max_rss: u64,
|
||||
max_rss_is_default: bool,
|
||||
|
|
@ -1319,7 +1282,7 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
|
|||
\\ --skip-oom-steps Instead of failing, skip steps that would exceed --maxrss
|
||||
\\ --fetch Exit after fetching dependency tree
|
||||
\\ --watch Continuously rebuild when source files are modified
|
||||
\\ --debounce <ms> Delay before rebuilding after watched file detection
|
||||
\\ --debounce <ms> Delay before rebuilding after changed file detected
|
||||
\\
|
||||
\\Project-Specific Options:
|
||||
\\
|
||||
|
|
|
|||
|
|
@ -637,6 +637,31 @@ fn addWatchInputFromPath(step: *Step, path: Build.Cache.Path, basename: []const
|
|||
try gop.value_ptr.append(gpa, basename);
|
||||
}
|
||||
|
||||
fn reset(step: *Step, gpa: Allocator) void {
|
||||
assert(step.state == .precheck_done);
|
||||
|
||||
step.result_error_msgs.clearRetainingCapacity();
|
||||
step.result_stderr = "";
|
||||
step.result_cached = false;
|
||||
step.result_duration_ns = null;
|
||||
step.result_peak_rss = 0;
|
||||
step.test_results = .{};
|
||||
|
||||
step.result_error_bundle.deinit(gpa);
|
||||
step.result_error_bundle = std.zig.ErrorBundle.empty;
|
||||
}
|
||||
|
||||
/// Implementation detail of file watching. Prepares the step for being re-evaluated.
|
||||
pub fn recursiveReset(step: *Step, gpa: Allocator) void {
|
||||
assert(step.state != .precheck_done);
|
||||
step.state = .precheck_done;
|
||||
step.reset(gpa);
|
||||
for (step.dependants.items) |dep| {
|
||||
if (dep.state == .precheck_done) continue;
|
||||
dep.recursiveReset(gpa);
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
_ = CheckFile;
|
||||
_ = CheckObject;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ const std = @import("../std.zig");
|
|||
const Watch = @This();
|
||||
const Step = std.Build.Step;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const assert = std.debug.assert;
|
||||
|
||||
dir_table: DirTable,
|
||||
/// Keyed differently but indexes correspond 1:1 with `dir_table`.
|
||||
|
|
@ -117,3 +118,56 @@ pub fn getDirHandle(gpa: Allocator, path: std.Build.Cache.Path) !LinuxFileHandle
|
|||
const stack_lfh: LinuxFileHandle = .{ .handle = stack_ptr };
|
||||
return stack_lfh.clone(gpa);
|
||||
}
|
||||
|
||||
pub fn markDirtySteps(w: *Watch, gpa: Allocator) !bool {
|
||||
const fanotify = std.os.linux.fanotify;
|
||||
const M = fanotify.event_metadata;
|
||||
var events_buf: [256 + 4096]u8 = undefined;
|
||||
var any_dirty = false;
|
||||
while (true) {
|
||||
var len = std.posix.read(w.fan_fd, &events_buf) catch |err| switch (err) {
|
||||
error.WouldBlock => return any_dirty,
|
||||
else => |e| return e,
|
||||
};
|
||||
var meta: [*]align(1) M = @ptrCast(&events_buf);
|
||||
while (len >= @sizeOf(M) and meta[0].event_len >= @sizeOf(M) and meta[0].event_len <= len) : ({
|
||||
len -= meta[0].event_len;
|
||||
meta = @ptrCast(@as([*]u8, @ptrCast(meta)) + meta[0].event_len);
|
||||
}) {
|
||||
assert(meta[0].vers == M.VERSION);
|
||||
const fid: *align(1) fanotify.event_info_fid = @ptrCast(meta + 1);
|
||||
switch (fid.hdr.info_type) {
|
||||
.DFID_NAME => {
|
||||
const file_handle: *align(1) std.os.linux.file_handle = @ptrCast(&fid.handle);
|
||||
const file_name_z: [*:0]u8 = @ptrCast((&file_handle.f_handle).ptr + file_handle.handle_bytes);
|
||||
const file_name = std.mem.span(file_name_z);
|
||||
const lfh: Watch.LinuxFileHandle = .{ .handle = file_handle };
|
||||
if (w.handle_table.getPtr(lfh)) |reaction_set| {
|
||||
if (reaction_set.getPtr(file_name)) |step_set| {
|
||||
for (step_set.keys()) |step| {
|
||||
if (step.state != .precheck_done) {
|
||||
step.recursiveReset(gpa);
|
||||
any_dirty = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
else => |t| std.log.warn("unexpected fanotify event '{s}'", .{@tagName(t)}),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn markFailedStepsDirty(gpa: Allocator, all_steps: []const *Step) void {
|
||||
for (all_steps) |step| switch (step.state) {
|
||||
.dependency_failure, .failure, .skipped => step.recursiveReset(gpa),
|
||||
else => continue,
|
||||
};
|
||||
// Now that all dirty steps have been found, the remaining steps that
|
||||
// succeeded from last run shall be marked "cached".
|
||||
for (all_steps) |step| switch (step.state) {
|
||||
.success => step.result_cached = true,
|
||||
else => continue,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue