From 5200c2baefbc855d36c07e2d6a9c1adeb693a542 Mon Sep 17 00:00:00 2001 From: Glenn Morris Date: Tue, 30 Jun 2015 14:59:04 -0400 Subject: [PATCH] Improve reproducibility of generated loaddefs file. * lisp/emacs-lisp/autoload.el (autoload-generate-file-autoloads): Make the return value the modtime of the input file (if no autoloads). (update-directory-autoloads): In the "no autoloads" section, use "most recent modtime" rather than "current time". ; http://lists.gnu.org/archive/html/emacs-devel/2015-06/msg00688.html --- lisp/emacs-lisp/autoload.el | 281 +++++++++++++++++++++++--------------------- 1 file changed, 144 insertions(+), 137 deletions(-) diff --git a/lisp/emacs-lisp/autoload.el b/lisp/emacs-lisp/autoload.el index a6fefebf3f5..9d1abdddb2b 100644 --- a/lisp/emacs-lisp/autoload.el +++ b/lisp/emacs-lisp/autoload.el @@ -522,116 +522,119 @@ 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 (load-name - (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)) - ;; 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 - (or noninteractive (message "Generating autoloads for %s..." file)) - (setq load-name - (if (stringp generated-autoload-load-name) - generated-autoload-load-name - (autoload-file-load-name absfile))) - ;; FIXME? Comparing file-names for equality with just equal - ;; is fragile, eg if one has an automounter prefix and one - ;; does not, but both refer to the same physical file. - (when (and outfile - (not - (if (memq system-type '(ms-dos windows-nt)) - (equal (downcase outfile) - (downcase (autoload-generated-file))) - (equal outfile (autoload-generated-file))))) - (setq otherbuf t)) - (save-excursion - (save-restriction - (widen) - (when autoload-builtin-package-versions - (let ((version (lm-header "version")) - package) - (and version - (setq version (ignore-errors (version-to-list version))) - (setq package (or (lm-header "package") - (file-name-sans-extension - (file-name-nondirectory file)))) - (setq output-start (autoload--setup-output - otherbuf outbuf absfile load-name)) - (let ((standard-output (marker-buffer output-start)) - (print-quoted t)) - (princ `(push (purecopy - ',(cons (intern package) version)) - package--builtin-versions)) - (princ "\n"))))) - - (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 - (setq output-start (autoload--setup-output - otherbuf outbuf absfile load-name))) - (autoload--print-cookie-text output-start load-name file)) - ((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 otherbuf (current-buffer)))) - (with-current-buffer (marker-buffer output-start) - (save-excursion - ;; Insert the section-header line which lists the file name - ;; and which functions are in it, etc. - (goto-char output-start) - (let ((relfile (file-relative-name absfile))) - (autoload-insert-section-header - (marker-buffer output-start) - () 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)))) - (or noninteractive - (message "Generating autoloads for %s...done" file))) - (or visited - ;; We created this buffer, so we should kill it. - (kill-buffer (current-buffer)))) - (or (not output-start) - ;; If the entries were added to some other buffer, then the file - ;; doesn't add entries to OUTFILE. - otherbuf)))) +\(or OUTBUF if OUTFILE is nil). The actual return value is +FILE's modification time." + (let (load-name + (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)) + ;; nil until we found a cookie. + output-start) + (when + (catch 'done + (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 + (or noninteractive (message "Generating autoloads for %s..." file)) + (setq load-name + (if (stringp generated-autoload-load-name) + generated-autoload-load-name + (autoload-file-load-name absfile))) + ;; FIXME? Comparing file-names for equality with just equal + ;; is fragile, eg if one has an automounter prefix and one + ;; does not, but both refer to the same physical file. + (when (and outfile + (not + (if (memq system-type '(ms-dos windows-nt)) + (equal (downcase outfile) + (downcase (autoload-generated-file))) + (equal outfile (autoload-generated-file))))) + (setq otherbuf t)) + (save-excursion + (save-restriction + (widen) + (when autoload-builtin-package-versions + (let ((version (lm-header "version")) + package) + (and version + (setq version (ignore-errors (version-to-list version))) + (setq package (or (lm-header "package") + (file-name-sans-extension + (file-name-nondirectory file)))) + (setq output-start (autoload--setup-output + otherbuf outbuf absfile load-name)) + (let ((standard-output (marker-buffer output-start)) + (print-quoted t)) + (princ `(push (purecopy + ',(cons (intern package) version)) + package--builtin-versions)) + (princ "\n"))))) + + (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 + (setq output-start (autoload--setup-output + otherbuf outbuf absfile load-name))) + (autoload--print-cookie-text output-start load-name file)) + ((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 otherbuf (current-buffer)))) + (with-current-buffer (marker-buffer output-start) + (save-excursion + ;; Insert the section-header line which lists the file name + ;; and which functions are in it, etc. + (goto-char output-start) + (let ((relfile (file-relative-name absfile))) + (autoload-insert-section-header + (marker-buffer output-start) + () 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)))) + (or noninteractive + (message "Generating autoloads for %s...done" file))) + (or visited + ;; We created this buffer, so we should kill it. + (kill-buffer (current-buffer)))) + (or (not output-start) + ;; If the entries were added to some other buffer, then the file + ;; doesn't add entries to OUTFILE. + otherbuf)) + (nth 5 (file-attributes absfile))))) (defun autoload-save-buffers () (while autoload-modified-buffers @@ -757,7 +760,7 @@ write its autoloads into the specified file instead." t files-re)) dirs))) (done ()) - (this-time (current-time)) + (last-time) ;; Files with no autoload cookies or whose autoloads go to other ;; files because of file-local autoload-generated-file settings. (no-autoloads nil) @@ -782,14 +785,14 @@ write its autoloads into the specified file instead." ;; 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))))))) + (setq 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. @@ -811,24 +814,28 @@ write its autoloads into the specified file instead." (push file done) (setq files (delete file files))))) ;; Elements remaining in FILES have no existing autoload sections yet. - (dolist (file files) - (cond - ((member (expand-file-name file) autoload-excludes) nil) - ;; Passing nil as second argument forces - ;; autoload-generate-file-autoloads to look for the right - ;; spot where to insert each autoloads section. - ((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)) + (let ((no-autoloads-time (or last-time '(0 0 0 0))) file-time) + (dolist (file files) + (cond + ((member (expand-file-name file) autoload-excludes) nil) + ;; Passing nil as second argument forces + ;; autoload-generate-file-autoloads to look for the right + ;; spot where to insert each autoloads section. + ((setq file-time + (autoload-generate-file-autoloads file nil buffer-file-name)) + (push file no-autoloads) + (if (time-less-p no-autoloads-time file-time) + (setq no-autoloads-time file-time))))) + + (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 no-autoloads-time) + (insert generate-autoload-section-trailer))) (let ((version-control 'never)) (save-buffer)) -- 2.11.4.GIT