Edited at

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

More than 1 year has passed since last update.


ビルドツールは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()の中身です。


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
%%====================================================================



ビルドと実行

ビルド方法です。

rebar3 compile

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

_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

% 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


リリースビルド

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

rebar3 release

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

_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

% ./_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


バックグランド実行

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

./_build/default/rel/myapp/bin/myapp start

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

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

停止します。

./_build/default/rel/myapp/bin/myapp stop


まとめ

お疲れ様でした。

これで使った開発、リリースビルドが行えるようになりました。

次回はrebarを使ったテスト、依存関係の解決などをご紹介します。





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