Build-On-Save
ZLS can be configured to build your project on save. If that results in errors, they will be displayed in your editor like any other diagnostic.
Configuration
There are two ways to opt into Build-On-Save:
- The
build.zig
of your project defines a “check” step. (recommended) - The
enable_build_on_save
config option has been manually set totrue
.
The first method avoids dealing with config options and can report compile errors quicker.
Add a “check” step to your build.zig
This part is more deeply tied to your specific project but the gist is the following: whatever you do to define your main executable or library, you do it again in a new step named check.
I’ll use for this example the executable definition step you get generated automatically from zig init
.
// Create a module just like in the `zig init` template.
const exe_mod = b.addModule("foo", .{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
});
// Any other code to define dependencies would probably be here.
// Create an executable from the given module.
// Still the same as the `zig init` template.
const exe = b.addExecutable(.{
.name = "foo",
.root_module = exe_mod,
});
b.installArtifact(exe);
// This is where the interesting part begins.
// As you can see we are re-defining the same executable but
// we're binding it to a dedicated build step.
const exe_check = b.addExecutable(.{
.name = "foo",
.root_module = exe_mod,
});
// There is no `b.installArtifact(exe_check);` here.
// Finally we add the "check" step which will be detected
// by ZLS and automatically enable Build-On-Save.
// If you copy this into your `build.zig`, make sure to rename 'foo'
const check = b.step("check", "Check if foo compiles");
check.dependOn(&exe_check.step);
The most important part about this second executable definition is that we ask to build it, but we never install it. If you look at the final line of the first section, you will see that we call b.installArtifact
on the original executable, but for the executable bound to the “check” step, we don’t.
This one-line difference will have a big impact on the resulting behavior of the build as it will add the -fno-emit-bin
flag to the compiler invocation which, in other words, means that Zig will analyze your code (and report any error) but it won’t bother calling into LLVM since you don’t plan to install the executable anyway.
The result is that you will get diagnostics pretty fast since you won’t have to go through the “LLVM Emit Code…” phase.
Once you’re done with this, restart your editor (or at least ZLS), save your file with an error in it, and enjoy your new spiffy diagnostics.
Customize build arguments
By default, ZLS will run the equivalent of the following command:
zig build check --watch
If no “check” step has been found and enable_build_on_save
has been enabled, it will fall back to the “install” step.
To further customize the build arguments, the build_on_save_args
config option can be used. Here are some arbitrary examples to showcase what could be done in theory:
- Override which build step(s) should be run:
"build_on_save_args": ["check", "test"]
- Set project-specific build options:
"build_on_save_args": ["-Dtarget=wasm32-wasi"]
- Set additional build system flags:
"build_on_save_args": ["-j4"]
Run zig build --help
to find out what build steps and build options are available in your project.
Special Thanks
This Guide is based on the “Improving Your Zig Language Server Experience” Blog Post by Loris Cro.