Emacs package management20 Mar 2015
Lately, I've been spending some time to automate and publish my Emacs config. Being able to quickly reproduce your config has many advantages, the main one being that you no longer have to spend time to make your config reproducible.
Happily, most of my config is already published in many packages, I only have to figure out a nice
layer to glue them together. Below, I'll show some automation for the packages managed by
Step 1: get the main directory
This is an important step that many other peoples' configs miss, even the ones that are designed to
be distributed. You can't just assume that the config will be located in
~/.emacs.d and rely on
Emacs defaults. Instead, it's nice to be able to clone the config into a random directory and launch
an Emacs from there, without messing with the currently installed Emacs.
It's also useful for having multiple repositories for different versions of Emacs. ELPA packages are byte-compiled, and the byte code can be incompatible between versions (for instance, 24.3 and 24.4). Having two independent checkouts with ELPA directory auto-generated really helps in that case.
So here is the code to get the main directory and define an ELPA directory with respect to that:
(defconst emacs-d (file-name-directory (file-chase-links load-file-name)) "The giant turtle on which the world rests.") (setq package-user-dir (expand-file-name "elpa" emacs-d))
Step 2: decide what you like
Next, I initialize the package and define some of the packages that I like, omitting the
dependencies that they bring. Note that the code of the whole post is stored in a separate file
packages.el that is not intended to be loaded on start up, so it's fine to call
(package-initialize) (setq package-archives '(("melpa" . "http://melpa.milkbox.net/packages/") ("gnu" . "http://elpa.gnu.org/packages/"))) (package-refresh-contents) (defconst ora-packages '(auto-compile auto-yasnippet ace-link ace-window company eclipse-theme flx-ido function-args headlong ido-occasional ido-vertical-mode lispy magit smex swiper use-package guide-key powerline projectile slime cider worf org-download make-it-so ukrainian-holidays netherlands-holidays j-mode) "List of packages that I like.")
Step 3: install and upgrade
The install step is pretty straightforward: install a package unless it's already installed. I tried to do something fancier for the upgrade, but in the end it was much more simple to just call the interactive interface. The last two lines are basically equivalent to pressing Uxy interactively:
;; install required (dolist (package ora-packages) (unless (package-installed-p package) (package-install package))) ;; upgrade installed (save-window-excursion (package-list-packages t) (package-menu-mark-upgrades) (package-menu-execute t))
Step 4: make it callable
Finally, I just create a Makefile with the following contents:
emacs ?= emacs upgrade: $(emacs) -batch -l packages.el run: $(emacs) -Q -l init.el up: upgrade run
Thanks to the first line, I can issue stuff like this on the shell:
emacs=emacs24 make up
This will use the
emacs24 executable, instead of whatever
emacs points to. Since the
target depends on
run targets, they will be executed in that order:
upgradewill install / upgrade all packages in a non-interactive Emacs
runtarget will start an interactive Emacs with already updated packages
I really like putting stuff in Makefiles, since they are very flexible, yet so easy to call. In the
very same Makefile, I have a
profile target from
the post on profiling Emacs start up. I also
wrote two packages related to Makefiles: helm-make and
make-it-so. The latter one is actually very interesting and
deserves its own post, I should maybe just clean it up a bit.
I'll just cite Gall's law here:
A complex system that works is invariably found to have evolved from a simple system that worked. A complex system designed from scratch never works and cannot be patched up to make it work. You have to start over with a working simple system.
I think it applies from both sides w.r.t. my Emacs config: it kind of works, but I really wish it was reproducible from the start, before making it complex.