Mercurial > emacs
changeset 109567:0fc9f7a0d319
Add support for non-default package repositories.
* lisp/emacs-lisp/package.el (package-archive-base): Var deleted.
(package-archives): New variable.
(package-archive-contents): Doc fix.
(package-load-descriptor): Do nothing if descriptor file is
missing.
(package--write-file-no-coding): New function.
(package-unpack-single): Use it.
(package-archive-id): New function.
(package-download-single, package-download-tar)
(package-menu-view-commentary): Use it.
(package-installed-p): Make second argument optional.
(package-read-all-archive-contents): New function.
(package-initialize): Use it.
(package-read-archive-contents): Add ARCHIVE argument.
(package--add-to-archive-contents): New function.
(package-install): Don't call package-read-archive-contents.
(package--download-one-archive): Store archive file in a
subdirectory of package-user-dir.
(package-menu-execute): Remove spurious line movement.
* lisp/emacs-lisp/package.el (package-load-list, package-archives)
(package-archive-contents, package-user-dir)
(package-directory-list, package--builtins, package-alist)
(package-activated-list, package-obsolete-alist): Mark as risky.
author | Chong Yidong <cyd@stupidchicken.com> |
---|---|
date | Wed, 28 Jul 2010 14:54:42 -0400 |
parents | d03bc4a3fced |
children | 7af1a36b6b28 3c9de3b961fe |
files | lisp/ChangeLog lisp/emacs-lisp/package.el |
diffstat | 2 files changed, 146 insertions(+), 74 deletions(-) [+] |
line wrap: on
line diff
--- a/lisp/ChangeLog Wed Jul 28 19:34:51 2010 +0200 +++ b/lisp/ChangeLog Wed Jul 28 14:54:42 2010 -0400 @@ -1,3 +1,33 @@ +2010-07-28 Chong Yidong <cyd@stupidchicken.com> + + * emacs-lisp/package.el (package-load-list, package-archives) + (package-archive-contents, package-user-dir) + (package-directory-list, package--builtins, package-alist) + (package-activated-list, package-obsolete-alist): Mark as risky. + +2010-07-28 Phil Hagelberg <phil@evri.com> + + Add support for non-default package repositories. + * emacs-lisp/package.el (package-archive-base): Var deleted. + (package-archives): New variable. + (package-archive-contents): Doc fix. + (package-load-descriptor): Do nothing if descriptor file is + missing. + (package--write-file-no-coding): New function. + (package-unpack-single): Use it. + (package-archive-id): New function. + (package-download-single, package-download-tar) + (package-menu-view-commentary): Use it. + (package-installed-p): Make second argument optional. + (package-read-all-archive-contents): New function. + (package-initialize): Use it. + (package-read-archive-contents): Add ARCHIVE argument. + (package--add-to-archive-contents): New function. + (package-install): Don't call package-read-archive-contents. + (package--download-one-archive): Store archive file in a + subdirectory of package-user-dir. + (package-menu-execute): Remove spurious line movement. + 2010-07-28 Jan Djärv <jan.h.d@swipnet.se> * cus-start.el (tool-bar-style): Add text-image-horiz.
--- a/lisp/emacs-lisp/package.el Wed Jul 28 19:34:51 2010 +0200 +++ b/lisp/emacs-lisp/package.el Wed Jul 28 14:54:42 2010 -0400 @@ -43,9 +43,6 @@ ;; currently register any of these, so this feature does not actually ;; work.) -;; This code supports a single package repository, ELPA. All packages -;; must be registered there. - ;; A package is described by its name and version. The distribution ;; format is either a tar file or a single .el file. @@ -55,11 +52,13 @@ ;; which consists of a call to define-package. It may also contain a ;; "dir" file and the info files it references. -;; A .el file will be named "NAME-VERSION.el" in ELPA, but will be +;; A .el file is named "NAME-VERSION.el" in the remote archive, but is ;; installed as simply "NAME.el" in a directory named "NAME-VERSION". -;; The downloader will download all dependent packages. It will also -;; byte-compile the package's lisp at install time. +;; The downloader downloads all dependent packages. By default, +;; packages come from the official GNU sources, but others may be +;; added by customizing the `package-archives' alist. Packages get +;; byte-compiled at install time. ;; At activation time we will set up the load-path and the info path, ;; and we will load the package's autoloads. If a package's @@ -207,6 +206,7 @@ Hence, the package is \"held\" at that version. If VERSION is nil, the package is not loaded (it is \"disabled\")." :type '(repeat symbol) + :risky t :group 'package :version "24.1") @@ -217,10 +217,16 @@ (declare-function lm-commentary "lisp-mnt" (&optional file)) (declare-function dired-delete-file "dired" (file &optional recursive trash)) -(defconst package-archive-base "http://elpa.gnu.org/packages/" - "Base URL for the Emacs Lisp Package Archive (ELPA). -Ordinarily you should not need to change this. -Note that some code in package.el assumes that this is an http: URL.") +(defcustom package-archives '(("gnu" . "http://elpa.gnu.org/packages/")) + "An alist of archives from which to fetch. +The default value points to the GNU Emacs package repository. +Each element has the form (ID . URL), where ID is an identifier +string for an archive and URL is a http: URL (a string)." + :type '(alist :key-type (string :tag "Archive name") + :value-type (string :tag "Archive URL")) + :risky t + :group 'package + :version "24.1") (defconst package-archive-version 1 "Version number of the package archive understood by this file. @@ -234,8 +240,10 @@ "Cache of the contents of the Emacs Lisp Package Archive. This is an alist mapping package names (symbols) to package descriptor vectors. These are like the vectors for `package-alist' -but have an extra entry which is 'tar for tar packages and -'single for single-file packages.") +but have extra entries: one which is 'tar for tar packages and +'single for single-file packages, and one which is the name of +the archive from which it came.") +(put 'package-archive-contents 'risky-local-variable t) (defcustom package-user-dir (locate-user-emacs-file "elpa") "Directory containing the user's Emacs Lisp packages. @@ -243,6 +251,7 @@ Apart from this directory, Emacs also looks for system-wide packages in `package-directory-list'." :type 'directory + :risky t :group 'package :version "24.1") @@ -259,6 +268,7 @@ These directories contain packages intended for system-wide; in contrast, `package-user-dir' contains packages for personal use." :type '(repeat directory) + :risky t :group 'package :version "24.1") @@ -293,6 +303,7 @@ (bubbles . [(0 5) nil "Puzzle game for Emacs."]))))) "Alist of all built-in packages. Maps the package name to a vector [VERSION REQS DOCSTRING].") +(put 'package--builtins 'risky-local-variable t) (defvar package-alist package--builtins "Alist of all packages available for activation. @@ -301,15 +312,18 @@ The value is generated by `package-load-descriptor', usually called via `package-initialize'. For user customizations of which packages to load/activate, see `package-load-list'.") +(put 'package-archive-contents 'risky-local-variable t) (defvar package-activated-list (mapcar #'car package-alist) "List of the names of currently activated packages.") +(put 'package-activated-list 'risky-local-variable t) (defvar package-obsolete-alist nil "Representation of obsolete packages. Like `package-alist', but maps package name to a second alist. The inner alist is keyed by version.") +(put 'package-obsolete-alist 'risky-local-variable t) (defconst package-subdirectory-regexp "^\\([^.].*\\)-\\([0-9]+\\(?:[.][0-9]+\\)*\\)$" @@ -361,16 +375,14 @@ (match-string 1 dirname))) (defun package-load-descriptor (dir package) - "Load the description file for a package. -DIR is the directory in which to find the package subdirectory, -and PACKAGE is the name of the package subdirectory. -Return nil if the package could not be found." - (let ((pkg-dir (expand-file-name package dir))) - (if (file-directory-p pkg-dir) - (load (expand-file-name (concat (package-strip-version package) - "-pkg") - pkg-dir) - nil t)))) + "Load the description file in directory DIR for package PACKAGE." + (let* ((pkg-dir (expand-file-name package dir)) + (pkg-file (expand-file-name + (concat (package-strip-version package) "-pkg") + pkg-dir))) + (when (and (file-directory-p pkg-dir) + (file-exists-p (concat pkg-file ".el"))) + (load pkg-file nil t)))) (defun package-load-all-descriptors () "Load descriptors for installed Emacs Lisp packages. @@ -613,20 +625,23 @@ (let ((load-path (cons pkg-dir load-path))) (byte-recompile-directory pkg-dir 0 t))))) +(defun package--write-file-no-coding (file-name excl) + (let ((buffer-file-coding-system 'no-conversion)) + (write-region (point-min) (point-max) file-name nil nil nil excl))) + (defun package-unpack-single (file-name version desc requires) "Install the contents of the current buffer as a package." ;; Special case "package". (if (string= file-name "package") - (write-region (point-min) (point-max) - (expand-file-name (concat file-name ".el") - package-user-dir) - nil nil nil nil) + (package--write-file-no-coding + (expand-file-name (concat file-name ".el") package-user-dir) + nil) (let* ((pkg-dir (expand-file-name (concat file-name "-" version) package-user-dir)) (el-file (expand-file-name (concat file-name ".el") pkg-dir)) (pkg-file (expand-file-name (concat file-name "-pkg.el") pkg-dir))) (make-directory pkg-dir t) - (write-region (point-min) (point-max) el-file nil nil nil 'excl) + (package--write-file-no-coding el-file 'excl) (let ((print-level nil) (print-length nil)) (write-region @@ -670,7 +685,7 @@ (defun package-download-single (name version desc requires) "Download and install a single-file package." (let ((buffer (url-retrieve-synchronously - (concat package-archive-base + (concat (package-archive-id name) (symbol-name name) "-" version ".el")))) (with-current-buffer buffer (package-handle-response) @@ -683,7 +698,7 @@ (defun package-download-tar (name version) "Download and install a tar package." (let ((tar-buffer (url-retrieve-synchronously - (concat package-archive-base + (concat (package-archive-id name) (symbol-name name) "-" version ".tar")))) (with-current-buffer tar-buffer (package-handle-response) @@ -692,12 +707,12 @@ (package-unpack name version) (kill-buffer tar-buffer)))) -(defun package-installed-p (package version) +(defun package-installed-p (package &optional min-version) (let ((pkg-desc (assq package package-alist))) (and pkg-desc - (package-version-compare version + (package-version-compare min-version (package-desc-vers (cdr pkg-desc)) - '>=)))) + '<=)))) (defun package-compute-transaction (result requirements) (dolist (elt requirements) @@ -772,16 +787,13 @@ (car contents) package-archive-version)) (cdr contents)))))) -(defun package-read-archive-contents () +(defun package-read-all-archive-contents () "Re-read `archive-contents' and `builtin-packages', if they exist. Set `package-archive-contents' and `package--builtins' if successful. Throw an error if the archive version is too new." - (let ((archive-contents (package--read-archive-file "archive-contents")) - (builtins (package--read-archive-file "builtin-packages"))) - (if archive-contents - ;; Version 1 of 'archive-contents' is identical to our - ;; internal representation. - (setq package-archive-contents archive-contents)) + (dolist (archive package-archives) + (package-read-archive-contents (car archive))) + (let ((builtins (package--read-archive-file "builtin-packages"))) (if builtins ;; Version 1 of 'builtin-packages' is a list where the car is ;; a split emacs version and the cdr is an alist suitable for @@ -793,6 +805,33 @@ (if (package-version-compare our-version (car elt) '>=) (setq result (append (cdr elt) result))))))))) +(defun package-read-archive-contents (archive) + "Re-read `archive-contents' and `builtin-packages' for ARCHIVE. +If successful, set `package-archive-contents' and `package--builtins'. +If the archive version is too new, signal an error." + (let ((archive-contents (package--read-archive-file + (concat "archives/" archive + "/archive-contents")))) + (if archive-contents + ;; Version 1 of 'archive-contents' is identical to our + ;; internal representation. + ;; TODO: merge archive lists + (dolist (package archive-contents) + (package--add-to-archive-contents package archive))))) + +(defun package--add-to-archive-contents (package archive) + "Add the PACKAGE from the given ARCHIVE if necessary. +Also, add the originating archive to the end of the package vector." + (let* ((name (car package)) + (version (aref (cdr package) 0)) + (entry (cons (car package) + (vconcat (cdr package) (vector archive)))) + (existing-package (cdr (assq name package-archive-contents)))) + (when (or (not existing-package) + (package-version-compare version + (aref existing-package 0) '>)) + (add-to-list 'package-archive-contents entry)))) + (defun package-download-transaction (transaction) "Download and install all the packages in the given transaction." (dolist (elt transaction) @@ -817,26 +856,21 @@ (defun package-install (name) "Install the package named NAME. Interactively, prompt for the package name. -The package is found on the archive site, see `package-archive-base'." +The package is found on one of the archives in `package-archive-base'." (interactive - (list (progn - ;; Make sure we're using the most recent download of the - ;; archive. Maybe we should be updating the archive first? - (package-read-archive-contents) - (intern (completing-read "Install package: " - (mapcar (lambda (elt) - (cons (symbol-name (car elt)) - nil)) - package-archive-contents) - nil t))))) + (list (intern (completing-read "Install package: " + (mapcar (lambda (elt) + (cons (symbol-name (car elt)) + nil)) + package-archive-contents) + nil t)))) (let ((pkg-desc (assq name package-archive-contents))) (unless pkg-desc - (error "Package '%s' not available for installation" + (error "Package '%s' is not available for installation" (symbol-name name))) - (let ((transaction - (package-compute-transaction (list name) - (package-desc-reqs (cdr pkg-desc))))) - (package-download-transaction transaction))) + (package-download-transaction + (package-compute-transaction (list name) + (package-desc-reqs (cdr pkg-desc))))) ;; Try to activate it. (package-initialize)) @@ -996,20 +1030,28 @@ ;; FIXME: query user? 'always)) -(defun package--download-one-archive (file) - "Download a single archive file and cache it locally." - (let ((buffer (url-retrieve-synchronously - (concat package-archive-base file)))) +(defun package-archive-id (name) + "Return the archive containing the package NAME." + (let ((desc (cdr (assq (intern-soft name) package-archive-contents)))) + (cdr (assoc (aref desc (- (length desc) 1)) package-archives)))) + +(defun package--download-one-archive (archive file) + "Download an archive file FILE from ARCHIVE, and cache it locally." + (let* ((archive-name (car archive)) + (archive-url (cdr archive)) + (dir (expand-file-name "archives" package-user-dir)) + (dir (expand-file-name archive-name dir)) + (buffer (url-retrieve-synchronously (concat archive-url file)))) (with-current-buffer buffer (package-handle-response) (re-search-forward "^$" nil 'move) (forward-char) (delete-region (point-min) (point)) - (setq buffer-file-name (concat (file-name-as-directory package-user-dir) - file)) + (make-directory dir t) + (setq buffer-file-name (expand-file-name file dir)) (let ((version-control 'never)) - (save-buffer)) - (kill-buffer buffer)))) + (save-buffer))) + (kill-buffer buffer))) (defun package-refresh-contents () "Download the ELPA archive description if needed. @@ -1019,9 +1061,9 @@ (interactive) (unless (file-exists-p package-user-dir) (make-directory package-user-dir t)) - (package--download-one-archive "archive-contents") - (package--download-one-archive "builtin-packages") - (package-read-archive-contents)) + (dolist (archive package-archives) + (package--download-one-archive archive "archive-contents")) + (package-read-all-archive-contents)) ;;;###autoload (defun package-initialize () @@ -1030,7 +1072,7 @@ (interactive) (setq package-obsolete-alist nil) (package-load-all-descriptors) - (package-read-archive-contents) + (package-read-all-archive-contents) ;; Try to activate all our packages. (mapc (lambda (elt) (package-activate (car elt) (package-desc-vers (cdr elt)))) @@ -1306,11 +1348,12 @@ For single-file packages, shows the commentary section from the header. For larger packages, shows the README file." (interactive) - (let* (start-point ok - (pkg-name (package-menu-get-package)) - (buffer (url-retrieve-synchronously (concat package-archive-base - pkg-name - "-readme.txt")))) + (let* ((pkg-name (package-menu-get-package)) + (buffer (url-retrieve-synchronously + (concat (package-archive-id pkg-name) + pkg-name + "-readme.txt"))) + start-point ok) (with-current-buffer buffer ;; FIXME: it would be nice to work with any URL type. (setq start-point url-http-end-of-headers) @@ -1322,7 +1365,7 @@ (insert "Package information for " pkg-name "\n\n") (if ok (insert-buffer-substring buffer start-point) - (insert "This package does not have a README file or commentary comment.\n")) + (insert "This package lacks a README file or commentary.\n")) (goto-char (point-min)) (view-mode))) (display-buffer new-buffer t)))) @@ -1355,7 +1398,6 @@ Emacs." (interactive) (goto-char (point-min)) - (forward-line 2) (while (not (eobp)) (let ((cmd (char-after)) (pkg-name (package-menu-get-package))