A Wild Game Appears! Part 2


 

Ahoyo!

 

 

Today we’re going to go from this:

 

 

to this:

 

And what took me hours and hours to figure out should hopefully, hopefully only take you a few weird minutes to copy.

 

This post isn’t theory. It’s recipe. I barely understand what I’m doing in the OS X application configuring space, and my goal is just to pull you up to my bare level of understanding, while shouting at you loudly to avoid the stupid mistake I made.

 

So let’s start with that mistake. Don’t worry about setting the minimum version of your program. It’s all bunk. Don’t use variables like MACOSX_DEPLOYMENT_TARGET or mmacos-version-min, because they don’t actually matter if your underlying frameworks are higher version, and they will be. Oh, they will be. You’ll end up with something that you know plays on an old machine, but the OS will refuse to play it.

 

Instead, just use a launcher program, and figure out the minimum version yourself by trying it on some old macs. I ignored that advice and it cost me hours and hours.

 

Packaging an application, in the common case, is actually pretty simple, albeit pretty weird. We’ll be working from a really nice stack overflow answer that seems to have worked out pretty well. In fact, it has worked so well that we’re going to make a tool out of it in a later version of Honey.

 

But for now, the manual way.

 

In order to present regular mac users a simple application, we need to do three (or four? six?) things:

 

1. Put our game inside a folder called ABearCs.app with a particular folder structure.
2. Make some icons.
3. Add our frameworks.
4. Add a little launcher.
5. Write a config file.
6. Use a truly bizarre little hacky tool to point our executable at the frameworks.

 

Cool, six things. Pretty much the only sensible thing is making icons.

 

 

Sigh. Weird thing 1.

 

Make a folder in ABearCs called Release. Inside this, make another folder called ABearCs. This is where we’re going to put our single application. It’s the last sensible thing we’ll do. Inside that folder, make another folder called ABearCs. Your folders should now look like this:

 

 

That last folder looks like a folder now, but later we’re going to add “.app” to the name, and it’s going to turn into a single application, with a pretty icon and everything.

 

Mac applications have a specific structure, so inside that app folder, we need to create a bunch more folders. Create one called Contents, and then inside it, create three more, called MacOS, Frameworks, and Resources. Your folders should now look like this mess:

 

 

Now we just need to put the game in there. From the top level, copy the ABearCs executable, config.txt, Art folder, and Sound folder to MacOS. Your folders should now look like this:

 

 

So that’s weird thing 1 done: we’ve put our actual program in a set of folders that conform to a very particular format.

 

 

Sigh. Weird thing 2.

 

 

I lied. Icons aren’t that sensible. In order to make an icon package, at bare minimum we’re going to have to take one large file (512×512 is ideal) and make it into five files in a weirdly named folder, like this:

 

 

Those names, specifically. You need those names, and each file needs to be the size indicated by the name. One way to do this is to open your image editor (eg, www.adobe.com/Illustrator‎ or Gimp) and manually shrink and save new copies several times.

 

Another way is to use the command line sips tool, running:

 

sips -z 256 256 icon_512x512.png --out icon_256x256.png
sips -z 128 128 icon_512x512.png --out icon_128x128.png
sips -z 32 32 icon_512x512.png --out icon_32x32.png
sips -z 16 16 icon_512x512.png --out icon_16x16.png

 

When you have your folder looking like the picture above, go to the command line and run:

iconutil -c icns -o ABearCs.icns ABearCs.iconset

This creates the iconset file that OS X needs to display icons. It’s just a collection of different sized pictures the system can use to scale icons.

 

 

Actually thing 3 makes sense.

 

 

When we compiled ABearCs last time with our SConstruct file, we specified frameworks. OpenGL we don’t need to worry about; every Mac has this. But the SDL frameworks aren’t on every Mac, aren’t even on most Macs. We need to copy these frameworks into the application.

 

Go to /Library/Frameworks and copy SDL2.framework, SDL2_image.framework, and SDL2_mixer.framework into the Frameworks folder of our weird construction. Your folders should now look like this:

 

 

That’s that done.

 

 

Back to weird things. Weird thing 4.

 

 

We could configure the application to call our executable ABearCs program directly, but if we do, the operating system will check which versions of OS X are allowed to run it, and for stupid reasons, it’s difficult to get our program to build without making itself require our (ie, the latest or almost latest) version of OS X. We could do it in XCode (gag), or by downloading a compatibility SDK (ugh).

 

Or, we could just make a little launcher program, version free, and have the application call the launcher, and have the launcher call your program. It’s crazy, but it works.

 

It’s also the case that the working directory of a Mac application is wherever the application is located (usually your Applications directory), and not the MacOS folder where we were required to put our actual program. That means all our local file loads (to config.txt, art files, and sound files) will break. We could write an operating system dependent method in our program which changes the working directory on OS X, meaning a lot more gross OS code.

 

Or we could just make a little launcher program. We can change the directory here before calling the actual game. Yeah, let’s do that.

 

In the MacOS folder, make a little file called launch, and chmod +x to give it executable permission. Add this shell script to the file:

 


#!/bin/bash
cd "${0%/*}"
./ABearCs

 

This changes to its own directory (HA!) and runs ABearCs. So that’s done.

 

 

Sigh. Weird thing 5.

 

 

If it’s not already crystal clear, I’ll say it: I don’t think this is a good process.

 

Now we come to the configuration file, and you’d think that’s sensible. It is not. Our version is very small and will be almost sensible. But the world of Mac application configurations is generally not sensible.

 

Make a file called Info.plist inside Contents, and fill it with this:

 


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>CFBundleGetInfoString</key>
  <string>ABearCs</string>
  <key>CFBundleExecutable</key>
  <string>launch</string>
  <key>CFBundleIdentifier</key>
  <string>FriendsOnMountains</string>
  <key>CFBundleName</key>
  <string>ABearCs</string>
  <key>CFBundleIconFile</key>
  <string>ABearCs.icns</string>
  <key>CFBundleShortVersionString</key>
  <string>0.01</string>
  <key>CFBundleInfoDictionaryVersion</key>
  <string>6.0</string>
  <key>CFBundlePackageType</key>
  <string>APPL</string>
  <key>IFMajorVersion</key>
  <integer>0</integer>
  <key>IFMinorVersion</key>
  <integer>1</integer>
</dict>
</plist>

 

The important things hee are the name (ABearCs), the IconFile (ABearCs.icns, which by default gets fetched from the resources directory), and the Executable (launch, which by default gets fetched from the MacOS directory). Don’t think too hard about anything else, or else go here and here knock yourself out.

 

 

Finally! Weird thing 6!

 

 

This one’s really weird. We’ve put our frameworks in a Frameworks directory, but we haven’t hooked them up to the program. Our program was compiled to search in /Library/Frameworks (or somewhere similar, somewhere outside our application) for those frameworks. We need to modify it to search inside the application.

 

Do we do that with a config setting? Oh no. No, we use a special tool to modify the executable itself.

 

In a terminal, navigate to the MacOS folder (ie, ABearCs/Release/ABearCs/ABearCs/Contents/MacOS). Run this command: otool -L ABearCs

 

You should see output roughly like this:

 


/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1443.13.0)
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
@rpath/SDL2.framework/Versions/A/SDL2 (compatibility version 1.0.0, current version 7.0.0)
@rpath/SDL2_image.framework/Versions/A/SDL2_image (compatibility version 1.0.0, current version 1.1.0)
@rpath/SDL2_mixer.framework/Versions/A/SDL2_mixer (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.0.0)

 

Those @rpaths are wrong; we’re going to change them to use @executable_path. Run the following three commands:

 

install_name_tool -change @rpath/SDL2.framework/Versions/A/SDL2 @executable_path/../Frameworks/SDL2.framework/Versions/A/SDL2 ABearCs

install_name_tool -change @rpath/SDL2_image.framework/Versions/A/SDL2_image @executable_path/../Frameworks/SDL2_image.framework/Versions/A/SDL2_image ABearCs

install_name_tool -change @rpath/SDL2_mixer.framework/Versions/A/SDL2_mixer @executable_path/../Frameworks/SDL2_mixer.framework/Versions/A/SDL2_mixer ABearCs

 

Now run otool -L ABearCs again. This time you should see:

 

/System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1443.13.0)
/System/Library/Frameworks/OpenGL.framework/Versions/A/OpenGL (compatibility version 1.0.0, current version 1.0.0)
@executable_path/../Frameworks/SDL2.framework/Versions/A/SDL2 (compatibility version 1.0.0, current version 7.0.0)
@executable_path/../Frameworks/SDL2_image.framework/Versions/A/SDL2_image (compatibility version 1.0.0, current version 1.1.0)
@executable_path/../Frameworks/SDL2_mixer.framework/Versions/A/SDL2_mixer (compatibility version 1.0.0, current version 1.0.0)
/usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.0.0)

 

Now the executable points at the local frameworks, and everything should work.

 

 

Now we have an app! Let’s package it up.

 

So your folders should look like this:

 

 

Rename the ABearCs folder (the one just above Contents, not the one just below Release) to ABearCs.app, and this magic should happen:

 

 

Yay! It’s a single application. If you double click it, it should run like any other. You can copy it to Applications, you can add it to the dock, everything.

 

Let’s wrap it up as a dmg. Open Disk Utility, and go to File->New Image->Image from Folder. Choose the outer ABearCs (the one just below Release). Save as ABearCs, inside the release folder. It should make you a dmg. Your folders, now in their final form, should look like this:

 

 

And we’re done with this dumb process. Let’s blow this popsicle stand.

 

To reward myself, I made a little landing page for this little game.

 

Next time we’ll return to writing new features for Honey. Yay!

 

Leave a Reply

Your email address will not be published. Required fields are marked *