The my-compile-command function was written to handle code
bases with sub-directories where you have to build from an upper
level, so M-x compile just won't work. You can always
set the compile command, but what if you have two (or more!) code
bases of this type that you have to work with daily? Or complex
code layouts?
By setting up the my-compile-dir-list,
my-compile-command allows you to just hit M-x
compile anywhere in the code base and it just works (tm).
The my-compile-command function is meant to be called from
the c-mode-common-hook. It takes the current buffers
directory and matches it against the
my-compile-dir-list. If there is a match, it sets the
compile-command appropriately.
The actual code is quite small, but I split it out into a package in the hopes more people would use it.
Here is the default my-compile-dir-list:
(defvar my-compile-dir-list
(list
;; 2.4 kernels need bzImage and modules for drivers
(list "/usr/src/linux-2.4[^/]*/" (concat make-j " bzImage modules") "linux")
;; 2.6 kernels just work
(list "/usr/src/linux[^/]*/" make-j "linux")
(list "/usr/src/git-2.6/" make-j "linux")
;; emacs needs gnu
(list ".*/[sx]?emacs[^/]*/src/" make-j "gnu")
(list ".*/[sx]?emacs[^/]*/" make-j "gnu")))
Each entry is a list of one to three elements. The first element is the regular expression to match against the current directory. The first match is the one used, so order is important. I put the 2.4 kernel entry first so it will be matched before 2.6 directories and possible future 2.7. Only this first element is required.
The second element is a string of args to add to the compile command. It can be nil.
The third element can be a string or a function. If it is a string, it is the style to use with the files. If it is a function, the function is called with the matched directory as the first arg and the second element as the second arg. All of the default entries specify a string.
The Linux kernel is an example of a large code base where you
must compile from the top level. Let's say you open the file
/usr/src/linux/drivers/mmc/sd.c. Let's also assume
that make-j is set to -j4, a reasonable
setting on a dual core laptop like I am using to write this.
That would match the entry (list "/usr/src/linux[^/]*/"
make-j "linux"). The match directory would be
/usr/src/linux/. If the third arg was a function, this
would be the directory passed to the function as the first arg.
The my-compile-command function would concat the matched
directory, and the args, and set the compile-command
to make -C /usr/src/linux/ -j4.
Being able to call a function allows you do just about anything you like. For example, I like to know when I am working on Pika code. Pika code also has kernel and user mode parts. So here is a stripped down function that I might use at Pika.
(defun pika-c-mode (dir arg)
(if (string-match "/kernel/" dir)
(progn
(c-set-style "linux")
(setq mode-name (concat "PK-" mode-name)))
(c-set-style "pika")
(setq mode-name (concat "PIKA-" mode-name))))
With a list entry like: (list "^/usr/src/pika/" nil
'pika-c-mode) I can not only get the correct compile
command, but I get the correct coding style and a visual
indication in the mode line.
Here is a more complex setup. Again, this is a simplified version from work:
(setq my-compile-dir-list
("^/usr/src/pika/applications/aohtest/" nil 'pika-c-mode) ;; rule 1
("^/usr/src/pika/applications/" nil 'pika-c-mode) ;; rule 2
("^/usr/src/pika/kernel/" nil 'pika-c-mode) ;; rule 3
("^/usr/src/pika/" nil 'pika-c-mode)) ;; rule 4
Of course, I am using the pika-c-mode from the
function example. But this time I am controlling the match
directory more carefully. When I am compiling aohtest
(or any sub-directory of), I will only compile
aohtest because of rule 1. If I am compiling any other
application, I will recompile all applications, including
aohtest, because of rule 2. This is laziness on my
part. I work much more often with aohtest then other
apps. Rule 3 says build only the kernel directories. Rule 4 is
basically like a default case in a switch. If there are not
matches, rule 4 will build the entire code base.
Note that for this example to work, there must be a fully functional Makefile at every level that has a rule. This would not work for the Linux kernel, since everything must always be built at the top level.
Because of the way subversion supports branching, I quite often end up with multiple copies of the same basic code base. It can be a pain to add all the directories to the list, especially temporary ones. So now the matching can be done against the subversion base.
For example, say you have a subversion repo at svn://repo/project with the trunk and branches. Let's say the trunk is svn://repo/progject/trunk and a 1.0 branch is svn://repo/project/branches/1.0. The following line would match the trunk and any branch:
(setq my-compile-dir-list ("svn://repo/project/" nil 'project-mode))
Back to S?X?Emacs.