Static Linking SDL3 when using Odin on MacOS
Table of Contents
Problem Description
Odin doesn't ship with a pre-compiled version of SDL3 for MacOS. If you were to use Raylib with Odin, all you need to do is import "vendor:raylib" in your source code and `odin build .` With SDL3, you need to provide the library yourself. By default, Odin looks for a system installation of SDL3. I prefer not to do system installs of SDL, and usually statically link the library. While doing so, I ran into a few road blocks.
If you're in a similar situation and just want to get something working, feel free to pull down this working example from my personal git forge.
Building a static SDL3 library
SDL3 can be easily built using cmake. Pull the SDL3 source code, unzip it, and run:
cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo -DSDL_STATIC=ON && cmake --build build
In your `build` folder, you should now have `libSDL3.a`.
Approach 1: Copy the sdl3 vendor folder
In Odin's `vendor/sdl3/sdl3_foreign.odin` file, it defines the SDL3 import as:
when ODIN_OS == .Windows { @(export) foreign import lib { "SDL3.lib" } } else { @(export) foreign import lib { "system:SDL3" } }
As far as I know, there's no way to override those definitions from the importing code. Any project that wants to define a different import path will need to copy Odin's vendor/sdl3 folder and update the imports:
when ODIN_OS == .Darwin { @(export) foreign import lib { "./libSDL3.a", } }
Then you'll need to copy the previously built `libSDL3.a` into your project's sdl3 folder. Your `main.odin` file should look something like this for testing:
package main import "sdl3" main :: proc() { _ = sdl3.Init(sdl3.INIT_VIDEO) }
Unfortunately, running `odin build .` will result in many linker errors. To save you some trouble, here's the contents of the full `sdl3/sdl3_foreign.odin` file:
package sdl3 when ODIN_OS == .Darwin { @(export) foreign import lib { "./libSDL3.a", "system:Cocoa.framework", "system:OpenGL.framework", "system:IOKit.framework", "system:AVFoundation.framework", "system:CoreAudio.framework", "system:AudioToolbox.framework", "system:CoreHaptics.framework", "system:CoreMedia.framework", "system:Metal.framework", "system:GameController.framework", "system:ForceFeedback.framework", "system:Carbon.framework", "system:CoreVideo.framework", "system:QuartzCore.framework", "system:UniformTypeIdentifiers.framework", } }
Now re-run `odin run .` and you should be good to go!
Approach 2: Edit Odin's vendor/sdl3/sdl3_foreign.odin file
Update the contents to look like this:
package sdl3 LIBRARY_PATH :: #config(SDL3_PATH, "system:SDL3") when ODIN_OS == .Windows { @(export) foreign import lib "SDL3.lib" } else when ODIN_OS == .Darwin { when LIBRARY_PATH == "system:SDL3" { @(export) foreign import lib {LIBRARY_PATH} } else { @(export) foreign import lib { "system:" + LIBRARY_PATH, "system:Cocoa.framework", "system:OpenGL.framework", "system:IOKit.framework", "system:AVFoundation.framework", "system:CoreAudio.framework", "system:AudioToolbox.framework", "system:CoreHaptics.framework", "system:CoreMedia.framework", "system:Metal.framework", "system:GameController.framework", "system:ForceFeedback.framework", "system:Carbon.framework", "system:CoreVideo.framework", "system:QuartzCore.framework", "system:UniformTypeIdentifiers.framework" } } } else { @(export) foreign import lib "system:SDL3" }
Then, when building the project, use a command like:
odin run . -define:SDL3_PATH=<absolute path to libSDL3.a file here>
Miscellaneous learnings/thoughts
- It would be great if the import path's for Odin's included vendored libraries could be configured by the user.
- I'm sure that the MacOS framework files are collected somewhere on disk, but for the lazy, the full list can be found here (likely outdated)
- You need to prepend the static archive's path with "system:" or else the path will be interpreted as a relbative path.