Automatically Installing Your Emacs Packages
list-packages command in modern Emacsen is handy for
finding and trying out new packages, but once we’ve found packages we
want to use, how can we have them automatically installed on all
machines where we use Emacs? There’s
a decent Stack Overflow question on the topic, but I
want to dig into the various answers a bit and provide a slightly
cleaner (IMHO) code snippet.
First let’s define that list of packages we want installed by adding a
defvar form to our
(defvar my/packages '(abc-mode ;; ⋮ zygospore))
Now, the obvious part of this problem is using
to check if each named package is installed and then installing the
missing ones. The less obvious part is that we may need to call
package-refresh-contents to get the package repository data
(especially on the first load) or else we’ll get errors. High-level,
there are four ways I know of to approach this problem:
- Always call
- Check if
- Test if
- Refresh if any packages are missing.
Number one works buts is extremely inefficient–I don’t want to wait for a package repo fetch every time I start Emacs.
Two and three have a fatal flaw: just because we have some package data doesn’t mean that we have the latest data. So it’s entirely possible that we don’t know about a recent package that the user wants to install–errors again.
(require 'cl-lib) (defun my/install-packages () "Ensure the packages I use are installed. See `my/packages'." (interactive) (let ((missing-packages (cl-remove-if #'package-installed-p my/packages))) (when missing-packages (message "Installing %d missing package(s)" (length missing-packages)) (package-refresh-contents) (mapc #'package-install missing-packages)))) (my/install-packages)
- The function2 exists so that you can invoke it manually (
M-x my/install-packages RET) if you’ve changed
- Keep in mind that re-evaluating
my/packagesafter changing it will not do anything because that’s how
defvarworks. Temporarily change
setqand you’ll be good to go.
cl-libis required for
cl-remove-if. How Elisp has made it this far without a
filterfunction, I don’t know.
- I’m not entirely sure if
nilpunning is idiomatic elisp or if there’s a more appropriate way to check for empty lists. Anyone know?