From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Alan Mackenzie Newsgroups: gmane.emacs.devel Subject: Refactoring of emacs-lisp/autoload.el Date: Tue, 12 Aug 2008 16:23:33 +0000 Message-ID: <20080812162333.GA7999@muc.de> NNTP-Posting-Host: lo.gmane.org Mime-Version: 1.0 Content-Type: text/plain; charset=us-ascii X-Trace: ger.gmane.org 1218558263 11240 80.91.229.12 (12 Aug 2008 16:24:23 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Tue, 12 Aug 2008 16:24:23 +0000 (UTC) Cc: Glenn Morris , Chong Yidong , Stefan Monnier To: emacs-devel@gnu.org Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Tue Aug 12 18:25:13 2008 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([199.232.76.165]) by lo.gmane.org with esmtp (Exim 4.50) id 1KSwg8-0005qY-Rh for ged-emacs-devel@m.gmane.org; Tue, 12 Aug 2008 18:25:02 +0200 Original-Received: from localhost ([127.0.0.1]:39498 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KSwfB-0000RQ-PS for ged-emacs-devel@m.gmane.org; Tue, 12 Aug 2008 12:24:01 -0400 Original-Received: from mailman by lists.gnu.org with tmda-scanned (Exim 4.43) id 1KSweT-000853-A0 for emacs-devel@gnu.org; Tue, 12 Aug 2008 12:23:17 -0400 Original-Received: from exim by lists.gnu.org with spam-scanned (Exim 4.43) id 1KSweR-00082h-DC for emacs-devel@gnu.org; Tue, 12 Aug 2008 12:23:16 -0400 Original-Received: from [199.232.76.173] (port=54488 helo=monty-python.gnu.org) by lists.gnu.org with esmtp (Exim 4.43) id 1KSweR-00082R-5v for emacs-devel@gnu.org; Tue, 12 Aug 2008 12:23:15 -0400 Original-Received: from colin.muc.de ([193.149.48.1]:2980 helo=mail.muc.de) by monty-python.gnu.org with esmtps (TLS-1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.60) (envelope-from ) id 1KSweP-0000yX-TP for emacs-devel@gnu.org; Tue, 12 Aug 2008 12:23:14 -0400 Original-Received: (qmail 51176 invoked by uid 3782); 12 Aug 2008 16:21:37 -0000 Original-Received: from acm.muc.de (pD9E52578.dip.t-dialin.net [217.229.37.120]) by colin2.muc.de (tmda-ofmipd) with ESMTP; Tue, 12 Aug 2008 18:21:33 +0200 Original-Received: (qmail 8772 invoked by uid 1000); 12 Aug 2008 16:23:34 -0000 Content-Disposition: inline User-Agent: Mutt/1.5.9i X-Delivery-Agent: TMDA/1.1.5 (Fettercairn) X-Primary-Address: acm@muc.de X-detected-kernel: by monty-python.gnu.org: FreeBSD 4.6-4.9 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:102348 Archived-At: 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 * 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)))) ! (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))) ! (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).