32
votes

I want to declare all packages that I want to use in emacs in a init.el file. I wonder if its possible to load the missing packages with e.g. MELPA when I startup emacs without going through the list and mark the ones I want to install?

6

6 Answers

42
votes

New answer:

While my original answer is still valid, I now use the method suggested by Jordon. use-package is a fantastic tool for tidy Emacs configurations. In fact, I was already using it for clean package loading when I wrote my original answer.

At that time, I neglected to read all the way to the bottom of its README, and therefore didn't realize that it could handle package installation as well:

For package.el users

You can use use-package to load packages from ELPA with package.el. This is particularly useful if you share your .emacs between several machines; the relevant packages will download automatically once placed in your .emacs. The :ensure key will install the package automatically if it is not already present:

(use-package tex-site
  :ensure auctex)

After loading package.el and initializing my package repositories, my configuration contains

(if (not (package-installed-p 'use-package))
    (progn
      (package-refresh-contents)
      (package-install 'use-package)))

(require 'use-package)

and subsequently many snippets like this:

;;; Expand-region
(use-package expand-region
  :ensure expand-region
  :bind ("C-=" . er/expand-region))

If you find this information useful, please give Jordon an upvote.

Original answer:

Have a look at Cask and Pallet, which work together to solve this problem.

From the Cask website:

Cask for Emacs is what Bundler is to Ruby. It aims to make ELPA dependency management in Emacs painless (as painless as it can be). This includes both your local Emacs installation and Emacs package development.

After installing Cask, you'll need to add something like this to your Emacs configuration:

(require 'cask)
(cask-initialize)

Cask defines a doman-specific language for elisp dependencies. For installing packages, you'll need something like what is outlined here:

(source melpa)

(depends-on "auto-complete")
(depends-on "dash")
(depends-on "f")
(depends-on "flycheck")
(depends-on "helm")
(depends-on "magit")
(depends-on "popup")
(depends-on "projectile")
(depends-on "s")
(depends-on "smartparens")
(depends-on "yasnippet")

Note that this does not go into your Emacs config file, but rather into ~/.emacs.d/Cask.

Pallet updates the Cask file with packages installed interactively, e.g. via M-x package-list-packages, so you don't have to manually maintain the above file.

34
votes

All of these answers will work, but I would highly recommend using use-package

found here: https://github.com/jwiegley/use-package

use-package not only will automatically install your missing packages, but it greatly simplifies your init.el.

Here's an example from my init.el

;; initial package setup
(push "path/to/use-package" load-path)
(require 'use-package)
(require 'package)
(mapc (lambda(p) (push p package-archives))
      '(("marmalade" . "http://marmalade-repo.org/packages/")
        ("melpa" . "http://melpa.milkbox.net/packages/")))
(package-refresh-contents)
(package-initialize)

;; this will install undo-tree if it's not there
;; and it will set it up how I want
(use-package undo-tree
  :init (global-undo-tree-mode 1)
  :bind (("C-c j" . undo-tree-undo)
         ("C-c k" . undo-tree-redo)
         ("C-c l" . undo-tree-switch-branch)
         ("C-c ;" . undo-tree-visualize))
  :ensure t)

Take a look at my init.el here: https://github.com/jordonbiondo/.emacs.d/blob/master/init.el

each use-package block will install the specified package if it is not there, and it encapsulates all my additional setup for packages like keybindings, hooks, and other customizations.

7
votes

A bit of code in your init file can do that quite easily:

(setq my-onlinep nil)
(unless
    (condition-case nil
        (delete-process
         (make-network-process
          :name "my-check-internet"
          :host "elpa.gnu.org"
          :service 80))
      (error t))
  (setq my-onlinep t))

(setq my-packages
      '(ack-and-a-half
        ac-nrepl
        ... more packages here ...
        web-mode
        yaml-mode
        yari
        yasnippet))

(when my-onlinep
  (package-refresh-contents)
  (cl-loop for p in my-packages
           unless (package-installed-p p)
           do (package-install p)))

The online check is not really needed but helps to avoid getting a stuck Emacs startup when there is no internet connection.

For my full config see here: http://steckerhalter.co.vu/steckemacs.html#sec-2-5

5
votes

In addition to excellent options suggested above you may also want take a look at el-get. I use it for my emacs setup and have been very happy with it.

How is it different?

Two main advantages of el-get are

1) It can fetch packages from variety of sources, the website lists the following possible sources

github, emacswiki, GNU ELPA or Marmalade, privately hosted pages, git, bzr, CVS etc

2) It can be used to run OS commands like make if required during package installation. This makes installation of packages that have non-elisp parts easier, eg. emacs-jedi, ropemacs etc

Installing el-get

Installation is pretty simple you will need to add the following bootstrap code given at their website to your init file.

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

(unless (require 'el-get nil 'noerror)
  (with-current-buffer
      (url-retrieve-synchronously
       "https://raw.github.com/dimitri/el-get/master/el-get-install.el")
    (goto-char (point-max))
    (eval-print-last-sexp)))

(el-get 'sync)

This will install el-get if it is not already installed.

Installing packages

You can then declare the packages you want to install and let el-get install them

;; List of packages you want to install
(defvar my-packages '(auto-complete flycheck smart-parens...))
;; This will install any package from my-packages which is not already installed
(el-get 'sync my-packages)

OR

You can always install packages manually by doing el-get-install <package-name> RET, I doubt you will want to install your dozens of different packages this way but it is possible.

Updating packages

Updating packages is simply a matter of doing el-get-update to update a specific package or el-get-update-all to update all the installed packages.

2
votes

Here's my answer to a similar question: https://stackoverflow.com/a/14838150/245173. The code will automatically install missing packages, but won't update existing ones, so only the first startup is slow.

0
votes

I liked @steckerhalter's answer because it doesn't require any package installation. I'm building upon it here with the following improvements:

  • looking at package-selected-packages variables so you don't have to specify the packages manually. This variable is populated by custom-set-variables when you install a package via the package manager.

  • do the network check only if there are packages not installed.

    (let ((not-installed-packages 
           (seq-remove 'package-installed-p package-selected-packages)))
    
      (when (consp not-installed-packages)
    
        (setq my-onlinep nil)
        (unless
            (condition-case nil
                (delete-process
                 (make-network-process
                  :name "my-check-internet"
                  :host "elpa.gnu.org"
                  :service 80))
              (error t))
          (setq my-onlinep t))
    
        (when my-onlinep
          (package-refresh-contents)
          (dolist (p not-installed-packages)
            (package-install p)))
    
        (let ((still-not-installed-packages 
               (seq-remove 'package-installed-p package-selected-packages)))
          (if (consp still-not-installed-packages)
              (message "Still not installed: %s" still-not-installed-packages)
            (message "Successfully installed: %s" not-installed-packages)))
        ))