Skip to content

Compiling Frogatto on Mac OS X

Richard Kettering edited this page Feb 17, 2023 · 3 revisions

This is a legacy document, and you should look at this, instead:

https://github.com/anura-engine/anura/blob/trunk/MacOS/README.md


Legacy document follows:

(Current as of 10.11 "El Capitan")

Assumes unix knowledge, does not assume knowledge of mac programming esoterica.

First, you'll need to download our source tree from git. Once you have it, it'll have several key folders that need to get put into XCode. All of the direct resources/etc needed by frogatto are in our source tree, the one category of items that are not provided are our library/framework dependencies. Instructions on how and where to get these are provided later.

Theory:

Frogatto is an SDL-based app, and importantly uses SDL to do event handling. Because carbon is strongly deprecated, Frogatto is a cocoa app. SDL event handling is designed around creating and running your own event loop. This flies in the face of how cocoa is supposed to work, because cocoa is neither supposed to relinquish event handling to you, nor to work via event loops; rather than running your own event loop (which is generally bad because it's polling, and eats processing time), in cocoa you're supposed to just provide a bunch of handler functions for whichever events you want to process, and not have a loop at all.

The way we resolve this is that when a cocoa app is launched, it will always fire a method named applicationDidFinishLaunching. What we do, is hijack the program inside that function; we start our event loop there, and keep that function from exiting until we're done with everything.

Creating the XCode Project:

Select the "cocoa application" template, and create a new project. You probably will want to put this somewhere inside your downloaded git repo; I put it in a folder named "MacOS".

XCode4 has a side-bar on the left which can be set to several modes, one of which (the one indicated by the folder icon) is a "file management" mode. If you select that mode, you'll see a folder tree containing the files our XCode project knows about.

TODO: check naming/list-of-files accuracy on this: There are several files in the resulting template that can be safely deleted. There will be a ProgramNameAppDelegate.m/h pair which we will not be using at all, a main.m file which will be replaced by one provided by SDL (namely SDLMain.m/h), and at least one file in the subfolder "resources", "*info.plist, which should be replaced by ours. There is also an "xib" file containing UI elements (such as the contents of our menus) that will be replaced by our SDLMain.nib

Source code and data files:

There are 4 folders of "data files", in our source tree, which XCode does nothing special with other than copying them into your app bundle. These are data, images, music, and sounds. Drag these into the aforementioned "file manager" left-hand sidebar in XCode. You'll be prompted to choose whether these should be added as folder references, or group references in XCode; the correct choice is folder reference. The difference between the two is that folders automatically update their contents if you add new things outside of XCode; as a result, they're great for data files, and not so great for source code files, because you cannot remove a source code file from your list of files to be built, without actually removing it from that directory on-disk. Most makefile-style build systems assume the programmer manually manages which files do and don't get built, and the files which are/aren't to be built are often lumped together in the same directory. In fact it's possible XCode won't even let you build source files in a folder reference. I haven't tried.

The fifth folder to be dragged in is src, which as per the above, should be made a "group reference".

Lastly, there will be a few files which are only for the mac build; these should be dragged into Frogatto/Resources in the sidebar. They are: Info.plist, icon.icns, and SDLMain.nib

Some of the source code files need to be removed for a regular frogatto build. TODO: list these.

  • load_level.cpp has a threaded variant, and a non-threaded; you'll need to pick one. ATM the threaded one doesn't play nicely on windows.

The SDLMain.m file needs to be turned from an Objective-C file, into an Objective-C++ file, or it's inclusion of some C++ header files won't work. There is a right-hand-sidebar that can be activated in XCode which allows this. Alternatively, the file can be renamed to have the extension "mm" rather than "m", which causes XCode to treat it by default as an Objective-C++ file. (This may be a good idea for our core team to do at some point, and this instruction will probably be preserved even if we do that, because knowing this is a bit obscure).

Some of the files need special settings changed for them. At the top of the file-browser tab of the sidebar is a major item: an icon of a blue "XCode" document, named after your project (in this case, presumably "frogatto"), and with a subtext like "1 target, Mac OS X SDK 10.7". This icon is your access to changing the build settings for the project, including the settings for header search paths, and copying in libraries, which we'll need to set later. You will need to amend the setting in the "target" section which gives a path to info.plist to point to our particular info.plist file. All paths like this are relative to the location of the XCode project file on disk, so this particular item will likely be resources/Info.plist

Acquiring Libraries/Frameworks and Setting Header Paths

XCode needs two things for our libraries; it needs the libraries themselves, which are in machine-code form (ending in .dylib, .a, or .framework); we tell XCode about these by dragging them into the project. It also needs header files, which describe what code is available in said frameworks; we tell XCode about these by supplying a "path" for each set, in the "build settings" (this will be a single line of text, with spaces separating each item. I will describe this for each item, and give a final result which might need amendments based on where you put your headers). Because the machine code for these libraries will likely be built by you, to be in the architectures you want, it's probably convenient for you to have both the source and built products of these inside the same folder as your project file.

Here's a quick list of the libraries we need:

  • OpenGL.framework
  • SDL.framework
  • SDL_mixer.framework
  • SDL_ttf.framework
  • SDL_image.framework
  • libboost_regex.dylib
  • libboost_iostreams.dylib
  • libboost_system.dylib
  • libboost_filesystem.dylib
  • libGLEW.1.6.0.dylib
  • GLM (OpenGL Mathematics)

OpenGL.framework

This file is already on your mac. Find it in /System/Library/Frameworks, and drag it into your file-browser sidebar in XCode, under Frameworks. You may wish to group it with AppKit, CoreData, and Foundation, and separate them from the non-apple frameworks; this can be done by selecting several items, and using the right-click menu item "New Group from Selection".

As described later, the header search path for this will just be .; i.e. the same folder as the project file.

SDL.framework, SDL_mixer.framework, SDL_ttf.framework, SDL_image.framework

Acquire these files from http://www.libsdl.org/download-1.2.php These will be pre-built by SDL as ready-made frameworks. At the time of this writing, you want SDL 1.2.14. Download these, and place them at the directory /Library/Frameworks/ (Note: don't confuse this with /System/Library/Frameworks/ - anything under /System/ should never be modified by a user of a mac system; user-installed drivers/libraries/etc all go in subfolders of /Library/.

Once there, drag these from your /Library/Frameworks/ folder into XCode - into the Frameworks folder in the XCode sidebar.

The header search paths for SDL et al are: /Library/Frameworks/SDL.framework/Headers /Library/Frameworks/SDL_image.framework/Headers /Library/Frameworks/SDL_mixer.framework/Headers /Library/Frameworks/SDL_ttf.framework/Headers

libboost_regex.dylib, libboost_iostreams.dylib, libboost_system.dylib, libboost_filesystem.dylib

Acquire these files from http://www.boost.org/ The latest version as of this writing is 1.53.0. You will need to build these yourself; boost provides a tool to build dylibs of them, which despite being rather intimidating at first glance is actually very easy to use which they have instructions on how to use. It's really as simple as running a few commands in sequence; because boost is well-maintained, you can rely on their configuration scripts to actually work.

Boost is provided as a tarball/zip file containing a single folder full of code. The short version of the build instructions is to take this unpacked folder, and put it in the same folder as your project file. cd into that directory in a terminal, and then run ./bootstrap.sh --prefix="./boost" to put the resulting headers and dylibs in a new folder named "boost" next to your project file (you might have to manually create this empty folder, first). It's possible to customize which specific boost sub-libraries get built, but because "everything" takes only ~10-20min and 200mb of disk space, you can safely run ./b2 install without changing that.

Boost has an easy way to build "fat binaries" of your resulting dylibs, btw. Invoking ./b2 itself will build your libraries; you can pass specific options to it to choose which libraries get built, by what compiler, and how they get built. Passing the address-model=32_64 option will make it build both 32-bit intel binaries (i.e. i386) and 64-bit intel binaries (x86-64), each merged together into a single file. One gotcha is that you can't merely pass the address-model argument alone; you must also pass architecture=x86 in order for it to build a fat binary rather than just a binary for your system's native architecture (often x86-64 these days). PPC support at this point is likely unimportant, but architecture=combined will give you a 4-way fat binary featuring both 32 and 64-bit variants of both PPC and intel code. An example command, with options for building only specific libraries and with the above architecture options, is: ./b2 --with-regex --with-system --with-iostreams architecture=x86 address-model=32_64 install

You'll now have a boost folder with include and lib inside it. Inside lib, you'll find the 3 boost libraries we currently need. Drag these into the Frameworks folder in the XCode sidebar.

The header search path for boost will be that include folder: ./boost/include

To build boost with clang, the i386 and x86_64 binaries need to be built separately. ./b2 toolset=clang cxxflags="-std=c++11 -stdlib=libc++ -arch i386" linkflags="-stdlib=libc++ -arch i386" --with-filesystem --with-regex --with-system --with-iostreams architecture=x86 install ./b2 toolset=clang cxxflags="-std=c++11 -stdlib=libc++ -arch x86_64" linkflags="-stdlib=libc++ -arch x86_64" --with-filesystem --with-regex --with-system --with-iostreams architecture=x86 install

Refs: https://trac.lvk.cs.msu.su/boost.build/ticket/230 http://stackoverflow.com/questions/8486077/how-to-compile-link-boost-with-clang-libc http://stackoverflow.com/questions/2887707/how-to-build-boost-with-c0x-support

libGLEW.1.6.0.dylib

Acquire the source for this from http://glew.sourceforge.net/ You will need to build this yourself. The source will come in a zip/tgz file, once unpacked I suggest putting the upacked folder in the same folder, named "glew-1.6.0", into the same folder as your project file. Building this is just a matter of opening a terminal, changing the working dir to that "glew-1.6.0" folder, and running the command "make", but with one gotcha: two files in the package ("Makefile" and "config/config.guess") have windows-style line endings, which break their ability to run on a mac/unix system. These need to have their line endings changed to "unix" style; a one-line command to swap this is: sed -e 's/\r$//' inputfile > outputfile (this does dos to unix; more info here)

The result of that will be a library file at: "./glew-1.6.0/lib/libGLEW.1.6.0.dylib", which you'll want to drag into Xcode's "Frameworks" folder in the sidebar.

The header path for glew, based on the above, will be "./glew-1.6.0/include"

Note regarding 32/64-bit versions of e.g. libGLEW, libPNG, and other libraries built with make - make takes darwin/OSX specific arguments to build fat binaries. Passing the following will do so: make CXXFLAGS="-arch i386 -arch x86_64" CFLAGS="-arch i386 -arch x86_64" LDFLAGS="-arch i386 -arch x86_64" - note that one common failure (which I myself ran into) is this error: gcc-4.2: -E, -S, -save-temps and -M options are not allowed with multiple -arch flags. This can be fixed with: ./configure --disable-dependency-tracking

  • final notes: The final contents of your "Header Search Paths" string will be:

. ./dependency-headers /Library/Frameworks/SDL.framework/Headers /Library/Frameworks/SDL_image.framework/Headers /Library/Frameworks/SDL_mixer.framework/Headers /Library/Frameworks/SDL_ttf.framework/Headers ./boost/include ./glew-1.6.0/include

XCode should automatically populate the item next to that, named "Library Search Paths", when you drag in the libs.

GLM - OpenGL Mathematics

http://glm.g-truc.net/ OpenGL Mathematics (GLM) is a header only C++ mathematics library for graphics software based on the OpenGL Shading Language (GLSL) specification.

Packaging Libraries and Frameworks

  • Because macs store opengl in a different place than standard unix systems, one quick-and-dirty hack needs to be done to allow us to still use the #include "GL/gl.h" which is standard on other systems. Inside the same folder as the project file, make a symlink named GL which points to the header folder inside the apple-provided OpenGL.framework. This can be done with the following command, assuming you cd into the same folder as your project file, first: ln -s /System/Library/Frameworks/OpenGL.framework/Versions/A/Headers GL On 10.9 or later: ln -s /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.9.sdk/System/Library/Frameworks/OpenGL.framework/Versions/A/Headers/ GL

  • Mac applications are expected to include, inside the app bundle, any frameworks which aren't part of the standard OS distribution. This is very different from software distribution on linux, such as apt-get. Thus, we'll need to have our boost, glew, and SDL libraries/frameworks copied into our app bundle. XCode needs to be told that we want these copied in during our building of the program. Click on the "Frogatto" project icon at the top of XCode's left sidebar to get at the project settings, and inside the "Frogatto" target, you'll see a tab labeled "Build Phases". You'll need to add a new build phase of the type "Copy Files". Into this, you want to drag all of our SDL, boost, and GLEW dylibs/frameworks from the sidebar.

  • The dylib frameworks we've built include a path listing inside them, which tell the linker where the program is supposed to look for the dylib, once the program is built . Even though XCode can find these files just fine, and even knows where the final destination of them inside the packaged app is, this is a case where "the right hand knows not what the left hand does"; this knowledge is not passed on into the packaged app, and without doing this, the app will have the linked files inside it, but won't be able to find them.

To fix these, you'll need to use a few tools; "otool -L path" will list the path that the dylib was installed at, and any dependencies

"install_name_tool -change oldpath newpath currentfile" can be used to modify the path for a dependency; it will need to be applied separately for each dependency. The currentfile parameter refers to the dylib file that's actually being modified. An example of a library which has an internal dependency is libboost_filesystem; it depends on libboost_system. Its dependency to that was changed via: install_name_tool -change "libboost_system.dylib" "@executable_path/../Frameworks/libboost_system.dylib" libboost_filesystem.dylib

"install_name_tool -id path currentfile" can be used to change the individual path for the dylib itself. Naturally there is only one. You will want to change this, for each of our four dylibs, to point to the Frameworks folder inside the app bundle we'll create. With "original_file_name" being replaced by each library's name, the path to change these to is "@executable_path/../Frameworks/original_file_name.dylib"

For example, one of the commands I ran, after cding into the same folder as my dylibs, was: install_name_tool -id "@executable_path/../Frameworks/libboost_iostreams.dylib" libboost_iostreams.dylib

#Custom SDL_Image# Anura uses palette-shifting which is keyed on exact RGB values - this is used to recycle art inside the game by displaying it in very different color schemes as you progress through levels. This runs into a nasty snag with versions of SDL >1.2.14 (including both 1.2.15, and 2.x), because those on macs have started using the built-in ImageIO library as their backend to read PNG images, and nastily, this causes all images in the game to be gamma-shifted (this is going through ICC/Colorsync correction, really).

To solve this, we're apparently able to build our own copy of SDL_Image, flagged with -DSDL_IMAGE_USE_COMMON_BACKEND, which will avoid doing this colorshifting. To do this, we download SDL_Image's source code via hg clone http://hg.libsdl.org/SDL_image/ which will give us a directory containing, amongst other things, an XCode file which can build a viable replacement SDL2_Image.framework which we can replace the official prepackaged one with.

To do this, however, we need copies of three ingredient libraries it depends on: libpng, libtiff, and libjpeg (with libtiff depending on libjpeg). To get these, we use homebrew to automate downloading the source code, and building dylib files. Be warned that we want a specific set of options for these, though, or we'll end up building dylibs which are hilariously incompatible and not suitable for distribution. We need the option --universal to ensure the libraries are both 32 and 64-bit; we also need --build-bottle to ensure that they do not use special processor operations only available on newer intel chips (basically when you build without these options, it tends to use special vector instructions or so-forth that simply won't run on older machines - if you try to deploy with these, it will run fine on your own machine, but when copied to someone else's machine, the game will die with an error like EXEC_BAD_INSTRUCTION (SIGILL)).

The process for making one of these goes like this:

  1. run brew install --build-bottle --universal libpng
  2. This will install files at e.g. /usr/local/Cellar/libpng/1.6.10 One of these will be the libpng16.16.dylib that we want.
  3. Copy this into the SDL_Image XCode project's Frameworks folder in the finder.
  4. chmod 777 this file, since its default ownership is to some special user/group related to homebrew
  5. Fix the install paths on this file by first inspecting it via otool -L ./libpng16.16.dylib, and then...
  6. Change the id linker path via: install_name_tool -id "@rpath/../Frameworks/libpng16.16.dylib" libpng16.16.dylib
  7. TODO document the XCode project file changes necessary.

There are a few special notes here:

  • the homebrew names of these recipes are: libpng jpeg and libtiff.
  • Since libjpeg relies on libtiff, you'll need to build jpeg first.
  • Since libjpeg relies on libtiff, you'll also need to correct its relative path using install_name_tool -change "/usr/local/lib/libjpeg.8.dylib" "@rpath/../Frameworks/libjpeg.8.dylib" libtiff.5.dylib
  • e.g. brew uninstall libpng is a good way to delete a homebrew library which you need to rebuild with different options. It does not clear homebrew's recipe cache of the source code, though.

#Addendum# (some notes on paths I need to read up on: http://code.google.com/p/sidelight/wiki/RpathSaver http://www.mikeash.com/pyblog/friday-qa-2009-11-06-linking-and-install-names.html http://www.dribin.org/dave/blog/archives/2009/11/15/rpath/ )

Clone this wiki locally