Skip to content

Commit

Permalink
implement runtime zig version detection for build on save
Browse files Browse the repository at this point in the history
The main motivation for this is to keep support for the latest mach nominated zig version which is `0.14.0-dev.1911+3bf89f55c`.
  • Loading branch information
Techatrix committed Dec 9, 2024
1 parent 29a7350 commit 2f2e93f
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 37 deletions.
66 changes: 38 additions & 28 deletions src/Server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const diff = @import("diff.zig");
const InternPool = @import("analyser/analyser.zig").InternPool;
const DiagnosticsCollection = @import("DiagnosticsCollection.zig");
const known_folders = @import("known-folders");
const build_runner_shared = @import("build_runner/shared.zig");
const BuildRunnerVersion = @import("build_runner/BuildRunnerVersion.zig").BuildRunnerVersion;

const signature_help = @import("features/signature_help.zig");
Expand Down Expand Up @@ -770,66 +771,67 @@ fn handleConfiguration(server: *Server, json: std.json.Value) error{OutOfMemory}

const Workspace = struct {
uri: types.URI,
build_on_save: if (build_on_save_supported) ?diagnostics_gen.BuildOnSave else void,

pub const build_on_save_supported =
std.process.can_spawn and
!zig_builtin.single_threaded and
std.Build.Watch.have_impl;
build_on_save: if (build_runner_shared.isBuildOnSaveSupportedComptime()) ?diagnostics_gen.BuildOnSave else void,

fn init(server: *Server, uri: types.URI) error{OutOfMemory}!Workspace {
const duped_uri = try server.allocator.dupe(u8, uri);
errdefer server.allocator.free(duped_uri);

return .{
.uri = duped_uri,
.build_on_save = if (build_on_save_supported) null else {},
.build_on_save = if (build_runner_shared.isBuildOnSaveSupportedComptime()) null else {},
};
}

fn deinit(workspace: *Workspace, allocator: std.mem.Allocator) void {
if (build_on_save_supported) {
if (build_runner_shared.isBuildOnSaveSupportedComptime()) {
if (workspace.build_on_save) |*build_on_save| build_on_save.deinit();
}
allocator.free(workspace.uri);
}

fn refreshBuildOnSave(workspace: *Workspace, server: *Server, options: struct {
/// Whether the build on save process should be restated if it is already running.
fn refreshBuildOnSave(workspace: *Workspace, args: struct {
server: *Server,
/// If null, build on save will be disabled
runtime_zig_version: ?std.SemanticVersion,
/// Whether the build on save process should be restarted if it is already running.
restart: bool,
}) error{OutOfMemory}!void {
comptime std.debug.assert(build_on_save_supported);
comptime std.debug.assert(build_runner_shared.isBuildOnSaveSupportedComptime());
comptime std.debug.assert(build_options.version.order(.{ .major = 0, .minor = 14, .patch = 0 }) == .lt); // Update `isBuildOnSaveSupportedRuntime` and build runner

const enable_build_on_save = server.config.enable_build_on_save orelse true;
const build_on_save_supported = if (args.runtime_zig_version) |version| build_runner_shared.isBuildOnSaveSupportedRuntime(version) else false;
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;

if (workspace.build_on_save) |*build_on_save| {
if (enable_build_on_save and !options.restart) return;
if (enable and !args.restart) return;
build_on_save.deinit();
workspace.build_on_save = null;
}

if (!enable_build_on_save) return;
if (!enable) return;

const zig_exe_path = server.config.zig_exe_path orelse return;
const zig_lib_path = server.config.zig_lib_path orelse return;
const build_runner_path = server.config.build_runner_path orelse return;
const zig_exe_path = args.server.config.zig_exe_path orelse return;
const zig_lib_path = args.server.config.zig_lib_path orelse return;
const build_runner_path = args.server.config.build_runner_path orelse return;

const workspace_path = @import("uri.zig").parse(server.allocator, workspace.uri) catch |err| {
const workspace_path = @import("uri.zig").parse(args.server.allocator, workspace.uri) catch |err| {
log.err("failed to parse URI '{s}': {}", .{ workspace.uri, err });
return;
};
defer server.allocator.free(workspace_path);
defer args.server.allocator.free(workspace_path);

workspace.build_on_save = @as(diagnostics_gen.BuildOnSave, undefined);
workspace.build_on_save.?.init(.{
.allocator = server.allocator,
.allocator = args.server.allocator,
.workspace_path = workspace_path,
.build_on_save_args = server.config.build_on_save_args,
.check_step_only = server.config.enable_build_on_save == null,
.build_on_save_args = args.server.config.build_on_save_args,
.check_step_only = args.server.config.enable_build_on_save == null,
.zig_exe_path = zig_exe_path,
.zig_lib_path = zig_lib_path,
.build_runner_path = build_runner_path,
.collection = &server.diagnostics_collection,
.collection = &args.server.diagnostics_collection,
}) catch |err| {
workspace.build_on_save = null;
log.err("failed to initilize Build-On-Save for '{s}': {}", .{ workspace.uri, err });
Expand Down Expand Up @@ -1006,7 +1008,8 @@ pub fn updateConfiguration(
}
}

if (Workspace.build_on_save_supported and
if (build_runner_shared.isBuildOnSaveSupportedComptime() and
options.resolve and
// If the client supports the `workspace/configuration` request, defer
// build on save initialization until after we have received workspace
// configuration from the server
Expand All @@ -1020,7 +1023,9 @@ pub fn updateConfiguration(
new_build_on_save_args;

for (server.workspaces.items) |*workspace| {
try workspace.refreshBuildOnSave(server, .{
try workspace.refreshBuildOnSave(.{
.server = server,
.runtime_zig_version = resolve_result.zig_runtime_version,
.restart = should_restart,
});
}
Expand Down Expand Up @@ -1102,15 +1107,18 @@ pub fn updateConfiguration(
}

if (server.config.enable_build_on_save orelse false) {
if (!Workspace.build_on_save_supported) {
if (!build_runner_shared.isBuildOnSaveSupportedComptime()) {
// 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)) {
log.warn("'enable_build_on_save' is ignored because Zig could not be found", .{});
} else if (!server.client_capabilities.supports_publish_diagnostics) {
log.warn("'enable_build_on_save' is ignored because it is not supported by {s}", .{server.client_capabilities.client_name orelse "your editor"});
} else if (server.status == .initialized and server.config.build_runner_path == null and options.resolve and resolve_result.build_runner_version != .resolved) {
} else if (server.status == .initialized and options.resolve and resolve_result.build_runner_version == .unresolved and server.config.build_runner_path == null) {
log.warn("'enable_build_on_save' is ignored because no build runner is available", .{});
} else if (server.status == .initialized and options.resolve and resolve_result.zig_runtime_version != null and !build_runner_shared.isBuildOnSaveSupportedRuntime(resolve_result.zig_runtime_version.?)) {
// There is one edge-case where build on save is not supported because of Linux pre 5.17
log.warn("'enable_build_on_save' is not supported by Zig {}", .{resolve_result.zig_runtime_version.?});
}
}

Expand Down Expand Up @@ -1239,8 +1247,10 @@ const ResolveConfigurationResult = struct {
zig_env: ?std.json.Parsed(configuration.Env),
zig_runtime_version: ?std.SemanticVersion,
build_runner_version: union(enum) {
/// no suitable build runner could be resolved based on the `zig_runtime_version`
/// If returned, guarantees `zig_runtime_version != null`.
resolved: BuildRunnerVersion,
/// no suitable build runner could be resolved based on the `zig_runtime_version`
/// If returned, guarantees `zig_runtime_version != null`.
unresolved,
unresolved_dont_error,
},
Expand Down
11 changes: 2 additions & 9 deletions src/build_runner/master.zig
Original file line number Diff line number Diff line change
Expand Up @@ -381,15 +381,8 @@ pub fn main() !void {
}.do, .{});
suicide_thread.detach();

if (!Watch.have_impl) return;

if (builtin.os.tag == .linux) blk: {
// std.build.Watch requires `FAN_REPORT_TARGET_FID` which is Linux 5.17+
const utsname = std.posix.uname();
const version = std.SemanticVersion.parse(&utsname.release) catch break :blk;
if (version.order(.{ .major = 5, .minor = 17, .patch = 0 }) != .lt) break :blk;
return;
}
if (!shared.isBuildOnSaveSupportedComptime()) return;
if (!shared.isBuildOnSaveSupportedRuntime(builtin.zig_version)) return;

var w = try Watch.init();

Expand Down
43 changes: 43 additions & 0 deletions src/build_runner/shared.zig
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,46 @@ pub const ServerToClient = struct {
string_bytes_len: u32,
};
};

const windows_support_version = std.SemanticVersion.parse("0.14.0-dev.625+2de0e2eca") catch unreachable;
const kqueue_support_version = std.SemanticVersion.parse("0.14.0-dev.2046+b8795b4d0") catch unreachable;
/// The Zig version which added `std.Build.Watch.have_impl`
const have_impl_flag_version = kqueue_support_version;

/// Returns true if is comptime known that build on save is supported.
pub inline fn isBuildOnSaveSupportedComptime() bool {
if (!std.process.can_spawn) return false;
if (builtin.single_threaded) return false;
return true;
}

pub fn isBuildOnSaveSupportedRuntime(runtime_zig_version: std.SemanticVersion) bool {
if (!isBuildOnSaveSupportedComptime()) return false;

if (builtin.os.tag == .linux) blk: {
// std.build.Watch requires `FAN_REPORT_TARGET_FID` which is Linux 5.17+
const utsname = std.posix.uname();
const version = std.SemanticVersion.parse(&utsname.release) catch break :blk;
if (version.order(.{ .major = 5, .minor = 17, .patch = 0 }) != .lt) break :blk;
return false;
}

// This code path is present to support runtime Zig version before `0.14.0-dev.2046+b8795b4d0`.
// The main motivation is to keep support for the latest mach nominated zig version which is `0.14.0-dev.1911+3bf89f55c`.
return switch (builtin.os.tag) {
.linux => true,
.windows => runtime_zig_version.order(windows_support_version) != .lt,
.dragonfly,
.freebsd,
.netbsd,
.openbsd,
.ios,
.macos,
.tvos,
.visionos,
.watchos,
.haiku,
=> runtime_zig_version.order(kqueue_support_version) != .lt,
else => false,
};
}

0 comments on commit 2f2e93f

Please sign in to comment.