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

Fix build runner #1029

Merged
merged 12 commits into from
Mar 5, 2023
17 changes: 4 additions & 13 deletions .github/workflows/build_runner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
check_build_runner:
strategy:
matrix:
zig_version: [0.9.1, 0.10.1, master]
zig_version: [master]

runs-on: ubuntu-latest

Expand All @@ -33,16 +33,7 @@ jobs:
with:
version: ${{ matrix.zig_version }}

- name: Create temp zig project
run: |
mkdir $RUNNER_TEMP/TEMP_ZIG_PROJECT
cd $RUNNER_TEMP/TEMP_ZIG_PROJECT
zig init-exe

- name: Check build_runner builds on master
if: ${{ matrix.zig_version == 'master' }}
run: zig build-exe $GITHUB_WORKSPACE/src/special/build_runner.zig --mod @build@::$RUNNER_TEMP/TEMP_ZIG_PROJECT/build.zig --deps @build@

- name: Check build_runner builds on older tagged releases
if: ${{ matrix.zig_version != 'master' }}
run: zig build-exe $GITHUB_WORKSPACE/src/special/build_runner.zig --pkg-begin @build@ $RUNNER_TEMP/TEMP_ZIG_PROJECT/build.zig --pkg-end
run: |
pwd
zig build --build-runner src/special/build_runner.zig
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ The Zig Language Server (zls) is a tool that implements Microsoft's Language Ser
- [Per-build Configuration Options](#per-build-configuration-options)
- [`BuildOption`](#buildoption)
- [Features](#features)
- [Using as a library](#using-as-a-library)
- [Related Projects](#related-projects)
- [Quick Thanks :)](#quick-thanks-)
- [License](#license)
Expand Down Expand Up @@ -129,6 +130,10 @@ The following LSP features are supported:
- Selection ranges
- Folding regions

## Using as a library

You can use zls as a library! [Check out this demo repo](https://github.com/zigtools/zls-as-lib-demo) for a good reference.

## Related Projects

- [`sublime-zig-language` by @prime31](https://github.com/prime31/sublime-zig-language)
Expand All @@ -139,7 +144,7 @@ The following LSP features are supported:
- [`known-folders` by @ziglibs](https://github.com/ziglibs/known-folders)
- Provides API to access known folders on Linux, Windows and Mac OS
- [`zls` by @zigtools](https://github.com/zigtools/zls)
- Used by many zls developers to more efficently work on zls
- Used by many zls developers to more efficiently work on zls

## Quick Thanks :)

Expand Down
4 changes: 2 additions & 2 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ const zls_version = std.builtin.Version{ .major = 0, .minor = 11, .patch = 0 };
pub fn build(b: *std.build.Builder) !void {
comptime {
const current_zig = builtin.zig_version;
const min_zig = std.SemanticVersion.parse("0.11.0-dev.1817+f6c934677") catch return; // package manager hashes made consistent on windows
const min_zig = std.SemanticVersion.parse("0.11.0-dev.1836+28364166e") catch return; // package manager stuff + --build-runner + zls as a library
if (current_zig.order(min_zig) == .lt) {
@compileError(std.fmt.comptimePrint("Your Zig version v{} does not meet the minimum build requirement of v{}", .{ current_zig, min_zig }));
}
Expand Down Expand Up @@ -160,7 +160,7 @@ pub fn build(b: *std.build.Builder) !void {

const build_options_module = exe_options.createModule();

const zls_module = b.createModule(.{
const zls_module = b.addModule("zls", .{
.source_file = .{ .path = "src/zls.zig" },
.dependencies = &.{
.{ .name = "known-folders", .module = known_folders_module },
Expand Down
12 changes: 6 additions & 6 deletions build.zig.zon
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@

.dependencies = .{
.known_folders = .{
.url = "https://github.com/ziglibs/known-folders/archive/53fe3b676f32e59d46f4fd201d7ab200e5f6cb98.tar.gz",
.hash = "12203e18503cd0fa097a4404b0c4f8535a68536886b536ae51c786455238ba5f183b",
.url = "https://github.com/ziglibs/known-folders/archive/d13ba6137084e55f873f6afb67447fe8906cc951.tar.gz",
.hash = "122028c00915d9b37296059be8a3883c718dbb5bd174350caedf152fed1f46f99607",
},
.tres = .{
.url = "https://github.com/ziglibs/tres/archive/d8b0c24a945da02fffdae731edd1903c6889e73c.tar.gz",
.hash = "12209914477ef8c4ef99accb293c4a7ec90acdd9e77d3f60f5e056449cbfad3a7fd8",
.url = "https://github.com/ziglibs/tres/archive/707a09313b42e05d6ae22d1590499eece5f968ce.tar.gz",
.hash = "1220beaae8d152baa941a10b7ef3d3a59d093b257047035e2373c3c2f876ad29ccc8",
},
.diffz = .{
.url = "https://github.com/ziglibs/diffz/archive/efc91679b000a2d7f86fb40930f0a95a0d349bff.tar.gz",
.hash = "122019f94ec81a7cf6e9810983603dbacfc65ed30aea8f277f05ba0ce7c1511fff3d",
.url = "https://github.com/ziglibs/diffz/archive/b966296b4489eb082b0831ec9a37d6f5e1906040.tar.gz",
.hash = "1220ed4aed884221108ad39f2658b69a91653e0bbc8ce429bc7f1bc4e58f6a751553",
},
},
}
2 changes: 1 addition & 1 deletion src/ComptimeInterpreter.zig
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ pub const Index = InternPool.Index;
pub const Key = InternPool.Key;
pub const ComptimeInterpreter = @This();

const log = std.log.scoped(.comptime_interpreter);
const log = std.log.scoped(.zls_comptime_interpreter);

allocator: std.mem.Allocator,
ip: InternPool,
Expand Down
135 changes: 63 additions & 72 deletions src/DocumentStore.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ const types = @import("lsp.zig");
const URI = @import("uri.zig");
const analysis = @import("analysis.zig");
const offsets = @import("offsets.zig");
const log = std.log.scoped(.store);
const log = std.log.scoped(.zls_store);
const Ast = std.zig.Ast;
const BuildAssociatedConfig = @import("BuildAssociatedConfig.zig");
const BuildConfig = @import("special/build_runner.zig").BuildConfig;
Expand Down Expand Up @@ -115,13 +115,13 @@ pub fn deinit(self: *DocumentStore) void {
self.cimports.deinit(self.allocator);
}

/// returns a handle to the given document
/// Returns a handle to the given document
pub fn getHandle(self: *DocumentStore, uri: Uri) ?*const Handle {
return self.handles.get(uri);
}

/// returns a handle to the given document
/// will load the document from disk if it hasn't been already
/// Returns a handle to the given document
/// Will load the document from disk if it hasn't been already
pub fn getOrLoadHandle(self: *DocumentStore, uri: Uri) ?*const Handle {
return self.getOrLoadHandleInternal(uri) catch null;
}
Expand All @@ -141,7 +141,9 @@ fn getOrLoadHandleInternal(self: *DocumentStore, uri: Uri) !?*const Handle {
return gop.value_ptr.*;
}

pub fn openDocument(self: *DocumentStore, uri: Uri, text: []const u8) error{OutOfMemory}!Handle {
/// Takes ownership of `new_text` which has to be allocated
/// with this DocumentStore's allocator
pub fn openDocument(self: *DocumentStore, uri: Uri, text: [:0]const u8) error{OutOfMemory}!Handle {
const tracy_zone = tracy.trace(@src());
defer tracy_zone.end();

Expand All @@ -157,9 +159,7 @@ pub fn openDocument(self: *DocumentStore, uri: Uri, text: []const u8) error{OutO
var handle = try self.allocator.create(Handle);
errdefer self.allocator.destroy(handle);

const duped_text = try self.allocator.dupeZ(u8, text);

handle.* = try self.createDocument(uri, duped_text, true);
handle.* = try self.createDocument(uri, text, true);
errdefer handle.deinit(self.allocator);

try self.handles.putNoClobber(self.allocator, handle.uri, handle);
Expand Down Expand Up @@ -189,7 +189,8 @@ pub fn closeDocument(self: *DocumentStore, uri: Uri) void {
self.garbageCollectionBuildFiles() catch {};
}

/// takes ownership of `new_text` which has to be allocated with `self.allocator`
/// Takes ownership of `new_text` which has to be allocated
/// with this DocumentStore's allocator
pub fn refreshDocument(self: *DocumentStore, uri: Uri, new_text: [:0]const u8) !void {
const tracy_zone = tracy.trace(@src());
defer tracy_zone.end();
Expand Down Expand Up @@ -430,13 +431,45 @@ fn loadBuildAssociatedConfiguration(allocator: std.mem.Allocator, build_file: Bu
return try std.json.parse(BuildAssociatedConfig, &token_stream, .{ .allocator = allocator });
}

/// runs the build.zig and extracts include directories and packages
/// has to be freed with `std.json.parseFree`
fn loadBuildConfiguration(
/// Caller owns returned memory!
pub fn populateBuildConfigurationArgs(
allocator: std.mem.Allocator,
args: *std.ArrayListUnmanaged([]const u8),
zig_exe_path: []const u8,
build_runner_path: []const u8,
) error{OutOfMemory}!void {
try args.appendSlice(allocator, &.{ zig_exe_path, "build", "--build-runner", build_runner_path });
}

/// Runs the build.zig and returns the run result
/// Args should be the output of `createBuildConfigurationArgs`
/// plus any additional custom arguments
/// Arena recommended
pub fn executeBuildRunner(
allocator: std.mem.Allocator,
build_file_path: []const u8,
args: []const []const u8,
) !std.ChildProcess.ExecResult {
const tracy_zone = tracy.trace(@src());
defer tracy_zone.end();

const build_file_directory_path = try std.fs.path.resolve(allocator, &.{ build_file_path, "../" });
defer allocator.free(build_file_directory_path);

return try std.ChildProcess.exec(.{
.allocator = allocator,
.argv = args,
.cwd = build_file_directory_path,
});
}

/// Runs the build.zig and extracts include directories and packages
/// Has to be freed with `std.json.parseFree`
pub fn loadBuildConfiguration(
allocator: std.mem.Allocator,
build_file: BuildFile,
config: Config,
runtime_zig_version: ZigVersionWrapper,
_: ZigVersionWrapper,
) !BuildConfig {
const tracy_zone = tracy.trace(@src());
defer tracy_zone.end();
Expand All @@ -446,54 +479,14 @@ fn loadBuildConfiguration(
const arena_allocator = arena.allocator();

const build_file_path = try URI.parse(arena_allocator, build_file.uri);
const directory_path = try std.fs.path.resolve(arena_allocator, &.{ build_file_path, "../" });

// TODO extract this option from `BuildAssociatedConfig.BuildOption`
const zig_cache_root: []const u8 = try std.fs.path.join(arena_allocator, &.{ directory_path, "zig-cache" });

// introduction of modified module cli arguments https://github.com/ziglang/zig/pull/14664
const module_version = comptime std.SemanticVersion.parse("0.11.0-dev.1718+2737dce84") catch unreachable;
const use_new_module_cli = runtime_zig_version.version.order(module_version) != .lt;

const standard_args = if (use_new_module_cli) blk: {
const build_module = try std.fmt.allocPrint(arena_allocator, "@build@::{s}", .{build_file_path});

break :blk [_][]const u8{
config.zig_exe_path.?,
"run",
config.build_runner_path.?,
"--cache-dir",
config.global_cache_path.?,
"--mod",
build_module,
"--deps",
"@build@",
"--",
config.zig_exe_path.?,
directory_path,
zig_cache_root,
config.build_runner_global_cache_path.?,
};
} else [_][]const u8{
config.zig_exe_path.?,
"run",
config.build_runner_path.?,
"--cache-dir",
config.global_cache_path.?,
"--pkg-begin",
"@build@",
build_file_path,
"--pkg-end",
"--",
config.zig_exe_path.?,
directory_path,
zig_cache_root,
config.build_runner_global_cache_path.?,
};

const arg_length = standard_args.len + if (build_file.build_associated_config) |cfg| if (cfg.build_options) |options| options.len else 0 else 0;
// NOTE: This used to be backwards compatible
// but then I came in like a wrecking ball

const arg_length = 4 + if (build_file.build_associated_config) |cfg| if (cfg.build_options) |options| options.len else 0 else 0;
var args = try std.ArrayListUnmanaged([]const u8).initCapacity(arena_allocator, arg_length);
args.appendSliceAssumeCapacity(standard_args[0..]);
try populateBuildConfigurationArgs(arena_allocator, &args, config.zig_exe_path.?, config.build_runner_path.?);

if (build_file.build_associated_config) |cfg| {
if (cfg.build_options) |options| {
for (options) |opt| {
Expand All @@ -502,16 +495,7 @@ fn loadBuildConfiguration(
}
}

const zig_run_result = try std.ChildProcess.exec(.{
.allocator = arena_allocator,
.argv = args.items,
.cwd = try std.fs.path.resolve(arena_allocator, &.{ config.zig_exe_path.?, "../" }),
});

defer {
arena_allocator.free(zig_run_result.stdout);
arena_allocator.free(zig_run_result.stderr);
}
var zig_run_result = try executeBuildRunner(arena_allocator, build_file_path, args.items);

errdefer blk: {
const joined = std.mem.join(arena_allocator, " ", args.items) catch break :blk;
Expand All @@ -527,13 +511,20 @@ fn loadBuildConfiguration(
else => return error.RunFailed,
}

const parse_options = std.json.ParseOptions{ .allocator = allocator };
const parse_options = std.json.ParseOptions{
.allocator = allocator,
// We ignore unknown fields so people can roll
// their own build runners in libraries with
// the only requirement being general adherance
// to the BuildConfig type
.ignore_unknown_fields = true,
};
var token_stream = std.json.TokenStream.init(zig_run_result.stdout);
var build_config = std.json.parse(BuildConfig, &token_stream, parse_options) catch return error.RunFailed;
errdefer std.json.parseFree(BuildConfig, build_config, parse_options);

for (build_config.packages) |*pkg| {
const pkg_abs_path = try std.fs.path.resolve(allocator, &[_][]const u8{ directory_path, pkg.path });
const pkg_abs_path = try std.fs.path.resolve(allocator, &[_][]const u8{ build_file_path, "..", pkg.path });
allocator.free(pkg.path);
pkg.path = pkg_abs_path;
}
Expand Down Expand Up @@ -688,7 +679,7 @@ fn uriInImports(
}

/// takes ownership of the text passed in.
fn createDocument(self: *DocumentStore, uri: Uri, text: [:0]u8, open: bool) error{OutOfMemory}!Handle {
fn createDocument(self: *DocumentStore, uri: Uri, text: [:0]const u8, open: bool) error{OutOfMemory}!Handle {
const tracy_zone = tracy.trace(@src());
defer tracy_zone.end();

Expand Down
Loading