TeX Live のスキームに含まれるパッケージを一覧したい話

2018-08-18   #TeX Live  #Ruby  #golf 

某ソフトウェアの保守作業をしていて,各スキームに含まれるパッケージの一覧が必要になりました.TeX Live のスキームというのは,要するに TeX Live のサブセットのことで,TeX Live をインストールしたいユーザがそれぞれの目的に適ったものを選択できるようにすることを意図して定義されたもののようです.具体的には

  • scheme-full: 全部入り TeX Live
  • scheme-basic: LaTeX するのに必要な最低限のもの
  • scheme-small: scheme-basic に XeTeX, MetaPost などを加えた小さめのサブセット
  • scheme-medium: 中ぐらいのサブセット

などがあります1.ちなみに,スキームの他にもコレクションと呼ばれるグループもあります.スキームがインストール上の便宜のために存在しているのに対して,コレクションは似た目的をもつパッケージを集めたものだと思います.

話が逸れましたが,とにかく各スキームに含まれるパッケージ,すなわち,あるスキームを選択したときにインストールされるパッケージの一覧が欲しいわけです.

まずはもちろん,ググりました.そして,アレWiki に次のように書いてあるのを見つけました:

詳細は texlive.tlpdb というファイルを見ればわかります

そう言われたので texlive.tlpdb を見ました

「……なるほど,わからん」

どういうことかというと,texlive.tlpdb には次のような包含関係2が(独特の形式で)列挙されています:

  • scheme-medium は collection-latex, collection-luatex, …… を含む
  • collection-luatex は collection-basic, lualibs, …… を含む

こんな調子が全部でおよそ2万5千行です.こんなの「見て」わかるわけがない!

とはいえ,TeX Live チームは tlmgr という素晴らしいツールを用意してくれていて,さすがに texlive.tlpdb を目 grep したり独自にパースしたりしなくても

$ tlmgr info --list <package|collection|scheme> ...

のようにすると,引数に与えたパッケージ(またはコレクション,スキーム)に関して texlive.tlpdb に記載されている情報を表示してくれます3

これで一件落着かと思いきや,まだそういうわけにもいきません.試しに scheme-basic の情報を表示してみます.

$ tlmgr info --list scheme-basic
package:     scheme-basic
category:    Scheme
shortdesc:   basic scheme (plain and latex)
longdesc:    This is the basic TeX Live scheme: it is a small set of files sufficient to typeset plain TeX or LaTeX documents in PostScript or PDF, using the Computer Modern fonts.  This scheme corresponds to collection-basic and collection-latex.
installed:   Yes
revision:    25923
sizes:       147293k
relocatable: Yes
depends:
	collection-basic
	collection-latex

ここで表示されるのは scheme-basic が collection-basic, collection-latex を含むということだけで,さらに collection-basic や collection-latex が何を含んでいるかを調べないことには「パッケージの一覧」を得ることはできません.scheme-basic は最小のスキームなので,それぞれのコレクションが何を含むのか tlmgr で調べてもいいかもしれませんが,もっと大きなスキームについてはそんなことをしていては日が暮れてしまいます.

残念ながら tlmgr には再帰的に包含関係を展開して表示する機能はなさそうだったので,自分でスクリプトを書くことにしました.しかし「見ればわかる」と言われている程度の情報を得るわけですから,そんな大掛かりなスクリプトを書くこともなさそうです.きっと短く1行でも短く書けることでしょう……

書けました:
$ echo scheme-basic | ruby -rjson -ne'def f a;(c=(b=JSON.parse(`tlmgr info --json #{[*a]*" "}`).flat_map{|r|r["depends"]}).grep /col.*-.*/)==[]?b:b+=(f c)end;p (f $_).uniq.sort'
うわぁ……

真面目な話をしましょう.

(見ればわかると思いますが)そんなに真剣に書いていないので,すべてのケースできちんと動作するかは保証できませんが,一応 echo <name> のところに適当なスキームやコレクションの名前を入れると,包含されるパッケージの一覧が表示されるはずです.

なるべく回数を減らす工夫はしていますが再帰的に tlmgr info の呼び出しを行うので動作はかなり遅いです4.上の scheme-small に含まれるパッケージを一覧にする例では,手許の PC (MBA 2017, 1.8 GHz Intel Core i5) で50秒ほどかかります.

かなりアレな解決法だった気もしますが,とりあえず求めていたパッケージの一覧が手に入ったのでよしとしましょう.


W「上記のワンライナーはまだもっと短くなる気がする.求ム,挑戦者」


  1. 詳しくは TeX Wikiアミノフェンさんのホンキの解説をご参照ください. [return]
  2. ここでは文脈上自然なので「包含関係」と言ってしまいますが,TeX Live 的に正しい用語は「依存関係」のようです. [return]
  3. --list を付けないと包含関係は省略されてしまいます.また,tlmgr の出力をプログラムで扱いたい場合は --json オプションの方が便利でしょう. [return]
  4. tlmgr info が遅いのは,結局のところ 13MB 近いサイズのある texlive.tlpdb の読み込みに時間がかかるからで,その他の処理はそこまで遅くないはずです.要するに,texlive.tlpdb のパースが1回で済むようなコードを書けばいいのですが,これだけのことをするのには労力に見合わない気がします(tlmgr が対応してくれるといいのだけどなぁ). [return]