Issues using SDL2 on Xcode on macOS

Hey guys, I’ve been having this issue with my SDL2 install and its been driving me crazy, I’ve asked around everywhere but no one can fix my issue for some reason, so I thought I’d come here. So, I’ve been trying to follow lazy foo’s tutorial and his first step isn’t working. I’m developing on both Intel and Apple Silicon computers and every time I install the framework to the /Library/Framework folder, I can’t seem to use it. Whenever I use “#include<SDL2/SDL.h>” the path autocompletes but when I try building, it says the file cannot be found. I have included the framework into the project and tried both the dev and runtime frameworks and neither work.

I then moved onto building the libraries myself following the instructions on the site but every time I run make, a bunch of alias files are created but when I tell macOS to show e the actual files, it says they cannot be found. So right now, I’m stuck, I don’t know what else to try and its really beginning to frustrate me. I feel like I’m just going round in circles. I’m currently on macOS Monterey and Xcode 13. Has anyone else had this issue or can anyone else fix it?

These are the steps I follow:

Put SDL2.framework in the same folder as YourProject.xcodeproj (don’t put it in /Library/Frameworks). Don’t use the command line to copy it, as in my experience it doesn’t preserve the internal symlinks.

In Xcode, in your project’s settings, select your project’s target. In the General tab, in the Frameworks, Libraries, and Embedded Content section, click the + to add another framework. Click Add OtherAdd Files and select SDL2.framework. This will make Xcode link your app against the SDL2 framework, and also tell Xcode to put it inside your app bundle so your users won’t have to have SDL installed.

In the Xcode project settings, in the Build Settings tab, find the Framework Search Paths setting. Make sure $(PROJECT_DIR) is included. Apparently this is automatically done on the latest Xcode, but still make sure to check because it didn’t use to be.

In the project settings Signing & Capabilities tab, scroll down and tick the box that says Disable Library Validation unless the SDL2.framework you’re using was built and signed by you. This will keep macOS from refusing to run your app (because your app is signed by you, but the framework is signed by someone else).

edit: Also, if you’re going to build SDL yourself for macOS or iOS, use the Xcode project that comes with the SDL sourcecode instead of messing around with make etc.

I have the same exact issue, and I am still trying to get to figure out a solution. If it helps, a Clean Build Folder should get your program running, but making any changes to your code afterwards will re-trigger the issue and you’ll have to clean build folder again. I have also found that going to Build Phases > Embedded Frameworks and checking the “Copy only when installing” box, then running will cause the program to crash with an __abort_with_payload. Then unchecking “Copy only when installing” again should allow the program to run again. This is a good (if a bit clunky) workaround for running a program that takes a long time to build.

If anyone has any idea what could possibly be going on here, that would be immensely helpful. I’ve been battling this weird error for almost a week now. If it helps, here’s the console log for when I get the __abort_with_payload error:

dyld[5340]: Library not loaded: @rpath/SDL2.framework/Versions/A/SDL2

Referenced from: <4710FA6C-D433-3D0F-94FA-91E0E31849EE> /Users/[username]/Library/Developer/Xcode/DerivedData/[project_name]-ctbicgopgppphhdyvhsejvjfyonf/Build/Products/Debug/[project_name]

Reason: tried: ‘/Users/[username]/Library/Developer/Xcode/DerivedData/[project_name]-ctbicgopgppphhdyvhsejvjfyonf/Build/Products/Debug/SDL2.framework/Versions/A/SDL2’ (no such file), ‘/System/Volumes/Preboot/Cryptexes/OS@rpath/SDL2.framework/Versions/A/SDL2’ (no such file), ‘/Library/Frameworks/SDL2.framework/Versions/A/SDL2’ (no such file), ‘/System/Library/Frameworks/SDL2.framework/Versions/A/SDL2’ (no such file, not in dyld cache)

The reason you get the __abort_with_payload is because it isn’t copying SDL2.framework to your app bundle when building. So it looks in other standard places it expects to find it, but doesn’t find it there either.

Did you follow the steps in my earlier post?

LazyFoo’s tutorials are… not always correct.

Whilst drafting my reply to you and re-executing your steps, I (well, mostly you) actually solved the issue.

Yes, I followed the steps in your post, but unfortunately it didn’t fix it. I tried setting both my Framework and System Framework search paths to $(PROJECT_DIR) but that didn’t work either (even though the SDL2.framework is in there). Even after setting those search paths, I still get __abort_with_payload and the same error message that says it’s searching in /Library/Frameworks. I got: dyld[16532]: Library not loaded: @rpath/SDL2.framework/Versions/A/SDL2

Where I incorrectly assumed @rpath is my project directory. Then I googled and found that this is actually the Runpath Search Path. So, I went to project settings>Target>Build Settings, searched “Runpath” (or, it’s in the ‘Linking’ section), and set the Runpath Search Paths to $(PROJECT_DIR).

So, from what I can tell, the problem is totally solved, but I assume it will involve downloading SDL2 for each project, since I need the framework to sit in my project directory.

Thanks so much for the help sjr, as I would never have solved this without your instructions. And @Armature89 , make sure to do this step if you’re still having this problem so many months later.

You don’t want to change the runpath. The reason is because, at runtime, you want it loading SDL2 from the framework shipped inside your app bundle.

You are building your game/whatever as an app bundle, right? The instructions were written with the assumption you’re building/distributing a macOS app bundle.

You put SDL2.framework next to YourProject.xcodeproj so that it’ll be in your source code repository, so you can always go back to the specific version of SDL that a given version of your app was built with. And Xcode will, by default, copy that into your app bundle when building so that you can just ship it with your app and your users won’t have to manually copy SDL2.framework to ~/Library/Frameworks or anything like that.

But changing @rpath can mess with that: at runtime, instead of looking inside your app bundle for SDL2.framework, and then system search paths, it will look at the hard-coded path to your source code repository, even on the end user’s machine! So leave it at the default of @executable_path/../Frameworks

Apologies as I’m not 100% clear on terminology, but I don’t think so. I am building my game as a command line tool (mainly for learning game dev). I don’t know if it’s straightforward to build it as an app bundle instead, or if I’m totally misunderstanding what that even means (google wasn’t overly helpful).

This makes sense to me, and I’d definitely like to find another solution but in the meantime I think I’ll have to live with it, just in the interest of getting some stuff done for my project. I wish I had a better idea of how this stuff works (and Xcode project templates as well) but it’s all a bit overwhelming and I’m not really sure of the best way to go about learning it.

You definitely want your game to be distributed as an app bundle. Basically, that’s a regular macOS app. It has the executable and everything else all bundled into one folder with a specific layout and a name that ends in .app. All resources, artwork, etc., will all be inside the app bundle. Picking any of the Xcode app templates besides Command Line Tool creates an app bundle.

You can’t get your app notarized if it isn’t an app bundle.

To make a regular macOS app that uses SDL:

  1. Create a new project in Xcode, and pick plain vanilla Mac App. You don’t need to choose the game one, just the regular “App” one. On the next screen, make sure the language is set to Objective-C (you don’t need to know Objective-C, as you’ll see).
  2. Delete any file that ends with .xib, .nib, or .storyboard
  3. Delete AppDelegate.h and AppDelegate.m
  4. Don’t delete Assets.xcassets. You’ll want to put macOS assets in there later, like your game’s icon.
  5. Delete main.m and replace it with your main.c or main.cpp or whatever
  6. Add your game’s other source code, if this is for an existing game.
  7. Add your game’s assets: In the file browser on the left side of Xcode, right click on the first folder and pick New Group without Folder. Name it “Assets” or “Resources” or whatever. Right click on the new group and pick “Add Files to [YourProject]”. Add the files, make sure that Copy If Needed is turned OFF, set Added Folders to Create Folder References, and make sure your game’s target is the one selected under Add to Targets
  8. Make sure that any file loading your code is doing is taking into account that the game’s assets will not be in the same folder as the executable or a sub-folder of it. Instead, use the SDL function SDL_GetBasePath() to find out what folder the game assets are in (will be something like /Applications/YourGame.app/Contents/Resources/). This function also returns the appropriate directory on Windows and Linux.
  9. Do everything from my earlier post.
1 Like

I copied all your steps and everything worked perfectly. Thanks so much for taking the time to help me and explain how this stuff works. Much appreciated!

1 Like

No problem, glad it all worked out! :+1:

Some of the confusion I think is that the tutorial has you start with a Command Line Tool “template”. You don’t in fact want to ultimately end up with a command line tool however.

Many of the steps from sjr (" 1. Delete any file that ends with .xib, .nib, or .storyboard", etc.) are essentially stripping down the plain vanilla Map App template … very much like what the Command Line Tool template gives you.

I was in a similar rut until I made the change to the source file:

-- #include <SDL.h>
++ #include <SDL2/SDL.h>

Not quite. The App template still builds an actual, full-fledged macOS app (an app bundle), even after you remove some of the unneeded files it creates. The Command Line Tool template just gives you a bare binary.

Removing any .xib, .nib, and .storyboard files is just removing the default UI stuff the template creates for you. Since SDL will be creating the window programmatically, we don’t need it.

As I pointed out in my earlier post, you can’t get your app notarized if it isn’t an app bundle.

I’ve been banging my head against getting this framework working cleanly since upgrading Xcode to 15.x. Three issues were recurring with horrible version-skew and many difficulties when upgrading to newer versions of SDL2. I have sanitized the installation which included one trick. Now I can upgrade to new versions of SDL2 without issue. The 3 issues were:

(1) I want to just replace older SDL2.framework to newer in /Library/Frameworks (as per instructions in distributed .dmg) without having to also install it in my project hierarchy.
(2) Runtime error - “can’t find the library (abort)” (or some similar verbiage)
(3) Cannot find #include <SDL2/SDL.h> at compile time

My sanitized solution (with the “trick”):
(1) After installing SDL2 in /Library/Frameworks (via Finder drag)… do these once only (not necessary every update)… drag SDL2.framework from /Library/Frameworks into the Xcode navigator area. Xcode will create a Frameworks group by default, if it’s not already there. In the “general” settings tab with the relevant “Target” selected, make sure the “Frameworks and Libraries” has an entry for the newly-dragged SDL2.framework with “Embed & Sign” pull-down selected.

(2) Once (1) is done (should only need to do this once and not every time SDL2 is updated), solve the runtime “Abort” issue by adding the SDL2.framework to the, now-available, “Embed Frameworks” “Build Phases” item by: make the triangle for this item point down, hit the newly available “+” in this newly opened section, hit “Add Other…”, Finder-drag SDL2.framework into the newly presented open dialog and hit the “Open” button. Now you should have SDL2.framework in the Embed Frameworks list. My “Code Sign On Co…” (whatever that means) is checked.

(3) Now the trick!!!
Without this silly trick, #include <SDL2/SDL.h> fails at compile time. (double-quote version fails too).

Logged in as an admin, in a terminal shell, do this:

cd /usr/local/include

(if you don’t have this directory; “mkdir -p /usr/local/include; cd /usr/local/include”)

ln -s /Library/Frameworks/SDL2.framework/Headers; mv Headers SDL2

Now in target settings, “Build Settings” tab, find “Header Search Paths” section and add “/usr/local/include” with “recursive” option selected (if it’s not already there). I didn’t need $(PROJECT_DIR) in this section but it should not matter if it’s there (providing you don’t have an additional SDL2.framework installed in your Project Directory (that’s the directory containing your .xcodeproj file)).

I used a symbolic link (-s) for the trick so that the link is not disrupted when you next upgrade your SDL2.framework.

Now, even when you do a Product->Clean Build Folder, the project should build and run without Xcode issues.

Conclusion: Too many moving parts! Thanks Apple for scattering options all over the place with very little helpful automation. I’m sure ChatGPT will mess it up just as easily when asked for help. I now have very few hairs left to pull out!

Additionally, if you’d like to perform “the trick” for SDL2_ttf.framework #include files, do:

cd /usr/local/include
ln -s /Library/Frameworks/SDL2_ttf.framework/Headers; mv Headers SDL2_ttf

in an admin terminal.

Or you could just put SDL2.framework alongside your .xcodeproj, and then add it to your Xcode project using the steps you mentioned in Part 1. Xcode will see that it’s in your project hierarchy and automatically set it to be embedded and signed, eliminating the need for Part 2.

This has the advantage that the version of SDL you’re testing with will be in version control. It also avoids the messiness of having SDL2.framework installed to a systemwide location (version conflicts, etc.)

Part 3 is unnecessary. If your Xcode project won’t build despite using #include <SDL2/SDL.h> and linking to SDL2.framework then something is wrong. The whole point of a .framework is that it’s supposed to be a complete package that Xcode and the rest of the system knows how to handle, and you don’t need to touch the files inside aside from #include statements.

Re: your conclusion: macOS frameworks are meant to help avoid this kind of stuff, but if you’re gonna go linking to files and folders inside them with symlinks that you yourself have created… :man_shrugging:

As for ChatGPT? vomits

Points taken, Sjr. Thanks for the response (appreciated).

I specifically don’t want to have different SDL2 versions across different projects and logins, though. I was trying to install and maintain a single version systemwide on a development Mac.

Part two, the need to carry a locally imbedded library that often did not match the compiled library. Eg a difference between “compiled with“ and “linked with” was horrible. In a single project with multiple targets, I was seeing multiple versions of the embedded library in the project navigation list (one for each target) and a separate project-wide framework. Frameworks are meant to contain all the required resources in one place. An illustration of how bad frameworks are integrated is that with no compile issue, the binary would fail at runtime (abort) because some part was not signed or the library was not dynamically found at app startup.

Since an Xcode upgrade, no matter what I used as a header path specification, the framework/Xcode integration was broken when #include, referenced something in the SDL2 framework. It would work when I installed hombrew’s SDL2, but hombrew and the original source are rarely at the same version. Not something I could allow to continue into production.

As far as I can tell, since there is no actual directory named “SDL2”, and the fact that .h files are hidden behind a framework “Headers” directory, it would require a mechanism in the integration of frameworks in Xcode to map “Headers/SDL.h” to “SDL2/SDL.h”. That has been broken on Xcode upgrade (it used to work but now it does not). The “trick“ I came up with, provides the missing filename path mapping. Once Apple fixes the broken framework mechanism, my mapping will be (properly) irrelevant. I wonder if there’s some obscure setting I’m missing that would allow #include to work again, or, perhaps the problem is that the SDL2 developers should recast their #include statements to omit SDL2/ pathing to accommodate Apple? Do you have some insight on how I can get Xcode’s path mapping to work with frameworks?

Thanks again for the response.

Phoneposting here, but

IMHO it’s a good idea to make sure the SDL framework you’re testing with is the one you’re shipping with your app to users.

In practice, for me, it hasn’t been a problem re: having an SDL2.framework per project. I can go back to an old project I haven’t touched in a while and it builds and runs without issues related to SDL, because the SDL2 framework it was last built with is part of the project.

What it sounds like is happening here re: “linked with” not matching “compiled with” is that, since you’re putting /usr/local/include in your header search path, the compiler is finding the SDL2 headers installed by Homebrew and is using those instead of the ones inside the framework.

Not finding the framework at runtime isn’t some flaw in frameworks, but an issue every operating system that supports dynamic libraries has.

Linking to /Library/Frameworks/SDL2.framework while also having it embedded and linked in the app bundle could be confusing the dynamic linker.

Using projects created via the steps I outlined in an earlier post, I’ve never had an Xcode upgrade break a project in a way that had anything to do with SDL.

So frameworks aren’t just an Xcode thing. Apple Clang (and possibly regular Clang) supports the “-framework” command line option for linking to frameworks. The operating system itself understands frameworks and app bundles.

The reason you need to specifically do #include <SDL2/SDL.h> rather than <SDL.h> or ”SDL.h when using SDL in a framework is because the SDL2 part is telling the compiler to look for SDL.h in a framework named “SDL2” (or a folder in the header search path named “SDL2”; I don’t know offhand which takes precedence if both exist)

One way to figure this out would be to follow the instructions from my earlier posts to create a test project and see if THAT works. And if so you can go through and see what differences exist between the test project and your project’s build settings.

So I’m running into an odd problem.

  • I’ve added the SDL2.framework to the same folder as my .xcodeproj project.
  • The SDL2.framework folder was added as a framework to the Xcode project via “Frameworks and Libraries”, + , Add other
  • I’ve ensured that the Framework Search Paths under Build Settings include $(PROJECT_DIR)

And the project compiles, links, and runs.

But if I change any of the source, I then bump into this error:

'SDL2/SDL.h' file not found

Did not find header 'SDL.h' in framework 'SDL2' (loaded from '/Users/{myname}/Library/Developer/Xcode/DerivedData/SDL-fiyrqbgsaxquumetfxdnlpqvyeal/Index.noindex/Build/Products/Debug')

If I do a clean build then it once again compiles, links, runs.

Any thoughts?

Now the issue is reduced to an Xcode bug. The replication details, starting from a “from scratch”, Apple-supplied, “one-liner” (“Hello World”) as follows:

  1. Start new project in Xcode, selecting “MacOS”, “Command Line Tool”.

  2. Product Name:FrameWorkTest, Team:None, Organization Identifier:frameworktest, Language:C.

  3. Create the project in a new folder. with the given (default) settings.

  4. hit Apple-R… yay… it compiles and runs!

  5. add this line under the #include <stdio.h>

#include "SDL2/SDL.h"

Now “main.c” source is showing “file not found” against this new #include line. That’s expected.

  1. Select FrameWorkTest from Navigator Pane (left hand side)

  2. Select “General” tab (middle pane)

  3. Finder drag “SDL2.framework” into Xcode’s middle-pane “Frameworks and Libraries” section (check the navigator pane (left pane) and now you have a “Frameworks” group containing “SDL2.framework”

After a few seconds the “file not found” indicator in “main.c” vanishes and the program is compilable (Apple+B). The Framework has been integrated (Hooray).

After a few seconds the “file not found” returns, though the program is still compilable (this seems to be a real-time parser mismatch with the compiler results, but not the main issue). I found the program is actually runable (Apple+R), probably because “main.c” has not been recompiled.

  1. Force a recompile by adding a space to “main.c” (and remove the space) and, once again hit Apple+R.

Now compile fails because “SDL2/SDL.h” is not found.

  1. Change the
#include "SDL2/SDL.h"

to

#include <SDL2/SDL.h>

“file not found” goes away after “Apple+R” forces a recompile. though now the runtime “abort” issue is present. Xcode is seriously confused at this point as putting double-quote version:

#include "SDL2/SDL.h"

back is fine now (no longer “file not found”) even though the source is back to a failing state. Fine. now to fix the runtime “abort” issue:

  1. Select “file view” in the Navigator pane (little folder icon). Select Project name, (topmost “FrameWorkTest” to show settings in middle pane). Select “Build Phases” tab.

  2. Drag “SDL2.framework” from the “Frameworks” group in the project Navigator pane (leftmost pane) into “Embed Frameworks” (middle pane)

  3. Now Apple+R will not compile (again) with “file not found” and I cannot get the project to compile and run without my “Trick” detailed above. Once the “Trick” is applied and the “file not found” goes away, the embed step (#12) lets the program compile and run - without “abort”.

Apple’s prepending of framework name to included files is inconsistent and faulty. The step 12 appears to undo the framework name mapping for #included files found in the framework. This step-by-step repeatable bug crib is not fixed by a “Product->Clean Build Folder…”.

Note 1: If step 12 is applied by doing a Finder drag and not a pane-to-pane drag, a (pointless) second Framework entry is introduced into the project, this confuses things further!

CLUDGE WARNING
Note 2: To save you from hunting back for, my “Trick” that gets around the “file not found” issue (total cludge but awaiting Apple’s real fix) is:

Phoneposting again (I’ll take a closer look tomorrow when I’m back at a computer)

The first thing that jumps out is you’re building a command-line application instead of a regular macOS app (aka a Cocoa App)

You need to start the project as a regular macOS app. You can’t embed a framework in a Command-Line App, because that’s just building a raw binary.

Choosing the App template instead of Command-Line App tells Xcode to build the full application bundle, which can have various things embedded (frameworks, your game’s assets, etc).

Tomorrow I’ll post a basic macOS SDL2 project, and the steps I used to create it.

I’ve been using frameworks in my own CLI C programs for years (SDL2 as well). The App template does not allow anything other than Swift or Objective-C as a language (perhaps as a C wrapper).

Way too much baggage for a plain ol’ C programmer!