Mercurial > hg > xemacs-beta
comparison lisp/package-get.el @ 371:cc15677e0335 r21-2b1
Import from CVS: tag r21-2b1
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:03:08 +0200 |
parents | 1d62742628b6 |
children | 6240c7796c7a |
comparison
equal
deleted
inserted
replaced
370:bd866891f083 | 371:cc15677e0335 |
---|---|
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> | |
8 ;; Keywords: internal | 6 ;; Keywords: internal |
9 | 7 |
10 ;; This file is part of XEmacs. | 8 ;; This file is part of XEmacs. |
11 | 9 |
12 ;; XEmacs is free software; you can redistribute it and/or modify it | 10 ;; XEmacs is free software; you can redistribute it and/or modify it |
28 | 26 |
29 ;;; Commentary: | 27 ;;; Commentary: |
30 | 28 |
31 ;; package-get - | 29 ;; package-get - |
32 ;; Retrieve a package and any other required packages from an archive | 30 ;; Retrieve a package and any other required packages from an archive |
33 ;; | |
34 ;; | |
35 ;; Note (JV): Most of this no longer aplies! | |
36 ;; | 31 ;; |
37 ;; The idea: | 32 ;; The idea: |
38 ;; A new XEmacs lisp-only release is generated with the following steps: | 33 ;; A new XEmacs lisp-only release is generated with the following steps: |
39 ;; 1. The maintainer runs some yet to be written program that | 34 ;; 1. The maintainer runs some yet to be written program that |
40 ;; generates all the dependency information. This should | 35 ;; generates all the dependency information. This should |
100 ;;; Change Log | 95 ;;; Change Log |
101 | 96 |
102 ;;; Code: | 97 ;;; Code: |
103 | 98 |
104 (require 'package-admin) | 99 (require 'package-admin) |
105 ;; (require 'package-get-base) | 100 (require 'package-get-base) |
106 | 101 |
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 | |
116 ;;;###autoload | |
117 (defvar package-get-base nil | 102 (defvar package-get-base nil |
118 "List of packages that are installed at this site. | 103 "List of packages that are installed at this site. |
119 For each element in the alist, car is the package name and the cdr is | 104 For each element in the alist, car is the package name and the cdr is |
120 a plist containing information about the package. Typical fields | 105 a plist containing information about the package. Typical fields |
121 kept in the plist are: | 106 kept in the plist are: |
158 For version information, it is assumed things are listed in most | 143 For version information, it is assumed things are listed in most |
159 recent to least recent -- in other words, the version names don't have to | 144 recent to least recent -- in other words, the version names don't have to |
160 be lexically ordered. It is debatable if it makes sense to have more than | 145 be lexically ordered. It is debatable if it makes sense to have more than |
161 one version of a package available.") | 146 one version of a package available.") |
162 | 147 |
163 (defcustom package-get-dir (temp-directory) | 148 (defvar package-get-dir (temp-directory) |
164 "*Where to store temporary files for staging." | 149 "*Where to store temporary files for staging.") |
165 :tag "Temporary directory" | 150 |
166 :type 'directory | 151 (defvar package-get-remote |
167 :group 'package-get) | 152 '( |
168 | 153 ("ftp.xemacs.org" "/pub/xemacs/beta/xemacs-21.0/packages/binary-packages") |
169 (define-widget 'host-name 'string | 154 ("ftp.xemacs.org" "/pub/xemacs/beta/xemacs-21.0/packages/single-file-packages") |
170 "A Host name." | 155 ("ftp.xemacs.org" "/pub/xemacs/package")) |
171 :tag "Host") | |
172 | |
173 (defcustom package-get-remote nil | |
174 "*List of remote sites to contact for downloading packages. | 156 "*List of remote sites to contact for downloading packages. |
175 List format is '(site-name directory-on-site). Each site is tried in | 157 List format is '(site-name directory-on-site). Each site is tried in |
176 order until the package is found. As a special case, `site-name' can be | 158 order until the package is found.") |
177 `nil', in which case `directory-on-site' is treated as a local directory." | 159 |
178 :tag "Package repository" | 160 (defvar package-get-remove-copy nil |
179 :type '(repeat (choice (list :tag "Local" (const :tag "Local" nil) directory ) | |
180 (list :tag "Remote" host-name directory) )) | |
181 :group 'package-get) | |
182 | |
183 ;;;###autoload | |
184 (defcustom package-get-download-sites | |
185 '( | |
186 ;; North America | |
187 ("Pre-Releases" "ftp.xemacs.org" "pub/xemacs/beta/experimental/packages") | |
188 ("xemacs.org" "ftp.xemacs.org" "pub/xemacs/packages") | |
189 ("crc.ca (Canada)" "ftp.crc.ca" "pub/packages/editors/xemacs/packages") | |
190 ("ualberta.ca (Canada)" "sunsite.ualberta.ca" "pub/Mirror/xemacs/packages") | |
191 ("uiuc.edu (United States)" "uiarchive.uiuc.edu" "pub/packages/xemacs/packages") | |
192 ("unc.edu (United States)" "metalab.unc.edu" "pub/packages/editors/xemacs/packages") | |
193 ("utk.edu (United States)" "ftp.sunsite.utk.edu" "pub/xemacs/packages") | |
194 | |
195 ;; South America | |
196 ("unicamp.br (Brazil)" "ftp.unicamp.br" "pub/xemacs/packages") | |
197 | |
198 ;; Europe | |
199 ("tuwien.ac.at (Austria)" "gd.tuwien.ac.at" "editors/xemacs/packages") | |
200 ("auc.dk (Denmark)" "sunsite.auc.dk" "pub/emacs/xemacs/packages") | |
201 ("doc.ic.ac.uk (England)" "sunsite.doc.ic.ac.uk" "packages/xemacs/packages") | |
202 ("mirror.ac.uk (England)" "ftp.mirror.ac.uk" "sites/ftp.xemacs.org/pub/xemacs/packages") | |
203 ("funet.fi (Finland)" "ftp.funet.fi" "pub/mirrors/ftp.xemacs.org/pub/tux/xemacs/packages") | |
204 ("pasteur.fr (France)" "ftp.pasteur.fr" "pub/computing/xemacs/packages") | |
205 ("tls.cena.fr (France)" "ftp.tls.cena.fr" "Emacs/xemacs/packages") | |
206 ("freenet.de (Germany)" "ftp.freenet.de" "pub/ftp.xemacs.org/tux/xemacs/packages") | |
207 ("tu-darmstadt.de (Germany)" "ftp.tu-darmstadt.de" "pub/editors/xemacs/packages") | |
208 ("kfki.hu (Hungary)" "ftp.kfki.hu" "pub/packages/xemacs/packages") | |
209 ("eunet.ie (Ireland)" "ftp.eunet.ie" "mirrors/ftp.xemacs.org/pub/xemacs/packages") | |
210 ("uniroma2.it (Italy)" "ftp.uniroma2.it" "unix/misc/dist/XEMACS/packages") | |
211 ("uio.no (Norway)" "sunsite.uio.no" "pub/xemacs/packages") | |
212 ("icm.edu.pl (Poland)" "ftp.icm.edu.pl" "pub/unix/editors/xemacs/packages") | |
213 ("srcc.msu.su (Russia)" "ftp.srcc.msu.su" "mirror/ftp.xemacs.org/packages") | |
214 ("sunet.se (Sweden)" "ftp.sunet.se" "pub/gnu/xemacs/packages") | |
215 ("cnlab-switch.ch (Switzerland)" "sunsite.cnlab-switch.ch" "mirror/xemacs/packages") | |
216 | |
217 ;; Asia | |
218 ("aist.go.jp (Japan)" "ring.aist.go.jp" "pub/text/xemacs/packages") | |
219 ("asahi-net.or.jp (Japan)" "ring.asahi-net.or.jp" "pub/text/xemacs/packages") | |
220 ("dti.ad.jp (Japan)" "ftp.dti.ad.jp" "pub/unix/editor/xemacs/packages") | |
221 ("jaist.ac.jp (Japan)" "ftp.jaist.ac.jp" "pub/GNU/xemacs/packages") | |
222 ("nucba.ac.jp (Japan)" "mirror.nucba.ac.jp" "mirror/xemacs/packages") | |
223 ("sut.ac.jp (Japan)" "sunsite.sut.ac.jp" "pub/archives/packages/xemacs/packages") | |
224 ("tsukuba.ac.jp (Japan)" "ftp.netlab.is.tsukuba.ac.jp" "pub/GNU/xemacs/packages") | |
225 ("kreonet.re.kr (Korea)" "ftp.kreonet.re.kr" "pub/tools/emacs/xemacs/packages") | |
226 ("nctu.edu.tw (Taiwan)" "coda.nctu.edu.tw" "Editors/xemacs/packages") | |
227 | |
228 ;; Africa | |
229 ("sun.ac.za (South Africa)" "ftp.sun.ac.za" "xemacs/packages") | |
230 | |
231 ;; Middle East | |
232 ("isu.net.sa (Saudi Arabia)" "ftp.isu.net.sa" "pub/mirrors/ftp.xemacs.org/packages") | |
233 | |
234 ;; Australia | |
235 ("aarnet.edu.au (Australia)" "mirror.aarnet.edu.au" "pub/xemacs/packages") | |
236 ) | |
237 "*List of remote sites available for downloading packages. | |
238 List format is '(site-description site-name directory-on-site). | |
239 SITE-DESCRIPTION is a textual description of the site. SITE-NAME | |
240 is the internet address of the download site. DIRECTORY-ON-SITE | |
241 is the directory on the site in which packages may be found. | |
242 This variable is used to initialize `package-get-remote', the | |
243 variable actually used to specify package download sites." | |
244 :tag "Package download sites" | |
245 :type '(repeat (list (string :tag "Name") host-name directory)) | |
246 :group 'package-get) | |
247 | |
248 (defcustom package-get-remove-copy t | |
249 "*After copying and installing a package, if this is T, then remove the | 161 "*After copying and installing a package, if this is T, then remove the |
250 copy. Otherwise, keep it around." | 162 copy. Otherwise, keep it around.") |
251 :type 'boolean | |
252 :group 'package-get) | |
253 | |
254 ;; #### it may make sense for this to be a list of names. | |
255 ;; #### also, should we rename "*base*" to "*index*" or "*db*"? | |
256 ;; "base" is a pretty poor name. | |
257 (defcustom package-get-base-filename "package-index.LATEST.pgp" | |
258 "*Name of the default package-get database file. | |
259 This may either be a relative path, in which case it is interpreted | |
260 with respect to `package-get-remote', or an absolute path." | |
261 :type 'file | |
262 :group 'package-get) | |
263 | |
264 (defcustom package-get-always-update nil | |
265 "*If Non-nil always make sure we are using the latest package index (base). | |
266 Otherwise respect the `force-current' argument of `package-get-require-base'." | |
267 :type 'boolean | |
268 :group 'package-get) | |
269 | |
270 (defcustom package-get-require-signed-base-updates nil | |
271 "*If set to a non-nil value, require explicit user confirmation for updates | |
272 to the package-get database which cannot have their signature verified via PGP. | |
273 When nil, updates which are not PGP signed are allowed without confirmation." | |
274 :type 'boolean | |
275 :group 'package-get) | |
276 | |
277 (defvar package-get-was-current nil | |
278 "Non-nil we did our best to fetch a current database.") | |
279 | |
280 | |
281 ;Shouldn't this be in package-ui? | |
282 ;;;###autoload | |
283 (defun package-get-download-menu () | |
284 "Build the `Add Download Site' menu." | |
285 (mapcar (lambda (site) | |
286 (vector (car site) | |
287 `(if (member (quote ,(cdr site)) | |
288 package-get-remote) | |
289 (setq package-get-remote | |
290 (delete (quote ,(cdr site)) package-get-remote)) | |
291 (package-ui-add-site (quote ,(cdr site)))) | |
292 :style 'toggle | |
293 :selected `(member (quote ,(cdr site)) | |
294 package-get-remote))) | |
295 package-get-download-sites)) | |
296 | |
297 ;;;###autoload | |
298 (defun package-get-require-base (&optional force-current) | |
299 "Require that a package-get database has been loaded. | |
300 If the optional FORCE-CURRENT argument or the value of | |
301 `package-get-always-update' is Non-nil, try to update the database | |
302 from a location in `package-get-remote'. Otherwise a local copy is used | |
303 if available and remote access is never done. | |
304 | |
305 Please use FORCE-CURRENT only when the user is explictly dealing with packages | |
306 and remote access is likely in the near future." | |
307 (setq force-current (or force-current package-get-always-update)) | |
308 (unless (and (boundp 'package-get-base) | |
309 package-get-base | |
310 (or (not force-current) package-get-was-current)) | |
311 (package-get-update-base nil force-current)) | |
312 (if (or (not (boundp 'package-get-base)) | |
313 (not package-get-base)) | |
314 (error "Package-get database not loaded") | |
315 (setq package-get-was-current force-current))) | |
316 | |
317 (defconst package-get-pgp-signed-begin-line "^-----BEGIN PGP SIGNED MESSAGE-----" | |
318 "Text for start of PGP signed messages.") | |
319 (defconst package-get-pgp-signature-begin-line "^-----BEGIN PGP SIGNATURE-----" | |
320 "Text for beginning of PGP signature.") | |
321 (defconst package-get-pgp-signature-end-line "^-----END PGP SIGNATURE-----" | |
322 "Text for end of PGP signature.") | |
323 | |
324 ;;;###autoload | |
325 (defun package-get-update-base-entry (entry) | |
326 "Update an entry in `package-get-base'." | |
327 (let ((existing (assq (car entry) package-get-base))) | |
328 (if existing | |
329 (setcdr existing (cdr entry)) | |
330 (setq package-get-base (cons entry package-get-base)) | |
331 (package-get-custom-add-entry (car entry) (car (cdr entry)))))) | |
332 | |
333 (defun package-get-locate-file (file &optional nil-if-not-found no-remote) | |
334 "Locate an existing FILE with respect to `package-get-remote'. | |
335 If FILE is an absolute path or is not found, simply return FILE. | |
336 If optional argument NIL-IF-NOT-FOUND is non-nil, return nil | |
337 if FILE can not be located. | |
338 If NO-REMOTE is non-nil never search remote locations." | |
339 (if (file-name-absolute-p file) | |
340 file | |
341 (let ((entries package-get-remote) | |
342 (expanded nil)) | |
343 (while entries | |
344 (unless (and no-remote (caar entries)) | |
345 (let ((expn (package-get-remote-filename (car entries) file))) | |
346 (if (and expn (file-exists-p expn)) | |
347 (setq entries nil | |
348 expanded expn)))) | |
349 (setq entries (cdr entries))) | |
350 (or expanded | |
351 (and (not nil-if-not-found) | |
352 file))))) | |
353 | |
354 (defun package-get-locate-index-file (no-remote) | |
355 "Locate the package-get index file. Do not return remote paths if NO-REMOTE | |
356 is non-nil." | |
357 (or (package-get-locate-file package-get-base-filename t no-remote) | |
358 (locate-data-file package-get-base-filename) | |
359 package-get-base-filename)) | |
360 | |
361 (defvar package-get-user-package-location | |
362 (concat "~" init-file-user user-init-directory)) | |
363 | |
364 (defun package-get-maybe-save-index (filename) | |
365 "Offer to save the current buffer as the local package index file, | |
366 if different." | |
367 (let ((location (package-get-locate-index-file t))) | |
368 (unless (and filename (equal filename location)) | |
369 (unless (equal (md5 (current-buffer)) | |
370 (with-temp-buffer | |
371 (insert-file-contents location) | |
372 (md5 (current-buffer)))) | |
373 (unless (file-writable-p location) | |
374 (setq location (expand-file-name package-get-base-filename | |
375 (expand-file-name "etc/" package-get-user-package-location)))) | |
376 (when (y-or-n-p (concat "Update package index in " location "? ")) | |
377 (let ((coding-system-for-write 'binary)) | |
378 (write-file location))))))) | |
379 | |
380 | |
381 ;;;###autoload | |
382 (defun package-get-update-base (&optional db-file force-current) | |
383 "Update the package-get database file with entries from DB-FILE. | |
384 Unless FORCE-CURRENT is non-nil never try to update the database." | |
385 (interactive | |
386 (let ((dflt (package-get-locate-index-file nil))) | |
387 (list (read-file-name "Load package-get database: " | |
388 (file-name-directory dflt) | |
389 dflt | |
390 t | |
391 (file-name-nondirectory dflt))))) | |
392 (setq db-file (expand-file-name (or db-file | |
393 (package-get-locate-index-file | |
394 (not force-current))))) | |
395 (if (not (file-exists-p db-file)) | |
396 (error "Package-get database file `%s' does not exist" db-file)) | |
397 (if (not (file-readable-p db-file)) | |
398 (error "Package-get database file `%s' not readable" db-file)) | |
399 (let ((buf (get-buffer-create "*package database*"))) | |
400 (unwind-protect | |
401 (save-excursion | |
402 (set-buffer buf) | |
403 (erase-buffer buf) | |
404 (insert-file-contents-literally db-file) | |
405 (package-get-update-base-from-buffer buf) | |
406 (if (file-remote-p db-file) | |
407 (package-get-maybe-save-index db-file))) | |
408 (kill-buffer buf)))) | |
409 | |
410 ;;;###autoload | |
411 (defun package-get-update-base-from-buffer (&optional buf) | |
412 "Update the package-get database with entries from BUFFER. | |
413 BUFFER defaults to the current buffer. This command can be | |
414 used interactively, for example from a mail or news buffer." | |
415 (interactive) | |
416 (setq buf (or buf (current-buffer))) | |
417 (let (content-beg content-end beg end) | |
418 (save-excursion | |
419 (set-buffer buf) | |
420 (goto-char (point-min)) | |
421 (setq content-beg (point)) | |
422 (setq content-end (save-excursion (goto-char (point-max)) (point))) | |
423 (when (re-search-forward package-get-pgp-signed-begin-line nil t) | |
424 (setq beg (match-beginning 0)) | |
425 (setq content-beg (match-end 0))) | |
426 (when (re-search-forward package-get-pgp-signature-begin-line nil t) | |
427 (setq content-end (match-beginning 0))) | |
428 (when (re-search-forward package-get-pgp-signature-end-line nil t) | |
429 (setq end (point))) | |
430 (if (not (and content-beg content-end beg end)) | |
431 (or (not package-get-require-signed-base-updates) | |
432 (yes-or-no-p "Package-get entries not PGP signed, continue? ") | |
433 (error "Package-get database not updated"))) | |
434 (if (and content-beg content-end beg end) | |
435 (if (not (condition-case nil | |
436 (or (fboundp 'mc-pgp-verify-region) | |
437 (load-library "mc-pgp") | |
438 (fboundp 'mc-pgp-verify-region)) | |
439 (error nil))) | |
440 (or (not package-get-require-signed-base-updates) | |
441 (yes-or-no-p | |
442 "No mailcrypt; can't verify package-get DB signature, continue? ") | |
443 (error "Package-get database not updated")))) | |
444 (if (and beg end | |
445 (fboundp 'mc-pgp-verify-region) | |
446 (or (not | |
447 (condition-case err | |
448 (mc-pgp-verify-region beg end) | |
449 (file-error | |
450 (and (string-match "No such file" (nth 2 err)) | |
451 (or (not package-get-require-signed-base-updates) | |
452 (yes-or-no-p | |
453 (concat "Can't find PGP, continue without " | |
454 "package-get DB verification? "))))) | |
455 (t nil))))) | |
456 (error "Package-get PGP signature failed to verify")) | |
457 ;; ToDo: We shoud call package-get-maybe-save-index on the region | |
458 (package-get-update-base-entries content-beg content-end) | |
459 (message "Updated package-get database")))) | |
460 | |
461 (defun package-get-update-base-entries (beg end) | |
462 "Update the package-get database with the entries found between | |
463 BEG and END in the current buffer." | |
464 (save-excursion | |
465 (goto-char beg) | |
466 (if (not (re-search-forward "^(package-get-update-base-entry" nil t)) | |
467 (error "Buffer does not contain package-get database entries")) | |
468 (beginning-of-line) | |
469 (let ((count 0)) | |
470 (while (and (< (point) end) | |
471 (re-search-forward "^(package-get-update-base-entry" nil t)) | |
472 (beginning-of-line) | |
473 (let ((entry (read (current-buffer)))) | |
474 (if (or (not (consp entry)) | |
475 (not (eq (car entry) 'package-get-update-base-entry))) | |
476 (error "Invalid package-get database entry found")) | |
477 (package-get-update-base-entry | |
478 (car (cdr (car (cdr entry))))) | |
479 (setq count (1+ count)))) | |
480 (message "Got %d package-get database entries" count)))) | |
481 | |
482 ;;;###autoload | |
483 (defun package-get-save-base (file) | |
484 "Write the package-get database to FILE. | |
485 | |
486 Note: This database will be unsigned of course." | |
487 (interactive "FSave package-get database to: ") | |
488 (package-get-require-base t) | |
489 (let ((buf (get-buffer-create "*package database*"))) | |
490 (unwind-protect | |
491 (save-excursion | |
492 (set-buffer buf) | |
493 (erase-buffer buf) | |
494 (goto-char (point-min)) | |
495 (let ((entries package-get-base) entry plist) | |
496 (insert ";; Package Index file -- Do not edit manually.\n") | |
497 (insert ";;;@@@\n") | |
498 (while entries | |
499 (setq entry (car entries)) | |
500 (setq plist (car (cdr entry))) | |
501 (insert "(package-get-update-base-entry (quote\n") | |
502 (insert (format "(%s\n" (symbol-name (car entry)))) | |
503 (while plist | |
504 (insert (format " %s%s %S\n" | |
505 (if (eq plist (car (cdr entry))) "(" " ") | |
506 (symbol-name (car plist)) | |
507 (car (cdr plist)))) | |
508 (setq plist (cdr (cdr plist)))) | |
509 (insert "))\n))\n;;;@@@\n") | |
510 (setq entries (cdr entries)))) | |
511 (insert ";; Package Index file ends here\n") | |
512 (write-region (point-min) (point-max) file)) | |
513 (kill-buffer buf)))) | |
514 | |
515 (defun package-get-interactive-package-query (get-version package-symbol) | |
516 "Perform interactive querying for package and optional version. | |
517 Query for a version if GET-VERSION is non-nil. Return package name as | |
518 a symbol instead of a string if PACKAGE-SYMBOL is non-nil. | |
519 The return value is suitable for direct passing to `interactive'." | |
520 (package-get-require-base t) | |
521 (let ( (table (mapcar '(lambda (item) | |
522 (let ( (name (symbol-name (car item))) ) | |
523 (cons name name) | |
524 )) | |
525 package-get-base)) | |
526 package package-symbol default-version version) | |
527 (save-window-excursion | |
528 (setq package (completing-read "Package: " table nil t)) | |
529 (setq package-symbol (intern package)) | |
530 (if get-version | |
531 (progn | |
532 (setq default-version | |
533 (package-get-info-prop | |
534 (package-get-info-version | |
535 (package-get-info-find-package package-get-base | |
536 package-symbol) nil) | |
537 'version)) | |
538 (while (string= | |
539 (setq version (read-string "Version: " default-version)) | |
540 "") | |
541 ) | |
542 (if package-symbol | |
543 (list package-symbol version) | |
544 (list package version)) | |
545 ) | |
546 (if package-symbol | |
547 (list package-symbol) | |
548 (list package))) | |
549 ))) | |
550 | |
551 ;;;###autoload | |
552 (defun package-get-delete-package (package &optional pkg-topdir) | |
553 "Delete an installation of PACKAGE below directory PKG-TOPDIR. | |
554 PACKAGE is a symbol, not a string. | |
555 This is just an interactive wrapper for `package-admin-delete-binary-package'." | |
556 (interactive (package-get-interactive-package-query nil t)) | |
557 (package-admin-delete-binary-package package pkg-topdir)) | |
558 | 163 |
559 ;;;###autoload | 164 ;;;###autoload |
560 (defun package-get-update-all () | 165 (defun package-get-update-all () |
561 "Fetch and install the latest versions of all currently installed packages." | 166 "Fetch and install the latest versions of all currently installed packages." |
562 (interactive) | 167 (interactive) |
563 (package-get-require-base t) | |
564 ;; Load a fresh copy | 168 ;; Load a fresh copy |
565 (catch 'exit | 169 (mapcar (lambda (pkg) |
566 (mapcar (lambda (pkg) | 170 (package-get-all |
567 (if (not (package-get (car pkg) nil 'never)) | 171 (car pkg) nil)) |
568 (throw 'exit nil) ;; Bail out if error detected | 172 packages-package-list)) |
569 )) | |
570 packages-package-list))) | |
571 | 173 |
572 ;;;###autoload | 174 ;;;###autoload |
573 (defun package-get-all (package version &optional fetched-packages install-dir) | 175 (defun package-get-all (package version &optional fetched-packages) |
574 "Fetch PACKAGE with VERSION and all other required packages. | 176 "Fetch PACKAGE with VERSION and all other required packages. |
575 Uses `package-get-base' to determine just what is required and what | 177 Uses `package-get-base' to determine just what is required and what |
576 package provides that functionality. If VERSION is nil, retrieves | 178 package provides that functionality. If VERSION is nil, retrieves |
577 latest version. Optional argument FETCHED-PACKAGES is used to keep | 179 latest version. Optional argument FETCHED-PACKAGES is used to keep |
578 track of packages already fetched. Optional argument INSTALL-DIR, | 180 track of packages already fetched." |
579 if non-nil, specifies the package directory where fetched packages | 181 (interactive "sPackage: \nsVersion: ") |
580 should be installed. | |
581 | |
582 Returns nil upon error." | |
583 (interactive (package-get-interactive-package-query t nil)) | |
584 (let* ((the-package (package-get-info-find-package package-get-base | 182 (let* ((the-package (package-get-info-find-package package-get-base |
585 package)) | 183 package)) |
586 (this-package (package-get-info-version | 184 (this-package (package-get-info-version |
587 the-package version)) | 185 the-package version)) |
588 (this-requires (package-get-info-prop this-package 'requires)) | 186 (this-requires (package-get-info-prop this-package 'requires)) |
589 ) | 187 ) |
590 (catch 'exit | 188 (setq version (package-get-info-prop this-package 'version)) |
591 (setq version (package-get-info-prop this-package 'version)) | 189 (unless (package-get-installedp package version) |
592 (unless (package-get-installedp package version) | 190 (package-get package version)) |
593 (if (not (package-get package version nil install-dir)) | 191 (setq fetched-packages |
594 (progn | 192 (append (list package) |
595 (setq fetched-packages nil) | 193 (package-get-info-prop this-package 'provides) |
596 (throw 'exit nil)))) | 194 fetched-packages)) |
597 (setq fetched-packages | 195 ;; grab everything that this package requires plus recursively |
598 (append (list package) | 196 ;; grab everything that the requires require. Keep track |
599 (package-get-info-prop this-package 'provides) | 197 ;; in `fetched-packages' the list of things provided -- this |
600 fetched-packages)) | 198 ;; keeps us from going into a loop |
601 ;; grab everything that this package requires plus recursively | 199 (while this-requires |
602 ;; grab everything that the requires require. Keep track | 200 (if (not (member (car this-requires) fetched-packages)) |
603 ;; in `fetched-packages' the list of things provided -- this | 201 (let* ((reqd-package (package-get-package-provider |
604 ;; keeps us from going into a loop | 202 (car this-requires))) |
605 (while this-requires | 203 (reqd-version (cadr reqd-package)) |
606 (if (not (member (car this-requires) fetched-packages)) | 204 (reqd-name (car reqd-package))) |
607 (let* ((reqd-package (package-get-package-provider | 205 (if (null reqd-name) |
608 (car this-requires) t)) | 206 (error "Unable to find a provider for %s" (car this-requires))) |
609 (reqd-version (cadr reqd-package)) | 207 (setq fetched-packages |
610 (reqd-name (car reqd-package))) | 208 (package-get-all reqd-name reqd-version fetched-packages))) |
611 (if (null reqd-name) | 209 ) |
612 (error "Unable to find a provider for %s" | 210 (setq this-requires (cdr this-requires))) |
613 (car this-requires))) | |
614 (if (not (setq fetched-packages | |
615 (package-get-all reqd-name reqd-version | |
616 fetched-packages | |
617 install-dir))) | |
618 (throw 'exit nil))) | |
619 ) | |
620 (setq this-requires (cdr this-requires))) | |
621 ) | |
622 fetched-packages | 211 fetched-packages |
623 )) | 212 )) |
624 | 213 |
625 ;;;###autoload | 214 ;;;###autoload |
626 (defun package-get-dependencies (packages) | 215 (defun package-get (package &optional version conflict) |
627 "Compute dependencies for PACKAGES. | |
628 Uses `package-get-base' to determine just what is required and what | |
629 package provides that functionality. Returns the list of packages | |
630 required by PACKAGES." | |
631 (package-get-require-base t) | |
632 (let ((orig-packages packages) | |
633 dependencies provided) | |
634 (while packages | |
635 (let* ((package (car packages)) | |
636 (the-package (package-get-info-find-package | |
637 package-get-base package)) | |
638 (this-package (package-get-info-version | |
639 the-package nil)) | |
640 (this-requires (package-get-info-prop this-package 'requires)) | |
641 (new-depends (set-difference | |
642 (mapcar | |
643 #'(lambda (reqd) | |
644 (let* ((reqd-package (package-get-package-provider reqd)) | |
645 (reqd-version (cadr reqd-package)) | |
646 (reqd-name (car reqd-package))) | |
647 (if (null reqd-name) | |
648 (error "Unable to find a provider for %s" reqd)) | |
649 reqd-name)) | |
650 this-requires) | |
651 dependencies)) | |
652 (this-provides (package-get-info-prop this-package 'provides))) | |
653 (setq dependencies | |
654 (union dependencies new-depends)) | |
655 (setq provided | |
656 (union provided (union (list package) this-provides))) | |
657 (setq packages | |
658 (union new-depends (cdr packages))))) | |
659 (set-difference dependencies orig-packages))) | |
660 | |
661 (defun package-get-load-package-file (lispdir file) | |
662 (let (pathname) | |
663 (setq pathname (expand-file-name file lispdir)) | |
664 (condition-case err | |
665 (progn | |
666 (load pathname t) | |
667 t) | |
668 (t | |
669 (message "Error loading package file \"%s\" %s!" pathname err) | |
670 nil)) | |
671 )) | |
672 | |
673 (defun package-get-init-package (lispdir) | |
674 "Initialize the package. | |
675 This really assumes that the package has never been loaded. Updating | |
676 a newer package can cause problems, due to old, obsolete functions in | |
677 the old package. | |
678 | |
679 Return `t' upon complete success, `nil' if any errors occurred." | |
680 (progn | |
681 (if (and lispdir | |
682 (file-accessible-directory-p lispdir)) | |
683 (progn | |
684 ;; Add lispdir to load-path if it doesn't already exist. | |
685 ;; NOTE: this does not take symlinks, etc., into account. | |
686 (if (let ( (dirs load-path) ) | |
687 (catch 'done | |
688 (while dirs | |
689 (if (string-equal (car dirs) lispdir) | |
690 (throw 'done nil)) | |
691 (setq dirs (cdr dirs)) | |
692 ) | |
693 t)) | |
694 (setq load-path (cons lispdir load-path))) | |
695 (if (not (package-get-load-package-file lispdir "auto-autoloads")) | |
696 (package-get-load-package-file lispdir "_pkg")) | |
697 t) | |
698 nil) | |
699 )) | |
700 | |
701 ;;;###autoload | |
702 (defun package-get (package &optional version conflict install-dir) | |
703 "Fetch PACKAGE from remote site. | 216 "Fetch PACKAGE from remote site. |
704 Optional arguments VERSION indicates which version to retrieve, nil | 217 Optional arguments VERSION indicates which version to retrieve, nil |
705 means most recent version. CONFLICT indicates what happens if the | 218 means most recent version. CONFLICT indicates what happens if the |
706 package is already installed. Valid values for CONFLICT are: | 219 package is already installed. Valid values for CONFLICT are: |
707 'always always retrieve the package even if it is already installed | 220 'always always retrieve the package even if it is already installed |
708 'never do not retrieve the package if it is installed. | 221 'never do not retrieve the package if it is installed. |
709 INSTALL-DIR, if non-nil, specifies the package directory where | |
710 fetched packages should be installed. | |
711 | 222 |
712 The value of `package-get-base' is used to determine what files should | 223 The value of `package-get-base' is used to determine what files should |
713 be retrieved. The value of `package-get-remote' is used to determine | 224 be retrieved. The value of `package-get-remote' is used to determine |
714 where a package should be retrieved from. The sites are tried in | 225 where a package should be retrieved from. The sites are tried in |
715 order so one is better off listing easily reached sites first. | 226 order so one is better off listing easily reached sites first. |
716 | 227 |
717 Once the package is retrieved, its md5 checksum is computed. If that | 228 Once the package is retrieved, its md5 checksum is computed. If that |
718 sum does not match that stored in `package-get-base' for this version | 229 sum does not match that stored in `package-get-base' for this version |
719 of the package, an error is signalled. | 230 of the package, an error is signalled." |
720 | 231 (interactive "xPackage List: ") |
721 Returns `t' upon success, the symbol `error' if the package was | |
722 successfully installed but errors occurred during initialization, or | |
723 `nil' upon error." | |
724 (interactive (package-get-interactive-package-query nil t)) | |
725 (catch 'skip-update | |
726 (let* ((this-package | 232 (let* ((this-package |
727 (package-get-info-version | 233 (package-get-info-version |
728 (package-get-info-find-package package-get-base | 234 (package-get-info-find-package package-get-base |
729 package) version)) | 235 package) version)) |
730 (latest (package-get-info-prop this-package 'version)) | |
731 (installed (package-get-key package :version)) | |
732 (this-requires (package-get-info-prop this-package 'requires)) | |
733 (found nil) | 236 (found nil) |
734 (search-dirs package-get-remote) | 237 (search-dirs package-get-remote) |
735 (base-filename (package-get-info-prop this-package 'filename)) | 238 (filename (package-get-info-prop this-package 'filename))) |
736 (package-status t) | |
737 filenames full-package-filename) | |
738 (if (null this-package) | 239 (if (null this-package) |
739 (if package-get-remote | 240 (error "Couldn't find package %s with version %s" |
740 (error "Couldn't find package %s with version %s" | 241 package version)) |
741 package version) | 242 (if (null filename) |
742 (error "No download sites or local package locations specified."))) | |
743 (if (null base-filename) | |
744 (error "No filename associated with package %s, version %s" | 243 (error "No filename associated with package %s, version %s" |
745 package version)) | 244 package version)) |
746 (setq install-dir | 245 (setq version (package-get-info-prop this-package 'version)) |
747 (package-admin-get-install-dir package install-dir | |
748 (or (eq package 'mule-base) (memq 'mule-base this-requires)))) | |
749 | |
750 ;; If they asked for the latest using version=nil, don't get an older | |
751 ;; version than we already have. | |
752 (if installed | |
753 (if (> (if (stringp installed) | |
754 (string-to-number installed) | |
755 installed) | |
756 (if (stringp latest) | |
757 (string-to-number latest) | |
758 latest)) | |
759 (if (not (null version)) | |
760 (warn "Installing %s package version %s, you had a newer version %s" | |
761 package latest installed) | |
762 (warn "Skipping %s package, you have a newer version %s" | |
763 package installed) | |
764 (throw 'skip-update t)))) | |
765 | |
766 ;; Contrive a list of possible package filenames. | |
767 ;; Ugly. Is there a better way to do this? | |
768 (setq filenames (cons base-filename nil)) | |
769 (if (string-match "^\\(..*\\)\.tar\.gz$" base-filename) | |
770 (setq filenames (append filenames | |
771 (list (concat (match-string 1 base-filename) | |
772 ".tgz"))))) | |
773 | |
774 (setq version latest) | |
775 (unless (and (eq conflict 'never) | 246 (unless (and (eq conflict 'never) |
776 (package-get-installedp package version)) | 247 (package-get-installedp package version)) |
777 ;; Find the package from the search list in package-get-remote | 248 ;; Find the package from search list in package-get-remote |
778 ;; and copy it into the staging directory. Then validate | 249 ;; and copy it into the staging directory. Then validate |
779 ;; the checksum. Finally, install the package. | 250 ;; the checksum. Finally, install the package. |
780 (catch 'done | 251 (while (and search-dirs |
781 (let (search-filenames current-dir-entry host dir current-filename | 252 (not (file-exists-p (package-get-staging-dir filename)))) |
782 dest-filename) | 253 (if (file-exists-p (package-get-remote-filename |
783 ;; In each search directory ... | 254 (car search-dirs) filename)) |
784 (while search-dirs | 255 (copy-file (package-get-remote-filename (car search-dirs) filename) |
785 (setq current-dir-entry (car search-dirs) | 256 (package-get-staging-dir filename)) |
786 host (car current-dir-entry) | 257 (setq search-dirs (cdr search-dirs)) |
787 dir (car (cdr current-dir-entry)) | |
788 search-filenames filenames | |
789 ) | |
790 | |
791 ;; Look for one of the possible package filenames ... | |
792 (while search-filenames | |
793 (setq current-filename (car search-filenames) | |
794 dest-filename (package-get-staging-dir current-filename)) | |
795 (cond | |
796 ;; No host means look on the current system. | |
797 ( (null host) | |
798 (setq full-package-filename | |
799 (substitute-in-file-name | |
800 (expand-file-name current-filename | |
801 (file-name-as-directory dir)))) | |
802 ) | |
803 | |
804 ;; If it's already on the disk locally, and the size is | |
805 ;; greater than zero ... | |
806 ( (and (file-exists-p dest-filename) | |
807 (let (attrs) | |
808 ;; file-attributes could return -1 for LARGE files, | |
809 ;; but, hopefully, packages won't be that large. | |
810 (and (setq attrs (file-attributes dest-filename)) | |
811 (> (nth 7 attrs) 0)))) | |
812 (setq full-package-filename dest-filename) | |
813 ) | |
814 | |
815 ;; If the file exists on the remote system ... | |
816 ( (file-exists-p (package-get-remote-filename | |
817 current-dir-entry current-filename)) | |
818 ;; Get it | |
819 (setq full-package-filename dest-filename) | |
820 (message "Retrieving package `%s' ..." | |
821 current-filename) | |
822 (sit-for 0) | |
823 (copy-file (package-get-remote-filename current-dir-entry | |
824 current-filename) | |
825 full-package-filename t) | |
826 ) | |
827 ) | |
828 | |
829 ;; If we found it, we're done. | |
830 (if (and full-package-filename | |
831 (file-exists-p full-package-filename)) | |
832 (throw 'done nil)) | |
833 ;; Didn't find it. Try the next possible filename. | |
834 (setq search-filenames (cdr search-filenames)) | |
835 ) | |
836 ;; Try looking in the next possible directory ... | |
837 (setq search-dirs (cdr search-dirs)) | |
838 ) | |
839 )) | 258 )) |
840 | 259 (if (not (file-exists-p (package-get-staging-dir filename))) |
841 (if (or (not full-package-filename) | 260 (error "Unable to find file %s" filename)) |
842 (not (file-exists-p full-package-filename))) | |
843 (if package-get-remote | |
844 (error "Unable to find file %s" base-filename) | |
845 (error | |
846 "No download sites or local package locations specified."))) | |
847 ;; Validate the md5 checksum | 261 ;; Validate the md5 checksum |
848 ;; Doing it with XEmacs removes the need for an external md5 program | 262 ;; Doing it with XEmacs removes the need for an external md5 program |
849 (message "Validating checksum for `%s'..." package) (sit-for 0) | |
850 (with-temp-buffer | 263 (with-temp-buffer |
851 (insert-file-contents-literally full-package-filename) | 264 ;; What ever happened to i-f-c-literally |
852 (if (not (string= (md5 (current-buffer)) | 265 (let (file-name-handler-alist) |
853 (package-get-info-prop this-package | 266 (insert-file-contents-internal (package-get-staging-dir filename))) |
854 'md5sum))) | 267 (if (not (string= (md5 (current-buffer)) |
855 (error "Package %s does not match md5 checksum" base-filename))) | 268 (package-get-info-prop this-package |
856 (package-admin-delete-binary-package package install-dir) | 269 'md5sum))) |
857 | 270 (error "Package %s does not match md5 checksum" filename))) |
858 (message "Installing package `%s' ..." package) (sit-for 0) | 271 (message "Retrieved package %s" filename) (sit-for 0) |
859 (let ((status | 272 (let ((status |
860 (package-admin-add-binary-package full-package-filename | 273 (package-admin-add-binary-package |
861 install-dir))) | 274 (package-get-staging-dir filename)))) |
862 (if (= status 0) | 275 (when (not (= status 0)) |
863 (progn | 276 (message "Package failed.") |
864 ;; clear messages so that only messages from | 277 (switch-to-buffer package-admin-temp-buffer))) |
865 ;; package-get-init-package are seen, below. | 278 (sit-for 0) |
866 (clear-message) | 279 (message "Added package") (sit-for 0) |
867 (if (package-get-init-package (package-admin-get-lispdir | |
868 install-dir package)) | |
869 (progn | |
870 (message "Added package `%s'" package) | |
871 (sit-for 0) | |
872 ) | |
873 (progn | |
874 ;; display message only if there isn't already one. | |
875 (if (not (current-message)) | |
876 (progn | |
877 (message "Added package `%s' (errors occurred)" | |
878 package) | |
879 (sit-for 0) | |
880 )) | |
881 (if package-status | |
882 (setq package-status 'errors)) | |
883 )) | |
884 ) | |
885 (message "Installation of package %s failed." base-filename) | |
886 (sit-for 0) | |
887 (switch-to-buffer package-admin-temp-buffer) | |
888 (setq package-status nil) | |
889 )) | |
890 (setq found t)) | 280 (setq found t)) |
891 (if (and found package-get-remove-copy) | 281 (if (and found package-get-remove-copy) |
892 (delete-file full-package-filename)) | 282 (delete-file (package-get-staging-dir filename))) |
893 package-status | 283 )) |
894 ))) | |
895 | 284 |
896 (defun package-get-info-find-package (which name) | 285 (defun package-get-info-find-package (which name) |
897 "Look in WHICH for the package called NAME and return all the info | 286 "Look in WHICH for the package called NAME and return all the info |
898 associated with it. See `package-get-base' for info on the format | 287 associated with it. See `package-get-base' for info on the format |
899 returned. | 288 returned. |
913 "In PACKAGE, return the plist associated with a particular VERSION of the | 302 "In PACKAGE, return the plist associated with a particular VERSION of the |
914 package. PACKAGE is typically as returned by | 303 package. PACKAGE is typically as returned by |
915 `package-get-info-find-package'. If VERSION is nil, then return the | 304 `package-get-info-find-package'. If VERSION is nil, then return the |
916 first (aka most recent) version. Use `package-get-info-find-prop' | 305 first (aka most recent) version. Use `package-get-info-find-prop' |
917 to retrieve a particular property from the value returned by this." | 306 to retrieve a particular property from the value returned by this." |
918 (interactive (package-get-interactive-package-query t t)) | 307 (interactive "xPackage Info: \nsVersion: ") |
919 (while (and version package (not (string= (plist-get (car package) 'version) version))) | 308 (while (and version package (not (string= (plist-get (car package) 'version) version))) |
920 (setq package (cdr package))) | 309 (setq package (cdr package))) |
921 (if package (car package))) | 310 (if package (car package))) |
922 | 311 |
923 (defun package-get-info-prop (package-version property) | 312 (defun package-get-info-prop (package-version property) |
954 Use `package-get-dir' for directory to store stuff. | 343 Use `package-get-dir' for directory to store stuff. |
955 Creates `package-get-dir' it it doesn't exist." | 344 Creates `package-get-dir' it it doesn't exist." |
956 (interactive "FPackage filename: ") | 345 (interactive "FPackage filename: ") |
957 (if (not (file-exists-p package-get-dir)) | 346 (if (not (file-exists-p package-get-dir)) |
958 (make-directory package-get-dir)) | 347 (make-directory package-get-dir)) |
959 (expand-file-name | 348 (concat |
960 (file-name-nondirectory (or (and (fboundp 'efs-ftp-path) | 349 (file-name-as-directory package-get-dir) |
961 (nth 2 (efs-ftp-path filename))) | 350 (file-name-nondirectory (or (nth 2 (efs-ftp-path filename)) filename)))) |
962 filename)) | 351 |
963 (file-name-as-directory package-get-dir))) | |
964 | 352 |
965 (defun package-get-remote-filename (search filename) | 353 (defun package-get-remote-filename (search filename) |
966 "Return FILENAME as a remote filename. | 354 "Return FILENAME as a remote filename. |
967 It first checks if FILENAME already is a remote filename. If it is | 355 It first checks if FILENAME already is a remote filename. If it is |
968 not, then it uses the (car search) as the remote site-name and the (cadr | 356 not, then it uses the (car search) as the remote site-name and the (cadr |
969 search) as the remote-directory and concatenates filename. In other | 357 search) as the remote-directory and concatenates filename. In other |
970 words | 358 words |
971 site-name:remote-directory/filename. | 359 site-name:remote-directory/filename |
972 | |
973 If (car search) is nil, (cadr search is interpreted as a local directory). | |
974 " | 360 " |
975 (if (file-remote-p filename) | 361 (if (efs-ftp-path filename) |
976 filename | 362 filename |
977 (let ((dir (cadr search))) | 363 (let ((dir (cadr search))) |
978 (concat (when (car search) | 364 (concat "/" |
979 (concat | 365 (car search) ":" |
980 (if (string-match "@" (car search)) | |
981 "/" | |
982 "/anonymous@") | |
983 (car search) ":")) | |
984 (if (string-match "/$" dir) | 366 (if (string-match "/$" dir) |
985 dir | 367 dir |
986 (concat dir "/")) | 368 (concat dir "/")) |
987 filename)))) | 369 filename)))) |
988 | 370 |
996 (package-get-info-find-package packages-package-list | 378 (package-get-info-find-package packages-package-list |
997 package) ':version) | 379 package) ':version) |
998 (if (floatp version) version (string-to-number version)))) | 380 (if (floatp version) version (string-to-number version)))) |
999 | 381 |
1000 ;;;###autoload | 382 ;;;###autoload |
1001 (defun package-get-package-provider (sym &optional force-current) | 383 (defun package-get-package-provider (sym) |
1002 "Search for a package that provides SYM and return the name and | 384 "Search for a package that provides SYM and return the name and |
1003 version. Searches in `package-get-base' for SYM. If SYM is a | 385 version. Searches in `package-get-base' for SYM. If SYM is a |
1004 consp, then it must match a corresponding (provide (SYM VERSION)) from | 386 consp, then it must match a corresponding (provide (SYM VERSION)) from |
1005 the package. | 387 the package." |
1006 | |
1007 If FORCE-CURRENT is non-nil make sure the database is up to date. This might | |
1008 lead to Emacs accessing remote sites." | |
1009 (interactive "SSymbol: ") | 388 (interactive "SSymbol: ") |
1010 (package-get-require-base force-current) | |
1011 (let ((packages package-get-base) | 389 (let ((packages package-get-base) |
1012 (done nil) | 390 (done nil) |
1013 (found nil)) | 391 (found nil)) |
1014 (while (and (not done) packages) | 392 (while (and (not done) packages) |
1015 (let* ((this-name (caar packages)) | 393 (let* ((this-name (caar packages)) |
1016 (this-package (cdr (car packages)))) ;strip off package name | 394 (this-package (cdr (car packages)))) ;strip off package name |
1017 (while (and (not done) this-package) | 395 (while (and (not done) this-package) |
1018 (if (or (eq this-name sym) | 396 (if (or (eq this-name sym) |
1019 (eq (cons this-name | 397 (eq (cons this-name |
1020 (package-get-info-prop (car this-package) 'version)) | 398 (package-get-info-prop (car this-package) 'version)) |
1021 sym) | 399 sym) |
1022 (member sym | 400 (member sym (package-get-info-prop (car this-package) 'provides))) |
1023 (package-get-info-prop (car this-package) 'provides))) | |
1024 (progn (setq done t) | 401 (progn (setq done t) |
1025 (setq found | 402 (setq found (list (caar packages) |
1026 (list (caar packages) | 403 (package-get-info-prop (car this-package) 'version)))) |
1027 (package-get-info-prop (car this-package) 'version)))) | |
1028 (setq this-package (cdr this-package))))) | 404 (setq this-package (cdr this-package))))) |
1029 (setq packages (cdr packages))) | 405 (setq packages (cdr packages))) |
1030 (when (interactive-p) | |
1031 (if found | |
1032 (message "%S" found) | |
1033 (message "No appropriate package found"))) | |
1034 found)) | 406 found)) |
1035 | 407 |
1036 ;; | 408 ;; |
1037 ;; customize interfaces. | 409 ;; customize interfaces. |
1038 ;; The group is in this file so that custom loads includes this file. | 410 ;; The group is in this file so that custom loads includes this file. |
1043 | 415 |
1044 ;;;###autoload | 416 ;;;###autoload |
1045 (defun package-get-custom () | 417 (defun package-get-custom () |
1046 "Fetch and install the latest versions of all customized packages." | 418 "Fetch and install the latest versions of all customized packages." |
1047 (interactive) | 419 (interactive) |
1048 (package-get-require-base t) | 420 ;; Load a fresh copy |
421 (load "package-get-custom.el") | |
1049 (mapcar (lambda (pkg) | 422 (mapcar (lambda (pkg) |
1050 (if (eval (intern (concat (symbol-name (car pkg)) "-package"))) | 423 (if (eval (intern (concat (symbol-name (car pkg)) "-package"))) |
1051 (package-get (car pkg) nil)) | 424 (package-get-all (car pkg) nil)) |
1052 t) | 425 t) |
1053 package-get-base)) | 426 package-get-base)) |
1054 | 427 |
1055 (defun package-get-ever-installed-p (pkg &optional notused) | 428 (defun package-get-ever-installed-p (pkg &optional notused) |
1056 (string-match "-package$" (symbol-name pkg)) | 429 (string-match "-package$" (symbol-name pkg)) |
1059 (if (package-get-info-find-package | 432 (if (package-get-info-find-package |
1060 packages-package-list | 433 packages-package-list |
1061 (intern (substring (symbol-name pkg) 0 (match-beginning 0)))) | 434 (intern (substring (symbol-name pkg) 0 (match-beginning 0)))) |
1062 t))) | 435 t))) |
1063 | 436 |
1064 (defvar package-get-custom-groups nil | 437 (defun package-get-file-installed-p (file &optional paths) |
1065 "List of package-get-custom groups") | 438 "Return absolute-path of FILE if FILE exists in PATHS. |
1066 | 439 If PATHS is omitted, `load-path' is used." |
1067 (defun package-get-custom-add-entry (package props) | 440 (if (null paths) |
1068 (let* ((category (plist-get props 'category)) | 441 (setq paths load-path) |
1069 (group (intern (concat category "-packages"))) | 442 ) |
1070 (custom-var (intern (concat (symbol-name package) "-package"))) | 443 (catch 'tag |
1071 (description (plist-get props 'description))) | 444 (let (path) |
1072 (when (not (memq group package-get-custom-groups)) | 445 (while paths |
1073 (setq package-get-custom-groups (cons group | 446 (setq path (expand-file-name file (car paths))) |
1074 package-get-custom-groups)) | 447 (if (file-exists-p path) |
1075 (eval `(defgroup ,group nil | 448 (throw 'tag path) |
1076 ,(concat category " package group") | 449 ) |
1077 :group 'packages))) | 450 (setq paths (cdr paths)) |
1078 (eval `(defcustom ,custom-var nil | 451 )))) |
1079 ,description | 452 |
1080 :group ',group | 453 (defun package-get-create-custom () |
1081 :initialize 'package-get-ever-installed-p | 454 "Creates a package customization file package-get-custom.el. |
1082 :type 'boolean)))) | 455 Entries in the customization file are retrieved from package-get-base.el." |
1083 | 456 (interactive) |
1084 | 457 ;; Load a fresh copy |
458 (let ((custom-buffer (find-file-noselect | |
459 (or (package-get-file-installed-p | |
460 "package-get-custom.el") | |
461 (concat (file-name-directory | |
462 (package-get-file-installed-p | |
463 "package-get-base.el")) | |
464 "package-get-custom.el")))) | |
465 (pkg-groups nil)) | |
466 | |
467 ;; clear existing stuff | |
468 (delete-region (point-min custom-buffer) | |
469 (point-max custom-buffer) custom-buffer) | |
470 (insert-string "(require 'package-get)\n" custom-buffer) | |
471 | |
472 (mapcar (lambda (pkg) | |
473 (let ((category (plist-get (car (cdr pkg)) 'category))) | |
474 (or (memq (intern category) pkg-groups) | |
475 (progn | |
476 (setq pkg-groups (cons (intern category) pkg-groups)) | |
477 (insert-string | |
478 (concat "(defgroup " category "-packages nil\n" | |
479 " \"" category " package group\"\n" | |
480 " :group 'packages)\n\n") custom-buffer))) | |
481 | |
482 (insert-string | |
483 (concat "(defcustom " (symbol-name (car pkg)) | |
484 "-package nil \n" | |
485 " \"" (plist-get (car (cdr pkg)) 'description) "\"\n" | |
486 " :group '" category "-packages\n" | |
487 " :initialize 'package-get-ever-installed-p\n" | |
488 " :type 'boolean)\n\n") custom-buffer))) | |
489 package-get-base) custom-buffer) | |
490 ) | |
491 | |
492 ;; need this first to avoid infinite dependency loops | |
1085 (provide 'package-get) | 493 (provide 'package-get) |
494 | |
495 ;; potentially update the custom dependencies every time we load this | |
496 (let ((custom-file (package-get-file-installed-p "package-get-custom.el")) | |
497 (package-file (package-get-file-installed-p "package-get-base.el"))) | |
498 ;; update custom file if it doesn't exist | |
499 (if (or (not custom-file) | |
500 (and (< (car (nth 5 (file-attributes custom-file))) | |
501 (car (nth 5 (file-attributes package-file)))) | |
502 (< (car (nth 5 (file-attributes custom-file))) | |
503 (car (nth 5 (file-attributes package-file)))))) | |
504 (save-excursion | |
505 (message "generating package customizations...") | |
506 (set-buffer (package-get-create-custom)) | |
507 (save-buffer) | |
508 (message "generating package customizations...done"))) | |
509 (load "package-get-custom.el")) | |
510 | |
1086 ;;; package-get.el ends here | 511 ;;; package-get.el ends here |