LaTeX と -output-directory


カレントディレクトリを散らかさずに LaTeX を処理する方法について、ざっくりとした方針はこの間の記事に書いた。しかし、現実はそう単純にはいかない。今回は、出力ディレクトリを指定する場合に遭遇する落とし穴とその対処法について考察する。

\include と補助ファイル

LaTeX 文書を複数のソースに分割する場合、分割したファイル(サブファイル)を読み込むコマンドとしては \input と \include の2つがある。後者は \includeonly が使えるとか、挿入する内容が新しいページに来るとかの特徴がある。しかし、 LaTeX 実行の自動化という観点で重要なのは、「\include によって挿入されるファイルについては .aux ファイルがメインファイルとは別個に作られる」ということである。

ここで、もしも \include しようとしているファイルがサブディレクトリ以下にあった場合、 .aux ファイルもサブディレクトリに作られる。 \include{sub/file} を実行すれば、補助ファイルは sub/file.aux という名前になる。

LaTeX を普通に実行する分にはこれで問題ないのだが、 -output-directory 付きで実行する場合はこれでは困る。入力ファイルと同じディレクトリに sub/ というディレクトリがあっても、一般には出力先のディレクトリには sub/ というディレクトリは存在しないので、 sub/file.aux への書き出しが失敗する。

対策としては、出力先のディレクトリにあらかじめ sub/ というサブディレクトリを掘っておけば良い。もちろん、 TeX を実行する前から「sub/ というサブディレクトリが必要」とはわからないので、次の手順を踏むことになる:

  1. 試しに実行してみる
  2. サブディレクトリがないので失敗する
  3. 足りないディレクトリを掘る
  4. もう一度実行

2番目の「失敗する」の失敗理由を判定する方法としては、ログファイルに I can't write on file `sub/file.aux' という文言が残っていること、そして、メインファイルの .aux に \@input{sub/file.aux} という形の行が存在するかどうかで判断できるだろう。

ちなみに、上位ディレクトリに存在するファイルを \include{../file} という感じで読み込もうとした場合に、補助ファイルが -output-directory で指定したディレクトリの外に作られてしまうのではと懸念されるかもしれない。しかし、 TeX Live のデフォルトでは texmf.cnf に openout_any = p という行が存在して、そういうディレクトリトラバーサル的なことを阻止してくれる。

シェルエスケープとの兼ね合い

TeX 言語では色々なことができるが、 TeX 言語だけで全ての処理を行うのは非現実的である。LaTeX のパッケージによっては、文書処理の一環として外部コマンドを起動するものが存在する。

カレントディレクトリを散らかさずに LaTeX 文書を処理したい、という場合に、このような外部コマンドの実行は問題となりうる。なぜなら、外部コマンド及びそれを起動する LaTeX パッケージは -output-directory の存在を知らず、カレントディレクトリにファイルを書き出す可能性があるからだ。(TeX の仕組みによってファイルを書き出す際は -output-directory の指定を尊重してくれるが、外部コマンドとしてはそんなものは知ったことではない。また、 LaTeX パッケージとしても -output-directory の指定内容を外部コマンドに教える術はおそらく存在しない。)

このような外部コマンド実行への対処法について考察する前に、外部コマンドを使ってファイルを生成するような LaTeX パッケージの例を見てみよう。

シェルエスケープを使ってファイルを処理するパッケージの例

以前なら graphics/graphicx パッケージが真っ先に挙がるところだったが、最近の TeX Live では .xbb ファイルを使わずに extractbb の実行結果をパイプで取得するようになっているので、ここには挙げない。

epstopdf (graphics/graphicx)

pdfTeX では EPS 形式の画像ファイルを直接取り込めない。そこで、 epstopdf というプログラムを使って、予め画像ファイルを PDF 形式に変換しておく。いちいち自分で epstopdf コマンドを実行するのは手間なので、それを代わりにやってくれるパッケージが epstopdf である。

最新の pdftex.def では自動的に epstopdf パッケージを読み込むようになっているので、このパッケージの存在を意識することなく EPS 形式の画像を埋め込める。

svg

SVG 形式の画像を LaTeX 文書に貼る際は、 Inkscape 等のプログラムを使って予め EPS 形式なり PDF 形式に変換したものを貼るというのが常套手段である。svg パッケージはその「Inkscape の実行」を肩代わりしてくれる。

graphviz

Graphviz の図を LaTeX 文書中に埋め込める。

例:

\documentclass{article}
\usepackage[pdf]{graphviz}
\begin{document}

% LaTeX 文書中に直接書く
\digraph{mydiagram}{
  source -> latex -> dvi -> dvipdfmx -> pdf;
  latex -> aux [headport=s, splines="curved"];
  aux -> latex [tailport=n];
  source [label="Source files (*.tex)"];
  {
    rank = same;
    latex [shape=box, label="LaTeX", width=1.5];
    aux [label="Auxiliary files (*.aux,...)"];
  }
  dvi [label="DVI file (*.dvi)"];
  dvipdfmx [shape=box, label="dvipdfmx", width=1.5];
  pdf [label="PDF file (*.pdf)"];
}

% 外部ファイル (file.dot) から読み込む
\inputdigraph{file}{dot}

\end{document}

gnuplottex

LaTeX ソース中に gnuplot のコマンドを書いて、 gnuplot でグラフを描画できる。

例:

\documentclass{article}
\usepackage{gnuplottex}
\begin{document}

% LaTeX 文書中に直接 gnuplot のコマンドを書く
\begin{gnuplot}
  plot sin(x)
\end{gnuplot}

% 外部のファイルを読み込む
\gnuplotloadfile{test.gnuplot}

\end{document}

tikzexternalize (TikZ)

文書に埋め込まれた TikZ の図を個々の画像ファイルとして取り出せる。TikZ を毎回処理していると遅いという場合に、 TikZ の処理を省くのに使える。

ちなみに、呼び出す外部コマンドは latex 自身である。

例:

\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{external}
\tikzexternalize
\begin{document}
\begin{tikzpicture}
  \draw (0,0) -- (1,0) -- (1,1) -- (0,1) -- cycle;
\end{tikzpicture}
\end{document}

minted

Python で書かれた Pygments というライブラリを使って、ソースコードのシンタックスハイライトを行う。

例:

\documentclass{article}
\usepackage{minted}
\begin{document}

% LaTeX 文書中に直接ソースコードを書く
\begin{minted}{lua}
function greet()
  print("Hello world!")
end
\end{minted}

% 外部ファイルを埋め込む
\inputminted{lua}{file.lua}

\end{document}

シェルエスケープ対策の考察

通常、外部コマンドはカレントディレクトリを基準としてファイルを書き出すので、 LaTeX を実行する前にカレントディレクトリを出力ディレクトリに移動すれば、入力ファイルの存在するディレクトリが汚染される事態は避けられる。(TeX 自身が入力ファイルを見つけられなくなってはいけないので、環境変数 TEXINPUTS を適宜設定しておく)

問題は、外部コマンドへの入力ファイルだ。外部コマンドへの入力ファイルが、 LaTeX パッケージによって書き出されたものなら問題ないが、そうでない場合は外部コマンドが入力ファイルを見つけられなくなる。

さっき挙げた例では、 epstopdf, svg, graphviz の \inputdigraph, gnuplottex の \gnuplotloadfile, minted の \inputminted が後者に該当する。

じゃあ、外部コマンドが絡む分については入力ファイルのあるディレクトリが汚染されるのを許容して、カレントディレクトリを移動しなければ良いかというと、今度は逆に、 LaTeX パッケージによって書き出されたファイルを外部コマンドに処理させる場合に問題が起こる。

つまり、入力ディレクトリと出力ディレクトリを分離しようという考え方と、外部コマンドでファイルを処理させる LaTeX パッケージは、根本的に相性が悪い。

一応、パッケージの作りによっては、希望はある。外部コマンドへの入力ファイルが変更されていなければ外部コマンドを実行しないようになっている場合(例:svg パッケージ)は、 LaTeX パッケージに先回りして正しいディレクトリ指定で外部コマンドを実行しておけば、問題を回避できるかもしれない。


こうも色々と問題が見えてくると、 LaTeX の処理時に入力ディレクトリと出力ディレクトリを分離しようという考え方が間違っているのではないかと思わざるを得ない。どうしても分離したければ、貧弱な -output-directory なんて使わずに、 OverlayFS のような仕組みを使った方がいいのでは?

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です