4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

gumi Inc.Advent Calendar 2016

Day 5

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

Last updated at Posted at 2016-12-05

ビルドツールはrebar

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

rebarを入手

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

wget https://s3.amazonaws.com/rebar3/rebar3
chmod 0755 rebar3

プロジェクトを作る

rebar3 new release myapp

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

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

実装する

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

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]: 見やすいよう出力を一部省略・変更しています。
4
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?