Emacs configuration with Cask

This post is about how I use Emacs package management in combination with Cask to manage my personal Emacs configuration. If you just want to have a look at that configuration, you can find it in my emacs-config repo at Bitbucket. Read on if you want to know how I got there...

DIY package management

When you use Emacs, you will rely on a whole plethora of Emacs packages that do not belong to the default installation. Until October of last year, I used a Makefile to retrieve those external Emacs packages. The following snippet is from that Makefile and shows how I retrieved package ace-jump-mode:

        - rm -rf externals/ace-jump-mode
        cd externals ; git clone git://github.com/winterTTr/ace-jump-mode.git

The Makefile contained a lot of awfully similar rules to install the other external packages I relied on. Ideally it would have also contained rules to update those packages, but I never got round to implement that.

The actual configuration of the external packages was done in the Emacs startup file init.el. The next Lisp snippet from that file shows the configuration of ace-jump-mode:

;use ace-jump-mode to improve navigation
(add-to-list 'load-path "~/.emacs.d/externals/ace-jump-mode")
  "Emacs quick move minor mode"

This home-grown solution to package management served me well over the years. It enabled me to get a new install of Emacs up-and-running rather quickly.

Using Emacs package management directly

Emacs has a package management infrastructure since version 24.1 [1]. It consists of a set of online repositories and a library to interact with them. Since then it has become the way to install packages and in October of last year, I finally started using it. Indeed it is a breeze to use, the biggest benefits being

  • the use of a global list of available packages: no need to search through GitHub or other online sources,
  • the one-click install of an interesting package: no need to add an additional rule to my Makefile, and
  • automatic support for upgrading installed packages.

Initially I used the Emacs package package.el directly to access the package management infrastructure. The following snippet from my configuration file shows how that looked:

(require 'package)
(add-to-list 'package-archives '("melpa" . "http://melpa.milkbox.net/packages/") t)

;Bozhidar Batsov at
;  http://batsov.com/articles/2012/02/19/package-management-in-emacs-the-good-the-bad-and-the-ugly/
;Thank you!

(defvar sks-packages
  '(ace-jump-mode     ;;to enable text-specific cursor jumps
  "A list of packages that should be installed at Emacs startup.")

(require 'cl)

(defun sks-packages-installed-p ()
  (loop for p in sks-packages
        when (not (package-installed-p p)) do (return nil)
        finally (return t)))

(unless (sks-packages-installed-p)
  ;; check for new packages (package versions)
  (message "%s" "Emacs is now refreshing its package database...")
  (message "%s" " done.")
  ;; install the missing packages
  (dolist (p sks-packages)
    (when (not (package-installed-p p))
      (package-install p))))

      (eval-after-load "ace-jump-mode-autoloads"

(eval-after-load "ace-jump-mode-autoloads"
       "Emacs quick move minor mode"
     (define-key global-map (kbd "C-0") 'ace-jump-mode)))

Please note that the value of variable sks-packages in this snippet specifies a single external package to keep this example concise. The original Lisp file specifies a dozen more packages.

This approach was a big improvement over my own solution. Installation and configuration were located in the same file and gone were the dependencies on an additional Makefile and VC clients.

Emacs package management via Cask

The code that makes sure all packages in sks-packages are installed at startup in case there were not yet installed, is from Bozhidar Batsov. Since then, he has developed Cask, which is, and I quote from its documentation,

[...] a project management tool for Emacs Lisp to automate the package development cycle; development, dependencies, testing, building, packaging and more.

Cask can also be used to manage dependencies for your local Emacs configuration.

Because of both those design goals, I decided to use Cask for my Emacs package management purposes.

Cask uses a so-called Cask file where you specify the external packages you rely on. For example, to install ace-jump-mode, my Cask file would look like this:

(source melpa) ;;archive of VCS snapshots built automatically from upstream repositories

(depends-on "ace-jump-mode")

(eval-after-load "ace-jump-mode-autoloads"
       "Emacs quick move minor mode"
     (define-key global-map (kbd "C-0") 'ace-jump-mode)))

The first lines specifies the online repository that should be searched and the third line specifies the external package itself [2]. To install this dependency, I execute the following command:

$ .emacs.d> cask

Cask installs the package in ~/.emacs.d/.cask/, where is my current Emacs version.

To update any installed dependencies, I just have to do:

$ .emacs.d> cask update

To close it off

As mentioned, you can find my Emacs configuration in my emacs-config repo. Although the repo is hosted at Bitbucket, it is a Git repo and not a Mercurial one as one might expect. I am using Git almost full-time now as my main client relies on it and have become more proficient with Git than with Mercurial. Furthermore, there is not much that can beat the excellent Emacs mode for interacting with Git, magit.

To conclude all this, previously my Emacs configuration was accessible from one of my public Launchpad repos. I hosted my configuration there as Launchpad uses the Bazaar version control system, which was the first distributed VCS I used. The last few years Bazaar adoption has declined and its development has slowed down so I do not gain much, if anything, from hosting my configuration there.

[1] Emacs version 24.1 has been released 2012-06-10.
[2] The Cask documentation lists the repos it supports here. The comments in this snippet are from that documentation.


Comments powered by Disqus