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") | |
d0cb5e04 RS |
237 | ;; Warn if we put a line in loaddefs.el |
238 | ;; that is long enough to cause trouble. | |
239 | (while (< (point) output-end) | |
240 | (let ((beg (point))) | |
241 | (end-of-line) | |
242 | (if (> (- (point) beg) 900) | |
243 | (progn | |
244 | (message "A line is too long--over 900 characters") | |
245 | (sleep-for 2) | |
246 | (goto-char output-end)))) | |
247 | (forward-line 1)) | |
0231f2dc JB |
248 | (goto-char output-end) |
249 | (insert generate-autoload-section-trailer))) | |
250 | (message "Generating autoloads for %s...done" file))) | |
e2b6138f | 251 | \f |
0231f2dc JB |
252 | (defconst generated-autoload-file "loaddefs.el" |
253 | "*File \\[update-file-autoloads] puts autoloads into. | |
254 | A .el file can set this in its local variables section to make its | |
255 | autoloads go somewhere else.") | |
256 | ||
257 | ;;;###autoload | |
258 | (defun update-file-autoloads (file) | |
259 | "Update the autoloads for FILE in `generated-autoload-file' | |
260 | \(which FILE might bind in its local variables)." | |
261 | (interactive "fUpdate autoloads for file: ") | |
262 | (let ((load-name (let ((name (file-name-nondirectory file))) | |
263 | (if (string-match "\\.elc?$" name) | |
264 | (substring name 0 (match-beginning 0)) | |
265 | name))) | |
d55e3ff0 | 266 | (found nil) |
0231f2dc JB |
267 | (existing-buffer (get-file-buffer file))) |
268 | (save-excursion | |
269 | ;; We want to get a value for generated-autoload-file from | |
270 | ;; the local variables section if it's there. | |
271 | (set-buffer (find-file-noselect file)) | |
272 | (set-buffer (find-file-noselect generated-autoload-file)) | |
273 | (save-excursion | |
274 | (save-restriction | |
275 | (widen) | |
276 | (goto-char (point-min)) | |
d55e3ff0 RM |
277 | ;; Look for the section for LOAD-NAME. |
278 | (while (and (not found) | |
279 | (search-forward generate-autoload-section-header nil t)) | |
0231f2dc JB |
280 | (let ((form (condition-case () |
281 | (read (current-buffer)) | |
282 | (end-of-file nil)))) | |
d55e3ff0 RM |
283 | (cond ((string= (nth 2 form) load-name) |
284 | ;; We found the section for this file. | |
285 | ;; Check if it is up to date. | |
286 | (let ((begin (match-beginning 0)) | |
287 | (last-time (nth 4 form)) | |
288 | (file-time (nth 5 (file-attributes file)))) | |
289 | (if (and (or (null existing-buffer) | |
290 | (not (buffer-modified-p existing-buffer))) | |
291 | (listp last-time) (= (length last-time) 2) | |
292 | (or (> (car last-time) (car file-time)) | |
293 | (and (= (car last-time) (car file-time)) | |
294 | (>= (nth 1 last-time) | |
295 | (nth 1 file-time))))) | |
296 | (progn | |
297 | (message "Autoload section for %s is up to date." | |
298 | file) | |
299 | (setq found 'up-to-date)) | |
300 | (search-forward generate-autoload-section-trailer) | |
301 | (delete-region begin (point)) | |
302 | (setq found t)))) | |
303 | ((string< load-name (nth 2 form)) | |
304 | ;; We've come to a section alphabetically later than | |
305 | ;; LOAD-NAME. We assume the file is in order and so | |
306 | ;; there must be no section for LOAD-NAME. We will | |
307 | ;; insert one before the section here. | |
308 | (goto-char (match-beginning 0)) | |
72c19d97 RM |
309 | (setq found 'new))))) |
310 | (or (eq found 'up-to-date) | |
311 | (and (eq found 'new) | |
312 | ;; Check that FILE has any cookies before generating a | |
313 | ;; new section for it. | |
314 | (save-excursion | |
315 | (set-buffer (find-file-noselect file)) | |
316 | (save-excursion | |
317 | (widen) | |
318 | (goto-char (point-min)) | |
319 | (if (search-forward (concat "\n" | |
320 | generate-autoload-cookie) | |
321 | nil t) | |
322 | nil | |
323 | (if (interactive-p) | |
324 | (message file " has no autoloads")) | |
325 | t)))) | |
57cf354d | 326 | (generate-file-autoloads file)))) |
e2b6138f | 327 | (if (interactive-p) (save-buffer)) |
0231f2dc JB |
328 | (if (and (null existing-buffer) |
329 | (setq existing-buffer (get-file-buffer file))) | |
330 | (kill-buffer existing-buffer))))) | |
331 | ||
332 | ;;;###autoload | |
333 | (defun update-autoloads-here () | |
d08589bf RM |
334 | "\ |
335 | Update sections of the current buffer generated by \\[update-file-autoloads]." | |
0231f2dc JB |
336 | (interactive) |
337 | (let ((generated-autoload-file (buffer-file-name))) | |
338 | (save-excursion | |
339 | (goto-char (point-min)) | |
340 | (while (search-forward generate-autoload-section-header nil t) | |
341 | (let* ((form (condition-case () | |
342 | (read (current-buffer)) | |
343 | (end-of-file nil))) | |
344 | (file (nth 3 form))) | |
345 | (if (and (stringp file) | |
346 | (or (get-file-buffer file) | |
347 | (file-exists-p file))) | |
348 | () | |
1eb0a345 RS |
349 | (setq file (if (y-or-n-p (format "Can't find library `%s'; remove its autoloads? " |
350 | (nth 2 form) file)) | |
0231f2dc JB |
351 | t |
352 | (condition-case () | |
1eb0a345 | 353 | (read-file-name (format "Find `%s' load file: " |
0231f2dc JB |
354 | (nth 2 form)) |
355 | nil nil t) | |
356 | (quit nil))))) | |
357 | (if file | |
358 | (let ((begin (match-beginning 0))) | |
359 | (search-forward generate-autoload-section-trailer) | |
360 | (delete-region begin (point)))) | |
361 | (if (stringp file) | |
362 | (generate-file-autoloads file))))))) | |
363 | ||
364 | ;;;###autoload | |
365 | (defun update-directory-autoloads (dir) | |
366 | "Run \\[update-file-autoloads] on each .el file in DIR." | |
367 | (interactive "DUpdate autoloads for directory: ") | |
9f4b7963 RS |
368 | (let ((enable-local-eval nil)) |
369 | (mapcar 'update-file-autoloads | |
370 | (directory-files dir t "^[^=].*\\.el$"))) | |
e2b6138f RM |
371 | (if (interactive-p) |
372 | (save-excursion | |
373 | (set-buffer (find-file-noselect generated-autoload-file)) | |
374 | (save-buffer)))) | |
0231f2dc JB |
375 | |
376 | ;;;###autoload | |
377 | (defun batch-update-autoloads () | |
378 | "Update the autoloads for the files or directories on the command line. | |
379 | Runs \\[update-file-autoloads] on files and \\[update-directory-autoloads] | |
380 | on directories. Must be used only with -batch, and kills Emacs on completion. | |
381 | Each file will be processed even if an error occurred previously. | |
1eb0a345 | 382 | For example, invoke `emacs -batch -f batch-update-autoloads *.el'." |
0231f2dc | 383 | (if (not noninteractive) |
23de5766 | 384 | (error "batch-update-autoloads is to be used only with -batch")) |
0231f2dc | 385 | (let ((lost nil) |
72c19d97 RM |
386 | (args command-line-args-left) |
387 | (enable-local-eval nil)) ;Don't query in batch mode. | |
388 | (message "Updating autoloads in %s..." generated-autoload-file) | |
389 | (let ((frob (function | |
390 | (lambda (file) | |
391 | (condition-case lossage | |
392 | (update-file-autoloads file) | |
393 | (error | |
394 | (princ ">>Error processing ") | |
395 | (princ file) | |
396 | (princ ": ") | |
397 | (if (fboundp 'display-error) | |
398 | (display-error lossage nil) | |
399 | (prin1 lossage)) | |
400 | (princ "\n") | |
401 | (setq lost t))))))) | |
402 | (while args | |
403 | (if (file-directory-p (expand-file-name (car args))) | |
404 | (let ((rest (directory-files (car args) t "\\.el$"))) | |
405 | (while rest | |
406 | (funcall frob (car rest)) | |
407 | (setq rest (cdr rest)))) | |
408 | (funcall frob (car args))) | |
e15068c4 | 409 | (setq args (cdr args)))) |
0231f2dc JB |
410 | (save-some-buffers t) |
411 | (message "Done") | |
412 | (kill-emacs (if lost 1 0)))) | |
413 | ||
414 | (provide 'autoload) | |
ffd56f97 | 415 | |
c0274f38 | 416 | ;;; autoload.el ends here |