Porting to Mac/gtk-mac-bundler
This page contains some notes on how gtk-mac-bundler (formerly called ige-mac-bundler) (and maybe other tools) do the job of making portable, self-contained GTK+ applications work on Mac. The notes were prepared during the course of porting ASCEND to Mac. Please feel free to set me straight on any wrong observations, missing information, etc.
The following observations come from bundling up GtkDemo using ige-mac-bundler.
GTK libraries are copied into the Contents/Resources/lib directory in the application bundle. Their 'install names' are changed so that they look like:
$ otool -L libgtk-quartz-2.0.0.dylib libgtk-quartz-2.0.0.dylib: /Users/john/gtk/inst/lib/libgtk-quartz-2.0.0.dylib (compatibility version 1801.0.0, current version 1801.2.0) @executable_path/../Resources/lib/libgdk_pixbuf-2.0.0.dylib (compatibility version 1801.0.0, current version 1801.2.0) @executable_path/../Resources/lib/libgdk-quartz-2.0.0.dylib (compatibility version 1801.0.0, current version 1801.2.0) @executable_path/../Resources/lib/libpangocairo-1.0.0.dylib (compatibility version 2401.0.0, current version 2401.5.0)
For the GtkDemo app, the following libraries are copied:
$ ls charset.alias libfreetype.6.dylib libgobject-2.0.0.dylib libpangoft2-1.0.0.dylib gtk-2.0 libgdk-quartz-2.0.0.dylib libgtk-quartz-2.0.0.dylib libpixman-1.0.dylib libatk-1.0.0.dylib libgdk_pixbuf-2.0.0.dylib libintl.8.dylib libpng12.0.dylib libcairo.2.dylib libgio-2.0.0.dylib libjpeg.7.dylib libtiff.3.dylib libexpat.1.dylib libglib-2.0.0.dylib libpango-1.0.0.dylib libfontconfig.1.dylib libgmodule-2.0.0.dylib libpangocairo-1.0.0.dylib
The environment variable GDK_PIXBUF_MODULE_FILE is used to specify the path to a file usually named gdk-pixbuf.loaders. This file contains a list of shared libraries that perform the task of loading various kinds of images into GTK+.
This gdk-pixbuf.loaders file can be automatically generated by the script ~/gtk/inst/bin/gdk-pixbuf-query-loaders.
This contents of gdk-pixbuf.loaders are automatically edited by ige-mac-bundler to replace the absolute file paths with paths like @executable_path/../Resources/lib/gtk-2.0/loaders/libpixbufloader-png.so, etc.
The pixbuf loaders are copied into the locally-consistent location Contents/Resources/lib/gtk-2.0/2.xx.xx/loaders, where xx.xx is the particular GTK+ version being used.
Icons and themes
Icons and themes are copied into Contents/Resources/share. I got the following files:
$ ls -R icons themes ./icons: hicolor ./icons/hicolor: index.theme ./themes: Default Emacs Raleigh ./themes/Default: gtk-2.0-key ./themes/Default/gtk-2.0-key: gtkrc ./themes/Emacs: gtk-2.0-key ./themes/Emacs/gtk-2.0-key: gtkrc ./themes/Raleigh: gtk-2.0 ./themes/Raleigh/gtk-2.0: gtkrc
Didn't see any sign of actual image or icon files anywhere... perhaps the default ones are embedded in the shared library somehow.
Pango configuration files
In Contents/Resources/etc/pango/pangorc the following text was found:
$ cat pangorc [Pango] ModuleFiles=./pango.modules
The file pango.modules is present in the same directory, but is empty.
GTK configuration files
In Contents/Resources/etc/gtk-2.0/gtkrc the following text was found:
$ cat gtkrc gtk-icon-theme-name = "Tango" gtk-enable-mnemonics = 0
In same directory, there is another empty file called gtk.immodules.
Use of install_name_tool
In ige-mac-bundler, all DYLD paths have been set relative to the @executable_path. The executable is kept inside the bundle as Contents/MacOS/GtkDemo-bin. This means, for example, that @executable_path/../Resources/lib takes the loader to folder containing all the GTK shared libraries.
Note: for the case of PyGTK bundles, assuming Apple Python or MacPython are being used but not being included in the bundle, we can't pull this trick, because the @executable_path would then be outside the application bundle, and no help for locating GTK libraries. An alternative approach might be possible using @rpath set on the PyGTK shared libraries, and this needs to be tried out.
The file Contents/MacOS/GtkDemo is the entry-point for the application (as directed by Info.plist) and is a shell-script that sets various required environment variables so that GTK can find all its stuff:
NOTE: in the following, the "`pwd`/$0" causes problems if you invoke the ige-mac-bundler-bundled from the command line from any directory other than Contents/MacOS. I think that this is a bug.
name="`basename $0`" tmp="`pwd`/$0" tmp=`dirname "$tmp"` tmp=`dirname "$tmp"` bundle=`dirname "$tmp"` bundle_contents="$bundle"/Contents bundle_res="$bundle_contents"/Resources bundle_lib="$bundle_res"/lib bundle_bin="$bundle_res"/bin bundle_data="$bundle_res"/share bundle_etc="$bundle_res"/etc export DYLD_LIBRARY_PATH="$bundle_lib" export XDG_CONFIG_DIRS="$bundle_etc"/xdg export XDG_DATA_DIRS="$bundle_data" export GTK_DATA_PREFIX="$bundle_res" export GTK_EXE_PREFIX="$bundle_res" export GTK_PATH="$bundle_res" export GTK2_RC_FILES="$bundle_etc/gtk-2.0/gtkrc" export GTK_IM_MODULE_FILE="$bundle_etc/gtk-2.0/gtk.immodules" export GDK_PIXBUF_MODULE_FILE="$bundle_etc/gtk-2.0/gdk-pixbuf.loaders" export PANGO_RC_FILE="$bundle_etc/pango/pangorc"
After the above, there is some locale-related stuff (see launcher.sh for the details.
Next the script does something with charset alias (FIXME what's this doing?):
if test -f "$bundle_lib/charset.alias"; then export CHARSETALIASDIR="$bundle_lib" fi
Next, a variable EXTRA_ARGS is initialised empty and an optional environment.sh is run, allowing command-line arguments to be appended to EXTRA_ARGS, or just to set additional application-specific or user-specific environment variables.
Then, the script throws out some Mac-related command-line parameters that are useless to GTK:
# Strip out the argument added by the OS. if [ x`echo "x$1" | sed -e "s/^x-psn_.*//"` == x ]; then shift 1 fi
Finally, the script launches the executable.
$EXEC "$bundle_contents/MacOS/$name-bin" $* $EXTRA_ARGS