Skip to content

Commit

Permalink
emscripten support
Browse files Browse the repository at this point in the history
  • Loading branch information
kassane committed Jan 7, 2024
1 parent a5ad8f4 commit 71335db
Showing 1 changed file with 62 additions and 53 deletions.
115 changes: 62 additions & 53 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ const std = @import("std");
const builtin = @import("builtin");
const Build = std.Build;
const CompileStep = Build.Step.Compile;
const RunStep = Build.Step.Run;
const Module = Build.Module;
const CrossTarget = std.zig.CrossTarget;
const CrossTarget = Build.ResolvedTarget;
const OptimizeMode = std.builtin.OptimizeMode;

pub const Backend = enum {
Expand All @@ -26,11 +27,10 @@ pub const LibSokolOptions = struct {
sysroot: ?[]const u8 = null,
package: ?*Build.Dependency = null,

fn packagePath(self: LibSokolOptions, b: *Build) []const u8 {
fn packagePath(self: LibSokolOptions, b: *Build) ?[]const u8 {
if (self.package) |dep|
return dep.path("").getPath(b)
else
return "";
return dep.path("").getPath(b);
return null;
}
};

Expand All @@ -39,11 +39,12 @@ pub fn build(b: *Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

const mod_sokol = b.addModule("sokol", .{ .source_file = .{ .path = "src/sokol/sokol.zig" } });
const mod_sokol = b.addModule("sokol", .{ .root_source_file = .{ .path = "src/sokol/sokol.zig" } });

const lib_sokol = buildLibSokol(b, .{
.target = target,
.optimize = optimize,
.package = b.dependency("emsdk", .{}),
.backend = if (force_gl) .gl else .auto,
.enable_wayland = b.option(bool, "wayland", "Compile with wayland-support (default: false)") orelse false,
.enable_x11 = b.option(bool, "x11", "Compile with x11-support (default: true)") orelse true,
Expand Down Expand Up @@ -96,12 +97,13 @@ const ExampleOptions = struct {

// build sokol into a static library
pub fn buildLibSokol(b: *Build, options: LibSokolOptions) !*CompileStep {
const is_wasm = options.target.getCpu().arch == .wasm32;
const is_wasm = options.target.result.isWasm();
var config = options;

// special case wasm, must compile as wasm32-emscripten, not wasm32-freestanding
var target = options.target;
if (is_wasm) {
target.os_tag = .emscripten;
target.result.os.tag = .emscripten;
}

const lib = b.addStaticLibrary(.{
Expand All @@ -111,19 +113,34 @@ pub fn buildLibSokol(b: *Build, options: LibSokolOptions) !*CompileStep {
.link_libc = true,
});
if (is_wasm) {
// need to add Emscripten SDK include path
if (b.sysroot == null) {
// need to add Emscripten SDK include path (system or package)
if (b.sysroot) |sysroot| {
config.sysroot = sysroot;
} else if (options.packagePath(b)) |path| {
config.sysroot = b.pathJoin(&.{ path, "upstream", "emscripten", "cache", "sysroot" });
const emsdk_exe = if (builtin.os.tag == .windows)
b.pathJoin(&.{ options.packagePath(b).?, "emsdk.ps1" })
else
b.pathJoin(&.{ options.packagePath(b).?, "emsdk" });

const emsdk_run = b.addSystemCommand(&.{ if (builtin.os.tag != .windows) "bash", emsdk_exe, "install", "latest" });
const emsdk_active = b.addSystemCommand(&.{ if (builtin.os.tag != .windows) "bash", emsdk_exe, "activate", "latest" });
emsdk_active.step.dependOn(&emsdk_run.step);
lib.step.dependOn(&emsdk_active.step);
} else {
std.log.err("Must provide Emscripten sysroot via '--sysroot [path/to/emsdk]/upstream/emscripten/cache/sysroot'", .{});
return error.Wasm32SysRootExpected;
}
const include_path = try std.fs.path.join(b.allocator, &.{ b.sysroot.?, "include" });
defer b.allocator.free(include_path);
lib.addIncludePath(.{ .path = include_path });
const include_path = b.pathJoin(&.{ config.sysroot.?, "include" });
lib.addSystemIncludePath(.{ .path = include_path }); // isystem
// TODO: zig get emscripten libs (wgu, libGL-webgl2,...)
// const lib_path = b.pathJoin(&.{ config.sysroot.?, "lib", "wasm32-emscripten" });
// lib.addLibraryPath(.{ .path = lib_path });
lib.defineCMacro("__EMSCRIPTEN__", "1");
}
var sokol_path: []const u8 = "src/sokol/c";
if (options.build_root) |build_root| {
sokol_path = try std.fmt.allocPrint(b.allocator, "{s}/src/sokol/c", .{build_root});
sokol_path = b.fmt("{s}/src/sokol/c", .{build_root});
}
const csources = [_][]const u8{
"sokol_log.c",
Expand All @@ -141,12 +158,10 @@ pub fn buildLibSokol(b: *Build, options: LibSokolOptions) !*CompileStep {
_backend = .metal;
} else if (lib.rootModuleTarget().os.tag == .windows) {
_backend = .d3d11;
} else if (lib.target.getCpu().arch == .wasm32) {
} else if (lib.rootModuleTarget().isWasm()) {
_backend = .gles3;
} else if (lib.target.getAbi() == .android) {
} else if (lib.rootModuleTarget().abi == .android) {
_backend = .gles3;
// } else if (lib.rootModuleTarget()_info.target.isWasm() and lib.rootModuleTarget().query.os_tag == .emscripten) {
// _backend = .wgpu;
} else {
_backend = .gl;
}
Expand All @@ -160,10 +175,10 @@ pub fn buildLibSokol(b: *Build, options: LibSokolOptions) !*CompileStep {
else => unreachable,
};

if (lib.target.isDarwin()) {
if (lib.rootModuleTarget().isDarwin()) {
for (csources) |csrc| {
lib.addCSourceFile(.{
.file = .{ .path = try std.fmt.allocPrint(b.allocator, "{s}/{s}", .{ sokol_path, csrc }) },
.file = .{ .path = b.fmt("{s}/{s}", .{ sokol_path, csrc }) },
.flags = &[_][]const u8{ "-ObjC", "-DIMPL", backend_option },
});
}
Expand All @@ -187,28 +202,28 @@ pub fn buildLibSokol(b: *Build, options: LibSokolOptions) !*CompileStep {
lib.linkFramework("OpenGL");
}
}
} else if (lib.target.getAbi() == .android) {
} else if (lib.rootModuleTarget().abi == .android) {
if (.gles3 != _backend) {
@panic("For android targets, you must have backend set to GLES3");
}
for (csources) |csrc| {
lib.addCSourceFile(.{
.file = .{ .path = try std.fmt.allocPrint(b.allocator, "{s}/{s}", .{ sokol_path, csrc }) },
.file = .{ .path = b.fmt("{s}/{s}", .{ sokol_path, csrc }) },
.flags = &[_][]const u8{ "-DIMPL", backend_option },
});
}
lib.linkSystemLibrary("GLESv3");
lib.linkSystemLibrary("EGL");
lib.linkSystemLibrary("android");
lib.linkSystemLibrary("log");
} else if (lib.target.isLinux()) {
} else if (lib.rootModuleTarget().os.tag == .linux) {
const egl_flag = if (options.force_egl) "-DSOKOL_FORCE_EGL " else "";
const x11_flag = if (!options.enable_x11) "-DSOKOL_DISABLE_X11 " else "";
const wayland_flag = if (!options.enable_wayland) "-DSOKOL_DISABLE_WAYLAND" else "";
const link_egl = options.force_egl or options.enable_wayland;
for (csources) |csrc| {
lib.addCSourceFile(.{
.file = .{ .path = try std.fmt.allocPrint(b.allocator, "{s}/{s}", .{ sokol_path, csrc }) },
.file = .{ .path = b.fmt("{s}/{s}", .{ sokol_path, csrc }) },
.flags = &[_][]const u8{ "-DIMPL", backend_option, egl_flag, x11_flag, wayland_flag },
});
}
Expand All @@ -228,25 +243,25 @@ pub fn buildLibSokol(b: *Build, options: LibSokolOptions) !*CompileStep {
if (link_egl) {
lib.linkSystemLibrary("egl");
}
} else if (lib.target.isWindows()) {
} else if (lib.rootModuleTarget().os.tag == .windows) {
for (csources) |csrc| {
lib.addCSourceFile(.{
.file = .{ .path = try std.fmt.allocPrint(b.allocator, "{s}/{s}", .{ sokol_path, csrc }) },
.file = .{ .path = b.fmt("{s}/{s}", .{ sokol_path, csrc }) },
.flags = &[_][]const u8{ "-DIMPL", backend_option },
});
}
lib.linkSystemLibraryName("kernel32");
lib.linkSystemLibraryName("user32");
lib.linkSystemLibraryName("gdi32");
lib.linkSystemLibraryName("ole32");
lib.linkSystemLibrary("kernel32");
lib.linkSystemLibrary("user32");
lib.linkSystemLibrary("gdi32");
lib.linkSystemLibrary("ole32");
if (.d3d11 == _backend) {
lib.linkSystemLibraryName("d3d11");
lib.linkSystemLibraryName("dxgi");
lib.linkSystemLibrary("d3d11");
lib.linkSystemLibrary("dxgi");
}
} else {
for (csources) |csrc| {
lib.addCSourceFile(.{
.file = .{ .path = try std.fmt.allocPrint(b.allocator, "{s}/{s}", .{ sokol_path, csrc }) },
.file = .{ .path = b.fmt("{s}/{s}", .{ sokol_path, csrc }) },
.flags = &[_][]const u8{ "-DIMPL", backend_option },
});
}
Expand All @@ -262,17 +277,16 @@ fn buildExample(b: *Build, comptime name: []const u8, options: ExampleOptions) v
.target = options.target,
.optimize = options.optimize,
});
if (e.rootModuleTarget().isWasm()) {
e.entry = .disabled;
e.pie = true;
}

e.linkLibrary(options.lib_sokol);
e.addModule("sokol", options.mod_sokol);
e.root_module.addImport("sokol", options.mod_sokol);
b.installArtifact(e);
var run: ?*Build.Step.Run = null;
if (e.rootModuleTarget().os.tag == .emscripten) {
run = try buildWasm(b, e, options.lib_sokol, .{});
} else {
run = b.addRunArtifact(e);
}
if (run) |r|
b.step("run-" ++ name, "Run " ++ name).dependOn(&r.step);
const run = b.addRunArtifact(e);
b.step("run-" ++ name, "Run " ++ name).dependOn(&run.step);
}

// a separate step to compile shaders, expects the shader compiler in ../sokol-tools-bin/
Expand Down Expand Up @@ -319,10 +333,10 @@ fn buildShaders(b: *Build) void {
}
}

// based on pacman.zig
fn buildWasm(b: *Build, lib: *CompileStep, libsokol: *CompileStep, config: LibSokolOptions) !*Build.Step.Run {
const emcc_path = b.pathJoin(&.{ emsdkPath(b), "upstream", "emscripten", "emcc" }); //try b.findProgram(&.{"emcc"}, &.{emsdkPath(b)});
const emrun_path = b.pathJoin(&.{ emsdkPath(b), "upstream", "emscripten", "emrun" }); //try b.findProgram(&.{"emrun"}, &.{emsdkPath(b)});
fn buildWasm(b: *Build, lib: *CompileStep, libsokol: *CompileStep, options: LibSokolOptions) !*RunStep {
// system path or package path
const emcc_path = b.findProgram(&.{"emcc"}, &.{}) catch b.pathJoin(&.{ options.packagePath(b), "upstream", "emscripten", "emcc" });
const emrun_path = b.findProgram(&.{"emrun"}, &.{}) catch b.pathJoin(&.{ options.packagePath(b), "upstream", "emscripten", "emrun" });

if (lib.rootModuleTarget().os.tag != .emscripten) {
std.log.err("Please build with 'zig build -Dtarget=wasm32-emscripten", .{});
Expand All @@ -340,7 +354,7 @@ fn buildWasm(b: *Build, lib: *CompileStep, libsokol: *CompileStep, config: LibSo
defer emcc_cmds.deinit();

try emcc_cmds.append(emcc_path);
if (config.optimize != .Debug)
if (options.optimize != .Debug)
try emcc_cmds.append("-Oz")
else
try emcc_cmds.append("-Og");
Expand All @@ -356,7 +370,7 @@ fn buildWasm(b: *Build, lib: *CompileStep, libsokol: *CompileStep, config: LibSo
try emcc_cmds.append("-sERROR_ON_UNDEFINED_SYMBOLS=0");

// TODO: fix undefined references
switch (config.backend) {
switch (options.backend) {
.wgpu => {
try emcc_cmds.append("-sUSE_WEBGPU=1");
},
Expand All @@ -380,8 +394,3 @@ fn buildWasm(b: *Build, lib: *CompileStep, libsokol: *CompileStep, config: LibSo
emrun.step.dependOn(&emcc.step);
return emrun;
}

fn emsdkPath(b: *Build) []const u8 {
const emsdk_dep = b.dependency("emsdk", .{});
return emsdk_dep.path("").getPath(b);
}

0 comments on commit 71335db

Please sign in to comment.