Archive for the 'Toolchain' Category

UTI Property List Helper 1.0

Friday, March 21st, 2008

For a forthcoming blog post, I was recently writing a test app that displays images. This had me once again slogging through the list of System-Declared Uniform Type Identifiers and assembling type declarations for the image types by hand.

I'm tired of doing that. So I wrote a new application called UTI Property List Helper.

The main window consists of a table view, in which you enter UTIs, and two text views displaying chunks of Info.plist XML data.

This application automatically updates the CFBundleDocumentTypes and UTImportedTypeDeclarations arrays in its window with information on the types you enter, obtained from Launch Services' database of all types registered by any app on your system.

No more searching for that OSType, or leaving it out because it's too much work—this app does all the grunt work for you. Enjoy.

.hgignore for Mac OS X applications

Thursday, March 20th, 2008

If you use version control (and you should), then you're familiar with the pollution that an Xcode build folder can put into your status output:

? build/.DS_Store
? build/Debug/UTI Plist Helper.app/Contents/Info.plist
? build/Debug/UTI Plist Helper.app/Contents/MacOS/UTI Plist Helper
? build/Debug/UTI Plist Helper.app/Contents/PkgInfo
? build/Debug/UTI Plist Helper.app/Contents/Resources/English.lproj/InfoPlist.strings
? build/Debug/UTI Plist Helper.app/Contents/Resources/English.lproj/MainMenu.nib/classes.nib
? build/Debug/UTI Plist Helper.app/Contents/Resources/English.lproj/MainMenu.nib/info.nib
? build/Debug/UTI Plist Helper.app/Contents/Resources/English.lproj/MainMenu.nib/keyedobjects.nib
⋮

Good version-control systems offer a way to ignore any file that matches a certain pattern. In the case of an Xcode project, you want to ignore the build folder and a few other things: .DS_Store files, backup nibs (those Foo~.nib packages that IB creates when you save), etc.

In Mercurial, the way to do that is to create a .hgignore file, and populate it with the patterns you want hg to ignore.

In order to save you repetitive work, here's a .hgignore file, already fully-populated, that you can use when versioning your Xcode-based project with Mercurial:

File: hgignore.bz2

syntax: glob

.DS_Store

*.swp
*~.nib

build

*.pbxuser
*.perspective
*.perspectivev3

What to do with this file

  1. Download it, and save the .bz2 file somewhere such as your Documents folder.
  2. cd into the top level of a repository.
  3. Extract the file using this command line: bunzip2 < ~/Documents/hgignore.bz2 > .hgignore
  4. Add the file: hg add .hgignore
  5. Commit it.

Thereafter, not only do you have a .hgignore file keeping your status output clean, but it's versioned, so it's easy for you to track and revert changes to the ignore file over time.

A public thank-you

Monday, March 17th, 2008

This goes out to whichever engineers at Apple fixed the Image Unit template to not suck.

Before Xcode 2.5, that template was useless. Now, it contains everything I need already set up, to the maximum extent possible.

Thank you, Apple engineers.

hg st says modified, but hg diff doesn’t say anything

Tuesday, March 11th, 2008

You have a puzzle. When you run hg st, it says one of your files is modified:

hg st                                                              %~/Python/run_tests(0)
M test.py

But when you run hg diff, it doesn't say anything about the file:

hg diff test.py                                                    %~/Python/run_tests(0)
___
                                                                   %~/Python/run_tests(0)

The reason, at least in my case, was that the file's permissions had changed. hg st acknowledges this change exactly the same way it acknowledges a change to the file's contents, but hg diff only shows changes to the contents of the file, not the metadata. Thus, a metadata change only gets reported by hg st and not by hg diff.

While I prefer hg over svn, this is one advantage that svn has over hg. hg only uses one column to indicate the type of change, and shows the same letter (M) for metadata changes as for content changes. svn, on the other hand, uses seven columns, and a metadata change puts the M in a different column.

There's no way to make hg st use the svn st format, but you can make hg diff show metadata changes. The way to do this is to edit your hgrc file and enable git mode:

[diff]
git=True

There are two hgrc files; you can choose to change either or both. You can edit ~/.hgrc (this is what I recommend), or you can edit the per-repository .hg/hgrc file. (There is no .hg/hgrc file by default, so if you haven't created one already, you would need to create it.)

The difference, as you've probably guessed, is that ~/.hgrc sets hg's default for all repositories, whereas the per-repository hgrc changes the setting only for one repository, overriding ~/.hgrc and hg's own defaults.

Once you make this change, the output from hg diff will include metadata information:

hg diff test.py                                                    %~/Python/run_tests(0)
diff --git a/test.py b/test.py
old mode 100755
new mode 100644
___
chmod a+x test.py                                                  %~/Python/run_tests(0)

Multi-stroke key bindings, Extended vi Edition

Thursday, February 21st, 2008

As some of you know, I use the multi-stroke key bindings by Jacob Rus to easily type strings such as ⌃⇧⌘⌫. I mostly use these characters in three places:

  1. Correspondence with users
  2. Posts here
  3. Documentation

And it sure beats looking those characters up in UnicodeChecker or the Character Palette.

But I was never satisfied with the original set of key bindings that that file provides. The first problem that I had was that the arrow keys are emacs keys, not vi keys:

Solid Left (back)⌃M + ⌃B
Solid Right (forward)⌃M + ⌃F
Solid Up (previous)⌃M + ⌃P
Solid Down (next)⌃M + ⌃N
Dotted Left (back)⌃M + B
Dotted Right (forward)⌃M + F
Dotted Up (previous)⌃M + P
Dotted Down (next)⌃M + N

I'm a vi guy, so I wanted the hjkl arrangement for my arrows. So that was the first change I made.

I also added a bunch of characters, and reassigned some of them:

Additions:
ChDescriptionKey sequence
Return^M + Return
Command^M + C
Command (Apple)^M + ^A
Option^M + ^O
Control^M + ^C
Shift^M + ^S
Caps Lock^M + ^S
Smiling face^M + ^F
Frowning face^M + F
×Multiplication sign^M + ^X
Modifications:
ChDescriptionKey sequenceReason for modification
The arrows (solid and dotted), as noted aboveBecause I prefer vi to emacs
Return^M + ^RTo free up ^E for Escape
Enter^M + RTo free up E for Eject
Home^M + Shift-^HTo free up ^H for ←
End^M + Shift-^ETo free up H for ⇠
Escape^M + ^ETo free up ^X for × (multiplication sign)
Eject^M + ETo keep Eject on the same key as Escape

In case you're wondering about the division sign (÷): The OS already provides this as ⌥/, and always has been (all the way back to 1984). I didn't need to add it.

Also, in case you're wondering about “Apple” above: The Apple logo is ⇧⌥K, so I didn't need to add that, either. (Plus, I never use it.)

File: DefaultKeyBinding.dict.bz2

This file contains all of the above bindings, as well as the ones from Jacob Rus' original that I didn't change.

To install it, put it in the KeyBindings folder in the Library folder in your Home folder. You may need to create the folder. The bindings are loaded on application launch by Cocoa, so you will need to relaunch any already-running applications in order to use the bindings in them.

Quickie: Finding an svn conflict marker with vim

Tuesday, February 12th, 2008
/[<=>]\{7\}

Weirdest compiler warning I’ve ever seen

Saturday, February 9th, 2008

Here's the code:

[[GrowlApplicationController sharedController] setQuitAfterOpen:YES];

And here's the warning:

Core/Source/GrowlApplicationBridgePathway.m:43: warning: ‘GrowlPreferencesController’ may not respond to ‘-setQuitAfterOpen:’

Wait, what? I said GrowlApplicationController, not GrowlPreferencesController!

The problem is that the compiler is recognizing “sharedController” as a class method, and assuming that I'm using the class that it knows has it—which, in this case, is GrowlPreferencesController. GrowlPreferencesController is declared in its header, which is imported by the prefix header; GrowlApplicationController, meanwhile, is declared only with @class (in another header). So I think it's assuming that GrowlApplicationController is a subclass or something.

However, +[GrowlPreferencesController sharedController] is typed as returning a GrowlPreferencesController *, so when I try to call a GrowlApplicationController method on it, it gives me that warning.

The fix is to #import "GrowlApplicationController.h" so that the compiler knows that GrowlApplicationController has a sharedController method of its own.

Generating a tarball of your project

Friday, February 8th, 2008

Every good version-control system has this as a built-in feature.

In the below, $TMP is a staging directory, such as “build”, “/tmp”, or “/Volumes/RAM Disk”. It's also where the final archive will end up.

  • svn: Sort of. svn export $TMP/MyProject && cd $TMP && tar cjf MyProject.tbz MyProject
  • darcs: darcs dist (outputs a .tar.gz file in the current working directory)
  • bzr: bzr export --format=tbz2 $TMP/MyProject.tbz
  • git: git archive --format=tar master | bzip2 > $TMP/MyProject.tbz
  • hg: hg archive -p MyProject -t tbz2 $TMP/MyProject.tbz

More Darcs win: Selective recording

Wednesday, December 12th, 2007

Darcs lets you choose which changes you want to include in a patch (commit). For example:

darcs rec                                                   %~/ASL test apps/aslsearch(0)
move ./aslsearch.m ./aslsearch.c
Shall I record this change? (1/?)  [ynWsfqadjkc], or ? for help: y
hunk ./aslsearch.c 1
-#import <Foundation/Foundation.h>
-
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <unistd.h>
-#include <string.h>
-#include <vis.h>
-#include <asl.h>
-
Shall I record this change? (2/?)  [ynWsfqadjkc], or ? for help: n
hunk ./aslsearch.c 29
-       NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
-
Shall I record this change? (3/?)  [ynWsfqadjkc], or ? for help: y
hunk ./aslsearch.c 75
-       [pool release];
Shall I record this change? (4/?)  [ynWsfqadjkc], or ? for help: y
hunk ./aslsearch.c 194
+       fprintf(stderr, "key: \e2\80\9c%s\e2\80\9d\noperator: 0x%x\nvalue: \e2\80\9c%s\e2\80\9d\n", key, operator, value);
Shall I record this change? (5/?)  [ynWsfqadjkc], or ? for help: n
hunk ./aslsearch.xcodeproj/project.pbxproj 10
-               8DD76F9A0486AA7600D96B5E /* aslsearch.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* aslsearch.m */; settings = {ATTRIBUTES = (); }; };
+               8DD76F9A0486AA7600D96B5E /* aslsearch.c in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* aslsearch.c */; settings = {ATTRIBUTES = (); }; };
Shall I record this change? (6/?)  [ynWsfqadjkc], or ? for help: ?
How to use record...
y: record this patch
n: don't record it
w: wait and decide later, defaulting to no

s: don't record the rest of the changes to this file
f: record the rest of the changes to this file

d: record selected patches, skipping all the remaining patches
a: record all the remaining patches
q: cancel record

j: skip to next patch
k: back up to previous patch
c: calculate number of patches
h or ?: show this help

<Space>: accept the current default (which is capitalized)
hunk ./aslsearch.xcodeproj/project.pbxproj 10
-               8DD76F9A0486AA7600D96B5E /* aslsearch.m in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* aslsearch.m */; settings = {ATTRIBUTES = (); }; };
+               8DD76F9A0486AA7600D96B5E /* aslsearch.c in Sources */ = {isa = PBXBuildFile; fileRef = 08FB7796FE84155DC02AAC07 /* aslsearch.c */; settings = {ATTRIBUTES = (); }; };
Shall I record this change? (6/?)  [ynWsfqadjkc], or ? for help: f
hunk ./aslsearch_Prefix.pch 5
-#ifdef __OBJC__
-#      import <Foundation/Foundation.h>
-#endif
-
Shall I record this change? (10/?)  [ynWsfqadjkc], or ? for help: y
hunk ./aslsearch_Prefix.pch 8
+#include <unistd.h>
Shall I record this change? (11/?)  [ynWsfqadjkc], or ? for help: n
What is the patch name? Switched source file from Obj-C to pure C
Do you want to add a long comment? [yn]n
Finished recording patch 'Switched source file from Obj-C to pure C'

(Before you mention the UTF-8 sequences that Darcs called out as invalid ASCII: Yes, I know.)

From svn to darcs: Listing unknown files

Sunday, December 2nd, 2007

If you're an svn user looking at darcs, you may wonder how to find unknown files—that is, files that you've created in your working directory, but haven't yet told your VCS about (with svn add or darcs add). In svn, the command is svn st, but in darcs, the procedure is not obvious.

The darcs command that most seems like what you need is darcs whatsnew, or, as I type it, simply darcs what. Like svn st, this shows you all files that have been added, removed, or modified; it also includes a diff, so not only is it your substitute for svn st, it's also your substitute for svn diff. But it doesn't show you unknown files.

Until you use the -l (that's a lowercase L) option.

darcs what -l changes the output greatly, as it's in a completely different format: one similar to that of svn st. The format is actually that of darcs what -s (summary mode); the difference is that -l also lists unknown files.

There are some differences between darcs what -l and svn st. Some of these differences are:

  • darcs uses fewer columns than svn: It has one status column, whereas svn has six.
  • darcs uses ‘a’ to indicate an unknown file, whereas svn uses ‘?’.
  • darcs uses ‘R’ to indicate a file that you've removed, whereas svn uses ‘D’ to indicate a file that you've deleted. (svn supports remove and rm as synonyms for its delete command, but darcs does not support delete nor rm as synonyms for its remove command.)

darcs is becoming my favorite VCS

Friday, October 26th, 2007

Here's a real-world situation that tells you why I'm falling in love with darcs.

I'm working on a plug-in that logs all notifications posted by Apple Mail to a couple of files. The idea of this plug-in is to enable Mail-bundle developers to develop more reliable plug-ins by listening for notifications rather than posing as Mail's classes or swizzling its methods.

(As you know, I don't ordinarily announce what I'm working on before it's ready; for this post, it's important to provide context.)

The first revision of the plug-in used NSLog, but I found that this spammed console.log rather heavily. So I changed it to use two separate log files, which I write to using NSFileHandles.

Then I started work on a new feature: datestamps. While working on that, however, I noticed that I had forgotten to close and release my two file handles in -dealloc. Oops.

Now, remember that I had local changes. If this code were versioned in svn, I would have to:

  1. bzip2 the source file (which renames it as a side-effect).
  2. svn up the file in order to restore the pristine copy.
  3. Open it in a separate editor (so as not to lose undo history) and add the closeFile and release messages that I'd forgotten.
  4. Save and exit.
  5. Commit.
  6. Delete the committed file and bunzip2 my original one.
  7. Go through an svn diff and redo the fix in my modified file so I don't clobber the changes in my next commit.

What a hassle. But darcs has first-class support for just this situation.

All I had to do was darcs amend-record. darcs asked me whether the most recent commit was the one I wanted to amend, then it offered me the choice of which hunks of local changes I wanted to include in the amendment. I said yes to the one with the closeFile and release messages and no to all the others. That's all I had to do.

Let me summarize that for you: I was able to retroactively fix my previous commit while I had other local changes in the same file, without sweeping up those other changes into the commit.

That's awesome.

What do different VCSs do when a file changes?

Thursday, October 11th, 2007

Here's the scenario:

  1. You change a file.
  2. You invoke your-vcs-here commit file.txt (or equivalent).
  3. You write a commit message in the editor.
  4. You suspend the editor with ^z and make another change to a file. (Perhaps you started the commit and a build at the same time, and the build failed because you forgot a comma.)
  5. You resume the editor, and issue :wq or ^x^c to save and exit.

What happens?

Subversion

svn commits the changed version of the file—in other words, the file as it existed when the editor exited.

Monotone

mtn notices that the file changed, tells you “you can't do that”, and throws away saves the commit message.

More precisely, it says:

mtn: misuse: file 'Test.txt' modified during commit, aborting

darcs

darcs commits the file as it existed before it started the editor. (Presumably, it copies changed files to a staging area rather than simply committing them from the WC.)

Mercurial

hg commits the file as it existed before it started the editor, just as darcs does.


UPDATE 2008-03-05: Jack, in a comment below, corrected me on what Monotone does with the commit message. I have applied this correction above.

UPDATE 2008-03-26: jpc, in a comment below, corrected me on what Mercurial does with the commit message. I thought that that's what I had written, but I wrote the wrong thing: that it acted like svn, rather than darcs. I apologize for the brainfart, and I have applied this correction above.

Growl Registration Dictionary Editor

Monday, October 1st, 2007

Since 0.7, Growl has been able to detect a registration dictionary in your application when your app launches—all you have to do is put it in a file with a certain name inside your app bundle. We call this feature auto-discovery or auto-registration.

I don't think people have been taking much advantage of this feature, though, for two reasons:

  1. We didn't mention it in the docs. (This would be the major reason.)
  2. There was no handy-dandy editor to quickly bash out a registration dictionary file.

Sure, we can all use Property List Editor, but that's not a tool honed to the purpose. You have to rifle through GrowlDefines.h to find all the different keys you can use in the dictionary (the docs only cover the most basic set).

Well, now there's a tool honed to the purpose.

Here's beta 1 of the Growl Registration Dictionary Editor. I invite you to try it. Obvious things worth doing:

  • Creating new reg dicts
  • Editing existing ones
  • Importing a reg dict from your saved tickets (great for porting your existing app to use auto-reg!)

Feedback will be appreciated. If you're subscribed to the Growl discussion list, you can send it there; if not, comments here are OK.

The source for the GRDE is in the Growl source repository.

If this announcement looks familiar to you, don't worry; you're not going crazy. I posted a similar message to the Growl discussion list, then decided to edit it a bit and post it here, since I know that at least several of you are Growl framework users who can put this app to good use.

New utility: Make RAM Disk

Saturday, September 29th, 2007

Make RAM Disk is a simple app to create and mount a RAM disk on Mac OS X 10.4 or later. It encapsulates my previously-stated series of terminal commands into a tiny little app—just launch it and go.

I use my RAM disk for three things:

  • Download holding area. I have Mail, Safari, OmniWeb, and Adium all set to download files to the RAM disk. If I decide I don't want to keep the download, I just leave it there until shutdown (only works on my desktop Mac).
  • iShowU storage. I create a 1-GiB RAM Disk named “iShowU temp” which iShowU is set to use as its scratch disk. Using a RAM disk for this helps your recording frame rate.
  • Xcode build folder. On my laptop, I symlink the project's build folder over to the RAM disk (e.g., mkdir /Volumes/RAM\ Disk/Adium-build && ln -s /Volumes/RAM\ Disk/Adium-build build). This cuts out a lot of the disk grinding that happens during a build, which I believe helps my battery life.

Enjoy!

Appcasts on Google

Thursday, September 27th, 2007

If you publish an appcast, you should also publish a robots.txt file that tells search engines such as Google not index the appcast file. I came to this realization after searching for “appcast editor”: the first four hits, and several of the others, are all just appcasts.

There is no point to having these indexed by Google, so save the bandwidth and help people's search results—tell the search engines not to index them.

And I never did find an appcast editor. I guess I get to write it by hand in vim. (There's one in MacCode, but it's not working yet.)

UPDATE: I knew there was an appcast editor out there! It's Feeder, by Reinvented Software. Thanks to Andy Kim for pointing it out in the comments.

zsh completion rocks

Monday, September 17th, 2007

Specifically, enable and configure zshcompsys. Here's what this will get you (6 seconds; 144 K; requires any version of QuickTime):

UPDATE 2007-09-18 11:54 PDT: More info in the comments.

Quicksilver + SVN = yum

Friday, August 17th, 2007

Go directly to the plug-ins pane of your Quicksilver Preferences and install the Subversion module. Do not pass Go. Do not check out $200.

Best when combined with the comma trick.

So far, I've tried both add and ci, and they work in the obvious way. They even provide a notification (which goes through the Growl notifier for me—that's another plug-in) with the command's output.

WWDC 2007 session videos are out

Monday, July 30th, 2007

If you attended WWDC, you can head over to ADC on iTunes and see what you missed.

A simple way to make your NSLogs and NSAsserts more informative

Wednesday, June 13th, 2007

OK, so I'm not totally radio-silent. I learned about this in a WWDC session, but since it's already public API in Tiger (actually, it's a GCC extension), I can talk about it.

It's a built-in macro called __PRETTY_FUNCTION__. This is a fully-qualified human-readable signature of the function you're in. The GCC docs don't mention this part, but it even works in Objective-C, in addition to C++ and plain C. Here's a test app, containing this code:

@implementation Blah(blah)
- (void)blah {
    NSLog(@"%s", __PRETTY_FUNCTION__);
}
@end

And the output:

2007-06-14 07:50:37.733 printmethod[1800] -[Blah(blah) blah]

Notice that it includes the class name, category name (if any), and method selector.

Note that that's a C-string, not an NSCFString. Be sure to set up your format string accordingly.

Bug in the accessors generator

Tuesday, June 5th, 2007
 unsigned short multiplierBase, multiplierPower;

The output from the accessors generator:

- (unsigned) short;
- (void) setShort:(unsigned)newShort;

- (unsigned) multiplierBase;
- (void) setMultiplierBase:(unsigned)newMultiplierBase;

- (unsigned) multiplierPower;
- (void) setMultiplierPower:(unsigned)newMultiplierPower;

This is both an advisory to you and a reminder to me. I'll be tackling this after WWDC.