Suppose I have two separate Zig projects - an engine, and a game (uses engine). The engine depends on some libs (sokol, cimgui, stbi, etc), and the game depends on just the engine to do the heavy lifting.
Transitive dependencies like sokol have options of their own which we can pass. The question is - how to pass them in my case? It would be as easy as defining b.option(bool, ...), but my web build complicates things and I feel like I got it the wrong way. Because linkWasm resolves dependencies again to do emscripten specific stuff like sysroot injection - I suppose it needs these options as well since otherwise it will be two completely different resolutions - one with .with_tracing and the other without it.
I would like to keep all the wiring inside the engine’s build.zig, and expose helpers. pacman.zig project gave me some ideas, but it does all the wiring by itself and depends on sokol directly whereas in my case it is game->engine->sokol+cimgui+zstbi and a lot more to wire - but maybe this is the correct way?
Could you give advice on how to re-structure my build so it becomes less complicated..
Engine’s build.zig:
const std = @import("std");
const Build = std.Build;
const builtin = @import("builtin");
const cimgui = @import("cimgui");
const sokol = @import("sokol");
// Hard-coded as of now.
const use_docking = false;
pub fn build(b: *Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
// Example option
const sokol_tracing = b.option(bool, "sokol_tracing", "Enable sokol's with_tracing option") orelse false;
const cimgui_conf = cimgui.getConfig(use_docking);
const dep_sokol = b.dependency("sokol", .{
.target = target,
.optimize = optimize,
.with_sokol_imgui = true,
.with_tracing = sokol_tracing,
});
const dep_cimgui = b.dependency("cimgui", .{
.target = target,
.optimize = optimize,
});
const dep_zstbi = b.dependency("zstbi", .{
.target = target,
.optimize = optimize,
});
dep_sokol.artifact("sokol_clib").root_module.addIncludePath(dep_cimgui.path(cimgui_conf.include_dir));
_ = b.addModule("zen", .{
.root_source_file = b.path("src/zen.zig"),
.target = target,
.optimize = optimize,
.imports = &.{
.{ .name = "sokol", .module = dep_sokol.module("sokol") },
.{ .name = "zstbi", .module = dep_zstbi.module("root") },
.{ .name = cimgui_conf.module_name, .module = dep_cimgui.module(cimgui_conf.module_name) },
},
});
// Compile engine shaders
const shaders_step = b.step("shaders", "Compile engine shaders");
const dep_shdc = dep_sokol.builder.dependency("shdc", .{});
const engine_shaders = .{"sprite"};
inline for (engine_shaders) |name| {
const step = try sokol.shdc.createSourceFile(b, .{
.shdc_dep = dep_shdc,
.input = "src/gx/shaders/" ++ name ++ ".glsl",
.output = "src/gx/shaders/" ++ name ++ ".zig",
.slang = default_slang,
});
shaders_step.dependOn(step);
}
}
const default_slang: sokol.shdc.Slang = .{
.glsl430 = false,
.glsl300es = true,
.metal_macos = true,
.hlsl5 = true,
};
pub const ShaderOptions = struct {
input: []const u8,
output: []const u8,
};
pub fn buildShaders(
b: *Build,
dep_zen: *Build.Dependency,
compile: *Build.Step.Compile,
shaders: []const ShaderOptions,
) !void {
const dep_sokol = dep_zen.builder.dependency("sokol", .{});
const dep_shdc = dep_sokol.builder.dependency("shdc", .{});
for (shaders) |s| {
const step = try sokol.shdc.createSourceFile(b, .{
.shdc_dep = dep_shdc,
.input = s.input,
.output = s.output,
.slang = default_slang,
});
compile.step.dependOn(step);
}
}
pub const LinkWasmOptions = struct {
lib: *Build.Step.Compile,
shell_file_path: ?Build.LazyPath = null,
use_filesystem: bool = true,
extra_args: []const []const u8 = &.{},
};
/// Do the emscripten link for a WASM build.
pub fn linkWasm(
b: *Build,
dep_zen: *Build.Dependency,
options: LinkWasmOptions,
) !*Build.Step.InstallDir {
const target = options.lib.root_module.resolved_target.?;
const optimize = options.lib.root_module.optimize.?;
const dep_sokol = dep_zen.builder.dependency("sokol", .{
.target = target,
.optimize = optimize,
.with_sokol_imgui = true,
.with_tracing = false, // Needs the option again
});
const dep_cimgui = dep_zen.builder.dependency("cimgui", .{
.target = target,
.optimize = optimize,
});
const dep_emsdk = dep_sokol.builder.dependency("emsdk", .{});
const dep_zstbi = dep_zen.builder.dependency("zstbi", .{
.target = target,
.optimize = optimize,
});
const sokol_clib = dep_sokol.artifact("sokol_clib");
const cimgui_clib = dep_cimgui.artifact("cimgui_clib");
// C libs need the emscripten system headers
const emsdk_incl = dep_emsdk.path("upstream/emscripten/cache/sysroot/include");
dep_zstbi.module("root").addSystemIncludePath(emsdk_incl);
sokol_clib.root_module.addSystemIncludePath(emsdk_incl);
cimgui_clib.root_module.addSystemIncludePath(emsdk_incl);
cimgui_clib.step.dependOn(&sokol_clib.step);
const shell = options.shell_file_path orelse dep_zen.path("shell.html");
return sokol.emLinkStep(b, .{
.lib_main = options.lib,
.target = target,
.optimize = optimize,
.emsdk = dep_emsdk,
.use_webgl2 = true,
.use_emmalloc = true,
.use_filesystem = options.use_filesystem,
.shell_file_path = shell,
.extra_args = options.extra_args,
});
}
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.