@@ -7,6 +7,7 @@ const assert = std.debug.assert;
77const fatal = std .zig .fatal ;
88
99dir_table : DirTable ,
10+ build_file : Cache.Path ,
1011os : Os ,
1112generation : Generation ,
1213
@@ -31,6 +32,8 @@ const Os = switch (builtin.os.tag) {
3132
3233 /// Keyed differently but indexes correspond 1:1 with `dir_table`.
3334 handle_table : HandleTable ,
35+ /// Initialized lazily.
36+ build_file_handle : ? FileHandle = null ,
3437 poll_fds : [1 ]posix.pollfd ,
3538
3639 const HandleTable = std .ArrayHashMapUnmanaged (FileHandle , ReactionSet , FileHandle .Adapter , false );
@@ -103,15 +106,15 @@ const Os = switch (builtin.os.tag) {
103106 return stack_lfh .clone (gpa );
104107 }
105108
106- fn markDirtySteps (w : * Watch , gpa : Allocator ) ! bool {
109+ fn markDirtySteps (w : * Watch , gpa : Allocator ) ! WaitResult {
107110 const fan_fd = w .os .getFanFd ();
108111 const fanotify = std .os .linux .fanotify ;
109112 const M = fanotify .event_metadata ;
110113 var events_buf : [256 + 4096 ]u8 = undefined ;
111114 var any_dirty = false ;
112115 while (true ) {
113116 var len = posix .read (fan_fd , & events_buf ) catch | err | switch (err ) {
114- error .WouldBlock = > return any_dirty ,
117+ error .WouldBlock = > return if ( any_dirty ) .dirty else .timeout ,
115118 else = > | e | return e ,
116119 };
117120 var meta : [* ]align (1 ) M = @ptrCast (& events_buf );
@@ -124,7 +127,7 @@ const Os = switch (builtin.os.tag) {
124127 any_dirty = true ;
125128 std .log .warn ("file system watch queue overflowed; falling back to fstat" , .{});
126129 markAllFilesDirty (w , gpa );
127- return true ;
130+ return .dirty ;
128131 }
129132 const fid : * align (1 ) fanotify.event_info_fid = @ptrCast (meta + 1 );
130133 switch (fid .hdr .info_type ) {
@@ -139,6 +142,12 @@ const Os = switch (builtin.os.tag) {
139142 if (reaction_set .getPtr (file_name )) | step_set |
140143 any_dirty = markStepSetDirty (gpa , step_set , any_dirty );
141144 }
145+
146+ if (FileHandle .Adapter .eql (undefined , lfh , w .os .build_file_handle .? , undefined ) and
147+ std .mem .eql (u8 , file_name , w .build_file .sub_path ))
148+ {
149+ return .build_file_changed ;
150+ }
142151 },
143152 else = > | t | std .log .warn ("unexpected fanotify event '{s}'" , .{@tagName (t )}),
144153 }
@@ -152,6 +161,20 @@ const Os = switch (builtin.os.tag) {
152161
153162 fn update (w : * Watch , gpa : Allocator , steps : []const * Step ) ! void {
154163 const fan_fd = w .os .getFanFd ();
164+
165+ if (w .os .build_file_handle == null ) {
166+ w .os .build_file_handle = try Os .getDirHandle (gpa , .{
167+ .root_dir = w .build_file .root_dir ,
168+ .sub_path = "" ,
169+ });
170+ posix .fanotify_mark (fan_fd , .{
171+ .ADD = true ,
172+ .ONLYDIR = true ,
173+ }, fan_mask , w .build_file .root_dir .handle .fd , "." ) catch | err | {
174+ fatal ("unable to watch build file at {}: {s}" , .{ w .build_file , @errorName (err ) });
175+ };
176+ }
177+
155178 // Add missing marks and note persisted ones.
156179 for (steps ) | step | {
157180 for (step .inputs .table .keys (), step .inputs .table .values ()) | path , * files | {
@@ -240,7 +263,7 @@ const Os = switch (builtin.os.tag) {
240263 else = > void ,
241264};
242265
243- pub fn init () ! Watch {
266+ pub fn init (build_file : Cache.Path ) ! Watch {
244267 switch (builtin .os .tag ) {
245268 .linux = > {
246269 const fan_fd = try std .posix .fanotify_init (.{
@@ -254,6 +277,7 @@ pub fn init() !Watch {
254277 }, 0 );
255278 return .{
256279 .dir_table = .{},
280+ .build_file = build_file ,
257281 .os = switch (builtin .os .tag ) {
258282 .linux = > .{
259283 .handle_table = .{},
@@ -342,6 +366,7 @@ pub const WaitResult = enum {
342366 /// File system watching triggered on files that were marked as inputs to at least one Step.
343367 /// Relevant steps have been marked dirty.
344368 dirty ,
369+ build_file_changed ,
345370 /// File system watching triggered but none of the events were relevant to
346371 /// what we are listening to. There is nothing to do.
347372 clean ,
@@ -353,10 +378,8 @@ pub fn wait(w: *Watch, gpa: Allocator, timeout: Timeout) !WaitResult {
353378 const events_len = try std .posix .poll (& w .os .poll_fds , timeout .to_i32_ms ());
354379 return if (events_len == 0 )
355380 .timeout
356- else if (try Os .markDirtySteps (w , gpa ))
357- .dirty
358381 else
359- .clean ;
382+ try Os . markDirtySteps ( w , gpa ) ;
360383 },
361384 else = > @compileError ("unimplemented" ),
362385 }
0 commit comments