Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

build runner fixes #2148

Merged
merged 7 commits into from
Jan 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,14 @@ const zls_version: std.SemanticVersion = .{ .major = 0, .minor = 14, .patch = 0,
const minimum_build_zig_version = "0.14.0-dev.2633+bc846c379";

/// Specify the minimum Zig version that is required to run ZLS:
/// make zig compiler processes live across rebuilds
/// std.Build: add new functions to create artifacts/Step.Compile from existing module
///
/// Examples of reasons that would cause the minimum runtime version to be bumped are:
/// - breaking change to the Zig Syntax
/// - breaking change to AstGen (i.e `zig ast-check`)
///
/// A breaking change to the Zig Build System should be handled by updating ZLS's build runner (see src\build_runner)
const minimum_runtime_zig_version = "0.14.0-dev.310+9d38e82b5";
const minimum_runtime_zig_version = "0.14.0-dev.2534+12d64c456";

const release_targets = [_]std.Target.Query{
.{ .cpu_arch = .x86_64, .os_tag = .windows },
Expand Down
44 changes: 43 additions & 1 deletion src/DocumentStore.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1489,6 +1489,34 @@ pub fn collectIncludeDirs(
return collected_all;
}

/// returns `true` if all c macro definitions could be collected
/// may return `false` because macros from a build.zig may not have been resolved already
/// **Thread safe** takes a shared lock
pub fn collectCMacros(
store: *DocumentStore,
allocator: std.mem.Allocator,
handle: *Handle,
c_macros: *std.ArrayListUnmanaged([]const u8),
) !bool {
const collected_all = switch (try handle.getAssociatedBuildFileUri2(store)) {
.none => true,
.unresolved => false,
.resolved => |build_file_uri| blk: {
const build_file = store.getBuildFile(build_file_uri).?;
const build_config = build_file.tryLockConfig() orelse break :blk false;
defer build_file.unlockConfig();

try c_macros.ensureUnusedCapacity(allocator, build_config.c_macros.len);
for (build_config.c_macros) |c_macro| {
c_macros.appendAssumeCapacity(try allocator.dupe(u8, c_macro));
}
break :blk true;
},
};

return collected_all;
}

/// returns the document behind `@cImport()` where `node` is the `cImport` node
/// if a cImport can't be translated e.g. requires computing a
/// comptime value `resolveCImport` will return null
Expand Down Expand Up @@ -1533,10 +1561,24 @@ pub fn resolveCImport(self: *DocumentStore, handle: *Handle, node: Ast.Node.Inde
return null;
};

var c_macros: std.ArrayListUnmanaged([]const u8) = .empty;
defer {
for (c_macros.items) |c_macro| {
self.allocator.free(c_macro);
}
c_macros.deinit(self.allocator);
}

const collected_all_c_macros = self.collectCMacros(self.allocator, handle, &c_macros) catch |err| {
log.err("failed to resolve include paths: {}", .{err});
return null;
};

const maybe_result = translate_c.translate(
self.allocator,
self.config,
include_dirs.items,
c_macros.items,
source,
) catch |err| switch (err) {
error.OutOfMemory => |e| return e,
Expand All @@ -1547,7 +1589,7 @@ pub fn resolveCImport(self: *DocumentStore, handle: *Handle, node: Ast.Node.Inde
};
var result = maybe_result orelse return null;

if (result == .failure and !collected_all_include_dirs) {
if (result == .failure and (!collected_all_include_dirs or !collected_all_c_macros)) {
result.deinit(self.allocator);
return null;
}
Expand Down
62 changes: 48 additions & 14 deletions src/Server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ const hover_handler = @import("features/hover.zig");
const selection_range = @import("features/selection_range.zig");
const diagnostics_gen = @import("features/diagnostics.zig");

const BuildOnSave = diagnostics_gen.BuildOnSave;
const BuildOnSaveSupport = build_runner_shared.BuildOnSaveSupport;

const log = std.log.scoped(.server);

// public fields
Expand Down Expand Up @@ -733,36 +736,57 @@ fn handleConfiguration(server: *Server, json: std.json.Value) error{OutOfMemory}

const Workspace = struct {
uri: types.URI,
build_on_save: if (build_runner_shared.isBuildOnSaveSupportedComptime()) ?diagnostics_gen.BuildOnSave else void,
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);
errdefer server.allocator.free(duped_uri);

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

fn deinit(workspace: *Workspace, allocator: std.mem.Allocator) void {
if (build_runner_shared.isBuildOnSaveSupportedComptime()) {
if (BuildOnSaveSupport.isSupportedComptime()) {
if (workspace.build_on_save) |*build_on_save| build_on_save.deinit();
}
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
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_runner_shared.isBuildOnSaveSupportedComptime());
comptime std.debug.assert(build_options.version.order(.{ .major = 0, .minor = 14, .patch = 0 }) == .lt); // Update `isBuildOnSaveSupportedRuntime` and build runner
comptime std.debug.assert(BuildOnSaveSupport.isSupportedComptime());

const build_on_save_supported = if (args.runtime_zig_version) |version| build_runner_shared.isBuildOnSaveSupportedRuntime(version) 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 All @@ -785,8 +809,8 @@ const Workspace = struct {
};
defer args.server.allocator.free(workspace_path);

workspace.build_on_save = @as(diagnostics_gen.BuildOnSave, undefined);
workspace.build_on_save.?.init(.{
std.debug.assert(workspace.build_on_save == null);
workspace.build_on_save = BuildOnSave.init(.{
.allocator = args.server.allocator,
.workspace_path = workspace_path,
.build_on_save_args = args.server.config.build_on_save_args,
Expand All @@ -796,7 +820,6 @@ const Workspace = struct {
.build_runner_path = build_runner_path,
.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 });
return;
};
Expand Down Expand Up @@ -971,7 +994,7 @@ pub fn updateConfiguration(
}
}

if (build_runner_shared.isBuildOnSaveSupportedComptime() and
if (BuildOnSaveSupport.isSupportedComptime() and
options.resolve and
// If the client supports the `workspace/configuration` request, defer
// build on save initialization until after we have received workspace
Expand Down Expand Up @@ -1070,7 +1093,7 @@ pub fn updateConfiguration(
}

if (server.config.enable_build_on_save orelse false) {
if (!build_runner_shared.isBuildOnSaveSupportedComptime()) {
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 @@ -1079,9 +1102,14 @@ pub fn updateConfiguration(
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 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.?});
} 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("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 @@ -1456,6 +1484,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
Loading