my-compile package


Introduction

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.

my-compile-dir-list

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.

A kernel example

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.

A function example

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.

A complex example

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.

Update 2011: svn support

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.


Contact Sean MacLennan
Last modified: Thu Oct 13 23:21:57 EDT 2011