Building 64-bit PyObjC Applications With Py2app



64-bit is upon us and has been for a while now. With Mac OS X Snow Leopard putting so much emphasis on 64-bit applications, you don't want to be left behind with your py2app built applications, don't you? Well, it turns out you're not entirely out of luck! It requires a bit of tweaking, but building 64-bit Python based applications with py2app is possible.

A Mac OS X 10.5 build setup is better

A little note before we start: Although I manage to build working 64-bit applications with py2app under Mac OS X 10.6, I have to build my plugins with the --alias flag or else I get a weird CFAllocator related crash. When building under 10.5, there's no such problem and the app runs fine under both 10.5 and 10.6. So if you haven't already, install a separate 10.5 build partition somewhere and build your apps from that.

Update 2010-03-04: With Python 2.6.5, that CFAllocator crash doesn't happen anymore.

64-bit Python

The first thing you need to do is to build yourself a 64-bit python. This is trivial to do and will usually go without problem. Just download the source package from http://www.python.org and do:

./configure --enable-universalsdk=/Developer/SDKs/MacOSX10.5.sdk --enable-framework \ 
--with-universal-archs=3-way && make && sudo make install

Remember that you'll have to rebuild any 3rd party package you installed (except those in pure python, of course) so that they support 64-bit as well.

Under Mac OS X 10.6, you might have to use --enable-universalsdk=/ instead. When I target 10.5 under Snow Leopard, I can't import hashlib anymore. (Update 2010-07-07: This problem doesn't exist with Python 2.7. This makes the statement "A Mac OS X 10.5 build setup is better" false now)

64-bit py2app

Update 2010-03-20: The macholib's patch I'm referring to below has been applied in the main repository's trunk, so it doesn't make sense for me anymore to maintain a fork of it. Py2app, however, still isn't patched.

Update 2010-07-30: After having tried the latest version of py2app, it seems that this 64-bit issue is now fixed, so you can use the official py2app. However, you might have to rebuild prebuilt binaries. For that, go to the "apptemplate" and "bundletemplate" folder, remove all files in the "prebuilt" subfolder and then run their respective "setup.py" again. Then, install py2app.

This is much trickier. The current code in py2app's trunk doesn't support 64-bit. When you try to build a plugin or an app with it, you'll get crashes (I don't quite remember which exactly). This is caused by shortcomings in both macholib and py2app. If you google hard enough, you'll find a very interesting post by Marc-Antoine Parent (thanks man!) who gives patches that magically make py2app work with 64-bit! Yay!

Those patches are a little old and target 10.4, which creates some problems, so you have to tweak py2app to target 10.5. There's also a problem with ResolveAlias that is used in py2app and doesn't seem to exist in the Carbon package anymore. Well, it turns out the fix for this is easy: Just replace these calls with FSResolveAlias calls and you're set.

But you don't have to care for any of this because I decided that I'd maintain my own forks of macholib (Update 2010-03-20: not anymore) and py2app (Update 2010-07-30: not anymore). Just use those instead of the official ones, and you should be good to go.

PyObjC's problem

PyObjC has a memory consumption problem. While this is not a 64-bit issue, I ran into it while trying to build 64-bit applications because it involved using PyObjC 2.2 instead of 1.4. It turns out that PyObjC's new bridge support code consumes a lot of memory (See for yourself, open the Activity Monitor and look at Python's memory usage when you execute import AppKit). My application that used to allocate 22mb of memory on launch when built with PyObjC 1.4 consumes 48mb with PyObjC 2.2 (and 95mb when in 64-bit mode!). That seems quite excessive to me and I'm in the process of trying to minimize that memory consumption excess.

While I haven't made much progress on that side (I'm still trying to understand PyObjC's code), I managed to cut memory usage by a great deal by avoiding AppKit imports in my python code. If you build your applications like I do (native Objective-C that embeds a py2app-built plugin), it's possible that you don't use any AppKit symbols in your python code. I used to not care and just do from AppKit import *, but after analyzing what I'm actually using, it turns out that I can get away with just Foundation, saving a whole 13mb of memory consumption on launch (and 30mb in 64-bit mode)! Moreover, not having to parse the huge AppKit bridge support file on launch make that launch significantly faster.

It's in my plans, however, to tackle PyObjC's bridge support soon. I've been using this package for so long, I guess it's time I contribute back a little bit.

Conclusion

See? It's not so hard to build 64-bit applications with py2app. It's not because you chose the best language ever to build your application that you ought to stay in the old 32-bit world. Ahh, it's a beautiful world!


This site is best viewed with Firefox while listening to The White Stripes