TeX の実行あれこれ

TeX の実行は色々面倒なことが多い。どういうところが面倒か、どういうアルゴリズムを組めば自動化できるかをまとめてみた。

目次

TeX の実行時のオプション

デフォルトでは、 TeX の実行時にエラーが起こると TeX が実行を中断してユーザーの指示を仰ぐようになっている。しかし、利用者の 99.9% は「そのまま TeX の実行を終了してエディターでエラーを修正する」以外の行動は取らないだろう。なので、ユーザーの指示を仰がずにそそまま終了して欲しい。

この話については以前にこのブログに記事を書いた。要約すると -halt-on-error -interaction=nonstopmode を指定すれば良い。

TeX処理系の気に食わない点(0) — エラー時の挙動

エラーの表示に関していうと、 -file-line-error も指定して損はないだろう。

というわけで、 TeX の実行時にはとりあえず

latex -halt-on-error -interaction=nonstopmode -file-line-error

という風に、3つのオプションはつけるようにしたい。SyncTeX 対応のビューワーを使う場合は、 -synctex=1 オプションも欲しいかもしれない。

折り返しの桁数【12月29日 追記】

デフォルトでは、 TeX が標準出力へ書き出す文字列は79文字で折り返される。

例:

$ etex '\immediate\write16{abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ}\bye'
This is pdfTeX, Version 3.14159265-2.6-1.40.17 (TeX Live 2016) (preloaded format=etex)
 restricted \write18 enabled.
entering extended mode
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzA
BCDEFGHIJKLMNOPQRSTUVWXYZ
No pages of output.
Transcript written on texput.log.

標準出力が改行文字によって折り返されるだけならまだ良いのだが、実はログファイル(この場合は texput.log)に書き出される内容も改行による折り返し (hard wrapping) の対象となっている。TeX の周辺ツールでログファイルを解析する際、この余計な改行は邪魔でしかない。

折り返しの文字数である「79」という数字は、 texmf.cnf というファイルに記述されている(texmf.cnf について詳しくはこの辺を参照)。現在の値を取得するには

$ kpsewhich -var-value=max_print_line

を実行すれば良い。

この値を変えるには、自分で texmf.cnf を編集しても良いのだが、環境変数 max_print_line によって設定することもできる。TeX 周辺ツールを作る際は環境変数で設定する方が現実的だろう。

$ env max_print_line=1000 etex '\immediate\write16{abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ}\bye'
This is pdfTeX, Version 3.14159265-2.6-1.40.17 (TeX Live 2016) (preloaded format=etex)
 restricted \write18 enabled.
entering extended mode
abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
No pages of output.
Transcript written on texput.log.

これは TeX Live での話で、 MiKTeX ではまた微妙に話が違うらしい(対応するコマンドラインオプションがあるようだ)。

参考:compiling – Avoid linebreaks in LaTeX console / log output – TeX – LaTeX Stack Exchange

関連する変数に error_linehalf_error_line というのもあって、これは、エラー時にエラー箇所の前後を何文字表示するかを制御する。

pTeX を使う際の PDF ファイル出力

一般的な利用者は TeX 文書を処理した最終的な出力として、(DVI プレビューや PostScript 出力ではなく) PDF ファイルを得たいという人が多いだろう。

pdfTeX, LuaTeXXeTeX は直接 PDF を出力できるので特に何もする必要がない。

一方、 pTeX 等の、直接 PDF を出力しない処理系を使う場合は、出力された DVI ファイルに対して dvipdfmx を実行する必要がある。2つのコマンドを実行するのは面倒くさい。

この手順を簡略化するため、 pTeX と dvipdfmx を一括で実行してくれるスクリプトがある:

ptex2pdf (GitHub)

ptex2pdf は、元々は DVI 経由の処理を想定していない TeXworks 向けに作られたようだが、普通にターミナルから実行することもできる。

複数回の実行

LaTeX で相互参照を使ったことがある人なら、正しい出力を得るためには「2回実行する必要がある」ことをご存知だろう。これは、LaTeX の相互参照の仕組みが

  • 1回目の実行でラベルの情報を補助ファイル (.aux) に書き出す
  • 2回目以降の実行では、補助ファイル (.aux) の内容を読んでラベルの情報を反映させる

という風に実装されているためだ。

複数回の実行が必要なのは相互参照に限らず、 \tableofcontents を使う場合や、 hyperref で PDF のしおりを生成する場合も、複数回の実行が必要になる。

さて、手動で複数回実行するのは面倒なので、複数回の実行が必要な場合に必要な回数だけ実行してくれるツールが欲しい、という話になる。

この目的のツールで一番有名なのは、 latexmk だろう。

有名な紹介記事: latexmk で楽々 TeX タイプセットの薦め(& biblatex+biberで先進的な参考文献処理) – konn-san.com

ところで、「複数回の実行が必要な場合に必要な回数だけ実行する」場合、TeX の実行回数は必ず有限回で済むだろうか?

答えは否だ。ファイルの内容によっては、実行するたびに補助ファイルの内容が変化して、毎回「再実行が必要」という判断になる可能性がある。例:

LaTeXの相互参照はいつでも解決(収束)するのか?

極端な例を挙げるなら、 pdfLaTeX でシェルエスケープを有効にして \label{\pdfcreationdate} \write18{sleep 1} と書けば、 .aux ファイルに時刻(秒単位)の情報が含まれる&実行に1秒以上かかるので、毎回 .aux ファイルの中身が変化する。

というわけで、latexmk 等の「複数回の実行が必要な場合に必要な回数だけ実行する」ツールには必ず「上限回数」が設定されていて、その回数だけ実行しても終わらなかった場合は実行を打ち切るようになっている。

BibTeX 等の実行

(後で書く)

ファイルを更新した際に自動で再処理したい

テキストエディターで TeX ファイルを保存したタイミングで自動で再処理が行われて PDF が生成されて欲しい。(統合環境を使っている人はボタン一発で処理できるのでこの手の機能は必要ないだろうが)

先述した latexmk には -pvc というオプションがあり、ファイルが更新された際に自動で TeX を実行してプレビューを更新することができる。

ところで、この -pvc-p -v -c という3つのオプションの集合体ではなく、 -pvc で1つのオプションである。キモい

カレントディレクトリを散らかさないでほしい

近年は、バックアップ目的、あるいは複数人での共同作業の目的で、 TeX 文書を Dropbox の中に置いて作業する人も多いだろう。しかし、 Dropbox の中で作業すると、 .log や .aux などのログファイル、補助ファイルまで同期されてしまって鬱陶しい。

latexmk のようなツールはこれらの「要らない」ファイルを削除してくれることもあるが、それでも「一瞬だけファイルができて、すぐに削除される」というのはあまり嬉しくない(履歴に残る)。どうせ削除するなら、そもそも作らないでくれ!

補助ファイルをカレントディレクトリに作らないようにするには、TeX のコマンドラインオプションとして -output-directory を渡し、出力された DVI ファイルなり PDF ファイルなりだけをカレントディレクトリにコピーすれば良い。それだけの簡単なことなのだが、筆者が知る限り、この種の処理を行ってくれる補助ツールというのはほとんど(知られてい)ない。

MiKTeX という Windows 向けの TeX ディストリビューションには -aux-directory というオプションが実装されているようだが、 TeX Live の TeX コマンドにはそういうオプションは実装されていない。(筆者は MiKTeX をインストールしていないので、実際に試してみたわけではない)

では、TeX Live で使えるそういう補助ツールが全くないかというとそうでもなくて、 GNU Texinfo に付属する texi2dvi/texi2pdf コマンドに --tidy オプションや --build-dir オプションを渡せば、補助ファイルの出力先を変えることができる。(texi2dvi/texi2pdf コマンドは Texinfo ファイルだけではなくて通常の LaTeX ファイルも処理できるのだ)

既存のツール各種

CTAN の Topic compilation を覗くと、いろいろなスクリプトが見つかる。いくつか紹介:

  • ptex2pdf (GitHub)
    • (u)p(la)tex と dvipdfmx をまとめて実行してくれる。すでに上の方で紹介した。
    • Lua で書かれている。
  • latexmk
    • 複数回の実行、 BibTeX 等の関連ツールの実行を自動でやってくれる。
    • この手のツールの中では最も有名なのではないだろうか。
    • Perl で書かれている。
  • latexn
    • 目的:複数回の実行
    • 実行するコマンドは決めうちで、カスタマイズ性は低い。
    • .aux, .idx, .bbl の変更を検出する。(それ以外の補助ファイルの変化は検出しない)
    • シェルスクリプトで書かれている。
  • shlatex
    • 目的:BibTeX 等の実行
    • LaTeX の再実行の回数は固定のようだ。LaTeX のコマンドも latex か pdflatex しか選べず、カスタマイズ性は低い。
    • シェルスクリプトで書かれている。

以下は CTAN にないが、言及しておく価値のあるプログラムである:

  • rubber
    • 名前が一般名詞なので検索性が低い。それ以上に、関連キーワードを追加すれば求める情報が出てくるかと思って “latex rubber” で検索した時の絶望感が酷い。悪い名付けの見本とでも言うべきだろう。(検索キーワードを “tex rubber” とすれば欲しい情報が出てくる)
    • 複数回の実行をやってくれる。
    • BibTeX等の実行にも対応しているようだ。
    • ソースコード中の magic comment で挙動をいじれる。
    • Python で書かれている。
  • texify (MiKTeX)
    • 複数回の実行をやってくれる。
    • 補助ファイルの出力先を指定できる。
  • texi2dvi/texi2pdf (GNU Texinfo)
    • 複数回の実行をやってくれる。
    • 補助ファイルの出力先を指定できる。
    • DVI 経由の PDF 出力にも対応。

自分で新しく補助ツールを作る際は、これらと名前が被らないようにしたい。

結果→ ClutTeX

TeX 実行の自動化ツールを作った (ClutTeX)

補助ツールの実装の方針

既存の補助ツールに満足できないので、新しく自分で作ってしまおう!という人のためのメモを書いておく。

再実行が必要か判定する方法

まず、相互参照に関しては、再実行が必要な場合(=.aux ファイルの内容が変化した場合)に LaTeX 自身がメッセージを出力するようになっている。LaTeX の出力の中にある

LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right.

というのがそれだ。LaTeX の出力(ログ)の中にこの行があれば、再実行が必要と判断して良い。

しかし、補助ファイルを使うコマンド・パッケージというのは、他にも色々ある。再実行が必要な場合に出力されるメッセージというのはパッケージによって違うだろうし、同じパッケージを使っていてもエンジンの種類によってメッセージが異なるかもしれない(具体的には、 rerunfilecheck パッケージは pdfTeX の機能を使って補助ファイルの変化を検出するので、 XeTeX では使えない)。

したがって、再実行の必要性の判定を真面目にやろうとすると、出力されるメッセージに頼らない別の方法を考える必要がある。

TeX の実行時に -recorder オプションをつけると、(Luaを介して読み書きしたものを除く)実行中に読み書きしたファイルの一覧を .fls ファイルに書き出してくれる。このファイル一覧には、 .aux や .out のような補助ファイルも含まれる。

このファイル一覧があれば、 TeX の実行の前後で入力ファイルの内容(実際にはハッシュ値を使うことになるだろう)を比較することにより、再実行の必要性を正確に判定することができそうだ。具体的には、「実行前と実行後で入力ファイルが全く変化していない」ならば、再実行の必要はない。

この方針だと、特定の LaTeX 文書を初めて処理する際には必ず2回以上実行することになる。相互参照しようがしまいが LaTeX は必ず .aux ファイルを生成するからだ。しかし、相互参照を使わないなら処理は1回で十分なはずだ。そんな場合でも2回実行するのは無駄ではないか。

そこで、初回実行の後に .aux ファイルの中身が実質空の場合(文書中でラベル等が定義されていなかった場合)は2回目の実行は不要と判断する。具体的には、 .aux ファイルの中身が \relax  の1行(空白と改行を含めて8バイト)の場合は、そのファイルは実質的に空だとみなす。あるいは、 .aux ファイルに関しては LaTeX のメッセージ出力を信頼して判断に使うという方針も考えられる。

LuaTeX の場合は、 Lua の機能(io.open など)によってファイルを読み書きすることもできるが、このようなファイルは recorder file (.fls) には載らない(例:LuaTeX-ja の luatexja-ruby が扱う .ltjruby ファイルは Lua の機能によって書き出されているので、 .fls に OUTPUT ナントカ.ltjruby という項目はない)。Luaによるファイルの読み書きをどうしても捕捉したければ、Lua initialization script(LuaTeX の –lua オプション)を使って io.open を乗っ取ることになるだろう。

ファイルの更新を監視する方法

誰でも思いつく単純な方法は、一定間隔ごとにファイルの更新をチェックする (polling) というものである。しかし、これではユーザーが何もしていなくても一定間隔ごとにファイルシステムへのアクセスが発生することになり、あまり効率がいいとは言えない。

ファイルの更新を監視したいアプリケーションのために、大抵の OS は、そのための API を提供しているものである。しかし、その API は OS ごとにまちまちだし、シェルスクリプトや簡易的なスクリプト言語から容易に扱えるようになっているとは限らない。

そこで、サードパーティーのプログラムの利用を検討する。調べたところ、 fswatch というプログラムは主要なOSに対応していて、シェルスクリプト等から扱いやすそうである。

fswatch がインストールされていない環境でもファイルの更新を監視したいという場合は、自前で polling することになるだろう。

既存の LaTeX 補助ツールはどういう方法を使っているか気になるところだが、 latexmk -pvc は polling を使っているように見えた。

カレントディレクトリを散らかさない方法

大雑把な方針はすでに書いた。TeX の実行時に -output-directory を指定して、出力先から成果物である DVI ファイルなり PDF ファイルなりをコピーすれば良い。

出力先をいちいち指定するのは面倒なので、補助ツールの側でテンポラリディレクトリ以下に適当なディレクトリを掘って、そこを使うようにして欲しい。ディレクトリ名には処理対象のファイルのフルパスのハッシュでも含めておけば、他と衝突しないだろう。

テンポラリディレクトリは揮発性かもしれないが、複数回の実行の間に内容を保ってくれていれば特に問題はない。むしろ、古い .aux ファイルが残っているせいで起こる種類のエラーもあるので、補助ファイルは適当なタイミングで一掃された方がありがたいかもしれない。

テンポラリディレクトリを使う際には、ディレクトリのパーミッションに気をつけること。Mac の場合は環境変数 TMPDIR に入っているディレクトリは現在のユーザーしかアクセスできないようになっているようなので問題ない。他の OS は知らん。

LuaTeX の場合、 Lua の io.open で書き出されるファイルは -output-directory の影響を受けない。どうにかしたければ Lua initialization script を使って io.open を乗っ取ることになるだろう。

-output-directory に関しては他にも語るべきことがある。詳しくは LaTeX と -output-directory を参照。

【2018年10月30日 更新】LuaTeX に関する注意点を追記した。それから別記事へのリンクを追加。

【2020年1月2日 更新】「LaTeXの相互参照はいつでも解決(収束)するのか?」へのリンクを追加。ClutTeXの紹介へのリンクを追加。


TeX の実行あれこれ」への2件のフィードバック

  1. ピンバック: LaTeX と -output-directory | 雑記帳

  2. ピンバック: TeX 実行の自動化ツールを作った (ClutTeX) | 雑記帳

コメントは停止中です。