I have dropped lazy dependencies for now and went with fully static, and apart from the downside of a couple of megabytes download (which only happens once) I’m quite happy with the result.
Essentially, the build.zig in the shader compiler package exports a function compile()
which returns a build step wrapping the shader compiler invocation and this also adds the input file via addFileArg()
so that the shader compiler only kicks in when the output is dirty.
The sokol-zig package has a regular static dependency on the shader compiler package (and this will always match the sokol-gfx version so upstream projects don’t need to worry about sokol-zig and the shader compiler being out of sync).
The sokol-zig build.zig re-exports the shader compiler build.zig module (just pub const shdc = @import("shdc");
), that way upstream projects can call the shader compiler just by importing the sokol-zig build.zig, smth like this:
const sokol = @import("sokol");
// ...
const shd_step = sokol.shdc.compile(.{ ... });
// ...
…this step is then added as dependency to the executable build step which needs the compiled shader file.
For now I’m quite happy with that solution, and the shader compiler is called ‘at the right time’ (either when the input shader file is changed, or when the shader compiler package is updated via the sokol-zig package).
Only downside is that static download, maybe I’ll have another go at the ‘split package approach’.
PS: somehow I feel like dependency build.zigs should be able to declare their own custom build steps without having to call a function via an import, e.g. something like a build step plugin system which allows to hook my own build steps which run custom code into the build system and upstream build.zig being able to add such ‘plugin build steps’ into the build graph just like pre-defined build steps.
… this ‘directly import a function from a dependency’ is convenient and powerful, but it feels a bit like a hack - and I think this would also solve the whole static-import versus lazyImport dilemma (both would no longer be needed - except maybe for some very esoteric use cases - and at least in my case, all functions that are called via import are helper functions which in the end just create and return a build step object).