autoload.el 23 KB
Newer Older
1
;; autoload.el --- maintain autoloads in loaddefs.el
Eric S. Raymond's avatar
Eric S. Raymond committed
2

3
;; Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 2001, 2002, 2003,
Glenn Morris's avatar
Glenn Morris committed
4
;;   2004, 2005, 2006, 2007 Free Software Foundation, Inc.
Erik Naggum's avatar
Erik Naggum committed
5

Karl Heuer's avatar
Karl Heuer committed
6
;; Author: Roland McGrath <roland@gnu.org>
Eric S. Raymond's avatar
Eric S. Raymond committed
7
;; Keywords: maint
Eric S. Raymond's avatar
Eric S. Raymond committed
8

Erik Naggum's avatar
Erik Naggum committed
9 10 11 12 13 14 15 16 17 18 19 20 21 22
;; This file is part of GNU Emacs.

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING.  If not, write to the
Lute Kamstra's avatar
Lute Kamstra committed
23 24
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
Jim Blandy's avatar
Jim Blandy committed
25

Richard M. Stallman's avatar
Richard M. Stallman committed
26
;;; Commentary:
27

Richard M. Stallman's avatar
Richard M. Stallman committed
28
;; This code helps GNU Emacs maintainers keep the loaddefs.el file up to
29 30 31 32
;; date.  It interprets magic cookies of the form ";;;###autoload" in
;; lisp source files in various useful ways.  To learn more, read the
;; source; if you're going to use this, you'd better be able to.

Eric S. Raymond's avatar
Eric S. Raymond committed
33 34
;;; Code:

Stefan Monnier's avatar
Stefan Monnier committed
35
(require 'lisp-mode)			;for `doc-string-elt' properties.
36
(require 'help-fns)			;for help-add-fundoc-usage.
37
(eval-when-compile (require 'cl))
Stefan Monnier's avatar
Stefan Monnier committed
38

39 40 41
(defvar generated-autoload-file "loaddefs.el"
   "*File \\[update-file-autoloads] puts autoloads into.
A `.el' file can set this in its local variables section to make its
42 43
autoloads go somewhere else.  The autoload file is assumed to contain a
trailer starting with a FormFeed character.")
44
(put 'generated-autoload-file 'safe-local-variable 'stringp)
45

46 47 48
;; This feels like it should be a defconst, but MH-E sets it to
;; ";;;###mh-autoload" for the autoloads that are to go into mh-loaddefs.el.
(defvar generate-autoload-cookie ";;;###autoload"
49 50 51 52 53 54
  "Magic comment indicating the following form should be autoloaded.
Used by \\[update-file-autoloads].  This string should be
meaningless to Lisp (e.g., a comment).

This string is used:

55
\;;;###autoload
56 57 58 59 60 61 62
\(defun function-to-be-autoloaded () ...)

If this string appears alone on a line, the following form will be
read and an autoload made for it.  If there is further text on the line,
that text will be copied verbatim to `generated-autoload-file'.")

(defconst generate-autoload-section-header "\f\n;;;### "
63
  "String that marks the form at the start of a new file's autoload section.")
64 65 66 67

(defconst generate-autoload-section-trailer "\n;;;***\n"
  "String which indicates the end of the section of autoloads for a file.")

68 69 70
(defconst generate-autoload-section-continuation ";;;;;; "
  "String to add on each continuation of the section header form.")

Jim Blandy's avatar
Jim Blandy committed
71
(defun make-autoload (form file)
72
  "Turn FORM into an autoload or defvar for source file FILE.
Stefan Monnier's avatar
Stefan Monnier committed
73 74 75 76 77
Returns nil if FORM is not a special autoload form (i.e. a function definition
or macro definition or a defcustom)."
  (let ((car (car-safe form)) expand)
    (cond
     ;; For complex cases, try again on the macro-expansion.
78
     ((and (memq car '(easy-mmode-define-global-mode define-global-minor-mode
79
		       define-globalized-minor-mode
Stefan Monnier's avatar
Stefan Monnier committed
80 81 82 83 84 85 86 87 88 89 90 91 92
		       easy-mmode-define-minor-mode define-minor-mode))
	   (setq expand (let ((load-file-name file)) (macroexpand form)))
	   (eq (car expand) 'progn)
	   (memq :autoload-end expand))
      (let ((end (memq :autoload-end expand)))
	;; Cut-off anything after the :autoload-end marker.
	(setcdr end nil)
	(cons 'progn
	      (mapcar (lambda (form) (make-autoload form file))
		      (cdr expand)))))

     ;; For special function-like operators, use the `autoload' function.
     ((memq car '(defun define-skeleton defmacro define-derived-mode
93 94
                   define-compilation-mode define-generic-mode
		   easy-mmode-define-global-mode define-global-minor-mode
95
		   define-globalized-minor-mode
96 97
		   easy-mmode-define-minor-mode define-minor-mode
		   defun* defmacro*))
98
      (let* ((macrop (memq car '(defmacro defmacro*)))
Stefan Monnier's avatar
Stefan Monnier committed
99
	     (name (nth 1 form))
100 101 102
	     (args (case car
		    ((defun defmacro defun* defmacro*) (nth 2 form))
		    ((define-skeleton) '(&optional str arg))
103 104
		    ((define-generic-mode define-derived-mode
                       define-compilation-mode) nil)
105
		    (t)))
Stefan Monnier's avatar
Stefan Monnier committed
106 107
	     (body (nthcdr (get car 'doc-string-elt) form))
	     (doc (if (stringp (car body)) (pop body))))
108 109 110 111
	(when (listp args)
	  ;; Add the usage form at the end where describe-function-1
	  ;; can recover it.
	  (setq doc (help-add-fundoc-usage doc args)))
Stefan Monnier's avatar
Stefan Monnier committed
112 113 114 115 116
	;; `define-generic-mode' quotes the name, so take care of that
	(list 'autoload (if (listp name) name (list 'quote name)) file doc
	      (or (and (memq car '(define-skeleton define-derived-mode
				    define-generic-mode
				    easy-mmode-define-global-mode
117
				    define-global-minor-mode
118
				    define-globalized-minor-mode
Stefan Monnier's avatar
Stefan Monnier committed
119 120 121 122 123
				    easy-mmode-define-minor-mode
				    define-minor-mode)) t)
		  (eq (car-safe (car body)) 'interactive))
	      (if macrop (list 'quote 'macro) nil))))

124
     ;; Convert defcustom to less space-consuming data.
Stefan Monnier's avatar
Stefan Monnier committed
125 126 127 128
     ((eq car 'defcustom)
      (let ((varname (car-safe (cdr-safe form)))
	    (init (car-safe (cdr-safe (cdr-safe form))))
	    (doc (car-safe (cdr-safe (cdr-safe (cdr-safe form)))))
129 130 131 132
	    ;; (rest (cdr-safe (cdr-safe (cdr-safe (cdr-safe form)))))
	    )
	`(progn
	   (defvar ,varname ,init ,doc)
Stefan Monnier's avatar
Stefan Monnier committed
133 134 135 136
	   (custom-autoload ',varname ,file
                            ,(condition-case nil
                                 (null (cadr (memq :set form)))
                               (error nil))))))
Stefan Monnier's avatar
Stefan Monnier committed
137

138 139 140 141 142 143 144 145 146
     ((eq car 'defgroup)
      ;; In Emacs this is normally handled separately by cus-dep.el, but for
      ;; third party packages, it can be convenient to explicitly autoload
      ;; a group.
      (let ((groupname (nth 1 form)))
        `(let ((loads (get ',groupname 'custom-loads)))
           (if (member ',file loads) nil
             (put ',groupname 'custom-loads (cons ',file loads))))))

Stefan Monnier's avatar
Stefan Monnier committed
147 148
     ;; nil here indicates that this is not a special autoload form.
     (t nil))))
Jim Blandy's avatar
Jim Blandy committed
149

150 151 152 153
;; Forms which have doc-strings which should be printed specially.
;; A doc-string-elt property of ELT says that (nth ELT FORM) is
;; the doc-string in FORM.
;; Those properties are now set in lisp-mode.el.
154

155 156 157 158
(defun autoload-generated-file ()
  (expand-file-name generated-autoload-file
                    (expand-file-name "lisp"
                                      source-directory)))
Jim Blandy's avatar
Jim Blandy committed
159

160
(defun autoload-trim-file-name (file)
Richard M. Stallman's avatar
Richard M. Stallman committed
161
  ;; Returns a relative file path for FILE
162 163 164 165
  ;; starting from the directory that loaddefs.el is in.
  ;; That is normally a directory in load-path,
  ;; which means Emacs will be able to find FILE when it looks.
  ;; Any extra directory names here would prevent finding the file.
166 167
  (setq file (expand-file-name file))
  (file-relative-name file
168
		      (file-name-directory generated-autoload-file)))
169

170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
(defun autoload-read-section-header ()
  "Read a section header form.
Since continuation lines have been marked as comments,
we must copy the text of the form and remove those comment
markers before we call `read'."
  (save-match-data
    (let ((beginning (point))
	  string)
      (forward-line 1)
      (while (looking-at generate-autoload-section-continuation)
	(forward-line 1))
      (setq string (buffer-substring beginning (point)))
      (with-current-buffer (get-buffer-create " *autoload*")
	(erase-buffer)
	(insert string)
	(goto-char (point-min))
	(while (search-forward generate-autoload-section-continuation nil t)
	  (replace-match " "))
	(goto-char (point-min))
	(read (current-buffer))))))

191 192
(defvar autoload-print-form-outbuf nil
  "Buffer which gets the output of `autoload-print-form'.")
193

194
(defun autoload-print-form (form)
195 196 197
  "Print FORM such that `make-docfile' will find the docstrings.
The variable `autoload-print-form-outbuf' specifies the buffer to
put the output in."
198 199 200 201 202 203
  (cond
   ;; If the form is a sequence, recurse.
   ((eq (car form) 'progn) (mapcar 'autoload-print-form (cdr form)))
   ;; Symbols at the toplevel are meaningless.
   ((symbolp form) nil)
   (t
204 205
    (let ((doc-string-elt (get (car-safe form) 'doc-string-elt))
	  (outbuf autoload-print-form-outbuf))
206 207 208 209 210 211 212 213 214 215
      (if (and doc-string-elt (stringp (nth doc-string-elt form)))
	  ;; We need to hack the printing because the
	  ;; doc-string must be printed specially for
	  ;; make-docfile (sigh).
	  (let* ((p (nthcdr (1- doc-string-elt) form))
		 (elt (cdr p)))
	    (setcdr p nil)
	    (princ "\n(" outbuf)
	    (let ((print-escape-newlines t)
		  (print-escape-nonascii t))
216 217 218
	      (dolist (elt form)
		(prin1 elt outbuf)
		(princ " " outbuf)))
219 220 221 222 223 224 225 226 227
	    (princ "\"\\\n" outbuf)
	    (let ((begin (with-current-buffer outbuf (point))))
	      (princ (substring (prin1-to-string (car elt)) 1)
		     outbuf)
	      ;; Insert a backslash before each ( that
	      ;; appears at the beginning of a line in
	      ;; the doc string.
	      (with-current-buffer outbuf
		(save-excursion
228
		  (while (re-search-backward "\n[[(]" begin t)
229 230 231 232 233 234 235 236 237 238 239 240
		    (forward-char 1)
		    (insert "\\"))))
	      (if (null (cdr elt))
		  (princ ")" outbuf)
		(princ " " outbuf)
		(princ (substring (prin1-to-string (cdr elt)) 1)
		       outbuf))
	      (terpri outbuf)))
	(let ((print-escape-newlines t)
	      (print-escape-nonascii t))
	  (print form outbuf)))))))

241 242 243 244 245 246 247 248 249 250 251 252 253 254
(defun autoload-ensure-default-file (file)
  "Make sure that the autoload file FILE exists and if not create it."
  (unless (file-exists-p file)
    (write-region
     (concat ";;; " (file-name-nondirectory file)
	     " --- automatically extracted autoloads\n"
	     ";;\n"
	     ";;; Code:\n\n"
	     "\n;; Local Variables:\n"
	     ";; version-control: never\n"
	     ";; no-byte-compile: t\n"
	     ";; no-update-autoloads: t\n"
	     ";; End:\n"
	     ";;; " (file-name-nondirectory file)
255
	     " ends here\n")
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
     nil file))
  file)

(defun autoload-insert-section-header (outbuf autoloads load-name file time)
  "Insert the section-header line,
which lists the file name and which functions are in it, etc."
  (insert generate-autoload-section-header)
  (prin1 (list 'autoloads autoloads load-name
	       (if (stringp file) (autoload-trim-file-name file) file)
	       time)
	 outbuf)
  (terpri outbuf)
  ;; Break that line at spaces, to avoid very long lines.
  ;; Make each sub-line into a comment.
  (with-current-buffer outbuf
    (save-excursion
      (forward-line -1)
      (while (not (eolp))
	(move-to-column 64)
	(skip-chars-forward "^ \n")
	(or (eolp)
	    (insert "\n" generate-autoload-section-continuation))))))

279 280 281
(defun autoload-find-file (file)
  "Fetch file and put it in a temp buffer.  Return the buffer."
  ;; It is faster to avoid visiting the file.
282
  (setq file (expand-file-name file))
283 284 285 286 287 288
  (with-current-buffer (get-buffer-create " *autoload-file*")
    (kill-all-local-variables)
    (erase-buffer)
    (setq buffer-undo-list t
          buffer-read-only nil)
    (emacs-lisp-mode)
289
    (setq default-directory (file-name-directory file))
290 291 292 293 294
    (insert-file-contents file nil)
    (let ((enable-local-variables :safe))
      (hack-local-variables))
    (current-buffer)))

295 296 297
(defvar no-update-autoloads nil
  "File local variable to prevent scanning this file for autoload cookies.")

298 299 300 301 302 303
(defun autoload-file-load-name (file)
  (let ((name (file-name-nondirectory file)))
    (if (string-match "\\.elc?\\(\\.\\|\\'\\)" name)
        (substring name 0 (match-beginning 0))
      name)))

Jim Blandy's avatar
Jim Blandy committed
304 305
(defun generate-file-autoloads (file)
  "Insert at point a loaddefs autoload section for FILE.
306
Autoloads are generated for defuns and defmacros in FILE
307
marked by `generate-autoload-cookie' (which see).
Jim Blandy's avatar
Jim Blandy committed
308
If FILE is being visited in a buffer, the contents of the buffer
309 310
are used.
Return non-nil in the case where no autoloads were added at point."
Jim Blandy's avatar
Jim Blandy committed
311 312 313
  (interactive "fGenerate autoloads for file: ")
  (let ((outbuf (current-buffer))
	(autoloads-done '())
314
	(load-name (autoload-file-load-name file))
Jim Blandy's avatar
Jim Blandy committed
315
	(print-length nil)
316
	(print-readably t)		; This does something in Lucid Emacs.
317
	(float-output-format nil)
Jim Blandy's avatar
Jim Blandy committed
318
	(done-any nil)
319
	(visited (get-file-buffer file))
320
        output-start)
Jim Blandy's avatar
Jim Blandy committed
321 322

    ;; If the autoload section we create here uses an absolute
Richard M. Stallman's avatar
Richard M. Stallman committed
323
    ;; file name for FILE in its header, and then Emacs is installed
Jim Blandy's avatar
Jim Blandy committed
324 325 326
    ;; under a different path on another system,
    ;; `update-autoloads-here' won't be able to find the files to be
    ;; autoloaded.  So, if FILE is in the same directory or a
Jim Blandy's avatar
Jim Blandy committed
327
    ;; subdirectory of the current buffer's directory, we'll make it
Jim Blandy's avatar
Jim Blandy committed
328 329
    ;; relative to the current buffer's directory.
    (setq file (expand-file-name file))
330 331 332 333 334 335 336
    (let* ((source-truename (file-truename file))
	   (dir-truename (file-name-as-directory
			  (file-truename default-directory)))
	   (len (length dir-truename)))
      (if (and (< len (length source-truename))
	       (string= dir-truename (substring source-truename 0 len)))
	  (setq file (substring source-truename len))))
Jim Blandy's avatar
Jim Blandy committed
337

338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
    (with-current-buffer (or visited
                             ;; It is faster to avoid visiting the file.
                             (autoload-find-file file))
      ;; Obey the no-update-autoloads file local variable.
      (unless no-update-autoloads
        (message "Generating autoloads for %s..." file)
        (setq output-start (with-current-buffer outbuf (point)))
        (save-excursion
          (save-restriction
            (widen)
            (goto-char (point-min))
            (while (not (eobp))
              (skip-chars-forward " \t\n\f")
              (cond
               ((looking-at (regexp-quote generate-autoload-cookie))
                (search-forward generate-autoload-cookie)
                (skip-chars-forward " \t")
                (setq done-any t)
                (if (eolp)
357 358 359 360 361 362 363 364 365 366 367 368
                    (condition-case err
                        ;; Read the next form and make an autoload.
                        (let* ((form (prog1 (read (current-buffer))
                                       (or (bolp) (forward-line 1))))
                               (autoload (make-autoload form load-name)))
                          (if autoload
                              (push (nth 1 form) autoloads-done)
                            (setq autoload form))
                          (let ((autoload-print-form-outbuf outbuf))
                            (autoload-print-form autoload)))
                      (error
                       (message "Error in %s: %S" file err)))
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404

                  ;; Copy the rest of the line to the output.
                  (princ (buffer-substring
                          (progn
                            ;; Back up over whitespace, to preserve it.
                            (skip-chars-backward " \f\t")
                            (if (= (char-after (1+ (point))) ? )
                                ;; Eat one space.
                                (forward-char 1))
                            (point))
                          (progn (forward-line 1) (point)))
                         outbuf)))
               ((looking-at ";")
                ;; Don't read the comment.
                (forward-line 1))
               (t
                (forward-sexp 1)
                (forward-line 1))))))

        (when done-any
          (with-current-buffer outbuf
            (save-excursion
              ;; Insert the section-header line which lists the file name
              ;; and which functions are in it, etc.
              (goto-char output-start)
              (autoload-insert-section-header
               outbuf autoloads-done load-name file
               (nth 5 (file-attributes file)))
              (insert ";;; Generated autoloads from "
                      (autoload-trim-file-name file) "\n"))
            (insert generate-autoload-section-trailer)))
        (message "Generating autoloads for %s...done" file))
      (or visited
          ;; We created this buffer, so we should kill it.
          (kill-buffer (current-buffer))))
    (not done-any)))
405

Jim Blandy's avatar
Jim Blandy committed
406
;;;###autoload
407
(defun update-file-autoloads (file &optional save-after)
Jim Blandy's avatar
Jim Blandy committed
408
  "Update the autoloads for FILE in `generated-autoload-file'
409
\(which FILE might bind in its local variables).
410 411 412 413 414
If SAVE-AFTER is non-nil (which is always, when called interactively),
save the buffer too.

Return FILE if there was no autoload cookie in it, else nil."
  (interactive "fUpdate autoloads for file: \np")
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
  (let ((existing-buffer (get-file-buffer file)))
    (with-temp-buffer
      ;; Let's presume the file is not visited, so we call
      ;; autoload-find-destination from a dummy buffer, except if the file
      ;; is visited, in which case we use that buffer instead.
      (if existing-buffer (set-buffer existing-buffer))

      (catch 'up-to-date
        (let ((buf (autoload-find-destination file)))
          (with-current-buffer buf
            (let ((no-autoloads (generate-file-autoloads file)))
              
              (and save-after
                   (buffer-modified-p)
                   (save-buffer))

              (if no-autoloads file))))))))

(defun autoload-find-destination (file)
  "Find the destination point of the current buffer's autoloads.
FILE is the file name of the current buffer.
Returns a buffer whose point is placed at the requested location.
Throws `up-to-date' if the file's autoloads are uptodate, otherwise
removes any prior now out-of-date autoload entries.
The current buffer only matters if it is visiting a file or if it has a buffer-local
value for some variables such as `generated-autoload-file', so it's OK
to call it from a dummy buffer if FILE is not currently visited."
  ;; (message "autoload-find-destination %S" file)
443
  (let ((load-name (autoload-file-load-name file))
444 445 446 447 448 449 450 451 452 453 454
        (existing-buffer (if buffer-file-name (current-buffer)))
	(found nil))
    (with-current-buffer
        ;; We must read/write the file without any code conversion,
        ;; but still decode EOLs.
        (let ((coding-system-for-read 'raw-text))
          (find-file-noselect
           (autoload-ensure-default-file (autoload-generated-file))))
      ;; This is to make generated-autoload-file have Unix EOLs, so
      ;; that it is portable to all platforms.
      (setq buffer-file-coding-system 'raw-text-unix)
455 456 457 458
      (or (> (buffer-size) 0)
	  (error "Autoloads file %s does not exist" buffer-file-name))
      (or (file-writable-p buffer-file-name)
	  (error "Autoloads file %s is not writable" buffer-file-name))
459 460 461 462 463 464 465 466 467 468 469
      (widen)
      (goto-char (point-min))
      ;; Look for the section for LOAD-NAME.
      (while (and (not found)
                  (search-forward generate-autoload-section-header nil t))
        (let ((form (autoload-read-section-header)))
          (cond ((string= (nth 2 form) load-name)
                 ;; We found the section for this file.
                 ;; Check if it is up to date.
                 (let ((begin (match-beginning 0))
                       (last-time (nth 4 form))
470 471 472 473
			   (file-time (nth 5 (file-attributes file))))
		       (if (and (or (null existing-buffer)
				    (not (buffer-modified-p existing-buffer)))
				(listp last-time) (= (length last-time) 2)
474
				(not (time-less-p last-time file-time)))
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490
                       (throw 'up-to-date nil)
                     (autoload-remove-section (match-beginning 0))
                     (setq found t))))
                ((string< load-name (nth 2 form))
                 ;; We've come to a section alphabetically later than
                 ;; LOAD-NAME.  We assume the file is in order and so
                 ;; there must be no section for LOAD-NAME.  We will
                 ;; insert one before the section here.
                 (goto-char (match-beginning 0))
                 (setq found t)))))
      (or found
          (progn
            ;; No later sections in the file.  Put before the last page.
            (goto-char (point-max))
            (search-backward "\f" nil t)))
      (current-buffer))))
491 492 493 494 495

(defun autoload-remove-section (begin)
  (goto-char begin)
  (search-forward generate-autoload-section-trailer)
  (delete-region begin (point)))
Jim Blandy's avatar
Jim Blandy committed
496 497

;;;###autoload
498
(defun update-directory-autoloads (&rest dirs)
499
  "\
500
Update loaddefs.el with all the current autoloads from DIRS, and no old ones.
501
This uses `update-file-autoloads' (which see) to do its work.
502 503 504 505 506 507
In an interactive call, you must give one argument, the name
of a single directory.  In a call from Lisp, you can supply multiple
directories as separate arguments, but this usage is discouraged.

The function does NOT recursively descend into subdirectories of the
directory or directories specified."
508
  (interactive "DUpdate autoloads from directory: ")
509
  (let* ((files-re (let ((tmp nil))
510
		     (dolist (suf (get-load-suffixes)
511 512 513
				  (concat "^[^=.].*" (regexp-opt tmp t) "\\'"))
		       (unless (string-match "\\.elc" suf) (push suf tmp)))))
	 (files (apply 'nconc
514 515
		       (mapcar (lambda (dir)
				 (directory-files (expand-file-name dir)
516
						  t files-re))
517 518 519
			       dirs)))
	 (this-time (current-time))
	 (no-autoloads nil)		;files with no autoload cookies.
520
	 (autoloads-file (autoload-generated-file))
521 522 523 524
	 (top-dir (file-name-directory autoloads-file)))

    (with-current-buffer
	(find-file-noselect (autoload-ensure-default-file autoloads-file))
525
      (save-excursion
526 527 528 529 530

	;; Canonicalize file names and remove the autoload file itself.
	(setq files (delete (autoload-trim-file-name buffer-file-name)
			    (mapcar 'autoload-trim-file-name files)))

531 532
	(goto-char (point-min))
	(while (search-forward generate-autoload-section-header nil t)
533
	  (let* ((form (autoload-read-section-header))
534
		 (file (nth 3 form)))
535 536 537 538 539 540 541 542 543
	    (cond ((and (consp file) (stringp (car file)))
		   ;; This is a list of files that have no autoload cookies.
		   ;; There shouldn't be more than one such entry.
		   ;; Remove the obsolete section.
		   (autoload-remove-section (match-beginning 0))
		   (let ((last-time (nth 4 form)))
		     (dolist (file file)
		       (let ((file-time (nth 5 (file-attributes file))))
			 (when (and file-time
544
				    (not (time-less-p last-time file-time)))
545 546 547 548
			   ;; file unchanged
			   (push file no-autoloads)
			   (setq files (delete file files)))))))
		  ((not (stringp file)))
549
		  ((not (file-exists-p (expand-file-name file top-dir)))
550
		   ;; Remove the obsolete section.
551 552 553 554
		   (autoload-remove-section (match-beginning 0)))
		  ((equal (nth 4 form) (nth 5 (file-attributes file)))
		   ;; File hasn't changed.
		   nil)
555 556 557
		  (t
		   (update-file-autoloads file)))
	    (setq files (delete file files)))))
558 559 560 561 562
      ;; Elements remaining in FILES have no existing autoload sections yet.
      (setq no-autoloads
	    (append no-autoloads
		    (delq nil (mapcar 'update-file-autoloads files))))
      (when no-autoloads
563 564
	;; Sort them for better readability.
	(setq no-autoloads (sort no-autoloads 'string<))
565 566 567 568 569 570 571
	;; Add the `no-autoloads' section.
	(goto-char (point-max))
	(search-backward "\f" nil t)
	(autoload-insert-section-header
	 (current-buffer) nil nil no-autoloads this-time)
	(insert generate-autoload-section-trailer))

572
      (save-buffer))))
Jim Blandy's avatar
Jim Blandy committed
573

574 575 576
(define-obsolete-function-alias 'update-autoloads-from-directories
    'update-directory-autoloads "22.1")

Jim Blandy's avatar
Jim Blandy committed
577 578
;;;###autoload
(defun batch-update-autoloads ()
579
  "Update loaddefs.el autoloads in batch mode.
580 581
Calls `update-directory-autoloads' on the command line arguments."
  (apply 'update-directory-autoloads command-line-args-left)
582
  (setq command-line-args-left nil))
Jim Blandy's avatar
Jim Blandy committed
583 584

(provide 'autoload)
Jim Blandy's avatar
Jim Blandy committed
585

586
;; arch-tag: 00244766-98f4-4767-bf42-8a22103441c6
Eric S. Raymond's avatar
Eric S. Raymond committed
587
;;; autoload.el ends here