Merge from emacs-24; up to 2012-04-21T14:12:27Z!sdl.web@gmail.com
[bpt/emacs.git] / lisp / emacs-lisp / package.el
index 0bd37ce..66370c6 100644 (file)
@@ -1,10 +1,10 @@
 ;;; package.el --- Simple package system for Emacs
 
-;; Copyright (C) 2007-2011 Free Software Foundation, Inc.
+;; Copyright (C) 2007-2012 Free Software Foundation, Inc.
 
 ;; Author: Tom Tromey <tromey@redhat.com>
 ;; Created: 10 Mar 2007
-;; Version: 0.9
+;; Version: 1.0
 ;; Keywords: tools
 
 ;; This file is part of GNU Emacs.
 
 ;;; ToDo:
 
+;; - a trust mechanism, since compiling a package can run arbitrary code.
+;;   For example, download package signatures and check that they match.
 ;; - putting info dirs at the start of the info path means
 ;;   users see a weird ordering of categories.  OTOH we want to
 ;;   override later entries.  maybe emacs needs to enforce
@@ -224,7 +226,10 @@ Each element has the form (ID . LOCATION).
  LOCATION specifies the base location for the archive.
   If it starts with \"http:\", it is treated as a HTTP URL;
   otherwise it should be an absolute directory name.
-  (Other types of URL are currently not supported.)"
+  (Other types of URL are currently not supported.)
+
+Only add locations that you trust, since fetching and installing
+a package can run arbitrary code."
   :type '(alist :key-type (string :tag "Archive name")
                 :value-type (string :tag "URL or directory name"))
   :risky t
@@ -277,7 +282,7 @@ contrast, `package-user-dir' contains packages for personal use."
   :version "24.1")
 
 ;; The value is precomputed in finder-inf.el, but don't load that
-;; until it's needed (i.e. when `package-intialize' is called).
+;; until it's needed (i.e. when `package-initialize' is called).
 (defvar package--builtins nil
   "Alist of built-in packages.
 The actual value is initialized by loading the library
@@ -377,30 +382,37 @@ controls which package subdirectories may be loaded.
 In each valid package subdirectory, this function loads the
 description file containing a call to `define-package', which
 updates `package-alist' and `package-obsolete-alist'."
-  (let ((all (memq 'all package-load-list))
-       (regexp (concat "\\`" package-subdirectory-regexp "\\'"))
-       name version force)
+  (let ((regexp (concat "\\`" package-subdirectory-regexp "\\'")))
     (dolist (dir (cons package-user-dir package-directory-list))
       (when (file-directory-p dir)
        (dolist (subdir (directory-files dir))
-         (when (and (file-directory-p (expand-file-name subdir dir))
-                    (string-match regexp subdir))
-           (setq name    (intern (match-string 1 subdir))
-                 version (match-string 2 subdir)
-                 force   (assq name package-load-list))
-           (when (cond
-                  ((null force)
-                   all) ; not in package-load-list
-                  ((null (setq force (cadr force)))
-                   nil) ; disabled
-                  ((eq force t)
-                   t)
-                  ((stringp force) ; held
-                   (version-list-= (version-to-list version)
-                                   (version-to-list force)))
-                  (t
-                   (error "Invalid element in `package-load-list'")))
-             (package-load-descriptor dir subdir))))))))
+         (when (string-match regexp subdir)
+           (package-maybe-load-descriptor (match-string 1 subdir)
+                                          (match-string 2 subdir)
+                                          dir)))))))
+
+(defun package-maybe-load-descriptor (name version dir)
+  "Maybe load a specific package from directory DIR.
+NAME and VERSION are the package's name and version strings.
+This function checks `package-load-list', before actually loading
+the package by calling `package-load-descriptor'."
+  (let ((force (assq (intern name) package-load-list))
+       (subdir (concat name "-" version)))
+    (and (file-directory-p (expand-file-name subdir dir))
+        ;; Check `package-load-list':
+        (cond ((null force)
+               (memq 'all package-load-list))
+              ((null (setq force (cadr force)))
+               nil) ; disabled
+              ((eq force t)
+               t)
+              ((stringp force) ; held
+               (version-list-= (version-to-list version)
+                               (version-to-list force)))
+              (t
+               (error "Invalid element in `package-load-list'")))
+        ;; Actually load the descriptor:
+        (package-load-descriptor dir subdir))))
 
 (defsubst package-desc-vers (desc)
   "Extract version from a package description vector."
@@ -457,8 +469,11 @@ NAME and VERSION are both strings."
 Optional arg MIN-VERSION, if non-nil, should be a version list
 specifying the minimum acceptable version."
   (require 'finder-inf nil t) ; For `package--builtins'.
-  (let ((elt (assq package package--builtins)))
-    (and elt (version-list-<= min-version (package-desc-vers (cdr elt))))))
+  (if (eq package 'emacs)
+      (version-list-<= min-version (version-to-list emacs-version))
+    (let ((elt (assq package package--builtins)))
+      (and elt (version-list-<= min-version
+                               (package-desc-vers (cdr elt)))))))
 
 ;; This function goes ahead and activates a newer version of a package
 ;; if an older one was already activated.  This is not ideal; we'd at
@@ -512,7 +527,7 @@ Required package `%s-%s' is unavailable"
 
 (defun define-package (name-string version-string
                                &optional docstring requirements
-                               &rest extra-properties)
+                               &rest _extra-properties)
   "Define a new package.
 NAME-STRING is the name of the package, as a string.
 VERSION-STRING is the version of the package, as a string.
@@ -572,7 +587,7 @@ EXTRA-PROPERTIES is currently unused."
 (defun package-generate-autoloads (name pkg-dir)
   (require 'autoload)         ;Load before we let-bind generated-autoload-file!
   (let* ((auto-name (concat name "-autoloads.el"))
-        (ignore-name (concat name "-pkg.el"))
+        ;;(ignore-name (concat name "-pkg.el"))
         (generated-autoload-file (expand-file-name auto-name pkg-dir))
         (version-control 'never))
     (unless (fboundp 'autoload-ensure-default-file)
@@ -595,16 +610,25 @@ untar into a directory named DIR; otherwise, signal an error."
        (error "Package does not untar cleanly into directory %s/" dir))))
   (tar-untar-buffer))
 
-(defun package-unpack (name version)
-  (let* ((dirname (concat (symbol-name name) "-" version))
+(defun package-unpack (package version)
+  (let* ((name (symbol-name package))
+        (dirname (concat name "-" version))
         (pkg-dir (expand-file-name dirname package-user-dir)))
     (make-directory package-user-dir t)
     ;; FIXME: should we delete PKG-DIR if it exists?
     (let* ((default-directory (file-name-as-directory package-user-dir)))
       (package-untar-buffer dirname)
-      (package-generate-autoloads (symbol-name name) pkg-dir)
-      (let ((load-path (cons pkg-dir load-path)))
-       (byte-recompile-directory pkg-dir 0 t)))))
+      (package--make-autoloads-and-compile name pkg-dir))))
+
+(defun package--make-autoloads-and-compile (name pkg-dir)
+  "Generate autoloads and do byte-compilation for package named NAME.
+PKG-DIR is the name of the package directory."
+  (package-generate-autoloads name pkg-dir)
+  (let ((load-path (cons pkg-dir load-path)))
+    ;; We must load the autoloads file before byte compiling, in
+    ;; case there are magic cookies to set up non-trivial paths.
+    (load (expand-file-name (concat name "-autoloads") pkg-dir) nil t)
+    (byte-recompile-directory pkg-dir 0 t)))
 
 (defun package--write-file-no-coding (file-name)
   (let ((buffer-file-coding-system 'no-conversion))
@@ -644,9 +668,7 @@ untar into a directory named DIR; otherwise, signal an error."
         nil
         pkg-file
         nil nil nil 'excl))
-      (package-generate-autoloads file-name pkg-dir)
-      (let ((load-path (cons pkg-dir load-path)))
-       (byte-recompile-directory pkg-dir 0 t)))))
+      (package--make-autoloads-and-compile file-name pkg-dir))))
 
 (defmacro package--with-work-buffer (location file &rest body)
   "Run BODY in a buffer containing the contents of FILE at LOCATION.
@@ -738,7 +760,8 @@ not included in this list."
              hold)
          (when (setq hold (assq next-pkg package-load-list))
            (setq hold (cadr hold))
-           (cond ((eq hold nil)
+           (cond ((eq hold t))
+                 ((eq hold nil)
                   (error "Required package '%s' is disabled"
                          (symbol-name next-pkg)))
                  ((null (stringp hold))
@@ -856,7 +879,13 @@ using `package-compute-transaction'."
                                 (package-desc-doc desc)
                                 (package-desc-reqs desc)))
        (t
-       (error "Unknown package kind: %s" (symbol-name kind)))))))
+       (error "Unknown package kind: %s" (symbol-name kind))))
+      ;; If package A depends on package B, then A may `require' B
+      ;; during byte compilation.  So we need to activate B before
+      ;; unpacking A.
+      (package-maybe-load-descriptor (symbol-name elt) v-string
+                                    package-user-dir)
+      (package-activate elt (version-to-list v-string)))))
 
 (defvar package--initialized nil)
 
@@ -871,6 +900,8 @@ archive in `package-archives'.  Interactively, prompt for NAME."
      ;; symbols for completion.
      (unless package--initialized
        (package-initialize t))
+     (unless package-archive-contents
+       (package-refresh-contents))
      (list (intern (completing-read
                    "Install package: "
                    (mapcar (lambda (elt)
@@ -884,9 +915,7 @@ archive in `package-archives'.  Interactively, prompt for NAME."
             (symbol-name name)))
     (package-download-transaction
      (package-compute-transaction (list name)
-                                 (package-desc-reqs (cdr pkg-desc)))))
-  ;; Try to activate it.
-  (package-initialize))
+                                 (package-desc-reqs (cdr pkg-desc))))))
 
 (defun package-strip-rcs-id (str)
   "Strip RCS version ID from the version string STR.
@@ -917,7 +946,7 @@ If the buffer does not contain a conforming package, signal an
 error.  If there is a package, narrow the buffer to the file's
 boundaries."
   (goto-char (point-min))
-  (unless (re-search-forward "^;;; \\([^ ]*\\)\\.el --- \\(.*\\)$" nil t)
+  (unless (re-search-forward "^;;; \\([^ ]*\\)\\.el ---[ \t]*\\(.*?\\)[ \t]*\\(-\\*-.*-\\*-[ \t]*\\)?$" nil t)
     (error "Packages lacks a file header"))
   (let ((file-name (match-string-no-properties 1))
        (desc      (match-string-no-properties 2))
@@ -1085,7 +1114,7 @@ makes them available for download."
   (unless (file-exists-p package-user-dir)
     (make-directory package-user-dir t))
   (dolist (archive package-archives)
-    (condition-case-no-debug nil
+    (condition-case-unless-debug nil
        (package--download-one-archive archive "archive-contents")
       (error (message "Failed to download `%s' archive."
                      (car archive)))))
@@ -1363,7 +1392,7 @@ If REMEMBER-POS is non-nil, keep point on the same entry.
 PACKAGES should be t, which means to display all known packages,
 or a list of package names (symbols) to display."
   ;; Construct list of ((PACKAGE . VERSION) STATUS DESCRIPTION).
-  (let (info-list name builtin)
+  (let (info-list name)
     ;; Installed packages:
     (dolist (elt package-alist)
       (setq name (car elt))
@@ -1448,21 +1477,21 @@ If optional arg BUTTON is non-nil, describe its associated package."
        (describe-package package))))
 
 ;; fixme numeric argument
-(defun package-menu-mark-delete (&optional num)
+(defun package-menu-mark-delete (&optional _num)
   "Mark a package for deletion and move to the next line."
   (interactive "p")
   (if (member (package-menu-get-status) '("installed" "obsolete"))
       (tabulated-list-put-tag "D" t)
     (forward-line)))
 
-(defun package-menu-mark-install (&optional num)
+(defun package-menu-mark-install (&optional _num)
   "Mark a package for installation and move to the next line."
   (interactive "p")
   (if (string-equal (package-menu-get-status) "available")
       (tabulated-list-put-tag "I" t)
     (forward-line)))
 
-(defun package-menu-mark-unmark (&optional num)
+(defun package-menu-mark-unmark (&optional _num)
   "Clear any marks on a package and move to the next line."
   (interactive "p")
   (tabulated-list-put-tag " " t))
@@ -1504,8 +1533,7 @@ If optional arg BUTTON is non-nil, describe its associated package."
     (dolist (entry tabulated-list-entries)
       ;; ENTRY is ((NAME . VERSION) [NAME VERSION STATUS DOC])
       (let ((pkg (car entry))
-           (status (aref (cadr entry) 2))
-           old)
+           (status (aref (cadr entry) 2)))
        (cond ((equal status "installed")
               (push pkg installed))
              ((equal status "available")
@@ -1590,7 +1618,7 @@ packages marked for deletion are removed."
                                delete-list
                                ", "))))
          (dolist (elt delete-list)
-           (condition-case-no-debug err
+           (condition-case-unless-debug err
                (package-delete (car elt) (cdr elt))
              (error (message (cadr err)))))
        (error "Aborted")))