Mercurial > hg > xemacs-beta
comparison lisp/package-get.el @ 321:19dcec799385 r21-0-58
Import from CVS: tag r21-0-58
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:46:44 +0200 |
parents | afd57c14dfc8 |
children | f2b5d7006b0a |
comparison
equal
deleted
inserted
replaced
320:73c75c43c1f2 | 321:19dcec799385 |
---|---|
1 ;;; package-get.el --- Retrieve XEmacs package | 1 ;;; package-get.el --- Retrieve XEmacs package |
2 | 2 |
3 ;; Copyright (C) 1998 by Pete Ware | 3 ;; Copyright (C) 1998 by Pete Ware |
4 | 4 |
5 ;; Author: Pete Ware <ware@cis.ohio-state.edu> | 5 ;; Author: Pete Ware <ware@cis.ohio-state.edu> |
6 ;; Heavy-Modifications: Greg Klanderman <greg@alphatech.com> | |
7 ;; Jan Vroonhof <vroonhof@math.ethz.ch> | |
6 ;; Keywords: internal | 8 ;; Keywords: internal |
7 | 9 |
8 ;; This file is part of XEmacs. | 10 ;; This file is part of XEmacs. |
9 | 11 |
10 ;; XEmacs is free software; you can redistribute it and/or modify it | 12 ;; XEmacs is free software; you can redistribute it and/or modify it |
26 | 28 |
27 ;;; Commentary: | 29 ;;; Commentary: |
28 | 30 |
29 ;; package-get - | 31 ;; package-get - |
30 ;; Retrieve a package and any other required packages from an archive | 32 ;; Retrieve a package and any other required packages from an archive |
33 ;; | |
34 ;; | |
35 ;; Note (JV): Most of this no longer aplies! | |
31 ;; | 36 ;; |
32 ;; The idea: | 37 ;; The idea: |
33 ;; A new XEmacs lisp-only release is generated with the following steps: | 38 ;; A new XEmacs lisp-only release is generated with the following steps: |
34 ;; 1. The maintainer runs some yet to be written program that | 39 ;; 1. The maintainer runs some yet to be written program that |
35 ;; generates all the dependency information. This should | 40 ;; generates all the dependency information. This should |
95 ;;; Change Log | 100 ;;; Change Log |
96 | 101 |
97 ;;; Code: | 102 ;;; Code: |
98 | 103 |
99 (require 'package-admin) | 104 (require 'package-admin) |
100 (require 'package-get-base) | 105 ;; (require 'package-get-base) |
101 | 106 |
107 (defgroup package-tools nil | |
108 "Tools to manipulate packages." | |
109 :group 'emacs) | |
110 | |
111 (defgroup package-get nil | |
112 "Automatic Package Fetcher and Installer." | |
113 :prefix "package-get" | |
114 :group 'package-tools) | |
115 | |
102 (defvar package-get-base nil | 116 (defvar package-get-base nil |
103 "List of packages that are installed at this site. | 117 "List of packages that are installed at this site. |
104 For each element in the alist, car is the package name and the cdr is | 118 For each element in the alist, car is the package name and the cdr is |
105 a plist containing information about the package. Typical fields | 119 a plist containing information about the package. Typical fields |
106 kept in the plist are: | 120 kept in the plist are: |
143 For version information, it is assumed things are listed in most | 157 For version information, it is assumed things are listed in most |
144 recent to least recent -- in other words, the version names don't have to | 158 recent to least recent -- in other words, the version names don't have to |
145 be lexically ordered. It is debatable if it makes sense to have more than | 159 be lexically ordered. It is debatable if it makes sense to have more than |
146 one version of a package available.") | 160 one version of a package available.") |
147 | 161 |
148 (defvar package-get-dir (temp-directory) | 162 (defcustom package-get-dir (temp-directory) |
149 "*Where to store temporary files for staging.") | 163 "*Where to store temporary files for staging." |
150 | 164 :tag "Temporary directory" |
151 (defvar package-get-remote | 165 :type 'directory |
152 '(("ftp.xemacs.org" "/pub/xemacs/packages")) | 166 :group 'package-get) |
167 | |
168 (define-widget 'host-name 'string | |
169 "A Host name." | |
170 :tag "Host") | |
171 | |
172 (defcustom package-get-remote nil | |
153 "*List of remote sites to contact for downloading packages. | 173 "*List of remote sites to contact for downloading packages. |
154 List format is '(site-name directory-on-site). Each site is tried in | 174 List format is '(site-name directory-on-site). Each site is tried in |
155 order until the package is found. As a special case, `site-name' can be | 175 order until the package is found. As a special case, `site-name' can be |
156 `nil', in which case `directory-on-site' is treated as a local directory.") | 176 `nil', in which case `directory-on-site' is treated as a local directory." |
157 | 177 :tag "Package repository" |
158 (defvar package-get-remove-copy nil | 178 :type '(repeat (choice (list :tag "Local" (const :tag "Local" nil) directory ) |
179 (list :tag "Remote" host-name directory) )) | |
180 :group 'package-get) | |
181 | |
182 (defcustom package-get-download-sites | |
183 '( | |
184 ;; North America | |
185 ("xemacs.org" "ftp.xemacs.org" "pub/xemacs/packages") | |
186 ("cso.uiuc.edu" "ftp.cso.uiuc.edu" "pub/packages/xemacs/packages") | |
187 | |
188 ;; South America | |
189 ("unicamp.br" "ftp.unicamp.br" "pub/xemacs/packages") | |
190 | |
191 ;; Europe | |
192 ("sunsite.cnlab-switch.ch" "sunsite.cnlab-switch.ch" "mirror/xemacs/packages") | |
193 ("tu-darmstadt.de" "ftp.tu-darmstadt.de" "pub/editors/xemacs/packages") | |
194 ("sunsite.auc.dk" "sunsite.auc.dk" "pub/emacs/xemacs/packages") | |
195 ("pasteur.fr" "ftp.pasteur.fr" "pub/computing/xemacs/packages") | |
196 ("cenatls.cena.dgac.fr" "ftp.cenatls.cena.dgac.fr" "pub/Emacs/xemacs/packages") | |
197 ("kfki.hu" "ftp.kfki.hu" "pub/packages/xemacs/packages") | |
198 ("uniroma2.it" "ftp.uniroma2.it" "unix/misc/dist/XEMACS/packages") | |
199 ("icm.edu.pl" "ftp.icm.edu.pl" "pub/unix/editors/xemacs/packages") | |
200 ("sunet.se" "ftp.sunet.se" "pub/gnu/xemacs/packages") | |
201 ("doc.ic.ac.uk" "ftp.doc.ic.ac.uk" "packages/xemacs/packages") | |
202 ("srcc.msu.su" "ftp1.srcc.msu.su" "mirror/ftp.xemacs.org/packages") | |
203 | |
204 ;; Asia | |
205 ("usyd.edu.au" "ftp.usyd.edu.au" "pub/Xemacs/packages") | |
206 ("netlab.is.tsukuba.ac.jp" "ftp.netlab.is.tsukuba.ac.jp" "pub/GNU/xemacs/packages") | |
207 ("jaist.ac.jp" "ftp.jaist.ac.jp" "pub/GNU/xemacs/packages") | |
208 ("ring.aist.go.jp" "ring.aist.go.jp" "pub/text/xemacs/packages") | |
209 ("ring.asahi-net.or.jp" "ring.asahi-net.or.jp" "pub/text/xemacs/packages") | |
210 ("SunSITE.sut.ac.jp" "SunSITE.sut.ac.jp" "pub/archives/packages/xemacs/packages") | |
211 ("dti.ad.jp" "ftp.dti.ad.jp" "pub/unix/editor/xemacs/packages") | |
212 ("kreonet.re.kr" "ftp.kreonet.re.kr" "pub/tools/emacs/xemacs/packages") | |
213 ) | |
214 "*List of remote sites available for downloading packages. | |
215 List format is '(site-description site-name directory-on-site). | |
216 SITE-DESCRIPTION is a textual description of the site. SITE-NAME | |
217 is the internet address of the download site. DIRECTORY-ON-SITE | |
218 is the directory on the site in which packages may be found. | |
219 This variable is used to initialize `package-get-remote', the | |
220 variable actually used to specify package download sites." | |
221 :tag "Package download sites" | |
222 :type '(repeat (list hostname directory)) | |
223 :group 'package-get) | |
224 | |
225 (defcustom package-get-remove-copy t | |
159 "*After copying and installing a package, if this is T, then remove the | 226 "*After copying and installing a package, if this is T, then remove the |
160 copy. Otherwise, keep it around.") | 227 copy. Otherwise, keep it around." |
228 :type 'boolean | |
229 :group 'package-get) | |
230 | |
231 ;; #### it may make sense for this to be a list of names. | |
232 ;; #### also, should we rename "*base*" to "*index*" or "*db*"? | |
233 ;; "base" is a pretty poor name. | |
234 (defcustom package-get-base-filename "package-index.LATEST.pgp" | |
235 "*Name of the default package-get database file. | |
236 This may either be a relative path, in which case it is interpreted | |
237 with respect to `package-get-remote', or an absolute path." | |
238 :type 'file | |
239 :group 'package-get) | |
240 | |
241 (defcustom package-get-always-update nil | |
242 "*If Non-nil always make sure we are using the latest package index (base). | |
243 Otherwise respect the `force-current' argument of `package-get-require-base'." | |
244 :type 'boolean | |
245 :group 'package-get) | |
246 | |
247 (defcustom package-get-require-signed-base-updates t | |
248 "*If set to a non-nil value, require explicit user confirmation for updates | |
249 to the package-get database which cannot have their signature verified via PGP. | |
250 When nil, updates which are not PGP signed are allowed without confirmation." | |
251 :type 'boolean | |
252 :group 'package-get) | |
253 | |
254 (defvar package-get-was-current nil | |
255 "Non-nil we did our best to fetch a current database.") | |
256 | |
257 ;;;###autoload | |
258 (defun package-get-download-menu () | |
259 "Build the `Add Download Site' menu." | |
260 (mapcar (lambda (site) | |
261 (vector (car site) | |
262 `(push (quote ,(cdr site)) | |
263 package-get-remote))) | |
264 package-get-download-sites)) | |
265 | |
266 ;;;###autoload | |
267 (defun package-get-require-base (&optional force-current) | |
268 "Require that a package-get database has been loaded. | |
269 If the optional FORCE-CURRENT argument or the value of | |
270 `package-get-always-update' is Non-nil, try to update the database | |
271 from a location in `package-get-remote'. Otherwise a local copy is used | |
272 if available and remote access is never done. | |
273 | |
274 Please use FORCE-CURRENT only when the user is explictly dealing with packages | |
275 and remote access is likely in the near future." | |
276 (setq force-current (or force-current package-get-always-update)) | |
277 (unless (and (boundp 'package-get-base) | |
278 package-get-base | |
279 (or (not force-current) package-get-was-current)) | |
280 (package-get-update-base nil force-current)) | |
281 (if (or (not (boundp 'package-get-base)) | |
282 (not package-get-base)) | |
283 (error "Package-get database not loaded") | |
284 (setq package-get-was-current force-current))) | |
285 | |
286 (defconst package-get-pgp-signed-begin-line "^-----BEGIN PGP SIGNED MESSAGE-----" | |
287 "Text for start of PGP signed messages.") | |
288 (defconst package-get-pgp-signature-begin-line "^-----BEGIN PGP SIGNATURE-----" | |
289 "Text for beginning of PGP signature.") | |
290 (defconst package-get-pgp-signature-end-line "^-----END PGP SIGNATURE-----" | |
291 "Text for end of PGP signature.") | |
292 | |
293 ;;;###autoload | |
294 (defun package-get-update-base-entry (entry) | |
295 "Update an entry in `package-get-base'." | |
296 (let ((existing (assq (car entry) package-get-base))) | |
297 (if existing | |
298 (setcdr existing (cdr entry)) | |
299 (setq package-get-base (cons entry package-get-base)) | |
300 (package-get-custom-add-entry (car entry) (car (cdr entry)))))) | |
301 | |
302 (defun package-get-locate-file (file &optional nil-if-not-found no-remote) | |
303 "Locate an existing FILE with respect to `package-get-remote'. | |
304 If FILE is an absolute path or is not found, simply return FILE. | |
305 If optional argument NIL-IF-NOT-FOUND is non-nil, return nil | |
306 if FILE can not be located. | |
307 If NO-REMOTE is non-nil never search remote locations." | |
308 (if (file-name-absolute-p file) | |
309 file | |
310 (let ((entries package-get-remote) | |
311 (expanded nil)) | |
312 (while entries | |
313 (unless (and no-remote (caar entries)) | |
314 (let ((expn (package-get-remote-filename (car entries) file))) | |
315 (if (and expn (file-exists-p expn)) | |
316 (setq entries nil | |
317 expanded expn)))) | |
318 (setq entries (cdr entries))) | |
319 (or expanded | |
320 (and (not nil-if-not-found) | |
321 file))))) | |
322 | |
323 (defun package-get-locate-index-file (no-remote) | |
324 "Locate the package-get index file. Do not return remote paths if NO-REMOTE | |
325 is non-nil." | |
326 (or (package-get-locate-file package-get-base-filename t no-remote) | |
327 (locate-data-file package-get-base-filename) | |
328 package-get-base-filename)) | |
329 | |
330 (defvar package-get-user-package-location user-init-directory) | |
331 | |
332 (defun package-get-maybe-save-index (filename) | |
333 "Offer to save the current buffer as the local package index file, | |
334 if different." | |
335 (let ((location (package-get-locate-index-file t))) | |
336 (unless (and filename (equal filename location)) | |
337 (unless (equal (md5 (current-buffer)) | |
338 (with-temp-buffer | |
339 (insert-file-contents location) | |
340 (md5 (current-buffer)))) | |
341 (unless (file-writable-p location) | |
342 (setq location (expand-file-name package-get-base-filename | |
343 (expand-file-name "etc/" package-get-user-package-location)))) | |
344 (when (y-or-n-p (concat "Update package index in" location "? ")) | |
345 (write-file location)))))) | |
346 | |
347 | |
348 ;;;###autoload | |
349 (defun package-get-update-base (&optional db-file force-current) | |
350 "Update the package-get database file with entries from DB-FILE. | |
351 Unless FORCE-CURRENT is non-nil never try to update the database." | |
352 (interactive | |
353 (let ((dflt (package-get-locate-index-file nil))) | |
354 (list (read-file-name "Load package-get database: " | |
355 (file-name-directory dflt) | |
356 dflt | |
357 t | |
358 (file-name-nondirectory dflt))))) | |
359 (setq db-file (expand-file-name (or db-file | |
360 (package-get-locate-index-file | |
361 (not force-current))))) | |
362 (if (not (file-exists-p db-file)) | |
363 (error "Package-get database file `%s' does not exist" db-file)) | |
364 (if (not (file-readable-p db-file)) | |
365 (error "Package-get database file `%s' not readable" db-file)) | |
366 (let ((buf (get-buffer-create "*package database*"))) | |
367 (unwind-protect | |
368 (save-excursion | |
369 (set-buffer buf) | |
370 (erase-buffer buf) | |
371 (insert-file-contents-internal db-file) | |
372 (package-get-update-base-from-buffer buf) | |
373 (if (file-remote-p db-file) | |
374 (package-get-maybe-save-index db-file))) | |
375 (kill-buffer buf)))) | |
376 | |
377 ;;;###autoload | |
378 (defun package-get-update-base-from-buffer (&optional buf) | |
379 "Update the package-get database with entries from BUFFER. | |
380 BUFFER defaults to the current buffer. This command can be | |
381 used interactively, for example from a mail or news buffer." | |
382 (interactive) | |
383 (setq buf (or buf (current-buffer))) | |
384 (let (content-beg content-end beg end) | |
385 (save-excursion | |
386 (set-buffer buf) | |
387 (goto-char (point-min)) | |
388 (setq content-beg (point)) | |
389 (setq content-end (save-excursion (goto-char (point-max)) (point))) | |
390 (when (re-search-forward package-get-pgp-signed-begin-line nil t) | |
391 (setq beg (match-beginning 0)) | |
392 (setq content-beg (match-end 0))) | |
393 (when (re-search-forward package-get-pgp-signature-begin-line nil t) | |
394 (setq content-end (match-beginning 0))) | |
395 (when (re-search-forward package-get-pgp-signature-end-line nil t) | |
396 (setq end (point))) | |
397 (if (not (and content-beg content-end beg end)) | |
398 (or (not package-get-require-signed-base-updates) | |
399 (yes-or-no-p "Package-get entries not PGP signed, continue? ") | |
400 (error "Package-get database not updated"))) | |
401 (if (and content-beg content-end beg end) | |
402 (if (not (condition-case nil | |
403 (or (fboundp 'mc-pgp-verify-region) | |
404 (load-library "mc-pgp") | |
405 (fboundp 'mc-pgp-verify-region)) | |
406 (error nil))) | |
407 (or (not package-get-require-signed-base-updates) | |
408 (yes-or-no-p | |
409 "No mailcrypt; can't verify package-get DB signature, continue? ") | |
410 (error "Package-get database not updated")))) | |
411 (if (and beg end | |
412 (fboundp 'mc-pgp-verify-region) | |
413 (or (not | |
414 (condition-case err | |
415 (mc-pgp-verify-region beg end) | |
416 (file-error | |
417 (and (string-match "No such file" (nth 2 err)) | |
418 (or (not package-get-require-signed-base-updates) | |
419 (yes-or-no-p | |
420 (concat "Can't find PGP, continue without " | |
421 "package-get DB verification? "))))) | |
422 (t nil))))) | |
423 (error "Package-get PGP signature failed to verify")) | |
424 ;; ToDo: We shoud call package-get-maybe-save-index on the region | |
425 (package-get-update-base-entries content-beg content-end) | |
426 (message "Updated package-get database")))) | |
427 | |
428 (defun package-get-update-base-entries (beg end) | |
429 "Update the package-get database with the entries found between | |
430 BEG and END in the current buffer." | |
431 (save-excursion | |
432 (goto-char beg) | |
433 (if (not (re-search-forward "^(package-get-update-base-entry" nil t)) | |
434 (error "Buffer does not contain package-get database entries")) | |
435 (beginning-of-line) | |
436 (let ((count 0)) | |
437 (while (and (< (point) end) | |
438 (re-search-forward "^(package-get-update-base-entry" nil t)) | |
439 (beginning-of-line) | |
440 (let ((entry (read (current-buffer)))) | |
441 (if (or (not (consp entry)) | |
442 (not (eq (car entry) 'package-get-update-base-entry))) | |
443 (error "Invalid package-get database entry found")) | |
444 (package-get-update-base-entry | |
445 (car (cdr (car (cdr entry))))) | |
446 (setq count (1+ count)))) | |
447 (message "Got %d package-get database entries" count)))) | |
448 | |
449 ;;;###autoload | |
450 (defun package-get-save-base (file) | |
451 "Write the package-get database to FILE. | |
452 | |
453 Note: This database will be unsigned of course." | |
454 (interactive "FSave package-get database to: ") | |
455 (package-get-require-base t) | |
456 (let ((buf (get-buffer-create "*package database*"))) | |
457 (unwind-protect | |
458 (save-excursion | |
459 (set-buffer buf) | |
460 (erase-buffer buf) | |
461 (goto-char (point-min)) | |
462 (let ((entries package-get-base) entry plist) | |
463 (insert ";; Package Index file -- Do not edit manually.\n") | |
464 (insert ";;;@@@\n") | |
465 (while entries | |
466 (setq entry (car entries)) | |
467 (setq plist (car (cdr entry))) | |
468 (insert "(package-get-update-base-entry (quote\n") | |
469 (insert (format "(%s\n" (symbol-name (car entry)))) | |
470 (while plist | |
471 (insert (format " %s%s %S\n" | |
472 (if (eq plist (car (cdr entry))) "(" " ") | |
473 (symbol-name (car plist)) | |
474 (car (cdr plist)))) | |
475 (setq plist (cdr (cdr plist)))) | |
476 (insert "))\n))\n;;;@@@\n") | |
477 (setq entries (cdr entries)))) | |
478 (insert ";; Package Index file ends here\n") | |
479 (write-region (point-min) (point-max) file)) | |
480 (kill-buffer buf)))) | |
161 | 481 |
162 (defun package-get-interactive-package-query (get-version package-symbol) | 482 (defun package-get-interactive-package-query (get-version package-symbol) |
163 "Perform interactive querying for package and optional version. | 483 "Perform interactive querying for package and optional version. |
164 Query for a version if GET-VERSION is non-nil. Return package name as | 484 Query for a version if GET-VERSION is non-nil. Return package name as |
165 a symbol instead of a string if PACKAGE-SYMBOL is non-nil. | 485 a symbol instead of a string if PACKAGE-SYMBOL is non-nil. |
166 The return value is suitable for direct passing to `interactive'." | 486 The return value is suitable for direct passing to `interactive'." |
487 (package-get-require-base t) | |
167 (let ( (table (mapcar '(lambda (item) | 488 (let ( (table (mapcar '(lambda (item) |
168 (let ( (name (symbol-name (car item))) ) | 489 (let ( (name (symbol-name (car item))) ) |
169 (cons name name) | 490 (cons name name) |
170 )) | 491 )) |
171 package-get-base)) | 492 package-get-base)) |
204 | 525 |
205 ;;;###autoload | 526 ;;;###autoload |
206 (defun package-get-update-all () | 527 (defun package-get-update-all () |
207 "Fetch and install the latest versions of all currently installed packages." | 528 "Fetch and install the latest versions of all currently installed packages." |
208 (interactive) | 529 (interactive) |
530 (package-get-require-base t) | |
209 ;; Load a fresh copy | 531 ;; Load a fresh copy |
210 (catch 'exit | 532 (catch 'exit |
211 (mapcar (lambda (pkg) | 533 (mapcar (lambda (pkg) |
212 (if (not (package-get (car pkg) nil 'never)) | 534 (if (not (package-get (car pkg) nil 'never)) |
213 (throw 'exit nil) ;; Bail out if error detected | 535 (throw 'exit nil) ;; Bail out if error detected |
214 )) | 536 )) |
215 packages-package-list))) | 537 packages-package-list))) |
216 | 538 |
217 ;;;###autoload | 539 ;;;###autoload |
218 (defun package-get-all (package version &optional fetched-packages) | 540 (defun package-get-all (package version &optional fetched-packages install-dir) |
219 "Fetch PACKAGE with VERSION and all other required packages. | 541 "Fetch PACKAGE with VERSION and all other required packages. |
220 Uses `package-get-base' to determine just what is required and what | 542 Uses `package-get-base' to determine just what is required and what |
221 package provides that functionality. If VERSION is nil, retrieves | 543 package provides that functionality. If VERSION is nil, retrieves |
222 latest version. Optional argument FETCHED-PACKAGES is used to keep | 544 latest version. Optional argument FETCHED-PACKAGES is used to keep |
223 track of packages already fetched. | 545 track of packages already fetched. Optional argument INSTALL-DIR, |
546 if non-nil, specifies the package directory where fetched packages | |
547 should be installed. | |
224 | 548 |
225 Returns nil upon error." | 549 Returns nil upon error." |
226 (interactive (package-get-interactive-package-query t nil)) | 550 (interactive (package-get-interactive-package-query t nil)) |
227 (let* ((the-package (package-get-info-find-package package-get-base | 551 (let* ((the-package (package-get-info-find-package package-get-base |
228 package)) | 552 package)) |
231 (this-requires (package-get-info-prop this-package 'requires)) | 555 (this-requires (package-get-info-prop this-package 'requires)) |
232 ) | 556 ) |
233 (catch 'exit | 557 (catch 'exit |
234 (setq version (package-get-info-prop this-package 'version)) | 558 (setq version (package-get-info-prop this-package 'version)) |
235 (unless (package-get-installedp package version) | 559 (unless (package-get-installedp package version) |
236 (if (not (package-get package version)) | 560 (if (not (package-get package version nil install-dir)) |
237 (progn | 561 (progn |
238 (setq fetched-packages nil) | 562 (setq fetched-packages nil) |
239 (throw 'exit nil)))) | 563 (throw 'exit nil)))) |
240 (setq fetched-packages | 564 (setq fetched-packages |
241 (append (list package) | 565 (append (list package) |
246 ;; in `fetched-packages' the list of things provided -- this | 570 ;; in `fetched-packages' the list of things provided -- this |
247 ;; keeps us from going into a loop | 571 ;; keeps us from going into a loop |
248 (while this-requires | 572 (while this-requires |
249 (if (not (member (car this-requires) fetched-packages)) | 573 (if (not (member (car this-requires) fetched-packages)) |
250 (let* ((reqd-package (package-get-package-provider | 574 (let* ((reqd-package (package-get-package-provider |
251 (car this-requires))) | 575 (car this-requires) t)) |
252 (reqd-version (cadr reqd-package)) | 576 (reqd-version (cadr reqd-package)) |
253 (reqd-name (car reqd-package))) | 577 (reqd-name (car reqd-package))) |
254 (if (null reqd-name) | 578 (if (null reqd-name) |
255 (error "Unable to find a provider for %s" | 579 (error "Unable to find a provider for %s" |
256 (car this-requires))) | 580 (car this-requires))) |
257 (if (not (setq fetched-packages | 581 (if (not (setq fetched-packages |
258 (package-get-all reqd-name reqd-version | 582 (package-get-all reqd-name reqd-version |
259 fetched-packages))) | 583 fetched-packages |
584 install-dir))) | |
260 (throw 'exit nil))) | 585 (throw 'exit nil))) |
261 ) | 586 ) |
262 (setq this-requires (cdr this-requires))) | 587 (setq this-requires (cdr this-requires))) |
263 ) | 588 ) |
264 fetched-packages | 589 fetched-packages |
265 )) | 590 )) |
591 | |
592 ;;;###autoload | |
593 (defun package-get-dependencies (packages) | |
594 "Compute dependencies for PACKAGES. | |
595 Uses `package-get-base' to determine just what is required and what | |
596 package provides that functionality. Returns the list of packages | |
597 required by PACKAGES." | |
598 (package-get-require-base t) | |
599 (let ((orig-packages packages) | |
600 dependencies provided) | |
601 (while packages | |
602 (let* ((package (car packages)) | |
603 (the-package (package-get-info-find-package | |
604 package-get-base package)) | |
605 (this-package (package-get-info-version | |
606 the-package nil)) | |
607 (this-requires (package-get-info-prop this-package 'requires)) | |
608 (new-depends (set-difference | |
609 (mapcar | |
610 #'(lambda (reqd) | |
611 (let* ((reqd-package (package-get-package-provider reqd)) | |
612 (reqd-version (cadr reqd-package)) | |
613 (reqd-name (car reqd-package))) | |
614 (if (null reqd-name) | |
615 (error "Unable to find a provider for %s" reqd)) | |
616 reqd-name)) | |
617 this-requires) | |
618 dependencies)) | |
619 (this-provides (package-get-info-prop this-package 'provides))) | |
620 (setq dependencies | |
621 (union dependencies new-depends)) | |
622 (setq provided | |
623 (union provided (union (list package) this-provides))) | |
624 (setq packages | |
625 (union new-depends (cdr packages))))) | |
626 (set-difference dependencies orig-packages))) | |
266 | 627 |
267 (defun package-get-load-package-file (lispdir file) | 628 (defun package-get-load-package-file (lispdir file) |
268 (let (pathname) | 629 (let (pathname) |
269 (setq pathname (expand-file-name file lispdir)) | 630 (setq pathname (expand-file-name file lispdir)) |
270 (condition-case err | 631 (condition-case err |
326 | 687 |
327 Returns `t' upon success, the symbol `error' if the package was | 688 Returns `t' upon success, the symbol `error' if the package was |
328 successfully installed but errors occurred during initialization, or | 689 successfully installed but errors occurred during initialization, or |
329 `nil' upon error." | 690 `nil' upon error." |
330 (interactive (package-get-interactive-package-query nil t)) | 691 (interactive (package-get-interactive-package-query nil t)) |
692 (catch 'skip-update | |
331 (let* ((this-package | 693 (let* ((this-package |
332 (package-get-info-version | 694 (package-get-info-version |
333 (package-get-info-find-package package-get-base | 695 (package-get-info-find-package package-get-base |
334 package) version)) | 696 package) version)) |
697 (latest (package-get-info-prop this-package 'version)) | |
698 (installed (package-get-key package :version)) | |
699 (this-requires (package-get-info-prop this-package 'requires)) | |
335 (found nil) | 700 (found nil) |
336 (search-dirs package-get-remote) | 701 (search-dirs package-get-remote) |
337 (base-filename (package-get-info-prop this-package 'filename)) | 702 (base-filename (package-get-info-prop this-package 'filename)) |
338 (package-status t) | 703 (package-status t) |
339 filenames full-package-filename) | 704 filenames full-package-filename) |
341 (error "Couldn't find package %s with version %s" | 706 (error "Couldn't find package %s with version %s" |
342 package version)) | 707 package version)) |
343 (if (null base-filename) | 708 (if (null base-filename) |
344 (error "No filename associated with package %s, version %s" | 709 (error "No filename associated with package %s, version %s" |
345 package version)) | 710 package version)) |
346 (if (null install-dir) | 711 (setq install-dir |
347 (setq install-dir (package-admin-get-install-dir nil))) | 712 (package-admin-get-install-dir package install-dir |
713 (or (eq package 'mule-base) (memq 'mule-base this-requires)))) | |
714 | |
715 ;; If they asked for the latest using version=nil, don't get an older | |
716 ;; version than we already have. | |
717 (if installed | |
718 (if (> (if (stringp installed) | |
719 (string-to-number installed) | |
720 installed) | |
721 (if (stringp latest) | |
722 (string-to-number latest) | |
723 latest)) | |
724 (if (not (null version)) | |
725 (warn "Installing %s package version %s, you had a newer version %s" | |
726 package latest installed) | |
727 (warn "Skipping %s package, you have a newer version %s" | |
728 package installed) | |
729 (throw 'skip-update t)))) | |
348 | 730 |
349 ;; Contrive a list of possible package filenames. | 731 ;; Contrive a list of possible package filenames. |
350 ;; Ugly. Is there a better way to do this? | 732 ;; Ugly. Is there a better way to do this? |
351 (setq filenames (cons base-filename nil)) | 733 (setq filenames (cons base-filename nil)) |
352 (if (string-match "^\\(..*\\)\.tar\.gz$" base-filename) | 734 (if (string-match "^\\(..*\\)\.tar\.gz$" base-filename) |
353 (setq filenames (append filenames | 735 (setq filenames (append filenames |
354 (list (concat (match-string 1 base-filename) | 736 (list (concat (match-string 1 base-filename) |
355 ".tgz"))))) | 737 ".tgz"))))) |
356 | 738 |
357 (setq version (package-get-info-prop this-package 'version)) | 739 (setq version latest) |
358 (unless (and (eq conflict 'never) | 740 (unless (and (eq conflict 'never) |
359 (package-get-installedp package version)) | 741 (package-get-installedp package version)) |
360 ;; Find the package from the search list in package-get-remote | 742 ;; Find the package from the search list in package-get-remote |
361 ;; and copy it into the staging directory. Then validate | 743 ;; and copy it into the staging directory. Then validate |
362 ;; the checksum. Finally, install the package. | 744 ;; the checksum. Finally, install the package. |
472 )) | 854 )) |
473 (setq found t)) | 855 (setq found t)) |
474 (if (and found package-get-remove-copy) | 856 (if (and found package-get-remove-copy) |
475 (delete-file full-package-filename)) | 857 (delete-file full-package-filename)) |
476 package-status | 858 package-status |
477 )) | 859 ))) |
478 | 860 |
479 (defun package-get-info-find-package (which name) | 861 (defun package-get-info-find-package (which name) |
480 "Look in WHICH for the package called NAME and return all the info | 862 "Look in WHICH for the package called NAME and return all the info |
481 associated with it. See `package-get-base' for info on the format | 863 associated with it. See `package-get-base' for info on the format |
482 returned. | 864 returned. |
573 (package-get-info-find-package packages-package-list | 955 (package-get-info-find-package packages-package-list |
574 package) ':version) | 956 package) ':version) |
575 (if (floatp version) version (string-to-number version)))) | 957 (if (floatp version) version (string-to-number version)))) |
576 | 958 |
577 ;;;###autoload | 959 ;;;###autoload |
578 (defun package-get-package-provider (sym) | 960 (defun package-get-package-provider (sym &optional force-current) |
579 "Search for a package that provides SYM and return the name and | 961 "Search for a package that provides SYM and return the name and |
580 version. Searches in `package-get-base' for SYM. If SYM is a | 962 version. Searches in `package-get-base' for SYM. If SYM is a |
581 consp, then it must match a corresponding (provide (SYM VERSION)) from | 963 consp, then it must match a corresponding (provide (SYM VERSION)) from |
582 the package." | 964 the package. |
965 | |
966 If FORCE-CURRENT is non-nil make sure the database is up to date. This might | |
967 lead to Emacs accessing remote sites." | |
583 (interactive "SSymbol: ") | 968 (interactive "SSymbol: ") |
969 (package-get-require-base force-current) | |
584 (let ((packages package-get-base) | 970 (let ((packages package-get-base) |
585 (done nil) | 971 (done nil) |
586 (found nil)) | 972 (found nil)) |
587 (while (and (not done) packages) | 973 (while (and (not done) packages) |
588 (let* ((this-name (caar packages)) | 974 (let* ((this-name (caar packages)) |
589 (this-package (cdr (car packages)))) ;strip off package name | 975 (this-package (cdr (car packages)))) ;strip off package name |
590 (while (and (not done) this-package) | 976 (while (and (not done) this-package) |
591 (if (or (eq this-name sym) | 977 (if (or (eq this-name sym) |
592 (eq (cons this-name | 978 (eq (cons this-name |
593 (package-get-info-prop (car this-package) 'version)) | 979 (package-get-info-prop (car this-package) 'version)) |
594 sym) | 980 sym) |
595 (member sym (package-get-info-prop (car this-package) 'provides))) | 981 (member sym |
982 (package-get-info-prop (car this-package) 'provides))) | |
596 (progn (setq done t) | 983 (progn (setq done t) |
597 (setq found (list (caar packages) | 984 (setq found |
598 (package-get-info-prop (car this-package) 'version)))) | 985 (list (caar packages) |
986 (package-get-info-prop (car this-package) 'version)))) | |
599 (setq this-package (cdr this-package))))) | 987 (setq this-package (cdr this-package))))) |
600 (setq packages (cdr packages))) | 988 (setq packages (cdr packages))) |
601 found)) | 989 found)) |
602 | 990 |
603 ;; | 991 ;; |
610 | 998 |
611 ;;;###autoload | 999 ;;;###autoload |
612 (defun package-get-custom () | 1000 (defun package-get-custom () |
613 "Fetch and install the latest versions of all customized packages." | 1001 "Fetch and install the latest versions of all customized packages." |
614 (interactive) | 1002 (interactive) |
1003 (package-get-require-base t) | |
615 ;; Load a fresh copy | 1004 ;; Load a fresh copy |
616 (load "package-get-custom.el") | 1005 (load "package-get-custom.el") |
617 (mapcar (lambda (pkg) | 1006 (mapcar (lambda (pkg) |
618 (if (eval (intern (concat (symbol-name (car pkg)) "-package"))) | 1007 (if (eval (intern (concat (symbol-name (car pkg)) "-package"))) |
619 (package-get-all (car pkg) nil)) | 1008 (package-get (car pkg) nil)) |
620 t) | 1009 t) |
621 package-get-base)) | 1010 package-get-base)) |
622 | 1011 |
623 (defun package-get-ever-installed-p (pkg &optional notused) | 1012 (defun package-get-ever-installed-p (pkg &optional notused) |
624 (string-match "-package$" (symbol-name pkg)) | 1013 (string-match "-package$" (symbol-name pkg)) |
627 (if (package-get-info-find-package | 1016 (if (package-get-info-find-package |
628 packages-package-list | 1017 packages-package-list |
629 (intern (substring (symbol-name pkg) 0 (match-beginning 0)))) | 1018 (intern (substring (symbol-name pkg) 0 (match-beginning 0)))) |
630 t))) | 1019 t))) |
631 | 1020 |
632 (defun package-get-file-installed-p (file &optional paths) | 1021 (defvar package-get-custom-groups nil |
633 "Return absolute-path of FILE if FILE exists in PATHS. | 1022 "List of package-get-custom groups") |
634 If PATHS is omitted, `load-path' is used." | 1023 |
635 (if (null paths) | 1024 (defun package-get-custom-add-entry (package props) |
636 (setq paths load-path) | 1025 (let* ((category (plist-get props 'category)) |
637 ) | 1026 (group (intern (concat category "-packages"))) |
638 (catch 'tag | 1027 (custom-var (intern (concat (symbol-name package) "-package"))) |
639 (let (path) | 1028 (description (plist-get props 'description))) |
640 (while paths | 1029 (when (not (memq group package-get-custom-groups)) |
641 (setq path (expand-file-name file (car paths))) | 1030 (setq package-get-custom-groups (cons package |
642 (if (file-exists-p path) | 1031 package-get-custom-groups)) |
643 (throw 'tag path) | 1032 (eval `(defgroup ,group nil |
644 ) | 1033 ,(concat category " package group") |
645 (setq paths (cdr paths)) | 1034 :group 'packages))) |
646 )))) | 1035 (eval `(defcustom ,custom-var nil |
647 | 1036 ,description |
648 (defun package-get-create-custom () | 1037 :group ',group |
649 "Creates a package customization file package-get-custom.el. | 1038 :initialize 'package-get-ever-installed-p |
650 Entries in the customization file are retrieved from package-get-base.el." | 1039 :type 'boolean)))) |
651 (interactive) | 1040 |
652 ;; Load a fresh copy | 1041 |
653 (let ((custom-buffer (find-file-noselect | |
654 (or (package-get-file-installed-p | |
655 "package-get-custom.el") | |
656 (expand-file-name | |
657 "package-get-custom.el" | |
658 (file-name-directory | |
659 (package-get-file-installed-p | |
660 "package-get-base.el")) | |
661 )))) | |
662 (pkg-groups nil)) | |
663 | |
664 ;; clear existing stuff | |
665 (delete-region (point-min custom-buffer) | |
666 (point-max custom-buffer) custom-buffer) | |
667 (insert-string "(require 'package-get)\n" custom-buffer) | |
668 | |
669 (mapcar (lambda (pkg) | |
670 (let ((category (plist-get (car (cdr pkg)) 'category))) | |
671 (or (memq (intern category) pkg-groups) | |
672 (progn | |
673 (setq pkg-groups (cons (intern category) pkg-groups)) | |
674 (insert-string | |
675 (concat "(defgroup " category "-packages nil\n" | |
676 " \"" category " package group\"\n" | |
677 " :group 'packages)\n\n") custom-buffer))) | |
678 | |
679 (insert-string | |
680 (concat "(defcustom " (symbol-name (car pkg)) | |
681 "-package nil \n" | |
682 " \"" (plist-get (car (cdr pkg)) 'description) "\"\n" | |
683 " :group '" category "-packages\n" | |
684 " :initialize 'package-get-ever-installed-p\n" | |
685 " :type 'boolean)\n\n") custom-buffer))) | |
686 package-get-base) custom-buffer) | |
687 ) | |
688 | |
689 ;; need this first to avoid infinite dependency loops | |
690 (provide 'package-get) | 1042 (provide 'package-get) |
691 | |
692 ;; potentially update the custom dependencies every time we load this | |
693 (let ((custom-file (package-get-file-installed-p "package-get-custom.el")) | |
694 (package-file (package-get-file-installed-p "package-get-base.el"))) | |
695 ;; update custom file if it doesn't exist | |
696 (if (or (not custom-file) | |
697 (and (< (car (nth 5 (file-attributes custom-file))) | |
698 (car (nth 5 (file-attributes package-file)))) | |
699 (< (car (nth 5 (file-attributes custom-file))) | |
700 (car (nth 5 (file-attributes package-file)))))) | |
701 (save-excursion | |
702 (message "generating package customizations...") | |
703 (set-buffer (package-get-create-custom)) | |
704 (save-buffer) | |
705 (message "generating package customizations...done"))) | |
706 (load "package-get-custom.el")) | |
707 | |
708 ;;; package-get.el ends here | 1043 ;;; package-get.el ends here |