doomemacs/core/autoload/packages.el
Henrik Lissner 93f7520c79
Refactor Doom core init process (again)
- Eager-load all core autoloaded libraries if autoloads file isn't
  present.
- Renames functions to be more descriptive of their true purpose:
  - doom-initialize-autoloads -> doom-load-autoloads-file
  - doom-load-env-vars -> doom-load-envvars-file
- Use doom-module-p instead of featurep! for backend use (the latter is
  mainly syntax sugar for module use, and evaluates at compile/expansion
  time, which may cause hash-table-p errors early in the startup
  process).
- Reorder plist library to prevent load order race condition with the
  functions using the macros that haven't been defined yet.
2019-07-22 23:22:54 +02:00

281 lines
11 KiB
EmacsLisp

;;; core/autoload/packages.el -*- lexical-binding: t; -*-
;;
;;; Package metadata
;;;###autoload
(defun doom-package-get (package &optional prop nil-value)
"Returns PACKAGE's `package!' recipe from `doom-packages'."
(let ((plist (cdr (assq package doom-packages))))
(if prop
(if (plist-member plist prop)
(plist-get plist prop)
nil-value)
plist)))
;;;###autoload
(defun doom-package-recipe (package &optional prop nil-value)
"Returns the `straight' recipe PACKAGE was registered with."
(let ((plist (gethash (symbol-name package) straight--recipe-cache)))
(if prop
(if (plist-member plist prop)
(plist-get plist prop)
nil-value)
plist)))
;;;###autoload
(defun doom-package-build-recipe (package &optional prop nil-value)
"Returns the `straight' recipe PACKAGE was installed with."
(let ((plist (nth 2 (gethash (symbol-name package) straight--build-cache))))
(if prop
(if (plist-member plist prop)
(plist-get plist prop)
nil-value)
plist)))
;;;###autoload
(defun doom-package-build-time (package)
"TODO"
(car (gethash (symbol-name package) straight--build-cache)))
;;;###autoload
(defun doom-package-dependencies (package &optional recursive noerror)
"Return a list of dependencies for a package."
(let ((deps (nth 1 (gethash (symbol-name package) straight--build-cache))))
(if recursive
(nconc deps (mapcan (lambda (dep) (doom-package-dependencies dep t t))
deps))
deps)))
(defun doom-package-depending-on (package &optional noerror)
"Return a list of packages that depend on the package named NAME."
(cl-check-type name symbol)
;; can't get dependencies for built-in packages
(unless (or (doom-package-build-recipe name)
noerror)
(error "Couldn't find %s, is it installed?" name))
(cl-loop for pkg in (hash-table-keys straight--build-cache)
for deps = (doom-package-dependencies pkg)
if (memq package deps)
collect pkg
and append (doom-package-depending-on pkg t)))
;;
;;; Predicate functions
;;;###autoload
(defun doom-package-built-in-p (package)
"Return non-nil if PACKAGE (a symbol) is built-in."
(eq (doom-package-build-recipe package :type)
'built-in))
;;;###autoload
(defun doom-package-installed-p (package)
"Return non-nil if PACKAGE (a symbol) is installed."
(file-directory-p (straight--build-dir (symbol-name package))))
;;;###autoload
(defun doom-package-registered-p (package)
"Return non-nil if PACKAGE (a symbol) has been registered with `package!'.
Excludes packages that have a non-nil :built-in property."
(when-let (plist (doom-package-get package))
(not (eval (plist-get plist :ignore) t))))
;;;###autoload
(defun doom-package-private-p (package)
"Return non-nil if PACKAGE was installed by the user's private config."
(doom-package-get package :private))
;;;###autoload
(defun doom-package-protected-p (package)
"Return non-nil if PACKAGE is protected.
A protected package cannot be deleted and will be auto-installed if missing."
(memq package doom-core-packages))
;;;###autoload
(defun doom-package-core-p (package)
"Return non-nil if PACKAGE is a core Doom package."
(or (doom-package-protected-p package)
(assq :core (doom-package-get package :modules))))
;;;###autoload
(defun doom-package-different-recipe-p (name)
"Return t if a package named NAME (a symbol) has a different recipe than it
was installed with."
(cl-check-type name symbol)
;; TODO
;; (when (doom-package-installed-p name)
;; (when-let* ((doom-recipe (assq name doom-packages))
;; (install-recipe (doom-package-recipe)))
;; (not (equal (cdr quelpa-recipe)
;; (cdr (plist-get (cdr doom-recipe) :recipe))))))
)
;;
;;; Package list getters
;;;###autoload
(cl-defun doom-find-packages (&key (installed 'any)
(private 'any)
(disabled 'any)
(pinned 'any)
(ignored 'any)
(core 'any)
_changed
backend
deps)
"Retrieves a list of primary packages (i.e. non-dependencies). Each element is
a cons cell, whose car is the package symbol and whose cdr is the quelpa recipe
(if any).
You can build a filtering criteria using one or more of the following
properties:
:backend 'quelpa|'elpa|'emacs|'any
Include packages installed through 'quelpa, 'elpa or 'emacs. 'any is the
wildcard.
:installed BOOL|'any
t = only include installed packages
nil = exclude installed packages
:private BOOL|'any
t = only include user-installed packages
nil = exclude user-installed packages
:core BOOL|'any
t = only include Doom core packages
nil = exclude Doom core packages
:disabled BOOL|'any
t = only include disabled packages
nil = exclude disabled packages
:ignored BOOL|'any
t = only include ignored packages
nil = exclude ignored packages
:pinned BOOL|ARCHIVE
Only return packages that are pinned (t), not pinned (nil) or pinned to a
specific archive (stringp)
:deps BOOL
Includes the package's dependencies (t) or not (nil).
Warning: this function is expensive, as it re-evaluates your all packages.el
files."
(delete-dups
(cl-loop for (sym . plist) in doom-packages
if (and (or (not backend)
(eq (doom-package-backend sym 'noerror) backend))
(or (eq ignored 'any)
(let* ((form (plist-get plist :ignore))
(value (eval form)))
(if ignored value (not value))))
(or (eq disabled 'any)
(if disabled
(plist-get plist :disable)
(not (plist-get plist :disable))))
(or (eq installed 'any)
(if installed
(doom-package-installed-p sym)
(not (doom-package-installed-p sym))))
(or (eq private 'any)
(let ((modules (plist-get plist :modules)))
(if private
(assq :private modules)
(not (assq :private modules)))))
(or (eq core 'any)
(let ((modules (plist-get plist :modules)))
(if core
(assq :core modules)
(not (assq :core modules)))))
(or (eq pinned 'any)
(cond ((eq pinned 't)
(plist-get plist :pin))
((null pinned)
(not (plist-get plist :pin)))
((equal (plist-get plist :pin) pinned)))))
collect (cons sym plist)
and if (and deps (not (doom-package-built-in-p sym)))
nconc
(cl-loop for pkg in (doom-package-dependencies sym 'recursive 'noerror)
if (or (eq installed 'any)
(if installed
(doom-package-installed-p pkg)
(not (doom-package-installed-p pkg))))
collect (cons pkg (cdr (assq pkg doom-packages)))))))
(defun doom--read-module-packages-file (file &optional eval noerror)
(with-temp-buffer ; prevent buffer-local settings from propagating
(condition-case e
(if (not eval)
(load file noerror t t)
(when (file-readable-p file)
(insert-file-contents file)
(delay-mode-hooks (emacs-lisp-mode))
(while (re-search-forward "(package! " nil t)
(save-excursion
(goto-char (match-beginning 0))
(unless (string-match-p
"^.*;" (buffer-substring-no-properties
(line-beginning-position)
(point)))
(cl-destructuring-bind (name . plist) (cdr (sexp-at-point))
(push (cons name
(plist-put plist :modules
(cond ((file-in-directory-p file doom-private-dir)
'((:private)))
((file-in-directory-p file doom-core-dir)
'((:core)))
((doom-module-from-path file)))))
doom-packages)))))))
((debug error)
(signal 'doom-package-error
(list (or (doom-module-from-path file)
'(:private . packages))
e))))))
;;;###autoload
(defun doom-package-list (&optional all-p)
"Retrieve a list of explicitly declared packages from enabled modules.
This excludes core packages listed in `doom-core-packages'.
If ALL-P, gather packages unconditionally across all modules, including disabled
ones."
(let ((noninteractive t)
(doom-modules (doom-modules))
doom-packages
doom-disabled-packages)
(doom--read-module-packages-file
(expand-file-name "packages.el" doom-core-dir)
all-p t)
(let ((private-packages (expand-file-name "packages.el" doom-private-dir)))
(unless all-p
;; We load the private packages file twice to ensure disabled packages
;; are seen ASAP, and a second time to ensure privately overridden
;; packages are properly overwritten.
(doom--read-module-packages-file private-packages t t))
(if all-p
(mapc #'doom--read-module-packages-file
(doom-files-in doom-modules-dir
:depth 2
:full t
:match "/packages\\.el$"))
(cl-loop for key being the hash-keys of doom-modules
for path = (doom-module-path (car key) (cdr key) "packages.el")
for doom--current-module = key
do (doom--read-module-packages-file path nil t)))
(doom--read-module-packages-file private-packages all-p t))
(nreverse doom-packages)))
;;
;;; Main functions
;;;###autoload
(defun doom/reload-packages ()
"Reload `doom-packages', `package' and `quelpa'."
(interactive)
(message "Reloading packages")
(doom-initialize-packages t)
(message "Reloading packages...DONE"))