“Chews data, spits packages.”
Elpaca is an elisp package manager. It allows users to find, install, update, and remove third-party packages for Emacs. It is a replacement for the built-in Emacs package manager, package.el.
If you’re impatient, take a look at the example configuration
- Emacs >= 27.1
- git (minimum version TBD)
- Windows users must be able to create symlinks.
user-emacs-directory
under the elpaca
subdirectory.
It then builds and activates Elpaca.
(declare-function elpaca-generate-autoloads "elpaca")
(defvar elpaca-directory (expand-file-name "elpaca/" user-emacs-directory))
(defvar elpaca-builds-directory (expand-file-name "builds/" elpaca-directory))
(when-let ((elpaca-repo (expand-file-name "repos/elpaca/" elpaca-directory))
(elpaca-build (expand-file-name "elpaca/" elpaca-builds-directory))
(elpaca-target (if (file-exists-p elpaca-build) elpaca-build elpaca-repo))
(elpaca-url "https://www.github.com/progfolio/elpaca.git")
((add-to-list 'load-path elpaca-target))
((not (file-exists-p elpaca-repo)))
(buffer (get-buffer-create "*elpaca-bootstrap*")))
(condition-case-unless-debug err
(progn
(unless (zerop (call-process "git" nil buffer t "clone" elpaca-url elpaca-repo))
(error "%s" (list (with-current-buffer buffer (buffer-string)))))
(byte-recompile-directory elpaca-repo 0 'force)
(require 'elpaca)
(elpaca-generate-autoloads "elpaca" elpaca-repo)
(kill-buffer buffer))
((error)
(delete-directory elpaca-directory 'recursive)
(with-current-buffer buffer
(goto-char (point-max))
(insert (format "\n%S" err))
(display-buffer buffer)))))
(require 'elpaca-autoloads)
(add-hook 'after-init-hook #'elpaca-process-queues)
(elpaca (elpaca :host github :repo "progfolio/elpaca"))
You’ll also want to disable package.el in your early-init file, and remove anything related to package.el in your init file.
e.g. calls to (package-activate-all)
.
(setq package-enable-at-startup nil)
The elpaca-example
macro in the following examples reduces verbosity.
It is not part of Elpaca.
Examples will use the following recipe menu, which offers a “burger” package recipe:
(defun elpaca-example-menu (_)
'((burger . (:recipe (:buns 2 :lettuce t :tomato t :beef t :cheese t :cook well-done :from elpaca-example-menu)))))
(ITEM . PROPS)
ITEM is a symbol uniquely identifying the package. PROPS is a plist with any of the following recipe keywords:
- :host | :fetcher
- A symbol or string representing the hosting service of the repository.
(example :host github) (example :fetcher gitlab) (example :host "www.example.com")
- :repo
- A string of the form
USER/REPO
.
(example :host github :repo "user/example")
- :branch
- The repository branch to check out when installing the package.
(example :host github :repo "user/example" :branch "main")
- :tag
- The tag to check out when installing the package.
(example :host github :repo "user/example" :tag "v1.0")
- :ref
- The git ref to check out when installing the package.
(example :host github :repo "user/example" :ref "a76ca0a") ;; Check out a specific commit.
- :depth
- The package repository’s history depth.
(example :depth 1) ;; Shallow clone with history truncated to 1 commit. (example :depth nil) ;; Full repository clone.
- :remotes
- A list of repository remotes.
The first element is the default remote when installing the package. If it is a string, it names the default remote. The remaining elements are lists of the form:
(NAME . PROPS)
NAME is a string used to name the remote. PROPS are recipe keyword/value pairs used to override values previously declared in the recipe.
(example :remotes ("origin"))
(example :host github :repo "original/example"
:remotes ("origin"
("fork" :host gitlab :repo "user/example-fork.el")))
- :main
- The name of the main elisp file.
When provided this can speed up the process of cloning and loading a package’s dependencies.
(example :main "example.el")
- :build
- A list of build steps, nil or t.
If the list starts with the symbol
:not
. The set difference ofelpaca-default-build-steps
and the remaining elements of the list are used.(example :build (:not autoloads))
- :inherit
- When non-nil, inherit PROPS from
elpaca-order-functions
and possiblyelpaca-menu-functions
. For example, without inheritance:
(elpaca-example (elpaca-recipe '(burger :inherit nil)))
returns the recipe as declared:
(:package "burger" :inherit nil)
With inheritance enabled:
(elpaca-example (elpaca-recipe '(burger :inherit t)))
the elpaca-example-menu provides the rest of the “burger” recipe.
(:package "burger" :beef t :buns 2 :cheese t :cook well-done :from elpaca-example-menu :inherit t :lettuce t :tomato t)
- elpaca-recipe-functions
- declared recipe
- elpaca-order-functions
- elpaca-menu-functions
(elpaca-example
(let ((elpaca-recipe-functions (lambda (recipe) '(:from recipe-functions :cheese extra)))
(elpaca-order-functions (lambda (order) '(:from order-functions :tomato nil))))
(elpaca-recipe '(burger))))
(:package "burger" :beef t :buns 2 :cheese extra :cook well-done :from recipe-functions :lettuce t :tomato nil)
elpaca-recipe-functions
runs via run-hook-with-args-until-success
just before installing the package.
Each function in the list should accept the current recipe as its sole argument and return either nil or a plist.
The first function to return a plist has its return value merged with the current recipe.
This is useful if you want to guarantee the values of certain keywords despite allowing recipe inheritance.
(elpaca-example
(let ((elpaca-recipe-functions
'((lambda (recipe)
"If a recipe calls for cheese, I always want extra."
(when (plist-get recipe :cheese) (list :cheese 'extra))))))
(elpaca-recipe '(burger))))
(:package "burger" :beef t :buns 2 :cheese extra :cook well-done :from elpaca-example-menu :lettuce t :tomato t)
((ITEM . DATA)...)
ITEM is a symbol uniquely identifying a package. DATA is a plist of package metadata. DATA must contain the following keywords:
- :recipe
- A package recipe.
- :source
- A string naming the menu.
It may also provide additional information about a package. For example, the Elpaca UI utilizes the following keywords when present:
- :url
- The package’s website URL.
- :description
- A description of the package.
- :date
- The time of package’s last update.
The function must accept one of the following REQUEST symbols as an argument:
- index
- Return the alist described above
- update
- update the menu’s alist.
(defun elpaca-menu-minimal (request_)
"A minimal menu example.
Ignore REQUEST, as this is a static, curated list of packages."
'((example :source "EXAMPLE" :recipe (example :host github :repo "user/example"))
(two :source "EXAMPLE" :recipe (two :host gitlab :repo "user/two"))))
Menus allow one to offer Elpaca users curated lists of package recipes. For example, melpulls implements an Elpaca menu for pending MELPA packages.
The elpaca-menu-functions
variable contains menu functions for the following package sources by default:
Menus are checked in order until one returns the requested menu item or the menu list is exhausted.
At a minimum, an order is a symbol which represents the name of a menu item:
(elpaca example)
An order may also be a partial or full recipe:
(elpaca (example :host gitlab))
(elpaca (example :host gitlab :repo "user/example" :inherit nil))
The abnormal hook elpaca-order-functions
runs via run-hook-with-args-until-success
before elpaca-menu-functions
.
Each function in the list should accept the current order as its sole argument and return either nil or a plist.
The first function to return a plist has its return value merged with the current order.
This is useful for declaring default order properties. For example, the following function disables recipe inheritance by default:
(elpaca-example
(let ((elpaca-order-functions '((lambda (_) '(:inherit nil)))))
(elpaca-recipe 'burger)))
(:package "burger" :inherit nil)
Queues make it possible to ensure a package is installed, activated, and configured prior to other packages.
The elpaca-queue
macro wraps calls to elpaca
, ensuring those orders are processed in their own queue.
This is especially useful when one wants to install a package to use later on in their init file.
For example, a package which implements an Elpaca menu:
(elpaca-queue (elpaca (melpulls :host github :repo "progfolio/melpulls"))
(add-to-list 'elpaca-menu-functions #'melpulls)
(elpaca-update-menus #'melpulls))
;; Implicitly queued into a new queue.
(elpaca menu-item-available-in-melpulls)
- elpaca
(order &rest body)
Installs ORDER and executes BODY after processing ORDER’s queue.
ORDER is an order as described above.
This macro is for programmatic use in one’s init file. Any of the following will install the “example” package:
(elpaca example) ;; recipe looked up in `elpaca-menu-functions'.
(elpaca example (message "Messaged after the order's queue has processed."))
(elpaca (example :host github :repo "user/example"))
(elpaca `(example :host github :repo "user/example"
,@(when (eq system-type 'darwin) ;; backqouting supported
(list :pre-build ((message "Mac specific pre-build"))))))
If ORDER is nil, BODY is still executed after processing the current queue.
(elpaca first (message "First configured"))
;; If this weren't wrapped in an `elpaca' call, it would be executed FIRST
;; Due to the "first" and "third" package installing asynchronously.
(elpaca nil (message "Second"))
(elpaca third (message "Third configured"))
- elpaca-use-package
(order &rest body)
A wrapper for the use-package macro. ORDER is the same as above. BODY must conform to use-package’s ARGS.
(elpaca use-package (require 'use-package)) ; install use-package (elpaca-use-package (example :host github :repo "user/example") :config (message "Example configured"))
The elpaca-manger
command will pop to the Elpaca manager buffer.
From this buffer you can search and act on all the packages Elpaca knows about.
The following commands are available in the Elpaca manager buffer:
Command | Default Binding | Description |
---|---|---|
elpaca-ui-send-input | ! | Send input string to current process. |
elpaca-ui-search-installed | I | Search for “#unique #installed” |
elpaca-ui-search-marked | M | Search for “#unique #marked” |
elpaca-ui-search-orphaned | O | Search for “#unique #orphan” |
elpaca-ui-search-refresh | R | Rerun the current search for BUFFER. |
elpaca-ui-search-tried | T | Search for “#unique #installed !#declared” |
elpaca-ui-unmark | U | Unmark current package. |
elpaca-ui-browse-package | b | Browse current package’s URL via ‘browse-url’. |
elpaca-ui-mark-delete | d | Mark package for delete action. |
elpaca-ui-mark-install | i | Mark package for install action. |
elpaca-log | l | Display ‘elpaca-log-buffer’. |
elpaca-manager | m | Display elpaca’s package management UI. |
elpaca-ui-mark-rebuild | r | Mark package for rebuild action. |
elpaca-ui-search | s | Filter current buffer by QUERY. If QUERY is nil, prompt for it. |
elpaca-status | t | Log most recent events for packages. |
elpaca-ui-mark-update | u | Mark package for update action. |
elpaca-visit | v | Open ITEM’s local repository directory. |
elpaca-ui-execute-marks | x | Execute each action in ‘elpaca-ui-marked-packages’. |
The following is an example init.el using Elpaca: