索引と参考文献をつくる makeindex・xindy・bibtex・biber は、LaTeX のマクロではなく 独立したプログラム です。だから引数の書き方も、終了コードも、ログの置き場所も、それぞれ勝手に決まっています。そして、ここに CI を静かに壊す事実が一つあります——makeindex は項目を捨てても終了コード 0 を返し、bibtex は「その文献が見つからない」と警告しても 0 を返します。 失敗はビルドの成否ではなく .ilg と .blg に書かれるのです。このページは、これら四つを LaTeX の側からではなくコマンドの側から見ます。索引の書き方そのものと文献データベースの設計は別ページが担当します。
bibtex doc と makeindex doc.idx — 拡張子を書くのはどちらか
答えは覚えるしかありません。文献系は拡張子なし、索引系は拡張子つき。 bibtex と biber は文書のジョブ名を受け取り、自分で .aux や .bcf を開きます。ここを間違えるとメッセージは意外なほど不親切です。bibtex doc.tex と打つと I couldn t open file name doc.tex.aux が返り、終了コードは 1。biber doc.tex は ERROR - Cannot find 'doc.tex.bcf'! です。どちらも「その名前に拡張子を足したもの」を探しているだけで、.tex が不正だとは言ってくれません。一方 makeindex・upmendex・texindy は入力ファイルそのものを受け取るので doc.idx と書きます。出力名を変えたいときは -o、スタイルを指定したいときは -s を使います。
bibtex doc # job name, no extension -> reads doc.aux, writes doc.bbl
biber doc # job name, no extension -> reads doc.bcf, writes doc.bbl
makeindex doc.idx # the file itself -> writes doc.ind and doc.ilg
upmendex -o doc.ind doc.idx
texindy -C utf8 -L german-din -o doc.ind doc.idx終了コードの一覧 — CI が失敗を見逃す場所
下の表は、この機械(TeX Live 2024)で実際に走らせて確かめた値です。読み取ってほしいのは、警告と失敗の線引きがプログラムごとに違うということ。bibtex は「エラーメッセージ」を出したときだけ 2 を返し、引用キーが .bib に無いだけなら警告扱いで 0 を返します。makeindex は入力ファイルが無ければ 1 を返しますが、中身の項目をいくつ捨てても 0 のままです。つまり latexmk や CI の成否だけを見ていると、索引から項目が消え、文献が空欄になったまま緑のチェックが付きます。索引と文献に関しては、終了コードではなく .ilg と .blg を検査するのが正しい防御です。
| 状況 | 終了コードとログ(TeX Live 2024 で実測) |
|---|---|
makeindex (entries rejected) | 0。捨てた項目は .ilg にだけ残る。-q を付けると画面にも出なくなる |
makeindex (no input file) | 1。Input index file nosuch.idx not found. と使い方の一行が出る |
upmendex (no input file) | 255。Nothing written in output file. と 1 errors, written in doc.ilg. |
bibtex (warnings only) | 0。Warning--I didn t find a database entry for "key" は失敗にならない |
bibtex (error messages) | 2。.bib の構文エラーや I found no database files のとき |
biber | 警告だけなら 0、ERROR - を出したら 2。集計は INFO - WARNINGS: 1 の形で末尾に出る |
makeindex の .ilg を読む——捨てられた項目はここにいる
makeindex は走るたびに .ind(組版される索引)と .ilg(作業の記録)を書きます。正常な回は素っ気ないものです——Scanning input file doc.idx....done (6 entries accepted, 0 rejected). Sorting entries....done (19 comparisons). Generating output file doc.ind....done (20 lines written, 0 warnings). 見るべきは括弧の中の数字だけ、と言ってもいいくらいです。試しに壊れた .idx を食わせると、accepted の数が減り、理由が並びます——!! Input index error (file = bad.idx, line = 4): に続いて -- Incomplete first argument (premature LFD). それでも終了コードは 0 のままです。accepted の数が \index を書いた数と合っているか、これを見るだけで大半の事故は防げます。ちなみに TeX Live 2024 同梱版は makeindex 2.17 で、起動時に自分を (kpathsea + Thai support) と名乗ります。
makeindex doc.idx
# This is makeindex, version 2.17 [TeX Live 2024] (kpathsea + Thai support).
# Scanning input file doc.idx....done (6 entries accepted, 0 rejected).
# Sorting entries....done (19 comparisons).
# Generating output file doc.ind....done (20 lines written, 0 warnings).
grep -c "^\\\\indexentry" doc.idx # compare this with "entries accepted"
grep "rejected" doc.ilg # the number CI should be watchingこの地味なプログラムには、意外な出自があります。作者は Pehong Chen ですが、man ページの謝辞には 「Leslie Lamport contributed significantly to the design of MakeIndex」 とあります。LaTeX を書いた本人が索引プログラムの設計に深く関わっていた、というわけです。\index の書式が LaTeX の他の命令と地続きに感じられるのは偶然ではありません。なお \index{読み@表示} の @ の使い方や、アクセント付きの語にソートキーを与える話は、索引そのもののページに詳しくあります。
どの索引プログラムを選ぶか——同じ 4 語を三者に並べさせてみる
選択の基準は一つ、扱う言語に照合(collation)が要るかどうかです。Zeta・Ähre・Apfel・Öl の 4 語をそのまま .idx に入れて三つのプログラムに渡すと、違いは一目で出ます。makeindex は Apfel, Zeta, Ähre, Öl の順にしました——Ä と Ö は UTF-8 のバイト値が Z より大きいので、Z の後ろに落ちます。texindy -C utf8 -L german-din と upmendex はどちらも Ähre, Apfel, Öl, Zeta と並べました。ドイツ語の DIN 規則どおり Ä を A として、Ö を O として扱った結果です。xindy(Joachim Schrod 作、release 2.5.1)は言語モジュールで、upmendex(version 1.08)は ICU 74.2 の照合アルゴリズムで、同じ答えに到達しています。
| プログラム | 同じ 4 語をどう並べたか | 選ぶ場面 |
|---|---|---|
makeindex | Apfel, Zeta, Ähre, Öl — 非 ASCII は Z の後ろへ | 英語だけ。手でソートキーを与える覚悟があるとき |
texindy | Ähre, Apfel, Öl, Zeta — -L german-din 指定 | ヨーロッパ諸語。-L に言語名を渡すだけで済む |
upmendex | Ähre, Apfel, Öl, Zeta — ICU による照合 | 日本語や多言語混在。makeindex のスタイルをそのまま使える |
mendex | 入力の文字コードを推測する。(guessed encoding #4: UTF-8 = utf8) と表示 | 古い pLaTeX の資産を触るとき。新規なら upmendex |
実務上の注意を二つ。第一に、texindy の書き出す .ind は makeindex のものと構造が違います。頭文字ごとの見出しを付けるために \lettergroup を使い、その定義も \providecommand で自分から書き込みます。文書側でこれを再定義していると衝突するので、乗り換えるときは一度出力を見てください。第二に、xindy は Common Lisp(この版では CLISP 2.49.93)の上で動くため起動が重く、巨大な索引では体感できるほど時間がかかります。日本語が混じるなら upmendex が速くて素直です。
bibtex の .blg の末尾にある奇妙な一覧
bibtex doc を走らせたあと doc.blg を開くと、警告のあとに見慣れない表が続きます——if$ -- 47、while$ -- 2、swap$ -- 1、substring$ -- 6……。これは BibTeX が内部に持つスタックマシンの命令が、それぞれ何回呼ばれたかの集計 です。.bst というスタイルファイルは設定ファイルではなく、この仮想機械のためのプログラムだからです。この回では 237 回でした。同じ .blg の冒頭には Capacity: max_strings=200000, hash_size=200000, hash_prime=170003 という行もあります。1980 年代のメモリ事情がそのまま残っている数字で、巨大な文献リストで Sorry---you ve exceeded BibTeX s に始まる悲鳴が上がるのはこの上限です。作者は Stanford の Oren Patashnik、TeX Live 2024 に入っているのは BibTeX 0.99d です。
BibTeX には 8 ビット・Unicode 対応の派生があり、TeX Live 2024 にはすべて入っています。bibtex8 は「8-bit Big BibTeX version 0.99d-x4.02」、bibtexu は「UTF-8 Big BibTeX」で ICU 74.2 を組み込んでいます。日本語では pbibtex(pTeX 系)と upbibtex(upTeX 系、upBibTeX 0.99d-j0.36-u1.30 (utf8.uptex) と名乗ります)を使います。どれも本体は同じ 0.99d 系統で、違うのは文字の扱いと照合だけです。よく出るエラーは二種類。.bib の書式が壊れていれば Illegal end of database file---line 14 of file broken.bib と I m skipping whatever remains of this entry が出て終了コードは 2、\bibliography の指すファイルが無ければ I found no database files---while reading file doc.aux です。
biber — 一行ごとに INFO / WARN / ERROR と名乗るログ
biber は Perl で書かれた新しい設計で、ログの体裁からしてまったく違います。INFO - This is Biber 2.19 に始まり、読み込んだ .bcf、見つけた引用キーの数、適用したロケール、書き出した .bbl までを一行ずつ INFO - の札を付けて報告します。問題があれば WARN - I didn t find a database entry for 'missingkey' (section 0) のように札が変わり、最後に INFO - WARNINGS: 1 と集計が出ます。この機械可読性が bibtex との最大の実務差です。 CI では grep で WARN - と ERROR - を数えるだけで検査が書けます。よくある事故は入力名の間違いで、biber doc.tex は ERROR - Cannot find 'doc.tex.bcf'! を出して 2 で終わります。なお biber も bibtex も同じ .blg という拡張子を使うので、両方試したときは中身の一行目を見て取り違えを防いでください。
biber doc
# INFO - This is Biber 2.19
# INFO - Found 2 citekeys in bib section 0
# INFO - Output to doc.bbl
# WARN - I didn t find a database entry for 'missingkey' (section 0)
# INFO - WARNINGS: 1
# a CI check that the exit code will not give you
grep -c "^WARN -\|^ERROR -" doc.blg
grep "rejected" doc.ilg実行の順番と、それを数えてくれる相手
順番は「組版 → 索引と文献 → 組版 → 組版」です。1 回目で LaTeX が .idx と .aux(biblatex なら .bcf)を書き、そこへこれらのプログラムを走らせて .ind と .bbl を作り、もう一度組版して取り込み、番号がずれていればさらにもう一度——という段取りになります。回数が固定でないのが厄介なところで、だからこそ latexmk のようなビルドツールが存在します。手で打つのは、どの段で止まったのかを切り分けたいときだけで十分です。切り分けの順序も決まっています。まず .idx や .bcf が生成されているかを見る(無ければ LaTeX 側の問題)、次に .ilg と .blg を読む(あればプログラム側の問題)、最後にもう一度組版して .ind と .bbl が本文に入ったかを見る。この三段で、原因はほぼ一点に絞れます。
# the classic Japanese sequence, written out
uplatex paper # writes paper.aux and paper.idx
upbibtex paper # reads paper.aux -> paper.bbl
upmendex paper.idx # reads paper.idx -> paper.ind
uplatex paper # pulls both in
uplatex paper # settles the numbering
# and the same thing delegated
latexmk paper.tex