Mercurial > hg > xemacs-beta
diff lisp/package-get.el @ 375:a300bb07d72d r21-2b3
Import from CVS: tag r21-2b3
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:04:51 +0200 |
parents | 6240c7796c7a |
children | d883f39b8495 |
line wrap: on
line diff
--- a/lisp/package-get.el Mon Aug 13 11:04:07 2007 +0200 +++ b/lisp/package-get.el Mon Aug 13 11:04:51 2007 +0200 @@ -97,8 +97,17 @@ ;;; Code: (require 'package-admin) -(require 'package-get-base) +;; (require 'package-get-base) + +(defgroup package-tools nil + "Tools to manipulate packages." + :group 'emacs) +(defgroup package-get nil + "Automatic Package Fetcher and Installer." + :prefix "package-get" + :group 'package-tools) + (defvar package-get-base nil "List of packages that are installed at this site. For each element in the alist, car is the package name and the cdr is @@ -145,25 +154,157 @@ be lexically ordered. It is debatable if it makes sense to have more than one version of a package available.") -(defvar package-get-dir (temp-directory) - "*Where to store temporary files for staging.") +(defcustom package-get-dir (temp-directory) + "*Where to store temporary files for staging." + :tag "Temporary directory" + :type 'directory + :group 'package-get) -(defvar package-get-remote +;; JV Any Custom expert know to get "Host" and "Dir" for the remote option +(defcustom package-get-remote '(("ftp.xemacs.org" "/pub/xemacs/packages")) "*List of remote sites to contact for downloading packages. List format is '(site-name directory-on-site). Each site is tried in order until the package is found. As a special case, `site-name' can be -`nil', in which case `directory-on-site' is treated as a local directory.") +`nil', in which case `directory-on-site' is treated as a local directory." + :tag "Package repository" + :type '(repeat (choice (list :tag "Local" (const :tag "Local" nil) directory ) + (list :tag "Remote" string string) )) + :group 'package-get) + +(defcustom package-get-remove-copy nil + "*After copying and installing a package, if this is T, then remove the +copy. Otherwise, keep it around." + :type 'boolean + :group 'package-get) + +(defcustom package-get-base-filename + "/ftp.xemacs.org:/pub/xemacs/packages/package-index.LATEST" + "*Name of the default package database file, usually on ftp.xemacs.org." + :type 'file + :group 'package-get) + +;;;###autoload +(defun package-get-require-base () + "Require that a package-get database has been loaded." + (when (or (not (boundp 'package-get-base)) + (not package-get-base)) + (package-get-update-base)) + (when (or (not (boundp 'package-get-base)) + (not package-get-base)) + (error "Package-get database not loaded"))) + +(defconst package-get-pgp-signed-begin-line "^-----BEGIN PGP SIGNED MESSAGE-----" + "Text for start of PGP signed messages.") +(defconst package-get-pgp-signature-begin-line "^-----BEGIN PGP SIGNATURE-----" + "Text for beginning of PGP signature.") +(defconst package-get-pgp-signature-end-line "^-----END PGP SIGNATURE-----" + "Text for end of PGP signature.") + +;;;###autoload +(defun package-get-update-base-entry (entry) + "Update an entry in `package-get-base'." + (let ((existing (assoc (car entry) package-get-base))) + (if existing + (setcdr existing (cdr entry)) + (setq package-get-base (cons entry package-get-base))))) + +;;;###autoload +(defun package-get-update-base (&optional db-file) + "Update the package-get database file with entries from DB-FILE." + (interactive (list + (read-file-name "Load package-get database: " + (file-name-directory package-get-base-filename) + package-get-base-filename + t + (file-name-nondirectory package-get-base-filename)))) + (setq db-file (expand-file-name (or db-file package-get-base-filename))) + (if (not (file-exists-p db-file)) + (error "Package-get database file `%s' does not exist" db-file)) + (if (not (file-readable-p db-file)) + (error "Package-get database file `%s' not readable" db-file)) + (let ((buf (get-buffer-create "*package database*"))) + (unwind-protect + (save-excursion + (set-buffer buf) + (erase-buffer buf) + (insert-file-contents-internal db-file) + (package-get-update-base-from-buffer buf)) + (kill-buffer buf)))) -(defvar package-get-remove-copy nil - "*After copying and installing a package, if this is T, then remove the -copy. Otherwise, keep it around.") +;;;###autoload +(defun package-get-update-base-from-buffer (&optional buf) + "Update the package-get database with entries from BUFFER. +BUFFER defaults to the current buffer. This command can be +used interactively, for example from a mail or news buffer." + (interactive) + (setq buf (or buf (current-buffer))) + (let (content-beg content-end beg end) + (save-excursion + (set-buffer buf) + (goto-char (point-min)) + (setq content-beg (point)) + (setq content-end (save-excursion (goto-char (point-max)) (point))) + (when (re-search-forward package-get-pgp-signed-begin-line nil t) + (setq beg (match-beginning 0)) + (setq content-beg (match-end 0))) + (when (re-search-forward package-get-pgp-signature-begin-line nil t) + (setq content-end (match-beginning 0))) + (when (re-search-forward package-get-pgp-signature-end-line nil t) + (setq end (point))) + (if (not (and content-beg content-end beg end)) + (or (yes-or-no-p "Package-get entries not PGP signed, continue? ") + (error "Package-get database not updated"))) + (if (and content-beg content-end beg end) + (if (not (condition-case nil + (or (fboundp 'mc-pgp-verify-region) + (load-library "mc-pgp") + (fboundp 'mc-pgp-verify-region)) + (error nil))) + (or (yes-or-no-p + "No mailcrypt; can't verify package-get DB signature, continue? ") + (error "Package-get database not updated")))) + (if (and beg end + (fboundp 'mc-pgp-verify-region) + (or (not + (condition-case err + (mc-pgp-verify-region beg end) + (file-error + (and (string-match "No such file" (nth 2 err)) + (yes-or-no-p + "Can't find PGP, continue without package-get DB verification? "))) + (t nil))))) + (error "Package-get PGP signature failed to verify")) + (package-get-update-base-entries content-beg content-end) + (message "Updated package-get database")))) + +(defun package-get-update-base-entries (beg end) + "Update the package-get database with the entries found between +BEG and END in the current buffer." + (save-excursion + (goto-char beg) + (if (not (re-search-forward "^(package-get-update-base-entry" nil t)) + (error "Buffer does not contain package-get database entries")) + (beginning-of-line) + (let ((count 0)) + (while (and (< (point) end) + (re-search-forward "^(package-get-update-base-entry" nil t)) + (beginning-of-line) + (let ((entry (read (current-buffer)))) + (if (or (not (consp entry)) + (not (eq (car entry) 'package-get-update-base-entry))) + (error "Invalid package-get database entry found")) + (package-get-update-base-entry + (car (cdr (car (cdr entry))))) + (setq count (1+ count)))) + (message "Got %d package-get database entries" count)))) (defun package-get-interactive-package-query (get-version package-symbol) "Perform interactive querying for package and optional version. Query for a version if GET-VERSION is non-nil. Return package name as a symbol instead of a string if PACKAGE-SYMBOL is non-nil. The return value is suitable for direct passing to `interactive'." + (package-get-require-base) (let ( (table (mapcar '(lambda (item) (let ( (name (symbol-name (car item))) ) (cons name name) @@ -206,6 +347,7 @@ (defun package-get-update-all () "Fetch and install the latest versions of all currently installed packages." (interactive) + (package-get-require-base) ;; Load a fresh copy (catch 'exit (mapcar (lambda (pkg) @@ -215,12 +357,14 @@ packages-package-list))) ;;;###autoload -(defun package-get-all (package version &optional fetched-packages) +(defun package-get-all (package version &optional fetched-packages install-dir) "Fetch PACKAGE with VERSION and all other required packages. Uses `package-get-base' to determine just what is required and what package provides that functionality. If VERSION is nil, retrieves latest version. Optional argument FETCHED-PACKAGES is used to keep -track of packages already fetched. +track of packages already fetched. Optional argument INSTALL-DIR, +if non-nil, specifies the package directory where fetched packages +should be installed. Returns nil upon error." (interactive (package-get-interactive-package-query t nil)) @@ -233,7 +377,7 @@ (catch 'exit (setq version (package-get-info-prop this-package 'version)) (unless (package-get-installedp package version) - (if (not (package-get package version)) + (if (not (package-get package version nil install-dir)) (progn (setq fetched-packages nil) (throw 'exit nil)))) @@ -256,7 +400,8 @@ (car this-requires))) (if (not (setq fetched-packages (package-get-all reqd-name reqd-version - fetched-packages))) + fetched-packages + install-dir))) (throw 'exit nil))) ) (setq this-requires (cdr this-requires))) @@ -264,6 +409,42 @@ fetched-packages )) +;;;###autoload +(defun package-get-dependencies (packages) + "Compute dependencies for PACKAGES. +Uses `package-get-base' to determine just what is required and what +package provides that functionality. Returns the list of packages +required by PACKAGES." + (package-get-require-base) + (let ((orig-packages packages) + dependencies provided) + (while packages + (let* ((package (car packages)) + (the-package (package-get-info-find-package + package-get-base package)) + (this-package (package-get-info-version + the-package nil)) + (this-requires (package-get-info-prop this-package 'requires)) + (new-depends (set-difference + (mapcar + #'(lambda (reqd) + (let* ((reqd-package (package-get-package-provider reqd)) + (reqd-version (cadr reqd-package)) + (reqd-name (car reqd-package))) + (if (null reqd-name) + (error "Unable to find a provider for %s" reqd)) + reqd-name)) + this-requires) + dependencies)) + (this-provides (package-get-info-prop this-package 'provides))) + (setq dependencies + (union dependencies new-depends)) + (setq provided + (union provided (union (list package) this-provides))) + (setq packages + (union new-depends (cdr packages))))) + (set-difference dependencies orig-packages))) + (defun package-get-load-package-file (lispdir file) (let (pathname) (setq pathname (expand-file-name file lispdir)) @@ -332,6 +513,7 @@ (package-get-info-version (package-get-info-find-package package-get-base package) version)) + (this-requires (package-get-info-prop this-package 'requires)) (found nil) (search-dirs package-get-remote) (base-filename (package-get-info-prop this-package 'filename)) @@ -343,8 +525,9 @@ (if (null base-filename) (error "No filename associated with package %s, version %s" package version)) - (if (null install-dir) - (setq install-dir (package-admin-get-install-dir nil))) + (setq install-dir + (package-admin-get-install-dir package install-dir + (or (eq package 'mule-base) (memq 'mule-base this-requires)))) ;; Contrive a list of possible package filenames. ;; Ugly. Is there a better way to do this? @@ -581,6 +764,7 @@ consp, then it must match a corresponding (provide (SYM VERSION)) from the package." (interactive "SSymbol: ") + (package-get-require-base) (let ((packages package-get-base) (done nil) (found nil)) @@ -612,6 +796,7 @@ (defun package-get-custom () "Fetch and install the latest versions of all customized packages." (interactive) + (package-get-require-base) ;; Load a fresh copy (load "package-get-custom.el") (mapcar (lambda (pkg) @@ -690,6 +875,8 @@ (provide 'package-get) ;; potentially update the custom dependencies every time we load this +(when nil ;; #### disable for now... -gk +(unless noninteractive (let ((custom-file (package-get-file-installed-p "package-get-custom.el")) (package-file (package-get-file-installed-p "package-get-base.el"))) ;; update custom file if it doesn't exist @@ -703,6 +890,7 @@ (set-buffer (package-get-create-custom)) (save-buffer) (message "generating package customizations...done"))) - (load "package-get-custom.el")) + (load "package-get-custom.el"))) +) ;;; package-get.el ends here