With the Cube up and running, I did as any developer would do and tried to think up a small app that I could write for it. I had to decide what the goal of this app would be…not what the app did, but what I wanted out of this experiment since let’s be honest: I’m not going to be spending a TON of time using that machine. I decided that I wanted to get a lesson in how different OS X is from OPENSTEP, and so to do that, I was going to write a little app that could be compiled and run on both my 10.7 Mac and this old Cube.
I wanted an app that wasn’t too complex, but at the same time would let me make use of non-trivial things like threads and interprocess communication. I decided that a good candidate for this would be a UI over the UNIX grep utility. Not a full-blown UI that presents all options, just something basic: give it a search term, a directory to start from, and a checkbox for Recursive. Or as I like to call it : grep with the only option I ever use.
Creating The App
My first stop was Interface Builder on OPENSTEP on the cube : had to make sure that this simple UI could be done without much work. Since the nib file format has changed significantly from the days of Project Builder compared to xibs in Xcode4, I allowed myself to think of the nibs as the one platform-dependent part. And if you think about it, that would make sense even for a real app, the controller logic might be common code, but the UI should be done to fit in with the rest of the system that it is running on.
With a photo of what the UI might look like with those glorious 4 shades of gray, I jumped into first building the Mac version of the app. I tried only making use of APIs that I knew had been around since 10.0 as that’d likely mean they’d also be in OpenStep.
It’s not exactly a complex app, in a couple hours I had a basic app. It spun off threads that would use NSTask to execute grep with the right arguments, process output, and feed it back to the main thread, and the results would be shown in the table.
I really had no clue what the porting process was going to be like. Maybe this would just compile and run (one can hope, right?). I copied the source files over to my OPENSTEP virtual machine, and fired up Project Builder to create the project. I used the VM instead of the cube just because it’d be faster. Have I mentioned how slow that cube is? Glacial.
Step 1 was to create the nib, again. Oldschool Interface Builder had no way of looking into class files for outlet and actions, hell, IBOutlet didn’t even exist as a keyword. You have to manually tell it about a class, add all outlets it might have, and the actions that can be triggered. In this case I only had a handful of either, and only 1 class instantiated in the nib (the app controller), so it wasn’t too painful.
The source files were imported into the project, and I attempted to build. Errors. A lot of errors. I commented out the parts where it was complaining about methods not existing (obviously I wasn’t diligent enough), and tried to focus on the other errors. I can fix methods not existing somehow afterwards. One of the errors really stumped me though. Google wasn’t too keen on helping out, and I don’t blame it, I bet you no one has seen this error since 1996. After some trial and error I realized that this version of gcc was so old that it expected all stack variables to be defined at the top of a method/function before any code. Not the end of the world, but man is that ever annoying. This meant having to go over every function and make sure everything was defined at the top.
In my original version, worker thread to main thread communication was done via -performSelectorOnMainThread:withObject:waitUntilDone:. OpenStep has NSThread, seems like a pretty safe assumption that it’d have had this NSObject method. Nope. Turns out that method wasn’t added to Cocoa until 10.2. This meant having to rethink how I was getting data back. Apparently the way to do inter-thread communication back then was to use NSConnection to get a proxy object. It felt a little heavy-handed to use this, since it’s made to support proxy objects to other hosts on the network, but when in Rome…
That was the big change, most of the rest was stuff like using NSString initializers that didn’t exist and could be easily swapped out.
Where’d -R go?!
My original app on the Mac would call `grep` or `grep -R` depending on whether the user checked the Recursive checkbox. On OPENSTEP it would just fail whenever I’d try recursive searches, and I was a little puzzled about why. Reading the (humourlessly short) man page told me that -R wasn’t an argument for grep. Well that sucks. Considered my options, like just dropping that checkbox from one or both versions, and decided instead to move the recursive functionality into the app. Now both could call grep with the same arguments all the time.
Back to the Mac
Once the app ran as expected on OPENSTEP, I moved the code back to the and tried to run it there. Like any good project, it didn’t work. Luckily it only needed a couple small changes with how I was getting the proxy object with NSConnection.
Here’s the app, in all of its glory. You can check out the sources on github:
Screenshots of it running on OS X and OPENSTEP.
Main features of the app:
- Can search recursively
- All searches happen on secondary threads so the UI shouldn’t be blocked
- Displays paths for results as well as the line number that matched, and the line of text itself.
- Shows the respective icon for each file
- Double clicking on a result opens that file with whatever app is associated with it
- Shows the app’s status and how many results found so far.