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