Did you know that you can compile elisp into byte-code? You did? Okay then, did you know that moderns emacs ship with the elisp compiler? Right, you knew that too? Well, in that case, do you compile your start-up configuration? If you don’t, this post could be for you.
First of all, what is the benefit of compiling? I can think of three.
- You might get a slight speed increase
- byte code loads faster than source code
- compilation picks up all sorts of errors
In my opinion, the first two points are not particularly important. The speed increase is extremely small and, even with the fairly large packages and customisation I have done, emacs starts up in a couple of sections without compiling everything to byte code. The real benefit [for me] is the error checking.
There is also a downside. Emacs always loads a byte code if it available, even if the source code is newer. This is pretty annoying and has bitten me on a few occasions when I have been wondering why emacs was not picking up my changes at start-up.
What kind of errors does the compiler find? Here are some of the errors from a recent compile.
In run-sql-command:
db.el:38:30:Warning: reference to free variable `*db-cmd*'
In toplevel form:
ftp-file-upload.el:16:1:Warning: defcustom for `*ftp-upload-skip-prompt*'
fails to specify containing group
In toplevel form:
htmlize.el:89:22:Warning: reference to free variable `unresolved'
In end of data:
htmlize.el:1770:1:Warning: the following functions are not known to be
defined: byte-compiler-options, warnings, htmlize-next-change,
htmlize-locate-file, specifier-spec-list, face-property,
color-instance-name, face-foreground-instance, face-background-instance,
color-instance-rgb-components, make-color-instance, face-font-instance,
font-instance-properties, face-strikethru-p, map-extents, extent-face,
extent-at, htmlize-faces-at-point, lazy-lock-fontify-region,
lazy-shot-fontify-region, dired-get-marked-files
In toplevel form:
my-ftp.el:27:7:Warning: assignment to free variable `*ftp-proc*'
my-ftp.el:29:68:Warning: reference to free variable `*ftp-proc*'
In end of data:
my-ftp.el:31:1:Warning: the function `ftp-mode' is not known to be defined.
In toplevel form:
shell-wrappers.el:87:13:Warning: reference to free variable `term-rawp-map'
In end of data:
shell-wrappers.el:98:1:Warning: the function `term-set-excape-char' is not
known to be defined.
In copy-files:
wordpress-loader.el:42:45:Warning: file-readable-p called with 0 arguments,
but requires 1
As you can see from the errors from htmlize, when you are writing elisp to be portable between xemacs and emacs, the compiler will emit spurious warnings. However, for someone like me, it is great. Look at those basic errors such misspelling term-set-excape-char and calling file-readable-p with no arguments. For dynamic languages, a decent lint style tool including the emacs byte code compiler, use strict in perl and pylint/pychecker/pyflakes can really help find a lot of bugs.
How do we set up the compiler? I prefer a batch-file that compiles everything that has changed.
set EMACS_FILES=c:/packages/emacs-files
set EMACS=c:\packages\emacs-22.1\bin\emacs.exe
%EMACS% --batch ^
--eval “(load \”%EMACS_FILES%/my-load-paths.el\”)” ^
–eval “(batch-byte-compile-if-not-done)” *.el
(That last eval is prefixed with two minuses but it looks like WordPress has changed it).
Emacs needs to know the paths where the modules live so that it can see if functions or variables are defined in files that have been required.
;; ———————————————————————- ;;
(load “c:/packages/emacs-files/my-vars.el”)
(add-to-list ‘load-path *elisp-dir*)
(add-to-list ‘load-path (expand-file-name “icicles” *elisp-dir*))
(add-to-list ‘load-path (expand-file-name “muse-3.12″ *elisp-dir*))
(add-to-list ‘load-path (expand-file-name “org-5.19a” *elisp-dir*))
(add-to-list ‘load-path (expand-file-name “remember-1.9″ *elisp-dir*))
(provide ‘my-load-paths)
;; ———————————————————————- ;;
Finally, I like to have as few files as possible referring to the top level directory so the emacs variable pointing to the top level gets placed in its own file.
;; ———————————————————————- ;;
(defvar *elisp-dir* “c:/packages/emacs-files/”)
(provide ‘my-vars)
;; ———————————————————————- ;;
JG