-
Notifications
You must be signed in to change notification settings - Fork 8
Creating macOS bundles
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.
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.
To bundle the dylibs we will use a 3rd party application macdylibbundler. You can install it via homebrew
:
brew install dylibbundler
brew link dylibbundler
Now, go back to the directory that contains App.app
and issue these commands:
On Intel Macs:
export DYLD_LIBRARY_PATH=/usr/local/opt/allegro/lib
On Apple silicon:
export DYLD_LIBRARY_PATH=/opt/homebrew/lib
And then:
dylibbundler -x App.app/Contents/MacOS/app -b -d App.app/Contents/MacOS -p @executable_path -s $DYLD_LIBRARY_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!).
In some cases, dylibbundler
will stop with an error when any files contain "extended attributes". Extended attributes are metadata that could potentially include user info, like when a file was previewed last, etc. These are added by macOS even when viewing the folder in Finder.
Luckily, you can easily clean out any extended attributes with xattr
.
To just view all the extended attributes that are present in your app package, use the following command:
xattr -lr App.app
(Where App.app
is the name of your app.)
To remove extended attributes from all of the files in one swoop, you can use the following command:
xattr -cr App.app
And that's it! For more info about using xattr
to remove extended attributes, you can view the documentation here.
As you saw, data is not be placed next to your executable in the bundle, but the bitmap is still loaded. How is this done? Allegro will automatically detect that it is running inside a bundle, and change the working directory to the Resource
directory. There's nothing that needs to be done here, but it is something to keep in mind.
That's it! Now you should be able to send your App.app
to your friends and it should work.