Files
lean-sdl3/lakefile.lean
Greg ShuflinandClaude Fable 5.1 2de47f0db9 Fix macOS link: use ld64 flags instead of GNU ld ones
ld64.lld rejects --allow-shlib-undefined and -rpath=$ORIGIN. Add a
macOS branch with -undefined dynamic_lookup and an @executable_path
rpath, and skip the Linux-only patchelf step in the justfile.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 15:26:20 -07:00

174 lines
6.1 KiB
Lean4

import Lake
open System Lake DSL
package SDL3
-- pin to a specific commit to avoid breakages
structure GitDep where
repo : String
rev : String
name : String
/-- The directory where this cloned repo should live --/
def GitDep.dir (dep : GitDep) (pkg : Package) : FilePath :=
pkg.dir / "vendor" / dep.name
/-- Invoke git to clone this dependency to a vendored directory --/
def GitDep.clone (dep : GitDep) (pkg : Package) : FetchM Unit := do
let dstDir := dep.dir pkg
if (<- dstDir.pathExists) then
logInfo s!"Directory {dstDir} already exists, skipping clone"
else
logInfo s!"Cloning {dep.repo} into {dstDir}"
proc {
cmd := "git"
args := #["clone", "--revision", dep.rev,
"--single-branch", "--depth", "1", "--recursive", dep.repo, dstDir.toString
]
}
def sdlDep : GitDep := {
repo := "https://github.com/libsdl-org/SDL.git"
rev := "f3a9f66292d49322652be01ee93412d0e9b74f0b"
name := "SDL"
}
def sdlImageDep : GitDep := {
repo := "https://github.com/libsdl-org/SDL_image.git"
rev := "d354e3d5146117f8b2f14096800965e56f9f7bfc"
name := "SDL_image"
}
def sdlTtfDep : GitDep := {
repo := "https://github.com/libsdl-org/SDL_ttf.git"
rev := "6b6bd588e8646360b08f624fb601cc2ec75c6ada"
name := "SDL_ttf"
}
def sdlMixerDep : GitDep := {
repo := "https://github.com/libsdl-org/SDL_mixer.git"
rev := "5cdf029bae982df1d6c210f915fc151a616d982f"
name := "SDL_mixer"
}
def sdlDeps : List GitDep := [sdlDep, sdlImageDep, sdlTtfDep, sdlMixerDep]
-- TODO: at some point, we should figure out a better way to set the C compiler
def compiler := if Platform.isWindows then "gcc" else "cc"
def buildCMakeProject (repoDir : FilePath) (args : Array String): FetchM (Unit) := do
logInfo s!"Building {repoDir} with CMake with args {args}"
let buildDir := repoDir / "build"
if (<- buildDir.pathExists) then
logInfo "Build directory already exists, skipping configuration step"
else
let configureBuild IO.Process.output {
cmd := "cmake",
args := #[
"-S", repoDir.toString,
"-B", buildDir.toString,
"-DBUILD_SHARED_LIBS=ON",
"-DCMAKE_BUILD_TYPE=Release",
s!"-DCMAKE_C_COMPILER={compiler}",] ++ args
}
if configureBuild.exitCode != 0 then
logError s!"Error configuring build: {configureBuild.stderr}"
logInfo "Build configured successfully"
let buildProject IO.Process.output {
cmd := "cmake",
args := #["--build", buildDir.toString, "--config", "Release"]
}
if buildProject.exitCode != 0 then
logError s!"Error building project: {buildProject.exitCode}"
logError s!"Project build stderr: {buildProject.stderr}"
logInfo s!"{repoDir} built successfully"
target libSDL3 pkg : Dynlib :=
return .pure { name := "SDL3", path := sdlDep.dir pkg / "build" / nameToSharedLib "SDL3" }
target libSDL3Image pkg : Dynlib :=
return .pure { name := "SDL3_image", path := sdlImageDep.dir pkg / "build" / nameToSharedLib "SDL3_image" }
target libSDL3Ttf pkg : Dynlib :=
return .pure { name := "SDL3_ttf", path := sdlTtfDep.dir pkg / "build" / nameToSharedLib "SDL3_ttf" }
target libSDL3Mixer pkg : Dynlib :=
return .pure { name := "SDL3_mixer", path := sdlMixerDep.dir pkg / "build" / nameToSharedLib "SDL3_mixer" }
target libleansdl pkg : FilePath := do
-- clone the git repositories we need so we can build them later
for dep in sdlDeps do
dep.clone pkg
-- build all the libraries we need
let sdlRepoBuildDir := (sdlDep.dir pkg / "build").toString
let sdlDirFlag := s!"-DSDL3_DIR={sdlRepoBuildDir}"
buildCMakeProject (sdlDep.dir pkg) #[]
buildCMakeProject (sdlImageDep.dir pkg) #[sdlDirFlag]
buildCMakeProject (sdlTtfDep.dir pkg) #[sdlDirFlag, "-DSDLTTF_VENDORED=true"]
buildCMakeProject (sdlMixerDep.dir pkg) #[sdlDirFlag, "-DSDLMIXER_VENDORED=true"]
logInfo "All libraries built successfully"
-- copy binaries
let binaryDstDir := ((<- getRootPackage).binDir)
IO.FS.createDirAll binaryDstDir
-- manually copy the DLLs we need to .lake/build/bin/ in the root directory for the game to work
for dep in sdlDeps do
let sourceDir := dep.dir pkg
logInfo s!"Copying binaries from {sourceDir} to {binaryDstDir}"
let buildDir := sourceDir / "build"
for entry in ( buildDir.readDir) do
if entry.path.extension != none then
copyFile entry.path (binaryDstDir / entry.path.fileName.get!)
-- compile the Lean-SDL C glue code and bundle into a static library
let srcJob inputTextFile (pkg.dir / "c" / "sdl.c")
let oFile := pkg.buildDir / "c" / "sdl.o"
let leanInclude := <- getLeanIncludeDir
let includeDirs : List FilePath := sdlDeps.map
(fun dep => dep.dir pkg / "include/")
let flags: List String := ["-fPIC"] ++
includeDirs.map (fun dir => s!"-I{dir}") ++
["-D_REENTRANT", s!"-I{leanInclude}"]
let sdlO buildO oFile srcJob #[] flags.toArray compiler
let name := nameToStaticLib "leansdl"
buildStaticLib (pkg.staticLibDir / name) #[sdlO]
def sdlLinkObjs : TargetArray FilePath := #[libleansdl]
def sdlLinkLibs : TargetArray Dynlib := #[libSDL3, libSDL3Image, libSDL3Ttf, libSDL3Mixer]
-- make sure to copy these link args into whatever project is using this library in order for it to work
-- This is because without "-rpath=$ORIGIN", the Linux executable will not load dynlibs next to the executable (i.e., the SDL ones you've copied there).
-- macOS's ld64 spells both flags differently: `-undefined dynamic_lookup` and `@executable_path` in place of `$ORIGIN`.
def sdlLinkArgs : Array String :=
if Platform.isWindows then
#[]
else if Platform.isOSX then
#["-Wl,-undefined,dynamic_lookup", "-Wl,-rpath,@executable_path"]
else
#["-Wl,--allow-shlib-undefined", "-Wl,-rpath=$ORIGIN"]
@[default_target]
lean_lib SDL where
moreLinkObjs := sdlLinkObjs
moreLinkLibs := sdlLinkLibs
moreLinkArgs := sdlLinkArgs
lean_exe «test-app» where
root := `TestApp
moreLinkObjs := sdlLinkObjs
moreLinkLibs := sdlLinkLibs
moreLinkArgs := sdlLinkArgs
lean_exe «webcam-app» where
root := `WebcamApp
moreLinkObjs := sdlLinkObjs
moreLinkLibs := sdlLinkLibs
moreLinkArgs := sdlLinkArgs