Tuesday, April 9, 2019

Deploying SDL2 + GLEW Applications Using XCode Tutorial

Hey all,

So, in a sort of "Part 2" of what is now a series on SDL2 and XCode (first part here), I want to go over how to deploy an application that you made using SDL2 onto a store, like Steam of Itch.io.

The reason I'm writing this is for 3 reasons:
  1. So that I don't forget.
  2. I just finished A Sound Plan (Steam Itch) and had a real hard time getting this all together, and I don't want you, the lovely lovely reader, to suffer as I have.
  3. There are virtually no tutorials on how to do this, despite SDL2's popularity and the rising rate of apps being made for OSX.
With that all said, let's get started.

You've just finished your game, it's a masterpiece and runs great on Windows and Linux, and you think deploying to OSX will be easy. You quickly realize that your previous assumption was wrong. You realize that OSX support of OpenGL is complete garbage. You realize that XCode is kind of a bad IDE, and that it's a miracle that anyone can make anything using it.

Maybe I'm the only one who thought those last two sentences, but you get where I'm coming from: The deployment process for this OS is completely different.

The first thing you may notice about any app in OSX is that they are actually a lot like folders, which you can peek inside and see all the lovely details. You can see this by right clicking an app and selecting: "Show Package Contents".

One thing you'll notice that's different from Windows and Linux executables: local files are not stored next to the executable, but rather in this "Resources" folder, and all libraries are in this "Frameworks" folder. Why does Apple organize things in such a way? I don't know, I'm not them, what a stupid question.

How do we get our files into those folders? Through XCode we can set up our build process as shown:

Notice the libraries are linked AND copied over.

How do we reference those files? Fortunately that part is easy (See below). Any files copied into the Resources folder can be referenced by prepending the value stored in path in the example below.
Example: "stuff.txt" would be referenced as: path + "stuff.txt"

CFBundleRef mainBundle = CFBundleGetMainBundle();
CFURLRef resourcesURL = CFBundleCopyResourcesDirectoryURL(mainBundle);
char path[PATH_MAX];
if (!CFURLGetFileSystemRepresentation(resourcesURL, TRUE, (UInt8 *)path, PATH_MAX))
  assert(!"OSX resource directory not found.");
CFRelease(resourcesURL);
 
chdir(path);
mWorkingDirectory = path; // Or just use path however you want. Just prepend to your local file names.

Last, and certainly not least, you may notice that if you try to run your application on another Mac, the app won't run, missing a .dylib or something. The reason for this is because of the install names of the .dylibs you used, these need to be set manually.

In Xcode, highlight your project, select "Build Phases":

Pictured: Click this in Build Phases

Add Run Script, and add lines like:

install_name_tool -id @executable_path/../Frameworks/libGLEW.1.13.0.dylib "$TARGET_BUILD_DIR/$TARGET_NAME.app/Contents/Frameworks/libGLEW.1.13.0.dylib"
install_name_tool -change /opt/local/lib/libGLEW.1.13.0.dylib @executable_path/../Frameworks/libGLEW.1.13.0.dylib "$TARGET_BUILD_DIR/$TARGET_NAME.app/Contents/MacOS/$PRODUCT_NAME"
codesign --force --sign - --preserve-metadata=identifier,entitlements "$TARGET_BUILD_DIR/$TARGET_NAME.app/Contents/Frameworks/libGLEW.1.13.0.dylib"

An explanation lies here, but basically you are setting the id of your .dylib, and then telling OSX what the executable path is for that .dylib. Code signing is optional if you are deploying via Steam or Itch.

Note: The first parameter after "install_name_tool -change" needs to match the install path of the library, or this command will do nothing. A good way to find out the install paths of your app is to run "otool" (included in XCode command line tools) on your app from the terminal.

With that, you should have a working app for OSX. Now get your game on Steam so I can review it, harshly.

Jimmy