Skip to content

Creating macOS bundles

Doug Thompson edited this page Aug 8, 2018 · 1 revision

Initial set up

In this tutorial we will show you how to manually bundle your Allegro macOS app for distribution to other people.

We will use this simple program to illustrate the main points:

#include <allegro5/allegro.h>
#include <allegro5/allegro_image.h>
#include <allegro5/allegro_native_dialog.h>

int main()
{
   if (!al_init()) {
      al_show_native_message_box(NULL, "Error", "Error", "Could not init Allegro!", NULL, 0);
   }
   al_init_image_addon();
   ALLEGRO_DISPLAY* disp = al_create_display(800, 600);
   if (!disp) {
      al_show_native_message_box(NULL, "Error", "Error", "Could not create display!", NULL, 0);
   }
   ALLEGRO_BITMAP* bmp = al_load_bitmap("mysha.pcx");
   if (!bmp) {
      al_show_native_message_box(NULL, "Error", "Error", "Could not load bitmap!", NULL, 0);
   }

   al_clear_to_color(al_map_rgb(0, 0, 0));
   al_draw_bitmap(bmp, 0, 0, 0);
   al_flip_display();

   al_rest(5.0);
   return 0;
}

Save that to app.c and grab a copy of mysha.pcx from Allegro's repository (its in examples/data directory).

Grab Allegro via homebrew:

brew install allegro

And compile and run the program above. It should display Mysha for a few seconds.

gcc -o app app.c -lallegro -lallegro_image -lallegro_dialog -lallegro_main
./app

If you try distributing this app to other computers you will most likely discover that it will not work because it depends on Allegro dylibs that are only installed on your computer. Of course you could get people to use homebrew to install them, but we can do better. We will create a macOS bundle which will encompass your binary, its data and all the shared libraries it uses.

Fixing the working directory

The first step we need to do is adjust where your app will look for its data files (just mysha.pcx in this example). Unlike how we have things now, things will not be placed next to your executable in the bundle (see below for the bundle layout). The easiest way to fix this is to adjust the working directory so that things work as if they were however. Conveniently, Allegro provides a function query this path. Simply add this line after al_init:

chdir(al_path_cstr(al_get_standard_path(ALLEGRO_RESOURCES_PATH), '/'));

and add this header include up top:

#include <unistd.h>

Compile the program again now, it should continue to work (and in fact this change is cross-platform, as ALLEGRO_RESOURCES_PATH points to the directory where the executable is). Note that using chdir is a bit of hack, if you want to be super-proper, you probably want to use absolute paths that start with ALLEGRO_RESOURCES_PATH.

Setting up the bundle directory structure and Info.plist.

Now, create a directory called App.app with the following structure:

App.app
└── Contents
    ├── Info.plist
    ├── MacOS
    │   └── app
    └── Resources
        └── mysha.pcx

Where app is your compiled binary, mysha.pcx is the image from before and Info.plist is a text file with these contents:

<?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>Test Allegro APP</string>
  <key>CFBundleExecutable</key>
  <string>app</string>
  <key>CFBundleIdentifier</key>
  <string>org.liballeg</string>
  <key>CFBundleName</key>
  <string>App</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>
  <key>NSHighResolutionCapable</key>
  <true/>
</dict>
</plist>

This configuration file describes your app, specifies various metadata etc. It's pretty self-explanatory. The last setting (NSHighResolutionCapable) enables Allegro to gracefully handle high-DPI display. You can also specify an icon for your bundle in this file, but that's beyond the scope of this tutorial.

At this point you should be able to run your from the finder, but we're not done yet! We still need to figure out how to deal with the dylibs.

Bundling the dylibs

To bundle the dylibs we will use a 3rd party application macdylibbundler. Install it now.

Now, go back to the directory that contains App.app and issue these commands:

export DYLD_LIBRARY_PATH=/usr/local/opt/allegro/lib
dylibbundler -x App.app/Contents/MacOS/app -b -d App.app/Contents/MacOS -p @executable_path

After it is done, App.app should look something like this:

App.app
└── Contents
    ├── Info.plist
    ├── MacOS
    │   ├── app
    │   ├── liballegro.5.2.4.dylib
    │   ├── liballegro.5.2.dylib
    │   ├── liballegro_dialog.5.2.4.dylib
    │   ├── liballegro_image.5.2.4.dylib
    │   ├── liballegro_main.5.2.4.dylib
    │   └── libwebp.7.dylib
    └── Resources
        └── mysha.pcx

As you can see, dylibbundler copied the dylibs your app uses, but more importantly it adjusted their internal search paths to be able to find each other (otherwise we'd just manually copy things over... it's just not that easy on macOS!).

That's it! Now you should be able to send your App.app to your friends and it should work.

You can’t perform that action at this time.