Skip to content

Commit

Permalink
fix(emscripten): ensure EMSDK setup completes before WASM compilation (
Browse files Browse the repository at this point in the history
  • Loading branch information
kassane authored Feb 12, 2025
1 parent eaddb83 commit b2a1620
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 44 deletions.
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,21 @@ zig build --help
# -Dwayland=[bool] Force Wayland (default: false, Linux only, not supported in main-line headers)
# -Degl=[bool] Force EGL (default: false, Linux only)
# -Dimgui=[bool] Add support for sokol_imgui.h bindings
# -Dsokol_imgui_cprefix=[string] Override Dear ImGui C bindings prefix for sokol_imgui.h (see SOKOL_IMGUI_CPREFIX)
# -Dcimgui_header_path=[string] Override the Dear ImGui C bindings header name (default: cimgui.h)
# -Dimgui-version=[enum] Select ImGui version to use
# Supported Values:
# default
# docking
# -Dubsan=[bool] Enable undefined behavior sanitizer
# -Dtsan=[bool] Enable thread sanitizer
# -Dartifact=[bool] Build artifacts (default: false)
# -DbetterC=[bool] Omit generating some runtime information and helper functions (default: false)
# -DzigCC=[bool] Use zig cc as compiler and linker (default: false)
# -Dshaders=[bool] Build shaders (default: false)
# -Dtarget=[string] The CPU architecture, OS, and ABI to build for
# -Dcpu=[string] Target CPU features to add or subtract
# -Dofmt=[string] Target object format
# -Ddynamic-linker=[string] Path to interpreter on the target system
# -Doptimize=[enum] Prioritize performance, safety, or binary size
# Supported Values:
Expand All @@ -79,13 +89,6 @@ zig build --help
# ReleaseFast
# ReleaseSmall
# -Dshared=[bool] Build sokol dynamic library (default: static)
# -Dubsan=[bool] Enable undefined behavior sanitizer
# -Dtsan=[bool] Enable thread sanitizer
# -Dimgui-version=[enum] Select ImGui version to use
# Supported Values:
# default
# docking

```
(also run `zig build -l` to get a list of build targets)

Expand All @@ -109,4 +112,4 @@ The sokol headers are created by Andre Weissflog (floooh) and sokol is released

[^1]: https://github.com/floooh/sokol-zig/
[^2]: https://github.com/floooh/sokol-rust/
[^3]: https://github.com/floooh/sokol/blob/master/LICENSE
[^3]: https://github.com/floooh/sokol/blob/master/LICENSE
113 changes: 81 additions & 32 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ pub const LibSokolOptions = struct {
use_x11: bool = true,
use_wayland: bool = false,
emsdk: ?*Build.Dependency = null,
use_ubsan: bool = false,
use_tsan: bool = false,
with_sokol_imgui: bool = false,
imgui_version: ?[]const u8 = null,
sokol_imgui_cprefix: ?[]const u8 = null,
cimgui_header_path: ?[]const u8 = null,
};

// helper function to resolve .auto backend based on target platform
Expand Down Expand Up @@ -62,8 +67,8 @@ pub fn buildLibSokol(b: *Build, options: LibSokolOptions) !*CompileStep {
.link_libc = true,
});

lib.root_module.sanitize_c = b.option(bool, "ubsan", "Enable undefined behavior sanitizer") orelse false;
lib.root_module.sanitize_thread = b.option(bool, "tsan", "Enable thread sanitizer") orelse false;
lib.root_module.sanitize_c = options.use_ubsan;
lib.root_module.sanitize_thread = options.use_tsan;

switch (options.optimize) {
.Debug, .ReleaseSafe => lib.bundle_compiler_rt = true,
Expand Down Expand Up @@ -192,23 +197,22 @@ pub fn buildLibSokol(b: *Build, options: LibSokolOptions) !*CompileStep {
}

if (options.with_sokol_imgui) {
if (b.lazyDependency("imgui", .{})) |dep| {
if (options.imgui_version) |imgui_version| {
const imgui = dep.path(imgui_version);
lib.addIncludePath(imgui);
}
}
if (options.sokol_imgui_cprefix) |cprefix| {
try cflags.append(b.fmt("-DSOKOL_IMGUI_CPREFIX={s}", .{cprefix}));
}
if (options.cimgui_header_path) |cimgui_header_path| {
try cflags.append(b.fmt("-DCIMGUI_HEADER_PATH=\"{s}\"", .{cimgui_header_path}));
}
lib.addCSourceFile(.{
.file = b.path(csrc_root ++ "sokol_imgui.c"),
.flags = cflags.slice(),
});
const imgui = try buildImgui(b, .{
.target = options.target,
.optimize = options.optimize,
.use_tsan = lib.root_module.sanitize_thread orelse false,
.use_ubsan = lib.root_module.sanitize_c orelse false,
});
if (options.emsdk) |emsdk| {
imgui.addSystemIncludePath(emSdkLazyPath(b, emsdk, &.{ "upstream", "emscripten", "cache", "sysroot", "include" }));
}
for (imgui.root_module.include_dirs.items) |dir| {
try lib.root_module.include_dirs.append(b.allocator, dir);
}
lib.linkLibrary(imgui);
}
return lib;
}
Expand All @@ -221,8 +225,21 @@ pub fn build(b: *Build) !void {
const opt_use_wayland = b.option(bool, "wayland", "Force Wayland (default: false, Linux only, not supported in main-line headers)") orelse false;
const opt_use_egl = b.option(bool, "egl", "Force EGL (default: false, Linux only)") orelse false;
const opt_with_sokol_imgui = b.option(bool, "imgui", "Add support for sokol_imgui.h bindings") orelse false;
const opt_sokol_imgui_cprefix = b.option([]const u8, "sokol_imgui_cprefix", "Override Dear ImGui C bindings prefix for sokol_imgui.h (see SOKOL_IMGUI_CPREFIX)");
const opt_cimgui_header_path = b.option([]const u8, "cimgui_header_path", "Override the Dear ImGui C bindings header name (default: cimgui.h)");
const sokol_backend: SokolBackend = if (opt_use_gl) .gl else if (opt_use_gles3) .gles3 else if (opt_use_wgpu) .wgpu else .auto;
const imguiver_path = switch (b.option(
imguiVersion,
"imgui-version",
"Select ImGui version to use",
) orelse imguiVersion.default) {
.default => "src",
.docking => "src-docking",
};

// For debug
const sanitize_c = b.option(bool, "ubsan", "Enable undefined behavior sanitizer") orelse false;
const sanitize_thread = b.option(bool, "tsan", "Enable thread sanitizer") orelse false;
// LDC-options options
const dub_artifact = b.option(bool, "artifact", "Build artifacts (default: false)") orelse false;
const opt_betterC = b.option(bool, "betterC", "Omit generating some runtime information and helper functions (default: false)") orelse false;
Expand All @@ -243,11 +260,32 @@ pub fn build(b: *Build) !void {
.use_x11 = opt_use_x11,
.use_egl = opt_use_egl,
.with_sokol_imgui = opt_with_sokol_imgui,
.sokol_imgui_cprefix = opt_sokol_imgui_cprefix,
.cimgui_header_path = opt_cimgui_header_path,
.imgui_version = imguiver_path,
.emsdk = emsdk,
.use_ubsan = sanitize_c,
.use_tsan = sanitize_thread,
});

var lib_imgui: ?*CompileStep = null;
if (opt_with_sokol_imgui) {
const imgui = try buildImgui(b, .{
.target = target,
.optimize = optimize,
.version = imguiver_path,
.use_ubsan = sanitize_c,
.use_tsan = sanitize_thread,
});
imgui.step.dependOn(&lib_sokol.step);
lib_imgui = imgui;
}

if (opt_shaders)
buildShaders(b, target);
if (dub_artifact) {
if (opt_with_sokol_imgui)
b.installArtifact(lib_imgui.?);
b.installArtifact(lib_sokol);
} else {
// build examples
Expand Down Expand Up @@ -281,6 +319,7 @@ pub fn build(b: *Build) !void {
const ldc = try ldcBuildStep(b, .{
.name = example,
.artifact = lib_sokol,
.imgui = lib_imgui,
.sources = &[_][]const u8{
b.fmt("{s}/src/examples/{s}.d", .{ rootPath(), example }),
},
Expand All @@ -297,6 +336,7 @@ pub fn build(b: *Build) !void {
.kind = if (target.result.isWasm()) .obj else .exe,
.emsdk = emsdk,
.backend = sokol_backend,
.with_sokol_imgui = opt_with_sokol_imgui,
});
b.getInstallStep().dependOn(&ldc.step);
}
Expand All @@ -314,7 +354,7 @@ pub fn build(b: *Build) !void {
}

// Use LDC2 (https://github.com/ldc-developers/ldc) to compile the D examples
pub fn ldcBuildStep(b: *Build, options: DCompileStep) !*std.Build.Step.InstallDir {
pub fn ldcBuildStep(b: *Build, options: DCompileStep) !*Build.Step.InstallDir {
// ldmd2: ldc2 wrapped w/ dmd flags
const ldc = try b.findProgram(&.{"ldmd2"}, &.{});

Expand Down Expand Up @@ -676,10 +716,21 @@ pub fn ldcBuildStep(b: *Build, options: DCompileStep) !*std.Build.Step.InstallDi
b.step("test", "Run all tests");

if (options.target.result.isWasm()) {
ldc_exec.step.dependOn(&options.artifact.?.step);
// get D object file and put it in the wasm artifact
const artifact = addArtifact(b, options);
artifact.addObjectFile(objpath);
artifact.linkLibrary(options.artifact.?);
if (options.artifact) |lib_sokol| {
if (options.with_sokol_imgui) {
if (options.imgui) |lib_imgui| {
for (lib_sokol.root_module.include_dirs.items) |include_dir| {
try lib_imgui.root_module.include_dirs.append(b.allocator, include_dir);
}
artifact.linkLibrary(lib_imgui);
}
}
artifact.linkLibrary(lib_sokol);
}
artifact.step.dependOn(&ldc_exec.step);
const backend = resolveSokolBackend(options.backend, options.target.result);
const link_step = try emLinkStep(b, .{
Expand Down Expand Up @@ -709,6 +760,11 @@ pub fn ldcBuildStep(b: *Build, options: DCompileStep) !*std.Build.Step.InstallDi
b.fmt("{s}.lib", .{lib_sokol.name}),
}));
} else {
if (options.with_sokol_imgui) {
if (options.imgui) |lib_imgui| {
ldc_exec.addArtifactArg(lib_imgui);
}
}
ldc_exec.addArtifactArg(lib_sokol);
for (lib_sokol.getCompileDependencies(false)) |item| {
if (item.kind == .lib) {
Expand Down Expand Up @@ -736,6 +792,8 @@ pub const DCompileStep = struct {
zig_cc: bool = false,
d_packages: ?[]const []const u8 = null,
artifact: ?*Build.Step.Compile = null,
imgui: ?*Build.Step.Compile = null,
with_sokol_imgui: bool = false,
emsdk: ?*Build.Dependency = null,
backend: SokolBackend = .auto,
};
Expand All @@ -751,7 +809,7 @@ pub fn addArtifact(b: *Build, options: DCompileStep) *Build.Step.Compile {
});
}

pub fn path(b: *std.Build, sub_path: []const u8) std.Build.LazyPath {
pub fn path(b: *Build, sub_path: []const u8) Build.LazyPath {
if (std.fs.path.isAbsolute(sub_path)) {
return .{
.cwd_relative = sub_path,
Expand All @@ -767,7 +825,7 @@ pub fn path(b: *std.Build, sub_path: []const u8) std.Build.LazyPath {
// -------------------------- Others Configuration --------------------------

// zig-cc wrapper for ldc2
pub fn buildZigCC(b: *std.Build, options: *std.Build.Step.Options) *std.Build.Step.Compile {
pub fn buildZigCC(b: *Build, options: *Build.Step.Options) *CompileStep {
const zigcc = b.addWriteFiles();

const exe = b.addExecutable(.{
Expand All @@ -780,7 +838,7 @@ pub fn buildZigCC(b: *std.Build, options: *std.Build.Step.Options) *std.Build.St
return exe;
}

pub fn buildOptions(b: *std.Build, target: std.Build.ResolvedTarget) !*std.Build.Step.Options {
pub fn buildOptions(b: *Build, target: Build.ResolvedTarget) !*Build.Step.Options {
const zigcc_options = b.addOptions();

// Native target, zig can read 'zig libc' contents and also link system libraries.
Expand Down Expand Up @@ -1155,28 +1213,19 @@ const libImGuiOptions = struct {
optimize: std.builtin.OptimizeMode,
use_ubsan: bool = false,
use_tsan: bool = false,
version: []const u8,
};
const imguiVersion = enum {
default,
docking,
};
fn buildImgui(b: *Build, options: libImGuiOptions) !*CompileStep {

// get the imgui source code
const imguiver_path = switch (b.option(
imguiVersion,
"imgui-version",
"Select ImGui version to use",
) orelse imguiVersion.default) {
.default => "src",
.docking => "src-docking",
};

const libimgui = b.addStaticLibrary(.{
.name = "imgui",
.target = options.target,
.optimize = options.optimize,
});

libimgui.root_module.sanitize_c = options.use_ubsan;
libimgui.root_module.sanitize_thread = options.use_tsan;

Expand All @@ -1194,7 +1243,7 @@ fn buildImgui(b: *Build, options: libImGuiOptions) !*CompileStep {
});

if (b.lazyDependency("imgui", .{})) |dep| {
const imgui = dep.path(imguiver_path);
const imgui = dep.path(options.version);
libimgui.addIncludePath(imgui);

libimgui.addCSourceFiles(.{
Expand Down
14 changes: 10 additions & 4 deletions dub.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -329,27 +329,33 @@ subPackage {
targetType "executable"
targetPath "build"
sourceFiles "src/examples/imgui.d"
libs "sokol"
libs "sokol" "imgui"
dflags "-preview=all" "-i=sokol" "-i=shaders" "-i=handmade" "-i=imgui"
libs "X11" "Xcursor" "Xi" "GL" "asound" platform="linux"
lflags "-w" "-lObjC" "-all_load" "-framework" "Cocoa" "-framework" "QuartzCore" "-framework" "Foundation" "-framework" "MetalKit" "-framework" "Metal" "-framework" "AudioToolbox" platform="osx"
libs "user32" "gdi32" "kernel32" "dxgi" "d3d11" "ole32" platform="windows"
lflags "-Lzig-out/lib" platform="posix"
lflags "/LIBPATH:zig-out/lib" platform="windows"
dflags "-P-I$HOME/.cache/zig/p/1220c640ad23d8800437166865e54f172c612fcfe6e000149a2b2af631c37b605f50/src" platform="posix"
dflags "-P-I$APPDATA\\Local\\zig\\p\\1220c640ad23d8800437166865e54f172c612fcfe6e000149a2b2af631c37b605f50\\src" platform="windows"
excludedSourceFiles "src/examples/sgl_context.d" "src/examples/bufferoffsets.d" "src/examples/instancing.d" "src/examples/offscreen.d" "src/examples/clear.d" "src/examples/saudio.d" "src/examples/vertexpull.d" "src/examples/quad.d" "src/examples/noninterleaved.d" "src/examples/debugtext.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/cube.d" "src/examples/mrt.d" "src/examples/blend.d" "src/examples/user_data.d" "src/examples/texcube.d" "src/examples/droptest.d" "src/examples/shapes.d" "src/examples/shaders/*.d"
preBuildCommands "zig build -Dshared -Doptimize=ReleaseFast -Dimgui -Dartifact"
preBuildCommands "zig build -Doptimize=ReleaseFast -Dimgui -Dartifact"
}

subPackage {
name "droptest"
targetType "executable"
targetPath "build"
sourceFiles "src/examples/droptest.d"
libs "sokol"
libs "sokol" "imgui"
dflags "-preview=all" "-i=sokol" "-i=shaders" "-i=handmade" "-i=imgui"
libs "X11" "Xcursor" "Xi" "GL" "asound" platform="linux"
lflags "-w" "-lObjC" "-all_load" "-framework" "Cocoa" "-framework" "QuartzCore" "-framework" "Foundation" "-framework" "MetalKit" "-framework" "Metal" "-framework" "AudioToolbox" platform="osx"
libs "user32" "gdi32" "kernel32" "dxgi" "d3d11" "ole32" platform="windows"
lflags "-Lzig-out/lib" platform="posix"
lflags "/LIBPATH:zig-out/lib" platform="windows"
dflags "-P-I$HOME/.cache/zig/p/1220c640ad23d8800437166865e54f172c612fcfe6e000149a2b2af631c37b605f50/src" platform="posix"
dflags "-P-I$APPDATA\\Local\\zig\\p\\1220c640ad23d8800437166865e54f172c612fcfe6e000149a2b2af631c37b605f50\\src" platform="windows"
excludedSourceFiles "src/examples/sgl_context.d" "src/examples/bufferoffsets.d" "src/examples/instancing.d" "src/examples/offscreen.d" "src/examples/clear.d" "src/examples/saudio.d" "src/examples/vertexpull.d" "src/examples/quad.d" "src/examples/noninterleaved.d" "src/examples/debugtext.d" "src/examples/triangle.d" "src/examples/sgl_points.d" "src/examples/cube.d" "src/examples/mrt.d" "src/examples/blend.d" "src/examples/user_data.d" "src/examples/texcube.d" "src/examples/imgui.d" "src/examples/shapes.d" "src/examples/shaders/*.d"
preBuildCommands "zig build -Dshared -Doptimize=ReleaseFast -Dimgui -Dartifact"
preBuildCommands "zig build -Doptimize=ReleaseFast -Dimgui -Dartifact"
}

0 comments on commit b2a1620

Please sign in to comment.