2014/7/28

erlang - otp application release tool

OTP application 最後發布時,需要一個工具幫我們分析 application 的相關 dependency libraries,發布正確的 OTP application。

目前有看到三個 release 工具: reltool, rebar, relx,雖然 reltool 是 OTP 官方提供的封裝工具,前面的部份先簡述 reltool 與 relx,relx 是因為 survey 了 cowboy 的關係,而去了解,功能跟 rebar 類似,但提供了更高階的封裝簡化工具,這個工具是 erlware 這個組織開發提供的。rebar 提供了比 reltool 高階的封裝方式,我們就以 rebar 為主,因為 rebar 的文件說明比 relx 還清楚。

reltool

reltool提供 GUI 與 CLI 兩種界面

reltool user guide
reltool doc
reltool使用指南
reltool使用入門

erlang.mk and relx

建構 OTP releases 一直以來都是個很麻煩的工作,除了 reltool, rebar 之外 cowboy 作者提供了另一個建立 application 的方式。

建立 OTP release 需要兩個步驟:

  1. compile source
    erlang.mk 用來處理第一個步驟,erlang.mk 是 GNU Makefile 的一個 include file,它可以用來 build project, 取得 building dependencies,產生文件,做 dialyzer 靜態分析。
  2. create a release
    relx 是個 release creation tool,它是一個執行檔。用來封裝OTP application,裡面包含了Erlang runtime, a boot script to start the node and all its applications, configuration files

最簡單的 Makefile,就只需要一個 PORJECT name 並 include erlang.mk

PROJECT = my_project

include erlang.mk

DEPS 定義本專案相關的 dependencies,條列出來後,在下面以 dep_cowboy 的方式定義該 library 的 repository URL 以及 commit number, tag 或 branch。 .PHONY 表示有兩個 build target,預設是 release。 release 最後會使用 relx 將 project 建構在 rel 目錄中。

PROJECT = ninenines

DEPS = cowboy erlydtl
dep_cowboy = https://github.com/extend/cowboy.git 0.8.5
dep_erlydtl = https://github.com/evanmiller/erlydtl.git 4d0dc8fb

.PHONY: release clean-release

release: clean-release all projects
    relx -o rel/$(PROJECT)

clean-release: clean-projects
    rm -rf rel/$(PROJECT)

include erlang.mk

這是 relx.config 檔,第一行定義 release name: ninenines,版本號碼1,裡面包含一個 application: ninenines,extended_start_script 告訴 relx 要建立一個可啟動 application 的 script,下一行 sys.config 代表可指定 erlang vm 的起始參數,

{release, {ninenines, "1"}, [ninenines]}.

{extended_start_script, true}.
{sys_config, "rel/sys.config"}.

{overlay, [
    {mkdir, "log"},
    {copy, "rel/vm.args",
        "releases/\{\{release_name\}\}-\{\{release_version\}\}/vm.args"}
]}.

參考文件 Build Erlang releases with erlang.mk and relx

rebar

編譯與安裝

  1. 下載 rebar source code
    git clone https://github.com/basho/rebar.git

  2. 編譯
    cd rebar
    ./bootstrap

    產生出 rebar 執行檔後,可以將 rebar 複製到任何地方,都可以使用。我們可以將 rebar 複製到 /usr/local/bin 或是 ~/bin 目錄,放在隨時可使用到的 PATH 裡面,就完成 rebar 的安裝了。

  3. 直接下載 rebar
    因為 rebar 是完全由 erlang 撰寫的,而且有整合成一個獨立的 escript,我們也可以直接下載編譯好的 rebar http://cloud.github.com/downloads/basho/rebar/rebar ,然後就能使用了。

  4. windows
    把在 linux 環境編譯出來的 rebar 複製到 windows 環境,把 rebar.cmd 與 rebar 放在 windows 的 PATH 路徑中可以存取到的地方,就可以直接使用 rebar 了。

測試專案

  1. mkdir myapp
    建立專案目錄

  2. rebar create-app appid=myapp
    產生專案的檔案骨架
    完成後會看到一個 src 目錄,裡面有三個檔案:myapp.app.src、myapp_app.erl、myapp_sup.erl

  3. rebar compile
    編譯 myall project,完成後會產生 ebin 目錄,也會得到一個 OTP 專案檔 myapp.app

  4. rebar clean
    清除編譯結果

命令列的參數

在開發過程中,最常用的功能有

  1. 編譯
  2. 單元測試和覆蓋分析
  3. 靜態分析(通過Dialyzer和Xref)
  4. 生成文檔
  5. 依賴管理

Rebar commands 提供了指令與參數列表

常見指令 description
compile 編譯
eunit 執行 eunit
doc 使用 edoc 產生文件
clean 清除所有生成的資料
一般指令 description
check_deps 檢查 rebar.config 的 dep libs
create 建立專案,必須要給予 template 的設定
create-app 根據 simpleapp.template 建立 app 專案
create-lib 根據 simplelib.template 建立 OTP library
create-node 根據 simplenode.template 建立一個 prototypical OTP embedded system
ct 執行 common_test suites
delete-deps 刪除依賴的專案原始碼
escriptize 以 ebin 目錄的 beam files 產生 escript 的執行檔
generate 使用 reltool 建立一個embedded system
generate-upgrade 產生 upgrade package
get-deps 根據 rebar.config 取得依賴的專案原始碼
list-deps 依賴的專案列表
update-deps 更新依賴的專案原始碼
xref 使用 xref 分析依賴

compile 支援的 source code 格式

source file destination file description
src/*.erl ebin/*.beam erlang source
src/*.app.src ebin/*.app otp application
c_src/*.c priv/.so port driver的c語言源代碼或者NIF共享鏈接庫
mibs/*.mib priv/mibs/*.bin SNMP 的 MIB 檔案
src/*.xrl src/*.erl leex 產生的檔案
src/*.yrl src/*.erl Yecc 產生的檔案
asn1/*.asn1 src/*.erl ASN-1文件
templates/*.dtl ebin/*_dtl.beam ErlyDTL模板文件 (需要額外安裝 ErlyDTL)
src/*.lfe ebin/*.beam LFE source code (需要額外安裝LFE)
src/*.peg ebin/*.beam Neotoma PEG 語法 source code (需要額外安裝Neotoma)
src/*.proto ebin/_pb.beam, include/_pb.hrl Protocol Buffers 參數(需要額外安裝protobuffs)

rebar.config 設定的選項

命令 設定的選項 description
compile erl_first_files 需要提前編譯的erlang源文件(例如behavior模塊)
compile erl_opts 編譯器支援的其他設定,請參閱文件
compile mib_first_files 需要提前編譯的mib文件列表 (例如, mib 文件中import部分的引用的RFC文件
compile src_dirs 列出其他包含erlang源文件的目錄
compile erlydtl_opts erlydtl 更多的支援的設定 ErlyDTL Templates
clean clean_files 需要在clean步驟刪除的文件列表,列出那些需要clean指令刪除的其他模塊的文件
doc edoc_opts edoc 支援的指令
eunit eunit_opts eunit支援的指令
eunit cover_enabled 開啟erlang的覆蓋率分析
eunit eunit_compile_opts Eunit編譯時用到的其他的選項
analyze dialyzer_opts 指定Dialyzer PLT 文件
build_plt dialyzer_opts 指定Dialyzer PLT 文件
check_plt dialyzer_opts 指定 Dialyzer PLT 文件
get-deps, delete-deps base_dir 為deps_dir 指定一個候選的目錄
get-deps, delete-deps deps_dir 設定存儲依賴檔的資料夾
get-deps, delete-deps deps 依賴的列表
generate target_dir 目標資料夾
generate overlay_vars Overlay variables file
xref xref_warnings 打開xref的警告
xref xref_checks Xref模塊中analyze/3支持的選項

目錄結構

rebar 遵循 OTP 的建議,包含了下列的資料夾:

  1. src
  2. ebin
  3. priv
  4. include
  5. test: eunit test source code
  6. c_src: C 語言寫的 Port Driver

Testing

rebar 支援 eunitcommon test 兩種 testing frameworks。

首先撰寫一些 eunit 測試的程式碼,在 apps/myapp/src/myapp_app.erl 的 -export 跟 程式的最後面,增加兩段 -ifdef(TEST) 到 -endif 的測試區塊。

-module(myapp_app).
-behaviour(application).

%% Application callbacks
-export([start/2, stop/1]).

-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-endif.

%% ===================================================================
%% Application callbacks
%% ===================================================================

start(_StartType, _StartArgs) ->
    myapp_sup:start_link().

stop(_State) ->
    ok.

-ifdef(TEST).

simple_test() ->
    ok = application:start(myapp),
    ?assertNot(undefined == whereis(myapp_sup)).

-endif.

因為直接執行 rebar eunit,連帶會對 deps 進行 eunit 的測試,所以增加 skip_deps 參數,只針對我們自己寫的程式進行測試。

rebar compile eunit skip_deps=true

如果要檢查 code coverage 的統計資料,我們必須在 rebar.config 增加以下設定

{cover_enabled, true}.

再執行一次 rebar compile eunit skip_deps=true ,就可以看到 code coverage analysis 結果寫在 apps/myapp/.eunit/index.html 檔案裡面。

其它

有關 deps, 建構 release package 以及升級的問題,就放在下一篇文章裡面說明。