text: After my recent Xcode learning experience, I thought I would see if I could accomplish the same things without it. It turns out that it’s pretty straightforward to use a plain old
  Makefile to create a nice double-clickable
  .app bundle.
  Makefiles are almost as old as Mac OS X’s UNIX underpinnings themselves, and pretty simple to use. The command line
  make tool checks the nearest Makefile for instructions, and then, well, makes the build happen.
  In it’s simplest form, you don’t need any Makefile at all. If you have a file called
  foo.c, typing
  make foo in the same directory will produce a binary called
  foo by compiling
  foo.c using your system’s C compiler (e.g. the one installed with Xcode). More sophisticated things, like creating a
  .app bundle, can be done by adding some variables and targets to the Makefile.
  Similar to last time, I have made a folder for this project.
  SDL2.framework is installed in my home directory under
  ~/Library/Frameworks/, and Xcode and OS X are up to date.
  Step 1: Create our main.c
  We’ll use the same code as last time, at least to start. Save this as
  main.c (or whatever you like, but remember to change later directions accordingly).
  #include <SDL2/SDL.h> int main(int argc, const char * argv[]){ if(SDL_Init(SDL_INIT_EVERYTHING) != 0) { puts("SDL_Init error"); return -1; } else { puts("SDL_Init success!"); return 0; } }
  What’s in an .app?
  That which we call a program, by any other name would smell as sweet…
  App bundles are a clever way of moving executables and their associated resources around while convincing the user that they’re one big file. Really, they’re just folders with names ending in
  .app, and a few special folders and files that OS X looks for. They are:
  Contents/Info.plist—- Tells Finder and friends what kind of bundle this is and how to use it.
  Contents/Resources/—- Resources needed by the app. Notably, frameworks (a Mac OS X specific way to package libraries) live here
  Contents/MacOS/—- we’ll be putting our executable in this folder
  We know where to find the SDL2 framework, and we will build our binary shortly, but what about
  Info.plist? We’ll have to steal one from an existing app and modify it. Right- or Control-click on an existing app (say, TextEdit) and choose “Show Package Contents”. Inside the app, you should see an
  Info.plist. Open it in your favorite text editor and have a look. (What the hell is all that?, you’re probably thinking.)
  Fortunately, it turns out that we can remove most of those properties and still have an app bundle that works. Here’s the template I came up with based on the minimal app from last time with Xcode:
  <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" ""> <plist version="1.0"> <dict> <key>CFBundleExecutable</key> <string>APP_NAME</string> <key>CFBundleIdentifier</key> <string>com.your-name.APP_NAME</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> <string>APP_NAME</string> <key>CFBundlePackageType</key> <string>APPL</string> </dict> </plist>
  Fill in
  your-name appropriately, then save this as
  Info.plist in the same directory as your
  Makefile.
  Step 2: Building a Bundle
  Before we automate this with a Makefile, it’s good practice to make sure you can put it all together in the terminal. After all, make just runs your commands!
  I’m calling my application SDLExample2 (original, I know). I’m putting it in a
  build directory just to keep things organized. (This way I can ignore the whole folder with
  .gitignore, no matter what build products I eventually have.)
  Make the .app folder and essential subfolders
  mkdir -p ./build/SDLExample2.app/Contents/{MacOS,Resources}
  Compile
  main.cto make an executable for the bundle
  cc -F "$HOME/Library/Frameworks" -framework SDL2 main.c -o main
  As you can probably guess,
  -Fspecifies a folder with frameworks in it.
  -frameworkindicates the name of a framework to link with. If you run it in the terminal you should see something like:
  $ ./main SDL_Init success!
  Copy the resulting binary into place
  cp ./main ./build/SDLExample2.app/Contents/MacOS/SDLExample2
  Copy the SDL2 framework into
  Resources
  cp -R "$HOME/Library/Frameworks/SDL2.framework" ./build/SDLExample2.app/Contents/Resources/
  Copy in the Info.plist file…
  cp Info.plist ./build/SDLExample2.app/Contents/
  …and edit it with your favorite text editor. It should look like this, unless you’ve named the app something other than
  SDLExample2:
  <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" ""> <plist version="1.0"> <dict> <key>CFBundleExecutable</key> <string>SDLExample2</string> <key>CFBundleIdentifier</key> <string>com.joseph-long.SDLExample2</string> <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> <key>CFBundleName</key> <string>SDLExample2</string> <key>CFBundlePackageType</key> <string>APPL</string> </dict> </plist>
  Now you should have an app! An app that doesn’t do much! Double click the SDLExample2 icon in the
  build folder and see. If you don’t get any error messages about corrupted or incomplete applications, you’re good to go.
  Step 3: Make the computer build you a bundle
  I was afraid of make and Makefiles for a long time, but I eventually figured out that my fear was from seeing Makefiles made to do things they really ought not to do. It’s not a very good language in which to write scripts!
  For simple tasks, however, they are more than adequate. Here is my Makefile to automate everything we did in Step 2.
  FRAMEWORK_PATH=$(HOME)/Library/Frameworks APP_NAME=SDLExample2 CFLAGS=-F $(FRAMEWORK_PATH) -framework SDL2 all: main clean_app package_app clean_app: rm -rf "./build/$(APP_NAME).app/" package_app: mkdir -p "./build/$(APP_NAME).app"/Contents/{MacOS,Resources} cp -R "$(FRAMEWORK_PATH)/SDL2.framework" "./build/$(APP_NAME).app/Contents/Resources/" cp Info.plist "./build/$(APP_NAME).app/Contents/" sed -e "s/APP_NAME/$(APP_NAME)/g" -i "" "./build/$(APP_NAME).app/Contents/Info.plist" cp ./main "./build/$(APP_NAME).app/Contents/MacOS/$(APP_NAME)"
  (n.b. These should be actual tab characters indenting lines in the Makefile, not spaces.)
  The Makefile begins with some variable definitions of the form
  FOO=bar. Including the value of one variable in another is done with the
  $(FOO) construct. (We don’t quote variables defined in Makefiles, as the quotes are interpreted literally.)
  CFLAGS is important, since it sets up extra arguments to be passed to the C compiler (like the ones in step 2.2).
  The
  all target is special, as it is built (or run) when
  make is invoked without target names. Here we tell it to “build
  main, remove any existing app bundle, and build the app bundle again from scratch”.
  The
  clean_app target just removes the entire bundle folder.
  The
  package_app target is basically the commands we did, one by one, in Step 2. The only thing different here is the use of
  sed‘s in-place-editing mode (
  -i "") to replace
  APP_NAME with our app name defined in the beginning of the Makefile.
  Also, note that we have quotes around arguments that include
  $(APP_NAME). This is so you won’t confuse the shell if you set
  APP_NAME to something with spaces in it (e.g.
  APP_NAME=SDL Example 2).
  Making
  make make
  Remove the
  main binary and app bundle, if they exist, then run
  make with no arguments. You should see something like this:
  # make cc -F /Users/josephoenix/Library/Frameworks -framework SDL2 main.c -o main rm -rf "./build/SDLExample2.app/" mkdir -p "./build/SDLExample2.app"/Contents/{MacOS,Resources} cp -R "/Users/josephoenix/Library/Frameworks/SDL2.framework" "./build/SDLExample2.app/Contents/Resources/" cp Info.plist "./build/SDLExample2.app/Contents/" sed -e "s/APP_NAME/SDLExample2/g" -i "" "./build/SDLExample2.app/Contents/Info.plist" cp ./main "./build/SDLExample2.app/Contents/MacOS/SDLExample2"
  Look in the
  ./build/ directory. Do you see an app bundle?
  You don’t need Xcode to build an app bundle after all!
  I know I should check my shell-based privilege here, but this took me all of one flight to figure out. (It was a short flight too! ATL to BWI.) On the other hand, I futzed around with Xcode for an entire evening to write that other post.