Help us understand the problem. What is going on with this article?

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

More than 3 years have 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. 見やすいよう出力を一部省略・変更しています。 

tadokoro
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away