Skip to content

feat(compat.sdl3): SDL3 3.4.2, built from source - #371

Merged
Sunrisepeak merged 3 commits into
mainfrom
feat/compat-sdl3
Sep 8, 2026
Merged

feat(compat.sdl3): SDL3 3.4.2, built from source#371
Sunrisepeak merged 3 commits into
mainfrom
feat/compat-sdl3

Conversation

@Sunrisepeak

Copy link
Copy Markdown
Member

The last of vulkan-rt's dependencies, and the one that could not ride along in #370: 266 sources, three platforms, and a config header that has to be generated. Shape E, and a sibling to compat.sdl2 rather than its replacement — SDL2 and SDL3 are different APIs with different sonames, and projects migrate on their own schedule.

Two things are easier than they were for SDL2

Both measured, not assumed:

  • No repacked tarball. SDL2's entry has to point at an xlings-res re-host because the upstream archive carries symlinks that break the windows runner. SDL3's carries none — tar tvzf | grep -c '^l' is 0 — so GLOBAL points straight at GitHub.
  • The config dispatcher is upstream's. include/build_config/SDL_build_config.h already selects _windows.h / _macos.h by platform, and both of those are checked in. Only linux falls through to SDL_build_config_minimal.h, which has no video driver at all. So exactly one config is generated, and only linux uses it.

The config may only claim what this index packages

That is a rule, not a preference. CMake probes the machine it runs on, so anything the host happens to have gets switched on and then fails to compile here. Three were found by failing builds:

switched off why
SDL_X11_XSCRNSAVER src/video/x11/SDL_x11dyn.h:69: fatal error: X11/extensions/scrnsaver.h: No such file or directory — there is no compat.xss
SDL_LIBTHAI the Thai line-breaker the X11 message-box toolkit uses
SDL_HIDAPI_LIBUSB hidapi's libusb backend

XTEST and XSYNC are off pre-emptively for the same reason.

What remains is checkable rather than hopeful: every library the finished config says it will dlopen must have a package here. That list is seven, and it matches linux.deps exactly:

libfribidi.so.0  compat.fribidi     libX11.so.6      compat.x11
libXcursor.so.1  compat.xcursor     libXext.so.6     compat.xext
libXfixes.so.3   compat.xfixes      libXi.so.6       compat.xi
libXrandr.so.2   compat.xrandr

Because the X11 driver is x11(dynamic), those are compile-time headers only — the package links against none of them.

The config was also generated with this index's toolchain and the index's own X11 headers, which is not a formality: mcpp's gcc has its own sysroot and cannot see /usr/include, so without them SDL's check fails and configure aborts outright. compat.curl records what the shortcut costs — a config generated with the host cc once asserted that ssize_t does not exist.

The source list is CMake's own answer

Not a reading of CMakeLists.txt. After configuring, the objects CMake decided to build were read back out of CMakeFiles/SDL3-static.dir/build.make and mapped to directories: 266 sources → 73 directories taken whole + exactly one taken in part. That one is src/core/linux, minus the six dbus/IME files (SDL_dbus.c, SDL_fcitx.c, SDL_ibus.c, SDL_ime.c, SDL_progressbar.c, SDL_system_theme.c) — the same set compat.sdl2 omits, and for the same reason: they are not guarded internally, they simply fail without dbus-1 headers.

Everything else is a directory glob, which works because SDL compiles unselected backends into empty translation unitsrender/direct3d12 and render/ps2 are in the common list and contribute nothing on linux.

macOS and Windows

They do not use the generated config, so their lists are derived from what the checked-in configs switch on (COREAUDIO / IOKIT / MFI / COCOA / METAL / GPU_METAL … and WASAPI / DSOUND / DINPUT / XINPUT / WGI / D3D11 / D3D12 …).

The non-obvious one is windows: SDL_THREAD_GENERIC_COND_SUFFIX and SDL_THREAD_GENERIC_RWLOCK_SUFFIX mean the windows backend implements condition variables and rwlocks by falling back to the generic ones, so thread/generic/SDL_syscond.c and SDL_sysrwlock.c must be linked alongside thread/windows/*. Only those two — the other four generic files share basenames with their windows twins, and taking the directory whole would compile two definitions of the same functions.

The test runs headless on all three platforms

It forces SDL's dummy video driver, so it exercises the real video subsystem without a display server:

  • header and runtime version agree (one tarball, not two), and equal 3.4.2
  • the compiled-in driver list contains dummy and offscreen — a config that fell through to SDL_build_config_minimal.h has neither, which is exactly the failure mode a "it compiled" test would miss
  • the dummy driver initialises, becomes the current driver, and produces a 320×240 window with a surface of that size that can be filled
  • SDL_GetTicks advances across a delay

Verified

leg result
linux · gcc 16.1.0 267 units compiled, test passes
linux · llvm 22.1.8 267 units compiled, binary runs

⚠️ macOS and Windows are derived as described above and confirmed by CI, not locally — there is no way to compile either from this machine. If a platform list is wrong, this is where it shows.

CN mirror at gitcode.com/mcpp-res/sdl3, byte-identical to upstream (verified by downloading it back and comparing sha256).

The last of vulkan-rt's dependencies, and the one that could not ride along in
#370: 266 sources, three platforms, and a config header that has to be
generated. Shape E, sibling to compat.sdl2 rather than its replacement — SDL2
and SDL3 are different APIs with different sonames.

Two things are easier than they were for SDL2, both measured rather than
assumed:

* NO REPACKED TARBALL. SDL2's entry points at an xlings-res re-host because the
  upstream archive carries symlinks that break the windows runner. SDL3's
  carries none (`tar tvzf | grep -c '^l'` is 0), so GLOBAL points straight at
  GitHub.
* THE CONFIG DISPATCHER IS UPSTREAM'S. include/build_config/SDL_build_config.h
  already selects _windows.h / _macos.h by platform and both are checked in.
  Only linux falls through to SDL_build_config_minimal.h, which has no video
  driver at all. So exactly one config is generated, and only linux uses it.

THE CONFIG MAY ONLY CLAIM WHAT THIS INDEX PACKAGES. CMake probes the machine it
runs on, so anything the host happens to have is switched on and then fails to
compile here. Three were found by failing builds — XSCRNSAVER (fatal error:
X11/extensions/scrnsaver.h), LIBTHAI, and HIDAPI_LIBUSB — and XTEST/XSYNC are
off pre-emptively. What remains is checkable rather than hopeful: every library
the finished config says it will dlopen must have a package here. That list is
seven (libfribidi, libX11, libXcursor, libXext, libXfixes, libXi, libXrandr)
and it matches linux.deps exactly.

It was also generated WITH THIS INDEX'S TOOLCHAIN and the index's own X11
headers, which is not a formality: mcpp's gcc has its own sysroot and cannot
see /usr/include, so without them SDL's check fails and configure aborts.
compat.curl records what the shortcut costs — a config generated with the host
cc once asserted that ssize_t does not exist.

The source list is CMake's own answer rather than a reading of CMakeLists:
after configuring, the objects it decided to build were read back out of
CMakeFiles/SDL3-static.dir/build.make and mapped to directories — 266 sources,
73 directories taken whole and exactly one in part (src/core/linux, minus the
six dbus/IME files compat.sdl2 also omits, which do not self-guard).

macOS and Windows do not use the generated config, so their lists are derived
from what the CHECKED-IN configs switch on. The non-obvious one is windows:
SDL_THREAD_GENERIC_COND_SUFFIX and _RWLOCK_SUFFIX mean the windows backend
falls back to the generic condition variable and rwlock, so
thread/generic/SDL_syscond.c and SDL_sysrwlock.c must be linked alongside
thread/windows/* — only those two, since the other four share basenames with
their windows twins.

The test drives SDL's DUMMY video driver, so it runs headless on all three
platforms: header/runtime version agreement, the compiled-in driver list
containing dummy AND offscreen (a config that fell through to minimal has
neither), the dummy driver actually initialising and producing a 320x240 window
with a surface of that size, and the timer advancing.

Verified: linux gcc 16.1.0 and linux llvm 22.1.8 both compile all 267 units and
run the test. macOS and Windows are derived as described and confirmed by CI —
there is no way to compile either from here. CN mirror at
gitcode.com/mcpp-res/sdl3, byte-identical to upstream (verified).
Two failures from the first CI run, both from assuming a shape rather than
checking it.

WINDOWS: SDL3 IS NOT ALL C. `core/windows`, `video/windows` and the GameInput
joystick backend (`joystick/gdk/SDL_gameinputjoystick.cpp`) carry C++ sources,
and `*/src/.../*.c` drops them silently — a glob that misses a file produces no
diagnostic at all until the link:

    lld-link: error: undefined symbol: WIN_InitGameInput
    lld-link: error: undefined symbol: SDL_GAMEINPUT_JoystickDriver

`render/direct3d12` has the same problem in the COMMON list: three of its files
are .cpp, so the `*.c` glob was linking half a renderer. Rather than patch only
the symbols the linker happened to name, the whole class was found by asking
the tree which sources are not .c — 34 directories, of which the windows-
relevant ones are these four. macOS was already right because its backends are
.m and the globs say .m.

LINUX/LLVM: `SDL_x11opengl.h` includes <GL/glx.h>, which is not in the Khronos
registry and needs `compat.glx-headers` — the same dependency, for the same
reason, that compat.sdl2 carries. This one is worth the comment it now has,
because it is invisible from two of the three places you would look: the gcc
toolchain's sysroot already carries GL/glx.h, so linux/gcc passed and so did a
local build with either compiler. Only the llvm leg says:

    fatal error: 'GL/glx.h' file not found

Verified after the fix that the header now resolves to the PACKAGE's copy
(.mcpp/.../compat-x-glx-headers/1.7.0/libglvnd-1.7.0/include/GL/glx.h) rather
than to whatever the toolchain happened to have, which is the difference
between a fix and a coincidence. Both linux legs pass; windows is CI's to
confirm.
The previous commit's message described this change; the file did not contain
it. The edit that was supposed to insert three .cpp globs into the windows
source list was written against a pattern with two globs on one line, while the
descriptor has one per line, so it matched nothing and changed nothing — and
because that particular substitution carried no assertion, it failed silently.
Only the direct3d12 line, which was a separate edit, landed.

CI said so immediately and identically: the same five undefined symbols, the
second time round.

    lld-link: error: undefined symbol: WIN_InitGameInput
    lld-link: error: undefined symbol: SDL_GAMEINPUT_JoystickDriver

The globs are in now, and the reasoning is recorded next to them: WIN_* is
defined in video/windows/SDL_windowsgameinput.cpp, the joystick driver in
joystick/gdk/SDL_gameinputjoystick.cpp, and core/windows carries two more C++
TUs. All four are reached because the CHECKED-IN windows config sets
SDL_JOYSTICK_GAMEINPUT — which this descriptor does not get to decide, so the
sources have to be there.

Linux still passes; windows is CI's to confirm.
@Sunrisepeak
Sunrisepeak merged commit cfd5a54 into main Sep 8, 2026
14 checks passed
@Sunrisepeak
Sunrisepeak deleted the feat/compat-sdl3 branch September 8, 2026 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant