文件监听依赖具体平台。下面的示例等待真实的文件系统事件,而不是轮询文件修改时间。

macOS 和 FreeBSD:kqueue

这个示例使用 kqueue 和 EVFILT_VNODE 监听 build.zig。请在 macOS 或 FreeBSD 系统上运行, 然后在另一个终端修改或重命名该文件。

const builtin = @import("builtin");
const std = @import("std");

pub fn main(init: std.process.Init) !void {
    if (comptime builtin.os.tag == .macos or builtin.os.tag == .freebsd) {
        try watchWithKqueue(init);
    } else {
        std.debug.print("This example requires macOS or a BSD system.\n", .{});
    }
}

fn watchWithKqueue(init: std.process.Init) !void {
    const io = init.io;
    const file = try std.Io.Dir.cwd().openFile(io, "build.zig", .{});
    defer file.close(io);

    const queue = std.c.kqueue();
    if (queue < 0) return error.KqueueUnavailable;
    defer std.Io.Threaded.closeFd(queue);

    const change = std.c.Kevent{
        .ident = @intCast(file.handle),
        .filter = std.c.EVFILT.VNODE,
        .flags = std.c.EV.ADD | std.c.EV.ENABLE | std.c.EV.CLEAR,
        .fflags = std.c.NOTE.WRITE | std.c.NOTE.DELETE | std.c.NOTE.RENAME,
        .data = 0,
        .udata = 0,
    };
    var changes = [_]std.c.Kevent{change};
    var events: [1]std.c.Kevent = undefined;
    std.debug.print("Waiting for a change to build.zig...\n", .{});

    const count = std.c.kevent(queue, &changes, 1, &events, 1, null);
    if (count < 0) return error.KeventFailed;
    if (count == 0) return error.NoEvent;

    std.debug.print("build.zig changed (flags: 0x{x})\n", .{events[0].fflags});
}

Linux:inotify

这个示例使用 inotify 监听当前目录。请在 Linux 上运行,然后在另一个终端创建、修改、移动 或删除文件。

const builtin = @import("builtin");
const std = @import("std");

pub fn main(init: std.process.Init) !void {
    _ = init;
    if (comptime builtin.os.tag == .linux) {
        try watchWithInotify();
    } else {
        std.debug.print("This example requires Linux.\n", .{});
    }
}

fn watchWithInotify() !void {
    const linux = std.os.linux;
    const descriptor_result = linux.inotify_init1(linux.IN.CLOEXEC);
    if (linux.errno(descriptor_result) != .SUCCESS) {
        return std.posix.unexpectedErrno(linux.errno(descriptor_result));
    }
    const descriptor: std.posix.fd_t = @intCast(descriptor_result);
    defer std.Io.Threaded.closeFd(descriptor);

    const mask = linux.IN.MODIFY | linux.IN.CREATE | linux.IN.DELETE | linux.IN.MOVE;
    const watch_result = linux.inotify_add_watch(descriptor, ".", mask);
    if (linux.errno(watch_result) != .SUCCESS) {
        return std.posix.unexpectedErrno(linux.errno(watch_result));
    }
    const watch: i32 = @intCast(watch_result);

    var buffer: [4096]u8 align(@alignOf(linux.inotify_event)) = undefined;
    std.debug.print("Waiting for a change in the current directory...\n", .{});
    const bytes_read = try std.posix.read(descriptor, &buffer);
    if (bytes_read < @sizeOf(linux.inotify_event)) return error.InvalidEvent;

    const event: *const linux.inotify_event = @ptrCast(&buffer);
    std.debug.print("directory event (mask: 0x{x})", .{event.mask});
    if (event.getName()) |name| {
        std.debug.print(": {s}", .{name});
    }
    std.debug.print("\n", .{});

    const remove_result = linux.inotify_rm_watch(descriptor, watch);
    if (linux.errno(remove_result) != .SUCCESS) {
        return std.posix.unexpectedErrno(linux.errno(remove_result));
    }
}