24. Release is the Word

24.1. それでも私は実行可能ですか?

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

../_images/pizza.png

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

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

24.2. 水漏れパイプを直す

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

../_images/pipes.png

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

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

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

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

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

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

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

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

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

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

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

{applications, [stdlib, kernel]}

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

Don’t Drink Too Much Kool-Aid:

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

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

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

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

24.3. 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/ ディレクトリの最上位に置かれます:

{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バージョンの異なるライブラリと組み合わせたり、適合したり出来るようにバージョンの記載もあります。 ここに書いてあるすべてのバージョン番号を取得するには、次のような関数呼び出しを行うだけでよいです:

$ 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 だけを得ることができるので、便利です。

../_images/cupcake.png

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

erts-5.8.4/
lib/
releases/

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

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

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

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

Don’t Drink Too Much Kool-Aid:

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

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

$ ./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

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

$ /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

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

24.4. reltoolでリリースする

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

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

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

../_images/reltool-levels.png

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

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

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

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

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

{boot_rel, "erlcount"}

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

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

{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シェルを起動します:

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 ディレクトリの中を見ると、多くのサブディレクトリがあることでしょう。

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

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

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

../_images/cuffs.png

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

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

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

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

{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/ ディレクトリを掃除したくなるでしょう。 そのときは、また泥臭い一連の関数呼び出しを行います:

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 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はランタイムに展開します。これは単にファイルを軽くするためのものです。

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

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

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

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

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

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

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

{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アプリケーションやバイナリを削り落とします。

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

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

{incl_sys_filters, [RegularExpressions]} {excl_sys_filters, [RegularExpressions]}

インクルードする前に、そのファイルが採用フィルタにマッチして、排除フィルタにマッチしないものかを確認します。 特定のファイルを取り除いたり、含めたりというときに使います。

{incl_app_filters, [RegularExpressions]} {excl_app_filters, [RegularExpressions]}

incl_sys_filtersexcl_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/ ディレクトリ配下にあるファイルを含めます。 allebinapp を組み合わせたものです。 これがデフォルト値になっています。

{app_file, keep | strip | all}

このオプションは、アプリケーションを含めるときに、アプリケーションファイルがどのように管理されるかを管理します。 keep を選ぶと、リリースで使われるアプリケーションファイルは、あなたが書いたアプリケーションファイルと同じものになることが保証されます。 これがデフォルト値です。 strip を選ぶと、Reltoolは含めたくないモジュール(フィルターなどのオプションで排除されたもの)を取り除いた新しいアプリケーションファイルを作ろうとします。 all を選ぶと、元のファイルは保存しますが、そこに追加するよう指定されたモジュールも追加します。 all のいいところは、アプリケーションファイルがない場合にアプリケーションファイルを生成してくれるところです。

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

{incl_cond, include | exclude | derived}

リリースの階層とアプリケーションの階層で設定された mod_cond オプションを上書きします

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

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

{debug_info, keep | strip}

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

24.4.5. 多すぎるよ!

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

../_images/rube.png

24.5. レシピ

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

24.5.1. 開発版

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

{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全体を配布することでしょう。 ある状況では、次のような事をしたいこともあるでしょう:

{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 を除外したい場合は、代わりにこのようなファイルを作成します:

{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 以外はすべてインクルードします。

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

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

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

{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 の機能だけだけをインポートしたいときは、設定ファイルはちょっと複雑になります:

{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 と関係があるという正規表現にマッチするファイルのみを含めるように明示的に伝えることができます。 このことで、最も制限の強いアプリケーションが作れるのかという疑問が湧いてきませんか。

24.5.3. 大意を持ったプログラマの小さなアプリケーション

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

{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_condderived にし、 app_filestrip にして、Reltoolに何かに使われているものだけを残すように命じています。 これが {app_file, strip} がリリース階層でも使われている理由です。

../_images/ez.png

profileembedded に設定されています。 先の例で .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の違いはそれほどおおきくありません。

24.6. リリースから開放された

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

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

../_images/release.png