A.1 :use-package-ensure-system-package

The :ensure-system-package keyword allows you to ensure certain executables are available on your system alongside your package declarations.6

To use this extension, add this immediately after loading use-package:

(use-package use-package-ensure-system-package)

Now you can use the :ensure-system-package keyword. Here’s an example usage:

(use-package foo
  :ensure-system-package foo)

This will expect a global binary package to exist called foo. If it does not, it will use your system package manager to attempt an install of a binary by the same name asynchronously. This requires the GNU ELPA package system-packages, so for this to work you must install that first.

One way of making sure it is installed is with use-package together with :ensure.

(use-package system-packages
  :ensure t)

For example, on a Debian GNU/Linux system, this would call ‘apt-get install foo’.

If the package is named differently than the binary, you can use a cons in the form of (binary . package-name). For example:

(use-package foo
  :ensure-system-package
  (foocmd . foo))

On a Debian GNU/Linux system, this would call apt install foo if Emacs could not locate the executable foocmd.7

:ensure-system-package can also take a cons where the cdr is a string that will get called by (async-shell-command) to install if it isn’t found. This does not depend on any external package.

(use-package tern
  :ensure-system-package (tern . "npm i -g tern"))

To install several packages, you can pass in a list of conses:

(use-package ruby-mode
  :ensure-system-package
  ((rubocop     . "gem install rubocop")
   (ruby-lint   . "gem install ruby-lint")
   (ripper-tags . "gem install ripper-tags")
   (pry         . "gem install pry")))

Finally, in case the package dependency does not provide a global executable, you can ensure that packages exist by checking the presence of a file by providing a string like so:

(use-package dash-at-point
  :if (eq system-type 'darwin)
  :ensure-system-package
  ("/Applications/Dash.app" . "brew cask install dash"))

:ensure-system-package will use system-packages-install to install system packages, except where a custom command has been specified, in which case it will be executed verbatim by async-shell-command.

The user options system-packages-package-manager and system-packages-use-sudo are honored, but not for custom commands. Custom commands should include the call to sudo in the command if needed.


Footnotes

(6)

On macOS, your exec-path might be different if you are starting Emacs as a GUI app instead of from a shell. If you find that Emacs on macOS cannot find some executables that you know are already installed, you could try the exec-path-from-shell package.

(7)

For manual testing, you could use the executable-find function, which is what ‘system-packages’ uses internally.