Commit | Line | Data |
---|---|---|
c0274f38 ER |
1 | ;;; autoload.el --- maintain autoloads in loaddefs.el. |
2 | ||
7e4263eb | 3 | ;;; Copyright (C) 1991, 1992, 1993 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 | ||
e41b2db1 ER |
24 | ;;; Commentary;: |
25 | ||
26 | ;; This code helps GNU Emacs maintainers keep the autoload.el file up to | |
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 JB |
97 | |
98 | (defun generate-file-autoloads (file) | |
99 | "Insert at point a loaddefs autoload section for FILE. | |
100 | autoloads are generated for defuns and defmacros in FILE | |
da8826b4 | 101 | marked by `generate-autoload-cookie' (which see). |
0231f2dc JB |
102 | If FILE is being visited in a buffer, the contents of the buffer |
103 | are used." | |
104 | (interactive "fGenerate autoloads for file: ") | |
105 | (let ((outbuf (current-buffer)) | |
0231f2dc JB |
106 | (autoloads-done '()) |
107 | (load-name (let ((name (file-name-nondirectory file))) | |
108 | (if (string-match "\\.elc?$" name) | |
109 | (substring name 0 (match-beginning 0)) | |
110 | name))) | |
111 | (print-length nil) | |
112 | (floating-output-format "%20e") | |
113 | (done-any nil) | |
7e4263eb | 114 | (visited (get-file-buffer file)) |
0231f2dc | 115 | output-end) |
daa37602 JB |
116 | |
117 | ;; If the autoload section we create here uses an absolute | |
118 | ;; pathname for FILE in its header, and then Emacs is installed | |
119 | ;; under a different path on another system, | |
120 | ;; `update-autoloads-here' won't be able to find the files to be | |
121 | ;; autoloaded. So, if FILE is in the same directory or a | |
e5d77022 | 122 | ;; subdirectory of the current buffer's directory, we'll make it |
daa37602 JB |
123 | ;; relative to the current buffer's directory. |
124 | (setq file (expand-file-name file)) | |
ae553cad JB |
125 | (let ((source-truename (file-truename file)) |
126 | (dir-truename (file-name-as-directory | |
127 | (file-truename default-directory)))) | |
128 | (if (and (< (length dir-truename) (length source-truename)) | |
129 | (string= dir-truename | |
130 | (substring source-truename 0 (length dir-truename)))) | |
131 | (setq file (substring file (length dir-truename))))) | |
daa37602 | 132 | |
0231f2dc | 133 | (message "Generating autoloads for %s..." file) |
6798a385 JB |
134 | (save-excursion |
135 | (unwind-protect | |
136 | (progn | |
137 | (set-buffer (find-file-noselect file)) | |
138 | (save-excursion | |
139 | (save-restriction | |
140 | (widen) | |
141 | (goto-char (point-min)) | |
142 | (while (not (eobp)) | |
143 | (skip-chars-forward " \t\n\f") | |
144 | (cond | |
145 | ((looking-at (regexp-quote generate-autoload-cookie)) | |
146 | (search-forward generate-autoload-cookie) | |
147 | (skip-chars-forward " \t") | |
148 | (setq done-any t) | |
149 | (if (eolp) | |
150 | ;; Read the next form and make an autoload. | |
c7516631 | 151 | (let* ((form (prog1 (read (current-buffer)) |
6798a385 JB |
152 | (forward-line 1))) |
153 | (autoload (make-autoload form load-name)) | |
154 | (doc-string-elt (get (car-safe form) | |
155 | 'doc-string-elt))) | |
c7516631 RS |
156 | (if autoload |
157 | (setq autoloads-done (cons (nth 1 form) | |
158 | autoloads-done)) | |
159 | (setq autoload form)) | |
160 | (if (and doc-string-elt | |
161 | (stringp (nth doc-string-elt autoload))) | |
162 | ;; We need to hack the printing because the | |
163 | ;; doc-string must be printed specially for | |
164 | ;; make-docfile (sigh). | |
165 | (let* ((p (nthcdr (1- doc-string-elt) | |
166 | autoload)) | |
167 | (elt (cdr p))) | |
168 | (setcdr p nil) | |
169 | (princ "\n(" outbuf) | |
170 | (mapcar (function (lambda (elt) | |
171 | (prin1 elt outbuf) | |
172 | (princ " " outbuf))) | |
173 | autoload) | |
174 | (princ "\"\\\n" outbuf) | |
175 | (princ (substring | |
176 | (prin1-to-string (car elt)) 1) | |
177 | outbuf) | |
178 | (if (null (cdr elt)) | |
179 | (princ ")" outbuf) | |
180 | (princ " " outbuf) | |
6798a385 | 181 | (princ (substring |
c7516631 RS |
182 | (prin1-to-string (cdr elt)) |
183 | 1) | |
184 | outbuf)) | |
185 | (terpri outbuf)) | |
186 | (print autoload outbuf))) | |
6798a385 JB |
187 | ;; Copy the rest of the line to the output. |
188 | (let ((begin (point))) | |
189 | (forward-line 1) | |
190 | (princ (buffer-substring begin (point)) outbuf)))) | |
c7516631 RS |
191 | ((looking-at ";") |
192 | ;; Don't read the comment. | |
193 | (forward-line 1)) | |
194 | (t | |
195 | (forward-sexp 1) | |
196 | (forward-line 1))))))) | |
6798a385 JB |
197 | (or visited |
198 | ;; We created this buffer, so we should kill it. | |
199 | (kill-buffer (current-buffer))) | |
200 | (set-buffer outbuf) | |
201 | (setq output-end (point-marker)))) | |
0231f2dc JB |
202 | (if done-any |
203 | (progn | |
204 | (insert generate-autoload-section-header) | |
205 | (prin1 (list 'autoloads autoloads-done load-name file | |
206 | (nth 5 (file-attributes file))) | |
207 | outbuf) | |
208 | (terpri outbuf) | |
209 | (insert ";;; Generated autoloads from " file "\n") | |
210 | (goto-char output-end) | |
211 | (insert generate-autoload-section-trailer))) | |
212 | (message "Generating autoloads for %s...done" file))) | |
e2b6138f | 213 | \f |
0231f2dc JB |
214 | (defconst generated-autoload-file "loaddefs.el" |
215 | "*File \\[update-file-autoloads] puts autoloads into. | |
216 | A .el file can set this in its local variables section to make its | |
217 | autoloads go somewhere else.") | |
218 | ||
219 | ;;;###autoload | |
220 | (defun update-file-autoloads (file) | |
221 | "Update the autoloads for FILE in `generated-autoload-file' | |
222 | \(which FILE might bind in its local variables)." | |
223 | (interactive "fUpdate autoloads for file: ") | |
224 | (let ((load-name (let ((name (file-name-nondirectory file))) | |
225 | (if (string-match "\\.elc?$" name) | |
226 | (substring name 0 (match-beginning 0)) | |
227 | name))) | |
228 | (done nil) | |
229 | (existing-buffer (get-file-buffer file))) | |
230 | (save-excursion | |
231 | ;; We want to get a value for generated-autoload-file from | |
232 | ;; the local variables section if it's there. | |
233 | (set-buffer (find-file-noselect file)) | |
234 | (set-buffer (find-file-noselect generated-autoload-file)) | |
235 | (save-excursion | |
236 | (save-restriction | |
237 | (widen) | |
238 | (goto-char (point-min)) | |
239 | (while (search-forward generate-autoload-section-header nil t) | |
240 | (let ((form (condition-case () | |
241 | (read (current-buffer)) | |
242 | (end-of-file nil)))) | |
243 | (if (string= (nth 2 form) load-name) | |
244 | (let ((begin (match-beginning 0)) | |
245 | (last-time (nth 4 form)) | |
246 | (file-time (nth 5 (file-attributes file)))) | |
247 | (if (and (or (null existing-buffer) | |
248 | (not (buffer-modified-p existing-buffer))) | |
249 | (listp last-time) (= (length last-time) 2) | |
250 | (or (> (car last-time) (car file-time)) | |
251 | (and (= (car last-time) (car file-time)) | |
252 | (>= (nth 1 last-time) | |
253 | (nth 1 file-time))))) | |
254 | (message "Autoload section for %s is up to date." | |
255 | file) | |
256 | (search-forward generate-autoload-section-trailer) | |
257 | (delete-region begin (point)) | |
258 | (generate-file-autoloads file)) | |
259 | (setq done t)))))) | |
260 | (if done | |
88e37e7b | 261 | ;; There was an existing section and we have updated it. |
0231f2dc | 262 | () |
88e37e7b RM |
263 | (if (save-excursion |
264 | (set-buffer (find-file-noselect file)) | |
265 | (save-excursion | |
266 | (search-forward generate-autoload-cookie nil t))) | |
267 | ;; There are autoload cookies in FILE. | |
268 | ;; Have the user tell us where to put the new section. | |
269 | (progn | |
270 | (save-window-excursion | |
271 | (switch-to-buffer (current-buffer)) | |
272 | (with-output-to-temp-buffer "*Help*" | |
273 | (princ (substitute-command-keys | |
274 | (format "\ | |
0231f2dc JB |
275 | Move point to where the autoload section |
276 | for %s should be inserted. | |
277 | Then do \\[exit-recursive-edit]." | |
88e37e7b RM |
278 | file)))) |
279 | (recursive-edit) | |
280 | (beginning-of-line)) | |
281 | (generate-file-autoloads file))))) | |
e2b6138f | 282 | (if (interactive-p) (save-buffer)) |
0231f2dc JB |
283 | (if (and (null existing-buffer) |
284 | (setq existing-buffer (get-file-buffer file))) | |
285 | (kill-buffer existing-buffer))))) | |
286 | ||
287 | ;;;###autoload | |
288 | (defun update-autoloads-here () | |
d08589bf RM |
289 | "\ |
290 | Update sections of the current buffer generated by \\[update-file-autoloads]." | |
0231f2dc JB |
291 | (interactive) |
292 | (let ((generated-autoload-file (buffer-file-name))) | |
293 | (save-excursion | |
294 | (goto-char (point-min)) | |
295 | (while (search-forward generate-autoload-section-header nil t) | |
296 | (let* ((form (condition-case () | |
297 | (read (current-buffer)) | |
298 | (end-of-file nil))) | |
299 | (file (nth 3 form))) | |
300 | (if (and (stringp file) | |
301 | (or (get-file-buffer file) | |
302 | (file-exists-p file))) | |
303 | () | |
304 | (setq file (if (y-or-n-p (format "Library \"%s\" (load \ | |
305 | file \"%s\") doesn't exist. Remove its autoload section? " | |
306 | (nth 2 form) file)) | |
307 | t | |
308 | (condition-case () | |
309 | (read-file-name (format "Find \"%s\" load file: " | |
310 | (nth 2 form)) | |
311 | nil nil t) | |
312 | (quit nil))))) | |
313 | (if file | |
314 | (let ((begin (match-beginning 0))) | |
315 | (search-forward generate-autoload-section-trailer) | |
316 | (delete-region begin (point)))) | |
317 | (if (stringp file) | |
318 | (generate-file-autoloads file))))))) | |
319 | ||
320 | ;;;###autoload | |
321 | (defun update-directory-autoloads (dir) | |
322 | "Run \\[update-file-autoloads] on each .el file in DIR." | |
323 | (interactive "DUpdate autoloads for directory: ") | |
324 | (mapcar 'update-file-autoloads | |
e2b6138f RM |
325 | (directory-files dir nil "\\.el$")) |
326 | (if (interactive-p) | |
327 | (save-excursion | |
328 | (set-buffer (find-file-noselect generated-autoload-file)) | |
329 | (save-buffer)))) | |
0231f2dc JB |
330 | |
331 | ;;;###autoload | |
332 | (defun batch-update-autoloads () | |
333 | "Update the autoloads for the files or directories on the command line. | |
334 | Runs \\[update-file-autoloads] on files and \\[update-directory-autoloads] | |
335 | on directories. Must be used only with -batch, and kills Emacs on completion. | |
336 | Each file will be processed even if an error occurred previously. | |
ffd56f97 | 337 | For example, invoke \"emacs -batch -f batch-update-autoloads *.el\"" |
0231f2dc | 338 | (if (not noninteractive) |
23de5766 | 339 | (error "batch-update-autoloads is to be used only with -batch")) |
0231f2dc JB |
340 | (let ((lost nil) |
341 | (args command-line-args-left)) | |
342 | (while args | |
343 | (catch 'file | |
344 | (condition-case lossage | |
345 | (if (file-directory-p (expand-file-name (car args))) | |
346 | (update-directory-autoloads (car args)) | |
347 | (update-file-autoloads (car args))) | |
348 | (error (progn (message ">>Error processing %s: %s" | |
349 | (car args) lossage) | |
350 | (setq lost t) | |
351 | (throw 'file nil))))) | |
352 | (setq args (cdr args))) | |
353 | (save-some-buffers t) | |
354 | (message "Done") | |
355 | (kill-emacs (if lost 1 0)))) | |
356 | ||
357 | (provide 'autoload) | |
ffd56f97 | 358 | |
c0274f38 | 359 | ;;; autoload.el ends here |