Selaa lähdekoodia

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).
Nicolas Goaziou 8 vuotta sitten
vanhempi
commit
ea1147a479
4 muutettua tiedostoa jossa 144 lisäystä ja 233 poistoa
  1. 67 0
      lisp/org.el
  2. 50 78
      lisp/ox-latex.el
  3. 9 67
      lisp/ox-man.el
  4. 18 88
      lisp/ox-texinfo.el

+ 67 - 0
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
 

+ 50 - 78
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.
-
-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))))
+create a log buffer and do not remove log files.
+
+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.

+ 9 - 67
lisp/ox-man.el

@@ -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)
 

+ 18 - 88
lisp/ox-texinfo.el

@@ -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)