So I pushed a rather significant improvement to PluginManager yesterday that finally fixed a rather large problem with Python support. To my knowledge, all of the plugins now work as expected. At least, objects now cross every bridge. Anyway, the more I work with the PluginManager, the more I realize that garbage collection is a pain in my ass.In the beginning was the easy stuff. Nu, Javascript, F-Script, Lua, and even Perl support was rather easy to implement. The bridges for these scripting languages were built over Objective-C or wrapped rather conveniently in Objective-C wrappers. Making them work was a relatively simple matter of figuring out the API.
Then there was Ruby. Ruby is a pain in the ass. There are a few competing implementations and each is significantly different. RubyCocoa may be the easiest to drop into an existing application but had a ton of subtle and not-so-subtle bugs that came up when embedding. MacRuby just worked great from the outset but has the significant problem of Objective-C garbage collection.
I say “significant problem” simply because garbage collection doesn’t work well with other code. If your app requires garbage collection, everything that it links to also needs to support garbage collection. Now, there are three states to GC. The first is completely off, the second is GC-supported, and the last is GC-enabled. In the second state, GC-supported, your application doesn’t use GC but can link to frameworks that have it enabled. It can’t, however, link to non-GC-supported or -enabled frameworks or libraries. The third state is fairly similar except that your application will use garbage collection.
This usually isn’t a huge problem. Nearly every third-party framework that I use can be recompiled with GC-support on without a hitch. It is as simple as retrieving the source, changing a compiler flag, and re-compiling. However, not every third-party framework provides source code to make this change so simple. Other times, such as in the case of CamelBones, the project isn’t quite amenable to turning on GC. These are bail and move on cases.
There’s also the problem of semi-GC-supported works. For instance, the Python framework can be linked into an application without problems. It can be embedded and used without a problem in GC-supported and -enabled apps. However, certain modules aren’t ready for GC-support. For instance, the objc module cannot be imported. If a script called from the embedded Python interpreter tries to import objc, it fails silently. If you try to import it via PyImport_Import*, then you’ll retrieve a NULL pointer (essentially, nothing). No error thrown, no explanation, just a lot of silent failing.
This means that using a GC-required framework means certain choices must be made. As I mentioned with PluginManager, these choices are becoming a pain in my ass. When I first made the switch to using MacRuby as the basis of Ruby support in PM, I quickly learned that it was incompatible with CamelBones. At the time, I had no problems with it. I never was a big fan of Perl (although I have great respect for it) and CamelBones appears to be a dying or dead codebase. The Perl community also doesn’t appear to be nearly as large as it was when I made a my transition to OS X (just after the release of 10.1). All things considered, losing Perl to pick up Ruby support wasn’t a difficult decision.
However, once I started working with MacRuby and turned on garbage collection support, I soon came across a bug. My Python support wasn’t working as I expected. It took me a while to figure it out since, as I mentioned above, it was failing silently. However, I found out that it wasn’t working as I wanted and came to the conclusion above: turning on GC-support caused the objc module to fail silently. This is a bit of a stickler for me. I love Python. I’d rather have Python than Ruby, but I still don’t want to have to choose.
I want Python and Ruby support, but can I have both? I suppose I could go through and see which modules work with GC and which ones don’t. If enough of what I depend on won’t work, it’d be wiser to go with Ruby since I expect it to work. However, if most of it works with the exception of objc, perhaps I could find a workaround. I don’t suppose it’d be too difficult to compile that particular module specifically and find some way to load it instead of the default module. The problem is that other modules may not work. If users expect an installed Python module to work but it fails silently because it wasn’t compiled correctly, there’s no win for Python support. It just leads to extra support questions for developers.
The solution here is rather simple: choose Python or Ruby. There’s not really a reasonable workaround if I choose to stick with MacRuby and the necessary garbage collection support. Until I can be assured that all Python modules are GC-compatible on OS X, I can’t enable it in good conscience. If I can get RubyCocoa to work (which I’m not anticipating), I can go back to having both. Unfortunately, PyObjC and garbage collection doesn’t work well together and can lead to subtle bugs.
Leave a Reply