今時のErlangプロジェクトの作り方(2/3)

# ビルドツールはrebar
rebarは実行バイナリ作成、テスト実行、依存モジュール管理など雑多なタスクを楽にしてくれます。version 3を使いましょう。

## rebarを入手
パスが通っているフォルダへダウンロードします。
前回の「今時のErlangプロジェクトの作り方(1/3)」の例に沿っている場合は、~/bin に移動して以下のコマンドを実行します。

```shell-session
wget https://s3.amazonaws.com/rebar3/rebar3
chmod 0755 rebar3
```

## プロジェクトを作る

```shell-session
rebar3 new release myapp
```

以下のようなファイルが作られます。

```txt
myapp
├── LICENSE
├── README.md
├── apps
│   └── myapp
│       └── src
│           ├── myapp.app.src # 起動時に読み込むモジュール列挙など
│           ├── myapp_app.erl # ここに処理を書いていきます
│           └── myapp_sup.erl # myapp_app.erlのsupervisor処理
├── config
│   ├── sys.config  # 設定情報をここに記載、依存モジュールの設定もここで行う
│   └── vm.args     # Erlangの起動オプション
└── rebar.config
```

## 実装する

ちょっとしたプログラムを書いてみます。

````erlang:myapp/src/myapp_hello.erl   
-module(myapp_hello).

-behavior(gen_server).

-define(DELAY, 1000).

-export([
         start_link/0,
         stop/0,
         init/1,
         handle_call/3,
         handle_cast/2,
         handle_info/2,
         code_change/3,
         terminate/2
        ]).

start_link() ->
    io:format("- start_link() ~n"),
    gen_server:start_link({local, myapp_hello}, ?MODULE, [], []).

stop() ->
    io:format("- stop() ~n"),
    gen_server:call(?MODULE, stop).

init(Args) ->
    io:format("- init(~p) ~n", [Args]),
    io:format("hello world"),
    {ok, {}}.

handle_call(stop, From, State) ->
    io:format("- handle_call(stop, ~p, ~p) ~n", [From, State]),
    {stop, normal, ok, State};
handle_call(Message, From, State) ->
    io:format("- handle_call(~p, ~p, ~p) ~n", [Message, From, State]),
    {reply, ok, State, ?DELAY}.

handle_cast(Message, State) ->
    io:format("- handle_cast(~p, ~p) ~n", [Message, State]),
    {noreply, State, ?DELAY}.

handle_info(timeout, State) ->
    io:format("- handle_info(timeout, ~p) ~n", [State]),
    {noreply, State, ?DELAY};
handle_info(Message, State) ->
    io:format("- handle_info(~p, ~p) ~n", [Message, State]),
    {noreply, State, ?DELAY}.

code_change(OldVsn, State, Extra) ->
    io:format("- code_change(~p, ~p, ~p) ~n", [OldVsn, State, Extra]),
    {ok, State}.

terminate(Reason, State) ->
    io:format("~p terminate ~p~n", [Reason, State]).
```

先程のプログラムを起動するためsupervisorも変更します。
変更箇所はinit()の中身です。

```erlang:myapp_sup.erl
%%%-------------------------------------------------------------------                            
%% @doc myapp top level supervisor.
%% @end
%%%-------------------------------------------------------------------

-module(myapp_sup).

-behaviour(supervisor).

%% API
-export([start_link/0]).

%% Supervisor callbacks
-export([init/1]).

-define(SERVER, ?MODULE).

%%====================================================================
%% API functions
%%====================================================================

start_link() ->
    supervisor:start_link({local, ?SERVER}, ?MODULE, []).

%%====================================================================
%% Supervisor callbacks
%%====================================================================

%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}
init([]) ->
    {ok, { {one_for_one, 0, 1},
           [
            {myapp_hello, {myapp_hello, start_link, []}, temporary, 1000, worker, [myapp_hello]}
            ]} }.

%%====================================================================
%% Internal functions
%%====================================================================
```

## ビルドと実行

ビルド方法です。

```shell-session
rebar3 compile
```

以下のファイルが作成されます。

```text
_build
└── default
    └── lib
        └── myapp
            ├── ebin
            │   ├── myapp.app
            │   ├── myapp_app.beam
            │   ├── myapp_hello.beam
            │   └── myapp_sup.beam
            ├── include -> ../../../../apps/myapp/include
            ├── priv -> ../../../../apps/myapp/priv
            └── src -> ../../../../apps/myapp/src
```

erlコマンドで実行してみましょう。[^1]

```text
% erl -pa _build/default/lib/myapp/ebin

1> {ok, Pid} = myapp_hello:start_link().
- start_link() 
- init([]) 
hello world{ok,<0.59.0>}

2> gen_server:call(Pid, knock).
- handle_call(knock, {<0.57.0>,#Ref<0.0.4.111>}, {}) 
{}

3> gen_server:call(Pid, stop).
- handle_call(stop, {<0.57.0>,#Ref<0.0.4.117>}, 1000) 
normal terminate 1000
ok
```

## リリースビルド

リリースビルドを行うと実行バイナリも作成されます。

```shell-session
rebar3 release
```

以下のファイルが作られます。

```text
_build
└── default
    ├── lib
    │   └── myapp
    │       ├── ebin
    │       │   ├── myapp.app
    │       │   ├── myapp_app.beam
    │       │   ├── myapp_hello.beam
    │       │   └── myapp_sup.beam
    │       ├── include -> ../../../../apps/myapp/include
    │       ├── priv -> ../../../../apps/myapp/priv
    │       └── src -> ../../../../apps/myapp/src
    └── rel
        └── myapp
            ├── bin
            │   ├── install_upgrade.escript
            │   ├── myapp
            │   ├── myapp-0.1.0
            │   ├── nodetool
            │   └── start_clean.boot
            ├── lib
            │   └── myapp-0.1.0 -> /home/tadokoro/git/erlang_start/myapp/_build/default/lib/myapp
            └── releases
                ├── 0.1.0
                │   ├── myapp.boot
                │   ├── myapp.rel
                │   ├── myapp.script
                │   ├── start_clean.boot
                │   ├── sys.config.orig -> /home/tadokoro/git/erlang_start/myapp/config/sys.config
                │   └── vm.args.orig -> /home/tadokoro/git/erlang_start/myapp/config/vm.args
                ├── RELEASES
                └── start_erl.data
```

### 実行

リリースビルドで作成されたファイルを実行してみます。[^1]

```shell-session
% ./_build/default/rel/myapp/bin/myapp console
- start_link() 
- init([]) 
hello world

(myapp@tvb)1> gen_server:call(myapp_hello, knock).
- handle_call(knock, {<0.226.0>,#Ref<0.0.1.167>}, {}) 
{}

(myapp@tvb)2> gen_server:call(myapp_hello, stop).
- handle_call(stop, {<0.226.0>,#Ref<0.0.1.173>}, 1000) 
normal terminate 1000
ok
```


### バックグランド実行

サーバ環境で動かすのに便利なバックグラウンド実行をしてみます。

```shell-session
./_build/default/rel/myapp/bin/myapp start
```

psコマンドでバックグランド動作している事が確認できます。[^1]

```shell-session
PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
3577  0.0  0.4 3216888 33408 pts/20  Ssl+ 12:02   0:00 ~/local/bin/erlang/19.1/erts-8.1/bin/beam.smp -K true -A30  --  -root ~/myapp/_build/default/rel/myapp -progname ~/myapp/_build/default/rel/myapp/bin/myapp -- -home ~/ -- -boot ~/myapp/_build/default/rel/myapp/releases/0.1.0/myapp -mode embedded -boot_var ERTS_LIB_DIR ~/local/bin/erlang/19.1/erts-8.1/../lib -config ~/myapp/_build/default/rel/myapp/releases/0.1.0/sys.config -sname myapp -setcookie myapp_cookie -- console
```

停止します。

```shell-session
./_build/default/rel/myapp/bin/myapp stop
```

## まとめ
お疲れ様でした。
これで使った開発、リリースビルドが行えるようになりました。
次回はrebarを使ったテスト、依存関係の解決などをご紹介します。

[^1]: 見やすいよう出力を一部省略・変更しています。