Mercurial > hg > xemacs-beta
comparison lisp/package-get.el @ 314:341dac730539 r21-0b55
Import from CVS: tag r21-0b55
author | cvs |
---|---|
date | Mon, 13 Aug 2007 10:44:22 +0200 |
parents | 9ea74add5d37 |
children | 512e409c26a2 |
comparison
equal
deleted
inserted
replaced
313:2905de29931f | 314:341dac730539 |
---|---|
153 ("ftp.xemacs.org" "/pub/xemacs/beta/xemacs-21.0/packages/binary-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") | 154 ("ftp.xemacs.org" "/pub/xemacs/beta/xemacs-21.0/packages/single-file-packages") |
155 ("ftp.xemacs.org" "/pub/xemacs/package")) | 155 ("ftp.xemacs.org" "/pub/xemacs/package")) |
156 "*List of remote sites to contact for downloading packages. | 156 "*List of remote sites to contact for downloading packages. |
157 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 |
158 order until the package is found.") | 158 order until the package is found. As a special case, `site-name' can be |
159 `nil', in which case `directory-on-site' is treated as a local directory.") | |
159 | 160 |
160 (defvar package-get-remove-copy nil | 161 (defvar package-get-remove-copy nil |
161 "*After copying and installing a package, if this is T, then remove the | 162 "*After copying and installing a package, if this is T, then remove the |
162 copy. Otherwise, keep it around.") | 163 copy. Otherwise, keep it around.") |
164 | |
165 (defun package-get-rmtree (directory) | |
166 "Delete a directory and all of its contents, recursively. | |
167 This is a feeble attempt at making a portable rmdir." | |
168 (let ( (orig-default-directory default-directory) files dirs dir) | |
169 (unwind-protect | |
170 (progn | |
171 (setq directory (file-name-as-directory directory)) | |
172 (setq files (directory-files directory nil nil nil t)) | |
173 (setq dirs (directory-files directory nil nil nil 'dirs)) | |
174 (while dirs | |
175 (setq dir (car dirs)) | |
176 (if (file-symlink-p dir) ;; just in case, handle symlinks | |
177 (delete-file dir) | |
178 (if (not (or (string-equal dir ".") (string-equal dir ".."))) | |
179 (package-get-rmtree (expand-file-name dir directory)))) | |
180 (setq dirs (cdr dirs)) | |
181 ) | |
182 (setq default-directory directory) | |
183 (condition-case err | |
184 (progn | |
185 (while files | |
186 (delete-file (car files)) | |
187 (setq files (cdr files)) | |
188 ) | |
189 (delete-directory directory) | |
190 ) | |
191 (file-error | |
192 (message "%s: %s: \"%s\"" (nth 1 err) (nth 2 err) (nth 3 err))) | |
193 ) | |
194 ) | |
195 (progn | |
196 (setq default-directory orig-default-directory) | |
197 )) | |
198 )) | |
163 | 199 |
164 ;;;###autoload | 200 ;;;###autoload |
165 (defun package-get-update-all () | 201 (defun package-get-update-all () |
166 "Fetch and install the latest versions of all currently installed packages." | 202 "Fetch and install the latest versions of all currently installed packages." |
167 (interactive) | 203 (interactive) |
168 ;; Load a fresh copy | 204 ;; Load a fresh copy |
169 (mapcar (lambda (pkg) | 205 (catch 'exit |
170 (package-get (car pkg) nil 'never)) | 206 (mapcar (lambda (pkg) |
171 packages-package-list)) | 207 (if (not (package-get (car pkg) nil 'never)) |
208 (throw 'exit nil) ;; Bail out if error detected | |
209 )) | |
210 packages-package-list))) | |
211 | |
212 (defun package-get-interactive-package-query (get-version package-symbol) | |
213 "Perform interactive querying for package and optional version. | |
214 Query for a version if GET-VERSION is non-nil. Return package name as | |
215 a symbol instead of a string if PACKAGE-SYMBOL is non-nil. | |
216 The return value is suitable for direct passing to `interactive'." | |
217 (let ( (table (mapcar '(lambda (item) | |
218 (let ( (name (symbol-name (car item))) ) | |
219 (cons name name) | |
220 )) | |
221 package-get-base)) | |
222 package package-symbol default-version version) | |
223 (save-window-excursion | |
224 (setq package (completing-read "Package: " table nil t)) | |
225 (setq package-symbol (intern package)) | |
226 (if get-version | |
227 (progn | |
228 (setq default-version | |
229 (package-get-info-prop | |
230 (package-get-info-version | |
231 (package-get-info-find-package package-get-base | |
232 package-symbol) nil) | |
233 'version)) | |
234 (while (string= | |
235 (setq version (read-string "Version: " default-version)) | |
236 "") | |
237 ) | |
238 (if package-symbol | |
239 (list package-symbol version) | |
240 (list package version)) | |
241 ) | |
242 (if package-symbol | |
243 (list package-symbol) | |
244 (list package))) | |
245 ))) | |
172 | 246 |
173 ;;;###autoload | 247 ;;;###autoload |
174 (defun package-get-all (package version &optional fetched-packages) | 248 (defun package-get-all (package version &optional fetched-packages) |
175 "Fetch PACKAGE with VERSION and all other required packages. | 249 "Fetch PACKAGE with VERSION and all other required packages. |
176 Uses `package-get-base' to determine just what is required and what | 250 Uses `package-get-base' to determine just what is required and what |
177 package provides that functionality. If VERSION is nil, retrieves | 251 package provides that functionality. If VERSION is nil, retrieves |
178 latest version. Optional argument FETCHED-PACKAGES is used to keep | 252 latest version. Optional argument FETCHED-PACKAGES is used to keep |
179 track of packages already fetched." | 253 track of packages already fetched. |
180 (interactive "sPackage: \nsVersion: ") | 254 |
255 Returns nil upon error." | |
256 (interactive (package-get-interactive-package-query t nil)) | |
181 (let* ((the-package (package-get-info-find-package package-get-base | 257 (let* ((the-package (package-get-info-find-package package-get-base |
182 package)) | 258 package)) |
183 (this-package (package-get-info-version | 259 (this-package (package-get-info-version |
184 the-package version)) | 260 the-package version)) |
185 (this-requires (package-get-info-prop this-package 'requires)) | 261 (this-requires (package-get-info-prop this-package 'requires)) |
186 ) | 262 ) |
187 (setq version (package-get-info-prop this-package 'version)) | 263 (catch 'exit |
188 (unless (package-get-installedp package version) | 264 (setq version (package-get-info-prop this-package 'version)) |
189 (package-get package version)) | 265 (unless (package-get-installedp package version) |
190 (setq fetched-packages | 266 (if (not (package-get package version)) |
191 (append (list package) | 267 (progn |
192 (package-get-info-prop this-package 'provides) | 268 (setq fetched-packages nil) |
193 fetched-packages)) | 269 (throw 'exit nil)))) |
194 ;; grab everything that this package requires plus recursively | 270 (setq fetched-packages |
195 ;; grab everything that the requires require. Keep track | 271 (append (list package) |
196 ;; in `fetched-packages' the list of things provided -- this | 272 (package-get-info-prop this-package 'provides) |
197 ;; keeps us from going into a loop | 273 fetched-packages)) |
198 (while this-requires | 274 ;; grab everything that this package requires plus recursively |
199 (if (not (member (car this-requires) fetched-packages)) | 275 ;; grab everything that the requires require. Keep track |
200 (let* ((reqd-package (package-get-package-provider | 276 ;; in `fetched-packages' the list of things provided -- this |
201 (car this-requires))) | 277 ;; keeps us from going into a loop |
202 (reqd-version (cadr reqd-package)) | 278 (while this-requires |
203 (reqd-name (car reqd-package))) | 279 (if (not (member (car this-requires) fetched-packages)) |
204 (if (null reqd-name) | 280 (let* ((reqd-package (package-get-package-provider |
205 (error "Unable to find a provider for %s" (car this-requires))) | 281 (car this-requires))) |
206 (setq fetched-packages | 282 (reqd-version (cadr reqd-package)) |
207 (package-get-all reqd-name reqd-version fetched-packages))) | 283 (reqd-name (car reqd-package))) |
208 ) | 284 (if (null reqd-name) |
209 (setq this-requires (cdr this-requires))) | 285 (error "Unable to find a provider for %s" |
286 (car this-requires))) | |
287 (if (not (setq fetched-packages | |
288 (package-get-all reqd-name reqd-version | |
289 fetched-packages))) | |
290 (throw 'exit nil))) | |
291 ) | |
292 (setq this-requires (cdr this-requires))) | |
293 ) | |
210 fetched-packages | 294 fetched-packages |
295 )) | |
296 | |
297 (defun package-get-load-package-file (lispdir file) | |
298 (let (pathname) | |
299 (setq pathname (expand-file-name file lispdir)) | |
300 (condition-case err | |
301 (progn | |
302 (load pathname t) | |
303 t) | |
304 (t | |
305 (message "Error loading package file \"%s\" %s!" pathname err) | |
306 nil)) | |
307 )) | |
308 | |
309 (defun package-get-init-package (lispdir) | |
310 "Initialize the package. | |
311 This really assumes that the package has never been loaded. Updating | |
312 a newer package can cause problems, due to old, obsolete functions in | |
313 the old package. | |
314 | |
315 Return `t' upon complete success, `nil' if any errors occurred." | |
316 (progn | |
317 (if (and lispdir | |
318 (file-accessible-directory-p lispdir)) | |
319 (progn | |
320 ;; Add lispdir to load-path if it doesn't already exist. | |
321 ;; NOTE: this does not take symlinks, etc., into account. | |
322 (if (let ( (dirs load-path) ) | |
323 (catch 'done | |
324 (while dirs | |
325 (if (string-equal (car dirs) lispdir) | |
326 (throw 'done nil)) | |
327 (setq dirs (cdr dirs)) | |
328 ) | |
329 t)) | |
330 (setq load-path (cons lispdir load-path))) | |
331 (package-get-load-package-file lispdir "auto-autoloads") | |
332 t) | |
333 nil) | |
211 )) | 334 )) |
212 | 335 |
213 ;;;###autoload | 336 ;;;###autoload |
214 (defun package-get (package &optional version conflict install-dir) | 337 (defun package-get (package &optional version conflict install-dir) |
215 "Fetch PACKAGE from remote site. | 338 "Fetch PACKAGE from remote site. |
226 where a package should be retrieved from. The sites are tried in | 349 where a package should be retrieved from. The sites are tried in |
227 order so one is better off listing easily reached sites first. | 350 order so one is better off listing easily reached sites first. |
228 | 351 |
229 Once the package is retrieved, its md5 checksum is computed. If that | 352 Once the package is retrieved, its md5 checksum is computed. If that |
230 sum does not match that stored in `package-get-base' for this version | 353 sum does not match that stored in `package-get-base' for this version |
231 of the package, an error is signalled." | 354 of the package, an error is signalled. |
232 (interactive "xPackage List: ") | 355 |
356 Returns `t' upon success, the symbol `error' if the package was | |
357 successfully installed but errors occurred during initialization, or | |
358 `nil' upon error." | |
359 (interactive (package-get-interactive-package-query nil t)) | |
233 (let* ((this-package | 360 (let* ((this-package |
234 (package-get-info-version | 361 (package-get-info-version |
235 (package-get-info-find-package package-get-base | 362 (package-get-info-find-package package-get-base |
236 package) version)) | 363 package) version)) |
237 (found nil) | 364 (found nil) |
238 (search-dirs package-get-remote) | 365 (search-dirs package-get-remote) |
239 (filename (package-get-info-prop this-package 'filename))) | 366 (base-filename (package-get-info-prop this-package 'filename)) |
367 (package-status t) | |
368 filenames full-package-filename package-lispdir) | |
240 (if (null this-package) | 369 (if (null this-package) |
241 (error "Couldn't find package %s with version %s" | 370 (error "Couldn't find package %s with version %s" |
242 package version)) | 371 package version)) |
243 (if (null filename) | 372 (if (null base-filename) |
244 (error "No filename associated with package %s, version %s" | 373 (error "No filename associated with package %s, version %s" |
245 package version)) | 374 package version)) |
375 (if (null install-dir) | |
376 (setq install-dir (package-admin-get-install-dir nil))) | |
377 | |
378 ;; Contrive a list of possible package filenames. | |
379 ;; Ugly. Is there a better way to do this? | |
380 (setq filenames (cons base-filename nil)) | |
381 (if (string-match "^\\(..*\\)\.tar\.gz$" base-filename) | |
382 (setq filenames (cons (concat (match-string 1 base-filename) ".tgz") | |
383 filenames))) | |
384 | |
246 (setq version (package-get-info-prop this-package 'version)) | 385 (setq version (package-get-info-prop this-package 'version)) |
247 (unless (and (eq conflict 'never) | 386 (unless (and (eq conflict 'never) |
248 (package-get-installedp package version)) | 387 (package-get-installedp package version)) |
249 ;; Find the package from search list in package-get-remote | 388 ;; Find the package from the search list in package-get-remote |
250 ;; and copy it into the staging directory. Then validate | 389 ;; and copy it into the staging directory. Then validate |
251 ;; the checksum. Finally, install the package. | 390 ;; the checksum. Finally, install the package. |
252 (while (and search-dirs | 391 (catch 'done |
253 (not (file-exists-p (package-get-staging-dir filename)))) | 392 (let (search-filenames current-dir-entry host dir current-filename) |
254 (if (file-exists-p (package-get-remote-filename | 393 ;; In each search directory ... |
255 (car search-dirs) filename)) | 394 (while search-dirs |
256 (copy-file (package-get-remote-filename (car search-dirs) filename) | 395 (setq current-dir-entry (car search-dirs) |
257 (package-get-staging-dir filename)) | 396 host (car current-dir-entry) |
258 (setq search-dirs (cdr search-dirs)) | 397 dir (car (cdr current-dir-entry)) |
398 search-filenames filenames) | |
399 | |
400 ;; Look for one of the possible package filenames ... | |
401 (while search-filenames | |
402 (setq current-filename (car search-filenames)) | |
403 (if (null host) | |
404 (progn | |
405 ;; No host means look on the current system. | |
406 (setq full-package-filename | |
407 (substitute-in-file-name | |
408 (expand-file-name current-filename | |
409 (file-name-as-directory dir)))) | |
410 ) | |
411 ;; If the file exists on the remote system ... | |
412 (if (file-exists-p (package-get-remote-filename | |
413 current-dir-entry current-filename)) | |
414 (progn | |
415 ;; Get it | |
416 (setq full-package-filename | |
417 (package-get-staging-dir current-filename)) | |
418 (message "Retrieving package `%s' ..." | |
419 current-filename) | |
420 (sit-for 0) | |
421 (copy-file (package-get-remote-filename current-dir-entry | |
422 current-filename) | |
423 )))) | |
424 ;; If we found it, we're done. | |
425 (if (file-exists-p full-package-filename) | |
426 (throw 'done nil)) | |
427 ;; Didn't find it. Try the next possible filename. | |
428 (setq search-filenames (cdr search-filenames)) | |
429 ) | |
430 ;; Try looking in the next possible directory ... | |
431 (setq search-dirs (cdr search-dirs)) | |
432 ) | |
259 )) | 433 )) |
260 (if (not (file-exists-p (package-get-staging-dir filename))) | 434 |
261 (error "Unable to find file %s" filename)) | 435 (if (or (not full-package-filename) |
436 (not (file-exists-p full-package-filename))) | |
437 (error "Unable to find file %s" base-filename)) | |
262 ;; Validate the md5 checksum | 438 ;; Validate the md5 checksum |
263 ;; Doing it with XEmacs removes the need for an external md5 program | 439 ;; Doing it with XEmacs removes the need for an external md5 program |
440 (message "Validating checksum for `%s'..." package) (sit-for 0) | |
264 (with-temp-buffer | 441 (with-temp-buffer |
265 ;; What ever happened to i-f-c-literally | 442 ;; What ever happened to i-f-c-literally |
266 (let (file-name-handler-alist) | 443 (let (file-name-handler-alist) |
267 (insert-file-contents-internal (package-get-staging-dir filename))) | 444 (insert-file-contents-internal full-package-filename)) |
268 (if (not (string= (md5 (current-buffer)) | 445 (if (not (string= (md5 (current-buffer)) |
269 (package-get-info-prop this-package | 446 (package-get-info-prop this-package |
270 'md5sum))) | 447 'md5sum))) |
271 (error "Package %s does not match md5 checksum" filename))) | 448 (error "Package %s does not match md5 checksum" base-filename))) |
272 (message "Retrieved package %s" filename) (sit-for 0) | 449 |
450 ;; Now delete old lisp directory, if any | |
451 ;; | |
452 ;; Gads, this is ugly. However, we're not supposed to use `concat' | |
453 ;; in the name of portability. | |
454 (if (and (setq package-lispdir (expand-file-name "lisp" install-dir)) | |
455 (setq package-lispdir (expand-file-name (symbol-name package) | |
456 package-lispdir)) | |
457 (file-accessible-directory-p package-lispdir)) | |
458 (progn | |
459 (message "Removing old lisp directory \"%s\" ..." package-lispdir) | |
460 (sit-for 0) | |
461 (package-get-rmtree package-lispdir) | |
462 )) | |
463 | |
464 (message "Installing package `%s' ..." package) (sit-for 0) | |
273 (let ((status | 465 (let ((status |
274 (package-admin-add-binary-package | 466 (package-admin-add-binary-package full-package-filename |
275 (package-get-staging-dir filename) | 467 install-dir))) |
276 install-dir))) | 468 (if (= status 0) |
277 (when (not (= status 0)) | 469 (progn |
278 (message "Package failed.") | 470 ;; clear messages so that only messages from |
279 (switch-to-buffer package-admin-temp-buffer))) | 471 ;; package-get-init-package are seen, below. |
280 (sit-for 0) | 472 (clear-message) |
281 (message "Added package") (sit-for 0) | 473 (if (package-get-init-package package-lispdir) |
474 (progn | |
475 (message "Added package `%s'" package) | |
476 (sit-for 0) | |
477 ) | |
478 (progn | |
479 ;; display message only if there isn't already one. | |
480 (if (not (current-message)) | |
481 (progn | |
482 (message "Added package `%s' (errors occurred)" | |
483 package) | |
484 (sit-for 0) | |
485 )) | |
486 (if package-status | |
487 (setq package-status 'errors)) | |
488 )) | |
489 ) | |
490 (message "Installation of package %s failed." base-filename) | |
491 (sit-for 0) | |
492 (switch-to-buffer package-admin-temp-buffer) | |
493 (setq package-status nil) | |
494 )) | |
282 (setq found t)) | 495 (setq found t)) |
283 (if (and found package-get-remove-copy) | 496 (if (and found package-get-remove-copy) |
284 (delete-file (package-get-staging-dir filename))) | 497 (delete-file full-package-filename)) |
498 package-status | |
285 )) | 499 )) |
286 | 500 |
287 (defun package-get-info-find-package (which name) | 501 (defun package-get-info-find-package (which name) |
288 "Look in WHICH for the package called NAME and return all the info | 502 "Look in WHICH for the package called NAME and return all the info |
289 associated with it. See `package-get-base' for info on the format | 503 associated with it. See `package-get-base' for info on the format |
304 "In PACKAGE, return the plist associated with a particular VERSION of the | 518 "In PACKAGE, return the plist associated with a particular VERSION of the |
305 package. PACKAGE is typically as returned by | 519 package. PACKAGE is typically as returned by |
306 `package-get-info-find-package'. If VERSION is nil, then return the | 520 `package-get-info-find-package'. If VERSION is nil, then return the |
307 first (aka most recent) version. Use `package-get-info-find-prop' | 521 first (aka most recent) version. Use `package-get-info-find-prop' |
308 to retrieve a particular property from the value returned by this." | 522 to retrieve a particular property from the value returned by this." |
309 (interactive "xPackage Info: \nsVersion: ") | 523 (interactive (package-get-interactive-package-query t t)) |
310 (while (and version package (not (string= (plist-get (car package) 'version) version))) | 524 (while (and version package (not (string= (plist-get (car package) 'version) version))) |
311 (setq package (cdr package))) | 525 (setq package (cdr package))) |
312 (if package (car package))) | 526 (if package (car package))) |
313 | 527 |
314 (defun package-get-info-prop (package-version property) | 528 (defun package-get-info-prop (package-version property) |
345 Use `package-get-dir' for directory to store stuff. | 559 Use `package-get-dir' for directory to store stuff. |
346 Creates `package-get-dir' it it doesn't exist." | 560 Creates `package-get-dir' it it doesn't exist." |
347 (interactive "FPackage filename: ") | 561 (interactive "FPackage filename: ") |
348 (if (not (file-exists-p package-get-dir)) | 562 (if (not (file-exists-p package-get-dir)) |
349 (make-directory package-get-dir)) | 563 (make-directory package-get-dir)) |
350 (concat | 564 (expand-file-name |
351 (file-name-as-directory package-get-dir) | 565 (file-name-nondirectory (or (nth 2 (efs-ftp-path filename)) filename)) |
352 (file-name-nondirectory (or (nth 2 (efs-ftp-path filename)) filename)))) | 566 (file-name-as-directory package-get-dir))) |
353 | 567 |
354 | 568 |
355 (defun package-get-remote-filename (search filename) | 569 (defun package-get-remote-filename (search filename) |
356 "Return FILENAME as a remote filename. | 570 "Return FILENAME as a remote filename. |
357 It first checks if FILENAME already is a remote filename. If it is | 571 It first checks if FILENAME already is a remote filename. If it is |
458 (interactive) | 672 (interactive) |
459 ;; Load a fresh copy | 673 ;; Load a fresh copy |
460 (let ((custom-buffer (find-file-noselect | 674 (let ((custom-buffer (find-file-noselect |
461 (or (package-get-file-installed-p | 675 (or (package-get-file-installed-p |
462 "package-get-custom.el") | 676 "package-get-custom.el") |
463 (concat (file-name-directory | 677 (expand-file-name |
464 (package-get-file-installed-p | 678 "package-get-custom.el" |
465 "package-get-base.el")) | 679 (file-name-directory |
466 "package-get-custom.el")))) | 680 (package-get-file-installed-p |
681 "package-get-base.el")) | |
682 )))) | |
467 (pkg-groups nil)) | 683 (pkg-groups nil)) |
468 | 684 |
469 ;; clear existing stuff | 685 ;; clear existing stuff |
470 (delete-region (point-min custom-buffer) | 686 (delete-region (point-min custom-buffer) |
471 (point-max custom-buffer) custom-buffer) | 687 (point-max custom-buffer) custom-buffer) |