Mercurial > hg > xemacs-beta
diff lisp/packages.el @ 428:3ecd8885ac67 r21-2-22
Import from CVS: tag r21-2-22
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:28:15 +0200 |
parents | |
children | abe6d1db359e |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/lisp/packages.el Mon Aug 13 11:28:15 2007 +0200 @@ -0,0 +1,547 @@ +;;; packages.el --- Low level support for XEmacs packages + +;; Copyright (C) 1997 Free Software Foundation, Inc. + +;; Author: Steven L Baur <steve@xemacs.org> +;; Maintainer: Steven L Baur <steve@xemacs.org> +;; Keywords: internal, lisp, dumped + +;; This file is part of XEmacs. + +;; XEmacs is free software; you can redistribute it and/or modify it +;; under the terms of the GNU General Public License as published by +;; the Free Software Foundation; either version 2, or (at your option) +;; any later version. + +;; XEmacs is distributed in the hope that it will be useful, but +;; WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +;; General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with XEmacs; see the file COPYING. If not, write to the Free +;; Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +;; 02111-1307, USA. + +;;; Synched up with: Not in FSF + +;;; Commentary: + +;; This file is dumped with XEmacs. + +;; This file provides low level facilities for XEmacs startup -- +;; particularly regarding the package setup. This code has to run in +;; what we call "bare temacs" -- i.e. XEmacs without the usual Lisp +;; environment. Pay special attention: + +;; - not to use the `lambda' macro. Use #'(lambda ...) instead. +;; (this goes for any package loaded before `subr.el'.) +;; +;; - not to use macros, because they are not yet available (and this +;; file must be loadable uncompiled.) This rules out CL-style +;; macros like `when', for instance. +;; +;; - not to use `defcustom'. If you must add user-customizable +;; variables here, use `defvar', and add the variable to +;; `cus-start.el'. + +;; Because of all this, make sure that the stuff you put here really +;; belongs here. + +;; This file requires find-paths.el. + +;;; Code: + +;;; Package versioning + +(defvar packages-package-list nil + "Database of loaded packages and version numbers") + +(defvar packages-hierarchy-depth 1 + "Depth of package hierarchies.") + +(defvar packages-load-path-depth 1 + "Depth of load-path search in package hierarchies.") + +(defvar packages-data-path-depth 1 + "Depth of data-path search in package hierarchies.") + +(defvar early-packages nil + "Packages early in the load path.") + +(defvar early-package-load-path nil + "Load path for packages early in the load path.") + +(defvar late-packages nil + "Packages late in the load path.") + +(defvar late-package-load-path nil + "Load path for packages late in the load path.") + +(defvar last-packages nil + "Packages last in the load path.") + +(defvar last-package-load-path nil + "Load path for packages last in the load path.") + +(defun packages-compute-package-locations (user-init-directory) + "Compute locations of the various package directories. +This is a list each of whose elements describes one directory. +A directory description is a three-element list. +The first element is either an absolute path or a subdirectory +in the XEmacs hierarchy. +The second component is one of the symbols EARLY, LATE, LAST, +depending on the load-path segment the hierarchy is supposed to +show up in. +The third component is a thunk which, if it returns NIL, causes +the directory to be ignored." + (list + (list (paths-construct-path (list user-init-directory "mule-packages")) + 'early #'(lambda () (featurep 'mule))) + (list (paths-construct-path (list user-init-directory "xemacs-packages")) + 'early #'(lambda () t)) + (list "site-packages" 'late #'(lambda () t)) + (list "infodock-packages" 'late #'(lambda () (featurep 'infodock))) + (list "mule-packages" 'late #'(lambda () (featurep 'mule))) + (list "xemacs-packages" 'late #'(lambda () t)))) + +(defun package-get-key-1 (info key) + "Locate keyword `key' in list." + (cond ((null info) + nil) + ((eq (car info) key) + (nth 1 info)) + (t (package-get-key-1 (cddr info) key)))) + +(defun package-get-key (name key) + "Get info `key' from package `name'." + (let ((info (assq name packages-package-list))) + (when info + (package-get-key-1 (cdr info) key)))) + +(defun package-provide (name &rest attributes) + (let ((info (if (and attributes (floatp (car attributes))) + (list :version (car attributes)) + attributes))) + (setq packages-package-list + (cons (cons name info) (remassq name packages-package-list))))) + +(defun package-require (name version) + (let ((pkg (assq name packages-package-list))) + (cond ((null pkg) + (error "Package %s has not been loaded into this XEmacsen" + name)) + ((< (package-get-key name :version) version) + (error "Need version %g of package %s, got version %g" + version name (cdr pkg))) + (t t)))) + +(defun package-delete-name (name) + (let (pkg) + ;; Delete ALL versions of package. + ;; This is pretty memory-intensive, as we use copy-alist when deleting + ;; package entries, to prevent side-effects in functions that call this + ;; one. + (while (setq pkg (assq name packages-package-list)) + (setq packages-package-list (delete pkg (copy-alist + packages-package-list))) + ) + )) + +;;; Build time stuff + +(defvar autoload-file-name "auto-autoloads.el" + "Filename that autoloads are expected to be found in.") + +(defvar packages-hardcoded-lisp + '( + ;; Nothing at this time + ) + "Lisp packages that are always dumped with XEmacs. +This includes every package that is loaded directly by a package listed +in dumped-lisp.el and is not itself listed.") + +(defvar packages-useful-lisp + '("bytecomp" + "byte-optimize" + "shadow" + "cl-macs") + "Lisp packages that need early byte compilation.") + +(defvar packages-unbytecompiled-lisp + '("paths.el" + "dumped-lisp.el" + "dumped-pkg-lisp.el" + "version.el" + "very-early-lisp.el") + "Lisp packages that should not be byte compiled.") + + +;; Copied from help.el, could possibly move it to here permanently. +;; Unlike the FSF version, our `locate-library' uses the `locate-file' +;; primitive, which should make it lightning-fast. + +(defun locate-library (library &optional nosuffix path interactive-call) + "Show the precise file name of Emacs library LIBRARY. +This command searches the directories in `load-path' like `M-x load-library' +to find the file that `M-x load-library RET LIBRARY RET' would load. +Optional second arg NOSUFFIX non-nil means don't add suffixes `.elc' or `.el' +to the specified name LIBRARY. + +If the optional third arg PATH is specified, that list of directories +is used instead of `load-path'." + (interactive (list (read-string "Locate library: ") + nil nil + t)) + (let ((result + (locate-file + library + (or path load-path) + (cond ((or (rassq 'jka-compr-handler file-name-handler-alist) + (and (boundp 'find-file-hooks) + (member 'crypt-find-file-hook find-file-hooks))) + ;; Compression involved. + (if nosuffix + '("" ".gz" ".Z") + '(".elc" ".elc.gz" "elc.Z" ".el" ".el.gz" ".el.Z" "" ".gz" ".Z"))) + (t + ;; No compression. + (if nosuffix + "" + '(".elc" ".el" ""))))))) + (and interactive-call + (if result + (message "Library is file %s" result) + (message "No library %s in search path" library))) + result)) + +(defun packages-add-suffix (str) + (if (null (string-match "\\.el\\'" str)) + (concat str ".elc") + str)) + +(defun packages-list-autoloads-path () + "List autoloads from precomputed load-path." + (let ((path load-path) + autoloads) + (while path + (if (file-exists-p (concat (car path) + autoload-file-name)) + (setq autoloads (cons (concat (car path) + autoload-file-name) + autoloads))) + (setq path (cdr path))) + autoloads)) + +(defun packages-list-autoloads (source-directory) + "List autoload files in (what will be) the normal lisp search path. +This function is used during build to find where the global symbol files so +they can be perused for their useful information." + (let ((files (directory-files (file-name-as-directory source-directory) + t ".*")) + file autolist) + ;; (print (prin1-to-string source-directory)) + ;; (print (prin1-to-string files)) + (while (setq file (car-safe files)) + (if (and (file-directory-p file) + (file-exists-p (concat (file-name-as-directory file) + autoload-file-name))) + (setq autolist (cons (concat (file-name-as-directory file) + autoload-file-name) + autolist))) + (setq files (cdr files))) + autolist)) + +;; The following function cannot be called from a bare temacs +(defun packages-new-autoloads () + "Return autoloads files that have been added or modified since XEmacs dump." + (require 'loadhist) + (let ((me (concat invocation-directory invocation-name)) + (path load-path) + result dir) + (while path + (setq dir (file-truename (car path))) + (let ((autoload-file (file-name-sans-extension (concat + dir + autoload-file-name)))) + ;; Check for: + ;; 1. An auto-autoload file that hasn't provided a feature (because + ;; it has been installed since XEmacs was dumped). + ;; 2. auto-autoload.el being newer than the executable + ;; 3. auto-autoload.elc being newer than the executable (the .el + ;; could be missing or compressed) + (when (or (and (null (file-provides autoload-file)) + (or (file-exists-p (concat autoload-file ".elc")) + (file-exists-p (concat autoload-file ".el")))) + (and (file-newer-than-file-p (concat autoload-file ".el") me) + (setq autoload-file (concat autoload-file ".el"))) + (and (file-newer-than-file-p (concat autoload-file + ".elc") + me) + (setq autoload-file (concat autoload-file ".elc")))) + (push autoload-file result))) + (setq path (cdr path))) + result)) + +;; The following function cannot be called from a bare temacs +(defun packages-reload-autoloads () + "Reload new or updated auto-autoloads files. +This is an extremely dangerous function to call after the user-init-files +is run. Don't call it or you'll be sorry." + (let ((autoload-list (packages-new-autoloads))) + (while autoload-list + (let* ((autoload-file (car autoload-list)) + (feature (car-safe (file-provides autoload-file)))) + (when feature + ;; (message "(unload-feature %S)" feature) + (unload-feature feature)) + (condition-case nil + (load autoload-file) + (t nil))) + (setq autoload-list (cdr autoload-list))))) + +;; Data-directory is really a list now. Provide something to search it for +;; directories. + +(defun locate-data-directory-list (name &optional dir-list) + "Locate the matching list of directories in a search path DIR-LIST. +If no DIR-LIST is supplied, it defaults to `data-directory-list'." + (unless dir-list + (setq dir-list data-directory-list)) + (let (found found-dir found-dir-list) + (while dir-list + (setq found (file-name-as-directory (concat (car dir-list) name)) + found-dir (file-directory-p found)) + (and found-dir + (setq found-dir-list (cons found found-dir-list))) + (setq dir-list (cdr dir-list))) + (nreverse found-dir-list))) + +;; Data-directory is really a list now. Provide something to search it for +;; a directory. + +(defun locate-data-directory (name &optional dir-list) + "Locate a directory in a search path DIR-LIST (a list of directories). +If no DIR-LIST is supplied, it defaults to `data-directory-list'." + (unless dir-list + (setq dir-list data-directory-list)) + (let (found found-dir) + (while (and (null found-dir) dir-list) + (setq found (file-name-as-directory (concat (car dir-list) name)) + found-dir (file-directory-p found)) + (or found-dir + (setq found nil)) + (setq dir-list (cdr dir-list))) + found)) + +;; Data-directory is really a list now. Provide something to search it for +;; files. + +(defun locate-data-file (name &optional dir-list) + "Locate a file in a search path DIR-LIST (a list of directories). +If no DIR-LIST is supplied, it defaults to `data-directory-list'. +This function is basically a wrapper over `locate-file'." + (locate-file name (or dir-list data-directory-list))) + +;; Path setup + +(defun packages-find-package-directories (roots base) + "Find a set of package directories." + ;; make sure paths-find-version-directory and paths-find-site-directory + ;; don't both pick up version-independent directories ... + (let ((version-directory (paths-find-version-directory roots base nil nil t)) + (site-directory (paths-find-site-directory roots base))) + (paths-uniq-append + (and version-directory (list version-directory)) + (and site-directory (list site-directory))))) + +(defvar packages-special-base-regexp "^\\(etc\\|info\\|lisp\\|lib-src\\|bin\\|pkginfo\\)$" + "Special subdirectories of packages.") + +(defvar packages-no-package-hierarchy-regexp + (concat "\\(" paths-version-control-filename-regexp "\\)" + "\\|" + "\\(" packages-special-base-regexp "\\)") + "Directories which can't be the roots of package hierarchies.") + +(defun packages-find-packages-in-directories (directories) + "Find all packages underneath directories in DIRECTORIES." + (paths-find-recursive-path directories + packages-hierarchy-depth + packages-no-package-hierarchy-regexp)) + +(defun packages-split-path (path) + "Split PATH at \"\", return pair with two components. +The second component is shared with PATH." + (let ((reverse-tail '()) + (rest path)) + (while (and rest (null (string-equal "" (car rest)))) + (setq reverse-tail (cons (car rest) reverse-tail)) + (setq rest (cdr rest))) + (if (null rest) + (cons path nil) + (cons (nreverse reverse-tail) (cdr rest))))) + +(defun packages-split-package-path (package-path) + "Split up PACKAGE-PATH into early, late and last components. +The separation is by \"\" components. +This returns (LIST EARLY-PACKAGES LATE-PACKAGES LAST-PACKAGES)." + ;; When in doubt, it's late + (let* ((stuff (packages-split-path package-path)) + (early (and (cdr stuff) (car stuff))) + (late+last (or (cdr stuff) (car stuff))) + (stuff (packages-split-path late+last)) + (late (car stuff)) + (last (cdr stuff))) + (list (packages-find-packages-in-directories early) + (packages-find-packages-in-directories late) + (packages-find-packages-in-directories last)))) + +(defun packages-deconstruct (list consumer) + "Deconstruct LIST and feed it to CONSUMER." + (apply consumer list)) + +(defun packages-find-packages-by-name (roots name) + "Find a package hierarchy by its name." + (packages-find-packages-in-directories + (if (and (file-name-absolute-p name) + (file-name-directory (expand-file-name name))) + (list (file-name-as-directory (expand-file-name name))) + (packages-find-package-directories roots name)))) + +(defun packages-find-packages-at-time + (roots package-locations time &optional default) + "Find packages at given time. +For the format of PACKAGE-LOCATIONS, see the global variable of the same name. +TIME is either 'EARLY, 'LATE, or 'LAST. +DEFAULT is a default list of packages." + (or default + (let ((packages '())) + (while package-locations + (packages-deconstruct + (car package-locations) + #'(lambda (name a-time thunk) + (if (and (eq time a-time) + (funcall thunk)) + (setq packages + (nconc packages + (packages-find-packages-by-name roots name)))))) + (setq package-locations (cdr package-locations))) + packages))) + +(defun packages-find-packages (roots package-locations) + "Find the packages." + (let ((envvar-value (getenv "EMACSPACKAGEPATH"))) + (if envvar-value + (packages-split-package-path (paths-decode-directory-path envvar-value)) + (packages-deconstruct + (packages-split-package-path configure-package-path) + #'(lambda (configure-early-packages + configure-late-packages + configure-last-packages) + (list (packages-find-packages-at-time roots package-locations 'early + configure-early-packages) + (packages-find-packages-at-time roots package-locations 'late + configure-late-packages) + (packages-find-packages-at-time roots package-locations 'last + configure-last-packages))))))) + +(defun packages-find-package-library-path (packages suffixes) + "Construct a path into a component of the packages hierarchy. +PACKAGES is a list of package directories. +SUFFIXES is a list of names of package subdirectories to look for." + (let ((directories + (apply + #'nconc + (mapcar #'(lambda (package) + (mapcar #'(lambda (suffix) + (file-name-as-directory (concat package suffix))) + suffixes)) + packages)))) + (paths-directories-which-exist directories))) + +(defun packages-find-package-load-path (packages) + "Construct the load-path component for packages. +PACKAGES is a list of package directories." + (paths-find-recursive-load-path + (packages-find-package-library-path packages + '("lisp")) + packages-load-path-depth)) + +(defun packages-find-package-exec-path (packages) + "Construct the exec-path component for packages. +PACKAGES is a list of package directories." + (packages-find-package-library-path packages + (list (paths-construct-path + (list "bin" system-configuration)) + "lib-src"))) + +(defun packages-find-package-info-path (packages) + "Construct the info-path component for packages. +PACKAGES is a list of package directories." + (packages-find-package-library-path packages '("info"))) + +(defun packages-find-package-data-path (packages) + "Construct the data-path component for packages. +PACKAGES is a list of package directories." + (paths-find-recursive-load-path + (packages-find-package-library-path packages + '("etc")) + packages-data-path-depth)) + +;; Loading package initialization files + +(defun packages-load-package-lisps (package-load-path base) + "Load all Lisp files of a certain name along a load path. +BASE is the base name of the files." + (mapcar #'(lambda (dir) + (let ((file-name (expand-file-name base dir))) + (condition-case error + (load file-name t t) + (error + (warn (format "Autoload error in: %s:\n\t%s" + file-name + (with-output-to-string + (display-error error nil)))))))) + package-load-path)) + +(defun packages-load-package-auto-autoloads (package-load-path) + "Load auto-autoload files along a load path." + (packages-load-package-lisps package-load-path + (file-name-sans-extension autoload-file-name))) + +(defun packages-handle-package-dumped-lisps (handle package-load-path) + "Load dumped-lisp.el files along a load path. +Call HANDLE on each file off definitions of PACKAGE-LISP there." + (mapcar #'(lambda (dir) + (let ((file-name (expand-file-name "dumped-lisp.el" dir))) + (if (file-exists-p file-name) + (let (package-lisp + ;; 20.4 packages could set this + preloaded-file-list) + (load file-name) + ;; dumped-lisp.el could have set this ... + (if package-lisp + (mapcar #'(lambda (base) + (funcall handle base)) + package-lisp)))))) + package-load-path)) + +(defun packages-load-package-dumped-lisps (package-load-path) + "Load dumped-lisp.el files along a load path. +Also load files off PACKAGE-LISP definitions there" + (packages-handle-package-dumped-lisps #'load package-load-path)) + +(defun packages-collect-package-dumped-lisps (package-load-path) + "Load dumped-lisp.el files along a load path. +Return list of files off PACKAGE-LISP definitions there" + (let ((*files* '())) + (packages-handle-package-dumped-lisps + #'(lambda (file) + (setq *files* (cons file *files*))) + package-load-path) + (reverse *files*))) + +(provide 'packages) + +;;; packages.el ends here