428
+ − 1 ;;; shadow.el --- Locate Emacs Lisp file shadowings.
+ − 2
+ − 3 ;; Copyright (C) 1995 Free Software Foundation, Inc.
+ − 4
+ − 5 ;; Author: Terry Jones <terry@santafe.edu>
+ − 6 ;; Keywords: lisp
+ − 7 ;; Created: 15 December 1995
+ − 8
+ − 9 ;; This file is part of XEmacs.
+ − 10
+ − 11 ;; XEmacs is free software; you can redistribute it and/or modify
+ − 12 ;; it under the terms of the GNU General Public License as published by
+ − 13 ;; the Free Software Foundation; either version 2, or (at your option)
+ − 14 ;; any later version.
+ − 15
613
+ − 16 ;; XEmacs is distributed in the hope that it will be useful,
428
+ − 17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+ − 18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ − 19 ;; GNU General Public License for more details.
+ − 20
+ − 21 ;; You should have received a copy of the GNU General Public License
613
+ − 22 ;; along with XEmacs; see the file COPYING. If not, write to the
428
+ − 23 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ − 24 ;; Boston, MA 02111-1307, USA.
+ − 25
+ − 26 ;;; Commentary:
+ − 27
+ − 28 ;; The functions in this file detect (`find-emacs-lisp-shadows')
+ − 29 ;; and display (`list-load-path-shadows') potential load-path
+ − 30 ;; problems that arise when Emacs Lisp files "shadow" each other.
+ − 31 ;;
+ − 32 ;; For example, a file XXX.el early in one's load-path will shadow
+ − 33 ;; a file with the same name in a later load-path directory. When
+ − 34 ;; this is unintentional, it may result in problems that could have
+ − 35 ;; been easily avoided. This occurs often (to me) when installing a
+ − 36 ;; new version of emacs and something in the site-lisp directory
+ − 37 ;; has been updated and added to the emacs distribution. The old
+ − 38 ;; version, now outdated, shadows the new one. This is obviously
+ − 39 ;; undesirable.
+ − 40 ;;
+ − 41 ;; The `list-load-path-shadows' function was run when you installed
+ − 42 ;; this version of emacs. To run it by hand in emacs:
+ − 43 ;;
+ − 44 ;; M-x load-library RET shadow RET
+ − 45 ;; M-x list-load-path-shadows
+ − 46 ;;
+ − 47 ;; or run it non-interactively via:
+ − 48 ;;
+ − 49 ;; emacs -batch -l shadow.el -f list-load-path-shadows
+ − 50 ;;
+ − 51 ;; Thanks to Francesco Potorti` <pot@cnuce.cnr.it> for suggestions,
+ − 52 ;; rewritings & speedups.
+ − 53
+ − 54 ;; 1998-08-15 Martin Buchholz: Speed up using hash tables instead of lists.
+ − 55
+ − 56 ;;; Code:
+ − 57
+ − 58 (defun find-emacs-lisp-shadows (&optional path)
+ − 59 "Return a list of Emacs Lisp files that create shadows.
+ − 60 This function does the work for `list-load-path-shadows'.
+ − 61
+ − 62 We traverse PATH looking for shadows, and return a \(possibly empty\)
+ − 63 even-length list of files. A file in this list at position 2i shadows
+ − 64 the file in position 2i+1. Emacs Lisp file suffixes \(.el and .elc\)
+ − 65 are stripped from the file names in the list.
+ − 66
+ − 67 See the documentation for `list-load-path-shadows' for further information."
+ − 68
+ − 69 (let (shadows ; List of shadowings, to be returned.
+ − 70 dir ; The dir being currently scanned.
+ − 71 curr-files ; This dir's Emacs Lisp files.
+ − 72 orig-dir ; Where the file was first seen.
+ − 73 (file-dirs ; File names ever seen, with dirs.
+ − 74 (make-hash-table :size 2000 :test 'equal))
+ − 75 (true-names ; Dirs ever considered.
+ − 76 (make-hash-table :size 50 :test 'equal))
+ − 77 (files-seen-this-dir ; Files seen so far in this dir.
+ − 78 (make-hash-table :size 100 :test 'equal))
+ − 79 )
+ − 80
+ − 81 (dolist (path-elt (or path load-path))
+ − 82
+ − 83 (setq dir (file-truename (or path-elt ".")))
+ − 84 (if (gethash dir true-names)
+ − 85 ;; We have already considered this PATH redundant directory.
+ − 86 ;; Show the redundancy if we are interactive, unless the PATH
+ − 87 ;; dir is nil or "." (these redundant directories are just a
+ − 88 ;; result of the current working directory, and are therefore
+ − 89 ;; not always redundant).
+ − 90 (or noninteractive
+ − 91 (and path-elt
+ − 92 (not (string= path-elt "."))
+ − 93 (message "Ignoring redundant directory %s" path-elt)))
+ − 94
+ − 95 (puthash dir t true-names)
+ − 96 (setq dir (or path-elt "."))
+ − 97 (setq curr-files (if (file-accessible-directory-p dir)
+ − 98 (directory-files dir nil ".\\.elc?$" t)))
+ − 99 (and curr-files
+ − 100 (not noninteractive)
+ − 101 (message "Checking %d files in %s..." (length curr-files) dir))
+ − 102
+ − 103 (clrhash files-seen-this-dir)
+ − 104
+ − 105 (dolist (file curr-files)
+ − 106
+ − 107 (setq file (substring
+ − 108 file 0 (if (string= (substring file -1) "c") -4 -3)))
+ − 109
+ − 110 ;; FILE now contains the current file name, with no suffix.
+ − 111 (unless (or (gethash file files-seen-this-dir)
+ − 112 ;; Ignore these files.
+ − 113 (member file
+ − 114 '("subdirs"
+ − 115 "auto-autoloads"
+ − 116 "custom-load"
2562
+ − 117 "custom-defines"
428
+ − 118 "dumped-lisp"
+ − 119 "_pkg"
+ − 120 "lpath")))
+ − 121 ;; File has not been seen yet in this directory.
+ − 122 ;; This test prevents us declaring that XXX.el shadows
+ − 123 ;; XXX.elc (or vice-versa) when they are in the same directory.
+ − 124 (puthash file t files-seen-this-dir)
+ − 125
+ − 126 (if (setq orig-dir (gethash file file-dirs))
+ − 127 ;; This file was seen before, we have a shadowing.
+ − 128 (setq shadows
+ − 129 (nconc shadows
+ − 130 (list (concat (file-name-as-directory orig-dir)
+ − 131 file)
+ − 132 (concat (file-name-as-directory dir)
+ − 133 file))))
+ − 134
+ − 135 ;; Not seen before, add it to the list of seen files.
+ − 136 (puthash file dir file-dirs))))))
+ − 137
+ − 138 ;; Return the list of shadowings.
+ − 139 shadows))
+ − 140
+ − 141
+ − 142 ;;;###autoload
+ − 143 (defun list-load-path-shadows ()
+ − 144 "Display a list of Emacs Lisp files that shadow other files.
+ − 145
+ − 146 This function lists potential load-path problems. Directories in the
+ − 147 `load-path' variable are searched, in order, for Emacs Lisp
+ − 148 files. When a previously encountered file name is found again, a
+ − 149 message is displayed indicating that the later file is \"hidden\" by
+ − 150 the earlier.
+ − 151
+ − 152 For example, suppose `load-path' is set to
+ − 153
+ − 154 \(\"/usr/gnu/emacs/site-lisp\" \"/usr/gnu/emacs/share/emacs/19.30/lisp\"\)
+ − 155
+ − 156 and that each of these directories contains a file called XXX.el. Then
+ − 157 XXX.el in the site-lisp directory is referred to by all of:
+ − 158 \(require 'XXX\), \(autoload .... \"XXX\"\), \(load-library \"XXX\"\) etc.
+ − 159
+ − 160 The first XXX.el file prevents emacs from seeing the second \(unless
+ − 161 the second is loaded explicitly via load-file\).
+ − 162
+ − 163 When not intended, such shadowings can be the source of subtle
+ − 164 problems. For example, the above situation may have arisen because the
+ − 165 XXX package was not distributed with versions of emacs prior to
+ − 166 19.30. An emacs maintainer downloaded XXX from elsewhere and installed
+ − 167 it. Later, XXX was updated and included in the emacs distribution.
+ − 168 Unless the emacs maintainer checks for this, the new version of XXX
+ − 169 will be hidden behind the old \(which may no longer work with the new
+ − 170 emacs version\).
+ − 171
+ − 172 This function performs these checks and flags all possible
+ − 173 shadowings. Because a .el file may exist without a corresponding .elc
+ − 174 \(or vice-versa\), these suffixes are essentially ignored. A file
+ − 175 XXX.elc in an early directory \(that does not contain XXX.el\) is
+ − 176 considered to shadow a later file XXX.el, and vice-versa.
+ − 177
+ − 178 When run interactively, the shadowings \(if any\) are displayed in a
+ − 179 buffer called `*Shadows*'. Shadowings are located by calling the
+ − 180 \(non-interactive\) companion function, `find-emacs-lisp-shadows'."
+ − 181
+ − 182 (interactive)
+ − 183 (let* ((path (copy-sequence load-path))
+ − 184 (tem path)
+ − 185 toplevs)
+ − 186 ;; If we can find simple.el in two places,
+ − 187 (while tem
+ − 188 (if (file-exists-p (expand-file-name "simple.el" (car tem)))
+ − 189 (setq toplevs (cons (car tem) toplevs)))
+ − 190 (setq tem (cdr tem)))
+ − 191 (if (> (length toplevs) 1)
+ − 192 ;; Cut off our copy of load-path right before
+ − 193 ;; the second directory which has simple.el in it.
+ − 194 ;; This avoids loads of duplications between the source dir
+ − 195 ;; and the dir where these files were copied by installation.
+ − 196 (let ((break (nth (- (length toplevs) 2) toplevs)))
+ − 197 (setq tem path)
+ − 198 (while tem
+ − 199 (if (eq (nth 1 tem) break)
+ − 200 (progn
+ − 201 (setcdr tem nil)
+ − 202 (setq tem nil)))
+ − 203 (setq tem (cdr tem)))))
+ − 204
+ − 205 (let* ((shadows (find-emacs-lisp-shadows path))
+ − 206 (n (/ (length shadows) 2))
+ − 207 (msg (format "%s Emacs Lisp load-path shadowing%s found"
+ − 208 (if (zerop n) "No" (concat "\n" (number-to-string n)))
+ − 209 (if (= n 1) " was" "s were"))))
+ − 210 (if (interactive-p)
+ − 211 (save-excursion
+ − 212 ;; We are interactive.
+ − 213 ;; Create the *Shadows* buffer and display shadowings there.
+ − 214 (let ((output-buffer (get-buffer-create "*Shadows*")))
+ − 215 (display-buffer output-buffer)
+ − 216 (set-buffer output-buffer)
+ − 217 (erase-buffer)
+ − 218 (while shadows
+ − 219 (insert (format "%s hides %s\n" (car shadows)
+ − 220 (car (cdr shadows))))
+ − 221 (setq shadows (cdr (cdr shadows))))
+ − 222 (insert msg "\n")))
+ − 223 ;; We are non-interactive, print shadows via message.
+ − 224 (when shadows
+ − 225 (message "This site has duplicate Lisp libraries with the same name.
+ − 226 If a locally-installed Lisp library overrides a library in the Emacs release,
+ − 227 that can cause trouble, and you should probably remove the locally-installed
+ − 228 version unless you know what you are doing.\n")
+ − 229 (while shadows
+ − 230 (message "%s hides %s" (car shadows) (car (cdr shadows)))
+ − 231 (setq shadows (cdr (cdr shadows))))
+ − 232 (message "%s" msg))))))
+ − 233
+ − 234 (provide 'shadow)
+ − 235
+ − 236 ;;; shadow.el ends here