Commit | Line | Data |
---|---|---|
c0274f38 ER |
1 | ;;; autoload.el --- maintain autoloads in loaddefs.el. |
2 | ||
d55e3ff0 | 3 | ;;; Copyright (C) 1991, 1992, 1993, 1994 Free Software Foundation, Inc. |
3a801d0c | 4 | ;;; |
e5167999 | 5 | ;; Author: Roland McGrath <roland@gnu.ai.mit.edu> |
e9571d2a | 6 | ;; Keywords: maint |
e5167999 | 7 | |
0231f2dc JB |
8 | ;;; This program is free software; you can redistribute it and/or modify |
9 | ;;; it under the terms of the GNU General Public License as published by | |
e5167999 | 10 | ;;; the Free Software Foundation; either version 2, or (at your option) |
0231f2dc JB |
11 | ;;; any later version. |
12 | ;;; | |
13 | ;;; This program is distributed in the hope that it will be useful, | |
14 | ;;; but WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
16 | ;;; GNU General Public License for more details. | |
17 | ;;; | |
18 | ;;; A copy of the GNU General Public License can be obtained from this | |
19 | ;;; program's author (send electronic mail to roland@ai.mit.edu) or from | |
20 | ;;; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA | |
21 | ;;; 02139, USA. | |
22 | ;;; | |
23 | ||
07b3798c | 24 | ;;; Commentary: |
e41b2db1 | 25 | |
00ee57f3 | 26 | ;; This code helps GNU Emacs maintainers keep the loaddefs.el file up to |
e41b2db1 ER |
27 | ;; date. It interprets magic cookies of the form ";;;###autoload" in |
28 | ;; lisp source files in various useful ways. To learn more, read the | |
29 | ;; source; if you're going to use this, you'd better be able to. | |
30 | ||
e5167999 ER |
31 | ;;; Code: |
32 | ||
0231f2dc JB |
33 | (defun make-autoload (form file) |
34 | "Turn FORM, a defun or defmacro, into an autoload for source file FILE. | |
35 | Returns nil if FORM is not a defun or defmacro." | |
36 | (let ((car (car-safe form))) | |
327ab40d RM |
37 | (if (memq car '(defun defmacro)) |
38 | (let ((macrop (eq car 'defmacro)) | |
39 | name doc) | |
0231f2dc JB |
40 | (setq form (cdr form)) |
41 | (setq name (car form)) | |
42 | ;; Ignore the arguments. | |
43 | (setq form (cdr (cdr form))) | |
44 | (setq doc (car form)) | |
45 | (if (stringp doc) | |
46 | (setq form (cdr form)) | |
47 | (setq doc nil)) | |
48 | (list 'autoload (list 'quote name) file doc | |
44893360 JB |
49 | (eq (car-safe (car form)) 'interactive) |
50 | (if macrop (list 'quote 'macro) nil))) | |
0231f2dc JB |
51 | nil))) |
52 | ||
53 | (defconst generate-autoload-cookie ";;;###autoload" | |
d08589bf RM |
54 | "Magic comment indicating the following form should be autoloaded. |
55 | Used by \\[update-file-autoloads]. This string should be | |
0231f2dc JB |
56 | meaningless to Lisp (e.g., a comment). |
57 | ||
58 | This string is used: | |
59 | ||
60 | ;;;###autoload | |
61 | \(defun function-to-be-autoloaded () ...) | |
62 | ||
63 | If this string appears alone on a line, the following form will be | |
64 | read and an autoload made for it. If there is further text on the line, | |
65 | that text will be copied verbatim to `generated-autoload-file'.") | |
66 | ||
67 | (defconst generate-autoload-section-header "\f\n;;;### " | |
68 | "String inserted before the form identifying | |
69 | the section of autoloads for a file.") | |
70 | ||
71 | (defconst generate-autoload-section-trailer "\n;;;***\n" | |
72 | "String which indicates the end of the section of autoloads for a file.") | |
73 | ||
daa37602 JB |
74 | ;;; Forms which have doc-strings which should be printed specially. |
75 | ;;; A doc-string-elt property of ELT says that (nth ELT FORM) is | |
76 | ;;; the doc-string in FORM. | |
77 | ;;; | |
78 | ;;; There used to be the following note here: | |
79 | ;;; ;;; Note: defconst and defvar should NOT be marked in this way. | |
80 | ;;; ;;; We don't want to produce defconsts and defvars that | |
81 | ;;; ;;; make-docfile can grok, because then it would grok them twice, | |
82 | ;;; ;;; once in foo.el (where they are given with ;;;###autoload) and | |
83 | ;;; ;;; once in loaddefs.el. | |
84 | ;;; | |
85 | ;;; Counter-note: Yes, they should be marked in this way. | |
86 | ;;; make-docfile only processes those files that are loaded into the | |
87 | ;;; dumped Emacs, and those files should never have anything | |
88 | ;;; autoloaded here. The above-feared problem only occurs with files | |
89 | ;;; which have autoloaded entries *and* are processed by make-docfile; | |
90 | ;;; there should be no such files. | |
91 | ||
0231f2dc | 92 | (put 'autoload 'doc-string-elt 3) |
daa37602 JB |
93 | (put 'defun 'doc-string-elt 3) |
94 | (put 'defvar 'doc-string-elt 3) | |
95 | (put 'defconst 'doc-string-elt 3) | |
96 | (put 'defmacro 'doc-string-elt 3) | |
0231f2dc | 97 | |
72c19d97 | 98 | (defun autoload-trim-file-name (file) |
1eb0a345 RS |
99 | ;; Returns a relative pathname of FILE |
100 | ;; starting from the directory that loaddefs.el is in. | |
101 | ;; That is normally a directory in load-path, | |
102 | ;; which means Emacs will be able to find FILE when it looks. | |
103 | ;; Any extra directory names here would prevent finding the file. | |
72c19d97 RM |
104 | (setq file (expand-file-name file)) |
105 | (file-relative-name file | |
1eb0a345 | 106 | (file-name-directory generated-autoload-file))) |
72c19d97 | 107 | |
0231f2dc JB |
108 | (defun generate-file-autoloads (file) |
109 | "Insert at point a loaddefs autoload section for FILE. | |
110 | autoloads are generated for defuns and defmacros in FILE | |
da8826b4 | 111 | marked by `generate-autoload-cookie' (which see). |
0231f2dc JB |
112 | If FILE is being visited in a buffer, the contents of the buffer |
113 | are used." | |
114 | (interactive "fGenerate autoloads for file: ") | |
115 | (let ((outbuf (current-buffer)) | |
0231f2dc JB |
116 | (autoloads-done '()) |
117 | (load-name (let ((name (file-name-nondirectory file))) | |
118 | (if (string-match "\\.elc?$" name) | |
119 | (substring name 0 (match-beginning 0)) | |
120 | name))) | |
121 | (print-length nil) | |
72c19d97 | 122 | (print-readably t) ; This does something in Lucid Emacs. |
aa7ea8bd | 123 | (float-output-format nil) |
0231f2dc | 124 | (done-any nil) |
7e4263eb | 125 | (visited (get-file-buffer file)) |
0231f2dc | 126 | output-end) |
daa37602 JB |
127 | |
128 | ;; If the autoload section we create here uses an absolute | |
129 | ;; pathname for FILE in its header, and then Emacs is installed | |
130 | ;; under a different path on another system, | |
131 | ;; `update-autoloads-here' won't be able to find the files to be | |
132 | ;; autoloaded. So, if FILE is in the same directory or a | |
e5d77022 | 133 | ;; subdirectory of the current buffer's directory, we'll make it |
daa37602 JB |
134 | ;; relative to the current buffer's directory. |
135 | (setq file (expand-file-name file)) | |
1265394f RM |
136 | (let* ((source-truename (file-truename file)) |
137 | (dir-truename (file-name-as-directory | |
138 | (file-truename default-directory))) | |
139 | (len (length dir-truename))) | |
140 | (if (and (< len (length source-truename)) | |
141 | (string= dir-truename (substring source-truename 0 len))) | |
142 | (setq file (substring source-truename len)))) | |
daa37602 | 143 | |
0231f2dc | 144 | (message "Generating autoloads for %s..." file) |
6798a385 JB |
145 | (save-excursion |
146 | (unwind-protect | |
147 | (progn | |
148 | (set-buffer (find-file-noselect file)) | |
149 | (save-excursion | |
150 | (save-restriction | |
151 | (widen) | |
152 | (goto-char (point-min)) | |
153 | (while (not (eobp)) | |
154 | (skip-chars-forward " \t\n\f") | |
155 | (cond | |
156 | ((looking-at (regexp-quote generate-autoload-cookie)) | |
157 | (search-forward generate-autoload-cookie) | |
158 | (skip-chars-forward " \t") | |
159 | (setq done-any t) | |
9ed4d64f RM |
160 | (if (eolp) |
161 | ;; Read the next form and make an autoload. | |
162 | (let* ((form (prog1 (read (current-buffer)) | |
ad9c7f2b | 163 | (or (bolp) (forward-line 1)))) |
9ed4d64f RM |
164 | (autoload (make-autoload form load-name)) |
165 | (doc-string-elt (get (car-safe form) | |
166 | 'doc-string-elt))) | |
167 | (if autoload | |
168 | (setq autoloads-done (cons (nth 1 form) | |
169 | autoloads-done)) | |
170 | (setq autoload form)) | |
171 | (if (and doc-string-elt | |
172 | (stringp (nth doc-string-elt autoload))) | |
173 | ;; We need to hack the printing because the | |
174 | ;; doc-string must be printed specially for | |
175 | ;; make-docfile (sigh). | |
176 | (let* ((p (nthcdr (1- doc-string-elt) | |
177 | autoload)) | |
178 | (elt (cdr p))) | |
179 | (setcdr p nil) | |
180 | (princ "\n(" outbuf) | |
181 | (let ((print-escape-newlines t)) | |
182 | (mapcar (function (lambda (elt) | |
183 | (prin1 elt outbuf) | |
184 | (princ " " outbuf))) | |
185 | autoload)) | |
186 | (princ "\"\\\n" outbuf) | |
187 | (let ((begin (save-excursion | |
188 | (set-buffer outbuf) | |
189 | (point)))) | |
6798a385 | 190 | (princ (substring |
72c19d97 RM |
191 | (prin1-to-string (car elt)) 1) |
192 | outbuf) | |
193 | ;; Insert a backslash before each ( that | |
194 | ;; appears at the beginning of a line in | |
195 | ;; the doc string. | |
196 | (save-excursion | |
197 | (set-buffer outbuf) | |
198 | (save-excursion | |
199 | (while (search-backward "\n(" begin t) | |
200 | (forward-char 1) | |
201 | (insert "\\")))) | |
202 | (if (null (cdr elt)) | |
203 | (princ ")" outbuf) | |
204 | (princ " " outbuf) | |
205 | (princ (substring | |
206 | (prin1-to-string (cdr elt)) | |
207 | 1) | |
208 | outbuf)) | |
9ed4d64f RM |
209 | (terpri outbuf))) |
210 | (let ((print-escape-newlines t)) | |
52859db8 | 211 | (print autoload outbuf)))) |
9ed4d64f RM |
212 | ;; Copy the rest of the line to the output. |
213 | (let ((begin (point))) | |
214 | (forward-line 1) | |
52859db8 | 215 | (princ (buffer-substring begin (point)) outbuf)))) |
72c19d97 RM |
216 | ((looking-at ";") |
217 | ;; Don't read the comment. | |
218 | (forward-line 1)) | |
219 | (t | |
220 | (forward-sexp 1) | |
221 | (forward-line 1))))))) | |
6798a385 JB |
222 | (or visited |
223 | ;; We created this buffer, so we should kill it. | |
224 | (kill-buffer (current-buffer))) | |
225 | (set-buffer outbuf) | |
226 | (setq output-end (point-marker)))) | |
0231f2dc JB |
227 | (if done-any |
228 | (progn | |
229 | (insert generate-autoload-section-header) | |
72c19d97 RM |
230 | (prin1 (list 'autoloads autoloads-done load-name |
231 | (autoload-trim-file-name file) | |
0231f2dc JB |
232 | (nth 5 (file-attributes file))) |
233 | outbuf) | |
234 | (terpri outbuf) | |
72c19d97 RM |
235 | (insert ";;; Generated autoloads from " |
236 | (autoload-trim-file-name file) "\n") | |
0231f2dc JB |
237 | (goto-char output-end) |
238 | (insert generate-autoload-section-trailer))) | |
239 | (message "Generating autoloads for %s...done" file))) | |
e2b6138f | 240 | \f |
0231f2dc JB |
241 | (defconst generated-autoload-file "loaddefs.el" |
242 | "*File \\[update-file-autoloads] puts autoloads into. | |
243 | A .el file can set this in its local variables section to make its | |
244 | autoloads go somewhere else.") | |
245 | ||
246 | ;;;###autoload | |
247 | (defun update-file-autoloads (file) | |
248 | "Update the autoloads for FILE in `generated-autoload-file' | |
249 | \(which FILE might bind in its local variables)." | |
250 | (interactive "fUpdate autoloads for file: ") | |
251 | (let ((load-name (let ((name (file-name-nondirectory file))) | |
252 | (if (string-match "\\.elc?$" name) | |
253 | (substring name 0 (match-beginning 0)) | |
254 | name))) | |
d55e3ff0 | 255 | (found nil) |
0231f2dc JB |
256 | (existing-buffer (get-file-buffer file))) |
257 | (save-excursion | |
258 | ;; We want to get a value for generated-autoload-file from | |
259 | ;; the local variables section if it's there. | |
260 | (set-buffer (find-file-noselect file)) | |
261 | (set-buffer (find-file-noselect generated-autoload-file)) | |
262 | (save-excursion | |
263 | (save-restriction | |
264 | (widen) | |
265 | (goto-char (point-min)) | |
d55e3ff0 RM |
266 | ;; Look for the section for LOAD-NAME. |
267 | (while (and (not found) | |
268 | (search-forward generate-autoload-section-header nil t)) | |
0231f2dc JB |
269 | (let ((form (condition-case () |
270 | (read (current-buffer)) | |
271 | (end-of-file nil)))) | |
d55e3ff0 RM |
272 | (cond ((string= (nth 2 form) load-name) |
273 | ;; We found the section for this file. | |
274 | ;; Check if it is up to date. | |
275 | (let ((begin (match-beginning 0)) | |
276 | (last-time (nth 4 form)) | |
277 | (file-time (nth 5 (file-attributes file)))) | |
278 | (if (and (or (null existing-buffer) | |
279 | (not (buffer-modified-p existing-buffer))) | |
280 | (listp last-time) (= (length last-time) 2) | |
281 | (or (> (car last-time) (car file-time)) | |
282 | (and (= (car last-time) (car file-time)) | |
283 | (>= (nth 1 last-time) | |
284 | (nth 1 file-time))))) | |
285 | (progn | |
286 | (message "Autoload section for %s is up to date." | |
287 | file) | |
288 | (setq found 'up-to-date)) | |
289 | (search-forward generate-autoload-section-trailer) | |
290 | (delete-region begin (point)) | |
291 | (setq found t)))) | |
292 | ((string< load-name (nth 2 form)) | |
293 | ;; We've come to a section alphabetically later than | |
294 | ;; LOAD-NAME. We assume the file is in order and so | |
295 | ;; there must be no section for LOAD-NAME. We will | |
296 | ;; insert one before the section here. | |
297 | (goto-char (match-beginning 0)) | |
72c19d97 RM |
298 | (setq found 'new))))) |
299 | (or (eq found 'up-to-date) | |
300 | (and (eq found 'new) | |
301 | ;; Check that FILE has any cookies before generating a | |
302 | ;; new section for it. | |
303 | (save-excursion | |
304 | (set-buffer (find-file-noselect file)) | |
305 | (save-excursion | |
306 | (widen) | |
307 | (goto-char (point-min)) | |
308 | (if (search-forward (concat "\n" | |
309 | generate-autoload-cookie) | |
310 | nil t) | |
311 | nil | |
312 | (if (interactive-p) | |
313 | (message file " has no autoloads")) | |
314 | t)))) | |
57cf354d | 315 | (generate-file-autoloads file)))) |
e2b6138f | 316 | (if (interactive-p) (save-buffer)) |
0231f2dc JB |
317 | (if (and (null existing-buffer) |
318 | (setq existing-buffer (get-file-buffer file))) | |
319 | (kill-buffer existing-buffer))))) | |
320 | ||
321 | ;;;###autoload | |
322 | (defun update-autoloads-here () | |
d08589bf RM |
323 | "\ |
324 | Update sections of the current buffer generated by \\[update-file-autoloads]." | |
0231f2dc JB |
325 | (interactive) |
326 | (let ((generated-autoload-file (buffer-file-name))) | |
327 | (save-excursion | |
328 | (goto-char (point-min)) | |
329 | (while (search-forward generate-autoload-section-header nil t) | |
330 | (let* ((form (condition-case () | |
331 | (read (current-buffer)) | |
332 | (end-of-file nil))) | |
333 | (file (nth 3 form))) | |
334 | (if (and (stringp file) | |
335 | (or (get-file-buffer file) | |
336 | (file-exists-p file))) | |
337 | () | |
1eb0a345 RS |
338 | (setq file (if (y-or-n-p (format "Can't find library `%s'; remove its autoloads? " |
339 | (nth 2 form) file)) | |
0231f2dc JB |
340 | t |
341 | (condition-case () | |
1eb0a345 | 342 | (read-file-name (format "Find `%s' load file: " |
0231f2dc JB |
343 | (nth 2 form)) |
344 | nil nil t) | |
345 | (quit nil))))) | |
346 | (if file | |
347 | (let ((begin (match-beginning 0))) | |
348 | (search-forward generate-autoload-section-trailer) | |
349 | (delete-region begin (point)))) | |
350 | (if (stringp file) | |
351 | (generate-file-autoloads file))))))) | |
352 | ||
353 | ;;;###autoload | |
354 | (defun update-directory-autoloads (dir) | |
355 | "Run \\[update-file-autoloads] on each .el file in DIR." | |
356 | (interactive "DUpdate autoloads for directory: ") | |
9f4b7963 RS |
357 | (let ((enable-local-eval nil)) |
358 | (mapcar 'update-file-autoloads | |
359 | (directory-files dir t "^[^=].*\\.el$"))) | |
e2b6138f RM |
360 | (if (interactive-p) |
361 | (save-excursion | |
362 | (set-buffer (find-file-noselect generated-autoload-file)) | |
363 | (save-buffer)))) | |
0231f2dc JB |
364 | |
365 | ;;;###autoload | |
366 | (defun batch-update-autoloads () | |
367 | "Update the autoloads for the files or directories on the command line. | |
368 | Runs \\[update-file-autoloads] on files and \\[update-directory-autoloads] | |
369 | on directories. Must be used only with -batch, and kills Emacs on completion. | |
370 | Each file will be processed even if an error occurred previously. | |
1eb0a345 | 371 | For example, invoke `emacs -batch -f batch-update-autoloads *.el'." |
0231f2dc | 372 | (if (not noninteractive) |
23de5766 | 373 | (error "batch-update-autoloads is to be used only with -batch")) |
0231f2dc | 374 | (let ((lost nil) |
72c19d97 RM |
375 | (args command-line-args-left) |
376 | (enable-local-eval nil)) ;Don't query in batch mode. | |
377 | (message "Updating autoloads in %s..." generated-autoload-file) | |
378 | (let ((frob (function | |
379 | (lambda (file) | |
380 | (condition-case lossage | |
381 | (update-file-autoloads file) | |
382 | (error | |
383 | (princ ">>Error processing ") | |
384 | (princ file) | |
385 | (princ ": ") | |
386 | (if (fboundp 'display-error) | |
387 | (display-error lossage nil) | |
388 | (prin1 lossage)) | |
389 | (princ "\n") | |
390 | (setq lost t))))))) | |
391 | (while args | |
392 | (if (file-directory-p (expand-file-name (car args))) | |
393 | (let ((rest (directory-files (car args) t "\\.el$"))) | |
394 | (while rest | |
395 | (funcall frob (car rest)) | |
396 | (setq rest (cdr rest)))) | |
397 | (funcall frob (car args))) | |
e15068c4 | 398 | (setq args (cdr args)))) |
0231f2dc JB |
399 | (save-some-buffers t) |
400 | (message "Done") | |
401 | (kill-emacs (if lost 1 0)))) | |
402 | ||
403 | (provide 'autoload) | |
ffd56f97 | 404 | |
c0274f38 | 405 | ;;; autoload.el ends here |