Efficient Emacs Package Management

Josh Burns,emacs

A huge pain point I see a lot of (especially, but not always, new) Emacs users having is how to go about installing and using packages from MELPA.

Emacs ships with the package.el library which will underpin your package management configuration.

Essentially think of installing packages in 2 phases, Bootstrap, Install and Configure.

Bootstrap package.el

First step is to have Emacs load and initialize package.el on startup.

(require 'package)
(setq package-enable-at-startup nil) ;; Prevent Emacs from executing package-initialize after reading init.el

Now we need to tell package.el where to look for packages. Most Emacs users have the Melpa and Melpa Stable repos loaded into their config. To add a repo to package.el's repo list use the add-to-list command to target the package-archives symbol like so.

;; ... Rest of file omitted
(add-to-list 'package-archives '("melpa-stable" . "http://stable.melpa.org/packages/") t)
(add-to-list 'package-archives '("melpa" . "http://melpa.org/packages/") t)

At this point we have a fully functional package manager, accessible from within emacs. Using the M-x package-install command we can invoke the installation of any package we want. What we want to happen is for Emacs to load a list of packages and then install them, upon startup. To do that we will have to write a little bit of wrapper code to first detect the installation status of a given package and then install it if its not there. This is essentially the primary feature of use-package itself. What needs to happen is emacs needs to emulate use-package in order to install it, then we can use it to do the same thing for other packages we have.

The following block of code is a little lengthy but we will break it down.

;; ... Rest of file omitted
(unless (package-installed-p 'use-package)
  (package-install 'use-package))
(require 'use-package)
(setq use-package-always-ensure 't)

Lets break that down, line by line so we can understand what this is doing and why.

;; We first enter a conditional block and check the result
;; of package-installed-p (which takes a package name and returns 
;; True or False) to determine if the use-package package is
;; installed. This unless block is evaluated on startup to
;; ensure our package manager is ready to go out of the box.
(unless (package-installed-p 'use-package)
  ;; If use-package is not installed we will tell emacs
  ;; to execute a refresh of the package index
  ;; so everything is up to date.
  ;; We then invoke package-install to rectify the situation and
  ;; install use-package.
  (package-install use-package))
;; We then load use-package using require
(require 'use-package)
;; Finally we set the always-ensure option to true. This tells use-package
;; to automatically add the `:ensure t` tag to package declarations which
;; causes use-package to perform the `require` step on each package.
(setq use-package-always-ensure 't)

Install and Configure Packages

We have a completed package management system baked right into our emacs config. Now reload emacs to take advantage of our new machinery. At this point we will start using the use-package function to build a manifest of packages to install. Normally to declare a package we write a function call like this.

(use-package my-package
  :ensure t)

Because we set the use-package-always-ensure to true, we don't need to specify the ensure tag. So to install a package all we need is the following code.

(use-package my-package)

When declaring a package you will usually have other steps to take. things like setting options and activating major/minor modes. This can all take place in the use-package declaration. We simply create a :config tag. and write our configuration steps underneath.

(use-package my-package
  (my-package-mode 1))

There is also an :init tag as well that defines lisp code that will evaulate before the package is loaded. This is opposed to :config which will evaluate after the package has loaded.

To read more about use-package and how to use it visit the use-package docs (opens in a new tab).


© Josh Burns.RSS