38436
|
1 ;;; shadow.el --- locate Emacs Lisp file shadowings
|
14058
|
2
|
68648
|
3 ;; Copyright (C) 1995, 2002, 2003, 2004, 2005,
|
|
4 ;; 2006 Free Software Foundation, Inc.
|
14058
|
5
|
|
6 ;; Author: Terry Jones <terry@santafe.edu>
|
|
7 ;; Keywords: lisp
|
|
8 ;; Created: 15 December 1995
|
|
9
|
|
10 ;; This file is part of GNU Emacs.
|
|
11
|
|
12 ;; GNU Emacs is free software; you can redistribute it and/or modify
|
|
13 ;; it under the terms of the GNU General Public License as published by
|
|
14 ;; the Free Software Foundation; either version 2, or (at your option)
|
|
15 ;; any later version.
|
|
16
|
|
17 ;; GNU Emacs is distributed in the hope that it will be useful,
|
|
18 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
19 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
20 ;; GNU General Public License for more details.
|
|
21
|
|
22 ;; You should have received a copy of the GNU General Public License
|
14169
|
23 ;; along with GNU Emacs; see the file COPYING. If not, write to the
|
64085
|
24 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
25 ;; Boston, MA 02110-1301, USA.
|
14058
|
26
|
|
27 ;;; Commentary:
|
14169
|
28
|
14058
|
29 ;; The functions in this file detect (`find-emacs-lisp-shadows')
|
|
30 ;; and display (`list-load-path-shadows') potential load-path
|
|
31 ;; problems that arise when Emacs Lisp files "shadow" each other.
|
|
32 ;;
|
|
33 ;; For example, a file XXX.el early in one's load-path will shadow
|
|
34 ;; a file with the same name in a later load-path directory. When
|
|
35 ;; this is unintentional, it may result in problems that could have
|
|
36 ;; been easily avoided. This occurs often (to me) when installing a
|
|
37 ;; new version of emacs and something in the site-lisp directory
|
|
38 ;; has been updated and added to the emacs distribution. The old
|
|
39 ;; version, now outdated, shadows the new one. This is obviously
|
|
40 ;; undesirable.
|
|
41 ;;
|
|
42 ;; The `list-load-path-shadows' function was run when you installed
|
|
43 ;; this version of emacs. To run it by hand in emacs:
|
|
44 ;;
|
|
45 ;; M-x load-library RET shadow RET
|
|
46 ;; M-x list-load-path-shadows
|
|
47 ;;
|
|
48 ;; or run it non-interactively via:
|
|
49 ;;
|
|
50 ;; emacs -batch -l shadow.el -f list-load-path-shadows
|
|
51 ;;
|
|
52 ;; Thanks to Francesco Potorti` <pot@cnuce.cnr.it> for suggestions,
|
|
53 ;; rewritings & speedups.
|
|
54
|
|
55 ;;; Code:
|
|
56
|
48459
f1f792410820
(defgroup lisp-shadow): New group name. Previous group name shadow is
Markus Rost <rost@math.uni-bielefeld.de>
diff
changeset
|
57 (defgroup lisp-shadow nil
|
21365
|
58 "Locate Emacs Lisp file shadowings."
|
|
59 :prefix "shadows-"
|
|
60 :group 'lisp)
|
|
61
|
|
62 (defcustom shadows-compare-text-p nil
|
19982
|
63 "*If non-nil, then shadowing files are reported only if their text differs.
|
21365
|
64 This is slower, but filters out some innocuous shadowing."
|
|
65 :type 'boolean
|
48459
f1f792410820
(defgroup lisp-shadow): New group name. Previous group name shadow is
Markus Rost <rost@math.uni-bielefeld.de>
diff
changeset
|
66 :group 'lisp-shadow)
|
19982
|
67
|
14058
|
68 (defun find-emacs-lisp-shadows (&optional path)
|
|
69 "Return a list of Emacs Lisp files that create shadows.
|
|
70 This function does the work for `list-load-path-shadows'.
|
|
71
|
|
72 We traverse PATH looking for shadows, and return a \(possibly empty\)
|
|
73 even-length list of files. A file in this list at position 2i shadows
|
|
74 the file in position 2i+1. Emacs Lisp file suffixes \(.el and .elc\)
|
|
75 are stripped from the file names in the list.
|
|
76
|
|
77 See the documentation for `list-load-path-shadows' for further information."
|
49598
|
78
|
14058
|
79 (or path (setq path load-path))
|
|
80
|
|
81 (let (true-names ; List of dirs considered.
|
|
82 shadows ; List of shadowings, to be returned.
|
|
83 files ; File names ever seen, with dirs.
|
|
84 dir ; The dir being currently scanned.
|
|
85 curr-files ; This dir's Emacs Lisp files.
|
|
86 orig-dir ; Where the file was first seen.
|
|
87 files-seen-this-dir ; Files seen so far in this dir.
|
|
88 file) ; The current file.
|
|
89
|
49598
|
90
|
14058
|
91 (while path
|
|
92
|
19982
|
93 (setq dir (directory-file-name (file-truename (or (car path) "."))))
|
14058
|
94 (if (member dir true-names)
|
|
95 ;; We have already considered this PATH redundant directory.
|
|
96 ;; Show the redundancy if we are interactiver, unless the PATH
|
|
97 ;; dir is nil or "." (these redundant directories are just a
|
|
98 ;; result of the current working directory, and are therefore
|
|
99 ;; not always redundant).
|
|
100 (or noninteractive
|
|
101 (and (car path)
|
|
102 (not (string= (car path) "."))
|
15736
|
103 (message "Ignoring redundant directory %s" (car path))))
|
19982
|
104
|
14058
|
105 (setq true-names (append true-names (list dir)))
|
19982
|
106 (setq dir (directory-file-name (or (car path) ".")))
|
14058
|
107 (setq curr-files (if (file-accessible-directory-p dir)
|
67018
|
108 (directory-files dir nil ".\\.elc?\\(\\.gz\\)?$" t)))
|
14058
|
109 (and curr-files
|
|
110 (not noninteractive)
|
15736
|
111 (message "Checking %d files in %s..." (length curr-files) dir))
|
19982
|
112
|
14058
|
113 (setq files-seen-this-dir nil)
|
|
114
|
|
115 (while curr-files
|
|
116
|
|
117 (setq file (car curr-files))
|
67018
|
118 (if (string-match "\\.gz$" file)
|
|
119 (setq file (substring file 0 -3)))
|
14058
|
120 (setq file (substring
|
|
121 file 0 (if (string= (substring file -1) "c") -4 -3)))
|
|
122
|
19226
|
123 ;; FILE now contains the current file name, with no suffix.
|
|
124 (unless (or (member file files-seen-this-dir)
|
|
125 ;; Ignore these files.
|
|
126 (member file '("subdirs")))
|
14058
|
127 ;; File has not been seen yet in this directory.
|
|
128 ;; This test prevents us declaring that XXX.el shadows
|
|
129 ;; XXX.elc (or vice-versa) when they are in the same directory.
|
|
130 (setq files-seen-this-dir (cons file files-seen-this-dir))
|
49598
|
131
|
14058
|
132 (if (setq orig-dir (assoc file files))
|
|
133 ;; This file was seen before, we have a shadowing.
|
19982
|
134 ;; Report it unless the files are identical.
|
|
135 (let ((base1 (concat (cdr orig-dir) "/" file))
|
|
136 (base2 (concat dir "/" file)))
|
|
137 (if (not (and shadows-compare-text-p
|
|
138 (shadow-same-file-or-nonexistent
|
|
139 (concat base1 ".el") (concat base2 ".el"))
|
|
140 ;; This is a bit strict, but safe.
|
|
141 (shadow-same-file-or-nonexistent
|
|
142 (concat base1 ".elc") (concat base2 ".elc"))))
|
22170
|
143 (setq shadows
|
|
144 (append shadows (list base1 base2)))))
|
14058
|
145
|
|
146 ;; Not seen before, add it to the list of seen files.
|
|
147 (setq files (cons (cons file dir) files))))
|
|
148
|
|
149 (setq curr-files (cdr curr-files))))
|
|
150 (setq path (cdr path)))
|
|
151
|
|
152 ;; Return the list of shadowings.
|
|
153 shadows))
|
|
154
|
19982
|
155 ;; Return true if neither file exists, or if both exist and have identical
|
|
156 ;; contents.
|
|
157 (defun shadow-same-file-or-nonexistent (f1 f2)
|
|
158 (let ((exists1 (file-exists-p f1))
|
|
159 (exists2 (file-exists-p f2)))
|
|
160 (or (and (not exists1) (not exists2))
|
|
161 (and exists1 exists2
|
|
162 (or (equal (file-truename f1) (file-truename f2))
|
|
163 ;; As a quick test, avoiding spawning a process, compare file
|
|
164 ;; sizes.
|
|
165 (and (= (nth 7 (file-attributes f1))
|
|
166 (nth 7 (file-attributes f2)))
|
53477
|
167 (eq 0 (call-process "cmp" nil nil nil "-s" f1 f2))))))))
|
14058
|
168
|
|
169 ;;;###autoload
|
|
170 (defun list-load-path-shadows ()
|
15756
|
171 "Display a list of Emacs Lisp files that shadow other files.
|
14058
|
172
|
|
173 This function lists potential load-path problems. Directories in the
|
|
174 `load-path' variable are searched, in order, for Emacs Lisp
|
15756
|
175 files. When a previously encountered file name is found again, a
|
|
176 message is displayed indicating that the later file is \"hidden\" by
|
14058
|
177 the earlier.
|
|
178
|
|
179 For example, suppose `load-path' is set to
|
|
180
|
|
181 \(\"/usr/gnu/emacs/site-lisp\" \"/usr/gnu/emacs/share/emacs/19.30/lisp\"\)
|
|
182
|
|
183 and that each of these directories contains a file called XXX.el. Then
|
|
184 XXX.el in the site-lisp directory is referred to by all of:
|
|
185 \(require 'XXX\), \(autoload .... \"XXX\"\), \(load-library \"XXX\"\) etc.
|
|
186
|
|
187 The first XXX.el file prevents emacs from seeing the second \(unless
|
|
188 the second is loaded explicitly via load-file\).
|
|
189
|
|
190 When not intended, such shadowings can be the source of subtle
|
|
191 problems. For example, the above situation may have arisen because the
|
|
192 XXX package was not distributed with versions of emacs prior to
|
|
193 19.30. An emacs maintainer downloaded XXX from elsewhere and installed
|
|
194 it. Later, XXX was updated and included in the emacs distribution.
|
|
195 Unless the emacs maintainer checks for this, the new version of XXX
|
|
196 will be hidden behind the old \(which may no longer work with the new
|
|
197 emacs version\).
|
|
198
|
|
199 This function performs these checks and flags all possible
|
|
200 shadowings. Because a .el file may exist without a corresponding .elc
|
|
201 \(or vice-versa\), these suffixes are essentially ignored. A file
|
|
202 XXX.elc in an early directory \(that does not contain XXX.el\) is
|
|
203 considered to shadow a later file XXX.el, and vice-versa.
|
|
204
|
|
205 When run interactively, the shadowings \(if any\) are displayed in a
|
|
206 buffer called `*Shadows*'. Shadowings are located by calling the
|
|
207 \(non-interactive\) companion function, `find-emacs-lisp-shadows'."
|
49598
|
208
|
14058
|
209 (interactive)
|
19313
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
210 (let* ((path (copy-sequence load-path))
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
211 (tem path)
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
212 toplevs)
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
213 ;; If we can find simple.el in two places,
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
214 (while tem
|
67018
|
215 (if (or (file-exists-p (expand-file-name "simple.el" (car tem)))
|
|
216 (file-exists-p (expand-file-name "simple.el.gz" (car tem))))
|
19313
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
217 (setq toplevs (cons (car tem) toplevs)))
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
218 (setq tem (cdr tem)))
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
219 (if (> (length toplevs) 1)
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
220 ;; Cut off our copy of load-path right before
|
44203
621fbfd0bf87
(list-load-path-shadows): Only ignore last copy of standard Lisp
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
221 ;; the last directory which has simple.el in it.
|
19313
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
222 ;; This avoids loads of duplications between the source dir
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
223 ;; and the dir where these files were copied by installation.
|
44203
621fbfd0bf87
(list-load-path-shadows): Only ignore last copy of standard Lisp
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
224 (let ((break (car toplevs)))
|
19313
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
225 (setq tem path)
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
226 (while tem
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
227 (if (eq (nth 1 tem) break)
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
228 (progn
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
229 (setcdr tem nil)
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
230 (setq tem nil)))
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
231 (setq tem (cdr tem)))))
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
232
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
233 (let* ((shadows (find-emacs-lisp-shadows path))
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
234 (n (/ (length shadows) 2))
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
235 (msg (format "%s Emacs Lisp load-path shadowing%s found"
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
236 (if (zerop n) "No" (concat "\n" (number-to-string n)))
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
237 (if (= n 1) " was" "s were"))))
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
238 (if (interactive-p)
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
239 (save-excursion
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
240 ;; We are interactive.
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
241 ;; Create the *Shadows* buffer and display shadowings there.
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
242 (let ((output-buffer (get-buffer-create "*Shadows*")))
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
243 (display-buffer output-buffer)
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
244 (set-buffer output-buffer)
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
245 (erase-buffer)
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
246 (while shadows
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
247 (insert (format "%s hides %s\n" (car shadows)
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
248 (car (cdr shadows))))
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
249 (setq shadows (cdr (cdr shadows))))
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
250 (insert msg "\n")))
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
251 ;; We are non-interactive, print shadows via message.
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
252 (when shadows
|
04175c55c49b
(list-load-path-shadows): Exclude, from the path we search, all but
Richard M. Stallman <rms@gnu.org>
diff
changeset
|
253 (message "This site has duplicate Lisp libraries with the same name.
|
19226
|
254 If a locally-installed Lisp library overrides a library in the Emacs release,
|
|
255 that can cause trouble, and you should probably remove the locally-installed
|
21927
|
256 version unless you know what you are doing.\n")
|
|
257 (while shadows
|
|
258 (message "%s hides %s" (car shadows) (car (cdr shadows)))
|
|
259 (setq shadows (cdr (cdr shadows))))
|
|
260 (message "%s" msg))))))
|
14058
|
261
|
|
262 (provide 'shadow)
|
|
263
|
52401
|
264 ;;; arch-tag: 0480e8a7-62ed-4a12-a9f6-f44ded9b0830
|
14058
|
265 ;;; shadow.el ends here
|