Skip to content

Commit

Permalink
add a fallback mode for build on save when using a pre 6.5 linux kernel
Browse files Browse the repository at this point in the history
  • Loading branch information
Techatrix committed Jan 26, 2025
1 parent 15aa6dc commit 6a16b27
Show file tree
Hide file tree
Showing 4 changed files with 110 additions and 23 deletions.
41 changes: 34 additions & 7 deletions src/Server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,7 @@ fn handleConfiguration(server: *Server, json: std.json.Value) error{OutOfMemory}
const Workspace = struct {
uri: types.URI,
build_on_save: if (BuildOnSaveSupport.isSupportedComptime()) ?BuildOnSave else void,
build_on_save_mode: if (BuildOnSaveSupport.isSupportedComptime()) ?enum { watch, manual } else void,

fn init(server: *Server, uri: types.URI) error{OutOfMemory}!Workspace {
const duped_uri = try server.allocator.dupe(u8, uri);
Expand All @@ -746,6 +747,7 @@ const Workspace = struct {
return .{
.uri = duped_uri,
.build_on_save = if (BuildOnSaveSupport.isSupportedComptime()) null else {},
.build_on_save_mode = if (BuildOnSaveSupport.isSupportedComptime()) null else {},
};
}

Expand All @@ -756,6 +758,16 @@ const Workspace = struct {
allocator.free(workspace.uri);
}

fn sendManualWatchUpdate(workspace: *Workspace) void {
comptime std.debug.assert(BuildOnSaveSupport.isSupportedComptime());

const build_on_save = if (workspace.build_on_save) |*build_on_save| build_on_save else return;
const mode = workspace.build_on_save_mode orelse return;
if (mode != .manual) return;

build_on_save.sendManualWatchUpdate();
}

fn refreshBuildOnSave(workspace: *Workspace, args: struct {
server: *Server,
/// If null, build on save will be disabled
Expand All @@ -765,7 +777,17 @@ const Workspace = struct {
}) error{OutOfMemory}!void {
comptime std.debug.assert(BuildOnSaveSupport.isSupportedComptime());

const build_on_save_supported = if (args.runtime_zig_version) |version| BuildOnSaveSupport.isSupportedRuntime(version) == .supported else false;
if (args.runtime_zig_version) |runtime_zig_version| {
workspace.build_on_save_mode = switch (BuildOnSaveSupport.isSupportedRuntime(runtime_zig_version)) {
.supported => .watch,
// If if build on save has been explicitly enabled, fallback to the implementation with manual updates
else => if (args.server.config.enable_build_on_save orelse false) .manual else null,
};
} else {
workspace.build_on_save_mode = null;
}

const build_on_save_supported = workspace.build_on_save_mode != null;
const build_on_save_wanted = args.server.config.enable_build_on_save orelse true;
const enable = build_on_save_supported and build_on_save_wanted;

Expand Down Expand Up @@ -1066,9 +1088,7 @@ pub fn updateConfiguration(
}

if (server.config.enable_build_on_save orelse false) {
if (!BuildOnSaveSupport.isSupportedComptime() and @TypeOf(BuildOnSaveSupport.minimum_zig_version) == void) {
log.info("'enable_build_on_save' is ignored because it is not supported on {s}", .{@tagName(zig_builtin.os.tag)});
} else if (!BuildOnSaveSupport.isSupportedComptime()) {
if (!BuildOnSaveSupport.isSupportedComptime()) {
// This message is not very helpful but it relatively uncommon to happen anyway.
log.info("'enable_build_on_save' is ignored because build on save is not supported by this ZLS build", .{});
} else if (server.status == .initialized and (server.config.zig_exe_path == null or server.config.zig_lib_path == null)) {
Expand All @@ -1080,9 +1100,10 @@ pub fn updateConfiguration(
} else if (server.status == .initialized and options.resolve and resolve_result.zig_runtime_version != null) {
switch (BuildOnSaveSupport.isSupportedRuntime(resolve_result.zig_runtime_version.?)) {
.supported => {},
.invalid_linux_kernel_version => |*utsname_release| log.warn("'enable_build_on_save' is ignored because it because the Linux version '{s}' could not be parsed", .{std.mem.sliceTo(utsname_release, 0)}),
.unsupported_linux_kernel_version => |kernel_version| log.warn("'enable_build_on_save' is ignored because it is not supported by Linux '{}' (requires at least {})", .{ kernel_version, BuildOnSaveSupport.minimum_linux_version }),
.unsupported_zig_version => log.warn("'enable_build_on_save' is ignored because it is not supported on {s} by Zig {} (requires at least {})", .{ @tagName(zig_builtin.os.tag), resolve_result.zig_runtime_version.?, BuildOnSaveSupport.minimum_zig_version }),
.invalid_linux_kernel_version => |*utsname_release| log.warn("Build-On-Save cannot run in watch mode because it because the Linux version '{s}' could not be parsed", .{std.mem.sliceTo(utsname_release, 0)}),
.unsupported_linux_kernel_version => |kernel_version| log.warn("Build-On-Save cannot run in watch mode because it is not supported by Linux '{}' (requires at least {})", .{ kernel_version, BuildOnSaveSupport.minimum_linux_version }),
.unsupported_zig_version => log.warn("Build-On-Save cannot run in watch mode because it is not supported on {s} by Zig {} (requires at least {})", .{ @tagName(zig_builtin.os.tag), resolve_result.zig_runtime_version.?, BuildOnSaveSupport.minimum_zig_version }),
.unsupported_os => log.warn("Build-On-Save cannot run in watch mode because it is not supported on {s}", .{@tagName(zig_builtin.os.tag)}),
}
}
}
Expand Down Expand Up @@ -1458,6 +1479,12 @@ fn saveDocumentHandler(server: *Server, arena: std.mem.Allocator, notification:
);
server.allocator.free(json_message);
}

if (BuildOnSaveSupport.isSupportedComptime()) {
for (server.workspaces.items) |*workspace| {
workspace.sendManualWatchUpdate();
}
}
}

fn closeDocumentHandler(server: *Server, _: std.mem.Allocator, notification: types.DidCloseTextDocumentParams) error{}!void {
Expand Down
80 changes: 66 additions & 14 deletions src/build_runner/master.zig
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ const mem = std.mem;
const process = std.process;
const ArrayList = std.ArrayList;
const Step = std.Build.Step;
const Watch = std.Build.Watch;
const Allocator = std.mem.Allocator;

pub const dependencies = @import("@dependencies");
Expand Down Expand Up @@ -376,20 +375,22 @@ pub fn main() !void {
return;
}

if (!std.Build.Watch.have_impl) {
std.log.warn("Build-On-Save is not supported on {} by Zig {}", .{ builtin.target.os.tag, builtin.zig_version });
process.exit(1);
}
var w = try Watch.init();

const suicide_thread = try std.Thread.spawn(.{}, struct {
fn do() void {
_ = std.io.getStdIn().reader().readByte() catch process.exit(1);
process.exit(0);
const message_thread = try std.Thread.spawn(.{}, struct {
fn do(ww: *Watch) void {
while (std.io.getStdIn().reader().readByte()) |tag| {
switch (tag) {
'\x00' => ww.trigger(),
else => process.exit(1),
}
} else |err| switch (err) {
error.EndOfStream => process.exit(0),
else => process.exit(1),
}
}
}.do, .{});
suicide_thread.detach();

var w = try Watch.init();
}.do, .{&w});
message_thread.detach();

const gpa = arena;
var transport = Transport.init(.{
Expand Down Expand Up @@ -430,7 +431,7 @@ pub fn main() !void {
// if any more events come in. After the debounce interval has passed,
// trigger a rebuild on all steps with modified inputs, as well as their
// recursive dependants.
var debounce_timeout: Watch.Timeout = .none;
var debounce_timeout: std.Build.Watch.Timeout = .none;
while (true) switch (try w.wait(gpa, debounce_timeout)) {
.timeout => {
markFailedStepsDirty(gpa, step_stack.keys());
Expand All @@ -457,6 +458,57 @@ fn markFailedStepsDirty(gpa: Allocator, all_steps: []const *Step) void {
};
}

/// A wrapper around `std.Build.Watch` that supports manually triggering recompilations.
const Watch = struct {
fs_watch: std.Build.Watch,
supports_fs_watch: bool,
manual_event: std.Thread.ResetEvent,
steps: []const *Step,

fn init() !Watch {
return .{
.fs_watch = if (@TypeOf(std.Build.Watch) != void) try std.Build.Watch.init() else {},
.supports_fs_watch = @TypeOf(std.Build.Watch) != void and shared.BuildOnSaveSupport.isSupportedRuntime(builtin.zig_version) == .supported,
.manual_event = .{},
.steps = &.{},
};
}

fn update(w: *Watch, gpa: Allocator, steps: []const *Step) !void {
if (@TypeOf(std.Build.Watch) != void and w.supports_fs_watch) {
return try w.fs_watch.update(gpa, steps);
}
w.steps = steps;
}

fn trigger(w: *Watch) void {
if (w.supports_fs_watch) {
@panic("received manualy filesystem event even though std.Build.Watch is supported");
}
w.manual_event.set();
}

fn wait(w: *Watch, gpa: Allocator, timeout: std.Build.Watch.Timeout) !std.Build.Watch.WaitResult {
if (@TypeOf(std.Build.Watch) != void and w.supports_fs_watch) {
return try w.fs_watch.wait(gpa, timeout);
}
switch (timeout) {
.none => w.manual_event.wait(),
.ms => |ms| w.manual_event.timedWait(@as(u64, ms) * std.time.ns_per_ms) catch return .timeout,
}
w.manual_event.reset();
markStepsDirty(gpa, w.steps);
return .dirty;
}

fn markStepsDirty(gpa: Allocator, all_steps: []const *Step) void {
for (all_steps) |step| switch (step.state) {
.precheck_done => continue,
else => step.recursiveReset(gpa),
};
}
};

const Run = struct {
max_rss: u64,
max_rss_is_default: bool,
Expand Down
8 changes: 6 additions & 2 deletions src/build_runner/shared.zig
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,8 @@ pub const BuildOnSaveSupport = union(enum) {
supported,
invalid_linux_kernel_version: if (builtin.os.tag == .linux) std.meta.FieldType(std.posix.utsname, .release) else noreturn,
unsupported_linux_kernel_version: if (builtin.os.tag == .linux) std.SemanticVersion else noreturn,
unsupported_zig_version: void,
unsupported_zig_version: if (@TypeOf(minimum_zig_version) != void) void else noreturn,
unsupported_os: if (@TypeOf(minimum_zig_version) == void) void else noreturn,

const linux_support_version = std.SemanticVersion.parse("0.14.0-dev.283+1d20ff11d") catch unreachable;
const windows_support_version = std.SemanticVersion.parse("0.14.0-dev.625+2de0e2eca") catch unreachable;
Expand Down Expand Up @@ -184,7 +185,6 @@ pub const BuildOnSaveSupport = union(enum) {
pub inline fn isSupportedComptime() bool {
if (!std.process.can_spawn) return false;
if (builtin.single_threaded) return false;
if (@TypeOf(minimum_zig_version) == void) return false;
return true;
}

Expand All @@ -204,6 +204,10 @@ pub const BuildOnSaveSupport = union(enum) {
};
}

if (@TypeOf(minimum_zig_version) == void) {
return .unsupported_os;
}

if (runtime_zig_version.order(minimum_zig_version) == .lt) {
return .unsupported_zig_version;
}
Expand Down
4 changes: 4 additions & 0 deletions src/features/diagnostics.zig
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,10 @@ pub const BuildOnSave = struct {
self.thread.join();
}

pub fn sendManualWatchUpdate(self: *BuildOnSave) void {
self.child_process.stdin.?.writeAll("\x00") catch {};
}

fn loop(
allocator: std.mem.Allocator,
child_process: *std.process.Child,
Expand Down

0 comments on commit 6a16b27

Please sign in to comment.