]> code.delx.au - gnu-emacs/blobdiff - lisp/emacs-lisp/package.el
* lisp/emacs-lisp/package.el (package-install-selected-packages):
[gnu-emacs] / lisp / emacs-lisp / package.el
index f94e7aaa741a91caa3b93458e8547bd3cfb7a94e..29de2c9b07f0615d41dc37e8454ee895122bab30 100644 (file)
@@ -1,6 +1,6 @@
 ;;; package.el --- Simple package system for Emacs  -*- lexical-binding:t -*-
 
-;; Copyright (C) 2007-2015 Free Software Foundation, Inc.
+;; Copyright (C) 2007-2016 Free Software Foundation, Inc.
 
 ;; Author: Tom Tromey <tromey@redhat.com>
 ;;         Daniel Hackney <dan@haxney.org>
@@ -182,7 +182,13 @@ If VERSION is a string, only that version is ever loaded.
  Any other version, even if newer, is silently ignored.
  Hence, the package is \"held\" at that version.
 If VERSION is nil, the package is not loaded (it is \"disabled\")."
-  :type '(repeat symbol)
+  :type '(repeat (choice (const all)
+                         (list :tag "Specific package"
+                               (symbol :tag "Package name")
+                               (choice :tag "Version"
+                                (const :tag "disable" nil)
+                                (const :tag "most recent" t)
+                                (string :tag "specific version")))))
   :risky t
   :version "24.1")
 
@@ -216,7 +222,7 @@ of it available such that:
 
 This variable has three possible values:
     nil: no packages are hidden;
-    `archive': only criteria (a) is used;
+    `archive': only criterion (a) is used;
     t: both criteria are used.
 
 This variable has no effect if `package-menu--hide-packages' is
@@ -234,7 +240,7 @@ Each element has the form (ARCHIVE-ID . PRIORITY).
 
 When installing packages, the package with the highest version
 number from the archive with the highest priority is
-selected. When higher versions are available from archives with
+selected.  When higher versions are available from archives with
 lower priorities, the user has to select those manually.
 
 Archives not in this list have the priority 0.
@@ -328,6 +334,7 @@ by running `package-install-selected-packages'.
 To check if a package is contained in this list here, use
 `package--user-selected-p', as it may populate the variable with
 a sane initial value."
+  :version "25.1"
   :type '(repeat symbol))
 
 (defcustom package-menu-async t
@@ -829,13 +836,21 @@ untar into a directory named DIR; otherwise, signal an error."
     (package--make-autoloads-and-stuff pkg-desc pkg-dir)
     ;; Update package-alist.
     (let ((new-desc (package-load-descriptor pkg-dir)))
-      ;; FIXME: Check that `new-desc' matches `desc'!
+      (unless (equal (package-desc-full-name new-desc)
+                     (package-desc-full-name pkg-desc))
+        (error "The retrieved package (`%s') doesn't match what the archive offered (`%s')"
+               (package-desc-full-name new-desc) (package-desc-full-name pkg-desc)))
+      ;; Activation has to be done before compilation, so that if we're
+      ;; upgrading and macros have changed we load the new definitions
+      ;; before compiling.
+      (package-activate-1 new-desc :reload :deps)
       ;; FIXME: Compilation should be done as a separate, optional, step.
       ;; E.g. for multi-package installs, we should first install all packages
       ;; and then compile them.
-      (package--compile new-desc))
-    ;; Try to activate it.
-    (package-activate name 'force)
+      (package--compile new-desc)
+      ;; After compilation, load again any files loaded by
+      ;; `activate-1', so that we use the byte-compiled definitions.
+      (package--load-files-for-activation new-desc :reload))
     pkg-dir))
 
 (defun package-generate-description-file (pkg-desc pkg-file)
@@ -918,11 +933,12 @@ untar into a directory named DIR; otherwise, signal an error."
 ;;;; Compilation
 (defvar warning-minimum-level)
 (defun package--compile (pkg-desc)
-  "Byte-compile installed package PKG-DESC."
+  "Byte-compile installed package PKG-DESC.
+This assumes that `pkg-desc' has already been activated with
+`package-activate-1'."
   (let ((warning-minimum-level :error)
         (save-silently inhibit-message)
         (load-path load-path))
-    (package--activate-autoloads-and-load-path pkg-desc)
     (byte-recompile-directory (package-desc-dir pkg-desc) 0 t)))
 
 ;;;; Inferring package from current buffer
@@ -1128,48 +1144,50 @@ Point is after the headers when BODY runs.
 FILE, if provided, is added to URL.
 URL can be a local file name, which must be absolute.
 ASYNC, if non-nil, runs the request asynchronously.
-ERROR-FORM is run only if an error occurs.  If NOERROR is
-non-nil, don't propagate errors caused by the connection or by
-BODY (does not apply to errors signaled by ERROR-FORM).
+ERROR-FORM is run only if a connection error occurs.  If NOERROR
+is non-nil, don't propagate connection errors (does not apply to
+errors signaled by ERROR-FORM or by BODY).
 
 \(fn URL &key ASYNC FILE ERROR-FORM NOERROR &rest BODY)"
   (declare (indent defun) (debug t))
   (while (keywordp (car body))
     (setq body (cdr (cdr body))))
-  (macroexp-let2* nil ((url-1 url))
-    `(cl-macrolet ((wrap-errors (&rest bodyforms)
-                                (let ((err (make-symbol "err")))
-                                  `(condition-case ,err
-                                       ,(macroexp-progn bodyforms)
-                                     ,(list 'error ',error-form
-                                            (list 'unless ',noerror
-                                                  `(signal (car ,err) (cdr ,err))))))))
+  (macroexp-let2* nil ((url-1 url)
+                       (noerror-1 noerror))
+    `(cl-macrolet ((unless-error (body-2 &rest before-body)
+                                 (let ((err (make-symbol "err")))
+                                   `(with-temp-buffer
+                                      (when (condition-case ,err
+                                                (progn ,@before-body t)
+                                              ,(list 'error ',error-form
+                                                     (list 'unless ',noerror-1
+                                                           `(signal (car ,err) (cdr ,err)))))
+                                        ,@body-2)))))
        (if (string-match-p "\\`https?:" ,url-1)
            (let* ((url (concat ,url-1 ,file))
                   (callback (lambda (status)
                               (let ((b (current-buffer)))
                                 (require 'url-handlers)
-                                (unwind-protect (wrap-errors
-                                                 (when-let ((er (plist-get status :error)))
-                                                   (error "Error retrieving: %s %S" url er))
-                                                 (goto-char (point-min))
-                                                 (unless (search-forward-regexp "^\r?\n\r?" nil 'noerror)
-                                                   (error "Error retrieving: %s %S" url "incomprehensible buffer"))
-                                                 (with-temp-buffer
-                                                   (url-insert-buffer-contents b url)
-                                                   (kill-buffer b)
-                                                   (goto-char (point-min))
-                                                   ,@body)))))))
+                                (unless-error ,body
+                                              (when-let ((er (plist-get status :error)))
+                                                (error "Error retrieving: %s %S" url er))
+                                              (with-current-buffer b
+                                                (goto-char (point-min))
+                                                (unless (search-forward-regexp "^\r?\n\r?" nil 'noerror)
+                                                  (error "Error retrieving: %s %S" url "incomprehensible buffer")))
+                                              (url-insert-buffer-contents b url)
+                                              (kill-buffer b)
+                                              (goto-char (point-min)))))))
              (if ,async
-                 (wrap-errors (url-retrieve url callback nil 'silent))
-               (with-current-buffer (wrap-errors (url-retrieve-synchronously url 'silent))
-                 (funcall callback nil))))
-         (wrap-errors (with-temp-buffer
-                        (let ((url (expand-file-name ,file ,url-1)))
-                          (unless (file-name-absolute-p url)
-                            (error "Location %s is not a url nor an absolute file name" url))
-                          (insert-file-contents url))
-                        ,@body))))))
+                 (unless-error nil (url-retrieve url callback nil 'silent))
+               (unless-error ,body (url-insert-file-contents url))))
+         (unless-error ,body
+                       (let ((url (expand-file-name ,file ,url-1)))
+                         (unless (file-name-absolute-p url)
+                           (error "Location %s is not a url nor an absolute file name" url))
+                         (insert-file-contents url)))))))
+
+(define-error 'bad-signature "Failed to verify signature")
 
 (defun package--check-signature-content (content string &optional sig-file)
   "Check signature CONTENT against STRING.
@@ -1181,7 +1199,7 @@ errors."
     (condition-case error
         (epg-verify-string context content string)
       (error (package--display-verify-error context sig-file)
-        (signal (car error) (cdr error))))
+             (signal 'bad-signature error)))
     (let (good-signatures had-fatal-error)
       ;; The .sig file may contain multiple signatures.  Success if one
       ;; of the signatures is good.
@@ -1197,10 +1215,10 @@ errors."
             (setq had-fatal-error t))))
       (when (and (null good-signatures) had-fatal-error)
         (package--display-verify-error context sig-file)
-        (error "Failed to verify signature %s" sig-file))
+        (signal 'bad-signature (list sig-file)))
       good-signatures)))
 
-(defun package--check-signature (location file &optional string async callback)
+(defun package--check-signature (location file &optional string async callback unwind)
   "Check signature of the current buffer.
 Download the signature file from LOCATION by appending \".sig\"
 to FILE.
@@ -1209,18 +1227,35 @@ STRING is the string to verify, it defaults to `buffer-string'.
 If ASYNC is non-nil, the download of the signature file is
 done asynchronously.
 
-If the signature is verified and CALLBACK was provided, CALLBACK
-is `funcall'ed with the list of good signatures as argument (the
-list can be empty).  If the signatures file is not found,
-CALLBACK is called with no arguments."
+If the signature does not verify, signal an error.
+If the signature is verified and CALLBACK was provided, `funcall'
+CALLBACK with the list of good signatures as argument (the list
+can be empty).
+If no signatures file is found, and `package-check-signature' is
+`allow-unsigned', call CALLBACK with a nil argument.
+Otherwise, an error is signaled.
+
+UNWIND, if provided, is a function to be called after everything
+else, even if an error is signaled."
   (let ((sig-file (concat file ".sig"))
         (string (or string (buffer-string))))
     (package--with-response-buffer location :file sig-file
       :async async :noerror t
-      :error-form (when callback (funcall callback nil))
-      (let ((sig (package--check-signature-content (buffer-substring (point) (point-max)) string sig-file)))
-        (when callback (funcall callback sig))
-        sig))))
+      ;; Connection error is assumed to mean "no sig-file".
+      :error-form (let ((allow-unsigned (eq package-check-signature 'allow-unsigned)))
+                    (when (and callback allow-unsigned)
+                      (funcall callback nil))
+                    (when unwind (funcall unwind))
+                    (unless allow-unsigned
+                      (error "Unsigned file `%s' at %s" file location)))
+      ;; OTOH, an error here means "bad signature", which we never
+      ;; suppress.  (Bug#22089)
+      (unwind-protect
+          (let ((sig (package--check-signature-content (buffer-substring (point) (point-max))
+                                                       string sig-file)))
+            (when callback (funcall callback sig))
+            sig)
+        (when unwind (funcall unwind))))))
 \f
 ;;; Packages on Archives
 ;; The following variables store information about packages available
@@ -1417,9 +1452,8 @@ loading packages twice."
 (defvar package--downloads-in-progress nil
   "List of in-progress asynchronous downloads.")
 
-(declare-function epg-check-configuration "epg-config"
-                  (config &optional minimum-version))
-(declare-function epg-configuration "epg-config" ())
+(declare-function epg-find-configuration "epg-config"
+                  (protocol &optional force))
 (declare-function epg-import-keys-from-file "epg" (context keys))
 
 ;;;###autoload
@@ -1483,19 +1517,12 @@ similar to an entry in `package-alist'.  Save the cached copy to
            location file content async
            ;; This function will be called after signature checking.
            (lambda (&optional good-sigs)
-             (unless (or good-sigs (eq package-check-signature 'allow-unsigned))
-               ;; Even if the sig fails, this download is done, so
-               ;; remove it from the in-progress list.
-               (package--update-downloads-in-progress archive)
-               (error "Unsigned archive `%s'" name))
-             ;; Either everything worked or we don't mind not signing.
-             ;; Write out the archives file.
              (write-region content nil local-file nil 'silent)
              ;; Write out good signatures into archive-contents.signed file.
              (when good-sigs
                (write-region (mapconcat #'epg-signature-to-string good-sigs "\n")
-                             nil (concat local-file ".signed") nil 'silent))
-             (package--update-downloads-in-progress archive))))))))
+                             nil (concat local-file ".signed") nil 'silent)))
+           (lambda () (package--update-downloads-in-progress archive))))))))
 
 (defun package--download-and-read-archives (&optional async)
   "Download descriptions of all `package-archives' and read them.
@@ -1526,11 +1553,15 @@ downloads in the background."
   (let ((default-keyring (expand-file-name "package-keyring.gpg"
                                            data-directory))
         (inhibit-message async))
+    (if (get 'package-check-signature 'saved-value)
+        (when package-check-signature
+          (epg-find-configuration 'OpenPGP))
+      (setq package-check-signature
+            (if (epg-find-configuration 'OpenPGP)
+                'allow-unsigned)))
     (when (and package-check-signature (file-exists-p default-keyring))
       (condition-case-unless-debug error
-          (progn
-            (epg-check-configuration (epg-configuration))
-            (package-import-keyring default-keyring))
+          (package-import-keyring default-keyring)
         (error (message "Cannot import default keyring: %S" (cdr error))))))
   (package--download-and-read-archives async))
 
@@ -1777,11 +1808,6 @@ if all the in-between dependencies are also in PACKAGE-LIST."
            location file content nil
            ;; This function will be called after signature checking.
            (lambda (&optional good-sigs)
-             (unless (or good-sigs (eq package-check-signature 'allow-unsigned))
-               ;; Even if the sig fails, this download is done, so
-               ;; remove it from the in-progress list.
-               (error "Unsigned package: `%s'"
-                 (package-desc-name pkg-desc)))
              ;; Signature checked, unpack now.
              (with-temp-buffer (insert content)
                                (let ((save-silently t))
@@ -1997,17 +2023,21 @@ If some packages are not installed propose to install them."
   ;; gets installed).
   (if (not package-selected-packages)
       (message "`package-selected-packages' is empty, nothing to install")
-    (cl-loop for p in package-selected-packages
-             unless (package-installed-p p)
-             collect p into lst
-             finally
-             (if lst
-                 (when (y-or-n-p
-                        (format "%s packages will be installed:\n%s, proceed?"
-                          (length lst)
-                          (mapconcat #'symbol-name lst ", ")))
-                   (mapc #'package-install lst))
-               (message "All your packages are already installed")))))
+    (let* ((not-installed (seq-remove #'package-installed-p package-selected-packages))
+           (available (seq-filter (lambda (p) (assq p package-archive-contents)) not-installed))
+           (difference (- (length not-installed) (length available))))
+      (cond
+       (available
+        (when (y-or-n-p
+               (format "%s packages will be installed:\n%s, proceed?"
+                       (length available)
+                       (mapconcat #'symbol-name available ", ")))
+          (mapc (lambda (p) (package-install p 'dont-select)) available)))
+       ((> difference 0)
+        (message "%s packages are not available (the rest already installed), maybe you need to `M-x package-refresh-contents'"
+                 difference))
+       (t
+        (message "All your packages are already installed"))))))
 
 \f
 ;;; Package Deletion
@@ -2637,6 +2667,7 @@ omitted from the package menu.  To toggle this, type \\[package-menu-toggle-hidi
 
 Values can be interactively added to this list by typing
 \\[package-menu-hide-package] on a package"
+  :version "25.1"
   :type '(repeat (regexp :tag "Hide packages with name matching")))
 
 (defun package-menu--refresh (&optional packages keywords)