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
This commit is contained in:
Glenn Morris 2015-06-30 14:59:04 -04:00
parent 0dfea4562e
commit 5200c2baef

View file

@ -522,116 +522,119 @@ If OUTFILE is non-nil and FILE specifies a `generated-autoload-file'
different from OUTFILE, then OUTBUF is ignored. different from OUTFILE, then OUTBUF is ignored.
Return non-nil if and only if FILE adds no autoloads to OUTFILE Return non-nil if and only if FILE adds no autoloads to OUTFILE
\(or OUTBUF if OUTFILE is nil)." \(or OUTBUF if OUTFILE is nil). The actual return value is
(catch 'done FILE's modification time."
(let (load-name (let (load-name
(print-length nil) (print-length nil)
(print-level nil) (print-level nil)
(print-readably t) ; This does something in Lucid Emacs. (print-readably t) ; This does something in Lucid Emacs.
(float-output-format nil) (float-output-format nil)
(visited (get-file-buffer file)) (visited (get-file-buffer file))
(otherbuf nil) (otherbuf nil)
(absfile (expand-file-name file)) (absfile (expand-file-name file))
;; nil until we found a cookie. ;; nil until we found a cookie.
output-start) output-start)
(with-current-buffer (or visited (when
;; It is faster to avoid visiting the file. (catch 'done
(autoload-find-file file)) (with-current-buffer (or visited
;; Obey the no-update-autoloads file local variable. ;; It is faster to avoid visiting the file.
(unless no-update-autoloads (autoload-find-file file))
(or noninteractive (message "Generating autoloads for %s..." file)) ;; Obey the no-update-autoloads file local variable.
(setq load-name (unless no-update-autoloads
(if (stringp generated-autoload-load-name) (or noninteractive (message "Generating autoloads for %s..." file))
generated-autoload-load-name (setq load-name
(autoload-file-load-name absfile))) (if (stringp generated-autoload-load-name)
;; FIXME? Comparing file-names for equality with just equal generated-autoload-load-name
;; is fragile, eg if one has an automounter prefix and one (autoload-file-load-name absfile)))
;; does not, but both refer to the same physical file. ;; FIXME? Comparing file-names for equality with just equal
(when (and outfile ;; is fragile, eg if one has an automounter prefix and one
(not ;; does not, but both refer to the same physical file.
(if (memq system-type '(ms-dos windows-nt)) (when (and outfile
(equal (downcase outfile) (not
(downcase (autoload-generated-file))) (if (memq system-type '(ms-dos windows-nt))
(equal outfile (autoload-generated-file))))) (equal (downcase outfile)
(setq otherbuf t)) (downcase (autoload-generated-file)))
(save-excursion (equal outfile (autoload-generated-file)))))
(save-restriction (setq otherbuf t))
(widen) (save-excursion
(when autoload-builtin-package-versions (save-restriction
(let ((version (lm-header "version")) (widen)
package) (when autoload-builtin-package-versions
(and version (let ((version (lm-header "version"))
(setq version (ignore-errors (version-to-list version))) package)
(setq package (or (lm-header "package") (and version
(file-name-sans-extension (setq version (ignore-errors (version-to-list version)))
(file-name-nondirectory file)))) (setq package (or (lm-header "package")
(setq output-start (autoload--setup-output (file-name-sans-extension
otherbuf outbuf absfile load-name)) (file-name-nondirectory file))))
(let ((standard-output (marker-buffer output-start)) (setq output-start (autoload--setup-output
(print-quoted t)) otherbuf outbuf absfile load-name))
(princ `(push (purecopy (let ((standard-output (marker-buffer output-start))
',(cons (intern package) version)) (print-quoted t))
package--builtin-versions)) (princ `(push (purecopy
(princ "\n"))))) ',(cons (intern package) version))
package--builtin-versions))
(princ "\n")))))
(goto-char (point-min)) (goto-char (point-min))
(while (not (eobp)) (while (not (eobp))
(skip-chars-forward " \t\n\f") (skip-chars-forward " \t\n\f")
(cond (cond
((looking-at (regexp-quote generate-autoload-cookie)) ((looking-at (regexp-quote generate-autoload-cookie))
;; If not done yet, figure out where to insert this text. ;; If not done yet, figure out where to insert this text.
(unless output-start (unless output-start
(setq output-start (autoload--setup-output (setq output-start (autoload--setup-output
otherbuf outbuf absfile load-name))) otherbuf outbuf absfile load-name)))
(autoload--print-cookie-text output-start load-name file)) (autoload--print-cookie-text output-start load-name file))
((looking-at ";") ((looking-at ";")
;; Don't read the comment. ;; Don't read the comment.
(forward-line 1)) (forward-line 1))
(t (t
(forward-sexp 1) (forward-sexp 1)
(forward-line 1)))))) (forward-line 1))))))
(when output-start (when output-start
(let ((secondary-autoloads-file-buf (let ((secondary-autoloads-file-buf
(if otherbuf (current-buffer)))) (if otherbuf (current-buffer))))
(with-current-buffer (marker-buffer output-start) (with-current-buffer (marker-buffer output-start)
(save-excursion (save-excursion
;; Insert the section-header line which lists the file name ;; Insert the section-header line which lists the file name
;; and which functions are in it, etc. ;; and which functions are in it, etc.
(goto-char output-start) (goto-char output-start)
(let ((relfile (file-relative-name absfile))) (let ((relfile (file-relative-name absfile)))
(autoload-insert-section-header (autoload-insert-section-header
(marker-buffer output-start) (marker-buffer output-start)
() load-name relfile () load-name relfile
(if secondary-autoloads-file-buf (if secondary-autoloads-file-buf
;; MD5 checksums are much better because they do not ;; MD5 checksums are much better because they do not
;; change unless the file changes (so they'll be ;; change unless the file changes (so they'll be
;; equal on two different systems and will change ;; equal on two different systems and will change
;; less often than time-stamps, thus leading to fewer ;; less often than time-stamps, thus leading to fewer
;; unneeded changes causing spurious conflicts), but ;; unneeded changes causing spurious conflicts), but
;; using time-stamps is a very useful optimization, ;; using time-stamps is a very useful optimization,
;; so we use time-stamps for the main autoloads file ;; so we use time-stamps for the main autoloads file
;; (loaddefs.el) where we have special ways to ;; (loaddefs.el) where we have special ways to
;; circumvent the "random change problem", and MD5 ;; circumvent the "random change problem", and MD5
;; checksum in secondary autoload files where we do ;; checksum in secondary autoload files where we do
;; not need the time-stamp optimization because it is ;; not need the time-stamp optimization because it is
;; already provided by the primary autoloads file. ;; already provided by the primary autoloads file.
(md5 secondary-autoloads-file-buf (md5 secondary-autoloads-file-buf
;; We'd really want to just use ;; We'd really want to just use
;; `emacs-internal' instead. ;; `emacs-internal' instead.
nil nil 'emacs-mule-unix) nil nil 'emacs-mule-unix)
(nth 5 (file-attributes relfile)))) (nth 5 (file-attributes relfile))))
(insert ";;; Generated autoloads from " relfile "\n"))) (insert ";;; Generated autoloads from " relfile "\n")))
(insert generate-autoload-section-trailer)))) (insert generate-autoload-section-trailer))))
(or noninteractive (or noninteractive
(message "Generating autoloads for %s...done" file))) (message "Generating autoloads for %s...done" file)))
(or visited (or visited
;; We created this buffer, so we should kill it. ;; We created this buffer, so we should kill it.
(kill-buffer (current-buffer)))) (kill-buffer (current-buffer))))
(or (not output-start) (or (not output-start)
;; If the entries were added to some other buffer, then the file ;; If the entries were added to some other buffer, then the file
;; doesn't add entries to OUTFILE. ;; doesn't add entries to OUTFILE.
otherbuf)))) otherbuf))
(nth 5 (file-attributes absfile)))))
(defun autoload-save-buffers () (defun autoload-save-buffers ()
(while autoload-modified-buffers (while autoload-modified-buffers
@ -757,7 +760,7 @@ write its autoloads into the specified file instead."
t files-re)) t files-re))
dirs))) dirs)))
(done ()) (done ())
(this-time (current-time)) (last-time)
;; Files with no autoload cookies or whose autoloads go to other ;; Files with no autoload cookies or whose autoloads go to other
;; files because of file-local autoload-generated-file settings. ;; files because of file-local autoload-generated-file settings.
(no-autoloads nil) (no-autoloads nil)
@ -782,14 +785,14 @@ write its autoloads into the specified file instead."
;; There shouldn't be more than one such entry. ;; There shouldn't be more than one such entry.
;; Remove the obsolete section. ;; Remove the obsolete section.
(autoload-remove-section (match-beginning 0)) (autoload-remove-section (match-beginning 0))
(let ((last-time (nth 4 form))) (setq last-time (nth 4 form))
(dolist (file file) (dolist (file file)
(let ((file-time (nth 5 (file-attributes file)))) (let ((file-time (nth 5 (file-attributes file))))
(when (and file-time (when (and file-time
(not (time-less-p last-time file-time))) (not (time-less-p last-time file-time)))
;; file unchanged ;; file unchanged
(push file no-autoloads) (push file no-autoloads)
(setq files (delete file files))))))) (setq files (delete file files))))))
((not (stringp file))) ((not (stringp file)))
((or (not (file-exists-p file)) ((or (not (file-exists-p file))
;; Remove duplicates as well, just in case. ;; Remove duplicates as well, just in case.
@ -811,24 +814,28 @@ write its autoloads into the specified file instead."
(push file done) (push file done)
(setq files (delete file files))))) (setq files (delete file files)))))
;; Elements remaining in FILES have no existing autoload sections yet. ;; Elements remaining in FILES have no existing autoload sections yet.
(dolist (file files) (let ((no-autoloads-time (or last-time '(0 0 0 0))) file-time)
(cond (dolist (file files)
((member (expand-file-name file) autoload-excludes) nil) (cond
;; Passing nil as second argument forces ((member (expand-file-name file) autoload-excludes) nil)
;; autoload-generate-file-autoloads to look for the right ;; Passing nil as second argument forces
;; spot where to insert each autoloads section. ;; autoload-generate-file-autoloads to look for the right
((autoload-generate-file-autoloads file nil buffer-file-name) ;; spot where to insert each autoloads section.
(push file no-autoloads)))) ((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 (when no-autoloads
;; Sort them for better readability. ;; Sort them for better readability.
(setq no-autoloads (sort no-autoloads 'string<)) (setq no-autoloads (sort no-autoloads 'string<))
;; Add the `no-autoloads' section. ;; Add the `no-autoloads' section.
(goto-char (point-max)) (goto-char (point-max))
(search-backward "\f" nil t) (search-backward "\f" nil t)
(autoload-insert-section-header (autoload-insert-section-header
(current-buffer) nil nil no-autoloads this-time) (current-buffer) nil nil no-autoloads no-autoloads-time)
(insert generate-autoload-section-trailer)) (insert generate-autoload-section-trailer)))
(let ((version-control 'never)) (let ((version-control 'never))
(save-buffer)) (save-buffer))