Yes, I promise I’ll shut up about Emacs package management via ELPA any minute now.
Based on the feedback I had on my last post about using a combination of melpa and melpa-stable, I looked into using pinned packages via the package-pinned-packages
variable that’s new in Emacs 24.4’s package.el. I couldn’t find any simple examples on how to use it, but a quick look at the source code and some playing around in ielm got me there. Well, after I finally upgraded to Emacs 24.4 on my main machine.
Using pinned packages via package-pinned-packages
is actually pretty simple. First, you need a list of ELPA package archives to pull packages from. If you’re already using ELPA, you most likely have package-archives
set up already. Second, you need to create the list of pinned packages. For me that was the majority of the work and it required a few round trips through the *Packages* buffer. It would have been smarter to uninstall all the base packages, run list-packages
once and note which package should be installed from which archive instead of relying on my famously selective memory. Don’t do what I did, just do it once. Either way, I got there. Eventually.
Once you have both variables set up, you’re left with a massively simplified loop that really just takes a list of packages and calls package-install
on them if they’re not already present in the system.
For those who like me learn better by looking at code but like me couldn’t find an example on how to use package-pinned-packages, here’s the updated code. It’s Emacs 24.4 only, partially for clarity and partially because I don’t need to support anything older than 24.4 in my .emacs anymore.
(require 'package)
(when (>= emacs-major-version 24)
(setq package-archives '(("ELPA" . "http://tromey.com/elpa/")
("gnu" . "http://elpa.gnu.org/packages/")
("melpa" . "http://melpa.org/packages/")
("melpa-stable" . "http://stable.melpa.org/packages/")
("marmalade" . "http://marmalade-repo.org/packages/")
)))
;; Check if we're on Emacs 24.4 or newer, if so, use the pinned package feature
(when (boundp 'package-pinned-packages)
(setq package-pinned-packages
'((bm . "marmalade")
(smex . "melpa-stable")
(zenburn-theme . "melpa-stable")
(anti-zenburn-theme . "melpa-stable")
(zen-and-art-theme . "marmalade")
(cider . "melpa-stable")
(clojure-mode . "melpa-stable")
(htmlize . "marmalade")
(rainbow-delimiters . "melpa-stable")
;; "unstable" package
(icicles . "melpa"))))
(package-initialize t)
(defun install-required-packages (package-list)
(when (>= emacs-major-version 24)
(package-refresh-contents)
(mapc (lambda (package)
(unless (require package nil t)
(package-install package)))
package-list)))
(setq required-package-list '(bm icicles smex zenburn-theme zen-and-art-theme htmlize cider clojure-mode rainbow-delimiters))
You can see the whole code is a lot more compact even with the formatting I use and most importantly, it’s a lot more readable. Keep in mind that I stripped out all the code that made the melpa-stable/melpa combination work in Emacs 24.3; the code gets more complicated again when you’re trying to accommodate both versions. For my use, I was happy to just get everything working for Emacs 24.4 after I upgraded the installs on my various machines.
So what’s the take away here?
Package management is hard. Well, all of us who have worked on medium and large software systems know that from first hand experience. Emacs is no different. I think having package-pinned-packages available is a nice feature in my use case. I only use the above code to bootstrap my various Emacsen and individual Emacs instances usually have a few more packages installed. If I wanted to pin all the packages I use I’d probably be grumpy by the end of the exercise but for the basic set of packages I use, this works better than my previous attempts.