BOLTS : Click to return to MacEdition homepage
 

Cocoa development in Emacs - Part 2

By Bork Bork on March 24, 2002 (reprinted from http://www.borkware.com with permission)

Cocoa programming In Emacs

Editing

Just edit your source files as usual in Emacs. If your Objective-C files end with ‘.m’, you’ll automagically be put into Objective-C mode. I find the default Objective-C formatting to be annoying. (I like four-space indentation.) In your .emacs file, add (c-set-style "bsd") – you can then use [tab] to indent the code just like in C mode. It’ll auto-format Objective-C methods with multi-line arguments thusly:
    [image drawAtPoint: NSZeroPoint
       fromRect: NSMakeRect(0, 0, WIDTH, HEIGHT)
       operation: compositingOperation
       fraction: 1.0];
... left-aligning and indenting all of the method names. Like C-mode you’ll need to do the dirty work of pressing [ret] [tab] after each line as you’re entering the code. If you want the arguments to line up too, you can force-include tabs by doing C-q [tab]. That’ll insert a tab character (which Emacs then interprets as a spacing operation) rather than having the tab interpreted as a command to indent the line. One handy shortcut: C-i is the same as the [tab] key, but it’s a whole lot easier to do C-q C-i than to do C-q [tab].

Depending on your Emacs mojo, you can set up various bits of magic to automatically create headers, NSView subclasses and other conveniences that Project Builder has. I find those annoying (I’m easily annoyed, if you couldn’t tell already) and always ended up deleting what Project Builder made for me anyway.

The compiling command I use relies on Project Builder having a list of involved files in the Files pane of the project window. To support this, I create the files as usual in Emacs. When it comes time to sticking them into the project, I open a Finder window to my source directory and drag the files into the appropriate place in the Project Builder window (which I keep minimized to hide the jazz I don’t use – see the window up there in my workspace screen shot). This has the nice side effect of letting me organize the files in Project Builder for public consumption (making nice little hierarchies) while freeing me of actually having to *use* nice little hierarchies that require extra time to open and close when looking through them.

Compiling

Now to the good stuff. To build in Emacs, run the program ’pbxbuild’ in a shell buffer in which you’ve changed to the directory where your Project Builder file lives. (translation: cd ~/Development/cocoa/Greeble). This command will generate a lot of spew. If you have syntax errors, the build will stop after the first file and say something like this:

BWQuartzViewDemo.m:88: illegal function call, found `;’
cpp-precomp: warning: errors during smart preprocessing, retrying in basic
mode
BWQuartzViewDemo.m: In function `DrawStarburst': BWQuartzViewDemo.m:8:
warning: unused variable `i&'; BWQuartzViewDemo.m: In function
`-[BWQuartzViewDemo drawRect:withContext:]': BWQuartzViewDemo.m:88:
parse error before `;' ...failed CompileC
/Users/markd/Development/Borkware/GrafDemo/build/GrafDemo.build/GrafDemo.bui
ld/Objects/ppc/BWQuartzViewDemo.o ...
** BUILD FAILED **

What had happened was that I forgot to close a parenthesis on line 88, and it looks like I got a bit sloppy on line 8 by leaving an unused variable. (Bad Bork!) What I’d do upon seeing this is go to the BWQuartzViewDemo.m (see random hints for moving between similarly-named buffers easier), jump to line 88 and assess the problem (which is usually just something dumb). You can go to line 88 by using M-x goto-line [ret] 88 [ret]. That’s too much typing, so I have a key binding to use – C-x n – as a shortcut. I thank the BooBooHead for this idea. Stick this in your .emacs file:
(global-set-key "\M-n" 'goto-line)

Granted, this is a little more work than clicking a bright red error line in one of the sliding Project Builder tabs. I look at it as an incentive to not make mistakes in the first place.

What doesn’t show up very clearly are warnings in source files. Since they’re not errors they don’t halt the pbxbuild command, and so they may get lost in its noise. I could probably write a spiffy Perl or Python script to filter pbxbuild and show those warnings, but I’m too lazy to do that. If I happen to cause something like:

BWQuartzViewDemo.m: In function `-[BWQuartzViewDemo initWithFrame:]':
BWQuartzViewDemo.m:52: warning: `BWQuartzView' does not respond to
`initwithFrame:' BWQuartzViewDemo.m:52: warning: passing arg 1 of
`objc_msgSendSuper' from
incompatible pointer type

... (note that I dorked the capitalization of init*w*ithFrame), I could miss that in the pbxbuild output. After a while you get used to the style of output from pbxbuild and you’ll notice the lines printed for a warning will catch your eye as they scroll past. You can also do a reverse search on “warning”, e.g. C-r warn

By default (at least with the December DevTools), the project depends on itself, so it will always generate a warning each time you build. That’s handy to know that you’ve found the beginning of your build and don’t have warnings. If you do see some warnings, you can see what they are and fix them. Don’t forget that M-> takes you back to the end of your shell buffer so you can get back to the command line, so checking for warnings (if you even suspect them) is C-r warn M-> (7 keystrokes) which can be executed very quickly.

Okay, remember the !x shell idiom I blathered about earlier? After you run pbxbuild the first time, you can run it quickly again by doing !p.

Running

Now here’s one of those awkward commands that I could wrap in a shell script, but I choose not to since I’m lazy. (I actually tried a Python script and a little Cocoa app to do this, and they failed in weird and wonderful ways, probably due to stdin and stdout not getting set up properly before executing the Cocoa program. After an hour I stopped).

If you’re in the directory with your project file (like with a project created by Project Builder), the executable for your program lives at:
build/ProjectName.app/Contents/MacOS/ProjectName

When I first start working with a program in a new shell session, I have to invoke that full path name on the command line to run the program:
% build/ProjectName.app/Contents/MacOS/ProjectName
I can hear you booing and hissing. “Where’s your love of few keystrokes? Where’s the speed, man? I don’t want to type that !&@><*e#! in every time."

First off, entering that command string isn’t too hard. You can do it with:

bu [tab] Proj [tab] app [tab] [tab] M [tab] [tab] [ret]

... 16 keystrokes for a 48-ish character string. Not as bad as it looks. For every subsequent invocation of this program, you can do the two-stroker !b (remember that fetches the last command that starts with ‘b’).

So once I get the buffer warmed up with a single:
% pbxbuild
... and a single: % build/ProjectName.app/Contents/MacOS/ProjectName
... my compile / run cycle is now:
% !p
... and do a quick eyeball pbxbuild’s spew:
% !b

... over and over and over (and over) again. I almost never quit my Emacs (or my Terminal.app for that matter) so my shell buffer stays warmed up for days and days (sometimes even weeks if I’m working on the same project), which makes the (not-really-that) onerous initial program launch not as bad has having to do it every single time.

Debugging

I love gdb. I’ve used it for over ten years on various Unix projects in my career. Jasik’s The Debugger is about the only other debugger I like nearly as much. (Well, and December’s ladebug on Digital Unix, back in the day when I was doing multithreaded apps.)

Invoking gdb is easy. It’s:
% gdb build/ProjectName.app/Contents/MacOS/ProjectName
Okay, so that’s kind of nasty to have to type in. Remember the !$ thingie (pick off the last word from a command)? Here’s a place where it’s very useful. Assume your Emacs shell buffer is warmed up having done at least one pbxbuild and one build/ProjectName/... command.

Now, compile:
% !p
... which runs the command pbxbuild ...
% !b ... which does the command
build/ProjectName.app/Contents/MacOS/ProjectName.

Oops, you crashed your program by Doing Something. Better run it in gdb: % gdb !$ ... which will then do the command gdb build/ProjectName.app/Contents/MacOS/ProjectName.

(Okay, okay – you could use !! also, since build/ProjectName.app/... is the entire command as well as being the last word. I like using the mental model of !! for commands and !$ for file names, and for feeding gdb you give it a file name, hence I use !$. I also try not to think too hard about this kind of stuff. It’s under my fingers, it’s fast, and it works.)

Here are some quick gdb commands to get you started

You can also run gdb inside of Emacs like an IDE (single-stepping shows a => in the code to show what line you’re on, setting breakpoints with a keystroke in the source code buffer). To do this, use: M-x gdb [ret]

... give it the path to the program file in your source code buffer nad do a C-x [space], which sets a break point. Then in the gdb buffer (called *gud-ProjectName*, which you can rename to something more reasonable) enter the run command. Then do whatever it takes to trigger your breakpoint. In the gdb buffer, do your ‘step’ and ’next’ commands and see the effect on your source code.

Personally, I don’t use debuggers very much: usually just when I have a crasher that I want to see a stack trace for. The command-line version of gdb in a shell is pretty much all I use now. If you do like using debuggers to step through stuff (handy if you’re dealing with a lot of unfamiliar code, or if you’re billing by the hour), the IDE mode may serve you well.

One of the cooler things you can do with the command-line gdb that you can’t do with the current crop of OS/X GUI debuggers is to ssh into a computer, attach to a running program, and poke around. Let’s say you have a user that’s consistently having problems with your GUI app that you just can’t reproduce. Perhaps the office has a stray Bogon generator. You can ssh into their machine (after they give you a login with sufficient privileges), attach gdb to their running program and set breakpoints as they mess around with stuff until the code breaks. They can then minimize the unresponsive windows of your app while you’re dinking around. They can get on with their work in other programs until you’re ready for them to do something else.

Random hints

Here are some random hints, shortcuts and techniques I use to optimize my workflow. What works for me might not work for you. The best thing is to just wade in, make mistakes, find out what works (and do it) and what annoys you (and avoid it).

Shells

tcsh comes by default with OS X. Unfortunately, sometimes Emacs spazzes out and says:
  Warning: no access to tty (Inappropriate ioctl for device).
  Thus no job control in this shell

... which stinks since you can’t control-C a program to interrupt it, and worst of all, programs that prompt for passwords (like scp and cvs which running remotely) will just not work – what a pain. One workaround is to use bash instead of tcsh. You can get bash from apple’s downloads, and make it your shell with:
sudo niutil -createprop . /users/bork shell /usr/local/bin/bash
... or whatever path you specify for bash.

Buffer names

If you have similarly named file names due to consistent nomenclature (for example, all fine Borkware classes start with BW), having to retype or tab-complete the file names all the time when switching between them is a royal pain in the ass. If I’m going to be working with a set of files for a while, I rename the buffer (which doesn’t affect the name of the file) to something easier to type.

So say I’m working with the files BWQuartzView.h, BWQuartzView.m, BWQuartzDemo.h and BWQuartzDemo.m. Switching between these files is awkward. What I do is rename the buffers thusly:

Moving to the .m files requires 4 keystrokes, C-x b q [tab] [ret] or C-x b d [tab] [ret], and three of those keystrokes are overhead you’ll have to pay no matter what. Moving to the respective .h file requires 5 C-x b hq [tab] [ret], etc. /

Split windows

C-x 2 splits Emacs horizontally. You can have a different buffer open in each pane (say, a header file above and the C source code below, or a source file above and the compiling shell that shows the errors below).

You can also split Emacs vertically with C-x 3. If you have a nice big screen, you can make your Emacs window huge and have your source and header files side-by-side.

You can subdivide these panes as well. C-x 3 C-x 2 will split Emacs vertically, then split the left-hand buffer in half (giving you three panes to play with). C-x o will move you ‘forward’ through the panes. A buddy of mine from BitWrangler gave me some Emacs magic to move ‘backwards’ through the panes as well, using C-x p:

(define-key global-map "\C-xp" 'previous-window)

(defun previous-window (&optional back-count)
  "Select the ARG'th previous window"
  (interactive "P")
  (if back-count
      (setq back-count (prefix-numeric-value back-count))
    (setq back-count 1))
  (other-window (- back-count)))

Moby.h

Emacs’ incremental search is awesome. C-s starts the search, then you type, and the first thing that matches what you’ve typed up to that point scrolls into view. I concatenate the Foundation Kit and Application Kit header files into one big “Moby” file. This is very handy for exploring the API, as well as getting the argument names and types for a message.

To create the file, execute these two lines of code in a shell:

% cat /System/Library/Frameworks/AppKit.framework/Headers/*.h > ~/moby.h
% cat /System/Library/Frameworks/Foundation.framework/Headers/*.h >> ~/moby.h

To find a particular class in there, do an incremental search on the tab character, like C-s C-q C-i NSObject

On to Part 3  Back to Part 1

E-mail this story to a friend

Talkback on this story!

Cannot connect to the database.
Please contact the administrator.