merge trun
[bpt/emacs.git] / lisp / emacs-lisp / autoload.el
index 5a5d6b8..382e25f 100644 (file)
@@ -1,6 +1,6 @@
-;; autoload.el --- maintain autoloads in loaddefs.el
+;; autoload.el --- maintain autoloads in loaddefs.el  -*- lexical-binding: t -*-
 
-;; Copyright (C) 1991-1997, 2001-2011  Free Software Foundation, Inc.
+;; Copyright (C) 1991-1997, 2001-2012  Free Software Foundation, Inc.
 
 ;; Author: Roland McGrath <roland@gnu.org>
 ;; Keywords: maint
 
 (require 'lisp-mode)                   ;for `doc-string-elt' properties.
 (require 'help-fns)                    ;for help-add-fundoc-usage.
-(eval-when-compile (require 'cl))
+(eval-when-compile (require 'cl-lib))
 
-(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
-autoloads go somewhere else.  The autoload file is assumed to contain a
-trailer starting with a FormFeed character.")
+(defvar generated-autoload-file nil
+  "File into which to write autoload definitions.
+A Lisp file can set this in its local variables section to make
+its autoloads go somewhere else.
+
+If this is a relative file name, the directory is determined as
+follows:
+ - If a Lisp file defined `generated-autoload-file' as a
+   file-local variable, use its containing directory.
+ - Otherwise use the \"lisp\" subdirectory of `source-directory'.
+
+The autoload file is assumed to contain a trailer starting with a
+FormFeed character.")
 ;;;###autoload
 (put 'generated-autoload-file 'safe-local-variable 'stringp)
 
@@ -78,78 +86,100 @@ that text will be copied verbatim to `generated-autoload-file'.")
 
 (defvar autoload-modified-buffers)      ;Dynamically scoped var.
 
-(defun make-autoload (form file)
+(defun make-autoload (form file &optional expansion)
   "Turn FORM into an autoload or defvar for source file FILE.
 Returns nil if FORM is not a special autoload form (i.e. a function definition
-or macro definition or a defcustom)."
+or macro definition or a defcustom).
+If EXPANSION is non-nil, we're processing the macro expansion of an
+expression, in which case we want to handle forms differently."
   (let ((car (car-safe form)) expand)
     (cond
+     ((and expansion (eq car 'defalias))
+      (pcase-let*
+          ((`(,_ ,_ ,arg . ,rest) form)
+           ;; `type' is non-nil if it defines a macro.
+           ;; `fun' is the function part of `arg' (defaults to `arg').
+           ((or (and (or `(cons 'macro ,fun) `'(macro . ,fun)) (let type t))
+                (and (let fun arg) (let type nil)))
+            arg)
+           ;; `lam' is the lambda expression in `fun' (or nil if not
+           ;; recognized).
+           (lam (if (memq (car-safe fun) '(quote function)) (cadr fun)))
+           ;; `args' is the list of arguments (or t if not recognized).
+           ;; `body' is the body of `lam' (or t if not recognized).
+           ((or `(lambda ,args . ,body)
+                (and (let args t) (let body t)))
+            lam)
+           ;; Get the `doc' from `body' or `rest'.
+           (doc (cond ((stringp (car-safe body)) (car body))
+                      ((stringp (car-safe rest)) (car rest))))
+           ;; Look for an interactive spec.
+           (interactive (pcase body
+                          ((or `((interactive . ,_) . ,_)
+                               `(,_ (interactive . ,_) . ,_)) t))))
+        ;; Add the usage form at the end where describe-function-1
+        ;; can recover it.
+        (when (listp args) (setq doc (help-add-fundoc-usage doc args)))
+        ;; (message "autoload of %S" (nth 1 form))
+        `(autoload ,(nth 1 form) ,file ,doc ,interactive ,type)))
+
+     ((and expansion (memq car '(progn prog1)))
+      (let ((end (memq :autoload-end form)))
+       (when end             ;Cut-off anything after the :autoload-end marker.
+          (setq form (copy-sequence form))
+          (setcdr (memq :autoload-end form) nil))
+        (let ((exps (delq nil (mapcar (lambda (form)
+                                        (make-autoload form file expansion))
+                                      (cdr form)))))
+          (when exps (cons 'progn exps)))))
+
      ;; For complex cases, try again on the macro-expansion.
      ((and (memq car '(easy-mmode-define-global-mode define-global-minor-mode
-                      define-globalized-minor-mode
+                       define-globalized-minor-mode defun defmacro
+                       ;; FIXME: we'd want `defmacro*' here as well, so as
+                       ;; to handle its `declare', but when autoload is run
+                       ;; CL is not loaded so macroexpand doesn't know how
+                       ;; to expand it!
                       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)))))
+          (memq (car expand) '(progn prog1 defalias)))
+      (make-autoload expand file 'expansion)) ;Recurse on the expansion.
 
      ;; For special function-like operators, use the `autoload' function.
-     ((memq car '(defun define-skeleton defmacro define-derived-mode
+     ((memq car '(define-skeleton define-derived-mode
                    define-compilation-mode define-generic-mode
                   easy-mmode-define-global-mode define-global-minor-mode
                   define-globalized-minor-mode
                   easy-mmode-define-minor-mode define-minor-mode
-                  defun* defmacro* define-overloadable-function))
-      (let* ((macrop (memq car '(defmacro defmacro*)))
+                  cl-defun defun* cl-defmacro defmacro*
+                   define-overloadable-function))
+      (let* ((macrop (memq car '(defmacro cl-defmacro defmacro*)))
             (name (nth 1 form))
-            (args (case car
-                     ((defun defmacro defun* defmacro*
-                        define-overloadable-function) (nth 2 form))
-                     ((define-skeleton) '(&optional str arg))
-                     ((define-generic-mode define-derived-mode
-                        define-compilation-mode) nil)
-                     (t)))
-            (body (nthcdr (get car 'doc-string-elt) form))
+            (args (pcase car
+                     ((or `defun `defmacro
+                          `defun* `defmacro* `cl-defun `cl-defmacro
+                          `define-overloadable-function) (nth 2 form))
+                     (`define-skeleton '(&optional str arg))
+                     ((or `define-generic-mode `define-derived-mode
+                          `define-compilation-mode) nil)
+                     (_ t)))
+            (body (nthcdr (or (function-get car 'doc-string-elt) 3) form))
             (doc (if (stringp (car body)) (pop body))))
-       (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)))
-        (let ((exp
-               ;; `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
-                                           define-global-minor-mode
-                                           define-globalized-minor-mode
-                                           easy-mmode-define-minor-mode
-                                           define-minor-mode)) t)
-                         (eq (car-safe (car body)) 'interactive))
-                     (if macrop (list 'quote 'macro) nil))))
-          (when macrop
-            ;; Special case to autoload some of the macro's declarations.
-            (let ((decls (nth (if (stringp (nth 3 form)) 4 3) form))
-                  (exps '()))
-              (when (eq (car-safe decls) 'declare)
-                ;; FIXME: We'd like to reuse macro-declaration-function,
-                ;; but we can't since it doesn't return anything.
-                (dolist (decl decls)
-                  (case (car-safe decl)
-                    (indent
-                     (push `(put ',name 'lisp-indent-function ',(cadr decl))
-                           exps))
-                    (doc-string
-                     (push `(put ',name 'doc-string-elt ',(cadr decl)) exps))))
-                (when exps
-                  (setq exp `(progn ,exp ,@exps))))))
-          exp)))
+        ;; Add the usage form at the end where describe-function-1
+        ;; can recover it.
+       (when (listp args) (setq doc (help-add-fundoc-usage doc args)))
+        ;; `define-generic-mode' quotes the name, so take care of that
+        `(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
+                                  define-global-minor-mode
+                                  define-globalized-minor-mode
+                                  easy-mmode-define-minor-mode
+                                  define-minor-mode)) t)
+                (eq (car-safe (car body)) 'interactive))
+           ,(if macrop ''macro nil))))
 
      ;; For defclass forms, use `eieio-defclass-autoload'.
      ((eq car 'defclass)
@@ -182,6 +212,11 @@ or macro definition or a defcustom)."
            (if (member ',file loads) nil
              (put ',groupname 'custom-loads (cons ',file loads))))))
 
+     ;; When processing a macro expansion, any expression
+     ;; before a :autoload-end should be included.  These are typically (put
+     ;; 'fun 'prop val) and things like that.
+     ((and expansion (consp form)) form)
+
      ;; nil here indicates that this is not a special autoload form.
      (t nil))))
 
@@ -190,6 +225,16 @@ or macro definition or a defcustom)."
 ;; the doc-string in FORM.
 ;; Those properties are now set in lisp-mode.el.
 
+(defun autoload-find-generated-file ()
+  "Visit the autoload file for the current buffer, and return its buffer.
+If a buffer is visiting the desired autoload file, return it."
+  (let ((enable-local-variables :safe)
+       (enable-local-eval nil))
+    ;; We used to use `raw-text' to read this file, but this causes
+    ;; problems when the file contains non-ASCII characters.
+    (find-file-noselect
+     (autoload-ensure-default-file (autoload-generated-file)))))
+
 (defun autoload-generated-file ()
   (expand-file-name generated-autoload-file
                     ;; File-local settings of generated-autoload-file should
@@ -233,7 +278,7 @@ put the output in."
    ;; Symbols at the toplevel are meaningless.
    ((symbolp form) nil)
    (t
-    (let ((doc-string-elt (get (car-safe form) 'doc-string-elt))
+    (let ((doc-string-elt (function-get (car-safe form) 'doc-string-elt))
          (outbuf autoload-print-form-outbuf))
       (if (and doc-string-elt (stringp (nth doc-string-elt form)))
          ;; We need to hack the printing because the
@@ -312,7 +357,7 @@ not be relied upon."
   "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 file time)
+  (prin1 `(autoloads ,autoloads ,load-name ,file ,time)
         outbuf)
   (terpri outbuf)
   ;; Break that line at spaces, to avoid very long lines.
@@ -338,7 +383,8 @@ which lists the file name and which functions are in it, etc."
     (emacs-lisp-mode)
     (setq default-directory (file-name-directory file))
     (insert-file-contents file nil)
-    (let ((enable-local-variables :safe))
+    (let ((enable-local-variables :safe)
+         (enable-local-eval nil))
       (hack-local-variables))
     (current-buffer)))
 
@@ -381,7 +427,8 @@ If FILE is being visited in a buffer, the contents of the buffer
 are used.
 Return non-nil in the case where no autoloads were added at point."
   (interactive "fGenerate autoloads for file: ")
-  (autoload-generate-file-autoloads file (current-buffer)))
+  (let ((generated-autoload-file buffer-file-name))
+    (autoload-generate-file-autoloads file (current-buffer))))
 
 (defvar print-readably)
 
@@ -428,7 +475,11 @@ Return non-nil if and only if FILE adds no autoloads to OUTFILE
                    generated-autoload-load-name
                  (autoload-file-load-name absfile)))
           (when (and outfile
-                     (not (equal outfile (autoload-generated-file))))
+                     (not
+                     (if (memq system-type '(ms-dos windows-nt))
+                         (equal (downcase outfile)
+                                (downcase (autoload-generated-file)))
+                       (equal outfile (autoload-generated-file)))))
             (setq otherbuf t))
           (save-excursion
             (save-restriction
@@ -459,7 +510,7 @@ Return non-nil if and only if FILE adds no autoloads to OUTFILE
                   (search-forward generate-autoload-cookie)
                   (skip-chars-forward " \t")
                   (if (eolp)
-                      (condition-case err
+                      (condition-case-unless-debug err
                           ;; Read the next form and make an autoload.
                           (let* ((form (prog1 (read (current-buffer))
                                          (or (bolp) (forward-line 1))))
@@ -494,13 +545,12 @@ Return non-nil if and only if FILE adds no autoloads to OUTFILE
 
           (when output-start
             (let ((secondary-autoloads-file-buf
-                   (if (local-variable-p 'generated-autoload-file)
-                       (current-buffer))))
+                   (if otherbuf (current-buffer))))
               (with-current-buffer (marker-buffer output-start)
                 (save-excursion
                   ;; Insert the section-header line which lists the file name
                   ;; and which functions are in it, etc.
-                  (assert (= ostart output-start))
+                  (cl-assert (= ostart output-start))
                   (goto-char output-start)
                   (let ((relfile (file-relative-name absfile)))
                     (autoload-insert-section-header
@@ -542,15 +592,22 @@ Return non-nil if and only if FILE adds no autoloads to OUTFILE
        (save-buffer)))))
 
 ;;;###autoload
-(defun update-file-autoloads (file &optional save-after)
-  "Update the autoloads for FILE in `generated-autoload-file'
-\(which FILE might bind in its local variables).
-If SAVE-AFTER is non-nil (which is always, when called interactively),
-save the buffer too.
+(defun update-file-autoloads (file &optional save-after outfile)
+  "Update the autoloads for FILE.
+If prefix arg SAVE-AFTER is non-nil, save the buffer too.
+
+If FILE binds `generated-autoload-file' as a file-local variable,
+autoloads are written into that file.  Otherwise, the autoloads
+file is determined by OUTFILE.  If called interactively, prompt
+for OUTFILE; if called from Lisp with OUTFILE nil, use the
+existing value of `generated-autoload-file'.
 
 Return FILE if there was no autoload cookie in it, else nil."
-  (interactive "fUpdate autoloads for file: \np")
-  (let* ((autoload-modified-buffers nil)
+  (interactive (list (read-file-name "Update autoloads for file: ")
+                    current-prefix-arg
+                    (read-file-name "Write autoload definitions to file: ")))
+  (let* ((generated-autoload-file (or outfile generated-autoload-file))
+        (autoload-modified-buffers nil)
          (no-autoloads (autoload-generate-file-autoloads file)))
     (if autoload-modified-buffers
         (if save-after (autoload-save-buffers))
@@ -568,12 +625,7 @@ removes any prior now out-of-date autoload entries."
     (let* ((buf (current-buffer))
            (existing-buffer (if buffer-file-name buf))
            (found nil))
-      (with-current-buffer
-          ;; We used to use `raw-text' to read this file, but this causes
-          ;; problems when the file contains non-ASCII characters.
-         (let ((enable-local-variables :safe))
-           (find-file-noselect
-            (autoload-ensure-default-file (autoload-generated-file))))
+      (with-current-buffer (autoload-find-generated-file)
         ;; This is to make generated-autoload-file have Unix EOLs, so
         ;; that it is portable to all platforms.
         (or (eq 0 (coding-system-eol-type buffer-file-coding-system))
@@ -632,20 +684,25 @@ removes any prior now out-of-date autoload entries."
 
 ;;;###autoload
 (defun update-directory-autoloads (&rest dirs)
-  "\
-Update loaddefs.el with all the current autoloads from DIRS, and no old ones.
-This uses `update-file-autoloads' (which see) to do its work.
-In an interactive call, you must give one argument, the name
-of a single directory.  In a call from Lisp, you can supply multiple
+  "Update autoload definitions for Lisp files in the directories DIRS.
+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."
+directory or directories specified.
+
+In an interactive call, prompt for a default output file for the
+autoload definitions, and temporarily bind the variable
+`generated-autoload-file' to this value.  When called from Lisp,
+use the existing value of `generated-autoload-file'.  If any Lisp
+file binds `generated-autoload-file' as a file-local variable,
+write its autoloads into the specified file instead."
   (interactive "DUpdate autoloads from directory: ")
   (let* ((files-re (let ((tmp nil))
-                    (dolist (suf (get-load-suffixes)
-                                 (concat "^[^=.].*" (regexp-opt tmp t) "\\'"))
-                      (unless (string-match "\\.elc" suf) (push suf tmp)))))
+                    (dolist (suf (get-load-suffixes))
+                      (unless (string-match "\\.elc" suf) (push suf tmp)))
+                     (concat "^[^=.].*" (regexp-opt tmp t) "\\'")))
         (files (apply 'nconc
                       (mapcar (lambda (dir)
                                 (directory-files (expand-file-name dir)
@@ -656,14 +713,14 @@ directory or directories specified."
          ;; Files with no autoload cookies or whose autoloads go to other
          ;; files because of file-local autoload-generated-file settings.
         (no-autoloads nil)
-         (autoload-modified-buffers nil))
+         (autoload-modified-buffers nil)
+        (generated-autoload-file
+         (if (called-interactively-p 'interactive)
+             (read-file-name "Write autoload definitions to file: ")
+           generated-autoload-file)))
 
-    (with-current-buffer
-       (let ((enable-local-variables :safe))
-         (find-file-noselect
-          (autoload-ensure-default-file (autoload-generated-file))))
+    (with-current-buffer (autoload-find-generated-file)
       (save-excursion
-
        ;; Canonicalize file names and remove the autoload file itself.
        (setq files (delete (file-relative-name buffer-file-name)
                            (mapcar 'file-relative-name files)))
@@ -734,60 +791,28 @@ directory or directories specified."
 (define-obsolete-function-alias 'update-autoloads-from-directories
     'update-directory-autoloads "22.1")
 
-(defvar autoload-make-program (or (getenv "MAKE") "make")
-  "Name of the make program in use during the Emacs build process.")
-
 ;;;###autoload
 (defun batch-update-autoloads ()
   "Update loaddefs.el autoloads in batch mode.
-Calls `update-directory-autoloads' on the command line arguments."
+Calls `update-directory-autoloads' on the command line arguments.
+Definitions are written to `generated-autoload-file' (which
+should be non-nil)."
   ;; For use during the Emacs build process only.
+  ;; Exclude those files that are preloaded on ALL platforms.
+  ;; These are the ones in loadup.el where "(load" is at the start
+  ;; of the line (crude, but it works).
   (unless autoload-excludes
-    (let* ((ldir (file-name-directory generated-autoload-file))
-          (default-directory
-            (file-name-as-directory
-             (expand-file-name (if (eq system-type 'windows-nt)
-                                   "../lib-src"
-                                 "../src") ldir)))
-          (mfile "Makefile")
-          (tmpfile "echolisp.tmp")
-          lim)
-      ;; Windows uses the 'echolisp' approach because:
-      ;; i) It does not have $lisp as a single simple definition, so
-      ;; it would be harder to parse the Makefile.
-      ;; ii) It can, since it already has $lisp broken up into pieces
-      ;; that the command-line can handle.
-      ;; Non-Windows builds do not use the 'echolisp' approach because
-      ;; no-one knows (?) the maximum safe command-line length on all
-      ;; supported systems.  $lisp is much longer there since it uses
-      ;; absolute paths, and it would seem a shame to split it just for this.
-      (when (file-readable-p mfile)
-       (if (eq system-type 'windows-nt)
-           (when (ignore-errors
-                  (if (file-exists-p tmpfile) (delete-file tmpfile))
-                  ;; FIXME call-process is better, if it works.
-                  (shell-command (format "%s echolisp > %s"
-                                         autoload-make-program tmpfile))
-                  (file-readable-p tmpfile))
-             (with-temp-buffer
-               (insert-file-contents tmpfile)
-               ;; FIXME could be a single while loop.
-               (while (not (eobp))
-                 (setq lim (line-end-position))
-                 (while (re-search-forward "\\([^ ]+\\.el\\)c?\\>" lim t)
-                   (push (expand-file-name (match-string 1))
-                         autoload-excludes))
-                 (forward-line 1))))
-         (with-temp-buffer
-           (insert-file-contents mfile)
-           (when (re-search-forward "^shortlisp= " nil t)
-             (while (and (not lim)
-                         (re-search-forward "\\.\\./lisp/\\([^ ]+\\.el\\)c?\\>"
-                                            nil t))
-               (push (expand-file-name (match-string 1) ldir)
-                     autoload-excludes)
-               (skip-chars-forward " \t")
-               (if (eolp) (setq lim t)))))))))
+    (let ((default-directory (file-name-directory generated-autoload-file))
+         file)
+      (when (file-readable-p "loadup.el")
+       (with-temp-buffer
+         (insert-file-contents "loadup.el")
+         (while (re-search-forward "^(load \"\\([^\"]+\\)\"" nil t)
+           (setq file (match-string 1))
+           (or (string-match "\\.el\\'" file)
+               (setq file (format "%s.el" file)))
+           (or (string-match "\\`site-" file)
+               (push (expand-file-name file) autoload-excludes)))))))
   (let ((args command-line-args-left))
     (setq command-line-args-left nil)
     (apply 'update-directory-autoloads args)))