satyrographos-repo-bin でパッケージをバイナリ配布している話

:::note info
これは「[SATySFi Advent Caleandar 2022](https://adventar.org/calendars/7515)」の5日目の記事です。
（4日目は [yozu さん](https://qiita.com/yasuo-ozu/items/bc8f4615ae20d936add9)、6日目は [yozu さん](https://adventar.org/calendars/7515)です。）
:::

SATySFiパッケージ[^1]のインストールは重いことで知られています。この原因として以下の2点が考えられます。

[^1]: SATySFi向けのライブラリパッケージも依存関係としてSATySFiを持っているため、ここでいう「重い」に含みます。

- ソースコードで配布されているため、ダウンロード容量が大きく、またコンパイルするのに時間がかかるため。
- 上記のコンパイルを行うためにはocaml-compilerやdune等、ocamlの世界で使われているツールが多数必要となり、それらをダウンロード＆コンパイル＆インストールする必要があるため。

これを解決するためにバイナリファイル版のパッケージリポジトリを作成しました。

https://github.com/yasuo-ozu/satyrographos-repo-bin

# 使用方法

`opam repository add` コマンドで追加します。 `--rank` オプションで優先度を指定することで、 `satyrographos-repo-bin` を優先して使うようになります。(`satyrographos-repo`の方は`satyrographos-repo-bin`にパッケージが存在しない場合のフォールバックとして機能する)

```bash
# creating local switch (optional)
opam switch create . --empty
eval $(opam env)

# Add repositories
opam repository add satysfi-external https://github.com/gfngfn/satysfi-external-repo.git
opam repository add satyrographos-repo-bin https://github.com/yasuo-ozu/satyrographos-repo-bin.git
opam repository add satyrographos-repo --rank 2 https://github.com/na4zagin3/satyrographos-repo.git

# List installable packages
opam list --all-versions --installable satysfi -o satyrographos

# install satysfi and satyrographos
opam install satysfi.0.8.0 satyrographos
```

# 注意点

- 今の所なぜだかMac版のパッケージは生成できていません。(本来はできるはず)
- satyrographos のwin64版はコンパイルに失敗しています。(ローカルで試せる環境がないのでデバッグできていない)

# リポジトリの構造

以下の2つのブランチから構成されます。

## main ブランチ

デフォルトブランチであり、`opam`コマンドから見えるファイルツリーです。後述する`build.sh`によって生成され自動的にコミットされます。以下のものを含みます。

- `packages` ディレクトリ ... opamが参照するパッケージデータベース
- `store`ディレクトリ ... バイナリを配布するためのアーカイブファイルがos, アーキテクチャ毎に分かれて入っている。
- `.github/workflows` ディレクトリ ... 作成したパッケージを実際にインストールするテストが含まれる

### パッケージファイについて

`package`ディレクトリ内のパッケージデータベースを構成する`opam`という名前のファイルで、例えば`packages/satysfi/satysfi.0.0.7+bin_x86_64_linux/opam`は以下のようになっています。

```opam
opam-version: "2.0"
synopsis: "A statically-typed, functional typesetting system"
description:
  "SATySFi is a typesetting system equipped with a statically-typed, functional programming language. It consists mainly of two “layers” ― the text layer and the program layer. The former is for writing documents in LaTeX-like syntax. The latter, which has OCaml-like syntax, is for defining functions and commands. SATySFi enables you to write documents markuped with flexible commands of your own making. In addition, its informative type error reporting will be a good help to your writing."
maintainer: "gfngfn"
authors: "gfngfn"
homepage: "https://github.com/gfngfn/SATySFi"
bug-reports: "https://github.com/gfngfn/SATySFi/issues"
depends: []
dev-repo: "git+https://github.com/gfngfn/SATySFi.git"
url {
  archive: "https://github.com/yasuo-ozu/satyrographos-repo-bin/raw/main/store/archives/satysfi.0.0.7%2Bbin_x86_64_linux.tar.gz"
  checksum: "eccb0693521e3330d0ebe517ff197946"
}
available: [ os = "linux" & arch = "x86_64" ]
```

これは、`satyrographos-repo`の対応するファイル

```opam
opam-version: "2.0"
maintainer: "gfngfn"
authors: [
  "gfngfn"
]
homepage: "https://github.com/gfngfn/SATySFi"
dev-repo: "git+https://github.com/gfngfn/SATySFi.git"
bug-reports: "https://github.com/gfngfn/SATySFi/issues"
build: [
  ["mkdir" "-p" "temp"]
  [make "-f" "Makefile" "PREFIX=%{prefix}%"]
]
install: [
  [make "-f" "Makefile" "install" "PREFIX=%{prefix}%"]
]
remove: [
  [make "-f" "Makefile" "uninstall" "PREFIX=%{prefix}%"]
]
# Packages whose version suffix is "+satysfi" are distributed on satysfi-external-repo.
depends: [
  "ocaml" {>= "4.10.0"}
  "batteries"
  "camlimages" {>= "5.0.1"}
  "camlpdf" {= "2.3.1+satysfi"}
  "core_kernel" {>= "v0.13" & < "v0.15"}
  "cppo" {build & >= "1.6.4" & < "1.7.0"}
  "dune" {build}
  "menhir"
  "ocamlfind" {build}
  "otfm" {= "0.3.7+satysfi"}
  "ppx_deriving"
  "re" {build}
  "uutf"
  "yojson-with-position" {= "1.4.2+satysfi"}
  "omd" {< "2.0.0~"}
]
synopsis: "A statically-typed, functional typesetting system"
description: """
SATySFi is a typesetting system equipped with a statically-typed, functional programming language. It consists mainly of two “layers” ― the text layer and the program layer. The former is for writing documents in LaTeX-like syntax. The latter, which has OCaml-like syntax, is for defining functions and commands. SATySFi enables you to write documents markuped with flexible commands of your own making. In addition, its informative type error reporting will be a good help to your writing."""
url {
  src: "https://github.com/gfngfn/SATySFi/archive/refs/tags/v0.0.7.tar.gz"
  checksum: [
    "sha512=807868e20b5b27e1e84a50d14af9e03bfd0ffdc5bac7635c58b416e369b7da56bf6f8e16df06b517a7ab586dbdac41fe32d36fe7bf0c036a7d845515425ab7ab"
  ]
}
```

から一部の項目(`install`, `build`, `remove`)を除き、依存関係`depends`を空にし、`url`を追加した形式となっています。このファイルは以下のスクリプトで生成されています[^2]。

[^2]: ちなみに `opam-bin` という、opamパッケージのバイナリパッケージングを行うソフトウェアが存在しますが、「既にメンテナンスされていないように見えること」「生成されるアーカイブファイルが大きすぎてGitHubに上げるのが適切でないように思えること」「最新の環境でうまく動かなかったこと」から採用していません。

```bash
$OPAM show --raw --no-lint "$PKGNAME" | $SED -e '/^name:/d' -e '/^version:/d' | \
$SED -ze 's/url\s*{[^}]*}//' | $SED -ze 's/depends:\s*\[[^]]*\]/depends: []/' | \
$SED -ze 's/build:\s*\[[^]]*\(\[[^]]*\][^]]*\)*\]//' | \
$SED -ze 's/install:\s*\[[^]]*\(\[[^]]*\][^]]*\)*\]//' | \
$SED -ze 's/remove:\s*\[[^]]*\(\[[^]]*\][^]]*\)*\]//' > "$DEST_OPAM_PATH"
echo "url {" >> "$DEST_OPAM_PATH"
echo "  archive: \"https://github.com/yasuo-ozu/satyrographos-repo-bin/raw/main/store/archives/${URL}.tar.gz\"" >> "$DEST_OPAM_PATH"
echo "  checksum: \"$MD5SUM\"" >> "$DEST_OPAM_PATH"
echo "}" >> "$DEST_OPAM_PATH"
echo "available: [ os = \"$TARGET_OS\" & arch = \"$TARGET_ARCH\" ]" >> "$DEST_OPAM_PATH"
```

opamでは、インストール方法が指定されていないパッケージの場合は、 `url.src` に指定されたアーカイブファイルを展開してファイルを配置するという挙動を行います[^3]。そのため、別に用意しておいたアーカイブファイルをインストールさせることができています。

[^3]: 実際にはアーカイブファイルの中に含まれる `パッケージ名.install` というテキストファイルを参照して必要なファイルのみ配置します。

## `develop` ブランチ

本リポジトリを管理している人が主に編集するファイルです。

- [`update.sh`](https://github.com/yasuo-ozu/satyrographos-repo-bin/blob/develop/update.sh) ... メインのスクリプト。`satyrographos-repo`に含まれる`satyrographos`, `satysfi`パッケージのうち、まだバイナリパッケージが生成されていないものを探してパッケージの生成を行う。
- `.github/workflows` ... `update.sh` を実行するためのCI

# まとめ

パッケージ、OS, アーキテクチャによってはまだ対応できていないものもありますが、とりあえず本リポジトリを設定しておけば、場合によってはインストールが早くなるでしょう。
不完全な部分が多いですが、ぜひコントリビューションを検討いただけると幸いです。