MacPython Logo from __future__ import *

Kailash and Friends Kailash Kher Kaipa

online mp3 Anoice albums buy Amund Maarud albums online Asia online CD Andy M. Stewart buy tracks Axis online Astral Rising A Beautiful Machine download CD Aereda buy tracks Aksent online tracks Absidia Atrium Carceri A Beautiful Machine Absolum buy CD Aryan Wind and Brumalis and Valhalla Saints online music Atomsmasher download albums AK1200 download music Angelzoom online CD Arturo Mantovani and his Orchestra buy music 16 buy tracks Ashtorath online CD Aimee Mann buy music Anael And Bradfield buy mp3 Autumnblaze download mp3 Aggrolites download CD Arj Snoek buy albums Ada buy CD Aalto Andy With Rama West A Beautiful Machine Absolum online tracks Asura albums online Albert Lee 4 Non Blondes A Beautiful Machine Absolum download albums Andrew Lloyd Webber and Ar Rahman online music African Head Charge download mp3 Amber Asylum online music Analena online music ANTIX feat ROB SALMON A.R. Rahman A Beautiful Machine Absolum online tracks African Blackwood buy mp3 Axis buy mp3 Alan Menken buy music Amoebic Dysentery buy Alph Secakuku A Beautiful Machine albums download Albita online Amparo Ochoa A Beautiful Machine download tracks Andy Partridge and Harold Budd download tracks Anubian Lights Alient Project A Beautiful Machine Absolum buy albums Antonio Forcione download CD Ali G Indahouse online mp3 Art and Jazz Messengers Blakey download Arab Strap A Beautiful Machine online albums Adema buy Agua de Annique A Beautiful Machine buy CD Avalanches download tracks Acroma Andi Deris A Beautiful Machine Absolum download tracks American Steel download albums Amanda Perez online 999 A Beautiful Machine download mp3 Arild Andersen download CD American Steel buy tracks Absolute Beginner download tracks Anubi online albums Ancient Wisdom online A Verse Unsung A Beautiful Machine buy music Aghast Andromeda Island A Beautiful Machine Absolum download Arlo Guthrie A Beautiful Machine online mp3 Aavepyora online albums Achillea buy Andrew Bird A Beautiful Machine buy music Alexey Aigui and Ensemble 4'33'' albums buy Abbey Lincoln and Archie Shepp download albums Archive download CD A Guy Called Gerald feat. D.S. download music Al Di Meola online music Abigail download music Angel Witch online music Adelaide

2007-08-11

Exploring Erlang @ C4[1]

Filed under: erlang, macosx — bob @ 2:46 pm

Slides from my Exploring Erlang talk today at C4[1] are available here: http://undefined.org/c4-1/

Yes, it is definitely caturday.

For those of you interested in learning more about Erlang, I highly recommend buying Joe Armstrong's Programming Erlang and/or read his 2003 ph.d thesis, which is kinda similar in content to his book but is more technical and is freely available: Making Reliable Distributed Systems in the Presence of Software Errors. The thesis is a bit more dense, but it was what really made Erlang "click" for me.

If you want to play with the examples, go to CEAN and download "Erlang/OTP Full (Standard)" at the bottom for you version of Mac OS X and follow the "How do I install CEAN to /usr/local/bin" FAQ entry. Alternatively, if you want to wait longer, you could use MacPorts to download and compile it. Once you've got that set up just run erl from the examples directory in the slides download and it should all work.

2006-09-14

svnsync: mirror your svn repository

Filed under: FreeBSD, macosx, subversion, universal binaries — bob @ 12:15 pm

Since the svnsync tool in Subversion 1.4 still lacks documentation, here's a quick example to get you started with it:

$ svnadmin create svn.mochikit.com
$ echo '#!/bin/sh' > svn.mochikit.com/hooks/pre-revprop-change
$ chmod +x svn.mochikit.com/hooks/pre-revprop-change
$ svnsync init file://$PWD/svn.mochikit.com http://svn.mochikit.com/
$ svnsync sync file://$PWD/svn.mochikit.com

The example performs the following tasks:

  • Creates a new local repository named svn.mochikit.com
  • Adds a trivial pre-revprop-change hook to the repository (required for svnsync)
  • Initializes the svn.mochikit.com repository to be a mirror of http://svn.mochikit.com/
  • Synchronizes the svn.mochikit.com repository with its initialized source

Note that this is point-in-time synchronization. To keep an up to date mirror you'll have to run svnsync sync on a regular basis (e.g. by cron job or post-commit hook).

If you're looking for an especially easy to install set of svn binaries for Mac OS X, I recommend Martin Ott's (of SubEthaEdit fame) build. I've been using it on all of my Macs for quite some time with no issues. Much less hassle than building it myself or using MacPorts (previously known as DarwinPorts).

2006-05-05

MacBook Pro first impressions

Filed under: macosx, universal binaries — bob @ 2:57 pm

After over three years with a 1ghz 15" titanium PowerBook I've finally upgraded to a 2ghz dual core 15" MacBook Pro! Compared to my old laptop, this machine is absolutely amazing. I've been waiting a LONG time for:

  • Built-in Bluetooth, 802.11g, USB 2.0
  • Higher resolution, contrast, brightness display
  • Trackpad with the two-finger scrolling
  • Much much improved video subsystem
  • A machine fast enough to effectively use spotlight

Additionally, this new kit is going to save me a lot of VNC sessions since I can run other operating systems in virtualization via Parallels, and it will allow me to more easily build and properly test universal binaries. Parallels seems to work pretty well for a beta product, but I'm definitely looking forward to seeing some competition (especially open source competition).

Despite its awesomeness, there are still some issues with the MacBook Pro:

  • The power supply whine is still there when the system isn't using a lot of juice, but apparently much less so than in earlier revisions.
  • These machines run HOT. I probably wouldn't try and put it on my lap if I wasn't wearing jeans, and it does make your hands a little uncomfortably warm when using the built-in keyboard (though I'm sure I'll get used to that).
  • The display doesn't tilt back quite as far as the old titanium powerbooks. Not a big deal, hopefully that just means they've solved the problem where the hinges would just snap after a year or two (and take out the backlight cable, argh).
  • Intel code on Mac OS X supposedly uses more RAM than the PPC equivalent, which means you should buy more RAM than you are accustomed to from earlier machines.

The RAM usage increase is somewhat surprising, because i386 binaries are more compact (the i386 libSystem is about 16% smaller than PPC). I'm guessing the increase is due to the lack of usable registers on i386, which increases the amount of stack necessary per thread. I doubt it's an alignment issue, because PPC definitely cares more about alignment than i386 does. A quick sample shows that Terminal.app with one window does use about 5% more real memory than the PPC equivalent (and a LOT more virtual memory, but that might just be Quartz or something).

5% doesn't sound like much, but I've heard bigger numbers for other kinds of applications. I definitely plan to upgrade this machine from 1gb to 2gb, but the only times I've really felt RAM-hungry so far are when I'm using Parallels (which understandably needs a lot of real memory).

2006-04-10

Python and Universal Binaries on Mac OS X

Filed under: PyObjC, macosx, py2app, python, setuptools, universal binaries — bob @ 2:41 pm

It's been a pretty long haul getting Python to do the right thing on Mac OS X with regard to Universal Binaries. The dust is settling, and it's about time to start publicizing the results.

In order to compile extensions with Universal MacPython, you absolutely must have the latest version of Xcode installed. When you install it, make absolutely sure that you Customize the installation and install the Mac OS X 10.4 Universal SDK. Universal MacPython requires this SDK to be installed, as it instructs the compiler and linker to use this specific SDK. There is a hook that will remove these special flags, but ONLY for legacy Mac OS X 10.3.9 users. Mac OS X 10.3.9 users should still be able to compile extensions, but the resultant binaries will be PPC only and shouldn't be distributed (as they will not work on Intel).

Besides the obvious endian fixes and distutils/configure hackery, Universal MacPython includes many usability enhancements over previous distributions of Python for Mac OS X:

  • IDLE, the Python IDE, should now ship with Mac keybindings and menus by default
  • Placing binaries in /usr/local/bin is now optional
  • The bin directory from the framework is now (by default) automatically added first to your PATH environment variable. This means that when you open a new terminal session and type python, you will get Universal Python 2.4.3. Yes, it's ugly to do this, but it's a necessary evil. PATH related issues are by far the most frequent source of confusion on pythonmac-sig (between Python itself and distutils-installed scripts), and this is the solution. darwinports does something quite similar.
  • pythonw is now an executable, instead of a shell script, so it may be used directly in a hash-bang line for a shell script (#!).
  • Users no longer need to know or care about pythonw. python and pythonw are the same binary, which simply does an execve to a Python executable inside of a "fake" application bundle to placate the WindowServer. Yes, this introduces a tiny little bit of overhead when invoking python due to the extra syscall, but it is again a necessary evil to work around a design flaw in Mac OS X.
  • Includes a working bsddb, readline, and curses.
  • Known compatible with Mac OS X 10.3.9 and later, without any patching necessary on Mac OS X 10.4.

The latest Universal Python source code is currently a fork of Python 2.4.3, and currently resides outside of the official Python subversion repository. For those interested in the source, it is here:

The latest binary release of Universal Python is currently available in the Universal Python 2.4 section of pythonmac.org packages! There is/was an older Universal Python build available somewhere on python.org but it is NOT what you should be using (it's built without compiler optimization and is roughly 50% slower on either architecture!):

PyObjC and py2app users with Universal MacPython should install PyObjC from SVN trunk (which will net you a universal-ready py2app). There is currently no tagged release of either project that is suitable for this build of Python, but we hope to fix that soon:

pythonmac.org packages has been split into two sections, one for universal packages, and one for legacy builds of Python. Currently there aren't many universal packages ready (largely because I don't have an Intel machine yet, so I can only test on PPC). The legacy packages for Python 2.4 are still compatible with Universal MacPython, however, they are NOT compatible with Intel.

Most packages should compile just fine, and setuptools/easy_install are already Universal MacPython aware. However, packages that depend on external libraries will not compile correctly of the dependent libraries aren't also Universal. This means that MySQL bindings, wxPython, and a few other popular packages are not yet readily available. This issue will resolve itself at some point due to the demand, but it's not resolved yet. There's a list of desired packages and their status at the UniversalPackages node on the pythonmac.org wiki.

2005-10-08

xattr - Python extended filesystem attributes

Filed under: macosx, python — bob @ 10:30 am

xattr is a Python wrapper for the extended attributes APIs available in Darwin 8.0+ (Mac OS X 10.4) and Linux 2.6+. 0.2 added Linux compatibility, and API compatibility with the GPL pyxattr module. Unlike pyxattr, xattr supports both Darwin and Linux, has a dict-like API, and it is MIT licensed.

Usage looks like this:

>>> import xattr
>>> x = xattr.xattr('tiger_8a428_userdvd.dmg')
>>> x
<xattr file='tiger_8a428_userdvd.dmg'>
>>> x.keys()
[u'com.apple.diskimages.recentcksum']
>>> dict(x)
{u'com.apple.diskimages.recentcksum': 'i:969254 ...more stuff'}
>>> x['com.apple.diskimages.recentcksum']
'i:969254 ...more stuff'
>>> x['com.apple.adc'] = 'select'
>>> dict(x)
{u'com.apple.adc': 'select',
 u'com.apple.diskimages.recentcksum': 'i:969254 ...more stuff'}
>>> del x['com.apple.adc']
>>> dict(x)
{u'com.apple.diskimages.recentcksum': 'i:969254 ...more stuff'}    

You can download xattr 0.2 from http://pythonmac.org/packages/ for Tiger's Python 2.3.5 or Python 2.4.1 (installed on Tiger).

svn trunk:
http://svn.red-bean.com/bob/xattr/trunk/
0.2 source:
http://svn.red-bean.com/bob/xattr/releases/xattr-0.2/

2005-07-18

Airport Express Hates Me

Filed under: PyObjC, iPod, macosx, python — bob @ 3:21 am

A few weeks ago I began my move from New York to San Francisco by... moving to Hawaii. For the summer, anyhow. In shipping, my trusty old Klipsch computer speakers seem to have eaten it. I tried hooking them up, and after some really painful shrieking, they worked for a few minutes.. until they caught fire. Really. My ex-favorite music spewing devices have found themselves a nice retirement home in a landfill somewhere on Maui.

I tried playing iPod DJ with the stereo here, but that doesn't really work very well for long stretches. It was time to break down and finally get an Airport Express. I looked around online a bit, and shipping was just ridiculous to Hawaii from most places (e.g. Smalldog wanted $95 S+H to ship a $120 refurb Airport Express to Kihei!). Fortunately, there is a Mac store in town, so I was able to pick one up at a reasonable price.

Immediately after bringing it home I plugged it in via ethernet, and tried to update the firmware. A few hundred times. It just wouldn't take. The Airport Admin utility would not do it. From either of my Macs. It'd try, and it would fail after a minute or two without even an error code. Eventually I found a link to download a standalone firmware updater application, which updated the firmware first try. Go figure. Certainly not the typical Apple experience.

Once it was updated and hooked up, it worked great. 90% of the time, anyway. iTunes will only play files that it natively understands through the Airport Express. Apparently, iTunes has decided that some of my MP3s were QuickTime files, and refused to play them through Airport Express, which means they were coming out of the built-in speakers. That didn't work out so well. I guess that sucks for people who care about OGG and other formats supported only via QuickTime.

It turns out that the reason these files were picked up as QuickTime is that iTunes has MPEG type code hate. MacAMP and SoundJam of yesteryear used MPEG, yet iTunes wants to see MPG3. It took me a lunch break to come up with a good way to track down all of these files without writing a lot of code. It was actually rather simple: the iTunes Music Library XML dump.

~/Music/iTunes/iTunes Music Library.xml is a brain-dump of iTunes that it creates every so often (on quit, I believe). It keeps track of almost everything that iTunes knows, all of your file's URLs, their type and creator codes, comments, metadata, you name it. It's also in plist format, which is extremely easy to work with from Python.

Assuming you have PyObjC installed, you can break into a Python interpreter and screw around with this rather easily:

% python
Python 2.4.1 (#2, Mar 31 2005, 00:05:10)
[GCC 3.3 20030304 (Apple Computer, Inc. build 1666)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from Foundation import *
>>> import os
>>> dbFile = os.path.expanduser("~/Music/iTunes/iTunes Music Library.xml")
>>> db = NSDictionary.dictionaryWithContentsOfFile_(dbFile)
>>> type(db)
<objective-c class NSCFDictionary at 0x30a4b0>

With the database in hand, we can start poking around:

>>> track = db[u'Tracks'].itervalues().next()
>>> print track.description()
{
    Album = "Live Code";
    Artist = "Front 242";
    "Artwork Count" = 1;
    "Bit Rate" = 160;
    ...

In these track dictionaries, there are two keys relevant to this exercise: u'File Type', and u'Location'. File type is the integer representation of the type code of the track. Location is the URL of the track. With this, it's pretty easy to pick out the locations of the tracks that are set to the MPEG type code:

>>> import struct
>>> badPaths = [
...     NSURL.URLWithString_(track[u'Location']).path()
...     for track in db[u'Tracks'].itervalues()
...     if track.get(u'File Type') == struct.unpack('>I', 'MPEG')[0]
... ]
>>> badPaths[0]
/Volumes/...

With this, all we need to do is change the type and creator. Fortunately, that's easy. We'll use the iTunes creator code hook, and the expected MPG3 type code:

>>> import MacOS
>>> for path in badPaths:
...     MacOS.SetCreatorAndType(path, 'hook', 'MPG3')
...

And with that, my Airport Express does just about everything I'd have expected it to out of the box ;)

2005-07-05

PyObjC First Steps

Filed under: PyObjC, macosx, python — bob @ 2:27 am

I've been neglecting Python a bit lately, but Ronald is going to cut a release of PyObjC 1.3.7 very soon, hopefully later this week. PyObjC 1.3.7 fixes a few bugs (particularly with Xcode 2.1 templates), has a more complete wrapping of Mac OS X 10.4 system frameworks (DiscRecording, SenTestingKit, SecurityFoundation), and adds a new TinyURLService example (which converts URLs on the pasteboard to tinyurl.com equivalents).

There's also a swath of new "First Steps" documentation in the PyObjC intro, which I wrote up earlier today:

First Steps

When dealing with the Objective-C runtime, there are certain patterns you need to learn when writing Python code. If you're not already an Objective-C programmer, some of them will seem strange or even "un-pythonic" at first. However, you will get used to it, and the way that PyObjC works is quite compliant with the Zen of Python (import this). In fact, Ronald is Dutch ;)

With no further ado, here are the three most important things you must know before embarking on any PyObjC voyage:

Underscores, and lots of them

Objective-C objects communicate with each other by sending messages. The syntax for messages is somewhere in-between Python's positional and keyword arguments. Specificlaly, Objective-C message dispatch uses positional arguments, but parts of the message name (called "selector" in Objective-C terminology) are interleaved with the arguments.

An Objective-C message looks like this:

[someObject doSomething:arg1 withSomethingElse:arg2];

The selector (message name) for the above snippet is this (note the colons):

doSomething:withSomethingElse:

In order to have a lossless and unambiguous translation between Objective-C messages and Python methods, the Python method name equivalent is simply the selector with colons replaced by underscores. Since each colon in an Objective-C selector is a placeholder for an argument, the number of underscores in the PyObjC-ified method name is the number of arguments that should be given.

The PyObjC translation of the above selector is (note the underscores):

doSomething_withSomethingElse_

The message dispatch, translated to PyObjC, looks like this:

someObject.doSomething_withSomethingElse_(arg1, arg2)

Methods that take one argument will have a trailing underscore.

It may take a little while to get used to, but PyObjC does not ever rename selectors. The trailing underscore will seem strange at first, especially for cases like this:

# note the trailing underscore
someObject.setValue_(aValue)

There are a few additional rules regarding message dispatch, see the Overview of the bridge for the complete rundown.

Two-phase instantiation

Objective-C, being a low-level runtime, separates the two concepts required to instantiate an object.

allocation:
Reserve a chunk of memory large enough to hold the new object, and make sure that all of its declared instance variables are set to "zero" (this means nil pointers to objects, 0 for integers, etc.).
initialization:
Fill in the blank slate allocated by the allocation phase.

In Objective-C, the convention is for allocation to be performed by a class method called alloc, and initialization is done with method beginning with the word init. For example, here is the syntax for instantiating an NSObject:

myObject = NSObject.alloc().init()

And here is an example for creating an NSData instance given a few bytes:

myData = NSData.alloc().initWithBytes_length_('the bytes', 9)

You must also follow this convention when subclassing Objective-C classes. When initializing, an object must always (directly or indirectly) call the designated initializer of its super. The designated initializer is the "most basic" initializer through which all initialization eventually ends up. The designated initializer for NSObject is init. To find the designated initializer for other classes, consult the documentation for that class. Here is an example of an NSObject subclass with a customized initialization phase:

class MyClass(NSObject):

    def init(self):
        """
        Designated initializer for MyClass
        """
        # ALWAYS call the super's designated initializer.
        # Also, make sure to re-bind "self" just in case it
        # returns something else!
        self = super(MyClass, self).init()

        self.myVariable = 10

        # Unlike Python's __init__, initializers MUST return self,
        # because they are allowed to return any object!
        return self

class MyOtherClass(MyClass):

    def initWithOtherVariable_(self, otherVariable):
        """
        Designated initializer for MyOtherClass
        """
        self = super(MyOtherClass, self).init()
        self.otherVariable = otherVariable
        return self

myInstance = MyClass.alloc().init()
myOtherInstance = MyOtherClass.alloc().initWithOtherVariable_(20)

Many Objective-C classes provide class methods that perform two-phase instantiation for you in one step. Several examples of this are:

# This is equivalent to:
#
#   myObject = NSObject.alloc().init()
#
myObject = NSObject.new()

# This is equivalent to:
#
#   myDict = NSDictionary.alloc().init()
#
myDict = NSDictionary.dictionary()

# This is equivalent to:
#
#   myString = NSString.alloc().initWithString_(u'my string')
#
myString = NSString.stringWithString_(u'my string')

Objective-C uses accessors everywhere

Unlike Python, Objective-C convention says to use accessors rather than directly accessing instance variables of other objects. This means that in order to access an instance variable value of an object valueContainer you will have to use the following syntax:

# Getting
# 
# notice the method call
#
myValue = valueContainer.value()

# Setting
# 
# notice the naming convention and trailing underscore
#
valueContainer.setValue_(myNewValue)

When writing your own classes from Python, this is a bit harder since Python only has one namespace for all attributes, even methods. If you choose to implement accessors from Python, then you will have to name the instance variable something else:

class MyValueHolder(NSObject):
    
    def initWithValue_(self, value):
        self = super(MyValueHolder, self).init()
        # It's recommended not to use typical Python convention here,
        # as instance variables prefixed with underscores are reserved
        # by the Objective-C runtime.  It still works if you use
        # underscores, however.
        self.ivar_value = value
        return self
    
    def value(self):
        return self.ivar_value
    
    def setValue_(self, value):
        self.ivar_value = value

It's also possible to use Key-Value Coding in some cases, which eliminates the need for writing most accessors, but only in scenarios where the rest of the code is using it.

2005-06-16

iPod model detection

Filed under: debugging, iPod, macosx, python — bob @ 3:55 pm

For various reasons, I have a need to determine the version of an iPod. Previously, I only needed to know if an iPod was "firmware version 2" or later (aka "Dock-connector"), and there's a whole slew of obvious ways to determine that (presence of a Notes folder would be the most obvious). There are also ways to grab iPod information with SPI (via COM on win32 or the private iPod.framework on Mac OS X), but I like to avoid that whenever possible.

So far, the most reliable method I've found was with a little parsing of the SysInfo file. This file is located at "$(IPOD_VOLUME)/iPod_Control/Device/SysInfo". The iPod_Control folder is going to be hidden on either platform, so you may not have noticed it before. Many examples of SysInfo files can be found by googling for some of the keys, such as buildID, visibleBuildID, boardHwName, etc. The most complete resource I've found is the on the iPodLinux forums.

As-is, the SysInfo files don't make any of the useful information very obvious. However, with a little reverse-engineering of the iPod Updater for Mac OS X, it was relatively easy to figure out what it was using to determine the iPod model.

In the Resources folder of any iPod Updater, you'll find a lot of interesting things. The most interesting bits are UpdaterVersions.plist and the Updates folder.

UpdaterVersions.plist is a typical XML property list with a single root key: Versions. Under this key you'll find a dictionary that maps updaterFamily to a dictionary of information about the iPods in that updaterFamily, not unlike the information you will find in a SysInfo. The dicts with a displayInAbout key with a true value are the most interesting, all of the other entries are simply variations on the theme (i.e. the iPod U2 Special Edition or the various colors of iPod Mini). When using this filter, you should have exactly one entry per unique iPodFamily value.

The Updates folder is interesting because it contains icons for each iPod, as well as the firmware images. The icons are named either DeviceIcon-$(iPodFamily).icns or DeviceIcon-$(iPodFamily)-$(iPodColor).icns. I'm relatively certain that the color can be determined from the last three characters of the pszSerialNumber, but I don't have enough iPods around to test that theory.

With all of this information, it would almost be easy to determine the model of an iPod. However, it's not quite that simple. Not all SysInfo files contain an iPodFamily key, and some that do incorrectly report 0! The only reliable identifier for an iPod family is the following (given a dict from either the UpdaterVersions.plist or from a SysInfo file):

def buildPair(dct):
    buildID = dct.get('buildID', 0) 
    visibleBuildID = dct.get('visibleBuildID') or dct.get('VisibleBuildID', 0)
    # grab these bits: 0xFF000000L
    return (buildID >> 24, visibleBuildID >> 24)

What I'm doing here is pulling the "major" version out of the version keys. It appears that the versioning convention is: 0x0ABC8000 where the firmware version is pretty-printed as "A.B.C", trimming "C" and possibly "B" if they are 0. This is similar to the hex-version-convention you see in gestaltSystemVersion ('sysv') and other places. It would be a little less ugly if the UpdaterVersions.plist used the same case as the SysInfo files for the visibileBuildID key! The reason that we need both the buildID and the visibleBuildID is that the iPod Mini and "3G iPod" both report a buildID major version of 2, however the iPod Mini has a visibleBuildID major version of 1.

Parsing the SysInfo file in a manner that will grab the information in the right way is pretty trivial, but perhaps not so obvious:

import os
def infoForMountedPod(path):
    path = os.path.join(path, u'iPod_Control', u'Device', u'SysInfo')
    lines = file(path).read().splitlines()
    d = {}
    for line in lines:
        try:
            key, value = line.split(': ', 1)
        except ValueError:
            continue
        try:
            value = long(value.split(None, 1)[0], 0)
        except (ValueError, IndexError):
            pass
        d[key] = value
    return d

infoForMountedPod simply looks for all lines that look like a key-value pair (containing a ": "). For each of these lines, it will set the key-value pair in the returned dictionary. For lines whose first "word" (split on whitespace) is parseable as an integer (typically as hex), it will be set as an integer. Otherwise, it is set to the string value of that line (i.e. pszSerialNumber).

Parsing UpdaterVersions.plist is pretty trivial too:

import plistlib
def parseUpdaterVersions(path):
    # this is Python 2.4 plistlib API
    # in 2.3 you can use plistlib.Plist.fromFile
    versions = plistlib.readPlist(path)
    buildPairs = {}
    families = {}
    for k,v in versions['Versions'].iteritems():
        # only pick out unique iPodFamily dicts
        if v['displayInAbout']:
            fam = v['iPodFamily']
            # skip pre-2.x iPods and iPod Shuffle.
            # This is specific to my use case, you may
            # not want this filter.
            if 1 < fam < 128:
                assert fam not in families
                families[fam] = v
                pair = buildPair(v)
                assert pair not in buildPairs
                buildPairs[pair] = v
    return buildPairs, families

Note that all of this code is cross-platform, but you'll need an UpdaterVersions.plist or equivalent from somewhere, and you'll need to pick up plistlib.py from Python's src/Lib/plat-mac dir if using it on some other platform. Alternatively, you could use ElementTree to parse the plist XML. Its iterparse documentation has an excellent example of how easily it can be used to parse these files.

2005-06-11

Universal Binaries with gcc and Xcode

Filed under: c, macosx, universal binaries — bob @ 11:16 am

Versions of Mac OS X prior to 10.4 did support universal binaries, but the shipping gcc did not, so it is possible to compile applications that will work on both 10.3 ppc (Panther) and 10.4 i386 (Tiger). To build Panther-compatible universal binaries, you'll need to use the following Custom Build Settings in Xcode 2.1 or later...

Per-architecture SDK Support for Universal Binaries in Xcode. This is according to documentation, but I couldn't get it to work:

SDKROOT_ppc = /Developer/SDKs/MacOSX10.3.9.sdk
SDKROOT_ppc64 = /Developer/SDKs/MacOSX10.4u.sdk
SDKROOT_i386 = /Developer/SDKs/MacOSX10.4u.sdk

I'm not sure how to translate per-architecture SDKs to gcc settings, but to build single-SDK Universal Binaries with GCC, there are some undocumented (not in the compiler man page, anyhow) flags that you can use. These flags are only available in GCC 4.0 and later, and probably only with Apple's toolchain.

Universal Binaries with GCC (in Makefile syntax):

SDK=/Developer/SDKs/MacOSX10.4u.sdk
CFLAGS= -isysroot ${SDK} -arch ppc -arch ppc64 -arch i386
LDFLAGS= -isysroot ${SDK} -syslibroot,${SDK}

Using lipo(1) to do per-architecture SDK builds with gcc should be possible, but painful. Hopefully it's not the only way! However, that's exactly what Xcode 2.1 does.

See Also:

2005-06-09

Python on Mac OS X (Intel) - Updates

Filed under: PyObjC, macosx, python, universal binaries — bob @ 9:22 pm

PyObjC

Ronald has customized the libffi export even further to work for Mach-O x86, mostly by removing the usage of autoconf. He has also managed to hack in some universal binaries support, but it really needs to move to distutils. All of the unit tests pass. This code should end up in ctypes too, probably.

The Xcode templates should work with Xcode 2.1 now, but they won't build universal binaries until py2app does.

objc.inject still doesn't work (since mach_inject isn't fixed yet).

Python

This is built as universal binaries by Apple. I haven't seen the patches yet, but whatever they're doing doesn't carry over to distutils, and it doesn't also include ppc64 support (though it certainly could, if Python didn't depend so much on autoconf crap). One way to fix this would be to create a custom pyconfig.h with reasonable compiler-determined values, and not using autoconf at all (an Xcode project might be a good idea). This has lots of problems of its own, because there are a lot of options that can be useful to configure, distutils reads in several autoconf-generated files, etc.

It's also going to take some time to figure out the gcc incantations to build against SDKs, as they're not documented. Especially if we're going to try and build against two different SDKs (i.e. 10.3 for PPC and 10.4.1 for x86).

If we do end up putting universal binaries support into distutils, it may require users to have the SDK installed in order to use distutils. Unfortunately this isn't the default when installing Xcode 2.1.

Four character codes, and probably other data structures, are still the wrong endian on x86.

The majority of bundlebuilder applications will not run on Intel machines without modification, even with Rosetta, because their executable is a script so Python is going to start up as an x86 binary. py2app-built applications don't have this problem.

It might make sense to add some intelligence to the extension importer to check for correct architecture before it tries to load the .so, or to have separate site-packages directories for each architecture, plus one for universal binaries. For example, it would make sense to have psyco available for x86, but maybe not anywhere else.

py2app

macholib needs to learn how to rewrite load commands of universal binaries.

If the py2app bootstraps are recompiled as universal binaries, then it should probably be able to detect whether the application needs to start up as ppc, because I don't want it to build incompatible apps like bundlebuilder does.

Next Page »

Powered by WordPress