changeset 1298:1b4bc72f433e

[xemacs-hg @ 2003-02-14 12:05:06 by ben] speedups to build process autoload.el: Factor out common code in generate-{c-,}file-autoloads-1 into new function generate-autoload-ish-1. \(I was originally going to use this for custom as well but ended up thinking better of it.) cus-dep.el: Cache the old computed values in custom-load.el and reuse them as necessary, to speed up running cus-dep (which would take 25-30 seconds to do all files in lisp/*, lisp/*/* on my Pentium III 700). Use `message' not `princ' to get correct newline behavior. Output messages showing each file we do actually process. update-elc-2.el: Rewrite algorithm to be much faster -- cache calls to directory-files and don't make needless calls to file-exists-p, file-directory-p because they're way way slow. Autoload early and only when update-elc has told us to. update-elc.el: If no files need byte compilation, signal to update-elc-2 to do any necessary autoload updating (using the file REBUILD_AUTOLOADS) rather than doing it ourselves, which would be way slow. Ignore updates to custom-load.el and auto-autoloads.el when checking to see whether autoloads need updating. Optimize out many unnecessary calls to file-exists-p to speed it up somewhat. (#### The remaining time is 50% or more in locate-file; this is presumably because, even though it has a cache, it's still statting each file to determine it's actually there. By calling directory-files ourselves, building a tree, and then looking in that tree, we could drastically shorten the time needed to do the locate operation.)
author ben
date Fri, 14 Feb 2003 12:05:07 +0000
parents 6c21360a544b
children 20da06d05889
files lisp/ChangeLog lisp/autoload.el lisp/cus-dep.el lisp/update-elc-2.el lisp/update-elc.el
diffstat 5 files changed, 425 insertions(+), 387 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Fri Feb 14 11:50:36 2003 +0000
+++ b/lisp/ChangeLog	Fri Feb 14 12:05:07 2003 +0000
@@ -17,57 +17,46 @@
 	* autoload.el (generate-autoload-ish-1):
 	* autoload.el (generate-file-autoloads-1):
 	* autoload.el (generate-c-file-autoloads-1):
-	* byte-optimize.el:
-	* byte-optimize.el (byte-compile-inline-expand):
-	* byte-optimize.el (byte-compile-unfold-lambda):
-	* byte-optimize.el (byte-optimize-form-code-walker):
-	* byte-optimize.el (byte-optimize-form):
-	* byte-optimize.el (byte-decompile-bytecode-1):
-	* byte-optimize.el (byte-optimize-lapcode):
+	Factor out common code in generate-{c-,}file-autoloads-1 into new
+	function generate-autoload-ish-1.  \(I was originally going to use
+	this for custom as well but ended up thinking better of it.)
+	
 	* cus-dep.el:
 	* cus-dep.el (cusload-hash-table-marker): New.
 	* cus-dep.el (Custom-make-dependencies-1):
+	Cache the old computed values in custom-load.el and reuse them as
+	necessary, to speed up running cus-dep (which would take 25-30
+	seconds to do all files in lisp/*, lisp/*/* on my Pentium III
+	700).  Use `message' not `princ' to get correct newline behavior.
+	Output messages showing each file we do actually process.
+	
 	* update-elc-2.el:
 	* update-elc-2.el (dirfiles-table): New.
 	* update-elc-2.el (do-update-elc-2):
 	* update-elc-2.el (batch-update-elc-2):
+	Rewrite algorithm to be much faster -- cache calls to
+	directory-files and don't make needless calls to file-exists-p,
+	file-directory-p because they're way way slow.
+	Autoload early and only when update-elc has told us to.
+
 	* update-elc.el:
 	* update-elc.el (dumped-exe): Removed.
 	* update-elc.el (dumped-exe-out-of-date-wrt-undumped-exe): Removed.
 	* update-elc.el (lisp-files-ignored-when-checking-for-autoload-updating): New.
 	* update-elc.el ((preloaded-file-list site-load-packages files-to-process)):
-
-2003-02-11  Ben Wing  <ben@xemacs.org>
-
-	* byte-optimize.el:
-	* byte-optimize.el (byte-compile-inline-expand):
-	* byte-optimize.el (byte-compile-unfold-lambda):
-	* byte-optimize.el (byte-optimize-form-code-walker):
-	* byte-optimize.el (byte-optimize-form):
-	* byte-optimize.el (byte-decompile-bytecode-1):
-	* byte-optimize.el (byte-optimize-lapcode):
+	If no files need byte compilation, signal to update-elc-2 to do
+	any necessary autoload updating (using the file REBUILD_AUTOLOADS)
+	rather than doing it ourselves, which would be way slow.  Ignore
+	updates to custom-load.el and auto-autoloads.el when checking to
+	see whether autoloads need updating.  Optimize out many
+	unnecessary calls to file-exists-p to speed it up somewhat. (####
+	The remaining time is 50% or more in locate-file; this is
+	presumably because, even though it has a cache, it's still
+	statting each file to determine it's actually there.  By calling
+	directory-files ourselves, building a tree, and then looking in
+	that tree, we could drastically shorten the time needed to do the
+	locate operation.)
 	
-	* update-elc-2.el:
-	* update-elc-2.el (dirfiles-table): New.
-	* update-elc-2.el (dolist): New.
-	* update-elc-2.el (do-update-elc-2):
-	* update-elc-2.el (batch-update-elc-2):
-	Rewrite algorithm to be much faster -- cache calls to
-	directory-files and don't make needless calls to file-exists-p,
-	file-directory-p because they're way way slow.
-
-	Autoload early and only when update-elc has told us to.
-	
-	* update-elc.el:
-	* update-elc.el (dumped-exe-exists): New.
-	* update-elc.el (lisp-files-ignored-when-checking-for-autoload-updating): New.
-	* update-elc.el ((preloaded-file-list site-load-packages files-to-process)):
-	If no files need byte compilation, signal to update-elc-2 to do
-	any necessary autoload updating rather than doing it ourselves,
-	which would be way slow.  Ignore updates to custom-load.el and
-	auto-autoloads.el when checking to see whether autoloads need
-	updating.
-
 2003-02-12  Jerry James  <james@xemacs.org>
 
 	* about.el (about-url-alist): Add my home page.
--- a/lisp/autoload.el	Fri Feb 14 11:50:36 2003 +0000
+++ b/lisp/autoload.el	Fri Feb 14 12:05:07 2003 +0000
@@ -2,7 +2,7 @@
 
 ;; Copyright (C) 1991, 1992, 1993, 1994, 1997 Free Software Foundation, Inc.
 ;; Copyright (C) 1995 Tinker Systems and INS Engineering Corp.
-;; Copyright (C) 1996, 2000, 2002 Ben Wing.
+;; Copyright (C) 1996, 2000, 2002, 2003 Ben Wing.
 
 ;; Author: Roland McGrath <roland@gnu.ai.mit.edu>
 ;; Keywords: maint
@@ -291,31 +291,33 @@
 are used."
   (interactive "fGenerate autoloads for file: ")
   (cond ((string-match "\\.el$" file)
-	 (generate-file-autoloads-1 file funlist))
+	 (generate-autoload-ish-1
+	  file
+	  (replace-in-string (file-name-nondirectory file) "\\.elc?$" "")
+	  nil #'generate-file-autoloads-1
+	  funlist))
 	;; #### jj, are C++ modules possible?
 	((string-match "\\.c$" file)
-	 (generate-c-file-autoloads-1 file funlist))
+	 (generate-autoload-ish-1
+	  file
+	  (replace-in-string (file-name-nondirectory file) "\\.c$" "")
+	  t #'generate-c-file-autoloads-1))
 	(t
 	 (error 'wrong-type-argument file "not a C or Elisp source file"))))
 
-(defun* generate-file-autoloads-1 (file funlist)
-  "Insert at point an 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."
+(defun* generate-autoload-ish-1 (file load-name literal fun-to-call &rest args)
+  "Insert at point an autoload-type section for FILE.
+If LITERAL, open the file literally, without decoding.
+Calls FUN-TO-CALL to compute the autoloads, passing it OUTBUF, LOAD-NAME,
+  TRIM-NAME, and ARGS."
   (let ((outbuf (current-buffer))
+	(trim-name (autoload-trim-file-name file))
 	(autoloads-done '())
-	(load-name (replace-in-string (file-name-nondirectory file)
-				      "\\.elc?$"
-				      ""))
-	(trim-name (autoload-trim-file-name file))
-	(dofiles (not (null funlist)))
 	(print-length nil)
 	(print-readably t) ; XEmacs
 	(float-output-format nil)
+	(visited (get-file-buffer file))
 	;; (done-any nil)
-	(visited (get-file-buffer file))
 	output-end)
 
     ;; If the autoload section we create here uses an absolute
@@ -332,74 +334,19 @@
 	  (progn
 	    (let ((find-file-hooks nil)
 		  (enable-local-variables nil))
-	      (set-buffer (or visited (find-file-noselect file)))
+	      (set-buffer (or visited (find-file-noselect file literal literal
+							  )))
+	      ;; This doesn't look right for C files, but it is.  The only
+	      ;; place we need the syntax table is when snarfing the Lisp
+	      ;; function name.
 	      (set-syntax-table emacs-lisp-mode-syntax-table))
-	    (save-excursion
-	      (save-restriction
-		(widen)
-		(goto-char (point-min))
-		(unless (search-forward generate-autoload-cookie nil t)
-		  (message "No autoloads found in %s" trim-name)
-		  (return-from generate-file-autoloads-1))
-
-		(message "Generating autoloads for %s..." trim-name)
-		(goto-char (point-min))
-		(while (if dofiles funlist (not (eobp)))
-		  (if (not dofiles)
-		      (skip-chars-forward " \t\n\f")
-		    (goto-char (point-min))
-		    (re-search-forward
-		     (concat "(def\\(un\\|var\\|const\\|macro\\) "
-			     (regexp-quote (symbol-name (car funlist)))
-			     "\\s "))
-		    (goto-char (match-beginning 0)))
-		  (cond
-		   ((or dofiles
-			(looking-at (regexp-quote generate-autoload-cookie)))
-		    (if dofiles
-			nil
-		      (search-forward generate-autoload-cookie)
-		      (skip-chars-forward " \t"))
-		    ;; (setq done-any t)
-		    (if (or dofiles (eolp))
-			;; 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))
-			       (doc-string-elt (get (car-safe form)
-						    'doc-string-elt)))
-			  (if autoload
-			      (setq autoloads-done (cons (nth 1 form)
-							 autoloads-done))
-			    (setq autoload form))
-			  (print-autoload autoload doc-string-elt outbuf ""))
-		      ;; Copy the rest of the line to the output.
-		      (let ((begin (point)))
-			;; (terpri outbuf)
-			(cond ((looking-at "immediate\\s *$") ; XEmacs
-			       ;; This is here so that you can automatically
-			       ;; have small hook functions copied to
-			       ;; auto-autoloads.el so that it's not necessary
-			       ;; to load a whole file just to get a two-line
-			       ;; do-nothing find-file-hook... --Stig
-			       (forward-line 1)
-			       (setq begin (point))
-			       (forward-sexp)
-			       (forward-line 1))
-			      (t
-			       (forward-line 1)))
-			(princ (buffer-substring begin (point)) outbuf))))
-		   ((looking-at ";")
-		    ;; Don't read the comment.
-		    (forward-line 1))
-		   (t
-		    (forward-sexp 1)
-		    (forward-line 1)))
-		  (if dofiles
-		      (setq funlist (cdr funlist)))))))
+	    (unless (setq autoloads-done
+			  (apply fun-to-call outbuf load-name trim-name args))
+	      (return-from generate-autoload-ish-1))
+	    )
 	(unless visited
-	    ;; We created this buffer, so we should kill it.
-	    (kill-buffer (current-buffer)))
+	  ;; We created this buffer, so we should kill it.
+	  (kill-buffer (current-buffer)))
 	(set-buffer outbuf)
 	(setq output-end (point-marker))))
     (if t ;; done-any
@@ -432,119 +379,153 @@
     (or noninteractive ; XEmacs: only need one line in -batch mode.
 	(message "Generating autoloads for %s...done" file))))
 
-(defun* generate-c-file-autoloads-1 (file funlist)
+(defun* generate-file-autoloads-1 (outbuf load-name trim-name funlist)
+  "Insert at point an 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."
+  (let ((autoloads-done '())
+	(dofiles (not (null funlist)))
+	)
+
+    (save-excursion
+      (save-restriction
+	(widen)
+	(goto-char (point-min))
+	(unless (search-forward generate-autoload-cookie nil t)
+	  (message "No autoloads found in %s" trim-name)
+	  (return-from generate-file-autoloads-1 nil))
+
+	(message "Generating autoloads for %s..." trim-name)
+	(goto-char (point-min))
+	(while (if dofiles funlist (not (eobp)))
+	  (if (not dofiles)
+	      (skip-chars-forward " \t\n\f")
+	    (goto-char (point-min))
+	    (re-search-forward
+	     (concat "(def\\(un\\|var\\|const\\|macro\\) "
+		     (regexp-quote (symbol-name (car funlist)))
+		     "\\s "))
+	    (goto-char (match-beginning 0)))
+	  (cond
+	   ((or dofiles
+		(looking-at (regexp-quote generate-autoload-cookie)))
+	    (if dofiles
+		nil
+	      (search-forward generate-autoload-cookie)
+	      (skip-chars-forward " \t"))
+	    ;; (setq done-any t)
+	    (if (or dofiles (eolp))
+		;; 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))
+		       (doc-string-elt (get (car-safe form)
+					    'doc-string-elt)))
+		  (if autoload
+		      (setq autoloads-done (cons (nth 1 form)
+						 autoloads-done))
+		    (setq autoload form))
+		  (print-autoload autoload doc-string-elt outbuf ""))
+	      ;; Copy the rest of the line to the output.
+	      (let ((begin (point)))
+		;; (terpri outbuf)
+		(cond ((looking-at "immediate\\s *$") ; XEmacs
+		       ;; This is here so that you can automatically
+		       ;; have small hook functions copied to
+		       ;; auto-autoloads.el so that it's not necessary
+		       ;; to load a whole file just to get a two-line
+		       ;; do-nothing find-file-hook... --Stig
+		       (forward-line 1)
+		       (setq begin (point))
+		       (forward-sexp)
+		       (forward-line 1))
+		      (t
+		       (forward-line 1)))
+		(princ (buffer-substring begin (point)) outbuf))))
+	   ((looking-at ";")
+	    ;; Don't read the comment.
+	    (forward-line 1))
+	   (t
+	    (forward-sexp 1)
+	    (forward-line 1)))
+	  (if dofiles
+	      (setq funlist (cdr funlist))))))
+    autoloads-done))
+
+(defun* generate-c-file-autoloads-1 (outbuf load-name trim-name funlist)
   "Insert at point an autoload section for the C file FILE.
 autoloads are generated for defuns and defmacros in FILE
 marked by `generate-c-autoload-cookie' (which see).
 If FILE is being visited in a buffer, the contents of the buffer
 are used."
-  (let ((outbuf (current-buffer))
-	(autoloads-done '())
-	(load-name (replace-in-string (file-name-nondirectory file)
-				      "\\.c?$"
-				      ""))
-	(exists-p-format 
+  (let ((exists-p-format 
 	 "(file-exists-p (expand-file-name \"%s.%s\" module-directory))")
-	(trim-name (autoload-trim-file-name file))
-	(print-length nil)
-	(print-readably t)
-	(float-output-format nil)
-	;; (done-any nil)
-	(visited (get-file-buffer file))
-	output-end)
-
-    ;; If the autoload section we create here uses an absolute
-    ;; pathname for FILE in its header, and then Emacs is installed
-    ;; under a different path on another system,
-    ;; `update-autoloads-here' won't be able to find the files to be
-    ;; autoloaded.  So, if FILE is in the same directory or a
-    ;; subdirectory of the current buffer's directory, we'll make it
-    ;; relative to the current buffer's directory.
-    (setq file (expand-file-name file))
+	(autoloads-done '())
+	)
 
     (save-excursion
-      (unwind-protect
-	  (progn
-	    (let ((find-file-hooks nil)
-		  (enable-local-variables nil))
-	      (set-buffer (or visited (find-file-noselect file t t)))
-	      ;; This doesn't look right, but it is.  The only place we need
-	      ;; the syntax table is when snarfing the Lisp function name.
-	      (set-syntax-table emacs-lisp-mode-syntax-table))
-	    (save-excursion
-	      (save-restriction
-		(widen)
+      (save-restriction
+	(widen)
+	(goto-char (point-min))
+	;; Is there a module name comment?
+	(when (search-forward generate-c-autoload-module nil t)
+	  (skip-chars-forward " \t")
+	  (let ((begin (point)))
+	    (skip-chars-forward "^ \t\n\f")
+	    (setq load-name (buffer-substring begin (point)))))
+	(if funlist
+	    (progn
+	      (message "Generating autoloads for %s..." trim-name)
+	      (princ "(when (or\n       " outbuf)
+	      (princ (format exists-p-format load-name "ell") outbuf)
+	      (princ "\n       " outbuf)
+	      (princ (format exists-p-format load-name "dll") outbuf)
+	      (princ "\n       " outbuf)
+	      (princ (format exists-p-format load-name "so") outbuf)
+	      ;; close the princ'd `or' form
+	      (princ ")\n       " outbuf)
+	      (dolist (arg funlist)
 		(goto-char (point-min))
-		;; Is there a module name comment?
-		(when (search-forward generate-c-autoload-module nil t)
-		  (skip-chars-forward " \t")
-		  (let ((begin (point)))
-		    (skip-chars-forward "^ \t\n\f")
-		    (setq load-name (buffer-substring begin (point)))))
-		(if funlist
-		    (progn
-		      (message "Generating autoloads for %s..." trim-name)
-		      (princ "(when (or\n       " outbuf)
-		      (princ (format exists-p-format load-name "ell") outbuf)
-		      (princ "\n       " outbuf)
-		      (princ (format exists-p-format load-name "dll") outbuf)
-		      (princ "\n       " outbuf)
-		      (princ (format exists-p-format load-name "so") outbuf)
-		      ;; close the princ'd `or' form
-		      (princ ")\n       " outbuf)
-		      (dolist (arg funlist)
-			(goto-char (point-min))
-			(re-search-forward
-			 (concat "DEFUN (\""
-				 (regexp-quote (symbol-name arg))
-				 "\""))
-			(goto-char (match-beginning 0))
-			(let ((autoload (make-c-autoload load-name)))
-			  (when autoload
-			    (push (nth 1 (nth 1 autoload)) autoloads-done)
-			    (print-autoload autoload 3 outbuf "  "))))
-		      ;; close the princ'd `when' form
-		      (princ ")" outbuf))
-		  (goto-char (point-min))
-		  (let ((match
-			 (search-forward generate-c-autoload-cookie nil t)))
-		    (unless match
-		      (message "No autoloads found in %s" trim-name)
-		      (return-from generate-c-file-autoloads-1))
-
-		    (message "Generating autoloads for %s..." trim-name)
-		    (princ "(when (or\n       " outbuf)
-		    (princ (format exists-p-format load-name "ell") outbuf)
-		    (princ "\n       " outbuf)
-		    (princ (format exists-p-format load-name "dll") outbuf)
-		    (princ "\n       " outbuf)
-		    (princ (format exists-p-format load-name "so") outbuf)
-		    ;; close the princ'd `or' form
-		    (princ ")\n       " outbuf)
-		    (while match
-		      (forward-line 1)
-		      (let ((autoload (make-c-autoload load-name)))
-			(when autoload
-			  (push (nth 1 (nth 1 autoload)) autoloads-done)
-			  (print-autoload autoload 3 outbuf "  ")))
-		      (setq match
-			    (search-forward generate-c-autoload-cookie nil t)))
-		    ;; close the princ'd `when' form
-		    (princ ")" outbuf))))))
-	(unless visited
-	  ;; We created this buffer, so we should kill it.
-	  (kill-buffer (current-buffer)))
-	(set-buffer outbuf)
-	(setq output-end (point-marker))))
-    (insert generate-autoload-section-header)
-    (prin1 (list 'autoloads autoloads-done load-name trim-name) outbuf)
-    (terpri outbuf)
-    (when (< output-end (point))
-      (setq output-end (point-marker)))
-    (goto-char output-end)
-    (insert generate-autoload-section-trailer)
-    (or noninteractive ; XEmacs: only need one line in -batch mode.
-	(message "Generating autoloads for %s...done" trim-name))))
+		(re-search-forward
+		 (concat "DEFUN (\""
+			 (regexp-quote (symbol-name arg))
+			 "\""))
+		(goto-char (match-beginning 0))
+		(let ((autoload (make-c-autoload load-name)))
+		  (when autoload
+		    (push (nth 1 (nth 1 autoload)) autoloads-done)
+		    (print-autoload autoload 3 outbuf "  "))))
+	      ;; close the princ'd `when' form
+	      (princ ")" outbuf))
+	  (goto-char (point-min))
+	  (let ((match
+		 (search-forward generate-c-autoload-cookie nil t)))
+	    (unless match
+	      (message "No autoloads found in %s" trim-name)
+	      (return-from generate-c-file-autoloads-1 nil))
+	    
+	    (message "Generating autoloads for %s..." trim-name)
+	    (princ "(when (or\n       " outbuf)
+	    (princ (format exists-p-format load-name "ell") outbuf)
+	    (princ "\n       " outbuf)
+	    (princ (format exists-p-format load-name "dll") outbuf)
+	    (princ "\n       " outbuf)
+	    (princ (format exists-p-format load-name "so") outbuf)
+	    ;; close the princ'd `or' form
+	    (princ ")\n       " outbuf)
+	    (while match
+	      (forward-line 1)
+	      (let ((autoload (make-c-autoload load-name)))
+		(when autoload
+		  (push (nth 1 (nth 1 autoload)) autoloads-done)
+		  (print-autoload autoload 3 outbuf "  ")))
+	      (setq match
+		    (search-forward generate-c-autoload-cookie nil t)))
+	    ;; close the princ'd `when' form
+	    (princ ")" outbuf)))))
+    autoloads-done))
 
 ;; Assorted utilities for generating  autoloads and pieces thereof
 
--- a/lisp/cus-dep.el	Fri Feb 14 11:50:36 2003 +0000
+++ b/lisp/cus-dep.el	Fri Feb 14 12:05:07 2003 +0000
@@ -1,6 +1,7 @@
 ;;; cus-dep.el --- Find customization dependencies.
 ;;
 ;; Copyright (C) 1997 Free Software Foundation, Inc.
+;; Copyright (C) 2003 Ben Wing.
 ;;
 ;; Author: Per Abrahamsen <abraham@dina.kvl.dk>, then
 ;;         Richard Stallman <rms@gnu.ai.mit.edu>, then
@@ -76,6 +77,11 @@
 ;; files.  This is not necessary under FSF (they simply use `put'),
 ;; since they have only one file with custom dependencies.  With the
 ;; advent of packages, we cannot afford the same luxury.
+;;
+;; Feb 2003: Added code to speed up building by caching the values we've
+;; constructed, and using them instead of scanning a file when custom-load
+;; is up-to-date w.r.t. the file.  Also use `message' not `princ' to print
+;; out messages so nl's are correctly inserted when necessary. --ben
 
 
 ;;; Code:
@@ -90,6 +96,7 @@
 ;; Don't change this, unless you plan to change the code in
 ;; cus-start.el, too.
 (defconst cusload-base-file "custom-load.el")
+(defconst cusload-hash-table-marker ";old-cus-dep-hash: ")
 
 ;; Be very careful when changing this function.  It looks easy to
 ;; understand, but is in fact very easy to break.  Be sure to read and
@@ -99,9 +106,11 @@
   (setq subdirs (mapcar #'expand-file-name subdirs))
   (with-temp-buffer
     (let ((enable-local-eval nil)
-	  (hash (make-hash-table :test 'eq)))
+	  (hash (make-hash-table :test 'eq))
+	  (hash-cache (make-hash-table :test 'equal))
+	  old-hash)
       (dolist (dir subdirs)
-	(princ (format "Processing %s\n" dir))
+	(message "Processing %s\n" dir)
 	(let ((cusload-file (expand-file-name cusload-base-file dir))
 	      (files (directory-files dir t "\\`[^=].*\\.el\\'")))
 	  ;; A trivial optimization: if no file in the directory is
@@ -110,46 +119,77 @@
 		   (dolist (file files t)
 		     (when (file-newer-than-file-p file cusload-file)
 		       (return nil))))
-	      (princ "(No changes need to be written)\n")
+	      (message "(No changes need to be written)")
+	    (when (file-exists-p cusload-file)
+	      (let ((buf (find-file-noselect cusload-file)))
+		(with-current-buffer buf
+		  (goto-char (point-min))
+		  (when (search-forward cusload-hash-table-marker nil t)
+		    (setq old-hash (read buf))))
+		(kill-buffer buf)))
 	    ;; Process directory
 	    (dolist (file files)
-	      (when (file-exists-p file)
-		(erase-buffer)
-		(insert-file-contents file)
-		(goto-char (point-min))
-		(let ((name (file-name-sans-extension
-			     (file-name-nondirectory file))))
-		  ;; Search for defcustom/defface/defgroup
-		  ;; expressions, and evaluate them.
-		  (while (re-search-forward
-			  "^(defcustom\\|^(defface\\|^(defgroup"
-			  nil t)
-		    (beginning-of-line)
-		    (let ((expr (read (current-buffer))))
-		      ;; We need to ignore errors here, so that
-		      ;; defcustoms with :set don't bug out.  Of
-		      ;; course, their values will not be assigned in
-		      ;; case of errors, but their `custom-group'
-		      ;; properties will by that time be in place, and
-		      ;; that's all we care about.
-		      (ignore-errors
-			(eval expr))
-		      ;; Hash the file of the affected symbol.
-		      (setf (gethash (nth 1 expr) hash) name))))))
+	      (let ((old-cache (if (hash-table-p old-hash)
+				   (gethash file old-hash t)
+				 t)))
+		(if (and (not (file-newer-than-file-p file cusload-file))
+			 (not (eq old-cache t)))
+		    (progn
+		      (dolist (c old-cache)
+			(puthash (car c) (cdr c) hash))
+		      (puthash file old-cache hash-cache))
+		  (erase-buffer)
+		  (insert-file-contents file)
+		  (goto-char (point-min))
+		  (let ((name (file-name-sans-extension
+			       (file-name-nondirectory file)))
+			cache
+			(first t))
+		    ;; Search for defcustom/defface/defgroup
+		    ;; expressions, and evaluate them.
+		    (while (re-search-forward
+			    "^(defcustom\\|^(defface\\|^(defgroup"
+			    nil t)
+		      (when first
+			(message "Computing custom-loads for %s..." name)
+			(setq first nil))
+		      (beginning-of-line)
+		      (let ((expr (read (current-buffer))))
+			;; We need to ignore errors here, so that
+			;; defcustoms with :set don't bug out.  Of
+			;; course, their values will not be assigned in
+			;; case of errors, but their `custom-group'
+			;; properties will by that time be in place, and
+			;; that's all we care about.
+			(ignore-errors
+			  (eval expr))
+			;; Hash the file of the affected symbol.
+			(setf (gethash (nth 1 expr) hash) name)
+			;; Remember the values computed.
+			(push (cons (nth 1 expr) name) cache)))
+		    (or cache
+			(message "No custom-loads for %s" name))
+		    (puthash file cache hash-cache)))
+		))
 	    (cond
 	     ((zerop (hash-table-count hash))
-	      (princ "(No customization dependencies")
-	      (when (file-exists-p cusload-file)
-		(princ (format ", deleting %s" cusload-file))
-		(delete-file cusload-file))
-	      (princ ")\n"))
+	      (if (not (file-exists-p cusload-file))
+		  (message "(No customization dependencies)")
+		(message "(No customization dependencies, deleting %s)"
+			 cusload-file)
+		(delete-file cusload-file)))
 	     (t
-	      (princ (format "Generating %s...\n" cusload-base-file))
+	      (message "Generating %s...\n" cusload-base-file)
 	      (with-temp-file cusload-file
 		(insert ";;; " cusload-base-file
 			" --- automatically extracted custom dependencies\n"
-			"\n;;; Code:\n\n"
-			"(autoload 'custom-add-loads \"cus-load\")\n\n")
+			"\n;;; Code:\n\n")
+		(insert cusload-hash-table-marker)
+		(let ((print-readably t)
+		      (standard-output (current-buffer)))
+		  (princ hash-cache)
+		  (terpri))
+		(insert "(autoload 'custom-add-loads \"cus-load\")\n\n")
 		(mapatoms
 		 (lambda (sym)
 		   (let ((members (get sym 'custom-group))
--- a/lisp/update-elc-2.el	Fri Feb 14 11:50:36 2003 +0000
+++ b/lisp/update-elc-2.el	Fri Feb 14 12:05:07 2003 +0000
@@ -69,6 +69,8 @@
     "^version\\.el$"
     "^very-early-lisp\\.el$"))
 
+(defvar dirfiles-table (make-hash-table :test 'equal))
+
 ;; SEEN accumulates the list of already-handled dirs.
 (defun do-update-elc-2 (dir compile-stage-p seen)
   (setq dir (file-name-as-directory dir))
@@ -76,65 +78,104 @@
   (unless (member (file-truename dir) seen)
     (push (file-truename dir) seen)
 
-    ;; Do this directory.
-    (if compile-stage-p
-	;; Stage 2: Recompile necessary .els
-	(let ((files (directory-files dir t "\\.el$"))
-	      file file-c)
-	  (while (setq file (car files))
-	    (setq files (cdr files))
-	    (setq file-c (concat file "c"))
-	    (when (and (file-exists-p file)
-		       (or (not (file-exists-p file-c))
-			   (file-newer-than-file-p file file-c))
-		       (let (ignore)
-			 (mapcar
-			  #'(lambda (regexp)
-			      (if (string-match regexp
-						(file-name-nondirectory file))
-				  (setq ignore t)))
-			  update-elc-ignored-files)
-			 (not ignore)))
-	      (byte-compile-file file))))
+    (let ((files (or (gethash dir dirfiles-table)
+		     (directory-files dir t nil t))))
+
+      ;; Do this directory.
+      (if compile-stage-p
+	  ;; Stage 2: Recompile necessary .els
+	  (dolist (file files)
+	    (when (string-match "\\.el$" file)
+	      (let ((file-c (concat file "c")))
+		(when (and (not (member file-c files))
+			   ;; no need to check for out-of-date-ness because
+			   ;; that was already done, and .elc removed.
+			   (let (ignore)
+			     (mapcar
+			      #'(lambda (regexp)
+				  (if (string-match
+				       regexp
+				       (file-name-nondirectory file))
+				      (setq ignore t)))
+			      update-elc-ignored-files)
+			     (not ignore)))
+		  (byte-compile-file file)))))
 
-      ;; Stage 1.
-      ;; Remove out-of-date elcs
-      (let ((files (directory-files dir t "\\.el$"))
-	    file file-c)
-	(while (setq file (car files))
-	  (setq files (cdr files))
-	  (setq file-c (concat file "c"))
-	  (when (and (file-exists-p file-c)
-		     (file-newer-than-file-p file file-c))
-	    (message "Removing out-of-date %s" file-c)
-	    (delete-file file-c))))
-      ;; Remove elcs without corresponding el
-      (let ((files (directory-files dir t "\\.elc$"))
-	    file file-c)
-	(while (setq file-c (car files))
-	  (setq files (cdr files))
-	  (setq file (replace-in-string file-c "c$" ""))
-	  (when (and (file-exists-p file-c)
-		     (not (file-exists-p file)))
-	    (message "Removing %s; no corresponding .el" file-c)
-	    (delete-file file-c)))))
+	;; Stage 1.
+	;; Remove out-of-date elcs
+	(let (deleted)
+	  (dolist (file files)
+	    (when (string-match "\\.el$" file)
+	      (let ((file-c (concat file "c")))
+		(when (and (member file-c files)
+			   (file-newer-than-file-p file file-c))
+		  (message "Removing out-of-date %s" file-c)
+		  (delete-file file-c)
+		  (push file-c deleted)))))
 
-    ;; We descend recursively
-    (let ((dirs (directory-files dir t nil t))
-          dir)
-      (while (setq dir (pop dirs))
-        (when (and (not (member (file-name-nondirectory dir)
-				update-elc-ignored-dirs))
-                   (file-directory-p dir))
-          (do-update-elc-2 dir compile-stage-p seen))))
+	;; Remove elcs without corresponding el
+	(dolist (file-c files)
+	  (when (string-match "\\.elc$" file-c)
+	    (let ((file (replace-in-string file-c "c$" "")))
+	      (when (not (member file files))
+		(message "Removing %s; no corresponding .el" file-c)
+		(delete-file file-c)
+		(push file-c deleted)))))
 
-    ))
+	(setq files (set-difference files deleted))))
+
+      (puthash dir files dirfiles-table)
+
+      ;; We descend recursively.  On my Windows machine, it is much faster
+      ;; to call directory-files again to recompute than to call
+      ;; file-directory-p on each member of the files list.
+      (dolist (dir (directory-files dir t nil t 'dir))
+	(when (not (member (file-name-nondirectory dir)
+			   update-elc-ignored-dirs))
+	  (do-update-elc-2 dir compile-stage-p seen))))))
 
 (defun batch-update-elc-2 ()
   (defvar command-line-args-left)
   (unless noninteractive
     (error "`batch-update-elc-2' is to be used only with -batch"))
   (let ((dir (car command-line-args-left)))
+    ;; don't depend on being able to autoload `update-autoload-files'!
+    (load "autoload")
+    (load "bytecomp")
+    (load "byte-optimize")
+    ;; #### the API used here is deprecated, convert to one with explicit
+    ;; arguments when it is available
+    ;; update-elc.el signals us to rebuild the autoloads when necessary.
+    ;; in some cases it will rebuild the autoloads itself, but doing it this
+    ;; way is slow, so we avoid it when possible.
+    (when (file-exists-p "../src/REBUILD_AUTOLOADS")
+      (let ((generated-autoload-file (expand-file-name "auto-autoloads.el" dir))
+	    (autoload-package-name "auto")) ; feature prefix
+	(update-autoload-files (list dir))
+	(byte-recompile-file generated-autoload-file 0))
+      (when (featurep 'mule)
+	(let* ((muledir (expand-file-name "../lisp/mule" (file-truename dir)))
+	       (generated-autoload-file
+		(expand-file-name "auto-autoloads.el" muledir))
+	       (autoload-package-name "mule")) ; feature prefix
+	  (update-autoload-files (list muledir))
+	  (byte-recompile-file generated-autoload-file 0))))
+    (when (featurep 'modules)
+      (let* ((moddir (expand-file-name "../modules" (file-truename dir)))
+	     (generated-autoload-file
+	      (expand-file-name "auto-autoloads.el" moddir))
+	     (autoload-package-name "modules")) ; feature prefix
+	(update-autoload-files
+	 (delete (concat (file-name-as-directory moddir) ".")
+		 (delete (concat (file-name-as-directory moddir) "..")
+			 (directory-files moddir t nil nil 0)))
+	 t)
+	(byte-recompile-file generated-autoload-file 0)))
+    ;; now load the (perhaps newly rebuilt) autoloads; we were called with
+    ;; -no-autoloads so they're not already loaded.
+    (load "../lisp/auto-autoloads")
+    (when (featurep 'mule)
+      (load "../lisp/mule/auto-autoloads"))
     ;; We remove all the bad .elcs before any byte-compilation, because
     ;; there may be dependencies between one .el and another (even across
     ;; directories), and we don't want to load an out-of-date .elc while
@@ -146,32 +187,6 @@
     (message "Recompiling updated .els in directory tree `%s'..." dir)
     (do-update-elc-2 dir t nil)
     (message "Recompiling updated .els in directory tree `%s'...done" dir)
-    ;; don't depend on being able to autoload `update-autoload-files'!
-    (load "autoload")
-    ;; #### the API used here is deprecated, convert to one with explicit
-    ;; arguments when it is available
-    (let ((generated-autoload-file (expand-file-name "auto-autoloads.el" dir))
-	  (autoload-package-name "auto")) ; feature prefix
-      (update-autoload-files (list dir))
-      (byte-recompile-file generated-autoload-file 0))
-    (when (featurep 'modules)
-      (let* ((moddir (expand-file-name "../modules" (file-truename dir)))
-	     (generated-autoload-file
-	      (expand-file-name "auto-autoloads.el" moddir))
-	     (autoload-package-name "modules")) ; feature prefix
-	(update-autoload-files
-	 (delete (concat (file-name-as-directory moddir) ".")
-		 (delete (concat (file-name-as-directory moddir) "..")
-			 (directory-files moddir t nil nil 0)))
-	 t)
-	(byte-recompile-file generated-autoload-file 0)))
-    (when (featurep 'mule)
-      (let* ((muledir (expand-file-name "../lisp/mule" (file-truename dir)))
-	     (generated-autoload-file
-	      (expand-file-name "auto-autoloads.el" muledir))
-	     (autoload-package-name "mule")) ; feature prefix
-	(update-autoload-files (list muledir))
-	(byte-recompile-file generated-autoload-file 0)))
     ;; likewise here.
     (load "cus-dep")
     (Custom-make-dependencies dir)
--- a/lisp/update-elc.el	Fri Feb 14 11:50:36 2003 +0000
+++ b/lisp/update-elc.el	Fri Feb 14 12:05:07 2003 +0000
@@ -59,9 +59,9 @@
 (defvar need-to-recompile-autoloads nil)
 (defvar need-to-recompile-mule-autoloads nil)
 (defvar undumped-exe nil)
-(defvar dumped-exe nil)
+;(defvar dumped-exe nil)
 (defvar dumped-exe-out-of-date-wrt-dump-files nil)
-(defvar dumped-exe-out-of-date-wrt-undumped-exe nil)
+;(defvar dumped-exe-out-of-date-wrt-undumped-exe nil)
 
 ;(setq update-elc-files-to-compile
 ;      (delq nil
@@ -112,6 +112,11 @@
     "very-early-lisp.el")
   "Lisp files that should not be byte compiled.")
 
+(defvar lisp-files-ignored-when-checking-for-autoload-updating
+  '("custom-load.el"
+    "auto-autoloads.el")
+  "Lisp files that should not trigger auto-autoloads rebuilding.")
+
 (defun update-elc-chop-extension (file)
   (if (string-match "\\.elc?$" file)
       (substring file 0 (match-beginning 0))
@@ -130,30 +135,31 @@
 	      ((file-exists-p "../src/xemacs") "../src/xemacs")
 	      (t nil)))
 
-  (let ((temacs-exe
-	 (cond ((file-exists-p "../src/temacs.exe") "../src/temacs.exe")
-	       ((file-exists-p "../src/temacs") "../src/temacs")
-	       (t nil)))
-	(data-file
-	 (cond ((file-exists-p "../src/xemacs.dmp") "../src/xemacs.dmp")
-	       (t nil))))
+  ;; Not currently used but might be at some point.
+;   (let ((temacs-exe
+; 	 (cond ((file-exists-p "../src/temacs.exe") "../src/temacs.exe")
+; 	       ((file-exists-p "../src/temacs") "../src/temacs")
+; 	       (t nil)))
+; 	(data-file
+; 	 (cond ((file-exists-p "../src/xemacs.dmp") "../src/xemacs.dmp")
+; 	       (t nil))))
 
-    ;; two setups here:
-    ;; (1) temacs.exe is undumped, dumped into xemacs.exe.  Happens with
-    ;;     unexec, but also with pdump under MS Windows native, since
-    ;;     the dumped data is stored as a resource in the xemacs.exe
-    ;;     executable.
-    ;; (2) xemacs.exe is dumped or undumped.  Running `xemacs -nd' gets
-    ;;     you the equivalent of `temacs'.  Dumping creates a file
-    ;;     `xemacs.dmp'.
+;     ;; two setups here:
+;     ;; (1) temacs.exe is undumped, dumped into xemacs.exe.  Happens with
+;     ;;     unexec, but also with pdump under MS Windows native, since
+;     ;;     the dumped data is stored as a resource in the xemacs.exe
+;     ;;     executable.
+;     ;; (2) xemacs.exe is dumped or undumped.  Running `xemacs -nd' gets
+;     ;;     you the equivalent of `temacs'.  Dumping creates a file
+;     ;;     `xemacs.dmp'.
 
-    (setq dumped-exe-out-of-date-wrt-undumped-exe
-	  (cond ((not dumped-exe) t)
-		(temacs-exe (file-newer-than-file-p temacs-exe dumped-exe))
-		((not data-file) t)
-		(t (file-newer-than-file-p dumped-exe data-file))))
-    )
-                                                              
+;     (setq dumped-exe-out-of-date-wrt-undumped-exe
+; 	  (cond ((not dumped-exe) t)
+; 		(temacs-exe (file-newer-than-file-p temacs-exe dumped-exe))
+; 		((not data-file) t)
+; 		(t (file-newer-than-file-p dumped-exe data-file))))
+;     (setq dumped-exe-exists (or (and temacs-exe dumped-exe)
+; 				(and data-file dumped-exe))))
 
   ;; Path setup
   (let ((package-preloaded-file-list
@@ -200,18 +206,18 @@
       ;; now check if .el or .elc is newer than the dumped exe.
       ;; if so, need to redump.
       (when (and dumped-exe arg-is-preloaded
-		 (or (and (file-exists-p full-arg-el)
-			  (file-newer-than-file-p full-arg-el dumped-exe))
-		     (and (file-exists-p full-arg-elc)
-			  (file-newer-than-file-p full-arg-elc dumped-exe))))
+		 ;; no need to check for existence of either of the files
+		 ;; because of the definition of file-newer-than-file-p.
+		 (or (file-newer-than-file-p full-arg-el dumped-exe)
+		     (file-newer-than-file-p full-arg-elc dumped-exe)))
 	(setq dumped-exe-out-of-date-wrt-dump-files t))
 
       (if (and (not (member (file-name-nondirectory arg)
 			    unbytecompiled-lisp-files))
 	       (not (member full-arg-el processed))
-	       (file-exists-p full-arg-el)
-	       (or (not (file-exists-p full-arg-elc))
-		   (file-newer-than-file-p full-arg-el full-arg-elc)))
+	       ;; no need to check for existence of either of the files
+	       ;; because of the definition of file-newer-than-file-p.
+	       (file-newer-than-file-p full-arg-el full-arg-elc))
 	  (setq processed (cons full-arg-el processed)))
 
       (setq files-to-process (cdr files-to-process))))
@@ -228,9 +234,14 @@
 	     (autoload-is-mule (equal dir "../lisp/mule")))
 	(while all-files-in-dir
 	  (let* ((full-arg (car all-files-in-dir)))
-	    (when (or (not (file-exists-p autoload-file))
-		      (and (file-exists-p full-arg)
-			   (file-newer-than-file-p full-arg autoload-file)))
+	    ;; custom-load.el always gets regenerated so don't let that
+	    ;; trigger us.
+	    (when (and (not
+			(member
+			 (file-name-nondirectory full-arg)
+			 lisp-files-ignored-when-checking-for-autoload-updating
+			 ))
+		       (file-newer-than-file-p full-arg autoload-file))
 	      (if autoload-is-mule
 		  (setq need-to-rebuild-mule-autoloads t)
 		(setq need-to-rebuild-autoloads t))))
@@ -245,15 +256,22 @@
   )
 
 (when (or need-to-rebuild-autoloads
+	  ;; no real need for the following check either, because if the file
+	  ;; doesn't exist, need-to-rebuild-autoloads gets set above.  but
+	  ;; it's only one call, so it won't slow things down much and it keeps
+	  ;; the logic cleaner.
 	  (not (file-exists-p "../lisp/auto-autoloads.el"))
-	  (not (file-exists-p "../lisp/auto-autoloads.elc"))
+	  ;; no need to check for file-exists of .elc due to definition
+	  ;; of file-newer-than-file-p
 	  (file-newer-than-file-p "../lisp/auto-autoloads.el"
 				  "../lisp/auto-autoloads.elc"))
   (setq need-to-recompile-autoloads t))
 
 (when (or need-to-rebuild-mule-autoloads
+	  ;; not necessary but ...  see comment above.
 	  (not (file-exists-p "../lisp/mule/auto-autoloads.el"))
-	  (not (file-exists-p "../lisp/mule/auto-autoloads.elc"))
+	  ;; no need to check for file-exists of .elc due to definition
+	  ;; of file-newer-than-file-p
 	  (file-newer-than-file-p "../lisp/mule/auto-autoloads.el"
 				  "../lisp/mule/auto-autoloads.elc"))
   (setq need-to-recompile-mule-autoloads t))
@@ -285,6 +303,9 @@
 	(if need-to-recompile-mule-autoloads
 	    '("-f" "batch-byte-compile-one-file"
 		   "../lisp/mule/auto-autoloads.el")))))
+  (condition-case nil
+      (delete-file "../src/REBUILD_AUTOLOADS")
+    (file-error nil))
   (cond ((and (not update-elc-files-to-compile)
 	      (not need-to-rebuild-autoloads)
 	      (not need-to-rebuild-mule-autoloads)
@@ -297,24 +318,16 @@
 	 (condition-case nil
 	     (delete-file "../src/BYTECOMPILE_CHANGE")
 	   (file-error nil)))
-	((and (not update-elc-files-to-compile)
-	      (not dumped-exe-out-of-date-wrt-dump-files)
-	      (not dumped-exe-out-of-date-wrt-undumped-exe))
+	((not update-elc-files-to-compile)
 	 ;; (2) We have no files to byte-compile, but we do need to
-	 ;;     regenerate and compile the auto-autoloads file. (This will
-	 ;;     be needed to be up-to-date before we run update-elc-2.)
-	 ;;     If the dumped exe exists and is up-to-date, both with
-	 ;;     respect to the undumped exe and the files that will be dumped
-	 ;;     into it, then we can use the dumped exe to rebuild the
-	 ;;     autoloads.  Else, we have to do it the "hard way" by loading
-	 ;;     raw temacs, running loadup, then regenerating the autoloads.
-	 ;;     #### We should see whether it's possible to load up a
-	 ;;     minimal number of files in order to get autoload.el to work.
-	 (load "raw-process.el")
-	 (apply 'call-process-internal dumped-exe nil t nil
-		(append
-		 '("-batch -no-autoloads -no-packages")
-		 do-autoload-commands))
+	 ;;     regenerate and compile the auto-autoloads file, so signal
+	 ;;     update-elc-2 to do it.  This is much faster than loading
+	 ;;     all the .el's and doing it here. (We only need to rebuild
+	 ;;     the autoloads here when we have files to compile, since
+	 ;;     they may depend on the updated autoloads.)
+	 (condition-case nil
+	     (write-region-internal "foo" nil "../src/REBUILD_AUTOLOADS")
+	   (file-error nil))
 	 (condition-case nil
 	     (delete-file "../src/BYTECOMPILE_CHANGE")
 	   (file-error nil)))
@@ -384,6 +397,6 @@
 	 ;;(print command-line-args)
 	 (load "loadup-el.el"))))
 
-(kill-emacs)
+;(kill-emacs)
 
 ;;; update-elc.el ends here