Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 73 additions & 2 deletions build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -51,14 +51,22 @@ pub fn build(b: *std.Build) !void {
,
) orelse .static;

const main_callbacks = b.option(bool, "main_callbacks",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be reasonable to duplicate the example program and rename it to callbacks_example, and make use of this feature there.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do!

\\build SDL3_main to enter the app through SDL_AppInit/Iterate/Event/Quit rather
\\than a classic SDL_main, defaults to false
) orelse false;

const can_san = target.result.os.tag != .ios;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What triggers the sanitizer to fail? Instead of turning it off, maybe we can patch SDL to fix the UB. I've landed other UBSAN related fixes in SDL and they've been happy to merge them.


// Cross-compiling to a target whose SDK isn't bundled with Zig (iOS, Android) needs the
// SDK's headers/frameworks/libs passed in explicitly. Scoped to the SDL library only, unlike
// `--sysroot` this does not re-roots paths across the whole build graph.
const system_paths: SystemPaths = .{
// On Apple, if paths are not passed explicitly then fall back to the SDK found via `xcrun`.

@MasonRemaley MasonRemaley Nov 1, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should have to bake in platform specific paths into this file. I would expect dependencies to be found one of three ways:
1. Provided by the build system (e.g. via build.zig.zon)
2. Provided by the system (e.g. -fsys=... and linkSystemLibrary/such)
3. Provided by the user (e.g. --search-prefix)

I think this might be on me for not reviewing some of the previous macOS PRs carefully enough. I have access to a mac for a few weeks, so unless you feel motivated to take on the task of cleaning that up feel free to just move this part of the changes into your example and I can work on that separately.

You can leave the translate C change here for now though since IIUC that's necessary for this to work at all.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that I ported my game to macOS, I understand why we're taking these paths in as build options. However, we shouldn't need to do any fixups on the paths here.

If you're cross compiling then the caller needs to provide these paths, if you're not cross compiling they can all be left off. I suspect what's happening on your end is you're technically cross compiling to mac from mac because you set -Dtarget explicitly, and this is causing you to need to find the paths yourself, but I could be mistaken.

If that's not the case let me know and I can help you figure out why it's not working for you without these changes. Good call on the translate C changes though, I just fixed those in master because I was blocked on them and credited you in the commit message.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, this is helpful for me (not much experience with this kind of thing, probably overcomplicating it). I'll work on understanding what's going on better and see if I can remove my SDK path stuff.

const system_paths = withAppleSdkFallback(b, target.result, .{
.include = b.option(std.Build.LazyPath, "include_path", "SDK include dir, e.g. $(xcrun --sdk iphoneos --show-sdk-path)/usr/include or <ndk sysroot>/usr/include"),
.framework = b.option(std.Build.LazyPath, "framework_path", "SDK framework dir, e.g. $(xcrun --sdk iphoneos --show-sdk-path)/System/Library/Frameworks"),
.library = b.option(std.Build.LazyPath, "library_path", "SDK library dir, e.g. $(xcrun --sdk iphoneos --show-sdk-path)/usr/lib"),
};
});

// Get the SO version. This is the same as the SDL version, but the major version is elided
// since it's baked into the name. This mirrors the official build process.
Expand All @@ -73,6 +81,7 @@ pub fn build(b: *std.Build) !void {
.target = target,
.optimize = optimize,
.link_libc = true,
.sanitize_c = if (can_san) null else .off,
}),
.linkage = linkage,
.version = sdl_so_version,
Expand Down Expand Up @@ -103,6 +112,24 @@ pub fn build(b: *std.Build) !void {
.flags = flags,
});

const main_lib = b.addLibrary(.{

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be a separate static library, or could we instead configure the existing SDL library differently depending on whether or not the callbacks feature is enabled?

.name = "SDL3_main",
.root_module = b.createModule(.{
.target = target,
.optimize = optimize,
.link_libc = true,
.sanitize_c = if (can_san) null else .off,
}),
.linkage = .static,
});
main_lib.root_module.addIncludePath(upstream.path("include"));
if (main_callbacks) {
main_lib.root_module.addCMacro("SDL_MAIN_USE_CALLBACKS", "1");
}
main_lib.root_module.addCSourceFile(.{ .file = b.path("src/sdl_main.c") });
addSystemPathsToModule(b, main_lib.root_module, system_paths);
b.installArtifact(main_lib);

if (default_target_config) {
const build_config_h = b.addConfigHeader(.{
.style = .{ .cmake = upstream.path("include/build_config/SDL_build_config.h.cmake") },
Expand Down Expand Up @@ -152,6 +179,7 @@ pub fn build(b: *std.Build) !void {
translate_c.defineCMacro("USING_GENERATED_CONFIG_H", "1");
translate_c.addIncludePath(upstream.path("include"));
translate_c.addIncludePath(upstream.path("src/video/khronos"));
addSystemPathsToTranslateC(b, translate_c, system_paths);
const module = translate_c.addModule("sdl3");
module.linkLibrary(lib);

Expand Down Expand Up @@ -206,3 +234,46 @@ pub fn addDepIncludePath(
lib.root_module.addIncludePath(dep.path(sub_path));
}
}

/// Fill in any of `paths` from the SDK found with `xcrun`. Left unset outside Apple or if no SDK is found.
pub fn withAppleSdkFallback(b: *std.Build, target: std.Target, paths: SystemPaths) SystemPaths {
if (!target.os.tag.isDarwin()) return paths;
if (paths.include != null and paths.framework != null) return paths;

const sdk = std.zig.system.darwin.getSdk(b.allocator, b.graph.io, &target) orelse return paths;

return .{
.include = paths.include orelse .{ .cwd_relative = b.pathJoin(&.{ sdk, "usr/include" }) },
.framework = paths.framework orelse .{ .cwd_relative = b.pathJoin(&.{ sdk, "System/Library/Frameworks" }) },
.library = paths.library orelse .{ .cwd_relative = b.pathJoin(&.{ sdk, "usr/lib" }) },
};
}

fn addSystemPathsToModule(b: *std.Build, module: *std.Build.Module, paths: SystemPaths) void {
if (paths.include) |p| module.addSystemIncludePath(p);
if (paths.framework) |p| {
module.addSystemFrameworkPath(p);
module.addSystemFrameworkPath(p.dirname().path(b, "SubFrameworks"));
}
if (paths.library) |p| module.addLibraryPath(p);
}

fn addSystemPathsToTranslateC(b: *std.Build, translate_c: *std.Build.Step.TranslateC, paths: SystemPaths) void {
if (paths.include) |p| translate_c.addSystemIncludePath(p);
if (paths.framework) |p| {
translate_c.addSystemFrameworkPath(p);
translate_c.addSystemFrameworkPath(p.dirname().path(b, "SubFrameworks"));
}
}

pub fn addAppleSdkPathsToModule(b: *std.Build, target: std.Target, module: *std.Build.Module) void {
if (!target.os.tag.isDarwin()) return;

const sdk = std.zig.system.darwin.getSdk(b.allocator, b.graph.io, &target) orelse
@panic("Couldn't detect Apple SDK");

module.addSystemIncludePath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "usr/include" }) });
module.addSystemFrameworkPath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "System/Library/Frameworks" }) });
module.addSystemFrameworkPath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "System/Library/SubFrameworks" }) });
module.addLibraryPath(.{ .cwd_relative = b.pathJoin(&.{ sdk, "usr/lib" }) });
}
3 changes: 3 additions & 0 deletions src/sdl_main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#ifdef SDL_MAIN_USE_CALLBACKS
#include <SDL3/SDL_main.h>
#endif