Deploy Issues on MacOS
Thursday, July 18, 2024
While trying to help get the BitGuessr game deployed on macOS, I ran into a few issues that were interesting, and I wanted to discuss the process of troubleshooting them.
Sometimes, using the deploy
tool
is easy, and sometimes it is not-so-easy. There are some challenges around
choosing the right level of reflection for the features used in the
application you are trying to deploy – we suggest starting with Full environment
and then reducing until the program breaks – but besides that
it is typically one command:
IN: scratchpad "bitguessr" deploy
That command results in a per-platform executable, which on
macOS is an .app
bundle that includes the
Factor executable, a deployed image, any resources the deployed image uses, and
any libraries that the deployed image depends on:
$ find bitguessr.app
bitguessr.app
bitguessr.app/Contents
bitguessr.app/Contents/Frameworks
bitguessr.app/Contents/Frameworks/libraylib.dylib
bitguessr.app/Contents/Info.plist
bitguessr.app/Contents/MacOS
bitguessr.app/Contents/MacOS/bitguessr
bitguessr.app/Contents/Resources
bitguessr.app/Contents/Resources/bitguessr
bitguessr.app/Contents/Resources/bitguessr/_resources
bitguessr.app/Contents/Resources/bitguessr/_resources/bitguessr_icon.png
bitguessr.app/Contents/Resources/bitguessr/_resources/bitguessr_soundtrack.wav
bitguessr.app/Contents/Resources/bitguessr/_resources/button-0.png
bitguessr.app/Contents/Resources/bitguessr/_resources/button-1.png
bitguessr.app/Contents/Resources/bitguessr/_resources/correct.wav
bitguessr.app/Contents/Resources/bitguessr/_resources/wrong.wav
bitguessr.app/Contents/Resources/bitguessr.image
bitguessr.app/Contents/Resources/Icon.icns
After building this application, checking that it works for me, and uploading it to the server, of course we got a bug report when someone else tried to run it:
$ ./bitguessr.app/Contents/MacOS/bitguessr
...
Cannot resolve C library function
Library: DLL" libraylib.dylib"
Symbol: InitWindow
DlError: none
See https://concatenative.org/wiki/view/Factor/Requirements
Is the InitWindow
symbol in the library?
$ nm -gU ./bitguessr.app/Contents/Frameworks/libraylib.dylib | grep InitWindow
0000000000017920 T _InitWindow
Yes, it is.
Is it loading the correct libraylib.dylib
file?
$ DYLD_PRINT_LIBRARIES=1 ./bitguessr.app/Contents/MacOS/bitguessr
...
dyld[69951]: <B5534AF8-58E9-3F59-A5DE-F33164570F6B> ./bitguessr.app/Contents/Frameworks/libraylib.dylib
Yes, it seems to be.
Let’s learn more about how dynamic libraries work. There is a nice thread on dynamic library identification that goes into some details about how these are identified and then loaded.
Let’s start with the library – we get our Raylib from Homebrew:
$ cd $(brew --prefix raylib)
$ otool -l libraylib.dylib | grep -A 2 LC_ID_DYLIB
cmd LC_ID_DYLIB
cmdsize 72
name /usr/local/opt/raylib/lib/libraylib.450.dylib (offset 24)
Okay, so this probably needs to be relative to a “runtime path” or rpath
,
which you can either set:
$ install_name_tool -id "@rpath/libraylib.dylib" libraylib.dylib
Or, fix by downloading a Raylib release that is already set properly for embedding.
Did it change?
$ otool -l libraylib.dylib | grep -A 2 LC_ID_DYLIB
cmd LC_ID_DYLIB
cmdsize 48
name @rpath/libraylib.dylib (offset 24)
Yes, it did!
Now that we have that, we can re-deploy and see if it works:
$ ./bitguessr.app/Contents/MacOS/bitguessr
...
Cannot resolve C library function
Library: DLL" libraylib.dylib"
Symbol: InitWindow
DlError: none
See https://concatenative.org/wiki/view/Factor/Requirements
Nope.
Okay, maybe the rpath
that is used to lookup dynamic libraries isn’t set
properly:
$ otool -l ./bitguessr.app/Contents/MacOS/bitguessr| grep -A 2 LC_RPATH
Hmm, it is not set at all. The dynamic linker maintains a list of these “runtime path” directories. Maybe we can make sure it looks in the right place by adding one:
$ cd ./bitguessr.app/Contents/MacOS
$ install_name_tool -add_rpath "@executable_path/../Frameworks" bitguessr
Okay, now it looks right:
$ otool -l ./bitguessr.app/Contents/MacOS/bitguessr | grep -A 2 LC_RPATH
cmd LC_RPATH
cmdsize 48
path @executable_path/../Frameworks (offset 12)
Let’s try again… and, it works!
I pushed a change to set the rpath
directory
properly for future deploys, changed to distributing it as an Apple Disk
image, and also made sure to
codesign the
application
so that it launches easily after being downloaded and gave Joseph
Oziel an updated macOS build of
BitGuessr which included an
Icons.icns
file in the Apple Icon Image
format for the
application icon.
Neat!