UP | HOME

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.

Date: 2025-12-15 15:40:21

Emacs 30.2 (Org mode 9.7.11)