• Home
  • RSS Feed
  • Log in

Arjan Molenaar

Bundling a Python application on Mac OS X with VirtualEnv
Posted by Arjan Molenaar in the early morning: March 31st, 2011

When it comes to distributing Python packages, Python has its own mechanism. The tooling (either easy_install or pip) allows you to install a Python package and its dependencies. Typically, those packages are installed as Python Eggs (Java has Jars, Ruby has Gems and Python has Eggs). However, one can not expect Mac users to use these command line tools to download and install Python applications, especially GUI applications.

How can you package an application, including library dependencies nicely on a Mac? That’s the task at hand when I wanted to make Gaphor available on the Mac platform.

The idea has been there for quite some time, and it became a lot more concrete once I started to compile and install my open source software using Homebrew.

In this post I’ll outline the way Gaphor is packaged for this platform. I started Gaphor quite some years ago on Linux. It’s written in Python using the GTK+ gui library. After I switched to OS X as my main OS, I still work on this project. The X11 server on OS X works pretty well, so nothing holds me back. Except that it’s hard to share this app with my fellow Mac users.

Gaphor and most of the dependent modules are available as Python eggs from the Python Package Index, hence installing it is a simple task. GTK+ is not available on Mac OS by default and PyGTK (GTK+’s Python bindings) is not available as egg, so those have to be installed from source. I started off with compiling the dependencies (GTK+ and friends) using Homebrew. Homebrew has a big advantage over MacPorts and Fink in that it does not try to do everything itself (build everything from scratch). Instead it tries to use functionality available on the system already (like X11, Python and some basic libs). With a fork of Stein Magnus Jodal’s Homebrew branch I was able to get GTK+ and PyGTK compiled and installed.

Next step is to get this stuff packages in an application bundle in order to make it available to a broad audience. The de-facto packaging tool, py2app, is not able to handle eggs. Since Gaphor depends on eggs (the services for running the application are defined in the egg meta data), I had to figure out some other way to package the application, while retaining the egg structure.

I started of creating a simple application structure: a Gaphor.app folder, a Info.plist file and some additional directories. Next thing is to make this environment Pythonized. The way to to this is to use VirtualEnv. VirtualEnv is a Python tool that helps you set up isolated environments. This can be very handy for example for developing and testing software. A simple

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

did the trick.

I needed to install all PyGTK related libraries in the application folder.

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

Where $SITEPACKAGES is a directory in the folder that the `virtualenv-ed’ python installation will use to install the site-packages (non-default python modules).

Of course PyGTK won’t do a thing without the backing of GTK+ itself, so those libraries also had to be packages. To solve this a listing of dependencies for the freshly installed libraries will do. otool is able to list all dependencies. Those dependencies can be copied and the paths can be changed to relative paths. Relative paths on OS X, funny enough, relate to the location of the binary that was initially started. In this case Python (which is installed in Gaphor.app/Contents/bin).

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

Some extra resource files had to be installed and that was it for GTK+. The launch script takes care of setting the environment so the application will work. Now the application itself:

$INSTALLDIR/bin/easy_install gaphor

That was easy! The latest version will be downloaded from the PyPI and easy_install is installing it. One of the benefits of using virtualenv is that easy_install as well as pip are installed by default. This makes it very, very easy to install python applications.

You can find the full script on GitHub.

It took a while to figure out all the details, but creating an installable app, Mac style, is rather trivial. Next is a Windows installer and something tells me it won’t be this easy :) .

Share

Tags: osx, Python
Filed under General, Python, Tools | 2 Comments »



2 Responses to “Bundling a Python application on Mac OS X with VirtualEnv”



    » End of an Era: PyGTK Johns Blog Says:
    Posted at: April 3, 2011 at 2:49 am

    [...] Mac improvements (all in one installer work) (bundling advice) [...]

    Reply


    Gia Emigholz Says:
    Posted at: December 3, 2011 at 4:41 pm

    If you setup virtual hosts on your localhost install, couldn’t you avoid having to search and replace [for localhost to yourdomain.com] your db files?

    Reply


Leave a Reply

Click here to cancel reply.


Xebia Sites

  • Xebia Corporate
  • Xebia France
  • Xebia India
  • Xebia Sweden

Categories

  • Java (311)
  • Agile (181)
  • General (136)
  • Scrum (67)
  • Architecture (64)
  • Testing (59)
  • Performance (46)
  • Middleware (56)
    • Deployment (38)
  • Xebia Labs (39)
  • SOA (31)
  • Podcast (31)
  • Project Management (28)
  • Tools (26)
  • Uncategorized (20)
  • lean architecture (20)
  • Quality Assurance (17)
  • Articles (13)
  • Requirements Management (13)
  • Virtualization (19)

Tag Cloud

    Concurrency Control Xebia Agile ACT Flex SOA Maven Eclipse Moving to India Frameworks Scrum TDD Java Oracle JPA implementation patterns Architecture XML Grails agile architectuur product owner Hibernate lean architectuur Javascript Spring Scala Lean JPA lean architecture Groovy Ajax

Archives

  • February 2012
  • January 2012
  • December 2011
  • November 2011
  • October 2011
  • September 2011
  • August 2011
  • July 2011
  • June 2011
  • May 2011
  • April 2011
  • March 2011
Avatars by Sterling Adventures