unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
* Refactoring of emacs-lisp/autoload.el
@ 2008-08-12 16:23 Alan Mackenzie
  2008-08-12 20:09 ` Stefan Monnier
  0 siblings, 1 reply; 10+ messages in thread
From: Alan Mackenzie @ 2008-08-12 16:23 UTC (permalink / raw)
  To: emacs-devel; +Cc: Glenn Morris, Chong Yidong, Stefan Monnier

Hi, Glenn, Stefan, Yidong and Emacs,

in my battle to be able to build Emacs (solved when I fixed my files.el,
which was fouling up file local variables), I refactored autoload.el, to
make it easier to understand and debug.

Although there were no bugs, as such, in that source file, I would like
my refactored version to supersede the existing version anyway, for the
following reasons:

#########################################################################
(i) It is better structured:
  o - the functions are coherently single purpose to a greater extent than
    before;
  o - the output file handling has been separated from source file
    considerations;
  o - two `catch' constructs (~120 and ~80 lines) have been eliminated.

(ii) It produces more consistent result in the comment sections of loaddefs.el
  etc.  In particular:
  o - The lines that identify the source file now always (rather than just
    sometimes) give a file name relative to the "starting directory" (usually
    .../lisp).  E.g.:

*** calc/calc-loaddefs.el.old   2008-08-12 13:17:32.983291472 +0000
--- calc/calc-loaddefs.el.new   2008-08-12 15:00:26.468779024 +0000
***************
*** 9,16 ****
  ;;;;;;  math-read-preprocess-string calcDigit-edit calcDigit-algebraic
  ;;;;;;  calc-alg-digit-entry calc-do-alg-entry calc-alg-entry calc-algebraic-entry
  ;;;;;;  calc-auto-algebraic-entry calc-do-calc-eval calc-do-quick-calc)
! ;;;;;;  "calc-aent" "calc-aent.el" "397561d73c948bd9256db97c177e84f6")
! ;;; Generated autoloads from calc-aent.el
  
  (autoload 'calc-do-quick-calc "calc-aent" "\
  Not documented
--- 9,16 ----
  ;;;;;;  math-read-preprocess-string calcDigit-edit calcDigit-algebraic
  ;;;;;;  calc-alg-digit-entry calc-do-alg-entry calc-alg-entry calc-algebraic-entry
  ;;;;;;  calc-auto-algebraic-entry calc-do-calc-eval calc-do-quick-calc)
! ;;;;;;  "calc-aent" "calc/calc-aent.el" "397561d73c948bd9256db97c177e84f6")
! ;;; Generated autoloads from calc/calc-aent.el

  o - The final section (which records files which had no autoload symbols) no
    longer includes any files for which there is a normal section higher up.
    For example, in lisp/loaddefs.el at the moment, "calc/calc-aent.el"
    violates this rule.  I have assumed that this is a bug.

(iii) The new autoload.el runs quite a lot faster than the old one.  :-)  Here
  are some comparitive timings, done under fair conditions on my 1.2 GHz
  Athlon box:

  OLD:                                    NEW:
  real    1m11.502s                       real    0m40.729s
  user    0m55.141s                       user    0m24.519s
  sys     0m15.981s                       sys     0m15.998s

  This is a speedup of ~75%.
#########################################################################


Here is the patch:


2008-08-12  Alan Mackenzie  <acm@muc.de>

	* emacs-lisp/autoload.el Refactor this source file.
	(autoload-ensure-unix-eols, autoload-entry-up-to-date-p)
	(autoload-extract-cookie)
	(autoload-locate-section-in-existing-buffer)
	(autoload-parse-source-buffer, autoload-setup-output-buffer): New
	functions which supersede autoload-ensure-default-file,
	autoload-file-load-name, autoload-find-destination,
	autoload-generate-file-autoloads, autoload-generated-file,
	autoload-save-buffers, generate-file-autoloads.

	(autoload-print-form-outbuf): Removed.
	

   
Index: autoload.el
===================================================================
RCS file: /cvsroot/emacs/emacs/lisp/emacs-lisp/autoload.el,v
retrieving revision 1.142
diff -c -r1.142 autoload.el
*** autoload.el	10 Jun 2008 09:02:38 -0000	1.142
--- autoload.el	12 Aug 2008 15:52:29 -0000
***************
*** 153,172 ****
  ;; the doc-string in FORM.
  ;; Those properties are now set in lisp-mode.el.
  
- (defun autoload-generated-file ()
-   (expand-file-name generated-autoload-file
-                     ;; File-local settings of generated-autoload-file should
-                     ;; be interpreted relative to the file's location,
-                     ;; of course.
-                     (if (not (local-variable-p 'generated-autoload-file))
-                         (expand-file-name "lisp" source-directory))))
- 
- 
  (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)
--- 153,164 ----
  ;; the doc-string in FORM.
  ;; Those properties are now set in lisp-mode.el.
  
  (defun autoload-read-section-header ()
    "Read a section header form.
! Point should be at the \"(\" following the \";;;### \"."
! ;; 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)
***************
*** 183,203 ****
  	(goto-char (point-min))
  	(read (current-buffer))))))
  
! (defvar autoload-print-form-outbuf nil
!   "Buffer which gets the output of `autoload-print-form'.")
! 
! (defun autoload-print-form (form)
    "Print FORM such that `make-docfile' will find the docstrings.
! The variable `autoload-print-form-outbuf' specifies the buffer to
! put the output in."
    (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
!     (let ((doc-string-elt (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
  	  ;; doc-string must be printed specially for
--- 175,191 ----
  	(goto-char (point-min))
  	(read (current-buffer))))))
  
! (defun autoload-print-form (form outbuf)
    "Print FORM such that `make-docfile' will find the docstrings.
! OUTBUF specifies the buffer to put the output in."
    (cond
     ;; If the form is a sequence, recurse.
!    ((eq (car form) 'progn)
!     (mapcar (lambda (f) (autoload-print-form f outbuf)) (cdr form)))
     ;; Symbols at the toplevel are meaningless.
     ((symbolp form) nil)
     (t
!     (let ((doc-string-elt (get (car-safe form) 'doc-string-elt)))
        (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
***************
*** 255,266 ****
  	    ";;; " basename
  	    " ends here\n")))
  
- (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 (autoload-rubric file) 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."
--- 243,248 ----
***************
*** 304,466 ****
          (substring name 0 (match-beginning 0))
        name)))
  
- (defun generate-file-autoloads (file)
-   "Insert at point a loaddefs autoload section for FILE.
- Autoloads are generated for defuns and defmacros in FILE
- marked by `generate-autoload-cookie' (which see).
- 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)))
- 
- ;; When called from `generate-file-autoloads' we should ignore
- ;; `generated-autoload-file' altogether.  When called from
- ;; `update-file-autoloads' we don't know `outbuf'.  And when called from
- ;; `update-directory-autoloads' it's in between: we know the default
- ;; `outbuf' but we should obey any file-local setting of
- ;; `generated-autoload-file'.
- (defun autoload-generate-file-autoloads (file &optional outbuf outfile)
-   "Insert an autoload section for FILE in the appropriate buffer.
- Autoloads are generated for defuns and defmacros in FILE
- marked by `generate-autoload-cookie' (which see).
- If FILE is being visited in a buffer, the contents of the buffer are used.
- OUTBUF is the buffer in which the autoload statements should be inserted.
- If OUTBUF is nil, it will be determined by `autoload-generated-file'.
- 
- If provided, OUTFILE is expected to be the file name of OUTBUF.
- If OUTFILE is non-nil and FILE specifies a `generated-autoload-file'
- different from OUTFILE, then OUTBUF is ignored.
- 
- Return non-nil if and only if FILE adds no autoloads to OUTFILE
- \(or OUTBUF if OUTFILE is nil)."
-   (catch 'done
-     (let ((autoloads-done '())
-           (load-name (autoload-file-load-name file))
-           (print-length nil)
- 	  (print-level nil)
-           (print-readably t)           ; This does something in Lucid Emacs.
-           (float-output-format nil)
-           (visited (get-file-buffer file))
-           (otherbuf nil)
-           (absfile (expand-file-name file))
-           relfile
-           ;; nil until we found a cookie.
-           output-start)
- 
-       (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)
-           (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))
-                   ;; If not done yet, figure out where to insert this text.
-                   (unless output-start
-                     (when (and outfile
-                                (not (equal outfile (autoload-generated-file))))
-                       ;; A file-local setting of autoload-generated-file says
-                       ;; we should ignore OUTBUF.
-                       (setq outbuf nil)
-                       (setq otherbuf t))
-                     (unless outbuf
-                       (setq outbuf (autoload-find-destination absfile))
-                       (unless outbuf
-                         ;; The file has autoload cookies, but they're
-                         ;; already up-to-date.  If OUTFILE is nil, the
-                         ;; entries are in the expected OUTBUF, otherwise
-                         ;; they're elsewhere.
-                         (throw 'done outfile)))
-                     (with-current-buffer outbuf
-                       (setq relfile (file-relative-name absfile))
-                       (setq output-start (point)))
-                     ;; (message "file=%S, relfile=%S, dest=%S"
-                     ;;          file relfile (autoload-generated-file))
-                     )
-                   (search-forward generate-autoload-cookie)
-                   (skip-chars-forward " \t")
-                   (if (eolp)
-                       (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)))
- 
-                     ;; 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 output-start
-             (let ((secondary-autoloads-file-buf
-                    (if (local-variable-p 'generated-autoload-file)
-                        (current-buffer))))
-               (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 relfile
-                    (if secondary-autoloads-file-buf
-                        ;; MD5 checksums are much better because they do not
-                        ;; change unless the file changes (so they'll be
-                        ;; equal on two different systems and will change
-                        ;; less often than time-stamps, thus leading to fewer
-                        ;; unneeded changes causing spurious conflicts), but
-                        ;; using time-stamps is a very useful optimization,
-                        ;; so we use time-stamps for the main autoloads file
-                        ;; (loaddefs.el) where we have special ways to
-                        ;; circumvent the "random change problem", and MD5
-                        ;; checksum in secondary autoload files where we do
-                        ;; not need the time-stamp optimization because it is
-                        ;; already provided by the primary autoloads file.
-                        (md5 secondary-autoloads-file-buf
-                             ;; We'd really want to just use
-                             ;; `emacs-internal' instead.
-                             nil nil 'emacs-mule-unix)
-                      (nth 5 (file-attributes relfile))))
-                   (insert ";;; Generated autoloads from " relfile "\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))))
-       ;; If the entries were added to some other buffer, then the file
-       ;; doesn't add entries to OUTFILE.
-       (or (not output-start) otherbuf))))
  \f
! (defun autoload-save-buffers ()
!   (while autoload-modified-buffers
!     (with-current-buffer (pop autoload-modified-buffers)
!       (save-buffer))))
  
  ;;;###autoload
  (defun update-file-autoloads (file &optional save-after)
--- 286,538 ----
          (substring name 0 (match-beginning 0))
        name)))
  
  \f
! (defun autoload-remove-section (begin)
!   (goto-char begin)
!   (search-forward generate-autoload-section-trailer)
!   (delete-region begin (point)))
! 
! (defun autoload-locate-section-in-existing-buffer (file out-file)
!   "Locate the position in OUT-FILE's buffer for the entry for FILE.
! FILE is the full path name of a source lisp file.
! OUT-FILE is file name of the output file \(e.g. \"loaddefs.el\").
! 
! There needn't be an existing entry for FILE at this position.
! 
! If necessary, this function loads (the already existing) OUT-FILE
! into a new buffer.
! 
! Return a buffer for OUT-FILE with point at the ^L at the start of
! the entry.  If neither OUT-FILE nor a buffer for it exists,
! return nil."
!   (let ((outbuf
! 	 (or (get-file-buffer out-file)
! 	     (and (file-exists-p out-file)
! 		  (find-file-noselect out-file))))
! 	(load-name (autoload-file-load-name file))
! 	section-name)
!     (and outbuf
! 	 (> (buffer-size outbuf) 0)
! 	 (with-current-buffer outbuf
! 	   (widen)
! 	   ;; Optimise for the new entry being near current pos in out-file.
! 	   (while (and (search-backward generate-autoload-section-header nil 'BOB)
! 		       (save-excursion
! 			 (goto-char (match-end 0))
! 			 (setq section-name (nth 2 (autoload-read-section-header)))
! 			 (or (null section-name) ; list of autoloadless files at EOF.
! 			     (string< load-name section-name)))))
! 	   (unless (and (looking-at generate-autoload-section-header)
! 			(string= load-name section-name))
! 	     (or (search-forward "\f" nil t)   ; Ensure (match-beginning 0) will work.
! 		 (error "Autoload file %s is malformed." out-file))
! 	     (backward-char)
! 	     (while (and (search-forward generate-autoload-section-header nil 'EOB)
! 			 (setq section-name (nth 2 (autoload-read-section-header)))
! 			 section-name (string< section-name load-name)))
! 	     (if (eobp)
! 		 (search-backward "\f")
! 	       (goto-char (match-beginning 0))))
! 	   outbuf))))
! 
! (defun autoload-entry-up-to-date-p (file out-file)
!   "Is there an existing up to date section for FILE in OUT-FILE?
! FILE is the full path name of a source lisp file, and its
! contents are the current buffer.
! 
! OUT-FILE is the file name of the output file \(e.g. \"loaddefs.el\").
! This file might not yet exist, a buffer for it may or may not exist.
! 
! If necessary, this function loads OUT-FILE into a new buffer."
!   (let* ((outbuf (autoload-locate-section-in-existing-buffer file out-file))
! 	 (section-header
! 	  (and outbuf
! 	       (with-current-buffer outbuf
! 		 (and (search-forward generate-autoload-section-header nil t)
! 		      (autoload-read-section-header)))))
! 	 (load-name (autoload-file-load-name file))
! 	 date-or-sum)
! 
!     (and section-header
! 	 (string= load-name (nth 2 section-header))
! 	 (setq date-or-sum (nth 4 section-header))
! 	 (cond
! 	  ((and (listp date-or-sum) (= (length date-or-sum) 2)) ; timestamp
! 	   (not (time-less-p date-or-sum
! 			     (nth 5 (file-attributes file)))))
! 	  ((stringp date-or-sum)	; MD5 checksum.
! 	   (equal date-or-sum
! 		  (md5 (current-buffer) nil nil 'emacs-mule)))))))
! 
! (defun autoload-ensure-unix-eols ()
!   "Set the buffer file coding system to use unix EOLs, if needed."
!   (unless (let ((eol-type (coding-system-eol-type buffer-file-coding-system)))
! 	    (cond ((and (numberp eol-type) (zerop eol-type)))
! 		  ((vectorp eol-type)
! 		   (set-buffer-file-coding-system (aref eol-type 0)))
! 		  (t (set-buffer-file-coding-system 'unix))))))
! 
! (defun autoload-setup-output-buffer (file out-file)
!   "Prepare a buffer \(e.g. loaddefs.el) for outputting autoload cookies.
! OUT-FILE is the file name of this buffer.
! FILE is the name of the source lisp file.
! 
! The return value is the buffer.
! 
! \"Prepare\" means:
!   1. If the buffer doesn't yet exist, create it and initialize it
! with boilerplate;
!   2. move to the right place in the buffer to insert new stuff;
! this is just before a ^L;
!   3. delete any existing entry for FILE;
!   4. Make sure the buffer's coding system is set for UNIX eols.
! 
! Note that we don't insert the section header for FILE here, since
! we haven't yet calculated its contents.
! 
! Point in this buffer is left at the place to insert the first cookie."
!   (let ((outbuf (autoload-locate-section-in-existing-buffer file out-file))
! 	(load-name (autoload-file-load-name file))
! 	section-header)
! 
!     (if outbuf
! 	;; Existing file or buffer; Delete any existing entry for FILE.
! 	(with-current-buffer outbuf
! 	  (autoload-ensure-unix-eols)
! 	  (if (save-excursion
! 		(and (search-forward generate-autoload-section-header nil t)
! 		     (setq section-header (autoload-read-section-header))
! 		     (string= (nth 2 section-header) load-name)))
! 	      (autoload-remove-section (point))))
! 
!       ;; Create a new output buffer.
!       (setq outbuf (find-file-noselect out-file))
!       (with-current-buffer outbuf
! 	(or (file-writable-p out-file)
! 	    (error "Autoloads file %s is not writable" out-file))
! 	(autoload-ensure-unix-eols)
! 	(insert (autoload-rubric out-file))
! 	(search-backward "\f")))
!     outbuf))
! 
! (defun autoload-extract-cookie (outbuf file)
!   "Extract the autoload cookie at point, write it into OUTBUF.
! Point is just after the autoload cookie \(usually \";;;###autoload\")
! on entry, and is left just after the autoload form on exit.
! 
! OUTBUF is the buffer (typically, loaddefs.el) into which we
! insert the new cookie at the buffer's current position.
! 
! FILE is the absolute name of the elisp source file containing the
! cookie.
! 
! Return the name defined (a symbol), or nil if there wasn't one."
!   (let ((load-name (autoload-file-load-name file))
! 	cookie-name)
!     (skip-chars-forward " \t")
!     (if (eolp)
! 	(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
! 		  (setq cookie-name (nth 1 form))
! 		(setq autoload form))
! 	      (autoload-print-form autoload outbuf))
! 	  (error
! 	   (message "Error in %s: %S" file err)))
! 
!       ;; 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))
!     cookie-name))
! 
! (defun autoload-parse-source-buffer (inbuf file)
!   "Extract the autoload specs, if any, from the buffer INBUF,
! writing them to the buffer whose file name is in the the variable
! `generated-autoload-file' \(which may have a file-local binding in
! INBUF).  The buffer isn't saved in this function.
! 
! FILE is the absolute name of the elisp source file loaded in the
! current buffer.
! 
! The current directory is the \"starting directory\" for the
! autoload generation, typically .../emacs/lisp/.
! 
! If the buffer for `generated-autoload-file' is actually written
! to, return this buffer.  Otherwise, return nil."
!   (let (autoloads-done	  ; list of autoload SYMBOLS written to output buffer.
! 	(load-name (autoload-file-load-name file))
! 	(relfile (file-relative-name file))
! 	cookie
! 	(cookie-regexp (concat "^" (regexp-quote generate-autoload-cookie)))
! 	outbuf
! 	output-started	   ; Position in outbuf of first cookie generated from
! 	                   ; current input file, or nil.
! 	(print-length nil)	 ; don't truncate a list when printing.
! 	(print-level nil)	 ; don't limit nesting of lists when printing.
! 	;; (print-readably t)  ; This does something in XEmacs.  Removed 2008-08.
! 	(float-output-format nil))  ; Use a default format for printing floats.
! 
!     (message "Generating autoloads for %s..." relfile)
!     (with-current-buffer inbuf
!       (unless no-update-autoloads ; file local variable which stops autoload stuff.
! 	(save-excursion
! 	  (save-restriction
! 	    (widen)
! 	    ;; If we already have a valid up to date entry for FILE, don't do
! 	    ;; anything.
! 	    (unless (autoload-entry-up-to-date-p file generated-autoload-file)
! 	      (goto-char (point-min))
! 	      (while (search-forward-regexp cookie-regexp nil t)
! 		;; Get the output buffer only when the first cookie has been found.
! 		(unless outbuf
! 		  (setq outbuf (autoload-setup-output-buffer
! 				file generated-autoload-file)
! 			output-started (with-current-buffer outbuf (point))))
! 		(if (setq cookie (autoload-extract-cookie outbuf file))
! 		    (push cookie autoloads-done)))
! 
! 	      ;; Write the section header for FILE.  This contains a summary list
! 	      ;; of functions at the beginning of the section in the output file.
! 	      (when outbuf
! 		(let ((date-or-checksum
! 		       (if (local-variable-p 'generated-autoload-file)
! 			   ;; MD5 checksums are much better because they do not
! 			   ;; change unless the file changes (so they'll be equal
! 			   ;; on two different systems and will change less often
! 			   ;; than time-stamps, thus leading to fewer unneeded
! 			   ;; changes causing spurious conflicts), but using
! 			   ;; time-stamps is a very useful optimization, so we
! 			   ;; use time-stamps for the main autoloads file
! 			   ;; (loaddefs.el) where we have special ways to
! 			   ;; circumvent the "random change problem", and MD5
! 			   ;; checksum in secondary autoload files where we do
! 			   ;; not need the time-stamp optimization because it is
! 			   ;; already provided by the primary autoloads file.
! 			   (md5 (current-buffer) nil nil 'emacs-mule-unix)
! 			 (nth 5 (file-attributes file))))) ; timestamp.
! 		  (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-started)
! 		      (autoload-insert-section-header
! 		       outbuf autoloads-done load-name relfile
! 		       date-or-checksum)
! 		      (insert ";;; Generated autoloads from " relfile "\n"))
! 		    (insert generate-autoload-section-trailer)))))))
! 	(message "Generating autoloads for %s...done" relfile)
! 	outbuf))))
  
  ;;;###autoload
  (defun update-file-autoloads (file &optional save-after)
***************
*** 471,561 ****
  
  Return FILE if there was no autoload cookie in it, else nil."
    (interactive "fUpdate autoloads for file: \np")
!   (let* ((autoload-modified-buffers nil)
!          (no-autoloads (autoload-generate-file-autoloads file)))
!     (if autoload-modified-buffers
!         (if save-after (autoload-save-buffers))
        (if (interactive-p)
!           (message "Autoload section for %s is up to date." file)))
!     (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.
! Returns nil if the file's autoloads are uptodate, otherwise
! removes any prior now out-of-date autoload entries."
!   (catch 'up-to-date
!     (let* ((load-name (autoload-file-load-name file))
!            (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.
!           (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.
!         (unless (zerop (coding-system-eol-type buffer-file-coding-system))
!           (set-buffer-file-coding-system 'unix))
!         (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))
!         (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))
!                          (file-time (nth 5 (file-attributes file))))
!                      (if (and (or (null existing-buffer)
!                                   (not (buffer-modified-p existing-buffer)))
!                               (or
!                                ;; last-time is the time-stamp (specifying
!                                ;; the last time we looked at the file) and
!                                ;; the file hasn't been changed since.
!                                (and (listp last-time) (= (length last-time) 2)
!                                     (not (time-less-p last-time file-time)))
!                                ;; last-time is an MD5 checksum instead.
!                                (and (stringp last-time)
!                                     (equal last-time
!                                            (md5 buf nil nil 'emacs-mule)))))
!                          (throw 'up-to-date nil)
!                        (autoload-remove-section begin)
!                        (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)))
!         (unless (memq (current-buffer) autoload-modified-buffers)
!           (push (current-buffer) autoload-modified-buffers))
!         (current-buffer)))))
! 
! (defun autoload-remove-section (begin)
!   (goto-char begin)
!   (search-forward generate-autoload-section-trailer)
!   (delete-region begin (point)))
  
  ;;;###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
  directories as separate arguments, but this usage is discouraged.
--- 543,570 ----
  
  Return FILE if there was no autoload cookie in it, else nil."
    (interactive "fUpdate autoloads for file: \np")
!   (let* ((generated-autoload-file (expand-file-name generated-autoload-file))
! 	 ;; The above is needed to "anchor" g-a-f to the current directory;
! 	 ;; otherwise it might be expanded relative to FILE's directory.
! 	 (ffile (expand-file-name file))
! 	 (visited (get-file-buffer ffile)) ; Is the file already loaded?
! 	 autoload-modified-buffer)
!     
!     (setq autoload-modified-buffer
! 	  (autoload-parse-source-buffer
! 	   (or visited (autoload-find-file ffile)) ffile))
! 
!     (if autoload-modified-buffer
! 	(if save-after
! 	    (with-current-buffer autoload-modified-buffer (save-buffer)))
        (if (interactive-p)
! 	  (message "Autoload section for %s is up to date." file))
!       file)))
  
  ;;;###autoload
  (defun update-directory-autoloads (&rest dirs)
    "\
  Update loaddefs.el with all the current autoloads from DIRS, and no old ones.
  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.
***************
*** 563,569 ****
  The function does NOT recursively descend into subdirectories of the
  directory or directories specified."
    (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)))))
--- 572,581 ----
  The function does NOT recursively descend into subdirectories of the
  directory or directories specified."
    (interactive "DUpdate autoloads from directory: ")
!   (let* ((generated-autoload-file (expand-file-name generated-autoload-file))
! 	 ;; The above is needed to "anchor" g-a-f to the current directory;
! 	 ;; otherwise it might be expanded relative to a file's directory.
! 	 (files-re (let ((tmp nil))
  		     (dolist (suf (get-load-suffixes)
  				  (concat "^[^=.].*" (regexp-opt tmp t) "\\'"))
  		       (unless (string-match "\\.elc" suf) (push suf tmp)))))
***************
*** 571,646 ****
  		       (mapcar (lambda (dir)
  				 (directory-files (expand-file-name dir)
  						  t files-re))
  			       dirs)))
!          (done ())
  	 (this-time (current-time))
           ;; 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))
  
!     (with-current-buffer
! 	(find-file-noselect
!          (autoload-ensure-default-file (autoload-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)))
! 
! 	(goto-char (point-min))
! 	(while (search-forward generate-autoload-section-header nil t)
! 	  (let* ((form (autoload-read-section-header))
! 		 (file (nth 3 form)))
! 	    (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
! 				    (not (time-less-p last-time file-time)))
! 			   ;; file unchanged
! 			   (push file no-autoloads)
! 			   (setq files (delete file files)))))))
! 		  ((not (stringp file)))
! 		  ((or (not (file-exists-p file))
!                        ;; Remove duplicates as well, just in case.
!                        (member file done))
!                    ;; Remove the obsolete section.
! 		   (autoload-remove-section (match-beginning 0)))
! 		  ((not (time-less-p (nth 4 form)
!                                      (nth 5 (file-attributes file))))
! 		   ;; File hasn't changed.
! 		   nil)
! 		  (t
!                    (autoload-remove-section (match-beginning 0))
!                    (if (autoload-generate-file-autoloads
!                         file (current-buffer) buffer-file-name)
!                        (push file no-autoloads))))
!             (push file done)
! 	    (setq files (delete file files)))))
!       ;; Elements remaining in FILES have no existing autoload sections yet.
!       (dolist (file files)
!         (if (autoload-generate-file-autoloads file nil buffer-file-name)
!             (push file no-autoloads)))
  
!       (when no-autoloads
  	;; Sort them for better readability.
  	(setq no-autoloads (sort no-autoloads 'string<))
! 	;; 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))
! 
!       (save-buffer)
!       ;; In case autoload entries were added to other files because of
!       ;; file-local autoload-generated-file settings.
!       (autoload-save-buffers))))
  
  (define-obsolete-function-alias 'update-autoloads-from-directories
      'update-directory-autoloads "22.1")
--- 583,629 ----
  		       (mapcar (lambda (dir)
  				 (directory-files (expand-file-name dir)
  						  t files-re))
+ 			       ;; "loaddefs.el", etc. aren't a problem here.
+ 			       ;; They have `no-update-autoloads' set.
  			       dirs)))
! 	 (default-autoload-dir (file-name-directory generated-autoload-file))
! 	 file visited
!          autoload-modified-buffers
  	 (this-time (current-time))
           ;; Files with no autoload cookies or whose autoloads go to other
           ;; files because of file-local autoload-generated-file settings.
! 	 no-autoloads)
  
!     (while files
!       (setq file (car files)  files (cdr files))
!       (setq visited (get-file-buffer file)) ; Is the file already loaded?
! 
!       ;; This buffer might locally bind `generated-autoload-file'.
!       (let* ((inbuf (or visited (autoload-find-file file)))
! 	     (autoload-buffer (autoload-parse-source-buffer inbuf file)))
! 	(if autoload-buffer
! 	    (or (memq autoload-buffer autoload-modified-buffers)
! 		(push autoload-buffer autoload-modified-buffers))
! 	  (push (file-relative-name file default-autoload-dir) no-autoloads))
! 	(unless visited (kill-buffer inbuf))))
  
!     ;; Write the list of autoloadless files.
!     (when no-autoloads
  	;; Sort them for better readability.
  	(setq no-autoloads (sort no-autoloads 'string<))
! 	;; Add the `no-autoloads' section.  Surely, we'll have opened
! 	;; `generated-autoload-file' by now?
! 	(if (setq visited (get-file-buffer generated-autoload-file))
! 	    (with-current-buffer visited
! 	      (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))))
! 
!     (while autoload-modified-buffers
!       (with-current-buffer (pop autoload-modified-buffers)
! 	(save-buffer)))))
  
  (define-obsolete-function-alias 'update-autoloads-from-directories
      'update-directory-autoloads "22.1")


-- 
Alan Mackenzie (Nuremberg, Germany).




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Refactoring of emacs-lisp/autoload.el
  2008-08-12 16:23 Refactoring of emacs-lisp/autoload.el Alan Mackenzie
@ 2008-08-12 20:09 ` Stefan Monnier
  2008-08-12 20:58   ` Glenn Morris
  2008-08-13 14:11   ` Alan Mackenzie
  0 siblings, 2 replies; 10+ messages in thread
From: Stefan Monnier @ 2008-08-12 20:09 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Glenn Morris, Chong Yidong, emacs-devel

> #########################################################################
> (i) It is better structured:
>   o - the functions are coherently single purpose to a greater extent than
>     before;
>   o - the output file handling has been separated from source file
>     considerations;
>   o - two `catch' constructs (~120 and ~80 lines) have been eliminated.

Sounds very good.

> (ii) It produces more consistent result in the comment sections of loaddefs.el
>   etc.  In particular:
>   o - The lines that identify the source file now always (rather than just
>     sometimes) give a file name relative to the "starting directory" (usually
>     .../lisp).  E.g.:

> *** calc/calc-loaddefs.el.old   2008-08-12 13:17:32.983291472 +0000
> --- calc/calc-loaddefs.el.new   2008-08-12 15:00:26.468779024 +0000
> ***************
> *** 9,16 ****
>   ;;;;;;  math-read-preprocess-string calcDigit-edit calcDigit-algebraic
>   ;;;;;;  calc-alg-digit-entry calc-do-alg-entry calc-alg-entry calc-algebraic-entry
>   ;;;;;;  calc-auto-algebraic-entry calc-do-calc-eval calc-do-quick-calc)
> ! ;;;;;;  "calc-aent" "calc-aent.el" "397561d73c948bd9256db97c177e84f6")
> ! ;;; Generated autoloads from calc-aent.el
  
>   (autoload 'calc-do-quick-calc "calc-aent" "\
>   Not documented
> --- 9,16 ----
>   ;;;;;;  math-read-preprocess-string calcDigit-edit calcDigit-algebraic
>   ;;;;;;  calc-alg-digit-entry calc-do-alg-entry calc-alg-entry calc-algebraic-entry
>   ;;;;;;  calc-auto-algebraic-entry calc-do-calc-eval calc-do-quick-calc)
> ! ;;;;;;  "calc-aent" "calc/calc-aent.el" "397561d73c948bd9256db97c177e84f6")
> ! ;;; Generated autoloads from calc/calc-aent.el

I'm not sure why you think it's an improvement: the previous behavior
was to use a file name relate to where the name was placed
(i.e. relative to the file in which the entry is created).  In my
opinions, relative file names placed in files should generally be
relative to the file in which they appear rather than relative to the
"top of the project".

Of course, there might be a good reason for your change, so I'm not
necessarily opposed to this.  More specifically, I think in this
particular instance it does not matter either way, so I don't have
a strong opinion.  I'd still be interested to hear your reason for
this change.  Just a personal preference?  Fix an actual problem?
Simplifies the code?

>   o - The final section (which records files which had no autoload symbols) no
>     longer includes any files for which there is a normal section higher up.
>     For example, in lisp/loaddefs.el at the moment, "calc/calc-aent.el"
>     violates this rule.  I have assumed that this is a bug.

No, this was not a bug.  This section is present to speed up the refresh
of the loaddefs.el file, so that files that don't have other entries in
loaddefs.el don't have to be opened&scanned when they haven't changed
since the last time we refreshed loaddefs.el.
So calc/calc-aent.el should probably be mentioned in this list so as to
avoid having to open&scan it when it hasn't changed.

> (iii) The new autoload.el runs quite a lot faster than the old one.  :-)  Here
>   are some comparitive timings, done under fair conditions on my 1.2 GHz
>   Athlon box:

>   OLD:                                    NEW:
>   real    1m11.502s                       real    0m40.729s
>   user    0m55.141s                       user    0m24.519s
>   sys     0m15.981s                       sys     0m15.998s

There are 2 ways to run this code: one is to rebuild loaddefs.el from
scratch, the other is to update a preexisting loaddefs.el (typically
just after "cvs update").  With "make bootstrap" you care about the
first, otherwise you care about the second.  I.e. I care about the
second ;-)

The second case used to be unusably slow and I made it a lot faster
around Emacs-21 (or so), so now it's fast enough: please test it after
changing a handful of files, just to make sure that it's not
significantly slower than before.

>   This is a speedup of ~75%.

That's great.  In parallel bootstrap builds, the loaddefs.el is sometimes
on the critical path, so that's a very welcome improvement.

I can't easily browse your patch with the machine I'm using right now,
so I can't comment on the actual code.  The current code (mostly due to
yours truly, sadly) is not very satisfactory indeed, so as long as the
new code is fully compatible with the various ways autoload.el is used
(i..e not just by Emacs itself but by other external 3rd party
packages), and as long as it doesn't significantly slow down the "no/few
changes" case, I see no reason not to install it,


        Stefan




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Refactoring of emacs-lisp/autoload.el
  2008-08-12 20:09 ` Stefan Monnier
@ 2008-08-12 20:58   ` Glenn Morris
  2008-08-13 14:13     ` Alan Mackenzie
  2008-08-13 14:11   ` Alan Mackenzie
  1 sibling, 1 reply; 10+ messages in thread
From: Glenn Morris @ 2008-08-12 20:58 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Alan Mackenzie, Chong Yidong, emacs-devel

Stefan Monnier wrote:

> changes" case, I see no reason not to install it,

Apart from the feature freeze, of course.




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Refactoring of emacs-lisp/autoload.el
  2008-08-12 20:09 ` Stefan Monnier
  2008-08-12 20:58   ` Glenn Morris
@ 2008-08-13 14:11   ` Alan Mackenzie
  2008-08-13 20:31     ` Stefan Monnier
  1 sibling, 1 reply; 10+ messages in thread
From: Alan Mackenzie @ 2008-08-13 14:11 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Glenn Morris, Chong Yidong, emacs-devel

Hi, Stefan!

Thanks for the comments:

On Tue, Aug 12, 2008 at 04:09:04PM -0400, Stefan Monnier wrote:

> > (ii) It produces more consistent result in the comment sections of loaddefs.el
> >   etc.  In particular:
> >   o - The lines that identify the source file now always (rather than just
> >     sometimes) give a file name relative to the "starting directory" (usually
> >     .../lisp).  E.g.:

[ .... ]

> I'm not sure why you think it's an improvement: the previous behavior
> was to use a file name relate to where the name was placed (i.e.
> relative to the file in which the entry is created).  In my opinions,
> relative file names placed in files should generally be relative to the
> file in which they appear rather than relative to the "top of the
> project".

It's just I hadn't understood why it was being done.  I see now that it
was being done right, and I've changed my code to do it the same way.

> >   o - The final section (which records files which had no autoload
> >     symbols) no longer includes any files for which there is a normal
> >     section higher up.  For example, in lisp/loaddefs.el at the
> >     moment, "calc/calc-aent.el" violates this rule.  I have assumed
> >     that this is a bug.

> No, this was not a bug.  This section is present to speed up the refresh
> of the loaddefs.el file, so that files that don't have other entries in
> loaddefs.el don't have to be opened&scanned when they haven't changed
> since the last time we refreshed loaddefs.el.
> So calc/calc-aent.el should probably be mentioned in this list so as to
> avoid having to open&scan it when it hasn't changed.

OK.  I haven't yet characterised fully the criterion for a file being in
this list.  I'm looking at that now.

> > (iii) The new autoload.el runs quite a lot faster than the old one.  :-)  Here
> >   are some comparitive timings, done under fair conditions on my 1.2 GHz
> >   Athlon box:

> >   OLD:                                    NEW:
> >   real    1m11.502s                       real    0m40.729s
> >   user    0m55.141s                       user    0m24.519s
> >   sys     0m15.981s                       sys     0m15.998s

> There are 2 ways to run this code: one is to rebuild loaddefs.el from
> scratch, the other is to update a preexisting loaddefs.el (typically
> just after "cvs update").  With "make bootstrap" you care about the
> first, otherwise you care about the second.  I.e. I care about the
> second ;-)

Ah..  I hadn't properly understood the loop through the existing entries
of loaddefs.el.  This is surely to minimise the searching within
loaddefs.el, which is very slow if it's done clumsily.

My strategy when inserting a new entry into loaddefs.el is to start
searching from the current position in that file.  So if the source files
are processed in alphabetical order, the newer autoload.el should be just
as fast (or, at least, fast enough).

> The second case used to be unusably slow and I made it a lot faster
> around Emacs-21 (or so), so now it's fast enough: please test it after
> changing a handful of files, just to make sure that it's not
> significantly slower than before.

I'll test it.  Could you suggest a typical test scenario, please?  How
many source files should I touch to test this?  3, 10, 30, 100?

> >   This is a speedup of ~75%.

> That's great.  In parallel bootstrap builds, the loaddefs.el is
> sometimes on the critical path, so that's a very welcome improvement.

> I can't easily browse your patch with the machine I'm using right now,
> so I can't comment on the actual code.  The current code (mostly due to
> yours truly, sadly) is not very satisfactory indeed, so as long as the
> new code is fully compatible with the various ways autoload.el is used
> (i..e not just by Emacs itself but by other external 3rd party
> packages), and as long as it doesn't significantly slow down the
> "no/few changes" case, I see no reason not to install it,

I agree with all that.  I've been taking as much care as possible.  But I
know nothing of its use by 3rd party packages.  Do you know of any such
uses, or is this a "there are probably some" situation?

>         Stefan

-- 
Alan Mackenzie (Nuremberg, Germany).




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Refactoring of emacs-lisp/autoload.el
  2008-08-12 20:58   ` Glenn Morris
@ 2008-08-13 14:13     ` Alan Mackenzie
  2008-08-13 16:11       ` Glenn Morris
  0 siblings, 1 reply; 10+ messages in thread
From: Alan Mackenzie @ 2008-08-13 14:13 UTC (permalink / raw)
  To: Glenn Morris; +Cc: Chong Yidong, Stefan Monnier, emacs-devel

Hi, Glenn,

On Tue, Aug 12, 2008 at 04:58:18PM -0400, Glenn Morris wrote:
> Stefan Monnier wrote:

> > changes" case, I see no reason not to install it,

> Apart from the feature freeze, of course.

This isn't a feature change.  At least, I'm doing everything within my
capability to avoid it being a feature change.

-- 
Alan Mackenzie (Nuremberg, Germany).




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Refactoring of emacs-lisp/autoload.el
  2008-08-13 14:13     ` Alan Mackenzie
@ 2008-08-13 16:11       ` Glenn Morris
  0 siblings, 0 replies; 10+ messages in thread
From: Glenn Morris @ 2008-08-13 16:11 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Chong Yidong, Stefan Monnier, emacs-devel

Alan Mackenzie wrote:

> This isn't a feature change.  At least, I'm doing everything within my
> capability to avoid it being a feature change.

Fair enough, it's not a feature change. I guess I misunderstand
Emacs's version of "feature freeze". I thought the idea was to make as
few changes as possible, to fix only specific, well-defined problems,
in order to get the most stable and tested end product possible.




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Refactoring of emacs-lisp/autoload.el
  2008-08-13 14:11   ` Alan Mackenzie
@ 2008-08-13 20:31     ` Stefan Monnier
  2008-08-14 13:17       ` Alan Mackenzie
  0 siblings, 1 reply; 10+ messages in thread
From: Stefan Monnier @ 2008-08-13 20:31 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Glenn Morris, Chong Yidong, emacs-devel

>> No, this was not a bug.  This section is present to speed up the refresh
>> of the loaddefs.el file, so that files that don't have other entries in
>> loaddefs.el don't have to be opened&scanned when they haven't changed
>> since the last time we refreshed loaddefs.el.
>> So calc/calc-aent.el should probably be mentioned in this list so as to
>> avoid having to open&scan it when it hasn't changed.

> OK.  I haven't yet characterised fully the criterion for a file being in
> this list.  I'm looking at that now.

Normally, all files should appear in loaddefs.el: either they have their
own section or they're in the "nothing to declare" section.
If there are a couple files not mentioned there, it's not tragic, tho.

The goal is that running "cd emacs/lisp; make autoloads" after
loaddefs.el has just been updated should only need to scan loaddefs.el,
and check the timestamp of all the .el files, and can then return
without reading any of the other (unchanged) elisp files, so that "make
autoloads" is almost immediate when it has nothing to do.

> Ah..  I hadn't properly understood the loop through the existing entries
> of loaddefs.el.  This is surely to minimise the searching within
> loaddefs.el, which is very slow if it's done clumsily.

That loop was fairly inefficient.

> My strategy when inserting a new entry into loaddefs.el is to start
> searching from the current position in that file.  So if the source files
> are processed in alphabetical order, the newer autoload.el should be just
> as fast (or, at least, fast enough).

Yes, that sounds fine.

>> The second case used to be unusably slow and I made it a lot faster
>> around Emacs-21 (or so), so now it's fast enough: please test it after
>> changing a handful of files, just to make sure that it's not
>> significantly slower than before.

> I'll test it.  Could you suggest a typical test scenario, please?  How
> many source files should I touch to test this?  3, 10, 30, 100?

Yes, 10, 30, 0, that sounds fine.

> I agree with all that.  I've been taking as much care as possible.

Great.

> But I know nothing of its use by 3rd party packages.  Do you know of
> any such uses, or is this a "there are probably some" situation?

I know I use it in sml-mode and haskell-mode and I know mh-e uses it as
well, but all three use it via batch-update-autoloads, so it should work
just fine.  I just expect other uses since there are ;;;###autoload
cookies for other entry points.  But indeed, I do not know of
concrete examples.


        Stefan




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Refactoring of emacs-lisp/autoload.el
  2008-08-13 20:31     ` Stefan Monnier
@ 2008-08-14 13:17       ` Alan Mackenzie
  2008-08-14 17:52         ` Stefan Monnier
  0 siblings, 1 reply; 10+ messages in thread
From: Alan Mackenzie @ 2008-08-14 13:17 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Glenn Morris, Chong Yidong, emacs-devel

Hi, Stefan!

On Wed, Aug 13, 2008 at 04:31:14PM -0400, Stefan Monnier wrote:
> > OK.  I haven't yet characterised fully the criterion for a file being
> > in this list.  I'm looking at that now.

> Normally, all files should appear in loaddefs.el: either they have their
> own section or they're in the "nothing to declare" section.
> If there are a couple files not mentioned there, it's not tragic, tho.

Ah, got it!  It was just I thought I'd seen a file with its own section
which was also in the "innocent" section at the end.  I've had a look for
this, and can't find it.  So I must have been mistaken.

> The goal is that running "cd emacs/lisp; make autoloads" after
> loaddefs.el has just been updated should only need to scan loaddefs.el,
> and check the timestamp of all the .el files, and can then return
> without reading any of the other (unchanged) elisp files, so that "make
> autoloads" is almost immediate when it has nothing to do.

OK.  This is obviously very important.  The patch I posted here isn't up
to scratch for that.  I'll need to improve it.

> > My strategy when inserting a new entry into loaddefs.el is to start
> > searching from the current position in that file.  So if the source
> > files are processed in alphabetical order, the newer autoload.el
> > should be just as fast (or, at least, fast enough).

> Yes, that sounds fine.

What I'll be doing is sorting the "no-autoloads" list so that it's in the
same order as the file entries.  I can then loop through all the files,
testing in turn whether the file was a "no-autoload" or had its own
section, and take the appropriate action.  That will mean never having to
search loaddefs.el for an entry, and not having to use `member' in the
"no-autoloads" list, since I will be stepping through both from start to
finish.

>         Stefan

-- 
Alan Mackenzie (Nuremberg, Germany).




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Refactoring of emacs-lisp/autoload.el
  2008-08-14 13:17       ` Alan Mackenzie
@ 2008-08-14 17:52         ` Stefan Monnier
  2008-09-01 21:55           ` Alan Mackenzie
  0 siblings, 1 reply; 10+ messages in thread
From: Stefan Monnier @ 2008-08-14 17:52 UTC (permalink / raw)
  To: Alan Mackenzie; +Cc: Glenn Morris, Chong Yidong, emacs-devel

>> Yes, that sounds fine.

> What I'll be doing is sorting the "no-autoloads" list so that it's in the
> same order as the file entries.  I can then loop through all the files,
> testing in turn whether the file was a "no-autoload" or had its own
> section, and take the appropriate action.  That will mean never having to
> search loaddefs.el for an entry, and not having to use `member' in the
> "no-autoloads" list, since I will be stepping through both from start to
> finish.

Sounds fine.  Tho using member hasn't been a problem until now (even
with its O(n^2) behavior), so I wouldn't waste any time trying to avoid it.


        Stefan




^ permalink raw reply	[flat|nested] 10+ messages in thread

* Re: Refactoring of emacs-lisp/autoload.el
  2008-08-14 17:52         ` Stefan Monnier
@ 2008-09-01 21:55           ` Alan Mackenzie
  0 siblings, 0 replies; 10+ messages in thread
From: Alan Mackenzie @ 2008-09-01 21:55 UTC (permalink / raw)
  To: Stefan Monnier; +Cc: Glenn Morris, Chong Yidong, emacs-devel

Hello, Stefan!

On Thu, Aug 14, 2008 at 01:52:24PM -0400, Stefan Monnier wrote:
> >> Yes, that sounds fine.

> > What I'll be doing is sorting the "no-autoloads" list so that it's in the
> > same order as the file entries.  I can then loop through all the files,
> > testing in turn whether the file was a "no-autoload" or had its own
> > section, and take the appropriate action.  That will mean never having to
> > search loaddefs.el for an entry, and not having to use `member' in the
> > "no-autoloads" list, since I will be stepping through both from start to
> > finish.

> Sounds fine.  Tho using member hasn't been a problem until now (even
> with its O(n^2) behavior), so I wouldn't waste any time trying to avoid it.

I've done all of what I suggested to autoloads.el; the patch is here
(although as yet without a ChangeLog entry).  Additionally, I have
introduced a new file local variable (`autoload-format-revision') to
facilitate the conversion of an "old" loaddefs.el file to a new.  This
update happens transparently to the hacker.

I've fixed what might be a bug in the future: in the old version, two
files with the same base name could not both have entries in
loaddefs.el.  In the new one, they can.  (There are 3 files called
"subdirs.el" in the no-autoloads list.).  This change makes the presence
of the "bare name" in a header (this one:

There's a slight awkwardness in this new version.  Two files
(ps-print.el and emulation/tpu-edt.el) are both sources and destinations
for autoloads.  They both get changed _after_ their entries are written
to loaddefs.el.  This makes no difference to the functionality of
loaddefs.el.

;;;### (autoloads (list-one-abbrev-table) "abbrevlist" "abbrevlist.el"
;;;;;;  (18494 62405))
;;; Generated autoloads from abbrevlist.el
                                          ^^^^^^^^^^^^
                                          ||||||||||||

), redundant.  I haven't changed this part of the format, though. 

The new version is quite a bit faster than the old for most uses.  It is
quite a bit slower (~2 seconds, factor 2) for the null case (where
nothing needs updating).  I haven't managed to work out why this is the
case.  It might not be too important.

Here are some comparitive timings, done under fair conditions, on my 1.2
GHz Athlon.  All were generated by the command % time make autoloads.  I
am puzzled as to why my new version is slower in the null case.  It is
considerably faster doing a complete build  (factor ~1.6) and doing a
substantial update (~100 files, same factor).  

1/- From scratch:
OLD                            NEW
real    1m11.570s              0m38.075s 
user    0m55.251s              0m22.167s 
sys     0m15.867s              0m15.438s 

2/- A null invocation (immediately after 1/-)   
real    0m2.039s               0m4.204s   <-----------
user    0m1.561s               0m3.393s 
sys     0m0.428s               0m0.575s 

3/- Second null invocation (immediately after 2/-)
real    0m2.297s               0m3.643s   <-----------
user    0m1.538s               0m2.968s 
sys     0m0.726s               0m0.663s 

4/- After updating .../emacs/lisp (108 .el files changed)
real    0m15.211s              0m9.114s 
user    0m10.094s              0m6.168s 
sys     0m4.981s               0m2.871s 


So, here's the patch (which is actually bigger than the source file).
Should I install it?



Index: autoload.el
===================================================================
RCS file: /cvsroot/emacs/emacs/lisp/emacs-lisp/autoload.el,v
retrieving revision 1.142
diff -c -r1.142 autoload.el
*** autoload.el	10 Jun 2008 09:02:38 -0000	1.142
--- autoload.el	1 Sep 2008 21:18:00 -0000
***************
*** 42,47 ****
--- 42,54 ----
  ;;;###autoload
  (put 'generated-autoload-file 'safe-local-variable 'stringp)
  
+ (defvar autoload-format-revision nil
+   "If this file-local variable is missing, we have an \"old-style\" file.
+ This is used exclusively in the local variables section of
+ loaddefs.el and similar files.  If it is absent from such a file,
+ the file's format needs to be updated by % make autoloads, etc.")
+ (put 'autoload-format-revision 'safe-local-variable 'numberp)
+ 
  ;; 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"
***************
*** 69,74 ****
--- 76,387 ----
  
  (defvar autoload-modified-buffers)      ;Dynamically scoped var.
  
+ ;; 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.
+ (defvar no-update-autoloads nil
+   "File local variable to prevent scanning this file for autoload cookies.")
+ 
+ (defun autoload-file-load-name (file)
+   (let ((name (file-name-nondirectory file)))
+     (if (string-match "\\.elc?\\(\\.\\|\\'\\)" name)
+         (substring name 0 (match-beginning 0))
+       name)))
+ 
+ (defun autoload-file-name< (f1 f2)
+   "Is file name F1 \"less than\" file name F2?
+ 
+ F1 and F2 are file names, either both absolute or both relative
+ to the same place.
+ 
+ Comparison is done by string<, primarily on the \"bare name\",
+ and secondarily on the directory."
+   (let ((bare1 (file-name-sans-extension (file-name-nondirectory f1)))
+ 	(bare2 (file-name-sans-extension (file-name-nondirectory f2))))
+     (or (string< bare1 bare2)
+ 	(and (string= bare1 bare2)
+ 	     (string< (or (file-name-directory f1) "")
+ 		      (or (file-name-directory f2) ""))))))
+ 
+ \f
+ (defun autoload-read-section-header ()
+   "Read the section header, if any, at the current position.
+ Point should be at the ^L at the start of a section.
+ Return the header, or nil."
+ ;; 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-excursion
+     (save-match-data
+       (when (looking-at generate-autoload-section-header)
+ 	(goto-char (match-end 0))
+ 	(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))))))))
+ 
+ (defun autoload-rubric (file &optional type)
+   "Return a string giving the appropriate autoload rubric for FILE.
+ TYPE (default \"autoloads\") is a string stating the type of
+ information contained in FILE."
+   (let ((basename (file-name-nondirectory file)))
+     (concat ";;; " basename
+ 	    " --- automatically extracted " (or type "autoloads") "\n"
+ 	    ";;\n"
+ 	    ";;; Code:\n\n"
+ 	    "\f\n"
+ 	    "(provide '" (file-name-sans-extension basename) ")\n"
+ 	    ";; Local Variables:\n"
+ 	    ";; version-control: never\n"
+ 	    ";; no-byte-compile: t\n"
+ 	    ";; no-update-autoloads: t\n"
+ 	    ";; coding: utf-8\n"
+ 	    ";; autoload-format-revision: 1\n" ; New, 2008-08.
+ 	    ";; End:\n"
+ 	    ";;; " basename
+ 	    " ends here\n")))
+ 
+ (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 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))))))
+ 
+ (defun autoload-forward-section (outbuf)
+   "Point in OUTBUF is at the ^L of a section.  Move forward to the next.
+ Throw an error if there is no next section."
+   (with-current-buffer outbuf
+     (forward-char)
+     (unless (search-forward "\f" nil t)
+       (error "autoload-forward-section has failed"))
+     (backward-char)))
+ 
+ (defun autoload-delete-current-section (outbuf)
+   "Delete the current section from OUTBUF (e.g. loaddefs.el).
+ Point is at the ^L at the start of the section."
+   (with-current-buffer outbuf
+     (let ((begin (point)))
+       (search-forward generate-autoload-section-trailer)
+       (delete-region begin (point)))))
+ 
+ (defun autoload-delete-upto-and-get-section-header-for (name outbuf)
+ "Get the section header from OUTBUF for NAME, deleting others.
+ 
+ OUTBUF is the buffer for, e.g. loaddefs.el.
+ NAME is the full name for the pertinent source file.
+ 
+ Point must be at the ^L at the start of a section.
+ 
+ If there are any sections in OUTBUF for files alphabetically
+ earlier than FILE and these files no longer exist, delete the
+ sections from OUTBUF.
+ 
+ Return the header for NAME, or nil if it doesn't exist."
+   (with-current-buffer outbuf
+     (let* ((relname (file-relative-name name))
+ 	   (header (autoload-read-section-header))
+ 	   (header-relname (and header (nth 3 header))))
+       (while (and header-relname (autoload-file-name< header-relname relname))
+ 	(if (file-exists-p (nth 3 header))
+ 	    (autoload-forward-section outbuf)
+ 	  (autoload-delete-current-section outbuf))
+ 	(setq header (autoload-read-section-header)
+ 	      header-relname (nth 3 header)))
+       (and (string= relname header-relname)
+ 	   header))))
+ 
+ (defun autoload-ensure-unix-eols ()
+   "Set the buffer file coding system to use unix EOLs, if needed."
+   (unless (let ((eol-type (coding-system-eol-type buffer-file-coding-system)))
+ 	    (cond ((and (numberp eol-type) (zerop eol-type)))
+ 		  ((vectorp eol-type)
+ 		   (set-buffer-file-coding-system (aref eol-type 0)))
+ 		  (t (set-buffer-file-coding-system 'unix))))))
+ 
+ (defun autoload-insert-format-revision (outbuf)
+   "Insert a local definition of `autoload-format-revision' into OUTBUF.
+ This is needed only when an old style loaddefs.el is converted to
+ a new style one, and done only when there is an existing Local
+ Variables section."
+   (with-current-buffer outbuf
+     (save-excursion
+       (widen)
+       (goto-char (point-max))
+       (when (search-backward "\n;; End:" (- (point) 200) t)
+ 	(insert "\n;; autoload-format-revision: 1")))))
+ 
+ (defun autoload-init-out-buffer (out-file)
+   "Prepare a buffer \(e.g. loaddefs.el) for outputting autoload cookies.
+ OUT-FILE is the file name of this buffer.
+ 
+ The return value is the buffer.
+ 
+ \"Prepare\" means:
+   1. If OUT-FILE doesn't yet exist and there isn't a buffer for
+ it, create its buffer and initialize it with boilerplate;
+   2. move to the first file entry in the buffer (or where it
+ would be); this is at a ^L;
+   3. Make sure the buffer's coding system is set for UNIX eols."
+   (let ((outbuf
+ 	 (or (get-file-buffer out-file)
+ 	     (and (file-exists-p out-file)
+ 		  (find-file-noselect out-file))
+ 	     (find-file-noselect out-file))))
+ 
+     (with-current-buffer outbuf
+       (if (= (buffer-size outbuf) 0)
+ 	  (progn (or (file-writable-p out-file)
+ 		     (error "Autoloads file %s is not writable" out-file))
+ 		 (insert (autoload-rubric out-file))
+ 		 (set (make-local-variable 'autoload-format-revision) 1)
+ 		 (search-backward "\f"))
+ 	(goto-char 0)
+ 	(search-forward "\f")
+ 	(backward-char))
+       (autoload-ensure-unix-eols))
+       outbuf))
+ 
+ \f
+ (defun autoload-replace-or-write-no-list (no-autoloads outbuf this-time)
+   "Write the \"no autoloads\" section to the right place in OUTBUF.
+ This includes deleting any existing \"no autoloads\" section.
+ 
+ NO-AUTOLOADS is the \(already sorted) list of files without autoloads.
+ OUTBUF is the buffer for the main autoload file, usually loaddefs.el.
+ THIS-TIME is the time the current updating operation started."
+   (with-current-buffer outbuf
+     (widen)
+     (goto-char (point-max))
+     (or (search-backward "\f" nil t)
+ 	(error
+ 	 "autoload-replace-or-write-no-list: uninitialized buffer %s" outbuf))
+     (let (header)
+       (save-excursion
+ 	(if (and (search-backward "\f" nil t)
+ 	       (looking-at generate-autoload-section-header)
+ 	       (setq header (autoload-read-section-header))
+ 	       (null (nth 1 header))
+ 	       (null (nth 2 header))
+ 	       (listp (nth 3 header)))
+ 	  (autoload-delete-current-section outbuf))))
+ 
+     (autoload-insert-section-header
+      (current-buffer) nil nil no-autoloads this-time)
+     (insert generate-autoload-section-trailer)))
+ 
+ (defun autoload-extract-no-list (outbuf)
+   "Get the list of files without autoloads from OUTBUF.
+ Return nil if there is no such list.
+ OUTBUF is a buffer in loaddefs.el format."
+   (with-current-buffer outbuf
+     (save-excursion
+       (widen)
+       (goto-char (point-max))
+       (and (search-backward "\f" nil t 2)
+ 	   (looking-at generate-autoload-section-header)
+ 	   (let ((header (autoload-read-section-header)))
+ 	     (and (listp (nth 3 header))
+ 		  (nth 3 header)))))))
+ 
+ (defun autoload-get-no-list-timestamp (outbuf)
+   "Get the timestamp of the no-autoloads list in OUTBUF.
+ Return nil is there isn't a no-autoloads list"
+   (with-current-buffer outbuf
+     (save-excursion
+       (widen)
+       (goto-char (point-max))
+       (and (search-backward "\f" nil t 2)
+ 	   (looking-at generate-autoload-section-header)
+ 	   (let ((header (autoload-read-section-header)))
+ 	     (nth 4 header))))))
+ 
+ ;; The next two are defined in CL, not the Emacs core.
+ (defmacro caddr (arg) `(car (cdr (cdr ,arg))))
+ (defmacro cdddr (arg) `(cdr (cdr (cdr ,arg))))
+ 
+ (defun autoload-pull-from-no-list-upto (name no-list)
+   "Step over or delete entries from NO-LIST while they're <= NAME.
+ An entry is deleted (by setcdr'ing (cdr NO-LIST)) if the file it
+ points at no longer exists.  Otherwise it is stepped over (by
+ changing PTR).
+ 
+ The current directory must be that of the output file \(usually
+ loaddefs.el) from which NO-LIST was read.
+ 
+ Return NAME, if it was found, or nil.
+ 
+ NAME is a filename, possibly with directory bits.
+ 
+ NO-LIST is a list whose cdr is the first element of the list of
+ pertinent filename entries."
+   (let* ((targ-relname (file-relative-name name))
+ 	 relname)
+     (while (and (setq relname (caddr no-list))
+ 		(autoload-file-name< relname targ-relname))
+       (if (file-exists-p relname)
+ 	  (setcdr no-list (cddr no-list))	; step over
+ 	(setcdr (cdr no-list) (cdddr no-list)))) ; delete element from list
+ 
+     (and relname
+ 	 (string= relname targ-relname)
+ 	 name)))
+ 
+ (defun autoload-remove-cur-no-list-entry (no-list)
+   "Remove the current entry from the NO-LIST list.
+ NO-LIST is a list whose caddr is the current entry.  The return
+ value is undefined."
+   (setcdr (cdr no-list) (cdddr no-list)))
+ 
+ (defun autoload-step-over-cur-no-list-entry (no-list)
+   "Step over the current entry in the NO-LIST list.
+ NO-LIST is a list whose caddr is the current entry.  The return
+ value is undefined."
+   (setcdr no-list (cddr no-list)))
+ 
+ (defun autoload-insert-no-list-entry (file no-list def-out-buffer)
+   "Insert an entry for FILE into the NO-LIST list, and step past it.
+ FILE is an absolute file name.
+ NO-LIST is a list whose caddr is the current entry.
+ DEF-OUT-BUFFER is the default output buffer, usually
+ \"loaddefs.el\"; we use this to get a relative filename for FILE.
+ The return value is undefined."
+   (let ((relname
+ 	 (file-relative-name
+ 	  file
+ 	  (file-name-directory (buffer-file-name def-out-buffer)))))
+     (setcdr (cdr no-list) (cons relname (cddr no-list))) ; insert new entry.
+     (setcdr no-list (cddr no-list))))			 ; step over it.
+ 
+ (defun autoload-sort-no-autoload-list (no-list)
+   "Sort the no autoload list NO-LIST into the canonical order.
+ This is needed only when converting an older format file to the
+ new format."
+   (setcdr no-list (sort (cdr no-list) 'autoload-file-name<)))
+ 
+ \f
  (defun make-autoload (form file)
    "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
***************
*** 148,203 ****
       ;; nil here indicates that this is not a special autoload form.
       (t nil))))
  
! ;; 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.
! 
! (defun autoload-generated-file ()
!   (expand-file-name generated-autoload-file
!                     ;; File-local settings of generated-autoload-file should
!                     ;; be interpreted relative to the file's location,
!                     ;; of course.
!                     (if (not (local-variable-p 'generated-autoload-file))
!                         (expand-file-name "lisp" source-directory))))
! 
! 
! (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))))))
! 
! (defvar autoload-print-form-outbuf nil
!   "Buffer which gets the output of `autoload-print-form'.")
! 
! (defun autoload-print-form (form)
    "Print FORM such that `make-docfile' will find the docstrings.
! The variable `autoload-print-form-outbuf' specifies the buffer to
! put the output in."
    (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
!     (let ((doc-string-elt (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
  	  ;; doc-string must be printed specially for
--- 461,477 ----
       ;; nil here indicates that this is not a special autoload form.
       (t nil))))
  
! (defun autoload-print-form (form outbuf)
    "Print FORM such that `make-docfile' will find the docstrings.
! OUTBUF specifies the buffer to put the output in."
    (cond
     ;; If the form is a sequence, recurse.
!    ((eq (car form) 'progn)
!     (mapcar (lambda (f) (autoload-print-form f outbuf)) (cdr form)))
     ;; Symbols at the toplevel are meaningless.
     ((symbolp form) nil)
     (t
!     (let ((doc-string-elt (get (car-safe form) 'doc-string-elt)))
        (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
***************
*** 235,283 ****
  	      (print-escape-nonascii t))
  	  (print form outbuf)))))))
  
! (defun autoload-rubric (file &optional type)
!   "Return a string giving the appropriate autoload rubric for FILE.
! TYPE (default \"autoloads\") is a string stating the type of
! information contained in FILE."
!   (let ((basename (file-name-nondirectory file)))
!     (concat ";;; " basename
! 	    " --- automatically extracted " (or type "autoloads") "\n"
! 	    ";;\n"
! 	    ";;; Code:\n\n"
! 	    "\f\n"
! 	    "(provide '" (file-name-sans-extension basename) ")\n"
! 	    ";; Local Variables:\n"
! 	    ";; version-control: never\n"
! 	    ";; no-byte-compile: t\n"
! 	    ";; no-update-autoloads: t\n"
! 	    ";; coding: utf-8\n"
! 	    ";; End:\n"
! 	    ";;; " basename
! 	    " ends here\n")))
  
! (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 (autoload-rubric file) 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 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))))))
  
  (defun autoload-find-file (file)
    "Fetch file and put it in a temp buffer.  Return the buffer."
--- 509,653 ----
  	      (print-escape-nonascii t))
  	  (print form outbuf)))))))
  
! (defun autoload-extract-cookie (outbuf file)
!   "Extract the autoload cookie at point, and write it into OUTBUF.
! Point is just after the autoload cookie \(usually \";;;###autoload\")
! on entry, and is left just after the autoload form on exit.
! 
! OUTBUF is the buffer (typically, loaddefs.el) into which we
! insert the new cookie at the buffer's current position.
! 
! FILE is the absolute name of the elisp source file containing the
! cookie.
! 
! Return the name defined (a symbol), or nil if there wasn't one."
!   (let ((load-name (autoload-file-load-name file))
! 	cookie-name)
!     (skip-chars-forward " \t")
!     (if (eolp)
! 	(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
! 		  (setq cookie-name (nth 1 form))
! 		(setq autoload form))
! 	      (autoload-print-form autoload outbuf))
! 	  (error
! 	   (message "Error in %s: %S" file err)))
! 
!       ;; 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))
!     cookie-name))
! 
! (defun autoload-parse-source-buffer (inbuf file)
!   "Extract the autoload specs, if any, from the buffer INBUF,
! writing them to the buffer whose file name is in the the variable
! `generated-autoload-file' \(which may have a file-local binding in
! INBUF).  The buffer isn't saved in this function.
! 
! FILE is the absolute name of the elisp source file loaded in the
! current buffer.
! 
! The current directory is the \"starting directory\" for the
! autoload generation, typically .../emacs/lisp/.
! 
! If the buffer for `generated-autoload-file' is actually written
! to, return this buffer.  Otherwise, return nil."
!   (let (autoloads-done	  ; list of autoload SYMBOLS written to output buffer.
! 	(load-name (autoload-file-load-name file))
! 	(relfile (file-relative-name file)) ; relative to starting directory.
! 	relfile-out	       ; ditto, relative to `generated-autoload-file'.
! 	cookie
! 	(cookie-regexp (concat "^" (regexp-quote generate-autoload-cookie)))
! 	outbuf
! 	output-started	   ; Position in outbuf of first cookie generated from
! 	                   ; current input file, or nil.
! 	(print-length nil)	 ; don't truncate a list when printing.
! 	(print-level nil)	 ; don't limit nesting of lists when printing.
! 	;; (print-readably t)  ; This does something in XEmacs.  Removed 2008-08.
! 	(float-output-format nil))  ; Use a default format for printing floats.
! 
!     (with-current-buffer inbuf
!       (unless no-update-autoloads
! 	(message "Generating autoloads for %s..." relfile)
! 	(save-excursion
! 	  (save-restriction
! 	    (widen)
! 	    (goto-char (point-min))
! 	    (while (search-forward-regexp cookie-regexp nil t)
! 	      ;; Get the output buffer only when the first cookie has been found.
! 	      (unless outbuf
! 		(setq outbuf
! 		      (or (get-file-buffer generated-autoload-file)
! 			  (autoload-init-out-buffer generated-autoload-file))
! 		      output-started (with-current-buffer outbuf (point)))
! 		;; Update OUTBUF to new format, if needed.
! 		(unless (local-variable-p 'autoload-format-revision outbuf)
! 		  (autoload-insert-format-revision outbuf)
! 		  (set (make-local-variable 'autoload-format-revision) 1)))
! 	      (if (setq cookie (autoload-extract-cookie outbuf file))
! 		  (push cookie autoloads-done)))
! 
! 	    ;; Write the section header for FILE into OUTBUF.  This contains a
! 	    ;; summary list of functions at the beginning of the section in
! 	    ;; the output file.
! 	    (when outbuf
! 	      (let ((date-or-checksum
! 		     (if (local-variable-p 'generated-autoload-file)
! 			 ;; MD5 checksums are much better because they do not
! 			 ;; change unless the file changes (so they'll be equal
! 			 ;; on two different systems and will change less often
! 			 ;; than time-stamps, thus leading to fewer unneeded
! 			 ;; changes causing spurious conflicts), but using
! 			 ;; time-stamps is a very useful optimization, so we
! 			 ;; use time-stamps for the main autoloads file
! 			 ;; (loaddefs.el) where we have special ways to
! 			 ;; circumvent the "random change problem", and MD5
! 			 ;; checksum in secondary autoload files where we do
! 			 ;; not need the time-stamp optimization because it is
! 			 ;; already provided by the primary autoloads file.
! 			 (md5 (current-buffer) nil nil 'emacs-mule-unix)
! 		       (nth 5 (file-attributes file))))) ; timestamp.
! 		(with-current-buffer outbuf
! 		  (setq relfile-out (file-relative-name file))
! 		  (save-excursion
! 		    ;; Insert the section-header line which lists the file name
! 		    ;; and which functions are in it, etc.
! 		    (goto-char output-started)
! 		    (autoload-insert-section-header
! 		     outbuf autoloads-done load-name relfile-out
! 		     date-or-checksum)
! 		    (insert ";;; Generated autoloads from " relfile-out "\n"))
! 		  (insert generate-autoload-section-trailer))))))
! 	(message "Generating autoloads for %s...done" relfile)
! 	outbuf))))
  
! \f
! (defun autoload-touched-p (file timestamp-md5 &optional buffer)
!   "Has FILE a later timestamp than or different md5 sum from TIMESTAMP-MD5?
  
! FILE is the absolute name of the file.
! TIMESTAMP-MD5 is the timestamp or MD5 sum of the file in its
! exisiting entry, or NIL.
! BUFFER, if supplied, is FILE's buffer.  It is ignored for a
! timestamp and must be supplied for an MD5 sum."
!   (cond
!    ((consp timestamp-md5)
!     (time-less-p timestamp-md5 (nth 5 (file-attributes file))))
!    ((stringp timestamp-md5)
!     (not (string-equal timestamp-md5 (md5 buffer nil nil 'emacs-mule-unix))))
!    (t t)))				; null timestamp-md5, file is new.
  
  (defun autoload-find-file (file)
    "Fetch file and put it in a temp buffer.  Return the buffer."
***************
*** 295,466 ****
        (hack-local-variables))
      (current-buffer)))
  
! (defvar no-update-autoloads nil
!   "File local variable to prevent scanning this file for autoload cookies.")
! 
! (defun autoload-file-load-name (file)
!   (let ((name (file-name-nondirectory file)))
!     (if (string-match "\\.elc?\\(\\.\\|\\'\\)" name)
!         (substring name 0 (match-beginning 0))
!       name)))
  
! (defun generate-file-autoloads (file)
!   "Insert at point a loaddefs autoload section for FILE.
! Autoloads are generated for defuns and defmacros in FILE
! marked by `generate-autoload-cookie' (which see).
! 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)))
! 
! ;; When called from `generate-file-autoloads' we should ignore
! ;; `generated-autoload-file' altogether.  When called from
! ;; `update-file-autoloads' we don't know `outbuf'.  And when called from
! ;; `update-directory-autoloads' it's in between: we know the default
! ;; `outbuf' but we should obey any file-local setting of
! ;; `generated-autoload-file'.
! (defun autoload-generate-file-autoloads (file &optional outbuf outfile)
!   "Insert an autoload section for FILE in the appropriate buffer.
! Autoloads are generated for defuns and defmacros in FILE
! marked by `generate-autoload-cookie' (which see).
! If FILE is being visited in a buffer, the contents of the buffer are used.
! OUTBUF is the buffer in which the autoload statements should be inserted.
! If OUTBUF is nil, it will be determined by `autoload-generated-file'.
! 
! If provided, OUTFILE is expected to be the file name of OUTBUF.
! If OUTFILE is non-nil and FILE specifies a `generated-autoload-file'
! different from OUTFILE, then OUTBUF is ignored.
! 
! Return non-nil if and only if FILE adds no autoloads to OUTFILE
! \(or OUTBUF if OUTFILE is nil)."
!   (catch 'done
!     (let ((autoloads-done '())
!           (load-name (autoload-file-load-name file))
!           (print-length nil)
! 	  (print-level nil)
!           (print-readably t)           ; This does something in Lucid Emacs.
!           (float-output-format nil)
!           (visited (get-file-buffer file))
!           (otherbuf nil)
!           (absfile (expand-file-name file))
!           relfile
!           ;; nil until we found a cookie.
!           output-start)
! 
!       (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)
!           (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))
!                   ;; If not done yet, figure out where to insert this text.
!                   (unless output-start
!                     (when (and outfile
!                                (not (equal outfile (autoload-generated-file))))
!                       ;; A file-local setting of autoload-generated-file says
!                       ;; we should ignore OUTBUF.
!                       (setq outbuf nil)
!                       (setq otherbuf t))
!                     (unless outbuf
!                       (setq outbuf (autoload-find-destination absfile))
!                       (unless outbuf
!                         ;; The file has autoload cookies, but they're
!                         ;; already up-to-date.  If OUTFILE is nil, the
!                         ;; entries are in the expected OUTBUF, otherwise
!                         ;; they're elsewhere.
!                         (throw 'done outfile)))
!                     (with-current-buffer outbuf
!                       (setq relfile (file-relative-name absfile))
!                       (setq output-start (point)))
!                     ;; (message "file=%S, relfile=%S, dest=%S"
!                     ;;          file relfile (autoload-generated-file))
!                     )
!                   (search-forward generate-autoload-cookie)
!                   (skip-chars-forward " \t")
!                   (if (eolp)
!                       (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)))
! 
!                     ;; 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 output-start
!             (let ((secondary-autoloads-file-buf
!                    (if (local-variable-p 'generated-autoload-file)
!                        (current-buffer))))
!               (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 relfile
!                    (if secondary-autoloads-file-buf
!                        ;; MD5 checksums are much better because they do not
!                        ;; change unless the file changes (so they'll be
!                        ;; equal on two different systems and will change
!                        ;; less often than time-stamps, thus leading to fewer
!                        ;; unneeded changes causing spurious conflicts), but
!                        ;; using time-stamps is a very useful optimization,
!                        ;; so we use time-stamps for the main autoloads file
!                        ;; (loaddefs.el) where we have special ways to
!                        ;; circumvent the "random change problem", and MD5
!                        ;; checksum in secondary autoload files where we do
!                        ;; not need the time-stamp optimization because it is
!                        ;; already provided by the primary autoloads file.
!                        (md5 secondary-autoloads-file-buf
!                             ;; We'd really want to just use
!                             ;; `emacs-internal' instead.
!                             nil nil 'emacs-mule-unix)
!                      (nth 5 (file-attributes relfile))))
!                   (insert ";;; Generated autoloads from " relfile "\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))))
!       ;; If the entries were added to some other buffer, then the file
!       ;; doesn't add entries to OUTFILE.
!       (or (not output-start) otherbuf))))
! \f
! (defun autoload-save-buffers ()
!   (while autoload-modified-buffers
!     (with-current-buffer (pop autoload-modified-buffers)
!       (save-buffer))))
  
  ;;;###autoload
  (defun update-file-autoloads (file &optional save-after)
--- 665,767 ----
        (hack-local-variables))
      (current-buffer)))
  
! (defun autoload-maybe-parse-foreign-file (file inbuf)
!   "Extract the autoloads from FILE, should FILE have changed,
! writing them into the buffer for `generated-autoload-file' \(a
! filename), WHICH HAS A BUFFER LOCAL VALUE in FILE.
! 
! If the `generated-autoload-file' buffer is written to, return it.
! Otherwise return nil.
! 
! FILE is the absolute file name of the elisp source file, and its
! buffer is INBUF."
!   (with-current-buffer inbuf	       ; pick up buffer local `generated-...'.
!     (let* ((outbuf (or (get-file-buffer generated-autoload-file)
! 		       (autoload-init-out-buffer generated-autoload-file)))
! 	   (header
! 	    (autoload-delete-upto-and-get-section-header-for file outbuf)))
!       (cond
!        ((and header (autoload-touched-p file (nth 4 header) inbuf)) ; md5 check
! 	(autoload-delete-current-section outbuf)
! 	(autoload-parse-source-buffer inbuf file))
!        ((null header)
! 	(autoload-parse-source-buffer inbuf file))
!        (t nil)))))
! 
! (defun autoload-maybe-parse-source-file (file no-list n-a-timestamp
! 					      def-out-buffer)
!   "Extract the autoloads from FILE, should FILE have changed,
! writing them into the buffer for `generated-autoload-file' \(a
! filename), which may have a buffer local value in FILE.  If there
! were any autoloads, the output buffer, otherwise return nil.
! 
! FILE is the absolute filename of an elisp source file.
! 
! NO-LIST is a list whose cddr is the current element in a list of
! source files which, up till now, have contained no autoloads.
! This function may pop elements from this inner list by setcdr its
! structure and may step \(cdr no-list) to the next element.
! 
! N-A-TIMESTAMP is the timestamp (NEVER an MD5 sum) of the existing
! no-autoloads list, or NIL.
! 
! DEF-OUT-BUFFER is the default output buffer, usually \"loaddefs.el\".
! 
! At the time of call, the current buffer is undefined.
! 
! An essential side effect of this function is to remove entries
! from the alphabetically ordered \"loaddefs.el\" and NO-LIST for
! files which no longer exist, and to insert an entry into NO-LIST
! for FILE if it lacks autoloads."
!   (let (inbuf visited  file-header outbuf)
!     (cond
!      ;; Case 1.  FILE is in the "no autoload" list.  All files with their own
!      ;; `generated-autoload-file' are also on this list.
!      ((with-current-buffer def-out-buffer
! 	(autoload-pull-from-no-list-upto file no-list))
!       (when (autoload-touched-p file n-a-timestamp)
! 	(setq visited (get-file-buffer file) ; is FILE already loaded?
! 	      inbuf (or visited (autoload-find-file file)))
! 	(if (local-variable-p 'generated-autoload-file inbuf)
! 	    (setq outbuf (autoload-maybe-parse-foreign-file file inbuf))
! 	  (if (setq outbuf (autoload-parse-source-buffer inbuf file))
! 	      (autoload-remove-cur-no-list-entry no-list)
! 	    (autoload-step-over-cur-no-list-entry no-list)))))
! 
!      ;; Case 2.  FILE has a section in the main loaddefs.el file, and hasn't
!      ;; been changed since.
!      ((and
!        (setq file-header
! 	     (autoload-delete-upto-and-get-section-header-for
! 	      file def-out-buffer))
!        (not (autoload-touched-p file (nth 4 file-header))))
!       (autoload-forward-section def-out-buffer))
! 
!      ;; Case 3.  File has a section in the main loaddefs.el buffer yet has
!      ;; been changed.
!      ;; We must load the file, so as to check `generated-autoload-file', which
!      ;; may have changed.  We could probably optimise this away, if we were
!      ;; willing to rely only on a timestamp.
!      ((setq file-header (autoload-delete-upto-and-get-section-header-for
! 			 file def-out-buffer))
!       (setq visited (get-file-buffer file) ; is FILE already loaded?
! 	    inbuf (or visited (autoload-find-file file)))
!       (autoload-delete-current-section def-out-buffer)
!       (if (local-variable-p 'generated-autoload-file inbuf)
! 	  (setq outbuf (autoload-maybe-parse-foreign-file file inbuf))
! 	(setq outbuf (autoload-parse-source-buffer inbuf file))))
! 
!      ;; Case 4.  FILE is new.  Scan it.
!      (t
!       (setq visited (get-file-buffer file) ; is FILE already loaded?
! 	    inbuf (or visited (autoload-find-file file)))
!       (with-current-buffer inbuf
! 	(unless (and (setq outbuf (autoload-parse-source-buffer inbuf file))
! 		     (not (local-variable-p 'generated-autoload-file inbuf)))
! 	  (autoload-insert-no-list-entry file no-list def-out-buffer)))))
  
!     (if (and inbuf (not visited)) (kill-buffer inbuf))
!     outbuf))
  
  ;;;###autoload
  (defun update-file-autoloads (file &optional save-after)
***************
*** 471,561 ****
  
  Return FILE if there was no autoload cookie in it, else nil."
    (interactive "fUpdate autoloads for file: \np")
!   (let* ((autoload-modified-buffers nil)
!          (no-autoloads (autoload-generate-file-autoloads file)))
!     (if autoload-modified-buffers
!         (if save-after (autoload-save-buffers))
        (if (interactive-p)
!           (message "Autoload section for %s is up to date." file)))
!     (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.
! Returns nil if the file's autoloads are uptodate, otherwise
! removes any prior now out-of-date autoload entries."
!   (catch 'up-to-date
!     (let* ((load-name (autoload-file-load-name file))
!            (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.
!           (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.
!         (unless (zerop (coding-system-eol-type buffer-file-coding-system))
!           (set-buffer-file-coding-system 'unix))
!         (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))
!         (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))
!                          (file-time (nth 5 (file-attributes file))))
!                      (if (and (or (null existing-buffer)
!                                   (not (buffer-modified-p existing-buffer)))
!                               (or
!                                ;; last-time is the time-stamp (specifying
!                                ;; the last time we looked at the file) and
!                                ;; the file hasn't been changed since.
!                                (and (listp last-time) (= (length last-time) 2)
!                                     (not (time-less-p last-time file-time)))
!                                ;; last-time is an MD5 checksum instead.
!                                (and (stringp last-time)
!                                     (equal last-time
!                                            (md5 buf nil nil 'emacs-mule)))))
!                          (throw 'up-to-date nil)
!                        (autoload-remove-section begin)
!                        (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)))
!         (unless (memq (current-buffer) autoload-modified-buffers)
!           (push (current-buffer) autoload-modified-buffers))
!         (current-buffer)))))
! 
! (defun autoload-remove-section (begin)
!   (goto-char begin)
!   (search-forward generate-autoload-section-trailer)
!   (delete-region begin (point)))
  
  ;;;###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
  directories as separate arguments, but this usage is discouraged.
--- 772,831 ----
  
  Return FILE if there was no autoload cookie in it, else nil."
    (interactive "fUpdate autoloads for file: \np")
!   (let* ((generated-autoload-file (expand-file-name generated-autoload-file))
! 	 ;; The above is needed to "anchor" g-a-f to the current directory;
! 	 ;; otherwise it might be expanded relative to FILE's directory.
! 	 (ffile (expand-file-name file))
! 	 (def-out-buffer (autoload-init-out-buffer generated-autoload-file))
! 	 (old-no-autoloads (autoload-extract-no-list def-out-buffer))
! 	 (no-autoloads (cons "dummy" (copy-tree old-no-autoloads))) ; gets modified
! 	 (cur-no-autoloads (cons nil no-autoloads)) ; "cur ptr" into no-autoloads.
! 	 (no-autoload-timestamp (autoload-get-no-list-timestamp def-out-buffer))
! 	 autoload-modified-buffer)
! 
!     ;; Upgrade an old loaddefs.el to the new format.
!     (unless (local-variable-p 'autoload-format-revision def-out-buffer)
!       (autoload-insert-format-revision def-out-buffer)
!       (set (make-local-variable 'autoload-format-revision) 1)
!       (autoload-sort-no-autoload-list no-autoloads))
! 
!     (setq autoload-modified-buffer
! 	  (autoload-maybe-parse-source-file
! 	   ffile
! 	   cur-no-autoloads
! 	   no-autoload-timestamp
! 	   def-out-buffer))
! 
!     (if autoload-modified-buffer
! 	(if save-after
! 	    (with-current-buffer autoload-modified-buffer (save-buffer)))
        (if (interactive-p)
! 	  (message "Autoload section for %s is up to date." file))
!       file)))
  
! (defun autoload-init-file-list (dirs)
!   "From DIRS, a list of directories, extract all pertinent files.
! 
! Return them as absolute filenames, sorted by alphabetic ordering
! of their basenames without extension, as a list."
!   (let* ((files-re (let ((tmp nil))
! 		     (dolist (suf (get-load-suffixes)
! 				  (concat "^[^=.].*" (regexp-opt tmp t) "\\'"))
! 		       (unless (string-match "\\.elc" suf) (push suf tmp)))))
! 	 (files
! 	  (sort
! 	   (apply 'nconc
! 		  (mapcar (lambda (dir)
! 			    (directory-files (expand-file-name dir)
! 					     t files-re t)) ; _unsorted_ list.
! 			  dirs))
! 	   'autoload-file-name<)))
!     files))
  
  ;;;###autoload
  (defun update-directory-autoloads (&rest dirs)
    "\
  Update loaddefs.el with all the current autoloads from DIRS, and no old ones.
  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.
***************
*** 563,646 ****
  The function does NOT recursively descend into subdirectories of the
  directory or directories specified."
    (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)))))
! 	 (files (apply 'nconc
! 		       (mapcar (lambda (dir)
! 				 (directory-files (expand-file-name dir)
! 						  t files-re))
! 			       dirs)))
!          (done ())
! 	 (this-time (current-time))
!          ;; 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))
! 
!     (with-current-buffer
! 	(find-file-noselect
!          (autoload-ensure-default-file (autoload-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)))
! 
! 	(goto-char (point-min))
! 	(while (search-forward generate-autoload-section-header nil t)
! 	  (let* ((form (autoload-read-section-header))
! 		 (file (nth 3 form)))
! 	    (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
! 				    (not (time-less-p last-time file-time)))
! 			   ;; file unchanged
! 			   (push file no-autoloads)
! 			   (setq files (delete file files)))))))
! 		  ((not (stringp file)))
! 		  ((or (not (file-exists-p file))
!                        ;; Remove duplicates as well, just in case.
!                        (member file done))
!                    ;; Remove the obsolete section.
! 		   (autoload-remove-section (match-beginning 0)))
! 		  ((not (time-less-p (nth 4 form)
!                                      (nth 5 (file-attributes file))))
! 		   ;; File hasn't changed.
! 		   nil)
! 		  (t
!                    (autoload-remove-section (match-beginning 0))
!                    (if (autoload-generate-file-autoloads
!                         file (current-buffer) buffer-file-name)
!                        (push file no-autoloads))))
!             (push file done)
! 	    (setq files (delete file files)))))
!       ;; Elements remaining in FILES have no existing autoload sections yet.
!       (dolist (file files)
!         (if (autoload-generate-file-autoloads file nil buffer-file-name)
!             (push file no-autoloads)))
! 
!       (when no-autoloads
! 	;; Sort them for better readability.
! 	(setq no-autoloads (sort no-autoloads 'string<))
! 	;; 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))
! 
!       (save-buffer)
!       ;; In case autoload entries were added to other files because of
!       ;; file-local autoload-generated-file settings.
!       (autoload-save-buffers))))
  
  (define-obsolete-function-alias 'update-autoloads-from-directories
      'update-directory-autoloads "22.1")
--- 833,881 ----
  The function does NOT recursively descend into subdirectories of the
  directory or directories specified."
    (interactive "DUpdate autoloads from directory: ")
!   (let* ((files (autoload-init-file-list dirs)) ; absolute file names.
! 	 (generated-autoload-file (expand-file-name generated-autoload-file))
! 	 (def-out-buffer (autoload-init-out-buffer generated-autoload-file))
! 	 (def-gen-dir (file-name-directory generated-autoload-file))
! 
! 	 (old-no-autoloads (autoload-extract-no-list def-out-buffer))
! 	 (no-autoloads (cons "dummy" (copy-tree old-no-autoloads))) ; gets modified
! 	 (cur-no-autoloads (cons nil no-autoloads)) ; "cur ptr" into no-autoloads.
! 	 (no-autoload-timestamp (autoload-get-no-list-timestamp def-out-buffer))
! 
! 	 file
! 	 autoload-buffer
! 	 autoload-modified-buffers
! 	 (this-time (current-time)))
! 
!     ;; Upgrade an old loaddefs.el to the new format.
!     (unless (local-variable-p 'autoload-format-revision def-out-buffer)
!       (autoload-insert-format-revision def-out-buffer)
!       (set (make-local-variable 'autoload-format-revision) 1)
!       (autoload-sort-no-autoload-list no-autoloads))
! 
!     (while files
!       (setq file (car files)  files (cdr files))
!       (setq autoload-buffer
! 	    (autoload-maybe-parse-source-file
! 	     file
! 	     cur-no-autoloads		; cur-no-autoloads gets setcdr'd.
! 	     no-autoload-timestamp
! 	     def-out-buffer))
!       (when autoload-buffer
! 	  (or (memq autoload-buffer autoload-modified-buffers)
! 	      (push autoload-buffer autoload-modified-buffers))))
! 
!     ;; Has the no-autoloads list changed?
!     (unless (equal old-no-autoloads (cdr no-autoloads))
!       (autoload-replace-or-write-no-list (cdr no-autoloads)
! 					 def-out-buffer this-time)
!       (or (memq def-out-buffer autoload-modified-buffers)
! 	  (push def-out-buffer autoload-modified-buffers)))
! 
!     (while autoload-modified-buffers
!       (with-current-buffer (pop autoload-modified-buffers)
! 	(save-buffer)))))
  
  (define-obsolete-function-alias 'update-autoloads-from-directories
      'update-directory-autoloads "22.1")

>         Stefan

-- 
Alan Mackenzie (Nuremberg, Germany).




^ permalink raw reply	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2008-09-01 21:55 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-08-12 16:23 Refactoring of emacs-lisp/autoload.el Alan Mackenzie
2008-08-12 20:09 ` Stefan Monnier
2008-08-12 20:58   ` Glenn Morris
2008-08-13 14:13     ` Alan Mackenzie
2008-08-13 16:11       ` Glenn Morris
2008-08-13 14:11   ` Alan Mackenzie
2008-08-13 20:31     ` Stefan Monnier
2008-08-14 13:17       ` Alan Mackenzie
2008-08-14 17:52         ` Stefan Monnier
2008-09-01 21:55           ` Alan Mackenzie

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).