llmk の基本

2020-12-20   #LaTeX  #llmk 

本稿は TeX & LaTeX Advent Calendar 2020 の20日目の記事です.19日目は hid_alma1026 さんでした.21日目は mattskala さんです.

llmk (Light LaTeX Make) の開発を開始して約2年が経ちました.とてもゆっくりとしたプロセスではありましたが,多くの方にご協力をいただきながら llmk は少しずつ成長し,ようやく TeX Live, MiKTeX に取り込まれて一般の LaTeX ユーザにも広く使ってもらえる状況まで漕ぎ着けることができました.

llmk のバージョン番号は未だに 0.x 台ですが,これは主に運用実績が乏しいことから「安定版」を称するには多くのバグが残っている可能性を危惧してのことで,仕様的には(よほど大きな欠陥が見つからない限り)このまま非互換な変更を入れることなくバージョン 1.0.0 をリリースするつもりでいます.そうした事情も鑑みると,そろそろ「現状の llmk」の基本的な使い方について日本語でも簡単に紹介してもいいかなと思い,今回筆を執りました.

本稿では概ね llmk 付属ドキュメントの README に相当する内容+αを扱います.つまりは llmk の根幹にあたる,おそらく今後も非互換な仕様変更が行われにくいだろう点1に絞って解説をしています.より詳細に踏み込んだ llmk の仕様全体については,その都度最新のリファレンスマニュアル(英語)を参照してください.

llmk の特長

llmk は LaTeX 文書に特化したビルドツールです.昨今では,LaTeX 文書を処理する方法は多岐に渡っています.また「どれか1つのツールが究極の選択肢で,他のすべてを凌駕している」ということもなく,それぞれに特長があるため,どれを採用するかは文書の性質に応じて決める必要があります.具体的には,次のような選択肢があることはよく知られています.

  • LaTeX エンジン:pdfLaTeX, XeLaTeX, LuaLaTeX, (u)pLaTeX, etc.
  • DVI ウェア:dvipdfmx, dvips, etc.
  • 参考文献処理システム:BibTeX, (u)pBibTeX, etc.
  • 索引処理システム:makeindex, xcindy, (up)mendex

LaTeX 文書の作成者は,こうした選択肢の中からある文書を処理する「正しい方法」を1つ選ぶことになります.例えば,典型的な pLaTeX 文書であれば platexpbibtexplatexplatexdvipdfmx と順に利用して,LaTeX ソースを最終的な PDF に変換するという手順がよく用いられるはずです.こうした一連の流れを「ワークフロー」と呼ぶことにします.

llmk の目的は,こうしたワークフローを簡単に記述し,環境をまたいでと共有できるようにすることです.LaTeX 文書に llmk の設定さえ書いておけば,Windows でも macOS でも Linux でも,あるいは Overleaf, Cloud LaTeX, arXiv といったオンライン環境であっても,まったく同じようにワークフローの解釈・実行ができるようにすることを目指しています(もちろん現時点ではまだ道半ばです).

llmk の機能は,このようなワークフローの簡単な記述およびその共有を第一に考えて設計されています.

  • シンプルで簡単な設定記述言語 (TOML) でワークフローを記述できることができます
  • ワークフローは LaTeX ソースに直接書くことも,別の専用ファイルに書くこともできます
  • TeX Live さえあれば OS を問わず動作します(クロスプラットフォーム)
  • どのような環境でも,ワークフロー記述はまったく同じ風に解釈・処理されます

LaTeX 専用のビルドツールは latexmk や arara など,いくつも存在していますが,llmk と同じ目的を持ち,上記の性質をすべて同時に満たすツールは llmk の他にはないはずです.

一方で,llmk は「シンプルであること」を目指していることもあり,複雑で独特な手順を必要とする LaTeX 文書のワークフロー記述には必ずしも適していません.そのような場合は,Make 等の汎用ビルドツールや,より高機能な別の LaTeX 専用ビルドツールを利用するといいでしょう.場合によっては llmk を用いて別のビルドツールを呼び出すようにしておくと都合がいい場合もあるかもしれません.

インストール

llmk は最新の TeX Live, MiKTeX に light-latex-make というパッケージ名で収録されています.これらのディストリビューションを使用している場合には,基本的には何も特別なことはしなくても最初からインストールされている状態になっているはずです2

お使いの TeX システムに llmk が含まれていない場合や開発版の最新バージョンを利用したいという場合は,手動でインストールすることも可能です.llmk のソースコードその他すべての関連成果物は GitHub リポジトリから入手可能です.現状 llmk は単一の実行可能スクリプトファイル (llmk.lua) なので,インストール手順は極めて単純です.同ファイルをダウンロードして texlua <path>/llmk.lua<path> はダウンロードした llmk.lua への絶対パスもしくは相対パス)を実行すれば必ず動作するはずです.なお UNIX 系のシステムであれば llmk.lua に実行権限を与え,llmk という名前で PATH 内のお好きな位置にコピーするかシンボリックリンクを貼るのが簡単でしょう.

基本の使い方

llmk の基本となる使い方は,LaTeX 文書のワークフローを LaTeX の文書ソース(ナンチャラ.tex ファイル)内で直接指定することです.

まずは用意した文書ソースのなるべく先頭に近い位置に,llmk 用の設定を記述します.こうした設定は3つ以上の連続する + マークのみ3を含む行で挟むように,TOML 形式で記述することができます.このように +++ を含む行で区切られた TOML によるワークフロー指定を記述できる領域を TOML フィールドと呼びます.言葉で説明するよりも,さっそく具体例を見るのが早いでしょう.

% +++
% latex = "xelatex"
% +++

\documentclass{article}
\begin{document}

Hello \textsf{llmk}!

\end{document}

latex = "xelatex" の部分が llmk に解釈させるためのワークフロー指定です.意味としては,容易に推測できると思いますが,LaTeX コマンドとして xelatex を使用するように指示しています.

上のコードが hello.tex という名前で保存されているとします.このファイルを llmk で処理するためには,ファイルと同じディレクトリで次のようなコマンドを実行します.

$ llmk hello.tex

こうすることで llmk は hello.tex の TOML フィールドに記述された設定内容を読み取り,そのワークフロー指示に従って組版処理の実行を行います.すなわち,この例では XeLaTeX を使用し最終結果である hello.pdf が生成されます.

llmk のリポジトリにある examples ディレクトリには,他にも llmk 用に設定が記述された LaTeX 文書の例が置かれているので,参考にするといいでしょう.

外部ファイルの使用

文書ソースにコメントの形で直接ワークフローを指定する代わりに,llmk.toml という名前の特別な外部ファイルにワークフローを記述することもできます.そもそもどのファイルが文書ソースなのかをシステムや共著者が知り得ないような場合には,こちらの方法を用いる方が適しています.

llmk.toml の記述方法は TOML フィールドの記述方法とほぼ同一ですが,必ず source キーを用いてどのファイルが文書ソースなのかを明示する必要があります.例えば,次のような具合です.

# llmk.toml
latex = "platex"
source = "hello-ja.tex"

この内容を llmk.toml というファイル名で保存します.llmk コマンド実行時に,何の引数も与えない場合は自動的にカレントディレクトリの llmk.toml が読み込まれ,そのワークフロー指定にしたがって処理が試みられます.

$ llmk

すなわち,いま考えている例では,platex を用いて hello-ja.tex の処理が実行されることになります.

ちなみに pLaTeX は直接 PDF を生成することはできず DVI 形式の出力を行うわけですが,DVI ファイルが生成された場合,llmk はデフォルトで dvipdfmx を使用して DVI → PDF の変換を行います.つまり,上述の設定を行うだけで hello-ja.pdf の生成まで一通り自動で実行されることになります.

他のマジックコメント形式の使用

再び文書ソースに直接ワークフローを指定する話に戻ります.TOML フィールドは llmk 独自の形式ですが,既存ツールの中には,別形式のワークフロー指定をサポートしているものがあります.ユーザの利便性を考えて,llmk はこうした形式のいくつかをサポートしており,また必要に応じて今後もサポート形式を増やしていく予定です4

例えば TeXShop やその派生プロジェクトは % !TeX という形から始まるマジックコメントで使用する LaTeX エンジンや BibTeX エンジンを指定することができます.

%! TEX TS-program = xelatex
%! BIB TS-program = biber
\documentclass{article}

この形式は llmk によってサポートされていて,TOML フィールドで次のように指定したのと同じように扱われます.

% +++
% latex = "xelatex"
% bibtex = "biber"
% +++
\documentclass{article}

また Emacs の LaTeX 向け拡張機能 YaTeX はシェバン風のよりシンプルな記法による LaTeX エンジンの指定をサポートしています.

%#!pdflatex
\documentclass{article}

llmk はこの形式もサポートしていて,次の指定と等価なものとして扱います.

% +++
% latex = "pdflatex"
% +++
\documentclass{article}

なおこのシェバン風の記法はファイルの先頭行でないと認識されないので注意してください.またファイル内に TOML フィールドが存在する場合は,TOML フィールドが最優先され,他のすべての形式のマジックコメントは無視されます5

生成ファイルの削除

LaTeX による処理を行うと,中間生成ファイル等がたくさん作られるので,これをまとめて削除したいということがよくあります.llmk のコマンドラインオプション --clean (-c) や --clobber (-C) を使用すると,ワークフロー実行によって生成されたファイルを一括で削除することができます.

  • --clean (-c): aux や log 等の中間生成ファイルを削除します
  • --clobber (-C): 中間生成ファイルに加え,PDF を含むすべての生成ファイルを削除します

引数の与え方は基本的にワークフロー実行をしたい場合と同様です.つまり

$ llmk --clean <file>

を実行すると <file> の処理によって生成されたファイルが削除されます(<file> は一度に複数指定することもできます).また引数 <file> を指定しなかった場合は llmk.toml が読み込まれ,その中で source に指定されているファイルの処理で生成されたファイルが削除されます.なお --clean, --clobber それぞれで削除するファイルは TOML フィールド等での設定でカスタマイズすることも可能です.

発展的な使い方

そもそも本稿は llmk の基本についての記事なので,発展的な使い方とは言うものの「発展的な使い方の基礎」に該当するような話だけを簡単に述べるに留めます.

ここまでで紹介した極めて単純なケースでは,TOML による llmk へのワークフロー指定のトップレベルで latexbibtex キーを用いて使用したいコマンド名を与えるだけでしたが,これではより複雑な設定(例えば各コマンドに与える具体的な引数の指定)を行うことはできません.そうした細かな制御を行いたい場合には,これから述べる llmk のワークフロー制御に関する知識が必要になります.

ワークフローの柔軟な制御

llmk のワークフロー制御のしくみは実はかなり単純です.鍵となるのは sequence 配列と programs テーブルという2つのデータ構造です.

このうち sequence 配列はワークフローで実行したいプログラムの名前を,実行する順番で格納するためのものです.もう1つの programs テーブルには各プログラムの実行方法に関して詳細な情報を持たせます.つまり実際のワークフロー処理にあたっては,llmk は sequence に指定されているプログラムを,逐次 programs テーブルの持つ詳細な設定を参照しながら実行していくことになります.

まずは単純な例として,フツーの upLaTeX 文書の処理をすべて ptex2pdf に丸投げする例を考えてみます.

# 文書ソースは hoge.tex
source = "hoge.tex"

# カスタム sequence
sequence = ["ptex2pdf"]

# ptex2pdf に関する詳細設定
[programs.ptex2pdf]
command = "ptex2pdf"
opts = ["-u", "-l"]
args = ["%T"]

先に結論を言うと,上記の内容を llmk.toml に保存して

$ llmk

を実行すると ptex2pdf -u -l "hoge.tex" が1回実行されて(hoge.tex が正当な upLaTeX 文書ならば)hoge.pdf が生成されます.

このワークフロー指定を詳しく見ていきます.まず sequence に単一のプログラム名 ptex2pdf が指定されているので,llmk はワークフロー処理時にこのプログラムの実行を行うわけですが,その際に必要な詳細な設定は [programs.ptex2pdf] テーブル内に記述されています.とりあえずコマンド名 (command) が ptex2pdf で,与えたいコマンドラインオプション (opts) が -u-l だと指定されていることはすぐにわかるでしょう.

問題はコマンドライン引数 (args) の指定 ["%S"] です6.各プログラムに対する指定の中で args キーに対しては %S などの特別な指定子を使用することができます.利用可能な指定子の例を挙げると:

  • %S: 文書ソースのファイル名
  • %B: %S のベース名(ファイル名から拡張子を除いたもの)

ということで,先の例では %S の部分が文書ソース (hoge.tex) に置換されて最終的に ptex2pdf -u -l "hoge.tex" が実行されることになります.

細かい点はさておき,ここではワークフローで実行したいプログラム名を sequence 配列に実行順に指定して,それぞれのプログラムに関する詳細な設定を [programs.<プログラム名>] 以下に記述するということがわかってもらえれば大丈夫です.

programs テーブルでは,それなりに色々な設定を行うことができますが,本稿ではその1つ1つを網羅的に解説することはしません.そうした詳細な解説はリファレンスマニュアルできっちり行っているので,必要に応じて参照してください.

デフォルト設定

llmk は,ユーザが sequenceprograms の内容を毎回ゼロから書かずとも済むように,典型的な LaTeX 文書の処理を行う際に都合がいいようなデフォルト設定を持っています.デフォルトの sequence の設定では,今までに何度か言及したように (u)pLaTeX など DVI ファイルを生成するような LaTeX エンジンを用いた場合は dvipdfmx を利用して PDF 化を行います.それは,llmk のデフォルト sequence が本質的には次のようになっているからです7

# デフォルト sequence (抜粋)
sequence = ["latex", "dvipdf"]

現在では(少なくとも日本の TeX 界では)主流な方法ではありませんが,dvipdfmx の代わりに dvips + ps2pdf を用いて PDF 化を行いたい場合には,sequence を次のように指定するだけで事足ります.

# カスタム sequence
sequence = ["latex", "dvips", "ps2pdf"]

llmk のデフォルト sequence 設定では dvipsps2pdf プログラムが呼び出されることはありませんが,programs テーブルには,最初からこれらのプログラムについての設定が収録されています.そのため,単純に dvips + ps2pdf の経路を使いたいということだけであれば,programs テーブルについては一切いじる必要がありません.もちろん,これらのプログラムに何らかの特別なコマンドラインオプションを与えたいという場合には programs テーブルの指定も行う必要があります.

本稿では,便宜的に簡略したデフォルト sequence だけを簡単に紹介しましたが,実際の sequenceprograms テーブルの完全なデフォルト設定はもう少し複雑です.例えば,デフォルトの latex プログラムは aux ファイルを監視して相互参照が解決されるまでコマンド実行を繰り返す(ただし上限も設定されている)ようになっていたり,bib ファイルや idx ファイルがある場合には自動的に bibtexmakeindex コマンドが実行され,さらにこれらの実行後には再び latex プログラムによる処理が十分回数行われるようになっていたりします.

こうしたデフォルト設定の詳細についても,やはりリファレンスマニュアルを参照してください.

ちょっと注意(いわゆる「お気持ち」表明)

上記の解説で何となく察せたかもしれませんが,llmk の根幹にある制御機構はとても単純で,ワークフロー指定や,ある指定の下での振る舞いを把握することはそこまで難しいことではありません.それはそれでよいのですが,単純すぎるが故にソースファイル間の依存関係が複雑な場合や,処理途中の状況に応じて処理を切り替えるようなことをする場合には却って複雑になってしまうこともあり得ます.また単一ソースから複数形式での出力を行いたい(複数のタスクを定義したい)ようなニーズへのサポートもありません.冒頭で述べたように,llmk は「典型ケースでシンプルにワークフローを指定できるようにすること」を目指すものなので,複雑な処理が必要な非典型ケースでは必ずしも有効でありません.

llmk は何百ページもある書籍などの作成で複雑なことをしたい LaTeX のパワーユーザ向けというよりも,せいぜい数十ページの論文やレポート作成をしたいライトユーザ向けということを意識して設計されています.複雑なワークフロー制御をガシガシやっていくのが中心的な使い方ではなく,トップレベルの latexbibtex キーを使用して,利用したい LaTeX エンジンを簡単に指定するだけで PDF にするところまで “それっぽく” よしなに処理をやっておいて欲しいという方に向いている気がします.

当たり前のことですが,ユースケースに応じて適切なツールを選ぶようにしましょう.

さらなる情報(すべて英語)

本稿では llmk の「基本」を解説しました.さらに多くの情報が必要という方のために,これまでに私が作成してきた llmk に関するほかの文献もご紹介しておきます.これらはすべて英語ですが,本稿で解説した基本的なことを抑えた上であれば,細かな仕様などについての解説は英語であったとしてもそこまで読むのは大変ではないだろうと期待しています.

  • リファレンスマニュアル:llmk の仕様を細部まで詳細に記述しています.原則としてあらゆる仕様を完全に網羅することを意図して書いており,実際その目的をほぼ達成していると思いますが,一方で llmk のことを何も知らない人が頭から読むのには適さない可能性が高いです.本稿を一通り読まれた方であれば,頭から読んだり,あるいは必要に応じて辞書的に読んだりしても十分活用できるはずです.
  • TUG 2020 の講演ビデオ (YouTube):オンライン開催された TeX の国際会議 TUG 2020 の講演ビデオです.このトークも英語です.冒頭でデモンストレーションをしています.
  • TUGboat 記事:上記講演に対応する TUGboat の記事です.llmk の仕様を詳細に述べるのではなく,その設計思想を中心に解説しました.また latexmk や arara などの既存ツールとの比較についても議論しています.

おわりに

llmk は現段階では発展途上の LaTeX 文書用ビルドツールです.仕様と実装はこれまでにある程度整理し,実用に耐え得るものかどうか検証を重ねてきたつもりではありますが,まだまだ実装上のバグや,必ずしも合理的でない仕様が残っている可能性があります.そのようなバグや問題点にお気づきの方は,日本語でも構いませんので,GitHub や Twitter で作者 (wtsnjp) までお知らせいただければと思います.

本稿の解説を読んで,もし「llmk ってやつは使えそうだな」と思っていただけたようであれば,試していただけると嬉しいです.


  1. あくまで「非互換な仕様変更が行われにくいと推測される」内容を扱うというだけで,本稿に記述した部分の仕様は絶対で,今後何があっても変更されることがないと保証するものではありません. ↩︎

  2. TeX Live をフルスキーム以外でインストールしていて llmk が含まれていない場合は tlmgr install light-latex-make でインストールしてください. ↩︎

  3. ただしコメント文字の % と最初の + の間にはスペースを入れることができます. ↩︎

  4. そうは言っても llmk の機能をフルに活用するには TOML フィールドを利用する以外の方法はありません.llmk で処理をすることが最初からわかっていて文書作成を行う場合には TOML フィールドの利用をおすすめします. ↩︎

  5. この仕様は「llmk のワークフロー指定には TOML フィールドを優先的に使って欲しいから」というのもありますが,それ以上に他ツール向けにマジックコメントで llmk の利用を指定して,実際の処理を llmk に丸投げする,ということを行った際に,llmk がまた llmk 自身を指定するマジックコメントを解釈して無限再帰してしまうのを防ぐ意図もあります. ↩︎

  6. 解説がややこしくなるので args["%S"] を指定しましたが,本当は ["%T"] と指定する方が望ましいです. ↩︎

  7. これは厳密な llmk のデフォルト sequence 設定ではありません.本稿の解説に関係のあるところだけを抜粋した形です. ↩︎