SDL3 ported to the Zig build system (with example games)

2024 is drawing its last breath and just in time for the new year, I’ve completed my port of SDL3 to the Zig build system. SDL3 is still in preview (current version: 3.1.6), but its ABI is stable as of 3.1.3 and it has been used in production by Valve for some time now.

The goal for this package was an SDL build that Just Works™ with as few prerequisites and caveats as possible. To my knowledge, this is the first port of SDL that

  • simultaneously supports Zig 0.12.1, 0.13.0 and 0.14.0-dev,
  • builds SDL with no modifications to the upstream SDL source code,
  • supports cross-compiling for Windows and Linux from any host system (macOS is unfortunately out of scope because of Xcode license terms),
  • supports all Linux feature backends, for example both Wayland and X11 for video, and
  • declares all license information upfront (if you’re someone who thinks that’s important).

(Note that this package was developed from scratch by carefully observing and replicating the behavior of the upstream SDL CMake build scripts. It was not based on any pre-existing SDL port.)

To demonstrate how simple and painless this package makes using SDL3, I’ve prepared two example games:

Breakout

Simple Breakout clone using SDL3 for video, audio, input, etc. Demonstrates how to @cImport, initialize and use SDL3 from Zig code.

preview

git clone https://github.com/castholm/zig-examples.git
cd zig-examples/breakout
zig build run

Snake

SDL’s example Snake game, written in C, built using the Zig build system. Demonstrates how to swap out an existing C project’s build system for zig build.

preview

git clone https://github.com/castholm/zig-examples.git
cd zig-examples/snake
zig build run

Please let me know if you find this package useful, or if you run into any issues when using it. The timing of this announcement holds a bit of a special meaning to me, because I wrote my first line of Zig code on the evening of December 31 2022 (shoutouts to Ziglings!) :smiley:

My goal is to keep this package maintained and in sync with future SDL3 releases, and to incrementally add support for more targets (for example, I am looking into wasm32-emscripten-none).

42 Likes

Cool! Thank you for all of this. I am definitely going to be referring back to your example games, they handle the C/zig interop stuff very neatly.

When I was messing around with SDL3, I built it separately and then linked to it in my build script. This is probably an annoying question, but if you’ve got a sec, could you explain more on the disadvantages of building SDL separately, using a task runner like Make or Task to run each step sequentially? I’m very much a zig beginner, and my C is rusty, and I don’t totally get the problem you’re solving. I didn’t have any trouble building SDL3’s C and my zig code in two steps, and I’m wondering if it’s going to cause me problems as my project grows.

My understanding is that the main benefit to your port is that it makes building for multiple platforms much easier, because it specifies all the config boilerplate, and removes CMake as a dependency. Is that right?

1 Like

Just tried it out on a mac, and it works like a charm! Thank you so much for doing this work and sharing it.

Any plans to also setup GitHub - libsdl-org/SDL_shadercross: Shader translation library for SDL's GPU API. in the same way? This GPU API is an exciting new addition to SDL3, I’ve been thinking about how nice it would be to have shader cross compilation working out of the box for zig as well.

2 Likes

It’s a good question and not annoying at all. There’s in principle nothing wrong with using Zig build system facilities like b.addSystemCommand to invoke the upstream CMake build script directly, or building in advance entirely outside of Zig and just referencing the artifacts directly, or using SDLs official release binaries, or linking against an SDL3 system library. Alternative approaches like those might even be recommended if you want to make sure you get a tried-and-true final result that you know you can trust.

You are mostly spot-on. My goal with the package was primarily just to make using SDL with Zig easy by removing as many prerequisites as possible aside from zig itself, and to let you cross-compile to/from Windows and Linux. It’s also fun to have a project that can be used demonstrate how Zig makes some traditionally difficult tasks easy, as a way to sell Zig itself and encourage more people to try it.

I come from a background of more high-level languages like C# where building applications usually “just works” and I think many of the traditional build systems used by C projects can feel daunting and sometimes exhausting to even begin to think about (especially on Windows) when you just want to start a new project and quickly get into a feedback loop of writing code and seeing the results on screen. I want aspiring hobbyist game devs to be able to install Zig, add a few lines of code to their build.zig and be able to immediately get into writing the actual code and get a good flow going. And then when they have a game, I want them to be able to build it for both Windows and Linux and upload them to some place like itch.io, even if they only use one OS as their primary driver and are unfamiliar with the other.

4 Likes

I might be interested in looking into SDL_shadercross but currently I have enough projects on my hands as it is, so if someone else beats me to the punch it that would be great. I have not tried it myself but GitHub - Beyley/SDL_shadercross_zig looks promising. There’s also Zig’s own SPIR-V backend which could become really interesting and fun once it matures further.

Same for the other SDL satellite libraries like SDL_mixer, SDL_ttf, SDL_image, etc., it would be great if they were packaged and cross-compile-able like this package. Though I would also like to see a push for more native Zig packages for things like audio mixing, fonts and image formats. SDL itself is a bit uniquely desirable since it’s positioned somewhat like “the libc for games”.

1 Like

You’re absolutely right, this is important! I forgot how intimidating even a good CMake config is to deal with if you’re not used to building other people’s stuff from source. And the way you’ve set it up means that it’s easy to see what’s actually happening if something goes wrong, because it’s just a regular build.zig.