(defconst generate-autoload-section-continuation ";;;;;; "
"String to add on each continuation of the section header form.")
+(defvar autoload-timestamps nil ; experimental, see bug#22213
+ "Non-nil means insert a timestamp for each input file into the output.
+We use these in incremental updates of the output file to decide
+if we need to rescan an input file. If you set this to nil,
+then we use the timestamp of the output file instead. As a result:
+ - for fixed inputs, the output will be the same every time
+ - incremental updates of the output file might not be correct if:
+ i) the timestamp of the output file cannot be trusted (at least
+ relative to that of the input files)
+ ii) any of the input files can be modified during the time it takes
+ to create the output
+ iii) only a subset of the input files are scanned
+ These issues are unlikely to happen in practice, and would arguably
+ represent bugs in the build system. Item iii) will happen if you
+ use a command like `update-file-autoloads', though, since it only
+ checks a single input file.")
+
(defvar autoload-modified-buffers) ;Dynamically scoped var.
(defun make-autoload (form file &optional expansion)
(enable-local-eval nil))
;; We used to use `raw-text' to read this file, but this causes
;; problems when the file contains non-ASCII characters.
- (let* ((delay-mode-hooks t)
- (file (autoload-generated-file))
- (file-missing (not (file-exists-p file))))
- (when file-missing
- (autoload-ensure-default-file file))
- (with-current-buffer
- (find-file-noselect
- (autoload-ensure-file-writeable
- file))
- ;; block backups when the file has just been created, since
- ;; the backups will just be the auto-generated headers.
- ;; bug#23203
- (when file-missing
- (setq buffer-backed-up t)
- (save-buffer))
- (current-buffer)))))
+ (let ((delay-mode-hooks t))
+ (find-file-noselect
+ (autoload-ensure-default-file (autoload-generated-file))))))
(defun autoload-generated-file ()
(expand-file-name generated-autoload-file
;;;###autoload
(put 'autoload-ensure-writable 'risky-local-variable t)
- (defun autoload-ensure-file-writeable (file)
- ;; Probably pointless, but replaces the old AUTOGEN_VCS in lisp/Makefile,
- ;; which was designed to handle CVSREAD=1 and equivalent.
- (and autoload-ensure-writable
- (let ((modes (file-modes file)))
- (if (zerop (logand modes #o0200))
- ;; Ignore any errors here, and let subsequent attempts
- ;; to write the file raise any real error.
- (ignore-errors (set-file-modes file (logior modes #o0200))))))
- file)
-
(defun autoload-ensure-default-file (file)
"Make sure that the autoload file FILE exists, creating it if needed.
If the file already exists and `autoload-ensure-writable' is non-nil,
make it writable."
- (write-region (autoload-rubric file) nil file))
+ (if (file-exists-p file)
+ ;; Probably pointless, but replaces the old AUTOGEN_VCS in lisp/Makefile,
+ ;; which was designed to handle CVSREAD=1 and equivalent.
+ (and autoload-ensure-writable
+ (let ((modes (file-modes file)))
+ (if (zerop (logand modes #o0200))
+ ;; Ignore any errors here, and let subsequent attempts
+ ;; to write the file raise any real error.
+ (ignore-errors (set-file-modes file (logior modes #o0200))))))
+ (write-region (autoload-rubric file) nil file))
+ file)
(defun autoload-insert-section-header (outbuf autoloads load-name file time)
"Insert the section-header line,
;; We'd really want to just use
;; `emacs-internal' instead.
nil nil 'emacs-mule-unix)
- (nth 5 (file-attributes relfile))))
+ (if autoload-timestamps
+ (nth 5 (file-attributes relfile))
+ t)))
(insert ";;; Generated autoloads from " relfile "\n")))
(insert generate-autoload-section-trailer))))
(or noninteractive
(let ((version-control 'never))
(save-buffer)))))
+;; FIXME This command should be deprecated.
+;; See http://debbugs.gnu.org/22213#41
;;;###autoload
(defun update-file-autoloads (file &optional save-after outfile)
"Update the autoloads for FILE.
(read-file-name "Write autoload definitions to file: ")))
(let* ((generated-autoload-file (or outfile generated-autoload-file))
(autoload-modified-buffers nil)
+ ;; We need this only if the output file handles more than one input.
+ ;; See http://debbugs.gnu.org/22213#38 and subsequent.
+ (autoload-timestamps t)
(no-autoloads (autoload-generate-file-autoloads file)))
(if autoload-modified-buffers
(if save-after (autoload-save-buffers))
(catch 'up-to-date
(let* ((buf (current-buffer))
(existing-buffer (if buffer-file-name buf))
+ (output-file (autoload-generated-file))
+ (output-time (if (file-exists-p output-file)
+ (nth 5 (file-attributes output-file))))
(found nil))
(with-current-buffer (autoload-find-generated-file)
;; This is to make generated-autoload-file have Unix EOLs, so
(file-time (nth 5 (file-attributes file))))
(if (and (or (null existing-buffer)
(not (buffer-modified-p existing-buffer)))
- (or
+ (cond
;; 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)
- (not (time-less-p last-time file-time)))
+ ((listp last-time)
+ (not (time-less-p last-time file-time)))
+ ;; FIXME? Arguably we should throw a
+ ;; user error, or some kind of warning,
+ ;; if we were called from update-file-autoloads,
+ ;; which can update only a single input file.
+ ;; It's not appropriate to use the output
+ ;; file modtime in such a case,
+ ;; if there are multiple input files
+ ;; contributing to the output.
+ ((and output-time (eq t last-time))
+ (not (time-less-p output-time file-time)))
;; last-time is an MD5 checksum instead.
- (and (stringp last-time)
- (equal last-time
- (md5 buf nil nil 'emacs-mule)))))
+ ((stringp last-time)
+ (equal last-time
+ (md5 buf nil nil 'emacs-mule)))))
(throw 'up-to-date nil)
(autoload-remove-section begin)
(setq found t))))
(generated-autoload-file
(if (called-interactively-p 'interactive)
(read-file-name "Write autoload definitions to file: ")
- generated-autoload-file)))
+ generated-autoload-file))
+ (output-time
+ (if (file-exists-p generated-autoload-file)
+ (nth 5 (file-attributes generated-autoload-file)))))
(with-current-buffer (autoload-find-generated-file)
(save-excursion
;; Remove the obsolete section.
(autoload-remove-section (match-beginning 0))
(setq last-time (nth 4 form))
+ (if (equal t last-time)
+ (setq last-time output-time))
(dolist (file file)
(let ((file-time (nth 5 (file-attributes file))))
(when (and file-time
(member (expand-file-name file) autoload-excludes))
;; Remove the obsolete section.
(autoload-remove-section (match-beginning 0)))
- ((not (time-less-p (nth 4 form)
+ ((not (time-less-p (let ((oldtime (nth 4 form)))
+ (if (equal t oldtime)
+ output-time
+ oldtime))
(nth 5 (file-attributes file))))
;; File hasn't changed.
nil)
(goto-char (point-max))
(search-backward "\f" nil t)
(autoload-insert-section-header
- (current-buffer) nil nil no-autoloads no-autoloads-time)
+ (current-buffer) nil nil no-autoloads (if autoload-timestamps
+ no-autoloads-time
+ t))
(insert generate-autoload-section-trailer)))
(let ((version-control 'never))
#include "coding.h"
#include "buffer.h"
#include "disptab.h"
+ #include "intervals.h"
#include "keymap.h"
/* Buffer used for reading from documentation file. */
{
char *buf;
bool changed = false;
+ bool nonquotes_changed = false;
unsigned char *strp;
char *bufp;
ptrdiff_t idx;
{
/* \= quotes the next character;
thus, to put in \[ without its special meaning, use \=\[. */
- changed = true;
+ changed = nonquotes_changed = true;
strp += 2;
if (multibyte)
{
\<foo> just sets the keymap used for \[cmd]. */
else if (strp[0] == '\\' && (strp[1] == '{' || strp[1] == '<'))
{
- struct buffer *oldbuf;
- ptrdiff_t start_idx;
+ {
/* This is for computing the SHADOWS arg for describe_map_tree. */
Lisp_Object active_maps = Fcurrent_active_maps (Qnil, Qnil);
- Lisp_Object earlier_maps;
ptrdiff_t count = SPECPDL_INDEX ();
strp += 2; /* skip \{ or \< */
start = strp;
- start_idx = start - SDATA (string);
+ ptrdiff_t start_idx = start - SDATA (string);
while ((strp - SDATA (string) < SBYTES (string))
&& *strp != '}' && *strp != '>')
}
/* Now switch to a temp buffer. */
- oldbuf = current_buffer;
+ struct buffer *oldbuf = current_buffer;
set_buffer_internal (XBUFFER (Vprin1_to_string_buffer));
/* This is for an unusual case where some after-change
function uses 'format' or 'prin1' or something else that
{
/* Get the list of active keymaps that precede this one.
If this one's not active, get nil. */
- earlier_maps = Fcdr (Fmemq (tem, Freverse (active_maps)));
+ Lisp_Object earlier_maps
+ = Fcdr (Fmemq (tem, Freverse (active_maps)));
describe_map_tree (tem, 1, Fnreverse (earlier_maps),
Qnil, 0, 1, 0, 0, 1);
}
Ferase_buffer ();
set_buffer_internal (oldbuf);
unbind_to (count, Qnil);
+ }
subst_string:
start = SDATA (tem);
length = SCHARS (tem);
length_byte = SBYTES (tem);
subst:
+ nonquotes_changed = true;
+ subst_quote:
changed = true;
{
ptrdiff_t offset = bufp - buf;
length = 1;
length_byte = sizeof uLSQM - 1;
idx = strp - SDATA (string) + 1;
- goto subst;
+ goto subst_quote;
}
else if (strp[0] == '`' && quoting_style == STRAIGHT_QUOTING_STYLE)
{
}
if (changed) /* don't bother if nothing substituted */
- tem = make_string_from_bytes (buf, nchars, bufp - buf);
+ {
+ tem = make_string_from_bytes (buf, nchars, bufp - buf);
+ if (!nonquotes_changed)
+ {
+ /* Nothing has changed other than quoting, so copy the string’s
+ text properties. FIXME: Text properties should survive other
+ changes too. */
+ INTERVAL interval_copy = copy_intervals (string_intervals (string),
+ 0, SCHARS (string));
+ if (interval_copy)
+ {
+ set_interval_object (interval_copy, tem);
+ set_string_intervals (tem, interval_copy);
+ }
+ }
+ }
else
tem = string;
xfree (buf);