Blog

Bündelung einer Python-Anwendung unter Mac OS X mit VirtualEnv

Arjan Molenaar

Arjan Molenaar

Aktualisiert Oktober 22, 2025
5 Minuten

Wenn es um die Verteilung von Python-Paketen geht, hat Python seinen eigenen Mechanismus. Mit den Werkzeugen (entweder easy_install oder pip) können Sie ein Python-Paket und seine Abhängigkeiten installieren. Normalerweise werden diese Pakete als Python Eggs installiert (Java hat Jars, Ruby hat Gems und Python hat Eggs). Man kann jedoch nicht erwarten, dass Mac-Benutzer diese Kommandozeilen-Tools verwenden, um Python-Anwendungen herunterzuladen und zu installieren, insbesondere GUI-Anwendungen.

Wie kann man eine Anwendung, einschließlich der Bibliotheksabhängigkeiten, auf einem Mac gut verpacken? Das war die Aufgabe, die ich zu lösen hatte, als ich Gaphor auf der Mac-Plattform verfügbar machen wollte.

Die Idee besteht schon seit einiger Zeit, und sie wurde viel konkreter, als ich begann, meine Open-Source-Software mit Homebrew zu kompilieren und zu installieren.

In diesem Beitrag werde ich Ihnen erläutern, wie Gaphor für diese Plattform verpackt ist. Ich habe Gaphor schon vor einigen Jahren unter Linux entwickelt. Es ist in Python geschrieben und verwendet die GTK+ GUI-Bibliothek. Nachdem ich auf OS X als mein Hauptbetriebssystem umgestiegen bin, arbeite ich immer noch an diesem Projekt. Der X11-Server unter OS X funktioniert ziemlich gut, so dass mich nichts aufhält. Außer, dass es schwer ist, diese Anwendung mit meinen Mac-Kollegen zu teilen.

Gaphor und die meisten abhängigen Module sind als Python-Ei im Python Package Index verfügbar, so dass die Installation eine einfache Aufgabe ist. GTK+ ist unter Mac OS standardmäßig nicht verfügbar und PyGTK (die Python-Bindungen von GTK+) ist nicht als Ei verfügbar, so dass diese Module aus dem Quellcode installiert werden müssen. Ich habe damit begonnen, die Abhängigkeiten (GTK+ und Freunde) mit Homebrew zu kompilieren. Homebrew hat gegenüber MacPorts und Fink den großen Vorteil, dass es nicht versucht, alles selbst zu machen (alles von Grund auf neu zu bauen). Stattdessen versucht es, bereits auf dem System vorhandene Funktionen zu nutzen (wie X11, Python und einige grundlegende Bibliotheken). Mit einem Fork des Homebrew-Zweigs von Stein Magnus Jodal konnte ich GTK+ und PyGTK kompilieren und installieren.

Der nächste Schritt besteht darin, dieses Zeug in ein Anwendungspaket zu packen, um es einem breiten Publikum zugänglich zu machen. Das De-facto-Paketierungstool py2app ist nicht in der Lage, Eier zu verarbeiten. Da Gaphor von Eiern abhängt (die Dienste zur Ausführung der Anwendung sind in den Metadaten des Eies definiert), musste ich einen anderen Weg finden, die Anwendung zu verpacken und dabei die Eierstruktur beizubehalten.

Ich habe damit begonnen, eine einfache Anwendungsstruktur zu erstellen: einen Ordner Gaphor.app, eine Datei Info.plist und einige zusätzliche Verzeichnisse. Als nächstes muss ich diese Umgebung in Python umwandeln. Der Weg dazu ist die Verwendung von VirtualEnv. VirtualEnv ist ein Python-Tool, mit dem Sie isolierte Umgebungen einrichten können. Dies kann zum Beispiel für die Entwicklung und das Testen von Software sehr nützlich sein. Eine einfache

[bash]
PYVER=2.6
APP=Gaphor.app
INSTALLDIR=$APP/Contents
virtualenv --python=python$PYVER --no-site-packages $INSTALLDIR
[/bash]

hat den Zweck erfüllt.

Ich musste alle PyGTK-bezogenen Bibliotheken in den Anwendungsordner installieren.

[bash]
LOCALDIR=/usr/local
SITEPACKAGES=$INSTALLDIR/lib/python$PYVER/site-packages
PYTHON_BREWS="pycairo pygobject pygtk pyrsvg"
for brew in $PYTHON_BREWS; do
cp -r $LOCALDIR/Cellar/$brew/*/lib/python$PYVER/site-packages/* $SITEPACKAGES
done
[/bash]

Dabei ist $SITEPACKAGES ein Verzeichnis in dem Ordner, den die `virtualenv-ed' Python-Installation für die Installation der Site-Packages (nicht standardmäßige Python-Module) verwenden wird.

Natürlich kann PyGTK ohne die Unterstützung von GTK+ selbst nichts ausrichten, so dass diese Bibliotheken auch Pakete sein müssen. Zur Lösung dieses Problems genügt eine Auflistung der Abhängigkeiten für die frisch installierten Bibliotheken. otool ist in der Lage, alle Abhängigkeiten aufzulisten. Diese Abhängigkeiten können kopiert werden und die Pfade können in relative Pfade geändert werden. Relative Pfade beziehen sich unter OS X lustigerweise auf den Speicherort der Binärdatei, die ursprünglich gestartet wurde. In diesem Fall Python (das in Gaphor.app/Contents/bin installiert ist).

[bash]
function resolve_deps() {
local lib=$1
local dep
otool -L $lib | grep -e "^.$LOCALDIR/" |
while read dep _; do
echo $dep
done
}
function fix_paths() {
local lib=$1
log Fixing $lib
for dep in resolve_deps $lib; do
log Fixing basename $lib
log "| $dep"
install_name_tool -change $dep @executable_path/../lib/basename $dep $lib
done
}
binlibs=find $INSTALLDIR -type f -name '*.so'
for lib in $binlibs; do
log Resolving $lib
resolve_deps $lib
fix_paths $lib
done | sort -u | while read lib; do
log Copying $lib
cp $lib $LIBDIR
chmod u+w $LIBDIR/basename $lib
fix_paths $LIBDIR/basename $lib
done
[/bash]

Einige zusätzliche Ressourcendateien mussten installiert werden, und das war's für GTK+. Das Startskript kümmert sich um die Einstellung der Umgebung, damit die Anwendung funktioniert. Und nun die Anwendung selbst:

[bash]
$INSTALLDIR/bin/easy_install gaphor
[/bash]

Das war ganz einfach! Die neueste Version wird von der PyPI heruntergeladen und easy_install installiert sie. Einer der Vorteile der Verwendung von virtualenv ist, dass sowohl easy_install als auch pip standardmäßig installiert werden. Das macht die Installation von Python-Anwendungen sehr, sehr einfach.

Sie finden das vollständige Skript auf GitHub.

Es hat eine Weile gedauert, alle Details herauszufinden, aber die Erstellung einer installierbaren Anwendung im Mac-Stil ist ziemlich trivial. Als Nächstes steht ein Windows-Installationsprogramm an, und irgendetwas sagt mir, dass das nicht so einfach sein wird :).

Verfasst von

Arjan Molenaar

Contact

Let’s discuss how we can support your journey.