31 August 2005

bdepend Problems Solved

I just figured out why I was having problems with module initialization. I also solved a long-standing mystery in my own mind: what the hell are those .mco files that Bigloo produces all the time? In fact, .mco files are module checksums---reproduced (I assume) every time the interface to a module changes. So, bdepend does, in fact, know about dependencies on (import ...) modules; it just uses the .mco files to compute these dependencies, not the .scm files themselves! Very smart, since the .scm file could change in a way that doesn't affect the interface and therefore wouldn't require recompilation of every module which (imports ...) said .scm file.

So the solution to my problem is to strike the -fno-mco flag on bdepend, and add a -fmco flag when bigloo is compiling a module. Ta-da! No more inconsistent module initialization, and no more complete system recompiles. Wonderful!

P.S.--Maybe someone (me?) should write something up for inclusion into the Bigloo manual about bdepend and mco files. There's very little in there right now (hence my problems making this work).

19 August 2005

Bigloo GC and SIGINT

I've noticed in my work with Bigloo that sometimes when a process running from the REPL is sent SIGINT (C-c), the GC hangs in the function GC_lock. I don't know if it's related to dynamic-load-ing on OS X, or whether this is a general problem, and the GC is not very resistant to SIGINT. I suppose it could also be my optimization options on the compiled C code (I'm optimizing pretty heavily for my numerical work). I'm just putting it down here so I don't forget to file a bug report if I ever figure out the cause.

16 August 2005

Inconsistent Module Initialization

I've been getting a lot of errors like the following lately from Bigloo:
*** ERROR:body:Inconsistent module initialization
Module `body' is inconsistently initialized by module `__nbody'.
At least of the two modules must be recompiled (see also -unsafev option).
and I think I finally realized what the problem is. I have a bunch of modules which are all separately compiled, and then put together by a "heap" file as described in the chapter from the Bigloo manual on libraries, but the Makefile I'm using doesn't think that the heap file depends on the separately compiled files. So, the code the heap file uses to initialize the modules it imports becomes stale whenever the exported interface of these modules changes.

This problem arises because bdepend doesn't think that import commands in the module header create dependencies. Normally this is true---bigloo modules can, in general, be separately compiled---but in the case of a heap file, it won't work. (It also won't work for the file which has the -dload-sym option on the command line if you're doing dynamic loading.) The heap file needs to be recompiled if any of the modules it imports have changed their exports. Probably it's easiest to just add a command to the Makefile so that the heap file depends on all the source files in the project; this will recompile it more than necessary, but it's easy and the heap file doesn't take very long to compile.

EDIT: Actually, this problem afflicts all files, not just the heap ones. If module A imports module B, then module A needs to be recompiled if module B's exported interface changes. I've submitted a bug report about bdepend not recognizing this to the bigloo list.

15 August 2005

Perserverance: 1 Static Linking: 0

Finally! I've figured out how to make dynamic linking of module files work with bigloo on Mac OS X! Details follow (though I should warn you that this is a total hack right now).

There are two steps involved: 1. Alter the bigloo compilation process so it builds the proper dynamic libraries and 2. Make sure that the executable bigloo is linked against the dynamic libraries, and not the static ones.

#1: The way I know to do #1 is to edit Makefile.config after running ./configure [whatever-options-you-want]. Scroll through Makefile.config and change the following options:

LD
LD=ld
LDFLAGS
LDFLAGS=-dylib -single_module
LDLIBS
-lc -lSystem.B -ldylib1.o -lm -lc -lgcc
SHAREDSUFFIX
SHAREDSUFFIX=dylib
Now issue the command
make
The make process will halt with an error! That's OK. Re-edit the Makefile.config file by changing the LDLIBS variable to LDLIBS=-lbigloogc-2.7a -lc -lSystem.B -ldylib1.o -lm -lc -lgcc (you should alter the "-2.7a" to whatever version of bigloo you're compiling). The compile should now finish. You will have both libbigloo***-***.a and libbigloo***-***.dylib in the lib subdirectory.

#2: Though it seems like you should be able to ensure that the bigloo executable is linked against the dynamic libs by using the --sharedcompiler=yes flag to the configure script, this doesn't work. However, the fix is easy enough: edit the dboot target of the file comptime/Makefile. There will be a section looks something like

doboot: $(O_OBJECTS)
 if [ "$(SHRD_COMP)" = "yes" -a -f $(BOOTLIBDIR)/lib$(LIBRARYNAME)_s$(VERSION)$(SHAREDSUFFIX) ]; then            gc=-lbigloogc-$(RELEASE);            if [ "$(GCCUSTOM)" = "no" ]; then       gc=-lgc;            fi;     $(CC) $(EXTRA_LD_OPT) $(CFLAGS) -o $(BIGLOO_DEST) $(O_OBJECTS) -L $(BOOTLIBDIR) -lbigloo_s-$(RELEASE) $$gc $(EXTRALIBS);  else            gc=$(BOOTLIBDIR)/libbigloogc-$(RELEASE).a;            if [ "$(GCCUSTOM)" = "no" ]; then               gc=-lgc;            fi;     $(CC) $(EXTRA_LD_OPT) $(CFLAGS) -o $(BIGLOO_DEST) $(O_OBJECTS) $(BOOTLIBDIR)/libbigloo_s-$(RELEASE).a $$gc $(EXTRALIBS);  fi
 -$(STRIP) $(BIGLOO_DEST)$(EXE_SUFFIX)
 @ echo "$(BIGLOO_DEST)$(EXE_SUFFIX) done..."
you want to make it look like:
doboot: $(O_OBJECTS)
 if [ "$(SHRD_COMP)" = "yes" -a -f $(BOOTLIBDIR)/lib$(LIBRARYNAME)_s$(VERSION)$(SHAREDSUFFIX) ]; then            gc=-lbigloogc-$(RELEASE);            if [ "$(GCCUSTOM)" = "no" ]; then       gc=-lgc;            fi;     $(CC) $(EXTRA_LD_OPT) $(CFLAGS) -o $(BIGLOO_DEST) $(O_OBJECTS) /tmp/bigloo2.7a/lib/2.7a/libbigloo_s-2.7a.dylib /tmp/bigloo2.7a/lib/2.7a/libbigloogc-2.7a.dylib  else            gc=$(BOOTLIBDIR)/libbigloogc-$(RELEASE).a;            if [ "$(GCCUSTOM)" = "no" ]; then               gc=-lgc;            fi;     $(CC) $(EXTRA_LD_OPT) $(CFLAGS) -o $(BIGLOO_DEST) $(O_OBJECTS) $(BOOTLIBDIR)/libbigloo_s-$(RELEASE).a $$gc $(EXTRALIBS);  fi
 -$(STRIP) $(BIGLOO_DEST)$(EXE_SUFFIX)
 @ echo "$(BIGLOO_DEST)$(EXE_SUFFIX) done..."
(The difference is that we have explicitly included the libbigloo_s-2.7a.dylib and the libbigloogc-2.7a.dylib libraries on the $(CC) command line---including the full paths!) Now delete the old executable at bin/bigloo (relative to the unpacked bigloo directory) and re-make. Check with otool -L bin/bigloo to make sure that the bigloo executable really links against the dynamic libraries. The output of my otool looks like
SYDNEYPACIFIC-THREE-FORTY-ONE:/tmp/bigloo2.7a farr$ otool -L bin/bigloo
bin/bigloo:
        /tmp/bigloo2.7a/lib/2.7a/libbigloo_s-2.7a.dylib (compatibility version 0.0.0, current version 0.0.0)
        /tmp/bigloo2.7a/lib/2.7a/libbigloogc-2.7a.dylib (compatibility version 0.0.0, current version 0.0.0)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 71.1.3)

Now, to make a loadable bundle, issue the following command (this example comes from the Bigloo manual, section 13.1 under the dynamic-load procedure documentation):

MACOSX_DEPLOYMENT_TARGET=10.3 gcc -bundle -undefined dynamic_lookup -o mod.bundle mod1.o mod2.o
This command replaces the ld -G -o lib.so mod1.o mod2.o command from the manual. You can now load the bundle with
(dynamic-load "mod.bundle")
from the REPL. You need the MACOSX_DEPLOYMENT_TARGET=10.3 so that it doesn't complain about the -undefined dynamic_lookup option; if you don't have 10.3, you can probably get away with -undefined suppress, but I haven't tried it.

Footnotes

1: I think it would be really neat if someone found a way to make all of this ad-hoc makefile-modifying happen automatically as part of the Bigloo build process on Mac OS X. I think it'll take someone more knowledgeable about the internals of the build process, but I may have a look at it later.

2:What happened here is that libbigloo requires the garbage collection library, so we have to include it in the LDLIBS variable. We can't put it in there at the beginning because the GC library doesn't exist yet, so we have to wait for the build to fail and then put it in. This is necessary because Mac OS X makes a distinction between dynamic libraries (which are treated like ELF static libraries for link purposes, but like ELF shared (or dynamic) libraries for loading at runtime) and loadable bundles (which are treated like ELF shared libraries all the time). In English: you have to have all the symbols for a .dylib at link time, though the libraries which contain them won't be loaded until run time, while you don't need the symbols for a .bundle until you use them at runtime.

3:And I don't understand why---the command that make issues has -lbigloogc-2.7a -lbigloo_s-2.7a and my documentation tells me that linking against the dynamic library version is the default in OS X, so I really don't understand what's going on here.

4: But very kludgy.

09 August 2005

Latest in the Quest for Speed

I found an interesting post which claims that Chicken is a bit faster than Gambit on floating-point code. There's a paper by Brad Lucier which talks about using Gambit to make a PDE solver, which would seem to refute this (Chicken doesn't achieve C speeds on numerics), but I suspect that his sparse matrix-vector multiply was memory-bound, so the speed of the code didn't matter too much.

In any case, by my measurements, Bigloo is much faster than either Chicken or Gambit on floating-point code, so it's still my choice. I would love to find a syntax-case implementation for it, but define-macro is probably just as good for my uses. I posted a message about my attempts (and failures) getting dynamic loading to work with Bigloo. If I could just make that work, it would make my development much easier (it's a real pain to have to recompile the REPL every time I want to define a new class or whatever).

Gambit Isn't Fast Without (declare (unsafe))

Gambit, nice as it is about loading compiled code into an REPL, isn't fast numerically unless you compile using (declare (unsafe)). (At least it wasn't in my recent quick tests of some floating-point code.) Bigloo is much better at eliminating type checks and producing fast numerical code, even in safe mode. So, I guess it's back to Bigloo, because I don't want to have to worry about blowing things up when I misuse parts of my system. Bummer---I was really enjoying the syntax-case macros and easy-to-load code from gambit.

07 August 2005

Gambit-C

I've just discovered a new scheme implementation which may be even better than Bigloo for my needs: Gambit-C. Gambit runs on Mac OS X, and compiles to C, just like Bigloo. However the compilation strategy is a bit different. Bigloo aims for roughly idiomatic C code (and relies on the C compiler for many optimizations), while Gambit really uses C as a portable assembly language (and therefore rarely cares about the C compiler optimizations). It looks like (just from reading results of google searches, etc) they are about equally fast in terms of numerical computation. However, Gambit has (from my point of view) a real advantage: loading dynamic libraries works on OS X! Therefore, I can compile my code, and load it into a running REPL---in Bigloo I had to recompile the REPL, and restart it to take advantage of newly compiled code. If the code changes, Gambit will compile it to XXX.o2, and XXX.o3, etc. Each time it's recompiled, the new object file is loaded, and the new functions replace the old ones---no need to stop the REPL process and restart. It feels much more like Common Lisp, but it will satisfy my advisor's need to have something which he can link into a pre-existing C library---the best of both worlds.

04 August 2005

More Bigloo Macros

Regarding my previous post about Bigloo Macros and the REPL, I have realized that it's vital to export the macro-expander function from the module in which the macro is defined. Not doing this prevents recognizing the expander when (eval '(define-expander ....)) is evaluated. (Maybe this was obvious to everyone else, but I didn't realize it for a while.)

01 July 2005

MzScheme and GSL Random Numbers

I just used the foreign interface to PLT Scheme to wrap up the GSL random number generators. I've posted the code here; it hasn't been extensively tested, but I think it'll work OK.

As an aside, I really, really like the foreign interface to PLT Scheme---it's by far the best one I've ever dealt with. This paper explains the rationale. Definitely The Right Way to do it.

25 June 2005

Bigloo, Macros and the REPL

I've just spent quite a while trying to make Bigloo do what I want with macros. What I want:
  1. To be able to write macros within a module which I can use while compiling code in that module or other modules which import the macro-module.
  2. To be able to write macros which are imported into an repl which incorporates the module in which they are written.
Doing this has required a bit of trickery. (The difficulty is that I want the macros at 3 times: 1. When the compiler is processing the module in which they are defined. 2. When the compiler is processing other modules which use them. 3. When the repl is up and running.)

The solution is to write modules with macros like this:

(module with-gensyms
   (eval (export-exports))
   (export (with-gensyms-expander x e)))

(define (with-gensyms-expander x e)
   (match-case x
      ((with-gensyms (?sym) . ?body)
       (e `(let ((,sym (gensym)))
              ,@body) e))
      ((with-gensyms (?sym1 . ?rest) . ?body)
       (e `(let ((,sym1 (gensym)))
              (with-gensyms ,rest ,@body)) e))))

(eval '(define-expander with-gensyms with-gensyms-expander))
And use them in other modules like this:
(module f64vector
   (eval (export-all))
   (import (with-gensyms "with-gensyms.scm")
           (do-macros "do-macros.scm"))
   (load (with-gensyms "with-gensyms.scm")
         (do-macros "do-macros.scm"))
   (type (tvector f64vector (double)))
   (export (f64vector::f64vector . inits)
           (with-f64vectors-expander x e)))

(define (f64vector::f64vector . inits)
   (let* ((n (length inits))
          (v (make-f64vector n 0.0)))
      (let loop ((i 0) (list inits))
         (if (null? list)
             v
             (begin 
                (f64vector-set! v i (car list))
                (loop (+fx i 1) (cdr list)))))))

(define (with-f64vectors-expander x e)
   (match-case x
      ((with-f64vectors ?vecs (<- . ?body))
       (with-gensyms (result n i)
          (e `(let* ((,n (f64vector-length ,(car vecs)))
                     (,result (make-f64vector ,n 0.0)))
                 (do-times (,i ,n)
                    (let ,(map (lambda (sym)
                                  `(,sym (f64vector-ref ,sym ,i)))
                               vecs)
                       (f64vector-set! ,result ,i
                                       (begin ,@body))))
                 ,result) e)))
      ((with-f64vectors ?vecs . ?body)
       (with-gensyms (i n)
          (let* ((vector-syms (map (lambda (sym) (cons sym (gensym))) vecs))
                 (process-body-term
                  (lambda (term)
                     (match-case term
                        ((?result <- . ?body)
                         `(f64vector-set! ,(cdr (assoc result vector-syms))
                                          ,i
                                          (begin ,@body)))
                        (?- term)))))
             (e `(let ((,n (f64vector-length ,(car vecs))))
                    (let ,(map (lambda (sym)
                                  `(,(cdr (assoc sym vector-syms)) ,sym))
                               vecs)
                       (do-times (,i ,n)
                          (let ,(map (lambda (sym)
                                        `(,sym (f64vector-ref ,sym ,i)))
                                     vecs)
                             ,@(map process-body-term body))))) e))))))

(eval '(define-expander with-f64vectors with-f64vectors-expander))
(Note that this module uses two macro-modules---one of which is not listed above---to define a third. I'm sorry for the confusing example, but it's what I have handy.)

How does this work? Here's my understanding:

  1. The compiler begins processing the with-gensyms.scm file. The compiler compiles it into with-gensyms.o which contains a module initialization routine that evaluates all the top-level commands in the module whenever the module is initialized---this ensures that the (eval '(define-expander with-gensyms ...)) runs whenever the module is initialized in compiled code (i.e. when running a custom repl).
  2. The compiler processes f64vector.scm. It sees the (load (with-gensyms ...)) command in the module header, and interprets the file with-gensyms.scm, installing the with-gensyms macro before processing the code in f64vector.scm. The f64vector.scm file compiles into f64vector.o which contains a module initialization routine which will initialize the with-gensyms module first (because bigloo sees that it is required by a (import (with-gensyms ...)) in the f64vector module header) whenever the f64vector module is required from compiled code (i.e. within the main routine which implements the repl).
Thus, when compiling the code, all the macros are interpreted, but when running the code (i.e. within the repl), the macro-expanders are actually compiled! Any module which needs the macros at compile time should use the (load ...) form in the module header, and any module which wishes to install these macros at run time should use the (import ...) form. Whew! The repl in question can be implemented by
(module repl
   (import (with-gensyms "with-gensyms.scm")
    (do-macros "do-macros.scm")
    (f64vector "f64vector.scm"))
   (main main))

(define (main argv)
   (repl))

It took me a long enough time to figure this out (and I wouldn't have figured it out without some suggestions from jg malecki (see this message and its antecedents---thanks jg) that I thought I should post the explanation here so that other people don't have to go through the same difficulties that I have.

23 June 2005

Making Scheme behave like Fortran 95

I just coded up a macro which makes Bigloo scheme behave more like Fortran 95 with respect to vectors. Usage:
(with-vectors (a b c)
   (a <- (+ b c)))
(with-vectors (a b c)
   (<- (+ a b c)))
(with-vectors (a b c)
   (a <- (+ b 2))
   (print "I can insert statements, too.")
   (c <- (+ a b)))

The second form above returns a fresh vector whose elements are the sums of the corresponding elements in a b and c. The length of the new vector is the same as the length of a. In all cases, the number of iterations of the body is the length of a (this isn't quite the right thing to do---that would be to make the number of iterations be the minimum of the lengths of a b and c or to throw an exception if a b and c are not the same length). The macro follows:

(define-expander with-vectors
   (lambda (x e)
      (match-case x
         ((with-vectors ?vecs (<- . ?body))
          (with-gensyms (result n i)
             (e `(let* ((,n (vector-length ,(car vecs)))
                        (,result (make-vector ,n)))
                    (do-times (,i ,n)
                       (let ,(map (lambda (sym) `(,sym (vector-ref ,sym ,i)))
                                  vecs)
                          (vector-set! ,result ,i
                                       (begin ,@body))))
                    ,result) e)))
         ((with-vectors ?vecs . ?body)
          (with-gensyms (i n)
             (let* ((vector-syms (map (lambda (sym) (cons sym (gensym))) vecs))
                    (process-body-term
                     (lambda (term)
                        (match-case term
                           ((?result <- . ?body)
                            `(vector-set! ,(cdr (assoc result vector-syms))
                                          ,i
                                          (begin ,@body)))
                           (?- term)))))
                (e `(let ((,n (vector-length ,(car vecs))))
                       (let ,(map (lambda (sym)
                                     `(,(cdr (assoc sym vector-syms)) ,sym))
                                  vecs)
                          (do-times (,i ,n)
                             (let ,(map (lambda (sym)
                                           `(,sym (vector-ref ,sym ,i)))
                                        vecs)
                                ,@(map process-body-term body))))) e)))))))
It requires a completely obvious (I hope) do-times macro, and also with-gensyms (a lisp favorite):
(define-expander do-times
   (lambda (x e)
      (match-case x
         ((do-times (?i ?n) . ?body)
          (with-gensyms (nn nn-1)
             (e `(let ((,nn ,n))
                    (let ((,nn-1 (-fx ,nn 1)))
                       (do ((,i 0 (+fx ,i 1)))
                           ((=fx ,i ,nn-1) ,@body)
                           ,@body))) e))))))

(define-expander with-gensyms
   (lambda (x e)
      (match-case x
         ((with-gensyms (?sym) . ?body)
          (e `(let ((,sym (gensym)))
                 ,@body) e))
         ((with-gensyms (?sym . ?rest-syms) . ?body)
          (e `(let ((,sym (gensym)))
                 (with-gensyms ,rest-syms ,@body)) e)))))
Nifty, huh? I also have one for my f64vectors (as described in my last post one can make vectors of doubles---the natural behavior from bigloo approximates SRFI 4):
(define-expander with-f64vectors
   (lambda (x e)
      (match-case x
         ((with-f64vectors ?vecs (<- . ?body))
          (with-gensyms (result n i)
             (e `(let* ((,n (f64vector-length ,(car vecs)))
                        (,result (make-f64vector ,n 0.0)))
                    (do-times (,i ,n)
                       (let ,(map (lambda (sym)
                                     `(,sym (f64vector-ref ,sym ,i)))
                                  vecs)
                          (f64vector-set! ,result ,i
                                       (begin ,@body))))
                    ,result) e)))
         ((with-f64vectors ?vecs . ?body)
          (with-gensyms (i n)
             (let* ((vector-syms (map (lambda (sym) (cons sym (gensym))) vecs))
                    (process-body-term
                     (lambda (term)
                        (match-case term
                           ((?result <- . ?body)
                            `(f64vector-set! ,(cdr (assoc result vector-syms))
                                          ,i
                                          (begin ,@body)))
                           (?- term)))))
                (e `(let ((,n (f64vector-length ,(car vecs))))
                       (let ,(map (lambda (sym)
                                     `(,(cdr (assoc sym vector-syms)) ,sym))
                                  vecs)
                          (do-times (,i ,n)
                             (let ,(map (lambda (sym)
                                           `(,sym (f64vector-ref ,sym ,i)))
                                        vecs)
                                ,@(map process-body-term body))))) e)))))))

22 June 2005

Typed Vectors in Bigloo

I learned at the beginning of the month how to make a typed vector (also called monomorphic, I think) in Bigloo scheme. I had known that Bigloo would automatically unbox vectors of doubles or integers if it could infer the type of them, but you can't guarantee this. This kept me from using Bigloo for my numerical projects---it's essential that such vectors be unboxed, and I didn't want to leave it up to the type-inferencing engine. However, at the beginning of the month a message came out on the Bigloo mailing list (it's no longer in the archive on gmane, and it came around before the inria archive, so this may be the only place you can find it): the way to make a typed-vector is demonstrated in the following example from Manuel Serrano:
(module foo
   (type (tvector array-of-int (int)))
   (export (foo::array-of-int ::int ::int))
   (main main))

(define (foo len init)
   (make-array-of-int len init))

(define (main x)
   (let ((v::array-of-int (foo 10 20)))
      (print v)
      (array-of-int-set! v 5 6)
      (print (array-of-int-ref v 5))))

Apparently you can also find more examples of this in the recette/vector.scm module in the source for Bigloo.

01 June 2005

All the President's Men...

I still remember reading All the President's Men when I was maybe in middle school. My parents were coming of age during that era, and reading the book made me feel like I was equally involved in the scandal. Learning yesterday that Deep Throat has finally come forward brought all that to mind again. I hope someone writes a book about him---it sounds like he is maybe a bit conflicted about his role, and that would make another interesting story.

31 May 2005

Almost beaten to the punch!

The shark-profile package has only been out for a few days, and I've been scooped! I'm glad that someone with more stamina and skill than I have has fixed the interrupt/floating-point interaction for SBCL on OS X. I've been using SB-SPROF a bit tonight, and it seems to be stable now. In consolation, I suppose there may still be people who wish to use shark instead of SB-SPROF, and I got several good weeks use out of SHARK-PROFILE, so all is not lost :).

27 May 2005

Profiling lisp with SBCL and Mac OS X

I just posted some code for profiling with shark (part of the CHUD toolkit on Mac OS X) and SBCL. I've been writing some fancy n-body integrators using common lisp for a research project, and wanted to profile the code. SBCL comes with a profiler (sb-sprof), but it doesn't work well---at all---on Mac OS X (see the reply to this message of mine for a description of the problem). So, I cooked up this code (with some help from Juho Snellman) which lets me use shark for profiling. It's quite a hack, but it does the job.

22 May 2005

Go New York Times!

Check this out:
While the proposal to close the case was ultimately rejected by senior officials, documents show that the inquiry was at a virtual standstill when an article in The New York Times on March 4, 2003, reported that at least one of the prisoner's deaths had been ruled a homicide, contradicting the military's earlier assertions that both had died of natural causes. Activity in the case quickly resumed.
I'm getting tired of our side being the bad guy so often. Why is the army not more thorough about disciplining those responsible for this kind of crap? At best, I would say it's apathy, or a desire to protect your own---however flawed they may be---and at worst, I would say it's because those in charge actually think this sort of thing is OK. Either way, it's not making me very happy. This also shows the media at its best---the army wasn't doing anything, and the NY Times got them moving. Let's hear it for the fourth estate!

21 May 2005

First Post

I've finally broken down and started a blog. Typically for me, it came impulsively as I was searching for information on the best way to rip books on tape (CD) in iTunes, and stumbled on http://aldoblog.com/blog/471. Since we're flying to California, Oregon and then the Netherlands this summer, we've got some 30 hours of time on planes, and thought we'd get some books on tape for Rachel's iPod. The above link gives the details, but the short version:
  • Set the import file format to AAC, 64kbit Stereo Rate, Mono Sampling
  • Use the Advanced menu option to consolidate the tracks into one (so you don't have 50-60 tracks for one book on the iPod).
  • Once the file is ripped, change its type from m4a to m4b so that iPod will automatically maintain your listening position rather than resetting to the beginning of each track. You can use the MakeBookmarkable script, or (I think) just change the extension of the imported file to m4b.
I intend to record such tibits here in the future so that they're easy to find.