Skip to content

emacsfodder/elpaca

 
 

Repository files navigation

Elpaca: An Elisp Package Manager

“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.

Installation

If you’re impatient, take a look at the example configuration

Requirements

  • Emacs >= 27.1
  • git (minimum version TBD)
  • Windows users must be able to create symlinks.

Bootstrap Snippet

To install Elpaca, add the following bootstrapping snippet to your init.el. It must come before any calls to other Elpaca functions/macros. This will clone Elpaca into your 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)

Basic concepts

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)))))

Recipes

A recipe provides Elpaca with the metadata necessary to build and install a package. It is a list of the form:
(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 of elpaca-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 possibly elpaca-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)

Inheritance precedence

The following list shows the order of precedence for inheritance. Each item takes precedence over the items which follow it.
  • 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

The abnormal hook 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)

Menus

A menu is a function which returns an alist of the form:
((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.

elpaca-menu-functions

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.

Orders

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))

elpaca-order-functions

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

Elpaca installs packages asynchronously. Orders are automatically queued in a list. A queue is considered “processed” when all of its orders have either finished or failed.

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)

Installing Packages

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"))
    

Finding, Updating, Removing Packages

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:

CommandDefault BindingDescription
elpaca-ui-send-input!Send input string to current process.
elpaca-ui-search-installedISearch for “#unique #installed”
elpaca-ui-search-markedMSearch for “#unique #marked”
elpaca-ui-search-orphanedOSearch for “#unique #orphan”
elpaca-ui-search-refreshRRerun the current search for BUFFER.
elpaca-ui-search-triedTSearch for “#unique #installed !#declared”
elpaca-ui-unmarkUUnmark current package.
elpaca-ui-browse-packagebBrowse current package’s URL via ‘browse-url’.
elpaca-ui-mark-deletedMark package for delete action.
elpaca-ui-mark-installiMark package for install action.
elpaca-loglDisplay ‘elpaca-log-buffer’.
elpaca-managermDisplay elpaca’s package management UI.
elpaca-ui-mark-rebuildrMark package for rebuild action.
elpaca-ui-searchsFilter current buffer by QUERY. If QUERY is nil, prompt for it.
elpaca-statustLog most recent events for packages.
elpaca-ui-mark-updateuMark package for update action.
elpaca-visitvOpen ITEM’s local repository directory.
elpaca-ui-execute-marksxExecute each action in ‘elpaca-ui-marked-packages’.

Example configuration

The following is an example init.el using Elpaca:

About

An elisp package manager

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Emacs Lisp 99.1%
  • Other 0.9%