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