comparison lisp/package-get.el @ 373:6240c7796c7a r21-2b2

Import from CVS: tag r21-2b2
author cvs
date Mon, 13 Aug 2007 11:04:06 +0200
parents cc15677e0335
children a300bb07d72d
comparison
equal deleted inserted replaced
372:49e1ed2d7ed8 373:6240c7796c7a
147 147
148 (defvar package-get-dir (temp-directory) 148 (defvar package-get-dir (temp-directory)
149 "*Where to store temporary files for staging.") 149 "*Where to store temporary files for staging.")
150 150
151 (defvar package-get-remote 151 (defvar package-get-remote
152 '( 152 '(("ftp.xemacs.org" "/pub/xemacs/packages"))
153 ("ftp.xemacs.org" "/pub/xemacs/beta/xemacs-21.0/packages/binary-packages")
154 ("ftp.xemacs.org" "/pub/xemacs/beta/xemacs-21.0/packages/single-file-packages")
155 ("ftp.xemacs.org" "/pub/xemacs/package"))
156 "*List of remote sites to contact for downloading packages. 153 "*List of remote sites to contact for downloading packages.
157 List format is '(site-name directory-on-site). Each site is tried in 154 List format is '(site-name directory-on-site). Each site is tried in
158 order until the package is found.") 155 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.")
159 157
160 (defvar package-get-remove-copy nil 158 (defvar package-get-remove-copy nil
161 "*After copying and installing a package, if this is T, then remove the 159 "*After copying and installing a package, if this is T, then remove the
162 copy. Otherwise, keep it around.") 160 copy. Otherwise, keep it around.")
161
162 (defun package-get-interactive-package-query (get-version package-symbol)
163 "Perform interactive querying for package and optional version.
164 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.
166 The return value is suitable for direct passing to `interactive'."
167 (let ( (table (mapcar '(lambda (item)
168 (let ( (name (symbol-name (car item))) )
169 (cons name name)
170 ))
171 package-get-base))
172 package package-symbol default-version version)
173 (save-window-excursion
174 (setq package (completing-read "Package: " table nil t))
175 (setq package-symbol (intern package))
176 (if get-version
177 (progn
178 (setq default-version
179 (package-get-info-prop
180 (package-get-info-version
181 (package-get-info-find-package package-get-base
182 package-symbol) nil)
183 'version))
184 (while (string=
185 (setq version (read-string "Version: " default-version))
186 "")
187 )
188 (if package-symbol
189 (list package-symbol version)
190 (list package version))
191 )
192 (if package-symbol
193 (list package-symbol)
194 (list package)))
195 )))
196
197 ;;;###autoload
198 (defun package-get-delete-package (package &optional pkg-topdir)
199 "Delete an installation of PACKAGE below directory PKG-TOPDIR.
200 PACKAGE is a symbol, not a string.
201 This is just an interactive wrapper for `package-admin-delete-binary-package'."
202 (interactive (package-get-interactive-package-query nil t))
203 (package-admin-delete-binary-package package pkg-topdir))
163 204
164 ;;;###autoload 205 ;;;###autoload
165 (defun package-get-update-all () 206 (defun package-get-update-all ()
166 "Fetch and install the latest versions of all currently installed packages." 207 "Fetch and install the latest versions of all currently installed packages."
167 (interactive) 208 (interactive)
168 ;; Load a fresh copy 209 ;; Load a fresh copy
169 (mapcar (lambda (pkg) 210 (catch 'exit
170 (package-get-all 211 (mapcar (lambda (pkg)
171 (car pkg) nil)) 212 (if (not (package-get (car pkg) nil 'never))
172 packages-package-list)) 213 (throw 'exit nil) ;; Bail out if error detected
214 ))
215 packages-package-list)))
173 216
174 ;;;###autoload 217 ;;;###autoload
175 (defun package-get-all (package version &optional fetched-packages) 218 (defun package-get-all (package version &optional fetched-packages)
176 "Fetch PACKAGE with VERSION and all other required packages. 219 "Fetch PACKAGE with VERSION and all other required packages.
177 Uses `package-get-base' to determine just what is required and what 220 Uses `package-get-base' to determine just what is required and what
178 package provides that functionality. If VERSION is nil, retrieves 221 package provides that functionality. If VERSION is nil, retrieves
179 latest version. Optional argument FETCHED-PACKAGES is used to keep 222 latest version. Optional argument FETCHED-PACKAGES is used to keep
180 track of packages already fetched." 223 track of packages already fetched.
181 (interactive "sPackage: \nsVersion: ") 224
225 Returns nil upon error."
226 (interactive (package-get-interactive-package-query t nil))
182 (let* ((the-package (package-get-info-find-package package-get-base 227 (let* ((the-package (package-get-info-find-package package-get-base
183 package)) 228 package))
184 (this-package (package-get-info-version 229 (this-package (package-get-info-version
185 the-package version)) 230 the-package version))
186 (this-requires (package-get-info-prop this-package 'requires)) 231 (this-requires (package-get-info-prop this-package 'requires))
187 ) 232 )
188 (setq version (package-get-info-prop this-package 'version)) 233 (catch 'exit
189 (unless (package-get-installedp package version) 234 (setq version (package-get-info-prop this-package 'version))
190 (package-get package version)) 235 (unless (package-get-installedp package version)
191 (setq fetched-packages 236 (if (not (package-get package version))
192 (append (list package) 237 (progn
193 (package-get-info-prop this-package 'provides) 238 (setq fetched-packages nil)
194 fetched-packages)) 239 (throw 'exit nil))))
195 ;; grab everything that this package requires plus recursively 240 (setq fetched-packages
196 ;; grab everything that the requires require. Keep track 241 (append (list package)
197 ;; in `fetched-packages' the list of things provided -- this 242 (package-get-info-prop this-package 'provides)
198 ;; keeps us from going into a loop 243 fetched-packages))
199 (while this-requires 244 ;; grab everything that this package requires plus recursively
200 (if (not (member (car this-requires) fetched-packages)) 245 ;; grab everything that the requires require. Keep track
201 (let* ((reqd-package (package-get-package-provider 246 ;; in `fetched-packages' the list of things provided -- this
202 (car this-requires))) 247 ;; keeps us from going into a loop
203 (reqd-version (cadr reqd-package)) 248 (while this-requires
204 (reqd-name (car reqd-package))) 249 (if (not (member (car this-requires) fetched-packages))
205 (if (null reqd-name) 250 (let* ((reqd-package (package-get-package-provider
206 (error "Unable to find a provider for %s" (car this-requires))) 251 (car this-requires)))
207 (setq fetched-packages 252 (reqd-version (cadr reqd-package))
208 (package-get-all reqd-name reqd-version fetched-packages))) 253 (reqd-name (car reqd-package)))
209 ) 254 (if (null reqd-name)
210 (setq this-requires (cdr this-requires))) 255 (error "Unable to find a provider for %s"
256 (car this-requires)))
257 (if (not (setq fetched-packages
258 (package-get-all reqd-name reqd-version
259 fetched-packages)))
260 (throw 'exit nil)))
261 )
262 (setq this-requires (cdr this-requires)))
263 )
211 fetched-packages 264 fetched-packages
212 )) 265 ))
213 266
267 (defun package-get-load-package-file (lispdir file)
268 (let (pathname)
269 (setq pathname (expand-file-name file lispdir))
270 (condition-case err
271 (progn
272 (load pathname t)
273 t)
274 (t
275 (message "Error loading package file \"%s\" %s!" pathname err)
276 nil))
277 ))
278
279 (defun package-get-init-package (lispdir)
280 "Initialize the package.
281 This really assumes that the package has never been loaded. Updating
282 a newer package can cause problems, due to old, obsolete functions in
283 the old package.
284
285 Return `t' upon complete success, `nil' if any errors occurred."
286 (progn
287 (if (and lispdir
288 (file-accessible-directory-p lispdir))
289 (progn
290 ;; Add lispdir to load-path if it doesn't already exist.
291 ;; NOTE: this does not take symlinks, etc., into account.
292 (if (let ( (dirs load-path) )
293 (catch 'done
294 (while dirs
295 (if (string-equal (car dirs) lispdir)
296 (throw 'done nil))
297 (setq dirs (cdr dirs))
298 )
299 t))
300 (setq load-path (cons lispdir load-path)))
301 (if (not (package-get-load-package-file lispdir "auto-autoloads"))
302 (package-get-load-package-file lispdir "_pkg"))
303 t)
304 nil)
305 ))
306
214 ;;;###autoload 307 ;;;###autoload
215 (defun package-get (package &optional version conflict) 308 (defun package-get (package &optional version conflict install-dir)
216 "Fetch PACKAGE from remote site. 309 "Fetch PACKAGE from remote site.
217 Optional arguments VERSION indicates which version to retrieve, nil 310 Optional arguments VERSION indicates which version to retrieve, nil
218 means most recent version. CONFLICT indicates what happens if the 311 means most recent version. CONFLICT indicates what happens if the
219 package is already installed. Valid values for CONFLICT are: 312 package is already installed. Valid values for CONFLICT are:
220 'always always retrieve the package even if it is already installed 313 'always always retrieve the package even if it is already installed
221 'never do not retrieve the package if it is installed. 314 'never do not retrieve the package if it is installed.
315 INSTALL-DIR, if non-nil, specifies the package directory where
316 fetched packages should be installed.
222 317
223 The value of `package-get-base' is used to determine what files should 318 The value of `package-get-base' is used to determine what files should
224 be retrieved. The value of `package-get-remote' is used to determine 319 be retrieved. The value of `package-get-remote' is used to determine
225 where a package should be retrieved from. The sites are tried in 320 where a package should be retrieved from. The sites are tried in
226 order so one is better off listing easily reached sites first. 321 order so one is better off listing easily reached sites first.
227 322
228 Once the package is retrieved, its md5 checksum is computed. If that 323 Once the package is retrieved, its md5 checksum is computed. If that
229 sum does not match that stored in `package-get-base' for this version 324 sum does not match that stored in `package-get-base' for this version
230 of the package, an error is signalled." 325 of the package, an error is signalled.
231 (interactive "xPackage List: ") 326
327 Returns `t' upon success, the symbol `error' if the package was
328 successfully installed but errors occurred during initialization, or
329 `nil' upon error."
330 (interactive (package-get-interactive-package-query nil t))
232 (let* ((this-package 331 (let* ((this-package
233 (package-get-info-version 332 (package-get-info-version
234 (package-get-info-find-package package-get-base 333 (package-get-info-find-package package-get-base
235 package) version)) 334 package) version))
236 (found nil) 335 (found nil)
237 (search-dirs package-get-remote) 336 (search-dirs package-get-remote)
238 (filename (package-get-info-prop this-package 'filename))) 337 (base-filename (package-get-info-prop this-package 'filename))
338 (package-status t)
339 filenames full-package-filename)
239 (if (null this-package) 340 (if (null this-package)
240 (error "Couldn't find package %s with version %s" 341 (error "Couldn't find package %s with version %s"
241 package version)) 342 package version))
242 (if (null filename) 343 (if (null base-filename)
243 (error "No filename associated with package %s, version %s" 344 (error "No filename associated with package %s, version %s"
244 package version)) 345 package version))
346 (if (null install-dir)
347 (setq install-dir (package-admin-get-install-dir nil)))
348
349 ;; Contrive a list of possible package filenames.
350 ;; Ugly. Is there a better way to do this?
351 (setq filenames (cons base-filename nil))
352 (if (string-match "^\\(..*\\)\.tar\.gz$" base-filename)
353 (setq filenames (append filenames
354 (list (concat (match-string 1 base-filename)
355 ".tgz")))))
356
245 (setq version (package-get-info-prop this-package 'version)) 357 (setq version (package-get-info-prop this-package 'version))
246 (unless (and (eq conflict 'never) 358 (unless (and (eq conflict 'never)
247 (package-get-installedp package version)) 359 (package-get-installedp package version))
248 ;; Find the package from search list in package-get-remote 360 ;; Find the package from the search list in package-get-remote
249 ;; and copy it into the staging directory. Then validate 361 ;; and copy it into the staging directory. Then validate
250 ;; the checksum. Finally, install the package. 362 ;; the checksum. Finally, install the package.
251 (while (and search-dirs 363 (catch 'done
252 (not (file-exists-p (package-get-staging-dir filename)))) 364 (let (search-filenames current-dir-entry host dir current-filename
253 (if (file-exists-p (package-get-remote-filename 365 dest-filename)
254 (car search-dirs) filename)) 366 ;; In each search directory ...
255 (copy-file (package-get-remote-filename (car search-dirs) filename) 367 (while search-dirs
256 (package-get-staging-dir filename)) 368 (setq current-dir-entry (car search-dirs)
257 (setq search-dirs (cdr search-dirs)) 369 host (car current-dir-entry)
370 dir (car (cdr current-dir-entry))
371 search-filenames filenames
372 )
373
374 ;; Look for one of the possible package filenames ...
375 (while search-filenames
376 (setq current-filename (car search-filenames)
377 dest-filename (package-get-staging-dir current-filename))
378 (cond
379 ;; No host means look on the current system.
380 ( (null host)
381 (setq full-package-filename
382 (substitute-in-file-name
383 (expand-file-name current-filename
384 (file-name-as-directory dir))))
385 )
386
387 ;; If it's already on the disk locally, and the size is
388 ;; greater than zero ...
389 ( (and (file-exists-p dest-filename)
390 (let (attrs)
391 ;; file-attributes could return -1 for LARGE files,
392 ;; but, hopefully, packages won't be that large.
393 (and (setq attrs (file-attributes dest-filename))
394 (> (nth 7 attrs) 0))))
395 (setq full-package-filename dest-filename)
396 )
397
398 ;; If the file exists on the remote system ...
399 ( (file-exists-p (package-get-remote-filename
400 current-dir-entry current-filename))
401 ;; Get it
402 (setq full-package-filename dest-filename)
403 (message "Retrieving package `%s' ..."
404 current-filename)
405 (sit-for 0)
406 (copy-file (package-get-remote-filename current-dir-entry
407 current-filename)
408 full-package-filename t)
409 )
410 )
411
412 ;; If we found it, we're done.
413 (if (and full-package-filename
414 (file-exists-p full-package-filename))
415 (throw 'done nil))
416 ;; Didn't find it. Try the next possible filename.
417 (setq search-filenames (cdr search-filenames))
418 )
419 ;; Try looking in the next possible directory ...
420 (setq search-dirs (cdr search-dirs))
421 )
258 )) 422 ))
259 (if (not (file-exists-p (package-get-staging-dir filename))) 423
260 (error "Unable to find file %s" filename)) 424 (if (or (not full-package-filename)
425 (not (file-exists-p full-package-filename)))
426 (error "Unable to find file %s" base-filename))
261 ;; Validate the md5 checksum 427 ;; Validate the md5 checksum
262 ;; Doing it with XEmacs removes the need for an external md5 program 428 ;; Doing it with XEmacs removes the need for an external md5 program
429 (message "Validating checksum for `%s'..." package) (sit-for 0)
263 (with-temp-buffer 430 (with-temp-buffer
264 ;; What ever happened to i-f-c-literally 431 ;; What ever happened to i-f-c-literally
265 (let (file-name-handler-alist) 432 (let (file-name-handler-alist)
266 (insert-file-contents-internal (package-get-staging-dir filename))) 433 (insert-file-contents-internal full-package-filename))
267 (if (not (string= (md5 (current-buffer)) 434 (if (not (string= (md5 (current-buffer))
268 (package-get-info-prop this-package 435 (package-get-info-prop this-package
269 'md5sum))) 436 'md5sum)))
270 (error "Package %s does not match md5 checksum" filename))) 437 (error "Package %s does not match md5 checksum" base-filename)))
271 (message "Retrieved package %s" filename) (sit-for 0) 438
439 (package-admin-delete-binary-package package install-dir)
440
441 (message "Installing package `%s' ..." package) (sit-for 0)
272 (let ((status 442 (let ((status
273 (package-admin-add-binary-package 443 (package-admin-add-binary-package full-package-filename
274 (package-get-staging-dir filename)))) 444 install-dir)))
275 (when (not (= status 0)) 445 (if (= status 0)
276 (message "Package failed.") 446 (progn
277 (switch-to-buffer package-admin-temp-buffer))) 447 ;; clear messages so that only messages from
278 (sit-for 0) 448 ;; package-get-init-package are seen, below.
279 (message "Added package") (sit-for 0) 449 (clear-message)
450 (if (package-get-init-package (package-admin-get-lispdir
451 install-dir package))
452 (progn
453 (message "Added package `%s'" package)
454 (sit-for 0)
455 )
456 (progn
457 ;; display message only if there isn't already one.
458 (if (not (current-message))
459 (progn
460 (message "Added package `%s' (errors occurred)"
461 package)
462 (sit-for 0)
463 ))
464 (if package-status
465 (setq package-status 'errors))
466 ))
467 )
468 (message "Installation of package %s failed." base-filename)
469 (sit-for 0)
470 (switch-to-buffer package-admin-temp-buffer)
471 (setq package-status nil)
472 ))
280 (setq found t)) 473 (setq found t))
281 (if (and found package-get-remove-copy) 474 (if (and found package-get-remove-copy)
282 (delete-file (package-get-staging-dir filename))) 475 (delete-file full-package-filename))
476 package-status
283 )) 477 ))
284 478
285 (defun package-get-info-find-package (which name) 479 (defun package-get-info-find-package (which name)
286 "Look in WHICH for the package called NAME and return all the info 480 "Look in WHICH for the package called NAME and return all the info
287 associated with it. See `package-get-base' for info on the format 481 associated with it. See `package-get-base' for info on the format
302 "In PACKAGE, return the plist associated with a particular VERSION of the 496 "In PACKAGE, return the plist associated with a particular VERSION of the
303 package. PACKAGE is typically as returned by 497 package. PACKAGE is typically as returned by
304 `package-get-info-find-package'. If VERSION is nil, then return the 498 `package-get-info-find-package'. If VERSION is nil, then return the
305 first (aka most recent) version. Use `package-get-info-find-prop' 499 first (aka most recent) version. Use `package-get-info-find-prop'
306 to retrieve a particular property from the value returned by this." 500 to retrieve a particular property from the value returned by this."
307 (interactive "xPackage Info: \nsVersion: ") 501 (interactive (package-get-interactive-package-query t t))
308 (while (and version package (not (string= (plist-get (car package) 'version) version))) 502 (while (and version package (not (string= (plist-get (car package) 'version) version)))
309 (setq package (cdr package))) 503 (setq package (cdr package)))
310 (if package (car package))) 504 (if package (car package)))
311 505
312 (defun package-get-info-prop (package-version property) 506 (defun package-get-info-prop (package-version property)
343 Use `package-get-dir' for directory to store stuff. 537 Use `package-get-dir' for directory to store stuff.
344 Creates `package-get-dir' it it doesn't exist." 538 Creates `package-get-dir' it it doesn't exist."
345 (interactive "FPackage filename: ") 539 (interactive "FPackage filename: ")
346 (if (not (file-exists-p package-get-dir)) 540 (if (not (file-exists-p package-get-dir))
347 (make-directory package-get-dir)) 541 (make-directory package-get-dir))
348 (concat 542 (expand-file-name
349 (file-name-as-directory package-get-dir) 543 (file-name-nondirectory (or (and (fboundp 'efs-ftp-path)
350 (file-name-nondirectory (or (nth 2 (efs-ftp-path filename)) filename)))) 544 (nth 2 (efs-ftp-path filename)))
351 545 filename))
546 (file-name-as-directory package-get-dir)))
352 547
353 (defun package-get-remote-filename (search filename) 548 (defun package-get-remote-filename (search filename)
354 "Return FILENAME as a remote filename. 549 "Return FILENAME as a remote filename.
355 It first checks if FILENAME already is a remote filename. If it is 550 It first checks if FILENAME already is a remote filename. If it is
356 not, then it uses the (car search) as the remote site-name and the (cadr 551 not, then it uses the (car search) as the remote site-name and the (cadr
456 (interactive) 651 (interactive)
457 ;; Load a fresh copy 652 ;; Load a fresh copy
458 (let ((custom-buffer (find-file-noselect 653 (let ((custom-buffer (find-file-noselect
459 (or (package-get-file-installed-p 654 (or (package-get-file-installed-p
460 "package-get-custom.el") 655 "package-get-custom.el")
461 (concat (file-name-directory 656 (expand-file-name
462 (package-get-file-installed-p 657 "package-get-custom.el"
463 "package-get-base.el")) 658 (file-name-directory
464 "package-get-custom.el")))) 659 (package-get-file-installed-p
660 "package-get-base.el"))
661 ))))
465 (pkg-groups nil)) 662 (pkg-groups nil))
466 663
467 ;; clear existing stuff 664 ;; clear existing stuff
468 (delete-region (point-min custom-buffer) 665 (delete-region (point-min custom-buffer)
469 (point-max custom-buffer) custom-buffer) 666 (point-max custom-buffer) custom-buffer)