Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,16 @@ jobs:
run: |
SDK="$(xcrun --sdk iphoneos --show-sdk-path)"
zig build -Dtarget=aarch64-ios -Dinclude_path="$SDK/usr/include" -Dframework_path="$SDK/System/Library/Frameworks" --summary all

- name: Setup Emscripten
if: runner.os == 'Linux'
uses: mymindstorm/setup-emsdk@v14
with:
version: 6.0.12

- name: Build for Emscripten
if: runner.os == 'Linux'
# The Emscripten sysroot is not bundled with Zig, and emsdk only populates it on first use.
run: |
embuilder build sysroot
zig build example -Dtarget=wasm32-emscripten -Dinclude_path="$(em-config CACHE)/sysroot/include" --summary all
17 changes: 16 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,18 @@ zig build run-example

This should produce a window with a pulsing gradient.

For Emscripten, the example is linked with `emcc` into `zig-out/web`, and `run-example` opens it in
your browser with `emrun`. Both need to be on your `PATH`, e.g. by sourcing `emsdk_env.sh`. On a
fresh emsdk install, the sysroot headers must be generated before the first build:

```sh
embuilder build sysroot
zig build run-example -Dtarget=wasm32-emscripten -Dinclude_path="$(em-config CACHE)/sysroot/include"
```

Zig can't link Emscripten executables, so your own app will need to be linked with `emcc` too. See
`build.zig` for an example of how to do this.

## Help, `SDL_Init` failed on Linux!

```sh
Expand Down Expand Up @@ -114,7 +126,10 @@ This library provides a default configuration for common targets:
`-Dinclude_path=$SDK/usr/include -Dframework_path=$SDK/System/Library/Frameworks` with
`SDK=$(xcrun --sdk iphoneos --show-sdk-path)`)
- [x] Android (pass `-Dinclude_path=<ndk>/toolchains/llvm/prebuilt/<host>/sysroot/usr/include`)
- [ ] [Emscripten (help wanted!)](https://github.com/allyourcodebase/SDL/issues/5)
- [x] Emscripten (the Emscripten sysroot is not bundled with Zig; pass
`-Dinclude_path=$(em-config CACHE)/sysroot/include` with
[emsdk](https://emscripten.org/docs/getting_started/downloads.html) activated, static linkage
only)
- [ ] [Consoles (help wanted!)](https://github.com/allyourcodebase/SDL/issues/6)

You can override the default target configuration by setting `default_target_config` to `false`, and
Expand Down
80 changes: 66 additions & 14 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const windows = @import("src/windows.zig");
const macos = @import("src/macos.zig");
const ios = @import("src/ios.zig");
const android = @import("src/android.zig");
const emscripten = @import("src/emscripten.zig");
const build_zon = @import("build.zig.zon");

const assert = std.debug.assert;
Expand Down Expand Up @@ -51,6 +52,12 @@ pub fn build(b: *std.Build) !void {
,
) orelse .static;

// Upstream doesn't support building SDL as a shared library for Emscripten
if (target.result.os.tag == .emscripten and linkage == .dynamic) {
std.log.err("'-Dlinkage=dynamic' is not supported when building SDL for Emscripten", .{});
std.process.exit(1);
}

// Cross-compiling to a target whose SDK isn't bundled with Zig (iOS, Android) needs the
// SDK's headers/frameworks/libs passed in explicitly. Scoped to the SDL library only, unlike
// `--sysroot` this does not re-roots paths across the whole build graph.
Expand Down Expand Up @@ -103,6 +110,15 @@ pub fn build(b: *std.Build) !void {
.flags = flags,
});

// Emscripten provides its own main callbacks implementation
if (target.result.os.tag != .emscripten) {
lib.root_module.addCSourceFiles(.{
.files = &sources.main_callbacks_generic,
.root = upstream.path("src"),
.flags = flags,
});
}

if (default_target_config) {
const build_config_h = b.addConfigHeader(.{
.style = .{ .cmake = upstream.path("include/build_config/SDL_build_config.h.cmake") },
Expand Down Expand Up @@ -136,6 +152,7 @@ pub fn build(b: *std.Build) !void {
.windows => windows.build(b, target.result, lib, build_config_h),
.macos => macos.build(b, target.result, lib, build_config_h),
.ios => ios.build(b, target.result, lib, build_config_h, system_paths),
.emscripten => emscripten.build(b, target.result, lib, build_config_h, system_paths),
else => @panic("target has no default config"),
}
}
Expand All @@ -152,26 +169,61 @@ pub fn build(b: *std.Build) !void {
translate_c.defineCMacro("USING_GENERATED_CONFIG_H", "1");
translate_c.addIncludePath(upstream.path("include"));
translate_c.addIncludePath(upstream.path("src/video/khronos"));
if (target.result.os.tag == .emscripten) {
// The SDL headers include libc headers, which Zig doesn't bundle for Emscripten
if (system_paths.include) |include_path| translate_c.addSystemIncludePath(include_path);
}
const module = translate_c.addModule("sdl3");
module.linkLibrary(lib);

// Add the example
const example = b.addExecutable(.{
.name = "example",
.root_module = b.createModule(.{
.root_source_file = b.path("src/example.zig"),
.target = target,
.optimize = optimize,
}),
});
example.root_module.addImport("sdl3", module);

const build_example_step = b.step("example", "Build the example app");
build_example_step.dependOn(&example.step);

const run_example = b.addRunArtifact(example);
const run_step = b.step("run-example", "Run the example app");
run_step.dependOn(&run_example.step);
if (target.result.os.tag == .emscripten) {
// Zig can't link Emscripten executables, so build the example as a static library and
// link it with `emcc`, which also generates the JavaScript and HTML needed to run it.
const example = b.addLibrary(.{
.name = "example",
.root_module = b.createModule(.{
.root_source_file = b.path("src/example.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
}),
});
example.root_module.addImport("sdl3", module);

const emcc = b.addSystemCommand(&.{ "emcc", "-sALLOW_MEMORY_GROWTH" });
emcc.addArtifactArg(example);
emcc.addArtifactArg(lib);
const html = emcc.addPrefixedOutputFileArg("-o", "example.html");

// `emcc` writes the `.js` and `.wasm` files next to the `.html` file
const install_example = b.addInstallDirectory(.{
.source_dir = html.dirname(),
.install_dir = .prefix,
.install_subdir = "web",
});
build_example_step.dependOn(&install_example.step);

const run_example = b.addSystemCommand(&.{"emrun"});
run_example.addFileArg(html);
run_step.dependOn(&run_example.step);
} else {
const example = b.addExecutable(.{
.name = "example",
.root_module = b.createModule(.{
.root_source_file = b.path("src/example.zig"),
.target = target,
.optimize = optimize,
}),
});
example.root_module.addImport("sdl3", module);
build_example_step.dependOn(&example.step);

const run_example = b.addRunArtifact(example);
run_step.dependOn(&run_example.step);
}
}

/// Fetches a lazy dependency, unless provided via `-fsys`.
Expand Down
Loading