This is my typical workspace |
I've moved the motivation for all of this to the rants section at the end. I figured most folks
aren't interested in my ego my history and just want
to get to the good stuff.
The instructions presented here match my particular development style. I don't mind my build environment having some rough edges. I don't mind having to expend a little extra mental energy when doing my work. I also want to minimize keystrokes when it makes sense. I do want my compile and running and debugging turnarounds to be as fast as possible. (Make Mistakes Faster was the advertising tag line for a classic Mac C compiler) Once my emacs buffers get warmed up, I've gotten my compiling and running down to two keystrokes each (well, three if you count "return". From here on I'm not counting the "return" key. nyah). With everything running in one emacs process, I never have to touch the mouse (or the annoying TiBook trackpad) while I'm reading or editing source code or browsing the system header files. I do use Mail.app for mail reading and iCab to read the Cocoa documentation, so I'm not a complete GUI luddite.
With that said, I presume you're somewhat familiar with emacs, how to open files, how to move around, and how keystrokes are described. For example, M-x goto-line [ret] 321 [ret] is press meta/escape, then x, type goto-line (with tab completion if you so desire), return, 321, return), and that you know what the .emacs startup initialization file is and how to add stuff to it. If you don't know emacs, you might want to run the built-in tutorial. (start emacs. Press escape, then 'x', then type 'help', press return, press 't', then follow along) O'Reilly (of course) has a book on emacs
The unix shell is a wonderful thing. You can batch process files
(line count of your source files: wc -l *.[hm])
have pipelines of commands
(find the pid of a program you want to kill: ps auxw | grep myRunawayApp).
Using CVS, you can also do all of your revision control at the command line.
One (sometimes) overlooked emacs feature is running a shell inside of an emacs buffer. You do this with M-x shell. *poof*. You now have a buffer named *shell* that will accept shell commands. The M-p and M-n keystrokes cycle you through commands you've previously entered during that shell session. One (frequently) overlooked emacs feature relating to shells is you can rename the buffer. I know, who doesn't just love the buffer name *shell*.
I name my shell buffer 'qwe' via M-x rename-buffer [ret] qwe [ret]. (which is even faster if you do M-x ren [tab] b [tab] qwe [ret] and have emacs do its command completion) Why such a weird name like 'qwe'? On US keyboards (which I'm using), 'qwe' are really fast to type. To bounce to my main shell, I just do C-x b qwe [ret] (5 keystrokes). Notice I said *main* shell. After you rename your shell, you can create another one with M-x shell. (that one I name 'asd') That's very handy if you frequently run commands out of two different working directories. In my qwe buffer, I cd /some/directory and in the asd I cd /some/other/directory/baby. I find bouncing between emacs shell buffers much faster than repeatedly changing directories in a single shell. Another advantage is that each shell will have its own command history, allowing for some handy shell shortcuts. One downer is that some shell built-in control keys (like C-p / C-n to cycle through the history) don't work in emacs.
The shell maintains a history of the last hundred or so commands that you have done. You can use some keystrokes to cycle through these commands or to search backwards for an interesting command. That's all well and good. I never use those features. Instead, I use the csh (and bash) !!, !$, and !x . No, that's not cursing at you. !! (I pronounce bang-bang) says "paste in the complete previous command", while !$ (which I pronounce as bang-string. Yeah, I learned to program using BASIC, and I'm proud) says "paste in the last word of the previous command", where word is the last blob of characters with a space before it. !x executes the most recent command that starts with x. So What. Big Deal.
Here's where things get fun. Say you have a complex command:
% find / -name "flargle.[mh]" -print(on your file system, print out the full path of any file named flargle.m or flargle.h)
That you had to interrupt and want to re-run (or you want to re-run it again). You can just re-do the command by doing
% !!
Or, if you decided you wanted to re-run the command, but direct output to a different file
% !! > /tmp/oopackor if you wanted wanted to put all of those files into a tarfile for safe keeping.
% tar cf ~/flarglefiles.tar `!!`(the backticks execute the find command in a subshell, then pastes the contents into the tar command, which then gets executed)
Now, say you've done a bunch of other commands, but now want to run that find command again. You can use the shell or emacs command history keys to laboriously cycle through commands, or you can do
% !fto execute the most recent command that starts with 'f', which hopefully will be your 'find' command. As you get accustomed to living on the command line and using the !! and !x form of commands, you'll develop an awareness of commands in your history.
Now what about the !$ combination which pulls off the last word of the previous command. Here's some quick examples:
% ls -l some-file-name-blah.h(to see the file size, permissions, etc. Presumably you used tab completion for the file, or you typed the whole thing just for fun.)
% wc -l !$(you've just now computed the line count without retyping any of the file name again)
% cvs diff !$(use cvs to show the differences in this file from what's in the source code management repository)
% cvs commit -m "twiddled the blargleflah" !$(and now you've checked in your changes) That's 4 different commands working on the same file, and you only really had to specify the file name once.
Of course, the nit-pickers in the audience will say "Yo! That !$ paradigm thingie will break if you deal with more than one file name at a time" And to those nit-pickers I say "well, yeah". Like I said In The Beginning, I don't mind some rough edges. If I need to work with multiple file names in one line, I'll use the shell or emacs commands to cycle through the history and edit the command. For a great majority of the time I'm dealing with one file for some operations, so !$ works perfectly well.
(setq c-default-style "bsd" c-basic-offset 4)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 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 such conveniences like 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 (where 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.
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.build/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 going to the
BWQuartzViewDemo.m (see random hints for moving between similarly-named
buffers easier), jumping to line 88 and assessing 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 thanks 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 the sliding Project Builder tabs. I look at it as an incentive to not make mistakes in the first place.
One thing that 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 awhile 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.
OK, 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.
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/ProjectNameI 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 48ish 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
% pbxbuildand a single
% build/ProjectName.app/Contents/MacOS/ProjectNamemy compile / run cycle is now
% !p(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.
Invoking gdb is easy. It's
% gdb build/ProjectName.app/Contents/MacOS/ProjectNameOK, 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/ProjecctName/... 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)
(ok, ok, you could use !! also, since the 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, 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, do a C-x [space] - that 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 privilege), 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.
Warning: no access to tty (Inappropriate ioctl for device). Thus no job control in this shellWhich 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 work around 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)
Correction - bash has the same problem. Check out the Emacs Quickies for a fix. In short: you'll need need to get the emacs source, add a single #define, and rebuild it.
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.
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 your '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)))
C-s
starts the
search, then you type, and the first thing that matchines you've
typed so far 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
I'm probably not your typical Mac developer (living inside of CodeWarrior most of the time). Way back when (in 1985 or so) I started programming the Mac using TML Pascal, which was pretty much just a compiler tool (I wasn't rich enough to afford a Lisa to use the development environment, and was too much of a wuss to learn 68K machine language to use the mac-native assembler.) The work cycle was editing your code in "Edit" (the programmer's editor) and then tell the editor to run the compiler. The editor was lean and mean. No fancy formatting, no second-guessing my intentions. I found the (toy) Macintosh Pascal environment (and later Lightspeed Pascal) editors to be infuriating for that reason. Make one typo which causes a syntax error and your code would be drawn outlined (e.g. unreadable, frequently shooting the line half-way across the screen since outlined type is nearly twice as wide as regular type). It would also straightjacket force a particular indentation style, with no deviation (even if you had good reasons to). So I stuck with Edit + TML Pascal. Lightspeed C came out shortly thereafter with a very simple stay-out-of-your-way editor plus a disturbingly fast compiler. I was in nerdvana.
Then along came MPW, the Macintosh Programmer's Workshop, which I started using in 1989 or so. I loved it. I never really comprehended the command line stuff at the time but I managed to have a nice worksheet of commands I could execute, and the editor was pretty much invisible to the work I was doing.
My first job out of college was at a unix shop where I found and fell in love with emacs. After I got used to the pretty bizarre (to my mind) command keys, I just moved in with all my stuff. I ran all my shells in one emacs process. I read my email there. I edited source code, compiled programs, and ran the debugger from there. My cow-orkers gave me a hard time because we were developing a programmer's graphical toolkit but I did most of my work in a single emacs window the size of my screen. (split down the middle with C-x 3 so I could see my header file and C file at the same time side-by-side in 80 column frames). And to top it off, the automatic indentation features of emacs actually worked well. They fit my coding style like a glove (with one exception involving the case labels in a switch statement, but I'm too lazy to find the final tweaks to address that)
After that job I was at AOL working on publishing tools for the 3.0 Mac client (as well as doing some small parts of the client itself) in C++ and PowerPlant. There I used Preditor as my editor (a really cool, if a bit unstable editor. When you're trapped in mouse-land, the grabbing-hand scrolling was a god-send) and Metrowerks Code Warrior as a compilation engine driven by the editor. (and to prove how off-kilter I was, I used and loved Jasik's The Debugger rather than the CW debugger) When the publisher team got sent away to California, I did web server programming (emacs + C) and later web site programming (emacs + Oracle + Tcl) on various flavors of unix.
And now comes Mac OS X, my ultimate fantasy. Wicked cool NeXT technology (even though the bastards wouldn't sell me a machine back in 1989. Grrrrr) in the form of the Cocoa libraries, unix underware, plus the delicious Mac OS fluffy goodness I've come to expect from Apple. Time to start programming on this dream team of technological magic.
There was just one problem : Project Builder. I love emacs. Tons of power packed into an unobtrusive editor. Project builder, while I'm sure is great for those who like IDEs, just grated on my nerves from day one. It supports emacs key bindings so my fingers felt at home. At least until I discovered that there were some fundamental differences - like C-v scrolling the window but not moving the cursor, or frequently used commands (like C-x C-s to save a file) that weren't bound.
Add to that the panels that slide around on their own power whenever you compile or run a program. I found myself doing a lot of panel position maintenance (and getting frustrated waiting for the panels to finish their moving. Yes it happens quickly, but it's still a non-zero amount of time consumed for eye candy I'm not interested in or impressed by). I know that Shift-Command-M and Command-Option-M will close various panels, but I occasionally didn't hold down shift or option hard enough and the (very annoying. I'm still easily annoyed) Command-M combination dutifully sent my window to the dock. So much for my train of thought.
The indentation features of Project Builder also didn't work nearly as well as they did in Preditor (or the ancient Edit as well). Sometimes you'd backspace over a single space and the editor would decide to remove a tab or two. It also only supported indent / outdent by a tab stop (e.g. Command-[) but not by a space (e.g. Shift-Command-[). I'm also very particular about my code presentation, and not having that kind of control (in lieu of the indentation features of emacs) was maddening.
The final straw was not being smart enough to figure out how to remove the 'breakpoint' bar from all the text editor windows. I never use it, yet I was always clicking in it leaving stray breakpoints around (which annoy me, so I just have to remove them), so a stray click led to dumping my train of thought plus a click-drag to remove the breakpoint (since just clicking on it again wouldn't remove it - just draw it grayed). I know, I know, "wah, poor baby. Bothered by the widdle biddy breakpoint arrows." Go stuff yourself. I don't want my development environment to annoy me.
After working through Aaron Hillegass' book completely in Project Builder hoping either to drink the kool-aid and learn to love Project Builder's editing environment, or perhaps coming across a project that required some spiffy Project Builder feature, I decided to figure out how to do all of my editing, compiling, and debugging entirely inside of emacs like in the Good Old Days. The other stuff that Project Builder does well, like managing and building projects, and tweaking build parameters, I'll let Project Builder handle. Also since I'm planning on sharing a bunch of source code with the world, I figured I better supply Project Builder projects if I expect folks to actually use them.