# Release is the Word

> Source: https://www.ymotongpoo.com/works/lyse-ja/24_release_is_the_word/


## それでも私は実行可能ですか？

なんと遠くまで来たことでしょう。 たくさん実装して、たくさん概念を学び、しかしまだ1つのErlang実行ファイルも世に出していません。 あなたも、Erlangシステムを立ち上げるには、特にコンパイラとかいうやつを呼ぶ多くの言語と比較しても、たくさんの努力が必要だ、ということに同意するでしょう。

<img src="pizza.png" class="align-right" alt="image" />

もちろんそれはまったくもって正しいです。 私たちはファイルをコンパイルし、アプリケーションを実行して、依存関係を確認して、クラッシュなどを処理することができますが、機能的なErlangシステムを簡単にデプロイまたは出荷できなければ、あまり便利とは言えません。 ピザハットのピザが、冷めた状態でしか宅配されないとしたら、どれだけの価値があるでしょうか。（申し訳ないですけど、冷たいピザが好きな人の話はここでは置いておきます）

OTPチームは、現実のシステムが現実にさせるという話になったときに、私たちをほったらかしにはしませんでした。 OTPリリースは、アプリケーションを最小限のリソースと依存でパッケージングする助けとなるようにつくられたシステムの一部です。

## 水漏れパイプを直す

最初のリリースでは、前の章の `ppool` と `erlcount` の両アプリケーションを再利用します。 しかし、それに取り掛かる前に、あちこちと何点か変更する必要があります。 あなたが本書に沿って自分のコードを書いているならば、両アプリケーションともに `release/` と言う名前の新しいディレクトリにコピーしましょう。以後この章ではそのディレクトリに対して作業を行います。

<img src="pipes.png" class="align-right" alt="image" />

`erlcount` に関して困ってしまう最初の事柄は、それが稼働を終えたら、VMが上がったままで何もしないままであることです。 たいていのアプリケーションに対しては永遠に稼働していてほしいと思いますが、今回は違います。 稼働し続けて欲しいと思っていたのは、シェルで色々と試しながら、手動でアプリケーションを起動する必要があったからであって、もうその必要は無いのです。

この理由から、BEAM仮想マシンを正しい手順で終了するコマンドを追加しました。 これを配置する最適な場所は、 `erlcount_dispatch.erl` 自身のterminate関数内です。その理由はこの関数が結果を取得した後に呼ばれるからです。 すべてを解体する完璧な関数は、 `init:stop/0` です。 この関数はかなり複雑ですが、私たちのアプリケーションを順序良く終了する面倒を見て、ファイルディスクリプタやソケットを取り除く等を、私たちの代わりにすべて行なってくれます。 したがって新しいstop関数は次のようになります：

``` erlang
terminate(_Reason, _State, _Data) ->
    init:stop().
```

そして、コード自体はこれで完成です。 まだやることはちょっとだけあります。 前の2つの章でアプリケーションファイルを定義したときに、それらのアプリケーションを稼働させるために必要最低限の情報だけを使ってファイルを作成しました。 Erlangを無事に動かすためにはもう2、3のフィールドが必要です。

まず、リリースを作る際に必要なErlangツールは、もう少し緻密なアプリケーションの説明書きを必要とします。 もちろんリリースのためのツール自体がドキュメントは理解することはありませんが、それでも開発者が最低でもアプリケーションがどんなものかすら書き残さないようなコード対しては直感的に心配します。 このような理由から、 `ppool.app` と `erlcount.app` の両方に説明用のタプルを追加する必要があります。

`ppool` には次のタプルを追加します：

``` erlang
{description, "Run and enqueue different concurrent tasks"}
```

`erlcount` には次のタプルを追加します：

``` erlang
{description, "Run regular expressions on Erlang source files"}
```

これで、別のシステムを検査するときに、何が行われているかをより理解できるようになりましたね。

非常に熱心な読者であれば、私がどこかですべてのアプリケーションは `stdlib` と `kernel` に依存している、と言ったことを覚えていることでしょう。 しかしながら、私たちの2つのアプリケーションファイルはこの2つのどちらについても触れていません。 いま変更した各々のアプリケーションファイルに `stdlib` と `kernel` についての記述を追加しましょう。 `ppool` アプリケーションファイルでその情報を追加するには次のタプルを追加する必要があります：

``` erlang
{applications, [stdlib, kernel]}
```

同様に、 `{applications, [stdlib, kernel, ppool]}` として、 `stdlib` と `kernel` の2つのアプリケーションを `erlcount` アプリケーションファイルにも追加します。

<div class="admonition">

**Don't Drink Too Much Kool-Aid**

リリースを手作業で行うときに（そしてすぐ後で触れるsystoolsを使ってリリースするときでさえも）この作業が実質何も影響を与えていないとしても、リストの中にこの2つのライブラリを追加することは絶対的に必要不可欠です。

`reltool` （この章で紹介するもう1つのツール）を使ってリリースする人々は、リリースしたものがきちんと動作するために、これらのライブラリアプリケーションが正しい順番でリストに追加している必要があります。 からかっているわけではありません。 私は、この章を書いているときにこの作業を忘れて、一晩かかっていったいどこがおかしいのかを探す羽目になり、結果最初にこの作業をしていなかったのだと気づきました。

Erlangのリリースシステムは、（非常に特別な場合を除いて）すべてのアプリケーションが依存しているのだから、暗黙的にこれらのアプリケーションを追加できるべきだ、と議論されてきました。 悲しいかな、まだそうではないのです。 私たちが自分でこの作業をしなければいけないのです。

</div>

終了関数を正しい場所に書いて、アプリケーションファイルを更新する、などの作業をしました。 リリース作業を始める前に最後にすることは、アプリケーション全体をコンパイルすることです。 Emakefileがある各ディレクトリで（ `erl -make` コマンドで）次々とコンパイルします。 これをやらなければ、Erlangのツールがあなたの代わりにこれを行なってくれはしないので、稼働しないリリースを打つことになります。 あいたたた。

## systoolsでリリースする

`systools` アプリケーションはErlangのリリースを行う上で最も簡単なものです。 ErlangリリースのEasy-Bake Oven®（おままごとオーブン）のようなものです。 `systool` のオーブンで美味しいリリースを作るには、まず基本的なレシピと材料のリストが必要です。 私たちの `erlcount` アプリケーションで上手に最低限のErlangのリリースを行うために、手書きで材料を書くとすると、次のようになるでしょう：

> erlcount 1.0.0の材料  
>
> > - お好きなErlangランタイムシステム（ERTS)
> > - 標準ライブラリ
> > - カーネルライブラリ
> > - `ppool` アプリケーション（必須）
> > - `erlcount` アプリケーション

私が料理が下手くそなことはお話しましたっけ。 私はパンケーキすら焼けるかどうかわかりませんが、少なくともOTPリリースのやり方は知っています。 `systools` を使ったOTPリリースのための材料リストは `erlcount-1.0.rel` と命名されたこのファイルのようになり、 `release/` ディレクトリの最上位に置かれます：

``` erlang
{release,
 {"erlcount", "1.0.0"},
 {erts, "5.8.4"},
 [{kernel, "2.14.4"},
  {stdlib, "1.17.4"},
  {ppool, "1.0.0", permanent},
  {erlcount, "1.0.0", transient}]}.
```

これは、先ほどの手書きのレシピとまったく同じ事を伝えていますが、さらにアプリケーションがどのように（一時的、暫定的、永続的）起動して欲しいかもわかります。 必要に応じて異なるErlangバージョンの異なるライブラリと組み合わせたり、適合したり出来るようにバージョンの記載もあります。 ここに書いてあるすべてのバージョン番号を取得するには、次のような関数呼び出しを行うだけでよいです：

``` erlang
$ erl
Erlang R14B03 (erts-5.8.4) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.8.4  (abort with ^G)
1> application:which_applications().
[{stdlib,"ERTS  CXC 138 10","1.17.4"},
 {kernel,"ERTS  CXC 138 10","2.14.4"}]
```

ここでは、R14B03を稼働させていました。 リリース番号（バージョンは5.8.4）の後にERTSのバージョンがあるのがお分かり頂けると思います。 その後 `application:which_applications()` を稼働しているシステム上で呼び出すことによって、私が必要としている `kernel` （2.14.4）と `stdlib` （1.17.4）の2つのバージョンを確認することが出来ます。 また一方で、必要なバージョンを明確にしておくと、複数のバージョンのErlangをインストールしていた場合に、あなたが行なっていることに一切悪影響を及ぼさない古いバージョンの `stdlib` だけを得ることができるので、便利です。

<img src="cupcake.png" class="align-left" alt="image" />

また、私が `release` に `erlcount` という名前を付けて、そのバージョンを1.0.0にしたことにも注目してください。 アプリケーションファイルに明記したように、 `ppool` と `erlcount` アプリケーションは両方共バージョン1.0.0を稼働させることにしているので、まだこのリリースは `ppool` と `erlcount` に対しては行われていません。

さて、もう私たちのアプリケーションはすべてコンパイルされて、必要な材料もおままごとオーブンの素晴らしいコンセプトも揃いました。 私たちに必要なのは実際のレシピです。

いまからいくつかの概念を知ろうとしています。 レシピはいくつかのことを教えてくれます。材料をどの順番で、どのように混ぜて、どのように調理していくか、などです。 追加する順序に関する部分は個々のアプリケーションファイルの依存関係のリストによって確認できます。 `systools` アプリケーションは十分賢いので、アプリケーションファイルを見て実行する前に何が必要かを理解してくれます。

Erlangの仮想マシンは、ブートファイルと呼ばれるものから取得できる基本的な設定で、自分自身を起動できます。 事実、独自の `erl` アプリケーションをシェルから起動する際には、暗黙のうちにErlangランタイムシステム（ERTS）をデフォルトのブートファイルで呼んでいます。 ブートファイルは「標準ライブラリを読み込む」「カーネルアプリケーションを読み込む」「与えられた関数を実行する」などの基本的な操作手順を与えます。 ブートファイルはブートスクリプトと呼ばれるものから生成されたバイナリファイルです。ブートスクリプトにはいま書いたような操作手順がタプル形式で書いてあります。 それではブートスクリプトの書き方を見てみましょう。

まず、次のような書き出しで始めます：

``` erlang
{script, {Name, Vsn},
 [
  {progress, loading},
  {preLoaded, [Mod1, Mod2, ...]},
  {path, [Dir1,"$ROOT/Dir",...]}.
  {primLoad, [Mod1, Mod2, ...]},
  ...
```

すいません、冗談です。私たち含め、誰もそんなことに時間を掛けられるほど暇な人はいません。 ブートスクリプトは `.rel` ファイルから簡単に生成出来ます。 Erlang VMを `release/` ディレクトリにいる状態で起動して、次のようなコマンドを実行するだけです：

``` erlang
$ erl -env ERL_LIBS .
...
1> systools:make_script("erlcount-1.0", [local]).
ok
```

ここで `release/` ディレクトリの中身を見てみると、 `erlcount-1.0.script` や `erlcount-1.boot` といった新しいファイルが沢山できているのがわかると思います。 このとき、 `local` オプションというのは、リリースが、現在インストールされているマシンだけではなく、どこでも実行出来るようにするためのものです。 [確認すべきたくさんのオプション](http://www.erlang.org/doc/man/systools.html) がありますが、 `systools` は `reltool` （次の章で触れます）ほど強力ではないので、いまはそれほど深く調べてません。

どのような場合でもブートスクリプトがあるわけですが、配布するにはまだコードが十分ではありません。 Erlangシェルに戻って、次のコマンドを実行しましょう：

``` erlang
2> systools:make_tar("erlcount-1.0", [{erts, "/usr/local/lib/erlang/"}]).
ok
```

あるいはWindows 7では次のようにします：

``` erlang
2> systools:make_tar("erlcount-1.0", [{erts, "C:/Program Files (x86)/erl5.8.4"}]).
ok
```

ここで、 `systools` はあなたのリリースファイルと（Erlangランタイムシステムのオプションのために）ERTSを探します。 ERTSのオプションを省略した場合には、リリースは自己実行不能になり、実行するにはシステム内にErlangがすでにインストールされている必要が出てきます。

上記の関数を呼び出すことで、 `erlcount-1.0.tar.gz` と言う名前のアーカイブファイルが作成されました。 展開すると次のようなディレクトリ構造を確認できると思います：

``` none
erts-5.8.4/
lib/
releases/
```

`erts-5.8.4` ディレクトリはランタイムシステムを含んでいることに気がつくと思います。 `lib/` ディレクトリは必要なアプリケーションをすべて含んでいて、 `releases` にはブートファイル等が入っています。

上のディレクトリ構造が確認できるディレクトリに移動しましょう。 そこで、 `erl` をコマンドラインから叩いてみましょう。 まず、 `erl` の実行ファイルとブートファイルがどこにあるかを指定します。（ `.boot` の拡張子は要りません） Linuxでは次のように実行します：

``` bash
$ ./erts-5.8.4/bin/erl -boot releases/1.0.0/start
```

このコマンドはWindows 7上のWindows PowerShellにおいても同様です。

<div class="admonition">

**Don't Drink Too Much Kool-Aid**

リリースがどのシステムでも動作するという保証はどこにもありません。 もしあなたが純粋なErlangコードをHiPEでネイティブコンパイルすることなしに使っているならば、コードは移植可能です。 問題は、ERTSがそれ自身で動作しない状態で出荷された場合です。この場合は多く異なるプラットフォームごとにバイナリパッケージを作成する必要があるか、あるいは単にBEAMファイルを関連するERTSなしに出荷し、ユーザに自分のマシン内にあるErlangシステム上でそれを実行するようにお願いするかのどちらかです。

</div>

コマンドをコンピュータ上のどこからでも実行できるように、絶対パスを指定することもできます。 しかし、いまはその方法では実行しないで下さい。 カレントディレクトリ内にソースファイルがないので意味がありません。 絶対パスを使って実行すると、解析したいディレクトリに移動でき、ファイルをそこから呼び出せるようになります。 （私が行ったように）相対パスを使って実行し、 `erlcount` アプリケーションを呼び出した場合には、コードがどのディレクトリを走査するか設定できたということになります。 `-erlcount directory "'<ディレクトリへのパス>'"` をコマンドに追加してみましょう。 Erlangとはわからないように、 `-noshell` という引数も追加しましょう。 私のマシン上では次のような実行結果になりました：

``` bash
$ ./erts-5.8.4/bin/erl -boot releases/1.0.0/start -erlcount directory '"/home/ferd/code/otp_src_R14B03/"' -noshell
Regex if\s.+-> has 3846 results
Regex case\s.+\sof has 55894 results
```

絶対パスを使った場合には次のようなコマンドになります：

``` bash
$ /home/ferd/code/learn-you-some-erlang/release/rel/erts-5.8.4/bin/erl -boot /home/ferd/code/learn-you-some-erlang/release/rel/releases/1.0.0/start -noshell
```

これを起動したときはいつでも、これが走査されるディレクトリとなります。 これをシェルスクリプト内やバッチファイル内にラップして、準備完了です。

## reltoolでリリースする

`systools` では不便なことがたくさんありました。 諸々の制御を行うにもあまり制御できず、またざっくりいってそのまま稼働させるにも不便が多いです。 ブートファイルに手動でパッチをあてたりするのはつらい作業です。 さらに、ファイルはいささか大きくなります。 リリース全体で20MB以上になりますし、もしもっと多くのアプリケーションを同梱しようとすれば状況はもっと悪くなります。 `reltool` を使うともっと強力な機能を得ることができるので快適にはなりますが、トレードオフとして複雑さは増します。

`reltool` はこのような設定ファイルで動作します：

``` erlang
{sys, [
    {lib_dirs, ["/home/ferd/code/learn-you-some-erlang/release/"]},
    {rel, "erlcount", "1.0.0",
     [kernel,
      stdlib,
      {ppool, permanent},
      {erlcount, transient}
     ]},
    {boot_rel, "erlcount"},
    {relocatable, true},
    {profile, standalone},
    {app, ppool, [{vsn, "1.0.0"},
                  {app_file, all},
                  {debug_info, keep}]},
    {app, erlcount, [{vsn, "1.0.0"},
                     {incl_cond, include},
                     {app_file, strip},
                     {debug_info, strip}]}
]}.
```

Erlangのこのユーザーフレンドリーさをよく見てください！ かなり正直に言って、reltoolを導入するのに楽な方法はありません。 これらの大量のオプションが必要で、これがなければ動作しません。 とてもややこしそうに思えるかもしれませんが、その裏にはロジックが組まれているのです。

まず最初に、reltoolは異なった階層の情報を含んでいます。 最初の階層はリリース全体に関わる情報です。 次の階層はアプリケーション固有の情報で、その後にモジュール固有の細かな制御を行うため階層があります：

![image](reltool-levels.png)

上の図のように、これらの階層それぞれに、異なるオプションが設定可能です。 設定可能なすべてのオプションを端から百科事典のように見ていくのではなく、いくつか本質的なオプションについて見ていき、その後あなたのアプリケーションによっては必要になるであろう設定について見ていきましょう。

最初のオプションは、特定のディレクトリに居続けたり、VMに渡す正しい `-env` 引数を設定したりするような、めんどうな必要項目を取り除く上で役に立ちます。 オプションは `lib_dirs` で、アプリケーションが置いてあるディレクトリのリストを設定します。 実際、 `-env ERL_LIBS <ディレクトリのリスト>` を追加する代わりに、 `{lib_dirs, [ListOfDirectories]` を設定することで、同様の結果を得られます。

その他に、reltoolの設定ファイルで必須なものは、 `rel` です。 このタプルは `systools` のために `.rel` ファイルに書いたものによく似ています。 上のデモファイルでは次のようなタプルを書いていました：

``` erlang
{rel, "erlcount", "1.0.0",
 [kernel,
  stdlib,
  {ppool, permanent},
  {erlcount, transient}
 ]},
```

これが、どのアプリケーションが正しく起動される必要があるかを設定しているものです。 このタプルのあとに、次の形式のタプルを追加します：

``` erlang
{boot_rel, "erlcount"}
```

この設定でreltoolに、誰かがリリース内にある `erl` バイナリを起動したときはいつでも、 `erlcount` リリースの中にあるアプリケーションを起動してほしい、と伝えます。 これら3つのオプション（ `lib_dirs`, `rel` そして `erlcount` ）で、正しいリリースを取得できます。

これらのタプルをreltoolがパース出来るようなフォーマットで書くと：

``` erlang
{sys, [
    {lib_dirs, ["/home/ferd/code/learn-you-some-erlang/release/"]},
    {rel, "erlcount", "1.0.0",
     [kernel,
      stdlib,
      {ppool, permanent},
      {erlcount, transient}
     ]},
    {boot_rel, "erlcount"}
]}.
```

やったね、これで `{sys, [Options]}` タプルに入れることができました。 私は、これを `erlcount-1.0.config` というファイル名で `release/` ディレクトリに保存しました。 これはどこでも好きな場所に保存して構いません。（とんでもなく速く書き込めたとしても、 `/dev/null` はだめですよ！）

そして、Erlangシェルを起動します：

``` erlang
1> {ok, Conf} = file:consult("erlcount-1.0.config").
{ok,[{sys,[{lib_dirs,["/home/ferd/code/learn-you-some-erlang/release/"]},
           {rel,"erlcount","1.0.0",
                [kernel,stdlib,{ppool,permanent},{erlcount,transient}]},
           {boot_rel,"erlcount"}]}]}
2> {ok, Spec} = reltool:get_target_spec(Conf).
{ok,[{create_dir,"releases",
...
3> reltool:eval_target_spec(Spec, code:root_dir(), "rel").
ok
```

ここでの最初の手順は設定を読み込んで `Conf` 変数に束縛します。 その後、これを `reltool:get_target_spec(Conf)` に渡します。 この関数を実行するとしばらく時間がかかったあと、処理に必要な大量の情報が返ってきます。 これらは無視して、ただ結果を `Spec` に保存します。

3つめのコマンドは `Spec` を引数に取って、reltoolに「私のリリース仕様を取得して、インストールされているErlangのパスをどれでもいいから選んで、それを `rel` ディレクトリに入れて下さい」と伝えます。 これで終わりです。 `rel` ディレクトリの中を見ると、多くのサブディレクトリがあることでしょう。

とりあえずそれは無視して、実行してみましょう：

``` bash
$ ./bin/erl -noshell
Regex if\s.+-> has 0 results
Regex case\s.+\sof has 0 results
```

おや、実行がちょっとすっきりしましたね。 ファイルのツリー構造を維持してさえいればこのファイルはどこに置いても大丈夫です。そして、どこでも好きなところから起動できます。

<img src="cuffs.png" class="align-left" alt="image" />

何かお気づきになったことがないですか。あったと期待しています。 バージョン番号まったく指定する必要がありませんでした。 reltoolはその点においてsystoolよりもちょっと賢いのです。 バージョンを指定しなければ、パス（ `code:root_dir()` または `lib_dirs` タプルの中に書いたもの）の中にあるものの中から自動的に最新のものを探してきます。

しかし、もし私が流行に鈍感で、クールではなく、時代遅れで、最新のアプリケーションには興味がなく、懐古主義だったらどうでしょうか。 まだディスコパンツを履いていて、古いERTSバージョンや古いライブラリバージョンを使いたがったらどうでしょう。（1977年よりは昔の格好はできません！）

ありがたいことに、Reltoolは古いバージョンのErlangを使う必要があるリリースも扱うことができます。 年長者を尊重することは、Erlangツールでは重要なコンセプトです。

古いバージョンのErlangがインストールされている場合は、 `{erts, [{vsn, Version}]}` というエントリを設定ファイルに追加することができます：

``` erlang
{sys, [
    {lib_dirs, ["/home/ferd/code/learn-you-some-erlang/release/"]},
    {erts, [{vsn, "5.8.3"}]},
    {rel, "erlcount", "1.0.0",
     [kernel,
      stdlib,
      {ppool, permanent},
      {erlcount, transient}
     ]},
    {boot_rel, "erlcount"}
]}.
```

新しいリリースを取り除くために `rel/` ディレクトリを掃除したくなるでしょう。 そのときは、また泥臭い一連の関数呼び出しを行います：

``` erlang
4> f(),
4> {ok, Conf} = file:consult("erlcount-1.0.config"),
4> {ok, Spec} = reltool:get_target_spec(Conf),
4> reltool:eval_target_spec(Spec, code:root_dir(), "rel").
ok
```

簡単におさらいすると、 `f()` はシェル内のすべての変数の束縛を解放します。 いま `rel` ディレクトリにいって、 `$ ./bin/erl` を実行すると次のような出力となるでしょう：

``` erlang
Erlang R14B02 (erts-5.8.3) [source] ...

Eshell V5.8.3  (abort with ^G)
1> Regex if\s.+-> has 0 results
Regex case\s.+\sof has 0 results
```

すばらしい。たとえ新しいバージョンのものを利用できたとしても、バージョン5.8.3を稼働させています。 ”Ah, ha, ha, ha, Stayin' alive.”

> [!NOTE]
> `rel/` ディレクトリを見てみると、systoolsにあったものと似ているけれど、 `lib/` ディレクトリに1つ違いがあるとわかるでしょう。 その1つとは、大量のディレクトリと `.ez` ファイルがあることです。 ディレクトリには開発する際に必要となるファイルが入っている `include/` ディレクトリと、保管しておく必要があるファイルが置いてある `priv/` ディレクトリがあります。 `.ez` ファイルは単なるzipされたBEAMファイルです。 Erlang VMはランタイムに展開します。これは単にファイルを軽くするためのものです。

しかし、待って下さい。他のモジュールはどうなったんでしょうか。

それでは、今度はリリース全体に関わる設定から離れて、アプリケーションに関係する設定の部分に移りましょう。 まだたくさんのリリース全体に関わるオプションがあるのですが、いまはそんなことには構っていられません。 あとでまた触れます。 アプリケーションには、もっと多くのタプルを追加することでバージョンを指定することができます：

``` erlang
{app, AppName, [{vsn, Version}]}
```

必要があればアプリケーションごとにこのタプルを追加します。

すべてにおいてもっと多くのオプションがあります。 もしリリースにデバッグ情報を入れたいかどうか、あるいは外したいかどうか、もっとコンパクトなアプリケーションファイルにするかどうか、私たちの定義を信用するかどうか、リリースに含めるもの含めないもの、あなたのアプリケーションが依存するであろうアプリケーションやモジュールを含める場合にどれくらい厳格に確認するか、などを指定することができます。 さらに、これらのオプションは、デフォルト値やそれに上書きする値を設定できるように、通常はリリース全体でもアプリケーション固有でも指定することができます。

かんたんな要約をします。複雑だと思ったら、そこは飛ばしてしまって、次のレシピを参照してください：

### リリースのみに関わるオプション

> `{lib_dirs, [ListOfDirs]}`
>
> > ライブラリを探すときに確認するディレクトリ
>
> `{app, AppName, [AppOptions]}`
>
> > アプリケーション固有のオプションが指定でき、通常はリリース全体のオプションよりも具体的に書かれている。
>
> `{boot_rel, ReleaseName}`
>
> > `erl` の実行ファイルを起動する際のデフォルトのリリース。 これで `erl` を起動するときにブートファイルを指定する必要がなくなる。
>
> `{rel, Name, Vsn, [Apps]}`
>
> > リリースに含まれるアプリケーション
>
> `{relocatable, true | false}`
>
> > リリースをどこからでも起動できるか、システムでハードコードされたパスからのみ起動できるようにするか。 デフォルトでは `true` に設定されていて、私は `false` にする明確な理由がなければ、そのままにしています。 いずれ、いつその必要が出てくるかわかるでしょう。
>
> `{profile, development | embedded | standalone}`
>
> > このオプションは、リリースの種類に基づいてデフォルトの `*_filters` （以下に説明あり）を指定する方法です。 デフォルトでは `development` が使われます。 このオプションでは各アプリケーションとERTSから暗黙のうちにファイルをインクルードします。 `standalone` プロファイルはもっと限定的で、 `embedded` プロファイルはさらに限定的で、デフォルトのERTSアプリケーションやバイナリを削り落とします。

### リリースとアプリケーション全体に関わるオプション

このセクションのすべてのオプションは、アプリケーションの階層で設定したオプションが、単純にシステムの階層で設定した値を上書きすることに留意して下さい。

> `{incl_sys_filters, [RegularExpressions]}` `{excl_sys_filters, [RegularExpressions]}`
>
> > インクルードする前に、そのファイルが採用フィルタにマッチして、排除フィルタにマッチしないものかを確認します。 特定のファイルを取り除いたり、含めたりというときに使います。
>
> `{incl_app_filters, [RegularExpressions]}` `{excl_app_filters, [RegularExpressions]}`
>
> > `incl_sys_filters` や `excl_sys_filters` と似ていますが、アプリケーション固有のファイルが対象です。
>
> `{incl_archive_filters, [RegularExpressions]}` `{excl_archive_filters, [RegularExpressions]}`
>
> > 最上位のどのディレクトリが `.ez` アーカイブファイルに含まれるべきか、あるいは排除されるべきかを指定します。（これについてはすぐ後でもう少し説明します）
>
> `{incl_cond, include | exclude | derived}`
>
> > 必ずしも `rel` タプルの中に指定されないアプリケーションファイルをどのように含めるか決めます。 `include` を選ぶと、Reltoolは見つけたものをすべて含めます。 `derived` を選ぶと、Reltoolは `rel` タプルの中にあるアプリケーションのいずれかに使われていると判断されたアプリケーションのみを含めます。 これがデフォルト値です。 `exclude` を選ぶと、デフォルトではアプリケーションをまったく含めません。 通常これは、含めるファイルを最小限にしたいときにリリースの階層で設定し、それをアプリケーションごとに必要に応じて上書きしていく、という形で設定します。
>
> `{mod_cond, all | app | ebin | derived | none}`
>
> > これはモジュールを含めるポリシーを制御します。 `none` を選ぶと、モジュールは一つも含まれません。 これはあまり便利ではありません。 `derived` は、Reltoolが、どのモジュールがすでに追加されている他のモジュールに使われているかを判断して、使われていると判断された場合にはそのモジュールを追加します。 `app` に設定すると、Reltoolはアプリケーションファイル内で記載のあるモジュールと、それが派生したモジュールをすべて含めます。 `ebin` に設定すると、 `ebin/` ディレクトリ配下にあるファイルを含めます。 `all` は `ebin` と `app` を組み合わせたものです。 これがデフォルト値になっています。
>
> `{app_file, keep | strip | all}`
>
> > このオプションは、アプリケーションを含めるときに、アプリケーションファイルがどのように管理されるかを管理します。 `keep` を選ぶと、リリースで使われるアプリケーションファイルは、あなたが書いたアプリケーションファイルと同じものになることが保証されます。 これがデフォルト値です。 `strip` を選ぶと、Reltoolは含めたくないモジュール（フィルターなどのオプションで排除されたもの）を取り除いた新しいアプリケーションファイルを作ろうとします。 `all` を選ぶと、元のファイルは保存しますが、そこに追加するよう指定されたモジュールも追加します。 `all` のいいところは、アプリケーションファイルがない場合にアプリケーションファイルを生成してくれるところです。

### モジュール固有のオプション

> `{incl_cond, include | exclude | derived}`
>
> > リリースの階層とアプリケーションの階層で設定された `mod_cond` オプションを上書きします

### すべての階層でのオプション

このオプションはすべての階層に適用されます。低い階層ほど優先されます。

> `{debug_info, keep | strip}`
>
> > ファイルが `debug_info` が付加された状況でコンパイル（そうすることをおすすめします）されたと想定して、このオプションでその情報を残しておくか取り除くかを決めることができます。 `debug_info` は、ファイルをデコンパイルしたり、デバッグするときに便利ですが、余計な容量を食います。

### 多すぎるよ！

ああ、そうですね、たくさんのことを書きすぎました。 設定可能なオプションはまだ全部は説明してませんが、そこそこ良い参考資料になったと思います。 すべてのオプションについて知りたい場合は、公式ドキュメントを確認しましょう。

![image](rube.png)

## レシピ

ここで、取得したいものに応じた、いくつかの汎用的なコツと秘訣ご紹介します。

### 開発版

開発用の何かを用意するのは比較的簡単です。 たいていデフォルトで事が足ります。 先に挙げた基本的なものをちゃんと正しい場所に置けば、それで十分です：

``` erlang
{sys, [
    {lib_dirs, ["/home/ferd/code/learn-you-some-erlang/release/"]},
    {rel, "erlcount", "1.0.0", [kernel, stdlib, ppool, erlcount]},
    {boot_rel, "erlcount"}
]}.
```

Reltoolがインポートに関する部分の面倒を見てくれます。 ある状況では、通常のVMからすべてを取得したいことでしょう。 チームにライブラリをいくつか同梱したVM全体を配布することでしょう。 ある状況では、次のような事をしたいこともあるでしょう：

``` erlang
{sys, [
    {lib_dirs, ["/home/ferd/code/learn-you-some-erlang/release/"]},
    {rel, "start_clean", "1.0.0", [kernel, stdlib]},
    {incl_cond, include},
    {debug_info, keep}
]}.
```

`incl_cond` をインクルードに設定することで、現在のERTS内で見つかるすべてのアプリケーションと `lib_dirs` がリリースに入ります。

> [!NOTE]
> `boot_rel` を指定しないときは、 `start_clean` という名前のリリースにしなければいけません。 このリリースは関連する `erl` ファイルを起動するときにデフォルトで選択されます。

特定のアプリケーション、たとえばまだ見たことがないので `megaco` を除外したい場合は、代わりにこのようなファイルを作成します：

``` erlang
{sys, [
    {lib_dirs, ["/home/ferd/code/learn-you-some-erlang/release/"]},
    {rel, "start_clean", "1.0.0", [kernel, stdlib]},
    {incl_cond, include},
    {debug_info, keep},
    {app, megaco, [{incl_cond, exclude}]}
]}.
```

ここで、1つ以上のアプリケーションを指定することができ（アプリケーションごとにタプルが必要にはなります）、それぞれがリリースの階層に設定されている `incl_cond` の設定を上書きします。 この場合は、 `megaco` 以外はすべてインクルードします。

### ライブラリの一部だけをインポートあるいはエクスポートする

私たちのリリースで、1つだけうっとうしいことは `ppool` などのアプリケーションが、たとえ必要なかったとしてもテストファイルもリリースの中に入れていたことです。 実際そのようになっていることは `rel/lib` へ行って `ppool-1.0.0.ez` を展開することで確認できます。（展開する前に拡張子を変更する必要はあるでしょう）

これらのファイルを取り除くのに、一番簡単な方法は排除フィルタを指定することです。たとえば次のような形です：

``` erlang
{sys, [
    {lib_dirs, ["/home/ferd/code/learn-you-some-erlang/release/"]},
    {rel, "start_clean", "1.0.0", [kernel, stdlib, ppool, erlcount]},
    {excl_app_filters, ["_tests.beam$"]}
]}.
```

アプリケーションの中の特定のファイルだけをインポートしたいとき、たとえば `erlcount_lib` の機能だけだけをインポートしたいときは、設定ファイルはちょっと複雑になります：

``` erlang
{sys, [
    {lib_dirs, ["/home/ferd/code/learn-you-some-erlang/release/"]},
    {rel, "start_clean", "1.0.0", [kernel, stdlib]},
    {incl_cond, derived}, % exclude would also work, but not include
    {app, erlcount, [{incl_app_filters, ["^ebin/erlcount_lib.beam$"]},
    {incl_cond, include}]}
]}.
```

この場合、 `{incl_cond, include}` をより制限の厳しい `incl_cond` に変更しました。 これは、もしリリースを大きくして、すべてのファイルをかき集めた場合、1つのライブラリだけを含めるには、他のすべてのライブラリを `excl_app_filters` に記載して排除する他方法がないからです。 しかしながら、もしより限定的な選択をしたい場合（この場合は、 `derived` に設定していて、 `rel` タプルに入っていないので `erlcount` は含みません）、リリースに `erlcount` アプリケーションは `erlcount_lib` と関係があるという正規表現にマッチするファイルのみを含めるように明示的に伝えることができます。 このことで、最も制限の強いアプリケーションが作れるのかという疑問が湧いてきませんか。

### 大きな心を持ったプログラマのための小さなアプリケーション

ここが、Reltoolがかなり複雑になる部分です、かなり詳細な設定ファイルになります：

``` erlang
{sys, [
    {lib_dirs, ["/home/ferd/code/learn-you-some-erlang/release/"]},
    {erts, [{mod_cond, derived},
            {app_file, strip}]},
    {rel, "erlcount", "1.0.0", [kernel, stdlib, ppool, erlcount]},
    {boot_rel, "erlcount"},
    {relocatable, true},
    {profile, embedded},
    {app_file, strip},
    {debug_info, strip},
    {incl_cond, exclude},
    {excl_app_filters, ["_tests.beam$"]},
    {app, stdlib, [{mod_cond, derived}, {incl_cond, include}]},
    {app, kernel, [{incl_cond, include}]},
    {app, ppool, [{vsn, "1.0.0"}, {incl_cond, include}]},
    {app, erlcount, [{vsn, "1.0.0"}, {incl_cond, include}]}
]}.
```

ああ、たくさんのことが行われています。 ERTSに関しては、Reltoolに必要な物だけを残すように明記しています。 `mod_cond` を `derived` にし、 `app_file` を `strip` にして、Reltoolに何かに使われているものだけを残すように命じています。 これが `{app_file, strip}` がリリース階層でも使われている理由です。

<img src="ez.png" class="align-right" alt="image" />

`profile` は `embedded` に設定されています。 先の例で `.ez` アーカイブを見ると、ソースファイル、テストディレクトリなどがありました。 `embedded` に切り替えると、インクルードファイルとバイナリと `prev/` ディレクトリのみが残されます。 たとえデバッグ情報付きでコンパイルされていたとしても、すべてのファイルから `debug_info` も取り除いています。 これは、デバッグの可能性を下げていますが、ファイルサイズは小さくなります。

テストファイルや設定に関するものも取り除いて、明示的に含まれるように記載しない限り（ `{incl_cond, exclude}` ）はどのアプリケーションも含まれません。 それから、この設定を含めたいアプリケーションごとに上書きしています。 もし何かが足りなければ、Reltoolが警告してくれるので、色々と設定を変更して、期待する結果が得られるまで試すことができます。 これは、私が `stdlib` でそれを行なって、いくつかのアプリケーションの中から最小限のファイルが残されたように、いくつかのアプリケーション設定を `{mod_cond, derived}` としたことと関係しています。

結局違いは何でしょうか。 私たちのアプリケーションは一般的なリリースでは35MB以上になります。 上の例でのリリースでは20MB以下に抑えられました。 かなりの部分を削りました。 しかしながらサイズは依然としてかなり大きいです。 これはERTSが原因で、これだけで18.5MBになります。 もし希望すれば、もっと深く踏み込んで、ERTSをもっと小さくビルドすることに関してまで細かく管理できます。 代わりに、アプリケーション内で使われるとわかっているERTS内のいくつかのバイナリファイルを選択することができます。たとえばスクリプトの実行ファイル、Erlangのリモート実行、テストフレームワークのバイナリ、他の実行コマンド（SMP付きあるいはSMPなしのErlang等）などです。

もっとも軽いリリースは、オプションを選択する際に、他のユーザが既にErlangをインストールしていることを期待し、 `rel/` ディレクトリの中身を `ERL_LIBS` 環境変数がさす場所に追加し、（systoolsで行なっていたように）ブートファイルを自分自身で呼び出すことを想定している場合のものです。 プログラマはこれらをスクリプトにラップするでしょう。

> [!NOTE]
> 最近では、Erlangプログラマはこれらのリリースは *rebar* と呼ばれるツールで行うことが本当に好きなようです。 RebarはEmakefileとReltoolのラッパの様な動作をします。 Reltoolがどのように動作するかを学んでおいてまったく損することはありません。Rebarはほとんど同じ設定ファイルを使いますし、ReltoolとRebarの違いはそれほどおおきくありません。

## リリースから開放された

これでリリースを扱う主な2つの方法についてはおしまいです。 複雑なトピックでしたが、リリースを行う上では標準的な方法です。 多くの読者にとってはアプリケーションで十分で、アプリケーションにしばらく固執するのは悪くはないのですが、いずれ運用管理部門の人に、あなたがデプロイする必要があるErlangアプリケーションをどのようにデプロイするか（あるいは少なくともデプロイに関してなんらかの事を知っている）という理由で気に入ってもらいたいときに、リリースの知識が役に立つでしょう。

もちろん、運用者が喜ぶことといったらダウンタイムがなくなる以上に何があるでしょうか。 次の課題は、リリースが稼働している間のソフトウェア更新についてです。

![image](release.png)

