リリースは言葉だ

私はもう実行可能ファイルですか?

ここまで来ました。これまでの作業、すべての概念、そして私たちはまだErlangの実行可能ファイルを1つも出荷していません。特に、コンパイラを呼び出すだけで済む多くの言語と比較して、Erlangシステムを立ち上げて実行するには多大な労力がかかるという点に同意するかもしれません。

A slice of pizza

もちろん、これは全くその通りです。ファイルのコンパイル、アプリケーションの実行、いくつかの依存関係のチェック、クラッシュの処理などはできますが、簡単にデプロイまたは出荷できる機能的なErlangシステムがなければ、あまり役に立ちません。冷えたピザしか配達できないのに、美味しいピザがあっても何の役に立つでしょうか?(冷めたピザが好きな人は、ここでは除外されていると感じるかもしれません。申し訳ありません。)

OTPチームは、実際のシステムが確実に実現するように、私たちを放っておきませんでした。OTPリリースは、アプリケーションを最小限のリソースと依存関係でパッケージ化するのに役立つシステムの一部です。

漏れているパイプの修理

最初のリリースでは、前回の章のppoolアプリケーションとerlcountアプリケーションを再利用します。ただし、その前に、いくつかの変更が必要です。本を読みながら自分のコードを書いている場合は、両方のアプリをrelease/という新しいディレクトリにコピーすることをお勧めします。この章の残りの部分では、これが完了していると想定します。

A leaky pipe with brown liquid dripping into a metal bucket

erlcountで一番気になるのは、実行が終わるとVMが何もせずに起動したままになることです。ほとんどのアプリケーションは永遠に実行し続けたいかもしれませんが、今回はそうではありません。実行を維持するのは、シェルでいくつかの操作をして、手動でアプリケーションを起動する必要がある場合に理にかなっていましたが、もう必要ないはずです。

このため、BEAM仮想マシンを正常にシャットダウンするコマンドを追加します。最適な場所は、結果を取得した後に呼び出されるため、erlcount_dispatch.erlの独自のterminate関数内です。すべてを終了するための最適な関数はinit:stop/0です。この関数は非常に複雑ですが、アプリケーションの終了を順番に処理し、ファイル記述子、ソケットなどを削除します。新しいstop関数は次のようになります。

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

コード自体はこれで終わりです。まだもう少し作業が必要です。前回の2つの章でアプリファイルを定義したとき、実行するために必要な最小限の情報を使用して定義しました。Erlangが完全に怒らないようにするために、さらにいくつかのフィールドが必要です。

まず、リリースをビルドするためのErlangツールは、アプリケーションの説明をもう少し正確にする必要があります。リリースのためのツールはドキュメントを理解していませんが、開発者が少なくともアプリケーションが何をするのかという考えを残すことを怠ったコードに対して、直感的な恐れを持っています。このため、ppool.appファイルとerlcount.appファイルの両方にdescriptionタプルを追加する必要があります。

ppoolには、次のものを追加します。

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

erlcountには、次のものを追加します。

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

これで、さまざまなシステムを調べるときに、何が起こっているかをよりよく理解できます。

注意深い読者は、すべてのアプリケーションがstdlibkernelに依存していると私が述べたことも覚えているでしょう。ただし、2つのアプリファイルにはこれらのいずれも記述されていません。両方のアプリケーションを両方のアプリファイルに追加しましょう。これには、ppoolアプリファイルに次のタプルを追加する必要があります。

{applications, [stdlib, kernel]}

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

クーラードを飲みすぎないでください
これは、手動でリリースを開始する場合(そして、すぐに確認するsystoolsでリリースを生成する場合でも)には事実上影響がないかもしれませんが、両方のライブラリをリストに追加することは絶対に不可欠です。

reltool(この章で取り上げる別のツール)を使用してリリースを生成する人は、リリースが正常に実行され、VMを適切な方法でシャットダウンできるようにするために、これらのアプリケーションを必要とします。冗談ではなく、これは必要なことです。この章を書くときに忘れてしまい、最初は自分が間違ったことをしていただけなのに、一体何が問題なのかを突き止めようとして一晩中作業することになりました。

理想的には、Erlangのリリースシステムは、これらのアプリケーションのほぼすべて(非常に特殊なケースを除く)がそれらに依存することを考えると、これらのアプリケーションを暗黙的に追加できると主張できるかもしれません。残念ながら、そうではありません。これに対処する必要があります。

終了処理を実装し、アプリファイルなどを更新しました。リリースに取り掛かる前の最後の手順は、すべてのアプリケーションをコンパイルすることです。各ディレクトリにあるEmakefileを(erl -makeで)順番に実行してください。そうしないと、Erlangのツールはコンパイルを行わず、実行するコードのないリリースになってしまいます。大変です。

Systoolsを使用したリリース

systoolsアプリケーションは、Erlangリリースをビルドする最も簡単な方法です。Erlangリリースのイージーベイクオーブン®のようなものです。systoolsオーブンから美味しいリリースを取り出すには、まず基本的なレシピと材料のリストが必要です。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}]}.

これは、手動レシピと同じ内容をすべて示しているだけで、アプリケーションをどのように開始するか(temporarytransientpermanent)を指定できます。また、必要に応じてさまざまな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を実行していました。リリース番号の直後に、ERTSバージョン(バージョンは5.8.4)が表示されます。次に、実行中のシステムでapplication:which_applications()を呼び出すと、kernel(2.14.4)とstdlib(1.17.4)に必要な2つのバージョンを確認できます。数値はErlangのバージョンによって異なります。ただし、必要なバージョンを明示的に指定することは、多くの異なるErlangインストールがある場合でも、実行中のものに悪影響を与えない古いバージョンのstdlibのみが必要になる可能性があるため、役立ちます。

A chocolate cupcake with pink creamy topping in a purplish paper, with a face, beard, legs and high heel shoes (pink)

また、リリースの名前をerlcountにして、バージョンを1.0.0にしたことにも注目してください。これは、アプリファイルで指定されているように、両方ともバージョン1.0.0を実行しているppoolerlcountアプリケーションとは関係ありません。

これで、コンパイルされたすべてのアプリケーション、材料のリスト、そして比喩的なイージーベイクオーブン®という素晴らしい概念が得られました。必要なのは実際のレシピです。

ここでいくつかの概念が登場します。レシピは、材料を追加する順序、それらを混ぜる方法、調理する方法などを教えてくれます。材料を追加する順序は、各アプリファイルの依存関係リストでカバーされています。systoolsアプリケーションは賢く、アプリファイルを見て、何が最初に実行される必要があるかを判断します。

Erlangの仮想マシンは、ブートファイルと呼ばれるものから取得した基本構成で起動できます。実際、シェルから独自のerlアプリケーションを起動すると、デフォルトのブートファイルを使用してErlangランタイムシステムが暗黙的に呼び出されます。そのブートファイルは、「標準ライブラリをロードする」、「カーネルアプリケーションをロードする」、「特定の関数を実行する」などの基本的な命令を与えます。そのブートファイルは、これらの命令を表すタプルを含むブートスクリプトと呼ばれるものから作成されたバイナリファイルです。そのようなブートスクリプトの作成方法を見ていきます。

まず、次のことから始めます。

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

冗談です。実際にそれをする人は誰もいないし、私たちもしません。ブートスクリプトは、.relファイルから簡単に生成できるものです。release/ディレクトリからErlang VMを起動して、次の行を呼び出します。

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

これで、ディレクトリを見ると、erlcount-1.0.scriptファイルとerlcount-1.0.bootファイルを含む、新しいファイルが多数作成されます。ここで、localオプションは、リリースを現在のインストールだけでなく、どこからでも実行できるようにする必要があることを意味します。他にも多くのオプションがありますが、systoolsはreltool(次のセクションで説明)ほど強力ではないため、詳細には調べません。

いずれにせよ、ブートスクリプトはありますが、コードを配布するには十分ではありません。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がリリースファイルとErlang Run Time System(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

Windows PowerShellを使用すると、Windows 7でも同じコマンドになります。

クーラードを飲みすぎないでください
リリースがどのシステムでも動作するという保証はありません。純粋なErlangコードを使用している場合、そのコードは移植可能です。問題は、一緒に配布するERTS自体が動作しない可能性があることです。大規模な定義のために、多数の異なるプラットフォーム向けに多数のバイナリパッケージを作成する必要があるか、関連するERTSなしでBEAMファイルのみを配布し、ユーザーに自分のコンピュータにある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

どこから実行しても、スキャンされるのはそのディレクトリになります。これをシェルスクリプトまたはバッチファイルにラップすれば、準備完了です。

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は異なるレベルの情報を受け取ります。最初のレベルには、リリース全体の情報が含まれます。2番目のレベルは、アプリケーション固有の情報になり、モジュール固有のレベルで詳細な制御が可能になります。

The levels of reltools: the release levels contains environment, applications and properties of the releases. The level under that, Applications, contains what to include, compression, debug_info, app files, etc. The last (and lowest) level, the Modules, contains what to include and debug_info

前のグラフのように、これらの各レベルで、異なるオプションが利用可能になります。可能なすべてのオプションを使用して百科事典的なアプローチを取るのではなく、いくつかの重要なオプションと、アプリケーションで何を求めているかに応じて、いくつかの可能な構成を説明します。

最初のオプションは、特定のディレクトリに存在したり、VMに正しい-env引数を設定したりする必要がある、やや面倒なニーズを取り除くのに役立ちます。オプションはlib_dirsで、アプリケーションが存在するディレクトリのリストを受け取ります。したがって、実際には-env ERL_LIBS <ディレクトリのリスト>を追加する代わりに、{lib_dirs, [ListOfDirectories]}を入力すると、同じ結果が得られます。

Reltool設定ファイルのもう1つの重要なオプションはrelです。このタプルは、systools用に作成した.relファイルと非常に似ています。上記のデモファイルでは、次のようになっていました。

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

そして、これはどのアプリを正しく起動する必要があるかを伝えるために必要になります。そのタプルの後には、次の形式のタプルを追加します。

{boot_rel, "erlcount"}

これにより、リリースに含まれるerlバイナリを実行するたびに、erlcountリリースのアプリを起動することをReltoolに伝えます。これらの3つのオプション(lib_dirsrel、およびboot_rel)だけで、有効なリリースを作成できます。

そのためには、これらのタプルを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]}タプルにラップするだけです。私の場合、これをrelease/ディレクトリにerlcount-1.0.configという名前のファイルに保存しました。どこにでも保存できます(/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番目のコマンドは、仕様を取得し、Reltoolに「Erlangのインストール先であるパスを使用して、リリース仕様を取得し、それを「rel」ディレクトリに押し込んでほしい」と伝えます。以上です。relディレクトリを見ると、そこに多数のサブディレクトリがあるはずです。

今のところ気にせず、次を呼び出すことができます。

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

ああ、実行するのが少し簡単になりました。これらのファイルは、同じファイルツリーを維持し、好きな場所から実行する限り、ほぼどこにでも配置できます。

a squid's tentacle being cut off so it could free itself from a pair of handcuffs

何か違いに気づきましたか?気づいてほしいと思っています。バージョン番号を何も指定する必要がありませんでした。Reltoolは、そこではSystoolsよりも少し賢いです。バージョンを指定しないと、パス(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で実行されます。ああ、ははは、はは、生き続けてください。

注: rel/ディレクトリを見ると、Systoolsを使用した場合と似たようなものが見えますが、違いの1つはlib/ディレクトリにあります。そこには、多数のディレクトリと.ezファイルが含まれるようになります。ディレクトリには、開発を行うときに必要なinclude/ファイルと、そこに保持する必要があるファイルがある場合のpriv/ディレクトリが含まれます。.ezファイルは、zipされたbeamファイルにすぎません。Erlang VMは、実行時にそれらを展開します。これは、物事を軽くするためだけです。

しかし、他のモジュールはどうですか?

ああ、これでリリース全体の設定から離れて、アプリケーションに関する設定を入力する必要があります。まだ多くのリリース全体のオプションがありますが、私たちは勢いに乗っているので、今すぐ停止するように求められることはありません。後でまた戻ってきます。アプリケーションの場合、さらにタプルを追加することでバージョンを指定できます。

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

そして、必要なアプリごとに1つずつ配置します。

これで、すべてに対してはるかに多くのオプションがあります。リリースにデバッグ情報を含めるか、それを削除するか、よりコンパクトなアプリファイルを作成しようとするか、または定義を信頼するか、含めるものまたは除外するもの、独自のアプリケーションが依存する可能性のあるアプリケーションとモジュールを含める際にどの程度厳密にするかなどを指定できます。さらに、これらのオプションは通常、リリース全体とアプリケーション全体の両方で定義できるため、デフォルトを指定してからオーバーライドする値を指定できます。

簡単な概要を以下に示します。複雑だと感じた場合は、スキップしてください。後で従うべきいくつかのクックブックレシピが表示されます。

リリースのみのオプション

{lib_dirs, [ListOfDirs]}
ライブラリを探すディレクトリ。
{excl_lib, otp_root}
R15B02で追加されたこのオプションを使用すると、最終リリースで標準のErlang/OTPパスから取得したものをすべて含めることなく、OTPアプリケーションをリリースの一部として指定できます。これにより、システムにインストールされている既存の仮想マシンからブート可能な基本的にライブラリであるリリースを作成できます。このオプションを使用する場合は、仮想マシンを$ erl -boot_var RELTOOL_EXT_LIB <リリースディレクトリへのパス>/lib -boot <ブートファイルへのパス>として起動する必要があります。これにより、リリースは現在のErlang/OTPインストールを使用できますが、カスタムリリース用に独自のライブラリが使用されます。
{app, AppName, [AppOptions]}
通常はリリース全体のオプションよりも具体的な、アプリケーション全体のオプションを指定できます。
{boot_rel, ReleaseName}
erl実行可能ファイルで起動するデフォルトのリリース。これは、erlを呼び出すときにブートファイルを指定する必要がないことを意味します。
{rel, Name, Vsn, [Apps]}
リリースに含めるアプリケーション。
{relocatable, true | false}
リリースは、システム内のハードコードされたパスからのみ、またはどこからでも実行できます。デフォルトではtrueに設定されており、特別な理由がない限り、そのようにしておきます。必要なときにわかるでしょう。
{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はappファイルに記載されているすべてのモジュールと派生したモジュールを保持します。ebinに設定すると、ebin/ディレクトリ内のモジュールと派生したモジュールを保持します。allオプションを使用すると、ebinappの使用を組み合わせたものになります。これがデフォルト値です。
{app_file, keep | strip | all}
このオプションは、アプリケーションを含める際に、appファイルをどのように管理するかを制御します。keepを選択すると、リリースで使用されるappファイルが、アプリケーション用に記述したファイルと同じになることが保証されます。これがデフォルトのオプションです。stripを選択すると、Reltoolは、不要なモジュール(フィルタやその他のオプションで除外されたモジュール)を削除した新しいappファイルを生成しようとします。allを選択すると、元のファイルが保持されますが、明示的に含まれるモジュールもそこに追加されます。allの優れている点は、appファイルがない場合に、appファイルを生成できることです。

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

{incl_cond, include | exclude | derived}
これにより、リリースレベルおよびアプリケーションレベルで定義されたmod_condオプションをオーバーライドできます。

全レベルオプション

これらのオプションは、すべてのレベルで機能します。レベルが低いほど、優先度が高くなります。

{debug_info, keep | strip}
ファイルがdebug_infoをオンにしてコンパイルされている(そうすることを推奨します)と仮定すると、このオプションを使用すると、それを保持するか削除するかを決定できます。debug_infoは、ファイルをデコンパイルしたり、デバッグしたりする場合などに役立ちますが、多少のスペースを占有します。

これは非常に複雑です

はい、非常に多くの情報があります。私は可能なすべてのオプションを網羅したわけではありませんが、それでも十分な参考になるはずです。全体を知りたい場合は、公式ドキュメントを確認してください。

A complex Rube Goldberg machine to represent the OTP Release process

レシピ

今のところ、達成したいことに応じて、いくつかの一般的なヒントとコツを紹介します。

開発バージョン

開発用の何かを用意するのは比較的簡単でなければなりません。多くの場合、デフォルトで十分です。以前に説明した基本的な項目を配置することに専念すれば、十分なはずです。

{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_condincludeに設定すると、現在のERTSインストールとlib_dirsで見つかったすべてのアプリケーションがリリースの一部になります。

注:boot_relが指定されていない場合、Reltoolが正常に動作するためには、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つ以上のアプリケーション(それぞれ独自のappタプルを持つ)を指定できます。各アプリケーションは、リリースレベルで設定されたincl_cond設定をオーバーライドします。したがって、この場合、megacoを除くすべてを含めます。

ライブラリの一部のみをインポートまたはエクスポートする

私たちのリリースでは、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に切り替えました。これは、大きくすべてを取り込むと、単一のライブラリを含める唯一の方法は、excl_app_filtersですべての他のライブラリを除外することになるためです。ただし、選択がより制限的な場合(この場合、derivedであり、relタプルの一部ではないためerlcountは含まれません)、erlcount_libに関連する正規表現に一致するファイルのみを含むerlcountアプリを含めるようにリリースに具体的に指示できます。これは、可能な限り制限の厳しいアプリケーションを作成する方法についての疑問を引き起こしますよね?

大きな心を持つプログラマー向けの小さなアプリ

ここで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, [{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}はリリースレベルでも使用されています。

a crate with a sign that sayz '.ez'

プロファイルはembeddedに設定されています。以前のケースで.ezアーカイブを見た場合、それらにはソースファイル、テストディレクトリなどが含まれていました。embeddedに切り替えると、ファイル、バイナリ、priv/ディレクトリのみが保持されます。また、たとえdebug_infoでコンパイルされていても、すべてのファイルからdebug_infoを削除しています。これは、デバッグ機能の一部が失われることを意味しますが、ファイルサイズは小さくなります。

私はまだテストファイルを取り除き、明示的に指示されるまでアプリケーションが含まれないように({incl_cond, exclude})設定しています。次に、含めたい各アプリでこの設定をオーバーライドします。何かが見つからない場合、Reltoolは警告を表示するので、目的の結果が得られるまで設定を移動したり、設定を試したりできます。一部のアプリケーション設定で、{mod_cond, derived}を使用して、一部のアプリケーションの最小限のファイルが保持されるようにすることが必要な場合があります。

最終的にどのような違いがあるのでしょうか?一般的なリリースの一部は35MBを超えていました。上記のリリースは20MB未満に削減されています。かなりの部分を削減しています。ただし、サイズはまだかなり大きいです。これは、ERTS自体が18.5MBを占めているためです。必要に応じて、さらに深く掘り下げて、ERTSの構築方法をマイクロ管理して、より小さなものを取得できます。あるいは、アプリケーションで使用されないことがわかっているERTS内のいくつかのバイナリファイル(スクリプト用の実行可能ファイル、Erlangのリモート実行、テストフレームワークからのバイナリ、さまざまな実行コマンド(SMPの有無にかかわらずErlangなど))を選択することもできます。

最も軽量なリリースは、他のユーザーがすでにErlangをインストールしていることを前提としたものです。このオプションを選択する場合は、ERL_LIBS環境変数の一部としてrel/ディレクトリの内容を追加し、ブートファイル(systoolsと同様)を自分で呼び出す必要がありますが、機能します。プログラマーは、これをスクリプトにラップして動作させることを望むかもしれません。

注:最近、Erlangプログラマーは、rebar3というツールでこれらのリリースをすべて処理してもらうという考えを非常に好んでいるようです。Rebar3はErlangコンパイラに対するラッパーとして機能し、リリースを処理します。Reltoolの動作を理解することに損失はありません。Rebar3はリリースに対してより高レベルの抽象化を使用しており、reltoolを理解することで、他のツールがどのように機能するかを簡単に理解できます。

リリースからの解放

これで、リリースを処理する2つの主要な方法が完了しました。複雑なトピックですが、物事を処理する標準的な方法です。アプリケーションは多くの読者にとって十分かもしれませんし、しばらくの間それらに固執しても悪いことはありませんが、運用保守担当者がErlangアプリケーションをデプロイする方法を知っている(または少なくともいくつかのアイデアを持っている)ことを考えると、時々リリースが役立つかもしれません。

もちろん、運用担当者を最も幸せにするのは、ダウンタイムがないことでしょう。次の課題は、リリースが実行中にソフトウェアをアップグレードすることです。

Parody of the poster of the Grease movie, where 'Grease' is replaced by 'Release', Olivia Newton-Jogn by Joe Armstrong and John Travolta by Bjarne Dacker