Skip to content

Commit

Permalink
Merge branch 'main' into xpack-emscripten-support
Browse files Browse the repository at this point in the history
  • Loading branch information
kassane authored Jan 9, 2025
2 parents 71ca80d + b52ef71 commit aa12ef9
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 108 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ jobs:
dub build :droptest
- name: (Zig) Build Shaders
run: zig build shaders
run: zig build -Dshaders
- name: (Zig) Running Test
if: runner.os != 'Windows'
run: zig build test -DzigCC
Expand Down
16 changes: 7 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,20 +95,18 @@ Checkout [sokol-tools](https://github.com/floooh/sokol-tools) for a sokol shader
here have been compiled using it with `-f sokol_d`!

```bash
zig build shaders # (re)generate D bindings from shaders.
zig build -Dshaders # (re)generate D bindings from shaders.
```

<br>

## License and attributions

<sub>
This code is released under the zlib license (see `LICENSE` for info). Parts of `gen_d.py` have been copied and modified from
the zig-bindings (https://github.com/floooh/sokol-zig/) and rust-bindings (https://github.com/floooh/sokol-rust/) for sokol.
</sub>
This code is released under the zlib license (see [LICENSE](LICENSE) for info). Parts of `gen_d.py` have been copied and modified from
the zig-bindings[^1] and rust-bindings[^2] for sokol.

The sokol headers are created by Andre Weissflog (floooh) and sokol is released under its own license[^3].

<sub>
The sokol headers are created by Andre Weissflog (floooh) and sokol is released under its own license here: https://github.com/floooh/sokol/blob/master/LICENSE
</sub>
</br>
[^1]: https://github.com/floooh/sokol-zig/
[^2]: https://github.com/floooh/sokol-rust/
[^3]: https://github.com/floooh/sokol/blob/master/LICENSE
226 changes: 128 additions & 98 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,13 @@ pub fn buildLibSokol(b: *Build, options: LibSokolOptions) !*CompileStep {
}
// one-time setup of Emscripten SDK
if (!options.with_sokol_imgui) {
if (try emSdkSetupStep(b, options.emsdk.?)) |emsdk_setup| {
lib.step.dependOn(&emsdk_setup.step);
if (options.emsdk) |emsdk| {
if (try emSdkSetupStep(b, emsdk)) |emsdk_setup| {
lib.step.dependOn(&emsdk_setup.step);
}
// add the Emscripten system include seach path
lib.addIncludePath(emSdkLazyPath(b, emsdk, &.{ "upstream", "emscripten", "cache", "sysroot", "include" }));
}
// add the Emscripten system include seach path
lib.addIncludePath(emSdkLazyPath(b, options.emsdk.?, &.{ "upstream", "emscripten", "cache", "sysroot", "include" }));
}
}

Expand Down Expand Up @@ -217,13 +219,16 @@ pub fn build(b: *Build) !void {

// LDC-options options
const dub_artifact = b.option(bool, "artifact", "Build artifacts (default: false)") orelse false;
const enable_betterC = b.option(bool, "betterC", "Omit generating some runtime information and helper functions (default: false)") orelse false;
const enable_zigcc = b.option(bool, "zigCC", "Use zig cc as compiler and linker (default: false)") orelse false;
const opt_betterC = b.option(bool, "betterC", "Omit generating some runtime information and helper functions (default: false)") orelse false;
const opt_zigcc = b.option(bool, "zigCC", "Use zig cc as compiler and linker (default: false)") orelse false;
// Build Shaders
const opt_shaders = b.option(bool, "shaders", "Build shaders (default: false)") orelse false;
// ldc2 w/ druntime + phobos2 works on MSVC
const target = b.standardTargetOptions(.{ .default_target = if (builtin.os.tag == .windows) try std.Target.Query.parse(.{ .arch_os_abi = "native-windows-msvc" }) else .{} });
const optimize = b.standardOptimizeOption(.{});

const emsdk = b.lazyDependency("emsdk", .{}) orelse null;
// Get emsdk dependency if targeting WebAssembly, otherwise null
const emsdk = enableWasm(b, target);
const lib_sokol = try buildLibSokol(b, .{
.target = target,
.optimize = optimize,
Expand All @@ -234,6 +239,8 @@ pub fn build(b: *Build) !void {
.with_sokol_imgui = opt_with_sokol_imgui,
.emsdk = emsdk,
});
if (opt_shaders)
buildShaders(b, target);
if (dub_artifact) {
b.installArtifact(lib_sokol);
} else {
Expand Down Expand Up @@ -271,14 +278,14 @@ pub fn build(b: *Build) !void {
.sources = &[_][]const u8{
b.fmt("{s}/src/examples/{s}.d", .{ rootPath(), example }),
},
.betterC = if (std.mem.eql(u8, example, "user-data")) false else enable_betterC,
.betterC = if (std.mem.eql(u8, example, "user-data")) false else opt_betterC,
.dflags = &.{
"-w",
"-preview=rvaluerefparam",
"-preview=dip1000",
},
// fixme: https://github.com/kassane/sokol-d/issues/1 - betterC works on darwin
.zig_cc = if (target.result.isDarwin() and !enable_betterC) false else enable_zigcc,
.zig_cc = if (target.result.isDarwin() and !opt_betterC) false else opt_zigcc,
.target = target,
.optimize = optimize,
// send ldc2-obj (wasm artifact) to emcc
Expand All @@ -289,7 +296,6 @@ pub fn build(b: *Build) !void {
b.getInstallStep().dependOn(&ldc.step);
}
}
buildShaders(b, target);

// build tests
// fixme: not building on Windows libsokol w/ kind test (missing cc [??])
Expand Down Expand Up @@ -652,7 +658,7 @@ pub fn ldcBuildStep(b: *Build, options: DCompileStep) !*std.Build.Step.InstallDi
.lib_main = artifact,
.target = options.target,
.optimize = options.optimize,
.emsdk = options.emsdk.?,
.emsdk = options.emsdk orelse null,
.use_webgpu = backend == .wgpu,
.use_webgl2 = backend != .wgpu,
.use_emmalloc = options.betterC,
Expand All @@ -664,7 +670,7 @@ pub fn ldcBuildStep(b: *Build, options: DCompileStep) !*std.Build.Step.InstallDi
.extra_args = &.{"-sSTACK_SIZE=512KB"},
});
link_step.step.dependOn(&ldc_exec.step);
const emrun = emRunStep(b, .{ .name = options.name, .emsdk = options.emsdk.? });
const emrun = emRunStep(b, .{ .name = options.name, .emsdk = options.emsdk orelse null });
emrun.step.dependOn(&link_step.step);
run.dependOn(&emrun.step);
} else {
Expand Down Expand Up @@ -775,7 +781,6 @@ fn buildShaders(b: *Build, target: Build.ResolvedTarget) void {
return;
}
const shdc_path = b.findProgram(&.{"sokol-shdc"}, &.{}) catch b.pathJoin(&.{ sokol_tools_bin_dir, optional_shdc.? });
const shdc_step = b.step("shaders", "Compile shaders (needs ../sokol-tools-bin)");
const glsl = if (target.result.isDarwin()) "glsl410" else "glsl430";
const slang = glsl ++ ":metal_macos:hlsl5:glsl300es:wgsl";
if (builtin.os.tag == .linux or builtin.os.tag == .macos) {
Expand All @@ -799,20 +804,27 @@ fn buildShaders(b: *Build, target: Build.ResolvedTarget) void {
"-f",
"sokol_d",
});
shdc_step.dependOn(&cmd.step);
b.default_step.dependOn(&cmd.step);
}
}
}

// ------------------------ Wasm Configuration ------------------------

// Enable fetch and install the Emscripten SDK
fn enableWasm(b: *Build, target: Build.ResolvedTarget) ?*Build.Dependency {
if (target.result.isWasm())
return b.lazyDependency("emsdk", .{}) orelse null;
return null;
}

// for wasm32-emscripten, need to run the Emscripten linker from the Emscripten SDK
// NOTE: ideally this would go into a separate emsdk-zig package
pub const EmLinkOptions = struct {
target: Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
lib_main: *Build.Step.Compile,
emsdk: *Build.Dependency,
emsdk: ?*Build.Dependency,
release_use_closure: bool = true,
release_use_lto: bool = false,
use_webgpu: bool = false,
Expand All @@ -826,84 +838,89 @@ pub const EmLinkOptions = struct {
};

pub fn emLinkStep(b: *Build, options: EmLinkOptions) !*Build.Step.InstallDir {
const emcc_path = emSdkLazyPath(b, options.emsdk, &.{ "upstream", "emscripten", "emcc" }).getPath(b);
const emcc = b.addSystemCommand(&.{emcc_path});
emcc.setName("emcc"); // hide emcc path
if (options.optimize == .Debug) {
emcc.addArgs(&.{
"-Og",
"-sSAFE_HEAP=1",
"-sSTACK_OVERFLOW_CHECK=1",
});
} else {
emcc.addArg("-sASSERTIONS=0");
if (options.optimize == .ReleaseSmall) {
emcc.addArg("-Oz");
if (options.emsdk) |emsdk| {
const emcc_path = emSdkLazyPath(b, emsdk, &.{ "upstream", "emscripten", "emcc" }).getPath(b);
const emcc = b.addSystemCommand(&.{emcc_path});
emcc.setName("emcc"); // hide emcc path
if (options.optimize == .Debug) {
emcc.addArgs(&.{
"-Og",
"-sSAFE_HEAP=1",
"-sSTACK_OVERFLOW_CHECK=1",
});
} else {
emcc.addArg("-O3");
emcc.addArg("-sASSERTIONS=0");
if (options.optimize == .ReleaseSmall) {
emcc.addArg("-Oz");
} else {
emcc.addArg("-O3");
}
if (options.release_use_lto) {
emcc.addArg("-flto");
}
if (options.release_use_closure) {
emcc.addArgs(&.{ "--closure", "1" });
}
}
if (options.release_use_lto) {
emcc.addArg("-flto");
if (options.use_webgpu) {
emcc.addArg("-sUSE_WEBGPU=1");
}
if (options.release_use_closure) {
emcc.addArgs(&.{ "--closure", "1" });
if (options.use_webgl2) {
emcc.addArg("-sUSE_WEBGL2=1");
}
if (!options.use_filesystem) {
emcc.addArg("-sNO_FILESYSTEM=1");
}
if (options.use_emmalloc) {
emcc.addArg("-sMALLOC='emmalloc'");
}
if (options.use_ubsan) {
emcc.addArg("-fsanitize=undefined");
}
if (options.shell_file_path) |shell_file_path| {
emcc.addPrefixedFileArg("--shell-file=", shell_file_path);
}
for (options.extra_args) |arg| {
emcc.addArg(arg);
}
}
if (options.use_webgpu) {
emcc.addArg("-sUSE_WEBGPU=1");
}
if (options.use_webgl2) {
emcc.addArg("-sUSE_WEBGL2=1");
}
if (!options.use_filesystem) {
emcc.addArg("-sNO_FILESYSTEM=1");
}
if (options.use_emmalloc) {
emcc.addArg("-sMALLOC='emmalloc'");
}
if (options.use_ubsan) {
emcc.addArg("-fsanitize=undefined");
}
if (options.shell_file_path) |shell_file_path| {
emcc.addPrefixedFileArg("--shell-file=", shell_file_path);
}
for (options.extra_args) |arg| {
emcc.addArg(arg);
}

if (options.use_drt) {
const libdruntime = fetch(b, .{
.url = "https://github.com/opendlang/opend/releases/download/CI/opend-latest-xpack-emscripten.tar.xz",
.file_name = "lib/libdruntime-ldc.a",
});
const libphobos2 = fetch(b, .{
.url = "https://github.com/opendlang/opend/releases/download/CI/opend-latest-xpack-emscripten.tar.xz",
.file_name = "lib/libphobos2-ldc.a",
});
emcc.addFileArg(libdruntime);
emcc.addFileArg(libphobos2);
}
// add the main lib, and then scan for library dependencies and add those too
emcc.addArtifactArg(options.lib_main);
for (options.lib_main.getCompileDependencies(false)) |item| {
if (item.kind == .lib) {
emcc.addArtifactArg(item);
if (options.use_drt) {
const libdruntime = fetch(b, .{
.url = "https://github.com/opendlang/opend/releases/download/CI/opend-latest-xpack-emscripten.tar.xz",
.file_name = "lib/libdruntime-ldc.a",
});
const libphobos2 = fetch(b, .{
.url = "https://github.com/opendlang/opend/releases/download/CI/opend-latest-xpack-emscripten.tar.xz",
.file_name = "lib/libphobos2-ldc.a",
});
emcc.addFileArg(libdruntime);
emcc.addFileArg(libphobos2);
}
}
emcc.addArg("-o");
const out_file = emcc.addOutputFileArg(b.fmt("{s}.html", .{options.lib_main.name}));
// add the main lib, and then scan for library dependencies and add those too
emcc.addArtifactArg(options.lib_main);
for (options.lib_main.getCompileDependencies(false)) |item| {
if (item.kind == .lib) {
emcc.addArtifactArg(item);
}
}
emcc.addArg("-o");
const out_file = emcc.addOutputFileArg(b.fmt("{s}.html", .{options.lib_main.name}));
// the emcc linker creates 3 output files (.html, .wasm and .js)
const install = b.addInstallDirectory(.{
.source_dir = out_file.dirname(),
.install_dir = .prefix,
.install_subdir = "web",
});
install.step.dependOn(&emcc.step);

// the emcc linker creates 3 output files (.html, .wasm and .js)
const install = b.addInstallDirectory(.{
.source_dir = out_file.dirname(),
// get the emcc step to run on 'zig build'
b.getInstallStep().dependOn(&install.step);
return install;
} else return b.addInstallDirectory(.{
.source_dir = b.path(""),
.install_dir = .prefix,
.install_subdir = "web",
});
install.step.dependOn(&emcc.step);

// get the emcc step to run on 'zig build'
b.getInstallStep().dependOn(&install.step);
return install;
}

// Use 'zig fetch' to download and unpack the specified URL, optionally verifying the checksum.
Expand Down Expand Up @@ -963,12 +980,23 @@ fn fetch(b: *std.Build, options: struct {
// NOTE: ideally this would go into a separate emsdk-zig package
pub const EmRunOptions = struct {
name: []const u8,
emsdk: *Build.Dependency,
emsdk: ?*Build.Dependency,
};
pub fn emRunStep(b: *Build, options: EmRunOptions) *Build.Step.Run {
const emrun_path = b.findProgram(&.{"emrun"}, &.{}) catch emSdkLazyPath(b, options.emsdk, &.{ "upstream", "emscripten", "emrun" }).getPath(b);
const emrun = b.addSystemCommand(&.{ emrun_path, b.fmt("{s}/web/{s}.html", .{ b.install_path, options.name }) });
return emrun;
if (options.emsdk) |emsdk| {
const emrun_path = b.findProgram(&.{"emrun"}, &.{}) catch emSdkLazyPath(b, emsdk, &.{ "upstream", "emscripten", "emrun" }).getPath(b);
const emrun = b.addSystemCommand(&.{ emrun_path, b.fmt("{s}/web/{s}.html", .{ b.install_path, options.name }) });
return emrun;
}
// workaround for emsdk not being available (non-artifact build)
return b.addRunArtifact(Build.Step.Compile.create(b, .{
.name = options.name,
.root_module = b.createModule(.{
.target = b.graph.host,
.optimize = .Debug,
}),
.kind = .obj,
}));
}

// helper function to build a LazyPath from the emsdk root and provided path components
Expand Down Expand Up @@ -1043,18 +1071,20 @@ fn buildImgui(b: *Build, options: libImGuiOptions) !*CompileStep {
const cimgui = dep.path(imguiver_path);
libimgui.addIncludePath(cimgui);

if (libimgui.rootModuleTarget().isWasm()) {
if (try emSdkSetupStep(b, options.emsdk.?)) |emsdk_setup| {
libimgui.step.dependOn(&emsdk_setup.step);
if (options.emsdk) |emsdk| {
if (libimgui.rootModuleTarget().isWasm()) {
if (try emSdkSetupStep(b, emsdk)) |emsdk_setup| {
libimgui.step.dependOn(&emsdk_setup.step);
}
// add the Emscripten system include seach path
libimgui.addIncludePath(emSdkLazyPath(b, emsdk, &.{
"upstream",
"emscripten",
"cache",
"sysroot",
"include",
}));
}
// add the Emscripten system include seach path
libimgui.addIncludePath(emSdkLazyPath(b, options.emsdk.?, &.{
"upstream",
"emscripten",
"cache",
"sysroot",
"include",
}));
}
libimgui.addCSourceFiles(.{
.root = cimgui,
Expand Down

0 comments on commit aa12ef9

Please sign in to comment.