article

Auto-installing packages in Emacs with ELPA and el-get

Those who live in emacs all know the pain of manually installing extra modes and extensions, especially on different hosts. System packages sometimes help, but they differ on every OS (Debian packages, MacPorts, etc.), don’t always exist, and need to be installed manually.

A better alternative is to use emacs itself to manage, install and update all the required extra code. The ELPA project provides a way to install packages from different repositories, while el-get can help you declare recipes to install and update code from ELPA, or even Github or Emacswiki.

First, let’s setup some code that tries to load ELPA and el-get, and installs them if they are not present.

; derived from ELPA installation
; http://tromey.com/elpa/install.html
(defun eval-url (url)
  (let ((buffer (url-retrieve-synchronously url)))
  (save-excursion
    (set-buffer buffer)
    (goto-char (point-min))
    (re-search-forward "^$" nil 'move)
    (eval-region (point) (point-max))
    (kill-buffer (current-buffer)))))

;; Load ELPA
(add-to-list 'load-path "~/.emacs.d/elpa")

(defun install-elpa ()
  (eval-url "http://tromey.com/elpa/package-install.el"))

(if (require 'package nil t)
    (progn
      ;; Emacs 24+ includes ELPA, but requires some extra setup
      ;; to use the (better) tromey repo
      (if (>= emacs-major-version 24)
          (setq package-archives
                (cons '("tromey" . "http://tromey.com/elpa/")
                package-archives)))
      (package-initialize))
  (install-elpa))

;; Load el-get
(add-to-list 'load-path "~/.emacs.d/el-get/el-get")

(defun install-el-get ()
  (eval-url
   "https://github.com/dimitri/el-get/raw/master/el-get-install.el"))

(unless (require 'el-get nil t)
  (install-el-get))

Note that when using emacs 24, package.el is distributed with emacs but it points to the GNU package repository; the code above adds tromey’s more complete ELPA repository to the sources.

Unfortunately, the ELPA installer script appends a snippet of code to the end of the ~/.emacs file to auto-load package.el on startup. To avoid any problem, it is best to manually remove that code any only keep the load-or-install code above.

Now that ELPA and el-get are setup, we can declare all the packages we want installed. They will be installed the first time emacs is started, and simply loaded in the future.

; extra recipes for packages unknown to el-get (yet)
(setq el-get-sources
      '((:name css-mode :type elpa)
        (:name js2-mode-mooz
               :type git
               :url "git://github.com/mooz/js2-mode.git"
               :load "js2-mode.el"
               :compile ("js2-mode.el")
               :features js2-mode)))

; list all packages you want installed
(setq my-el-get-packages
      (append
       '(css-mode egg gist js2-mode-mooz)
       (mapcar 'el-get-source-name el-get-sources)))

(el-get 'sync my-el-get-packages)

The el-get 'sync call does all the magic, based on all the packages past in argument (as my-el-get-packages). Any packages for which el-get has a recipe can be installed.

The el-get-sources variable allows to declare extra custom recipes for code to install. In the example above, css-mode is simply pulled from the ELPA repository, while js2-mode-mooz is fetched directly from Github.

Replicating this code in all your ~/.emacs files is a very easy and convenient way to bootstrap the same emacs environment on multiple hosts.

Leave a Reply