Build-On-Save doesn't work in sub projects
Zig Version
0.15.0-dev.369+1a2ceb36c
ZLS Version
0.15.0-dev.64+e81b740
Client / Code Editor / Extensions
Helix
Steps to Reproduce and Observed Behavior
mkdir -p main && cd main
zig init
add the following
const exe_check = b.addExecutable(.{
.name = "main",
.root_module = exe_mod,
});
const check = b.step("check", "Check compilation errors");
check.dependOn(&exe_check.step);
to main/build.zig
mkdir -p examples/sub && cd examples/sub
zig init
Add a similar check step to examples/sub/build.zig
const exe_check = b.addExecutable(.{
.name = "sub",
.root_module = exe_mod,
});
const check = b.step("check", "Check compilation errors");
check.dependOn(&exe_check.step);
Expected Behavior
Biuld-On-Save should work in the sub Zig project just as it does in the main project.
Because I noticed if I move main/examples/sub to become a top level project mv main/examples/sub sub Build-On-Save works as expected.
Relevant log output
info ( main ): Starting ZLS 0.15.0-dev.64+e81b740 @ '/home/ultracode/.local/bin/zls'
info ( main ): Log File: /home/ultracode/.cache/zls/zls.log (info)
info (server): Client Info: helix-25.01.1 (340934db)
info (server): Autofix Mode: source.fixall
info (server): added Workspace Folder: file:///home/ultracode/repos/zig/zvips
info (server): Set config option 'enable_build_on_save' to true
info (server): Set config option 'build_on_save_args' to ["check","--watch","-fincremental"]
info (server): Set config option 'warn_style' to true
info (server): Set config option 'highlight_global_var_declarations' to true
info (server): Set config option 'builtin_path' to "/home/ultracode/.cache/zls/builtin.zig"
info (server): Set config option 'zig_lib_path' to "/home/ultracode/.local/zig/zig-linux-x86_64-0.15.0-dev.369+1a2ceb36c/lib"
info (server): Set config option 'zig_exe_path' to "/home/ultracode/.local/bin/zig"
info (server): Set config option 'build_runner_path' to "/home/ultracode/.cache/zls/build_runner/06ec558a7bde93433271005c5551b33e/build_runner.zig"
info (server): Set config option 'global_cache_path' to "/home/ultracode/.cache/zls"
info (server): trying to start Build-On-Save for 'file:///home/ultracode/repos/zig/zvips'
info (store ): Loaded build file 'file:///home/ultracode/repos/zig/zvips/examples/basic/build.zig'
info (store ): Loaded build file 'file:///home/ultracode/repos/zig/zvips/build.zig'
info (store ): Loaded build file 'file:///home/ultracode/repos/zig/zvips/bindings/build.zig'
Helix Config
[language-server.zls.config]
enable_build_on_save = true
build_on_save_args = ["check", "--watch", "-fincremental"]
I would love to fix this issue as it is affecting the developer experience on zvips, a new project I'm working on. I would appreciate some guidance since I'm new to the codebase.
As it stands right now, ZLS is intentionally only running build on save in the workspace root directory. The project may contain arbitrarily many subprojects with their own "check" step which does not scale well if ZLS would run build on save separately for every one of them.
One possible approach to get build on save running for sub projects would be to modify the "check" step of the root project to run the "check" step of your subprojects:
Update the build.zig.zon of the root project to depend on the sub project:
.{
.dependencies = .{
.my_subproject = .{
.path = "examples/sub",
},
},
}
Then hook up the "check" steps in the build.zig of the root project:
const my_subproject = b.dependency("my_subproject", .{ .target = target, .optimize = optimize });
const my_subproject_check = &my_subproject.builder.top_level_steps.get("check").?.step;
const check = b.step("check", "Check compilation errors");
check.dependOn(&exe_check.step);
check.dependOn(my_subproject_check);
This approach also has the advantage that manually running zig build check in the root project will also report compile errors of the linked subprojects.
Let me know if this approach works for you.
I don't think it would be appropriate for my use case because the examples directory is ignored in the release artifact and the Zig build runner requires all dependencies to be available at build time but won't be in the end users package.
As it stands right now, ZLS is intentionally only running build on save in the workspace root directory. The project may contain arbitrarily many subprojects with their own "check" step which does not scale well if ZLS would run build on save separately for every one of them.
I agree with the above. What I'm rooting for is a slight modification to how we determine the workspace root directory.
The idea
- Every directory/subdirectory with a dedicated
build.zigis a workspace candidate - We set the workspace root relative to the present working directory For instance if we have a directory structure like bellow
📂 Directory structure
📂 zvips
├── 📂 examples
│ └── 📂 src
│ │ └── ⚡ sub_main.zig
│ └── ⚡ build.zig
├── 📂 lib
│ └── ⚡ main.zig
├── ⚡ build.zig
├── ⚡ build.zig.zon
├── LICENSE
└── README.md
When I cd zvips, zvips is the project workspace root and zvips/build.zig is the build file of the workspace so zls would only provide Build-On-Save daignostics for only files/projects(dependencies) referenced from zvips/build.zig hence the current behaviour of not running arbitrary check steps
But when I cd zvips/examples, since the examples subdirectory contains a dedicated build.zig the workspace root is zvips/examples and the zvips/examples/build.zig is used by the build runner and Build-On-Save for dependency fetching and Diagnostics. So If from my Editor I open src/sub_main.zig I get Build-On-Save diagnostics but if I later open ../lib/main.zig from my editor, only basic syntax diagnostics is given because it isn't referenced in anyway in examples/build.zig.
TLDR
- We determine the workspace root relative to the
pwdwhere an editor instance was opened initially - If that directory doesn't have a
build.zigwe move up parent directories a number of time (1-3max - maybe configurable) to see if we find abuild.zig, If found, we try to use it for the project else fallback to only syntax error with some information inzls.logabout why we can't doBuild-On-Savediagnostics - If that directory has a
build.zigthen our job is done and we use thisbuild.zigas input for our build runner andBuild-On-Save. Modules in this workspace and linked dependencies like your recommendation above works but anything outside of that scope give only basic diagnostics.
What do you think?
There are also cases where the directory initially opened in the editor is a non-zig project (for example a JavaScript project), and it has a child directory with build.zig. Would be great if this could be handled somehow.