Merge pull request #683 from jcmoyer/build-options
Allow user defined options to be passed to build_runner
This commit is contained in:
commit
a70beba6d2
24
README.md
24
README.md
@ -18,6 +18,8 @@ Zig Language Server, or `zls`, is a language server for Zig. The Zig wiki states
|
|||||||
- [Build Options](#build-options)
|
- [Build Options](#build-options)
|
||||||
- [Updating Data Files](#updating-data-files)
|
- [Updating Data Files](#updating-data-files)
|
||||||
- [Configuration Options](#configuration-options)
|
- [Configuration Options](#configuration-options)
|
||||||
|
- [Per-build Configuration Options](#per-build-configuration-options)
|
||||||
|
- [`BuildOption`](#buildoption)
|
||||||
- [Features](#features)
|
- [Features](#features)
|
||||||
- [VS Code](#vs-code)
|
- [VS Code](#vs-code)
|
||||||
- [Sublime Text](#sublime-text)
|
- [Sublime Text](#sublime-text)
|
||||||
@ -117,6 +119,28 @@ The following options are currently available.
|
|||||||
|`max_detail_length`|`usize`|`1024 * 1024`| The detail field of completions is truncated to be no longer than this (in bytes).
|
|`max_detail_length`|`usize`|`1024 * 1024`| The detail field of completions is truncated to be no longer than this (in bytes).
|
||||||
| `skip_std_references` | `bool` | `false` | When true, skips searching for references in std. Improves lookup speed for functions in user's code. Renaming and go-to-definition will continue to work as is.
|
| `skip_std_references` | `bool` | `false` | When true, skips searching for references in std. Improves lookup speed for functions in user's code. Renaming and go-to-definition will continue to work as is.
|
||||||
|
|
||||||
|
### Per-build Configuration Options
|
||||||
|
|
||||||
|
The following options can be set on a per-project basis by placing `zls.build.json` in the project root directory next to `build.zig`.
|
||||||
|
|
||||||
|
| Option | Type | Default value | What it Does |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `relative_builtin_path` | `?[]const u8` | `null` | If present, this path is used to resolve `@import("builtin")` |
|
||||||
|
| `build_options` | `?[]BuildOption` | `null` | If present, this contains a list of user options to pass to the build. This is useful when options are used to conditionally add packages in `build.zig`. |
|
||||||
|
|
||||||
|
#### `BuildOption`
|
||||||
|
|
||||||
|
`BuildOption` is defined as follows:
|
||||||
|
|
||||||
|
```zig
|
||||||
|
const BuildOption = struct {
|
||||||
|
name: []const u8,
|
||||||
|
value: ?[]const u8 = null,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
When `value` is present, the option will be passed the same as in `zig build -Dname=value`. When `value` is `null`, the option will be passed as a flag instead as in `zig build -Dflag`.
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
`zls` supports most language features, including simple type function support, using namespace, payload capture type resolution, custom packages, `cImport` and others.
|
`zls` supports most language features, including simple type function support, using namespace, payload capture type resolution, custom packages, `cImport` and others.
|
||||||
|
@ -1,4 +1,44 @@
|
|||||||
// Configuration options related to a specific `BuildFile`.
|
//! Configuration options related to a specific `BuildFile`.
|
||||||
|
const std = @import("std");
|
||||||
|
|
||||||
|
pub const BuildOption = struct {
|
||||||
|
name: []const u8,
|
||||||
|
value: ?[]const u8 = null,
|
||||||
|
|
||||||
|
/// Frees the strings assocated with this `BuildOption` and invalidates `self`.
|
||||||
|
pub fn deinit(self: *BuildOption, allocator: std.mem.Allocator) void {
|
||||||
|
allocator.free(self.name);
|
||||||
|
if (self.value) |val| {
|
||||||
|
allocator.free(val);
|
||||||
|
}
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Duplicates the `BuildOption`, copying internal strings. Caller owns returned option with contents
|
||||||
|
/// allocated using `allocator`.
|
||||||
|
pub fn dupe(self: BuildOption, allocator: std.mem.Allocator) !BuildOption {
|
||||||
|
const copy_name = try allocator.dupe(u8, self.name);
|
||||||
|
errdefer allocator.free(copy_name);
|
||||||
|
const copy_value = if (self.value) |val|
|
||||||
|
try allocator.dupe(u8, val)
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
return BuildOption{
|
||||||
|
.name = copy_name,
|
||||||
|
.value = copy_value,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Formats the `BuildOption` as a command line parameter compatible with `zig build`. This will either be
|
||||||
|
/// `-Dname=value` or `-Dname`. Caller owns returned slice allocated using `allocator`.
|
||||||
|
pub fn formatParam(self: BuildOption, allocator: std.mem.Allocator) ![]const u8 {
|
||||||
|
if (self.value) |val| {
|
||||||
|
return try std.fmt.allocPrint(allocator, "-D{s}={s}", .{ self.name, val });
|
||||||
|
} else {
|
||||||
|
return try std.fmt.allocPrint(allocator, "-D{s}", .{self.name});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// If provided this path is used when resolving `@import("builtin")`
|
/// If provided this path is used when resolving `@import("builtin")`
|
||||||
/// It is relative to the directory containing the `build.zig`
|
/// It is relative to the directory containing the `build.zig`
|
||||||
@ -6,3 +46,6 @@
|
|||||||
/// This file should contain the output of:
|
/// This file should contain the output of:
|
||||||
/// `zig build-exe/build-lib/build-obj --show-builtin <options>`
|
/// `zig build-exe/build-lib/build-obj --show-builtin <options>`
|
||||||
relative_builtin_path: ?[]const u8 = null,
|
relative_builtin_path: ?[]const u8 = null,
|
||||||
|
|
||||||
|
/// If provided, this list of options will be passed to `build.zig`.
|
||||||
|
build_options: ?[]BuildOption = null,
|
||||||
|
@ -23,9 +23,16 @@ const BuildFile = struct {
|
|||||||
uri: []const u8,
|
uri: []const u8,
|
||||||
config: BuildFileConfig,
|
config: BuildFileConfig,
|
||||||
builtin_uri: ?[]const u8 = null,
|
builtin_uri: ?[]const u8 = null,
|
||||||
|
build_options: ?[]BuildAssociatedConfig.BuildOption = null,
|
||||||
|
|
||||||
pub fn destroy(self: *BuildFile, allocator: std.mem.Allocator) void {
|
pub fn destroy(self: *BuildFile, allocator: std.mem.Allocator) void {
|
||||||
if (self.builtin_uri) |builtin_uri| allocator.free(builtin_uri);
|
if (self.builtin_uri) |builtin_uri| allocator.free(builtin_uri);
|
||||||
|
if (self.build_options) |opts| {
|
||||||
|
for (opts) |*opt| {
|
||||||
|
opt.deinit(allocator);
|
||||||
|
}
|
||||||
|
allocator.free(opts);
|
||||||
|
}
|
||||||
allocator.destroy(self);
|
allocator.destroy(self);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -111,10 +118,12 @@ fn loadBuildAssociatedConfiguration(allocator: std.mem.Allocator, build_file: *B
|
|||||||
const directory_path = build_file_path[0 .. build_file_path.len - "build.zig".len];
|
const directory_path = build_file_path[0 .. build_file_path.len - "build.zig".len];
|
||||||
|
|
||||||
const options = std.json.ParseOptions{ .allocator = allocator };
|
const options = std.json.ParseOptions{ .allocator = allocator };
|
||||||
const build_associated_config = blk: {
|
var build_associated_config = blk: {
|
||||||
const config_file_path = try std.fs.path.join(allocator, &[_][]const u8{ directory_path, "zls.build.json" });
|
const config_file_path = try std.fs.path.join(allocator, &[_][]const u8{ directory_path, "zls.build.json" });
|
||||||
defer allocator.free(config_file_path);
|
defer allocator.free(config_file_path);
|
||||||
|
|
||||||
|
log.info("Attempting to load build-associated config from {s}", .{config_file_path});
|
||||||
|
|
||||||
var config_file = std.fs.cwd().openFile(config_file_path, .{}) catch |err| {
|
var config_file = std.fs.cwd().openFile(config_file_path, .{}) catch |err| {
|
||||||
if (err == error.FileNotFound) return;
|
if (err == error.FileNotFound) return;
|
||||||
return err;
|
return err;
|
||||||
@ -133,6 +142,11 @@ fn loadBuildAssociatedConfiguration(allocator: std.mem.Allocator, build_file: *B
|
|||||||
defer allocator.free(absolute_builtin_path);
|
defer allocator.free(absolute_builtin_path);
|
||||||
build_file.builtin_uri = try URI.fromPath(allocator, absolute_builtin_path);
|
build_file.builtin_uri = try URI.fromPath(allocator, absolute_builtin_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (build_associated_config.build_options) |opts| {
|
||||||
|
build_file.build_options = opts;
|
||||||
|
build_associated_config.build_options = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const LoadBuildConfigContext = struct {
|
const LoadBuildConfigContext = struct {
|
||||||
@ -156,11 +170,15 @@ fn loadBuildConfiguration(context: LoadBuildConfigContext) !void {
|
|||||||
const global_cache_path = context.global_cache_path;
|
const global_cache_path = context.global_cache_path;
|
||||||
const zig_exe_path = context.zig_exe_path;
|
const zig_exe_path = context.zig_exe_path;
|
||||||
|
|
||||||
|
var arena = std.heap.ArenaAllocator.init(allocator);
|
||||||
|
defer arena.deinit();
|
||||||
|
const arena_allocator = arena.allocator();
|
||||||
|
|
||||||
const build_file_path = context.build_file_path orelse try URI.parse(allocator, build_file.uri);
|
const build_file_path = context.build_file_path orelse try URI.parse(allocator, build_file.uri);
|
||||||
defer if (context.build_file_path == null) allocator.free(build_file_path);
|
defer if (context.build_file_path == null) allocator.free(build_file_path);
|
||||||
const directory_path = build_file_path[0 .. build_file_path.len - "build.zig".len];
|
const directory_path = build_file_path[0 .. build_file_path.len - "build.zig".len];
|
||||||
|
|
||||||
const args: []const []const u8 = &[_][]const u8{
|
const standard_args = [_][]const u8{
|
||||||
zig_exe_path,
|
zig_exe_path,
|
||||||
"run",
|
"run",
|
||||||
build_runner_path,
|
build_runner_path,
|
||||||
@ -177,6 +195,16 @@ fn loadBuildConfiguration(context: LoadBuildConfigContext) !void {
|
|||||||
context.global_cache_root,
|
context.global_cache_root,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var args = try arena_allocator.alloc([]const u8, standard_args.len + if (build_file.build_options) |opts| opts.len else 0);
|
||||||
|
defer arena_allocator.free(args);
|
||||||
|
|
||||||
|
args[0..standard_args.len].* = standard_args;
|
||||||
|
if (build_file.build_options) |opts| {
|
||||||
|
for (opts) |opt, i| {
|
||||||
|
args[standard_args.len + i] = try opt.formatParam(arena_allocator);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const zig_run_result = try std.ChildProcess.exec(.{
|
const zig_run_result = try std.ChildProcess.exec(.{
|
||||||
.allocator = allocator,
|
.allocator = allocator,
|
||||||
.argv = args,
|
.argv = args,
|
||||||
|
@ -57,6 +57,30 @@ pub fn main() !void {
|
|||||||
|
|
||||||
defer builder.destroy();
|
defer builder.destroy();
|
||||||
|
|
||||||
|
while (nextArg(args, &arg_idx)) |arg| {
|
||||||
|
if (std.mem.startsWith(u8, arg, "-D")) {
|
||||||
|
const option_contents = arg[2..];
|
||||||
|
if (option_contents.len == 0) {
|
||||||
|
log.err("Expected option name after '-D'\n\n", .{});
|
||||||
|
return error.InvalidArgs;
|
||||||
|
}
|
||||||
|
if (std.mem.indexOfScalar(u8, option_contents, '=')) |name_end| {
|
||||||
|
const option_name = option_contents[0..name_end];
|
||||||
|
const option_value = option_contents[name_end + 1 ..];
|
||||||
|
if (try builder.addUserInputOption(option_name, option_value)) {
|
||||||
|
log.err("Option conflict '-D{s}'\n\n", .{option_name});
|
||||||
|
return error.InvalidArgs;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const option_name = option_contents;
|
||||||
|
if (try builder.addUserInputFlag(option_name)) {
|
||||||
|
log.err("Option conflict '-D{s}'\n\n", .{option_name});
|
||||||
|
return error.InvalidArgs;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
builder.resolveInstallPrefix(null, Builder.DirList{});
|
builder.resolveInstallPrefix(null, Builder.DirList{});
|
||||||
try runBuild(builder);
|
try runBuild(builder);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user