1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2026-01-09 13:10:57 -08:00

Various improvements for deferred installation

This commit is contained in:
Radon Rosborough 2017-03-18 19:34:28 -07:00
parent 57e38152e1
commit ecc5fddda4

View file

@ -185,21 +185,43 @@ Must be set before loading use-package."
(defcustom use-package-ensure-function 'use-package-ensure-elpa (defcustom use-package-ensure-function 'use-package-ensure-elpa
"Function that ensures a package is installed. "Function that ensures a package is installed.
This function is called with three arguments: the name of the This function is called with four arguments: the name of the
package declared in the `use-package' form; the argument passed package declared in the `use-package' form; the argument passed
to `:ensure'; and the current `state' plist created by previous to `:ensure'; the current `state' plist created by previous
handlers. Note that this function is called whenever `:ensure' is handlers; and a keyword indicating the context in which the
provided, even if it is nil. It is up to the function to decide installation is occurring.
on the semantics of the various values for `:ensure'.
The default value uses package.el to install the package." Note that this function is called whenever `:ensure' is provided,
even if it is nil. It is up to the function to decide on the
semantics of the various values for `:ensure'.
This function should return non-nil if the package is installed.
The default value uses package.el to install the package.
Possible values for the context keyword are:
:byte-compile - package installed during byte-compilation
:ensure - package installed normally by :ensure
:autoload - deferred installation triggered by an autoloaded
function
:after - deferred installation triggered by the loading of a
feature listed in the :after declaration
:config - deferred installation was specified at the same time
as :demand, so the installation was triggered
immediately
:unknown - context not provided
Note that third-party code can provide other values for the
context keyword by calling `use-package-install-deferred-package'
with the appropriate value."
:type '(choice (const :tag "package.el" use-package-ensure-elpa) :type '(choice (const :tag "package.el" use-package-ensure-elpa)
(function :tag "Custom")) (function :tag "Custom"))
:group 'use-package) :group 'use-package)
(defcustom use-package-pre-ensure-function 'ignore (defcustom use-package-pre-ensure-function 'ignore
"Function that is called upon installation deferral. "Function that is called upon installation deferral.
It is called with the same arguments as It is called immediately with the same arguments as
`use-package-ensure-function', but only if installation has been `use-package-ensure-function', but only if installation has been
deferred. It is intended for package managers other than deferred. It is intended for package managers other than
package.el which might want to activate the autoloads of a package.el which might want to activate the autoloads of a
@ -577,9 +599,7 @@ manually updated package."
;; ;;
(defvar use-package--deferred-packages (make-hash-table) (defvar use-package--deferred-packages (make-hash-table)
"Hash mapping packages to forms which install them. "Hash mapping packages to data about their installation.
If `use-package' needs to install one of the named packages, it
will evaluate the corresponding form to do so.
The keys are not actually symbols naming packages, but rather The keys are not actually symbols naming packages, but rather
symbols naming the features which are the names of \"packages\" symbols naming the features which are the names of \"packages\"
@ -587,14 +607,29 @@ required by `use-package' forms. Since
`use-package-ensure-function' could be set to anything, it is `use-package-ensure-function' could be set to anything, it is
actually impossible for `use-package' to determine what package actually impossible for `use-package' to determine what package
is supposed to provide the feature being ensured just based on is supposed to provide the feature being ensured just based on
the value of `:ensure'. The values are unevaluated Lisp forms. ") the value of `:ensure'.
(defun use-package-install-deferred-package Each value is a cons, with the car being the the value passed to
(name &optional no-prompt) `:ensure' and the cdr being the `state' plist. See
`use-package-install-deferred-package' for information about how
these values are used to call `use-package-ensure-function'.")
(defun use-package-install-deferred-package (name &optional context)
"Install a package whose installation has been deferred. "Install a package whose installation has been deferred.
NAME should be a symbol naming a package (actually, a feature). NAME should be a symbol naming a package (actually, a feature).
The user is prompted for confirmation first, unless NO-PROMPT is This is done by calling `use-package-ensure-function' is called
non-nil. Return t if the package is installed, nil otherwise." with four arguments: the key (NAME) and the two elements of the
cons in `use-package--deferred-packages' (the value passed to
`:ensure', and the `state' plist), and a keyword providing
information about the context in which the installation is
happening. (This defaults to `:unknown' but can be overridden by
providing CONTEXT.)
Return t if the package is installed, nil otherwise. (This is
determined by the return value of `use-package-ensure-function'.)
If the package is installed, its entry is removed from
`use-package--deferred-packages'. If the package has no entry in
`use-package--deferred-packages', do nothing and return t."
(interactive (interactive
(let ((packages nil)) (let ((packages nil))
(maphash (lambda (package info) (maphash (lambda (package info)
@ -607,13 +642,16 @@ non-nil. Return t if the package is installed, nil otherwise."
packages packages
nil nil
'require-match) 'require-match)
'no-prompt) :interactive)
(user-error "No packages with deferred installation")))) (user-error "No packages with deferred installation"))))
(when (or no-prompt (let ((spec (gethash name use-package--deferred-packages)))
(y-or-n-p (format "Install package %S? " name))) (if spec
(eval (gethash name use-package--deferred-packages)) (when (funcall use-package-ensure-function
(remhash name use-package--deferred-packages) name (car spec) (cdr spec)
t)) (or context :unknown))
(remhash name use-package--deferred-packages)
t)
t)))
(defalias 'use-package-normalize/:defer-install 'use-package-normalize-test) (defalias 'use-package-normalize/:defer-install 'use-package-normalize-test)
@ -628,7 +666,7 @@ non-nil. Return t if the package is installed, nil otherwise."
;; installation was actually set up or not, so we need to set one ;; installation was actually set up or not, so we need to set one
;; marker value in `:defer-install', and then change it to a ;; marker value in `:defer-install', and then change it to a
;; different value in `:ensure', if the first one is present. (The ;; different value in `:ensure', if the first one is present. (The
;; first marker is `:ensure', and the second is `:defer'.) ;; first marker is `:defer-install', and the second is `:ensure'.)
(plist-put state :defer-install (when defer :defer-install)))) (plist-put state :defer-install (when defer :defer-install))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -647,21 +685,28 @@ non-nil. Return t if the package is installed, nil otherwise."
(concat ":ensure wants an optional package name " (concat ":ensure wants an optional package name "
"(an unquoted symbol name)"))))))) "(an unquoted symbol name)")))))))
(defun use-package-ensure-elpa (name ensure state &optional no-refresh) (defun use-package-ensure-elpa (name ensure state context &optional no-refresh)
(let ((package (or (and (eq ensure t) (use-package-as-symbol name)) (let ((package (or (when (eq ensure t) (use-package-as-symbol name))
ensure))) ensure)))
(when package (when package
(require 'package) (require 'package)
(if (package-installed-p package) (or (package-installed-p package)
t (not (or
(if (and (not no-refresh) ;; Contexts in which the confirmation prompt is
(assoc package (bound-and-true-p package-pinned-packages))) ;; bypassed.
(package-read-all-archive-contents)) (member context '(:byte-compile :ensure :config))
(if (or (assoc package package-archive-contents) no-refresh) (y-or-n-p (format "Install package %S?" name))))
(package-install package)
(progn (progn
(package-refresh-contents) (when (assoc package (bound-and-true-p package-pinned-packages))
(use-package-ensure-elpa name ensure state t))))))) (package-read-all-archive-contents))
(if (assoc package package-archive-contents)
(progn (package-install package) t)
(progn
(package-refresh-contents)
(when (assoc package (bound-and-true-p
package-pinned-packages))
(package-read-all-archive-contents))
(package-install package))))))))
(defun use-package-handler/:ensure (name keyword ensure rest state) (defun use-package-handler/:ensure (name keyword ensure rest state)
(let* ((body (use-package-process-keywords name rest (let* ((body (use-package-process-keywords name rest
@ -672,26 +717,28 @@ non-nil. Return t if the package is installed, nil otherwise."
(if (eq (plist-get state :defer-install) (if (eq (plist-get state :defer-install)
:defer-install) :defer-install)
(plist-put state :defer-install :ensure) (plist-put state :defer-install :ensure)
state))) state))))
(pre-ensure-form `(,use-package-pre-ensure-function
',name ',ensure ',state))
(ensure-form `(,use-package-ensure-function
',name ',ensure ',state))
(defer-install (plist-get state :defer-install)))
;; We want to avoid installing packages when the `use-package' ;; We want to avoid installing packages when the `use-package'
;; macro is being macro-expanded by elisp completion (see ;; macro is being macro-expanded by elisp completion (see
;; `lisp--local-variables'), but still do install packages when ;; `lisp--local-variables'), but still do install packages when
;; byte-compiling to avoid requiring `package' at runtime. ;; byte-compiling to avoid requiring `package' at runtime.
(cond (cond
(defer-install ((plist-get state :defer-install)
(push (push
`(puthash ',name ',ensure-form `(puthash ',name '(,ensure . ,state)
use-package--deferred-packages) use-package--deferred-packages)
body) body)
(push pre-ensure-form body)) (push `(,use-package-pre-ensure-function
',name ',ensure ',state)
body))
((bound-and-true-p byte-compile-current-file) ((bound-and-true-p byte-compile-current-file)
(eval ensure-form)) ; Eval when byte-compiling, ;; Eval when byte-compiling,
(t (push ensure-form body))) ; or else wait until runtime. (funcall use-package-ensure-function
name ensure state :byte-compile))
;; or else wait until runtime.
(t (push `(,use-package-ensure-function
',name ',ensure ',state :ensure)
body)))
body)) body))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -1079,7 +1126,8 @@ deferred until the prefix key sequence is pressed."
(use-package-error (use-package-error
(format "Autoloading failed to define function %S" (format "Autoloading failed to define function %S"
command)) command))
(when (use-package-install-deferred-package ',package-name) (when (use-package-install-deferred-package
',package-name :autoload)
(require ',package-name) (require ',package-name)
(let ((use-package--recursive-autoload t)) (let ((use-package--recursive-autoload t))
(if (called-interactively-p 'any) (if (called-interactively-p 'any)
@ -1136,14 +1184,19 @@ deferred until the prefix key sequence is pressed."
(defalias 'use-package-normalize/:after 'use-package-normalize-symlist) (defalias 'use-package-normalize/:after 'use-package-normalize-symlist)
(defun use-package-require-after-load (features name) (defun use-package-require-after-load
(features name &optional deferred-install)
"Return form for after any of FEATURES require NAME." "Return form for after any of FEATURES require NAME."
`(progn `(progn
,@(mapcar ,@(mapcar
(lambda (feat) (lambda (feat)
`(eval-after-load `(eval-after-load
(quote ,feat) (quote ,feat)
(quote (require (quote ,name) nil t)))) ,(macroexp-progn
`(,@(when deferred-install
`((use-package-install-deferred-package
',name :after)))
'(require ',name nil t)))))
features))) features)))
(defun use-package-handler/:after (name keyword arg rest state) (defun use-package-handler/:after (name keyword arg rest state)
@ -1152,7 +1205,11 @@ deferred until the prefix key sequence is pressed."
(name-string (use-package-as-string name))) (name-string (use-package-as-string name)))
(use-package-concat (use-package-concat
(when arg (when arg
(list (use-package-require-after-load arg name))) (list (use-package-require-after-load
;; Here we are checking the marker value for deferred
;; installation set in `use-package-handler/:ensure'.
;; See also `use-package-handler/:defer-install'.
arg name (eq (plist-get state :defer-install) :ensure))))
body))) body)))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -1214,7 +1271,7 @@ deferred until the prefix key sequence is pressed."
;; installation set in `use-package-handler/:ensure'. See also ;; installation set in `use-package-handler/:ensure'. See also
;; `use-package-handler/:defer-install'. ;; `use-package-handler/:defer-install'.
(when (eq (plist-get state :defer-install) :ensure) (when (eq (plist-get state :defer-install) :ensure)
(use-package-install-deferred-package name 'no-prompt)) (use-package-install-deferred-package name 'no-prompt :config))
(use-package--with-elapsed-timer (use-package--with-elapsed-timer
(format "Loading package %s" name) (format "Loading package %s" name)
(if use-package-expand-minimally (if use-package-expand-minimally