hyper-literate/scribble-lib/scribble/private/run-pdflatex.rkt
2017-03-03 07:26:25 -07:00

102 lines
4.1 KiB
Racket

#lang scheme/base
(require scheme/system scheme/port)
(provide run-pdflatex run-dvipdf-latex run-xelatex)
(define (run-pdflatex file [notify void]) (run file notify 'pdflatex))
(define (run-dvipdf-latex file [notify void])
(parameterize ([function-name 'run-dvipdf-latex])
(run file notify 'dvipdf)))
(define (run-xelatex file [notify void])
(parameterize ([function-name 'run-xelatex])
(run file notify 'xelatex)))
(define max-runs 5)
(define (run file notify type)
(define latex-cmd-name (cond [(equal? type 'pdflatex) "pdflatex"]
[(equal? type 'dvipdf) "latex"]
[(equal? type 'xelatex) "xelatex"]
[else (err "unknown run type ~a" type)]))
(define cmd
(list (get-latex-binary latex-cmd-name)
"-interaction=batchmode"
(format "~a" file)))
(define logfile (path-replace-suffix file #".log"))
(define (run)
(unless (parameterize ([current-output-port (open-output-nowhere)])
(apply system* cmd))
(unless (file-exists? logfile)
(err "did not generate a log file at ~a" logfile))
(call-with-input-file* logfile
(lambda (log) (copy-port log (current-error-port))))
(err "got error exit code")))
(let loop ([n 0])
(when (= n max-runs)
(err "didn't get a stable result after ~a runs" n))
(if (zero? n)
(notify "running ~a on ~a" latex-cmd-name file)
(notify " running ~a~a time"
(add1 n)
(case (normalize-for-suffix (add1 n)) [(2) 'nd] [(3) 'rd] [else 'th])))
(run)
;; see if we get a "Rerun" note, these seem to come in two flavors
;; * Label(s) may have changed. Rerun to get cross-references right.
;; * Package longtable Warning: Table widths have changed. Rerun LaTeX.
(cond [(call-with-input-file* logfile
(lambda (log) (regexp-match? #px#"changed\\.\\s+Rerun" log)))
(loop (add1 n))]
[(zero? n)
(notify "WARNING: no \"Rerun\" found in first run of pdflatex for ~a"
file)]))
(when (equal? type 'dvipdf)
(define dvi-file (path-replace-suffix file #".dvi"))
(define ps-file (path-replace-suffix file #".ps"))
(unless (file-exists? dvi-file) (err "didn't find .dvi file"))
(define dvips (get-latex-binary "dvips"))
(define pstopdf (get-latex-binary "pstopdf"))
(notify "running dvips on ~a" dvi-file)
(define stderr (open-output-bytes))
(unless (parameterize ([current-output-port (open-output-nowhere)]
[current-error-port stderr])
(system* dvips dvi-file))
(displayln (get-output-bytes stderr))
(err "got error exit code"))
(unless (parameterize ([current-output-port (open-output-nowhere)]
[current-error-port stderr])
(system* pstopdf ps-file))
(displayln (get-output-bytes stderr))
(err "got error exit code")))
(path-replace-suffix file #".pdf"))
(define (normalize-for-suffix n)
(cond
[(<= 10 n 20) 0]
[else (modulo n 10)]))
(define (get-latex-binary name)
(define ans
(case (system-type)
[(macosx) (or (find-executable-path name)
(for/or ([macosx-candidate-dir (in-list macosx-candidate-dirs)])
(define macosx-candidate (build-path macosx-candidate-dir name))
(and (file-exists? macosx-candidate)
macosx-candidate)))]
[(windows) (or (find-executable-path name)
(find-executable-path (format "~a.exe" name)))]
[(unix) (find-executable-path name)]))
(unless ans
(err (format "could not find a `~a' executable" name)))
ans)
(define function-name (make-parameter 'run-pdflatex))
(define (err fmt . args) (apply error (function-name) fmt args))
;; under mac os x, gui apps do not get started with
;; a good path environment, so put likely candidates
;; for directories holding latex/pdflatex binaries
;; here so that the "scribble pdf" button is more
;; likely to work in drracket
(define macosx-candidate-dirs
'("/usr/texbin" "/Library/TeX/texbin"))