2017-06-08 11:47:56 +02:00
|
|
|
;;; core/autoload/packages.el -*- lexical-binding: t; -*-
|
2017-02-02 19:15:58 -05:00
|
|
|
|
2019-06-11 08:01:42 +02:00
|
|
|
(require 'core-packages)
|
|
|
|
(load! "cache") ; in case autoloads haven't been generated yet
|
|
|
|
|
2017-04-04 12:29:34 -04:00
|
|
|
|
2018-03-14 18:25:22 -04:00
|
|
|
(defun doom--packages-choose (prompt)
|
|
|
|
(let ((table (cl-loop for pkg in package-alist
|
2019-06-11 08:01:42 +02:00
|
|
|
unless (doom-package-built-in-p (cdr pkg))
|
2018-03-14 18:25:22 -04:00
|
|
|
collect (cons (package-desc-full-name (cdr pkg))
|
|
|
|
(cdr pkg)))))
|
|
|
|
(cdr (assoc (completing-read prompt
|
|
|
|
(mapcar #'car table)
|
|
|
|
nil t)
|
|
|
|
table))))
|
|
|
|
|
|
|
|
(defun doom--refresh-pkg-cache ()
|
|
|
|
"Clear the cache for `doom-refresh-packages-maybe'."
|
|
|
|
(setq doom--refreshed-p nil)
|
|
|
|
(doom-cache-set 'last-pkg-refresh nil))
|
|
|
|
|
2017-02-03 07:58:16 -05:00
|
|
|
;;;###autoload
|
2018-03-14 18:25:22 -04:00
|
|
|
(defun doom-refresh-packages-maybe (&optional force-p)
|
|
|
|
"Refresh ELPA packages, if it hasn't been refreshed recently."
|
2017-05-27 18:29:32 +02:00
|
|
|
(when force-p
|
2018-03-14 18:25:22 -04:00
|
|
|
(doom--refresh-pkg-cache))
|
2018-01-06 17:00:14 -05:00
|
|
|
(unless (or (doom-cache-get 'last-pkg-refresh)
|
2017-11-08 22:49:11 +01:00
|
|
|
doom--refreshed-p)
|
2018-06-20 02:23:55 +02:00
|
|
|
(condition-case e
|
2017-05-19 22:29:47 +02:00
|
|
|
(progn
|
2017-05-27 18:29:32 +02:00
|
|
|
(message "Refreshing package archives")
|
2018-06-20 12:45:03 +02:00
|
|
|
(package-refresh-contents)
|
2018-05-24 16:41:14 +02:00
|
|
|
(doom-cache-set 'last-pkg-refresh t 1200))
|
2018-06-20 02:23:55 +02:00
|
|
|
((debug error)
|
2018-03-14 18:25:22 -04:00
|
|
|
(doom--refresh-pkg-cache)
|
2018-06-20 02:23:55 +02:00
|
|
|
(signal 'doom-error e)))))
|
2017-05-19 22:29:47 +02:00
|
|
|
|
2019-06-11 08:01:42 +02:00
|
|
|
|
|
|
|
;;
|
|
|
|
;;; Package metadata
|
|
|
|
|
2017-02-03 07:58:16 -05:00
|
|
|
;;;###autoload
|
2019-06-11 08:01:42 +02:00
|
|
|
(defun doom-package-plist (package)
|
|
|
|
"Returns PACKAGE's `package!' recipe from `doom-packages'."
|
|
|
|
(cdr (assq package doom-packages)))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun doom-package-desc (package)
|
|
|
|
"Returns PACKAGE's desc struct from `package-alist'."
|
|
|
|
(cadr (assq (or (car (doom-package-prop package :recipe))
|
|
|
|
package)
|
|
|
|
package-alist)))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun doom-package-true-name (package)
|
|
|
|
"Return PACKAGE's true name.
|
|
|
|
|
|
|
|
It is possible for quelpa packages to be given a psuedonym (the first argument
|
|
|
|
of `package!'). Its real name is the car of package's :recipe. e.g.
|
|
|
|
|
|
|
|
(package! X :recipe (Y :fetcher github :repo \"abc/def\"))
|
|
|
|
|
|
|
|
X's real name is Y."
|
|
|
|
(let ((sym (car (doom-package-prop package :recipe))))
|
|
|
|
(or (and (symbolp sym)
|
|
|
|
(not (keywordp sym))
|
|
|
|
sym)
|
|
|
|
package)))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun doom-package-psuedo-name (package)
|
|
|
|
"TODO"
|
|
|
|
(or (cl-loop for (package . plist) in doom-packages
|
|
|
|
for recipe-name = (car (plist-get plist :recipe))
|
|
|
|
if (eq recipe-name package)
|
|
|
|
return recipe-name)
|
|
|
|
package))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun doom-package-backend (package &optional noerror)
|
|
|
|
"Return backend that PACKAGE was installed with.
|
|
|
|
|
|
|
|
Can either be elpa, quelpa or emacs (built-in). Throws an error if NOERROR is
|
|
|
|
nil and the package isn't installed.
|
|
|
|
|
|
|
|
See `doom-package-recipe-backend' to get the backend PACKAGE is registered with
|
|
|
|
\(as opposed to what it is was installed with)."
|
|
|
|
(cl-check-type package symbol)
|
|
|
|
(let ((package-truename (doom-package-true-name package)))
|
|
|
|
(cond ((assq package-truename quelpa-cache) 'quelpa)
|
|
|
|
((assq package-truename package-alist) 'elpa)
|
|
|
|
((doom-package-built-in-p package) 'emacs)
|
|
|
|
((not noerror) (error "%s package is not installed" package)))))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun doom-package-recipe-backend (package &optional noerror)
|
|
|
|
"Return backend that PACKAGE is registered with.
|
|
|
|
|
|
|
|
See `doom-package-backend' to get backend for currently installed package."
|
|
|
|
(cl-check-type package symbol)
|
|
|
|
(cond ((not (doom-package-registered-p package))
|
|
|
|
(unless noerror
|
|
|
|
(error "%s package is not registered" package)))
|
|
|
|
((eval (doom-package-prop package :built-in))
|
|
|
|
'emacs)
|
|
|
|
((doom-package-prop package :recipe)
|
|
|
|
'quelpa)
|
|
|
|
('elpa)))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun doom-package-prop (package prop &optional nil-value)
|
|
|
|
"Return PROPerty in PACKAGE's plist.
|
|
|
|
|
|
|
|
Otherwise returns NIL-VALUE if package isn't registered or PROP doesn't
|
|
|
|
exist/isn't specified."
|
|
|
|
(cl-check-type package symbol)
|
|
|
|
(cl-check-type prop keyword)
|
|
|
|
(if-let (plist (doom-package-plist package))
|
|
|
|
(if (plist-member plist prop)
|
|
|
|
(plist-get plist prop)
|
|
|
|
nil-value)
|
|
|
|
nil-value))
|
|
|
|
|
|
|
|
|
|
|
|
;;
|
|
|
|
;;; Predicate functions
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun doom-package-built-in-p (package)
|
|
|
|
"Return non-nil if PACKAGE (a symbol) is built-in."
|
|
|
|
(unless (doom-package-installed-p package)
|
|
|
|
(or (package-built-in-p (doom-package-true-name package))
|
|
|
|
(locate-library (symbol-name package) nil doom-site-load-path))))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun doom-package-installed-p (package)
|
|
|
|
"Return non-nil if PACKAGE (a symbol) is installed."
|
|
|
|
(when-let (desc (doom-package-desc package))
|
|
|
|
(and (package-installed-p desc)
|
|
|
|
(file-directory-p (package-desc-dir desc)))))
|
|
|
|
|
|
|
|
;;;###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."
|
|
|
|
(let ((package (or (cl-loop for (pkg . plist) in doom-packages
|
|
|
|
for newname = (car (plist-get plist :recipe))
|
|
|
|
if (and (symbolp newname)
|
|
|
|
(eq newname package))
|
|
|
|
return pkg)
|
|
|
|
package)))
|
|
|
|
(when-let (plist (doom-package-plist package))
|
|
|
|
(not (eval (plist-get plist :ignore))))))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun doom-package-private-p (package)
|
|
|
|
"Return non-nil if PACKAGE was installed by the user's private config."
|
|
|
|
(doom-package-prop package :private))
|
2017-02-03 07:58:16 -05:00
|
|
|
|
2019-06-11 08:01:42 +02:00
|
|
|
;;;###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 (doom-package-true-name 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-prop package :modules))))
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun doom-package-different-backend-p (package)
|
|
|
|
"Return t if a PACKAGE (a symbol) has a new backend than what it was installed
|
|
|
|
with. Returns nil otherwise, or if package isn't installed."
|
|
|
|
(cl-check-type package symbol)
|
|
|
|
(and (doom-package-installed-p package)
|
|
|
|
(not (doom-get-depending-on package)) ; not a dependency
|
|
|
|
(not (eq (doom-package-backend package 'noerror)
|
|
|
|
(doom-package-recipe-backend package 'noerror)))))
|
|
|
|
|
|
|
|
;;;###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)
|
|
|
|
(when (doom-package-installed-p name)
|
|
|
|
(let ((package-truename (doom-package-true-name name)))
|
|
|
|
(when-let* ((quelpa-recipe (assq package-truename quelpa-cache))
|
|
|
|
(doom-recipe (assq package-truename doom-packages)))
|
|
|
|
(not (equal (cdr quelpa-recipe)
|
|
|
|
(cdr (plist-get (cdr doom-recipe) :recipe))))))))
|
|
|
|
|
|
|
|
(defvar quelpa-upgrade-p)
|
2017-02-03 07:58:16 -05:00
|
|
|
;;;###autoload
|
|
|
|
(defun doom-package-outdated-p (name)
|
2019-06-11 08:01:42 +02:00
|
|
|
"Determine whether NAME (a symbol) is outdated or not.
|
|
|
|
|
|
|
|
If outdated, returns a list, whose car is NAME, and cdr the current version list
|
|
|
|
and latest version list of the package."
|
2018-06-07 17:59:23 +02:00
|
|
|
(cl-check-type name symbol)
|
2019-06-11 08:01:42 +02:00
|
|
|
(when-let (desc (doom-package-desc name))
|
2017-08-06 16:30:53 +02:00
|
|
|
(let* ((old-version (package-desc-version desc))
|
2017-02-11 00:46:42 -05:00
|
|
|
(new-version
|
|
|
|
(pcase (doom-package-backend name)
|
2019-06-11 08:01:42 +02:00
|
|
|
(`quelpa
|
|
|
|
(let ((recipe (doom-package-prop name :recipe))
|
2017-02-19 06:59:55 -05:00
|
|
|
(dir (expand-file-name (symbol-name name) quelpa-build-dir))
|
2017-06-03 21:29:33 +02:00
|
|
|
(inhibit-message (not doom-debug-mode))
|
|
|
|
(quelpa-upgrade-p t))
|
2019-06-11 08:01:42 +02:00
|
|
|
(if-let (ver (quelpa-checkout recipe dir))
|
2017-02-11 00:46:42 -05:00
|
|
|
(version-to-list ver)
|
|
|
|
old-version)))
|
2019-06-11 08:01:42 +02:00
|
|
|
(`elpa
|
2017-02-11 00:46:42 -05:00
|
|
|
(let ((desc (cadr (assq name package-archive-contents))))
|
|
|
|
(when (package-desc-p desc)
|
|
|
|
(package-desc-version desc)))))))
|
2018-04-05 02:28:42 -04:00
|
|
|
(unless (and (listp old-version) (listp new-version))
|
|
|
|
(error "Couldn't get version for %s" name))
|
|
|
|
(when (version-list-< old-version new-version)
|
2017-02-11 00:46:42 -05:00
|
|
|
(list name old-version new-version)))))
|
2017-02-03 07:58:16 -05:00
|
|
|
|
2018-09-07 21:52:11 -04:00
|
|
|
|
2019-06-11 08:01:42 +02:00
|
|
|
;;
|
|
|
|
;;; Package list getters
|
2018-02-20 17:55:53 -05:00
|
|
|
|
2017-02-03 07:58:16 -05:00
|
|
|
;;;###autoload
|
2019-03-06 00:26:33 -05:00
|
|
|
(cl-defun doom-find-packages (&key (installed 'any)
|
|
|
|
(private 'any)
|
|
|
|
(disabled 'any)
|
|
|
|
(pinned 'any)
|
|
|
|
(ignored 'any)
|
|
|
|
(core 'any)
|
2019-06-11 08:01:42 +02:00
|
|
|
_changed
|
2019-03-06 00:26:33 -05:00
|
|
|
backend
|
|
|
|
deps)
|
2018-06-10 17:07:23 +02:00
|
|
|
"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).
|
2017-02-03 07:58:16 -05:00
|
|
|
|
2018-06-11 23:21:56 +02:00
|
|
|
You can build a filtering criteria using one or more of the following
|
|
|
|
properties:
|
|
|
|
|
2019-03-06 00:26:33 -05:00
|
|
|
: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
|
2018-06-11 23:21:56 +02:00
|
|
|
:pinned BOOL|ARCHIVE
|
|
|
|
Only return packages that are pinned (t), not pinned (nil) or pinned to a
|
|
|
|
specific archive (stringp)
|
2018-09-07 21:52:11 -04:00
|
|
|
:deps BOOL
|
2019-03-06 00:26:33 -05:00
|
|
|
Includes the package's dependencies (t) or not (nil).
|
2018-06-11 23:21:56 +02:00
|
|
|
|
2018-06-10 17:07:23 +02:00
|
|
|
Warning: this function is expensive, as it re-evaluates your all packages.el
|
|
|
|
files."
|
2019-06-11 08:01:42 +02:00
|
|
|
(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-get-dependencies-for 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)))))))
|
2019-03-06 00:26:33 -05:00
|
|
|
|
2019-03-10 08:10:59 -04:00
|
|
|
(defun doom--read-module-packages-file (file &optional raw noerror)
|
|
|
|
(with-temp-buffer ; prevent buffer-local settings from propagating
|
|
|
|
(condition-case e
|
|
|
|
(if (not raw)
|
|
|
|
(load file noerror t t)
|
|
|
|
(when (file-readable-p file)
|
|
|
|
(insert-file-contents file)
|
|
|
|
(while (re-search-forward "(package! " nil t)
|
|
|
|
(save-excursion
|
|
|
|
(goto-char (match-beginning 0))
|
|
|
|
(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)
|
2019-04-23 20:46:58 -04:00
|
|
|
'((:private)))
|
2019-03-10 08:10:59 -04:00
|
|
|
((file-in-directory-p file doom-core-dir)
|
2019-04-23 20:46:58 -04:00
|
|
|
'((:core)))
|
2019-03-10 08:10:59 -04:00
|
|
|
((doom-module-from-path file)))))
|
|
|
|
doom-packages))))))
|
|
|
|
((debug error)
|
|
|
|
(signal 'doom-package-error
|
|
|
|
(list (or (doom-module-from-path file)
|
|
|
|
'(:private . packages))
|
|
|
|
e))))))
|
|
|
|
|
2019-03-06 00:26:33 -05:00
|
|
|
;;;###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'.
|
|
|
|
|
2019-03-10 08:10:59 -04:00
|
|
|
If ALL-P, gather packages unconditionally across all modules, including disabled
|
|
|
|
ones."
|
|
|
|
(let ((noninteractive t)
|
|
|
|
(doom--stage 'packages)
|
|
|
|
(doom-modules (doom-modules))
|
|
|
|
doom-packages
|
|
|
|
doom-disabled-packages
|
|
|
|
package-pinned-packages)
|
|
|
|
(doom--read-module-packages-file (expand-file-name "packages.el" doom-core-dir) all-p)
|
|
|
|
(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 nil t))
|
|
|
|
(if all-p
|
|
|
|
(mapc #'doom--read-module-packages-file
|
|
|
|
(doom-files-in doom-modules-dir
|
|
|
|
:depth 2
|
|
|
|
:full t
|
2019-06-16 15:27:49 +02:00
|
|
|
:match "/packages\\.el$"
|
|
|
|
:sort nil))
|
2019-03-10 08:10:59 -04:00
|
|
|
(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))
|
|
|
|
(append (cl-loop for package in doom-core-packages
|
|
|
|
collect (list package :modules '((:core internal))))
|
|
|
|
(nreverse doom-packages))))
|
2017-02-19 06:59:55 -05:00
|
|
|
|
2018-06-14 00:04:12 +02:00
|
|
|
;;;###autoload
|
|
|
|
(defun doom-get-package-alist ()
|
|
|
|
"Returns a list of all desired packages, their dependencies and their desc
|
|
|
|
objects, in the order of their `package! blocks.'"
|
|
|
|
(cl-remove-duplicates
|
2019-03-06 00:26:33 -05:00
|
|
|
(cl-loop for name in (mapcar #'car doom-packages)
|
2018-09-25 10:31:52 -04:00
|
|
|
if (assq name package-alist)
|
|
|
|
nconc (cl-loop for dep in (package--get-deps name)
|
|
|
|
if (assq dep package-alist)
|
|
|
|
collect (cons dep (cadr it)))
|
|
|
|
and collect (cons name (cadr it)))
|
2018-06-14 00:04:12 +02:00
|
|
|
:key #'car
|
|
|
|
:from-end t))
|
|
|
|
|
2017-02-19 06:59:55 -05:00
|
|
|
;;;###autoload
|
2018-09-07 21:52:11 -04:00
|
|
|
(defun doom-get-depending-on (name &optional noerror)
|
2017-02-19 06:59:55 -05:00
|
|
|
"Return a list of packages that depend on the package named NAME."
|
2018-06-07 17:59:23 +02:00
|
|
|
(cl-check-type name symbol)
|
2019-06-11 08:01:42 +02:00
|
|
|
(setq name (or (car (doom-package-prop name :recipe)) name))
|
|
|
|
(unless (doom-package-built-in-p name)
|
|
|
|
(if-let (desc (cadr (assq name package-alist)))
|
2018-09-07 21:52:11 -04:00
|
|
|
(mapcar #'package-desc-name (package--used-elsewhere-p desc nil t))
|
|
|
|
(unless noerror
|
|
|
|
(error "Couldn't find %s, is it installed?" name)))))
|
2017-02-03 07:58:16 -05:00
|
|
|
|
2017-04-04 22:17:33 -04:00
|
|
|
;;;###autoload
|
2018-09-07 21:52:11 -04:00
|
|
|
(defun doom-get-dependencies-for (name &optional recursive noerror)
|
2017-04-04 22:17:33 -04:00
|
|
|
"Return a list of dependencies for a package."
|
2018-06-07 17:59:23 +02:00
|
|
|
(cl-check-type name symbol)
|
2018-09-07 21:52:11 -04:00
|
|
|
;; can't get dependencies for built-in packages
|
2019-06-11 08:01:42 +02:00
|
|
|
(unless (doom-package-built-in-p name)
|
|
|
|
(if-let (desc (doom-package-desc name))
|
2018-09-07 21:52:11 -04:00
|
|
|
(let* ((deps (mapcar #'car (package-desc-reqs desc)))
|
2019-06-11 08:01:42 +02:00
|
|
|
(deps (cl-remove-if #'doom-package-built-in-p deps)))
|
2018-09-07 21:52:11 -04:00
|
|
|
(if recursive
|
|
|
|
(nconc deps (mapcan (lambda (dep) (doom-get-dependencies-for dep t t))
|
|
|
|
deps))
|
|
|
|
deps))
|
|
|
|
(unless noerror
|
|
|
|
(error "Couldn't find %s, is it installed?" name)))))
|
2017-04-04 22:17:33 -04:00
|
|
|
|
2017-02-03 07:58:16 -05:00
|
|
|
;;;###autoload
|
2017-06-05 14:21:07 +02:00
|
|
|
(defun doom-get-outdated-packages (&optional include-frozen-p)
|
2017-02-11 00:46:42 -05:00
|
|
|
"Return a list of packages that are out of date. Each element is a list,
|
|
|
|
containing (PACKAGE-SYMBOL OLD-VERSION-LIST NEW-VERSION-LIST).
|
2017-02-09 04:25:32 -05:00
|
|
|
|
2017-06-05 14:21:07 +02:00
|
|
|
If INCLUDE-FROZEN-P is non-nil, check frozen packages as well.
|
|
|
|
|
2018-06-17 21:35:58 +02:00
|
|
|
Used by `doom-packages-update'."
|
2018-05-25 02:53:48 +02:00
|
|
|
(doom-refresh-packages-maybe doom-debug-mode)
|
2019-04-24 18:00:05 -04:00
|
|
|
(cl-loop for package in (mapcar #'car package-alist)
|
2019-06-11 08:01:42 +02:00
|
|
|
when (and (or (not (eval (doom-package-prop package :freeze)))
|
2019-04-24 18:00:05 -04:00
|
|
|
include-frozen-p)
|
2019-06-11 08:01:42 +02:00
|
|
|
(not (eval (doom-package-prop package :ignore)))
|
2019-04-24 18:00:05 -04:00
|
|
|
(not (doom-package-different-backend-p package))
|
|
|
|
(doom-package-outdated-p package))
|
|
|
|
collect it))
|
2017-02-03 07:58:16 -05:00
|
|
|
|
2017-02-02 19:15:58 -05:00
|
|
|
;;;###autoload
|
2017-02-03 07:58:16 -05:00
|
|
|
(defun doom-get-orphaned-packages ()
|
2017-02-09 04:25:32 -05:00
|
|
|
"Return a list of symbols representing packages that are no longer needed or
|
|
|
|
depended on.
|
|
|
|
|
2018-06-17 21:35:58 +02:00
|
|
|
Used by `doom-packages-autoremove'."
|
2017-02-19 06:59:55 -05:00
|
|
|
(let ((package-selected-packages
|
2019-03-06 00:26:33 -05:00
|
|
|
(mapcar #'car (doom-find-packages :ignored nil :disabled nil))))
|
2019-06-11 08:01:42 +02:00
|
|
|
(append (cl-remove-if #'doom-package-registered-p (package--removable-packages))
|
2017-11-05 16:04:31 +01:00
|
|
|
(cl-loop for pkg in package-selected-packages
|
2017-12-22 04:02:47 -05:00
|
|
|
if (and (doom-package-different-backend-p pkg)
|
2019-06-11 08:01:42 +02:00
|
|
|
(not (doom-package-built-in-p pkg)))
|
2017-11-05 16:04:31 +01:00
|
|
|
collect pkg))))
|
2017-02-03 07:58:16 -05:00
|
|
|
|
|
|
|
;;;###autoload
|
2019-03-06 00:26:33 -05:00
|
|
|
(defun doom-get-missing-packages ()
|
2017-04-04 22:17:42 -04:00
|
|
|
"Return a list of requested packages that aren't installed or built-in, but
|
|
|
|
are enabled (with a `package!' directive). Each element is a list whose CAR is
|
|
|
|
the package symbol, and whose CDR is a plist taken from that package's
|
|
|
|
`package!' declaration.
|
2017-02-09 04:25:32 -05:00
|
|
|
|
2018-06-17 21:35:58 +02:00
|
|
|
Used by `doom-packages-install'."
|
2018-06-11 23:21:56 +02:00
|
|
|
(cl-loop for (name . plist)
|
2019-03-06 00:26:33 -05:00
|
|
|
in (doom-find-packages :ignored nil
|
|
|
|
:disabled nil
|
|
|
|
:deps t)
|
2019-06-11 08:01:42 +02:00
|
|
|
if (and (equal (plist-get plist :pin)
|
|
|
|
(ignore-errors
|
|
|
|
(package-desc-archive
|
|
|
|
(cadr (assq name package-alist)))))
|
2018-09-07 21:52:11 -04:00
|
|
|
(or (not (doom-package-installed-p name))
|
2018-02-20 17:55:53 -05:00
|
|
|
(doom-package-different-backend-p name)
|
|
|
|
(doom-package-different-recipe-p name)))
|
2018-06-11 23:21:56 +02:00
|
|
|
collect (cons name plist)))
|
2017-02-03 07:58:16 -05:00
|
|
|
|
|
|
|
|
|
|
|
;;
|
|
|
|
;; Main functions
|
|
|
|
|
2018-06-08 01:42:08 +02:00
|
|
|
(defun doom--delete-package-files (name-or-desc)
|
|
|
|
(let ((pkg-build-dir
|
|
|
|
(if (package-desc-p name-or-desc)
|
|
|
|
(package-desc-dir name-or-desc)
|
|
|
|
(expand-file-name (symbol-name name-or-desc) quelpa-build-dir))))
|
|
|
|
(when (file-directory-p pkg-build-dir)
|
|
|
|
(delete-directory pkg-build-dir t))))
|
|
|
|
|
2018-05-20 12:05:17 +02:00
|
|
|
;;;###autoload
|
2017-02-04 21:07:54 -05:00
|
|
|
(defun doom-install-package (name &optional plist)
|
2017-02-03 07:58:16 -05:00
|
|
|
"Installs package NAME with optional quelpa RECIPE (see `quelpa-recipe' for an
|
|
|
|
example; the package name can be omitted)."
|
2018-06-07 17:59:23 +02:00
|
|
|
(cl-check-type name symbol)
|
2019-06-11 08:01:42 +02:00
|
|
|
(when (and (doom-package-installed-p name)
|
|
|
|
(not (doom-package-built-in-p name)))
|
2018-02-20 17:55:53 -05:00
|
|
|
(if (or (doom-package-different-backend-p name)
|
|
|
|
(doom-package-different-recipe-p name))
|
2018-01-04 16:09:40 -05:00
|
|
|
(doom-delete-package name t)
|
|
|
|
(user-error "%s is already installed" name)))
|
2017-11-13 18:00:46 +01:00
|
|
|
(let* ((inhibit-message (not doom-debug-mode))
|
2019-06-11 08:01:42 +02:00
|
|
|
(plist (or plist (doom-package-plist name))))
|
|
|
|
(if-let (recipe (plist-get plist :recipe))
|
2018-06-08 01:42:08 +02:00
|
|
|
(condition-case e
|
|
|
|
(let (quelpa-upgrade-p)
|
|
|
|
(quelpa recipe))
|
2018-06-20 02:23:55 +02:00
|
|
|
((debug error)
|
|
|
|
(doom--delete-package-files name)
|
|
|
|
(signal (car e) (cdr e))))
|
2017-11-13 18:00:46 +01:00
|
|
|
(package-install name))
|
2019-06-11 08:01:42 +02:00
|
|
|
(if (not (doom-package-installed-p name))
|
2018-06-08 01:42:08 +02:00
|
|
|
(doom--delete-package-files name)
|
2019-03-04 04:51:49 -05:00
|
|
|
(add-to-list 'package-selected-packages name nil 'eq)
|
2018-06-23 20:11:43 +02:00
|
|
|
(setf (alist-get name doom-packages) plist)
|
2018-05-18 01:11:20 +02:00
|
|
|
name)))
|
2017-02-03 07:58:16 -05:00
|
|
|
|
2018-05-20 12:05:17 +02:00
|
|
|
;;;###autoload
|
2017-06-08 11:47:56 +02:00
|
|
|
(defun doom-update-package (name &optional force-p)
|
2017-07-02 16:47:02 +02:00
|
|
|
"Updates package NAME (a symbol) if it is out of date, using quelpa or
|
|
|
|
package.el as appropriate."
|
2018-06-07 17:59:23 +02:00
|
|
|
(cl-check-type name symbol)
|
2019-06-11 08:01:42 +02:00
|
|
|
(unless (doom-package-installed-p name)
|
2019-02-19 15:03:14 -05:00
|
|
|
(error "%s isn't installed" name))
|
2017-11-13 18:01:38 +01:00
|
|
|
(when (doom-package-different-backend-p name)
|
|
|
|
(user-error "%s's backend has changed and must be uninstalled first" name))
|
2017-06-08 11:47:56 +02:00
|
|
|
(when (or force-p (doom-package-outdated-p name))
|
2017-06-03 21:30:41 +02:00
|
|
|
(let ((inhibit-message (not doom-debug-mode))
|
2019-06-11 08:01:42 +02:00
|
|
|
(desc (doom-package-desc name)))
|
2017-02-04 21:07:54 -05:00
|
|
|
(pcase (doom-package-backend name)
|
2018-06-07 17:59:23 +02:00
|
|
|
(`quelpa
|
2019-06-11 08:01:42 +02:00
|
|
|
(let ((name (doom-package-true-name name)))
|
|
|
|
(condition-case e
|
|
|
|
(let ((quelpa-upgrade-p t))
|
|
|
|
(quelpa (assq name quelpa-cache)))
|
|
|
|
((debug error)
|
|
|
|
(doom--delete-package-files name)
|
|
|
|
(signal (car e) (cdr e))))))
|
2018-06-07 17:59:23 +02:00
|
|
|
(`elpa
|
2017-06-03 21:30:41 +02:00
|
|
|
(let* ((archive (cadr (assq name package-archive-contents)))
|
2017-06-03 21:01:32 +02:00
|
|
|
(packages
|
|
|
|
(if (package-desc-p archive)
|
|
|
|
(package-compute-transaction (list archive) (package-desc-reqs archive))
|
|
|
|
(package-compute-transaction () (list (list archive))))))
|
2017-06-05 03:15:52 +02:00
|
|
|
(package-download-transaction packages))))
|
2017-06-08 11:47:56 +02:00
|
|
|
(unless (doom-package-outdated-p name)
|
2018-06-08 01:42:08 +02:00
|
|
|
(doom--delete-package-files desc)
|
2017-06-08 11:47:56 +02:00
|
|
|
t))))
|
2017-02-03 07:58:16 -05:00
|
|
|
|
2018-05-20 12:05:17 +02:00
|
|
|
;;;###autoload
|
2017-05-15 22:35:57 +02:00
|
|
|
(defun doom-delete-package (name &optional force-p)
|
2017-02-03 07:58:16 -05:00
|
|
|
"Uninstalls package NAME if it exists, and clears it from `quelpa-cache'."
|
2018-06-07 17:59:23 +02:00
|
|
|
(cl-check-type name symbol)
|
2019-06-11 08:01:42 +02:00
|
|
|
(unless (doom-package-installed-p name)
|
2017-04-18 05:45:24 -04:00
|
|
|
(user-error "%s isn't installed" name))
|
2017-06-10 23:32:59 +02:00
|
|
|
(let ((inhibit-message (not doom-debug-mode))
|
2019-06-11 08:01:42 +02:00
|
|
|
(name (doom-package-true-name name)))
|
|
|
|
(when-let (spec (assq name quelpa-cache))
|
|
|
|
(delq! spec quelpa-cache)
|
|
|
|
(quelpa-save-cache))
|
|
|
|
(package-delete (doom-package-desc name) force-p)
|
2018-06-08 01:42:08 +02:00
|
|
|
(doom--delete-package-files name)
|
2019-06-11 08:01:42 +02:00
|
|
|
(not (doom-package-installed-p name))))
|
2017-02-03 07:58:16 -05:00
|
|
|
|
|
|
|
|
|
|
|
;;
|
2018-05-24 19:10:29 +02:00
|
|
|
;; Interactive commands
|
2017-02-02 19:15:58 -05:00
|
|
|
|
2019-03-06 00:26:33 -05:00
|
|
|
;;;###autoload
|
|
|
|
(defun doom/reload-packages ()
|
|
|
|
"Reload `doom-packages', `package' and `quelpa'."
|
|
|
|
(interactive)
|
|
|
|
(message "Reloading packages")
|
|
|
|
(doom-initialize-packages t)
|
|
|
|
(message "Reloading packages...DONE"))
|
|
|
|
|
2017-02-02 19:15:58 -05:00
|
|
|
;;;###autoload
|
2018-05-24 19:10:29 +02:00
|
|
|
(defun doom/update-package (pkg)
|
|
|
|
"Prompts the user with a list of outdated packages and updates the selected
|
|
|
|
package. Use this interactively. Use `doom-update-package' for direct
|
|
|
|
calls."
|
|
|
|
(declare (interactive-only t))
|
|
|
|
(interactive
|
|
|
|
(let* ((packages (doom-get-outdated-packages))
|
2019-01-21 03:48:10 -05:00
|
|
|
(selection (if packages
|
|
|
|
(completing-read "Update package: "
|
|
|
|
(mapcar #'car packages)
|
|
|
|
nil t)
|
|
|
|
(user-error "All packages are up to date")))
|
2019-01-24 02:13:16 -05:00
|
|
|
(name (car (assoc (intern selection) package-alist))))
|
2019-01-21 03:48:10 -05:00
|
|
|
(unless name
|
|
|
|
(user-error "'%s' is already up-to-date" selection))
|
2019-01-24 02:13:16 -05:00
|
|
|
(list (assq name packages))))
|
2018-05-24 19:10:29 +02:00
|
|
|
(cl-destructuring-bind (package old-version new-version) pkg
|
2019-06-11 08:01:42 +02:00
|
|
|
(if-let (desc (doom-package-outdated-p package))
|
2018-05-24 19:10:29 +02:00
|
|
|
(let ((old-v-str (package-version-join old-version))
|
|
|
|
(new-v-str (package-version-join new-version)))
|
|
|
|
(if (y-or-n-p (format "%s will be updated from %s to %s. Update?"
|
|
|
|
package old-v-str new-v-str))
|
|
|
|
(message "%s %s (%s => %s)"
|
|
|
|
(if (doom-update-package package t) "Updated" "Failed to update")
|
|
|
|
package old-v-str new-v-str)
|
|
|
|
(message "Aborted")))
|
|
|
|
(message "%s is up-to-date" package))))
|
|
|
|
|
|
|
|
|
|
|
|
;;
|
|
|
|
;; Advice
|
|
|
|
|
|
|
|
;;;###autoload
|
|
|
|
(defun doom*package-delete (desc &rest _)
|
|
|
|
"Update `quelpa-cache' upon a successful `package-delete'."
|
|
|
|
(let ((name (package-desc-name desc)))
|
2019-06-11 08:01:42 +02:00
|
|
|
(unless (doom-package-installed-p name)
|
|
|
|
(when-let (spec (assq name quelpa-cache))
|
2018-06-23 16:48:58 +02:00
|
|
|
(setq quelpa-cache (delq spec quelpa-cache))
|
|
|
|
(quelpa-save-cache)
|
|
|
|
(doom--delete-package-files name)))))
|
2018-05-24 19:10:29 +02:00
|
|
|
|
|
|
|
|
2018-06-11 23:18:15 +02:00
|
|
|
;;
|
|
|
|
;; Make package.el cooperate with Doom
|
|
|
|
|
|
|
|
;; Updates QUELPA after deleting a package
|
|
|
|
;;;###autoload
|
|
|
|
(advice-add #'package-delete :after #'doom*package-delete)
|
|
|
|
|
|
|
|
;; Replace with Doom variants
|
|
|
|
;;;###autoload
|
2019-03-14 14:27:33 -04:00
|
|
|
(advice-add #'package-autoremove :override #'doom//autoremove)
|
2018-06-17 21:35:58 +02:00
|
|
|
|
2018-06-11 23:18:15 +02:00
|
|
|
;;;###autoload
|
2019-03-14 14:27:33 -04:00
|
|
|
(advice-add #'package-install-selected-packages :override #'doom//install)
|