Last time, I reported that I was working on automating the release builds for my strategic leaf-raking business simulation game, Toytles: Leaf Raking.
Due to other priorities taking up my time, this effort is still ongoing, and for much longer than I would like.
Sprints 2026-MJ_5 and 2026-MJ_6: Release SDL3 update version
In progress:
- Ensure Mac version can still be built/deployed
To recap, back in October, I started the work of updating Toytles: Leaf Raking from libSDL2 to libSDL3. I had expected that it might take me a week or two at most, assuming I could put in the hours.
It was mostly straightforward and quick to do except for when it came to dealing with libSDL3_mixer, which was significantly different enough from libSDL2_mixer in design that it required some rearchitecting of my own code.
I spent weeks of calendar time on that effort, even though it only represented a handful of hours of game development in all of December, and I finally finished at the end of January.
Next was putting out a new release on every platform that I support. Linux was easy, as I do my development there, and the scripts I use to make a release are mostly the same.
The Windows port took a couple of weeks, although it represented again only a handful of hours of effort, and most of this effort was trying to make sure that the next time I want to cut a release will be automatic and fast.
And here I am, still working on the Mac port, and the story is similar. In the last couple of weeks, I put in only a little more than 5 hours of game development time in. Which is too bad, because I really needed to put some time in to solve this one.
Warning: the following gets a bit technical.
Basically, to release a Mac build, I want to make sure my game supports both Intel-based Macs and the Apple Silicon-based Macs. Apple provides a way to do so with the so-called “fat” binary, or Universal Binary. Basically, it’s one application that can run on either one of the hardware options without the player needing to worry about it.
Of course, to build it that way, you need to explicitly configure the build to support both architectures.
Last time, I did so by manually building libSDL2 and my game on my Intel-based Mac Mini, then copying the binaries to my Mac in Cloud instance of an M1-based Mac, building new libraries there, then using lipo to combine the libraries into a Universal Binary version, then building the game with those libraries.
This time, I don’t want to do it manually, and I don’t want to depend on two different Macs. I want to make this part automated as well.
Now, SDL3’s documentation mentions how to build a Universal Binary for the Mac:
cmake .. "-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64" -DCMAKE_OSX_DEPLOYMENT_TARGET=10.13
And if I extract the SDL3 source code and use cmake manually, it works just fine.
However, because I wanted to automate this build as part of my own project’s build scripts, I wanted to have my project’s cmake scripts configure and build SDL3 and all of the other SDL3-based libraries.
And it felt like no matter how I tried to make things work, CMake was doing the most surprising wrong things and never the right thing in terms of passing the CMAKE_OSX_ARCHITECTURES variable to SDL3’s cmake scripts.
Now, I think CMake and CSS have something in common: it’s relatively easy to put together something that seems to work but might have fundamental flaws based on misunderstanding or ignorance of how it is supposed to work.
For CSS, things felt a lot less random once I put in time to understand the Box Model. I was no longer randomly trying different values for margins or padding to get things to line up.
CMake, on the other hand, continues to be a challenge for me to learn. The docs are great…once you know how everything works, but until then, they seem quite abstract and confusing.
Anyway, I spent a few hours trying to figure out why the following cmake script would not result in the cmake command above:
SET(SDL3_CONFIGURE_ARGS
"-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64"
"-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15"
-DCMAKE_INSTALL_PREFIX=${TARGET_INSTALL_DIR})
MAKE_DIRECTORY(${PROJECT_BINARY_DIR}/${SDL3_EXTRACTED_DIR}/build)
ADD_CUSTOM_TARGET(SDL3_SHARED_LIBRARY
ALL
${CMAKE_COMMAND} ${SDL3_CONFIGURE_ARGS} ..
COMMAND make
COMMAND make install
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/${SDL3_EXTRACTED_DIR}/build
COMMENT "BUILDING libSDL3")
As an explanation, I am creating the SDL3_SHARED_LIBRARY target as a custom command. It calls cmake with some arguments (SDL3_CONFIGURE_ARGS), then runs make and make install.
Now, you’ll notice that I have the arguments separate from the command. I have a few, including a target install directory that I want to specify. I don’t want to install to the Mac’s system. I want it to be in a known location for my own build.
And when I didn’t have the -DCMAKE_OSX_ARCHITECTURES argument specified, it would just build on the native architecture of the Mac I happened to be building on. It would specify the deployment target and the target install directory correctly.
That is, passing SDL3_CONFIGURE_ARGS to my custom target’s cmake command worked just fine.
But once I added the -DCMAKE_OSX_ARCHITECTURES argument with two values separated by a semicolon, it stopped interpreting things correctly.
In the above case (which wasn’t my first iteration, by the way), it would build the arm64 version. The semicolon seemed to get interpreted in a way that it was treating it as the end of the command, so x86_64 was seen as a new command.
That’s annoying. What about if we add VERBATIM to the invocation of ADD_CUSTOM_TARGET()?
Well, all that does is treat SDL3_CONFIGURE_ARGS as a single argument being passed to cmake. That is, instead of three separate arguments, it’s one big one…and completely wrong.
Also annoying.
I tried escaping quotes, quoting around escaped quotes, having no quotes, and using or not using VERBATIM for each case. I just kept getting the semicolon treated as the end of a command, leaving x86_64 as a completely separate command, or I got it to treat arm64;x86_64 as a list to expand (so it passed along “arm64 [space] x86_64”), or that one long argument.
I just couldn’t get it to do what I wanted, which was to pass along the arguments as I actually wrote them.
In the end, I found that the problem goes away when I don’t try to use a variable that represents multiple arguments, which makes CMake treat it as a list to sometimes expand or not based on some seemingly non-intuitive rules. I instead now use a separate argument:
SET(MULTI_ARCH_ARG
"-DCMAKE_OSX_ARCHITECTURES=arm64;x86_64")
SET(SDL3_CONFIGURE_ARGS
-DCMAKE_OSX_DEPLOYMENT_TARGET=10.15
-DCMAKE_INSTALL_PREFIX=${TARGET_INSTALL_DIR})
ADD_CUSTOM_TARGET(SDL3_SHARED_LIBRARY
ALL
${CMAKE_COMMAND} "${MULTI_ARCH_ARG}" ${SDL3_CONFIGURE_ARGS} WORKING_DIRECTORY
COMMAND make
COMMAND make install
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/${SDL3_EXTRACTED_DIR}
COMMENT "BUILDING libSDL3")
Note that I pass along MULTI_ARCH_ARG by quoting in the cmake command, but I don’t quote the SDL3_CONFIGURE_ARGS.
The above worked, and I could move on.
I could have moved on earlier by not trying to do the above, but I was determined to figure out how to get CMake to do what I wanted, and in the process, I am now more familiar with how CMake variables and lists work. Next time I run into something similar, I have this experience under my belt, and I think the struggle was worth it for what I gained.
And now I’m fighting with SDL3_ttf dependencies not building with multiple architectures. SDL3_ttf depends on Freetype and Harfbuzz, and those are apparently building with the current architecture (arm64) and so when the x86_64 version of SDL3_ttf is trying to build, it can’t find the x86_64 version of those dependencies. Hopefully solving this one won’t take another week.
Thanks for reading, and stay curious!
—
Want to learn about future Freshly Squeezed games I am creating? Sign up for the GBGames Curiosities newsletter, and download the full color Player’s Guides to my existing and future games for free!

