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 lovegdb
. 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
-
help
– Shows help. -
run
– Runs the program. -
where
– Shows a stack trace (handy if you crash).
The stack trace is newest functions on top, oldest on the bottom. If you have a deep stack, you may need to scroll up. I recommend running gdb inside of an Emacs shell buffer so you can scroll around it like any Emacs buffer. -
up
/down
– Move up and down the stack frames (moving from function to function on the call stack). You can give these commands a numerical argument to move#
stack frames:
up 5
-
break -[ClassName methodName: args:]
- Set a breakpoint on a particular method in Objective-C. So, you could do something like
break -[BWCurveCanvas drawRect:]
-
info breakpoints
– See your breakpoints as a numbered list. -
del #
– Delete a breakpoint. -
disable breakpoint #
– Disable a particular break point. You can later re-enable it by usingenable breakpoint #
. -
print someVariableName
– Display the contents of a variable. -
po someObjectReference
– Call the object’s toString method and display it. Note this may be dangerous if the object is trashed. -
thread apply all where
– See a stack trace for all threads. -
list
– See where in the code you are. -
next
– Execute the next statement, not going into functions. -
step
– Execute the next statement, stepping into functions. -
kill
– Terminate the program. -
quit
– Exit the debugger and terminate the program. -
detach
– Let the program run free and disassociate the debugger. -
attach
– Attach the debugger to an already running program.
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:
-
BWQuartzView.m : quartz
-
BWQuartzView.h : hquartz
-
BWQuartzDemo.h : demo
-
BWQuartzDemo.h : hdemo
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