Add `org-compile-file' and `org-file-newer-than-p'
* lisp/org.el (org-compile-file): (org-file-newer-than-p): New functions. * lisp/ox-latex.el (org-latex-compile): Use new functions. * lisp/ox-man.el (org-man-compile): Use new functions. (org-man-collect-errors): Remove it. * lisp/ox-texinfo.el (org-texinfo-compile): Use new functions. (org-texinfo-collect-errors): Remove function as it is not accurate enough (e.g., it doesn't handle internationalization).
This commit is contained in:
parent
14f853dceb
commit
ea1147a479
67
lisp/org.el
67
lisp/org.el
|
@ -22852,6 +22852,73 @@ hierarchy of headlines by UP levels before marking the subtree."
|
|||
(call-interactively 'org-mark-element)
|
||||
(org-mark-element)))
|
||||
|
||||
(defun org-file-newer-than-p (file time)
|
||||
"Non-nil if FILE is newer than TIME.
|
||||
FILE is a filename, as a string, TIME is a list of integers, as
|
||||
returned by, e.g., `current-time'."
|
||||
(and (file-exists-p file)
|
||||
;; Only compare times up to whole seconds as some file-systems
|
||||
;; (e.g. HFS+) do not retain any finer granularity. As
|
||||
;; a consequence, make sure we return non-nil when the two
|
||||
;; times are equal.
|
||||
(not (time-less-p (cl-subseq (nth 5 (file-attributes file)) 0 2)
|
||||
(cl-subseq time 0 2)))))
|
||||
|
||||
(defun org-compile-file (source process ext &optional err-msg log-buf spec)
|
||||
"Compile a SOURCE file using PROCESS.
|
||||
|
||||
PROCESS is either a function or a list of shell commands, as
|
||||
strings. EXT is a file extension, without the leading dot, as
|
||||
a string. It is used to check if the process actually succeeded.
|
||||
|
||||
PROCESS must create a file with the same base name and directory
|
||||
as SOURCE, but ending with EXT. The function then returns its
|
||||
filename. Otherwise, it raises an error. The error message can
|
||||
then be refined by providing string ERR-MSG, which is appended to
|
||||
the standard message.
|
||||
|
||||
If PROCESS is a function, it is called with a single argument:
|
||||
the SOURCE file.
|
||||
|
||||
If it is a list of commands, each of them is called using
|
||||
`shell-command'. By default, in each command, %b, %f and %o are
|
||||
replaced, respectively, with SOURCE base name, SOURCE full name
|
||||
and SOURCE directory. It is possible, however, to use more
|
||||
place-holders by specifying them in optional argument SPEC, as an
|
||||
alist following the pattern (CHARACTER . REPLACEMENT-STRING).
|
||||
|
||||
When PROCESS is a list of commands, optional argument LOG-BUF can
|
||||
be set to a buffer or a buffer name. `shell-command' then uses
|
||||
it for output.
|
||||
|
||||
`default-directory' is set to SOURCE directory during the whole
|
||||
process."
|
||||
(let* ((base-name (file-name-sans-extension (file-name-nondirectory source)))
|
||||
(full-name (file-truename source))
|
||||
(out-dir (file-name-directory source))
|
||||
;; Properly set working directory for compilation.
|
||||
(default-directory (if (file-name-absolute-p source)
|
||||
(file-name-directory full-name)
|
||||
default-directory))
|
||||
(time (current-time))
|
||||
(err-msg (if (stringp err-msg) (concat ". " err-msg) "")))
|
||||
(save-window-excursion
|
||||
(pcase process
|
||||
((pred functionp) (funcall process (shell-quote-argument source)))
|
||||
((pred consp)
|
||||
(let ((log-buf (and log-buf (get-buffer-create log-buf)))
|
||||
(spec (append spec
|
||||
`((?b . ,(shell-quote-argument base-name))
|
||||
(?f . ,(shell-quote-argument full-name))
|
||||
(?o . ,(shell-quote-argument out-dir))))))
|
||||
(dolist (command process)
|
||||
(shell-command (format-spec command spec) log-buf))))
|
||||
(_ (error "No valid command to process %S%s" source err-msg)))
|
||||
;; Check for process failure.
|
||||
(let ((output (concat out-dir base-name "." ext)))
|
||||
(unless (org-file-newer-than-p output time)
|
||||
(error (format "File %S wasn't produced%s" output err-msg)))
|
||||
output))))
|
||||
|
||||
;;; Indentation
|
||||
|
||||
|
|
126
lisp/ox-latex.el
126
lisp/ox-latex.el
|
@ -3519,87 +3519,59 @@ Return PDF file's name."
|
|||
"Compile a TeX file.
|
||||
|
||||
TEXFILE is the name of the file being compiled. Processing is
|
||||
done through the command specified in `org-latex-pdf-process'.
|
||||
done through the command specified in `org-latex-pdf-process',
|
||||
which see. Output is redirected to \"*Org PDF LaTeX Output*\"
|
||||
buffer.
|
||||
|
||||
When optional argument SNIPPET is non-nil, TEXFILE is a temporary
|
||||
file used to preview a LaTeX snippet. In this case, do not
|
||||
create a log buffer and do not bother removing log files.
|
||||
create a log buffer and do not remove log files.
|
||||
|
||||
Return PDF file name or an error if it couldn't be produced."
|
||||
(let* ((base-name (file-name-sans-extension (file-name-nondirectory texfile)))
|
||||
(full-name (file-truename texfile))
|
||||
(compiler (or (with-temp-buffer
|
||||
(save-excursion (insert-file-contents full-name))
|
||||
(when (and (search-forward-regexp
|
||||
(regexp-opt org-latex-compilers) (line-end-position 2) t)
|
||||
(progn (beginning-of-line)
|
||||
(looking-at-p "%")))
|
||||
(match-string 0)))
|
||||
"pdflatex"))
|
||||
(out-dir (file-name-directory texfile))
|
||||
;; Properly set working directory for compilation.
|
||||
(default-directory (if (file-name-absolute-p texfile)
|
||||
(file-name-directory full-name)
|
||||
default-directory))
|
||||
(time (current-time))
|
||||
warnings)
|
||||
(unless snippet (message "Processing LaTeX file %s..." texfile))
|
||||
(save-window-excursion
|
||||
(cond
|
||||
;; A function is provided: Apply it.
|
||||
((functionp org-latex-pdf-process)
|
||||
(funcall org-latex-pdf-process (shell-quote-argument texfile)))
|
||||
;; A list is provided: Replace %b, %f and %o with appropriate
|
||||
;; values in each command before applying it. Note that while
|
||||
;; "%latex" and "%bibtex" is used in `org-latex-pdf-process',
|
||||
;; they are replaced with "%L" and "%B" to adhere to
|
||||
;; format-spec. Output is redirected to "*Org PDF LaTeX
|
||||
;; Output*" buffer.
|
||||
((consp org-latex-pdf-process)
|
||||
(let ((outbuf (and (not snippet)
|
||||
(get-buffer-create "*Org PDF LaTeX Output*")))
|
||||
(spec (list (cons ?B (shell-quote-argument org-latex-bib-compiler))
|
||||
(cons ?L (shell-quote-argument compiler))
|
||||
(cons ?b (shell-quote-argument base-name))
|
||||
(cons ?f (shell-quote-argument full-name))
|
||||
(cons ?o (shell-quote-argument out-dir)))))
|
||||
(dolist (command org-latex-pdf-process)
|
||||
(let ((c (replace-regexp-in-string
|
||||
"%\\(latex\\|bibtex\\)\\>"
|
||||
(lambda (str) (upcase (substring str 0 2)))
|
||||
command)))
|
||||
(shell-command (format-spec c spec) outbuf)))
|
||||
;; Collect standard errors from output buffer.
|
||||
(setq warnings (and (not snippet)
|
||||
(org-latex--collect-warnings outbuf)))))
|
||||
(t (error "No valid command to process to PDF")))
|
||||
(let ((pdffile (concat out-dir base-name ".pdf")))
|
||||
;; Check for process failure. Provide collected errors if
|
||||
;; possible.
|
||||
(if (or (not (file-exists-p pdffile))
|
||||
;; Only compare times up to whole seconds as some filesystems
|
||||
;; (e.g. HFS+) do not retain any finer granularity.
|
||||
(time-less-p (cl-subseq (nth 5 (file-attributes pdffile)) 0 2)
|
||||
(cl-subseq time 0 2)))
|
||||
(error (format "PDF file %s wasn't produced" pdffile))
|
||||
;; Else remove log files, when specified, and signal end of
|
||||
;; process to user, along with any error encountered.
|
||||
(unless snippet
|
||||
(when org-latex-remove-logfiles
|
||||
(dolist (file (directory-files
|
||||
out-dir t
|
||||
(concat (regexp-quote base-name)
|
||||
"\\(?:\\.[0-9]+\\)?"
|
||||
"\\."
|
||||
(regexp-opt org-latex-logfiles-extensions))))
|
||||
(delete-file file)))
|
||||
(message (concat "PDF file produced"
|
||||
(cond
|
||||
((eq warnings 'error) " with errors.")
|
||||
(warnings (concat " with warnings: " warnings))
|
||||
(t "."))))))
|
||||
;; Return output file name.
|
||||
pdffile))))
|
||||
Return PDF file name or raise an error if it couldn't be
|
||||
produced."
|
||||
(unless snippet (message "Processing LaTeX file %s..." texfile))
|
||||
(let* ((compiler
|
||||
(or (with-temp-buffer
|
||||
(save-excursion (insert-file-contents texfile))
|
||||
(and (search-forward-regexp (regexp-opt org-latex-compilers)
|
||||
(line-end-position 2)
|
||||
t)
|
||||
(progn (beginning-of-line) (looking-at-p "%"))
|
||||
(match-string 0)))
|
||||
"pdflatex"))
|
||||
(process (if (functionp org-latex-pdf-process) org-latex-pdf-process
|
||||
;; Replace "%latex" and "%bibtex" with,
|
||||
;; respectively, "%L" and "%B" so as to adhere to
|
||||
;; `format-spec' specifications.
|
||||
(mapcar (lambda (command)
|
||||
(replace-regexp-in-string
|
||||
"%\\(?:bib\\|la\\)tex\\>"
|
||||
(lambda (m) (upcase (substring m 0 2)))
|
||||
command))
|
||||
org-latex-pdf-process)))
|
||||
(spec `((?B . ,(shell-quote-argument org-latex-bib-compiler))
|
||||
(?L . ,(shell-quote-argument compiler))))
|
||||
(log-buf-name "*Org PDF LaTeX Output*")
|
||||
(log-buf (and (not snippet) (get-buffer-create log-buf-name)))
|
||||
(outfile (org-compile-file texfile process "pdf"
|
||||
(format "See %S for details" log-buf-name)
|
||||
log-buf spec)))
|
||||
(unless snippet
|
||||
(when org-latex-remove-logfiles
|
||||
(mapc #'delete-file
|
||||
(directory-files
|
||||
(file-name-directory texfile) t
|
||||
(concat (regexp-quote (file-name-base outfile))
|
||||
"\\(?:\\.[0-9]+\\)?\\."
|
||||
(regexp-opt org-latex-logfiles-extensions)))))
|
||||
(let ((warnings (org-latex--collect-warnings log-buf)))
|
||||
(message (concat "PDF file produced"
|
||||
(cond
|
||||
((eq warnings 'error) " with errors.")
|
||||
(warnings (concat " with warnings: " warnings))
|
||||
(t "."))))))
|
||||
;; Return output file name.
|
||||
outfile))
|
||||
|
||||
(defun org-latex--collect-warnings (buffer)
|
||||
"Collect some warnings from \"pdflatex\" command output.
|
||||
|
|
|
@ -1127,73 +1127,15 @@ FILE is the name of the file being compiled. Processing is done
|
|||
through the command specified in `org-man-pdf-process'.
|
||||
|
||||
Return PDF file name or an error if it couldn't be produced."
|
||||
(let* ((base-name (file-name-sans-extension (file-name-nondirectory file)))
|
||||
(full-name (file-truename file))
|
||||
(out-dir (file-name-directory file))
|
||||
(time (current-time))
|
||||
;; Properly set working directory for compilation.
|
||||
(default-directory (if (file-name-absolute-p file)
|
||||
(file-name-directory full-name)
|
||||
default-directory))
|
||||
errors)
|
||||
(message "Processing Groff file %s..." file)
|
||||
(save-window-excursion
|
||||
(cond
|
||||
;; A function is provided: Apply it.
|
||||
((functionp org-man-pdf-process)
|
||||
(funcall org-man-pdf-process (shell-quote-argument file)))
|
||||
;; A list is provided: Replace %b, %f and %o with appropriate
|
||||
;; values in each command before applying it. Output is
|
||||
;; redirected to "*Org PDF Groff Output*" buffer.
|
||||
((consp org-man-pdf-process)
|
||||
(let ((outbuf (get-buffer-create "*Org PDF Groff Output*")))
|
||||
(dolist (command org-man-pdf-process)
|
||||
(shell-command
|
||||
(replace-regexp-in-string
|
||||
"%b" (shell-quote-argument base-name)
|
||||
(replace-regexp-in-string
|
||||
"%f" (shell-quote-argument full-name)
|
||||
(replace-regexp-in-string
|
||||
"%o" (shell-quote-argument out-dir) command t t) t t) t t)
|
||||
outbuf))
|
||||
;; Collect standard errors from output buffer.
|
||||
(setq errors (org-man-collect-errors outbuf))))
|
||||
(t (error "No valid command to process to PDF")))
|
||||
(let ((pdffile (concat out-dir base-name ".pdf")))
|
||||
;; Check for process failure. Provide collected errors if
|
||||
;; possible.
|
||||
(if (or (not (file-exists-p pdffile))
|
||||
;; Only compare times up to whole seconds as some
|
||||
;; filesystems (e.g. HFS+) do not retain any finer
|
||||
;; granularity.
|
||||
(time-less-p (cl-subseq (nth 5 (file-attributes pdffile)) 0 2)
|
||||
(cl-subseq time 0 2)))
|
||||
(error "PDF file %s wasn't produced%s"
|
||||
pdffile
|
||||
(if errors (concat ": " errors) ""))
|
||||
;; Else remove log files, when specified, and signal end of
|
||||
;; process to user, along with any error encountered.
|
||||
(when org-man-remove-logfiles
|
||||
(dolist (ext org-man-logfiles-extensions)
|
||||
(let ((file (concat out-dir base-name "." ext)))
|
||||
(when (file-exists-p file) (delete-file file)))))
|
||||
(message (concat "Process completed"
|
||||
(if (not errors) "."
|
||||
(concat " with errors: " errors)))))
|
||||
;; Return output file name.
|
||||
pdffile))))
|
||||
|
||||
(defun org-man-collect-errors (buffer)
|
||||
"Collect some kind of errors from \"groff\" output
|
||||
BUFFER is the buffer containing output.
|
||||
Return collected error types as a string, or nil if there was
|
||||
none."
|
||||
(with-current-buffer buffer
|
||||
(save-excursion
|
||||
(goto-char (point-max))
|
||||
;; Find final run
|
||||
nil )))
|
||||
|
||||
(message "Processing Groff file %s..." file)
|
||||
(let ((output (org-compile-file file org-man-pdf-process "pdf")))
|
||||
(when org-man-remove-logfiles
|
||||
(let ((base (file-name-sans-extension output)))
|
||||
(dolist (ext org-man-logfiles-extensions)
|
||||
(let ((file (concat base "." ext)))
|
||||
(when (file-exists-p file) (delete-file file))))))
|
||||
(message "Process completed.")
|
||||
output))
|
||||
|
||||
(provide 'ox-man)
|
||||
|
||||
|
|
|
@ -1563,96 +1563,26 @@ this command to convert it."
|
|||
(defun org-texinfo-compile (file)
|
||||
"Compile a texinfo file.
|
||||
|
||||
FILE is the name of the file being compiled. Processing is
|
||||
done through the command specified in `org-texinfo-info-process'.
|
||||
FILE is the name of the file being compiled. Processing is done
|
||||
through the command specified in `org-texinfo-info-process',
|
||||
which see. Output is redirected to \"*Org INFO Texinfo Output*\"
|
||||
buffer.
|
||||
|
||||
Return INFO file name or an error if it couldn't be produced."
|
||||
(let* ((base-name (file-name-sans-extension (file-name-nondirectory file)))
|
||||
(full-name (file-truename file))
|
||||
(out-dir (file-name-directory file))
|
||||
(time (current-time))
|
||||
;; Properly set working directory for compilation.
|
||||
(default-directory (if (file-name-absolute-p file)
|
||||
(file-name-directory full-name)
|
||||
default-directory))
|
||||
errors)
|
||||
(message "Processing Texinfo file %s..." file)
|
||||
(save-window-excursion
|
||||
;; Replace %b, %f and %o with appropriate values in each command
|
||||
;; before applying it. Output is redirected to "*Org INFO
|
||||
;; Texinfo Output*" buffer.
|
||||
(let ((outbuf (get-buffer-create "*Org INFO Texinfo Output*")))
|
||||
(with-current-buffer outbuf (compilation-mode))
|
||||
(dolist (command org-texinfo-info-process)
|
||||
(shell-command
|
||||
(replace-regexp-in-string
|
||||
"%b" (shell-quote-argument base-name)
|
||||
(replace-regexp-in-string
|
||||
"%f" (shell-quote-argument full-name)
|
||||
(replace-regexp-in-string
|
||||
"%o" (shell-quote-argument out-dir) command t t) t t) t t)
|
||||
outbuf))
|
||||
;; Collect standard errors from output buffer.
|
||||
(setq errors (org-texinfo-collect-errors outbuf)))
|
||||
(let ((infofile (concat out-dir base-name ".info")))
|
||||
;; Check for process failure. Provide collected errors if
|
||||
;; possible.
|
||||
(if (or (not (file-exists-p infofile))
|
||||
;; Only compare times up to whole seconds as some
|
||||
;; filesystems (e.g. HFS+) do not retain any finer
|
||||
;; granularity.
|
||||
(time-less-p (cl-subseq (nth 5 (file-attributes infofile)) 0 2)
|
||||
(cl-subseq time 0 2)))
|
||||
(error "INFO file %s wasn't produced%s" infofile
|
||||
(if errors (concat ": " errors) ""))
|
||||
;; Else remove log files, when specified, and signal end of
|
||||
;; process to user, along with any error encountered.
|
||||
(when org-texinfo-remove-logfiles
|
||||
(dolist (ext org-texinfo-logfiles-extensions)
|
||||
(let ((file (concat out-dir base-name "." ext)))
|
||||
(when (file-exists-p file) (delete-file file)))))
|
||||
(message (concat "Process completed"
|
||||
(if (not errors) "."
|
||||
(concat " with errors: " errors)))))
|
||||
;; Return output file name.
|
||||
infofile))))
|
||||
|
||||
(defun org-texinfo-collect-errors (buffer)
|
||||
"Collect some kind of errors from \"makeinfo\" command output.
|
||||
|
||||
BUFFER is the buffer containing output.
|
||||
|
||||
Return collected error types as a string, or nil if there was
|
||||
none."
|
||||
(with-current-buffer buffer
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
;; Find final "makeinfo" run.
|
||||
(when t
|
||||
(let ((case-fold-search t)
|
||||
(errors ""))
|
||||
(when (save-excursion
|
||||
(re-search-forward "perhaps incorrect sectioning?" nil t))
|
||||
(setq errors (concat errors " [incorrect sectioning]")))
|
||||
(when (save-excursion
|
||||
(re-search-forward "missing close brace" nil t))
|
||||
(setq errors (concat errors " [syntax error]")))
|
||||
(when (save-excursion
|
||||
(re-search-forward "Unknown command" nil t))
|
||||
(setq errors (concat errors " [undefined @command]")))
|
||||
(when (save-excursion
|
||||
(re-search-forward "No matching @end" nil t))
|
||||
(setq errors (concat errors " [block incomplete]")))
|
||||
(when (save-excursion
|
||||
(re-search-forward "requires a sectioning" nil t))
|
||||
(setq errors (concat errors " [invalid section command]")))
|
||||
(when (save-excursion
|
||||
(re-search-forward "\\[unexpected\ ]" nil t))
|
||||
(setq errors (concat errors " [unexpected error]")))
|
||||
(when (save-excursion
|
||||
(re-search-forward "misplaced " nil t))
|
||||
(setq errors (concat errors " [syntax error]")))
|
||||
(and (org-string-nw-p errors) (org-trim errors)))))))
|
||||
(message "Processing Texinfo file %s..." file)
|
||||
(let* ((log-name "*Org INFO Texinfo Output*")
|
||||
(log (get-buffer-create log-name))
|
||||
(output
|
||||
(org-compile-file file org-texinfo-info-process "info"
|
||||
(format "See %S for details" log-name)
|
||||
log)))
|
||||
(when org-texinfo-remove-logfiles
|
||||
(let ((base (file-name-sans-extension output)))
|
||||
(dolist (ext org-texinfo-logfiles-extensions)
|
||||
(let ((file (concat base "." ext)))
|
||||
(when (file-exists-p file) (delete-file file))))))
|
||||
(message "Process completed.")
|
||||
output))
|
||||
|
||||
|
||||
(provide 'ox-texinfo)
|
||||
|
|
Loading…
Reference in New Issue