reltool需要的配置文件通常是这样的:
{sys, [ %% Global config {lib_dir, LibDir} {rel, RelName1, RelVsn1, [AppList]}, {rel, RelName2, RelVsn2, [AppList]}, {boot_rel, RelName1}, {incl_cond, InclCond}, {mod_cond, ModCond}, {app_file, AppFile}, {incl_sys_filters, InclSysFilter}, {excl_sys_filters, ExclSysFilter}, {incl_app_filters, InclAppFilter}, {excl_app_filters, ExclAppFilter}, {incl_archive_filters, InclArchFilter}, {excl_archive_filters, ExclArchFilter}, %% App config {app, AppName1, [{incl_cond, InclCond}, {mod_cond, ModCond}, {lib_dir, LibDir}]}, {app, AppName2, [{incl_cond, InclCond}, {mod_cond, ModCond}, {lib_dir, LibDir},{mod, ModName, [{incl_cond,InclCond}]} ]}, %% Module config {mod, ModName, [{incl_cond, InclCond}]} … ] }.整个配置文件可分为全局配置(或者称为sys配置,以下统称为全局配置),application配置以及module配置。部分配置项可同时出现在全局配置,application配置,module配置中,例如incl_cond。同时出现在几个部分的配置项,其优先级为module最高,application其次,全局最低。
使用reltool完成打包,通常会按顺序执行下面几个命令:
%% 读配置文件 {ok, Config} = file:consule("reltool.config"). %% 根据配置文件生成目标系统的规格("动作") {ok, Spec} = reltool:get_target_spec(Config). %% release制作与打包 reltool:eval_target_spec(Spec, code:root_dir(), rel).如果是通过rebar打包,通常是这么几个步骤:
1. 执行命令生成reltool配置文件等其他相关文件 ./rebar create-node nodeid=Node
2. 按需修改配置及相关文件
3. 执行命令完成release制作与打包 ./rebar generate
打包后最终会包含哪些application,与此相关的主要配置项是incl_cond。
先来看几个例子:有三个应用分别取名app_a,app_b,app_c,每个应用都有三个模块,分别为a1,a2,a3;b1,b2,b3;c1,c2,c3。其中a1模块会调用b1模块的导出函数,b2模块会调用c2模块的导出函数。另外app_a,app_b,app_c都会依赖stdlib和kernel,此外,app_a还会依赖otp的compiler。
其目录结构为:
编写不同的配置并打包到rel目录中。
配置1:
{sys, [ {lib_dirs, ["./"]}, {rel, "test", "1", [kernel, stdlib, compiler, app_a ]}, {boot_rel, "test"}, {incl_cond, include}, {mod_cond,app}, %%其他配置项省略 ... ] }.最终打包的结果是:所有的app均被打包,包括app_a,app_b,app_c,以及otp所有的系统应用。
配置2:
{sys, [ {lib_dirs, ["/"]}, {rel, "test", "1", [kernel, stdlib, compiler, app_a ]}, {boot_rel, "test"}, {incl_cond, derived}, {mod_cond, all}, {app, app_a, [{incl_cond, include}]}, ... ] }.最终打包的结果是:除了app_a,app_b,app_c外,还包含了一部分otp应用,如stdlib,kernel,crypto等。
配置3:
{sys, [ {lib_dirs, ["./"]}, {rel, "test", "1", [kernel, stdlib, compiler, app_a ]}, {boot_rel, "test"}, {incl_cond, exclude}, {mod_cond, all}, {app, stdlib, [{incl_cond, include}]}, {app, kernel, [{incl_cond, include}]}, {app, compiler, [{incl_cond, include}]}, {app, app_a, [{incl_cond, include}]}, {app, app_b, [{incl_cond, include}]}, ... ] }.最终打包结果为:仅包含了stdlib,kernel,compiler,app_a,app_b这几个应用。
总结:incl_cond可设置的值包括include,exclude,derived。
include:这个很简单,就是应用需要被包含打包。所以在配置1中,由于app均未设置incl_cond,即使用全局incl_cond配置项的值(include),因此所有的应用均被打包。
exclude:这个也好理解,就是不需要包含或者排除。对于配置3,只有几个application指定为include,其余的均使用全局的配置,因此结果也是显而易见的。
derived:这个是指如果被其他指定为include的app所间接使用,那么也需要被打包。那么怎么理解被其他app所使用?erlang又是怎么判断一个application将使用另一个application呢?reltool在打包的时候,通过xref分析所有module调用外部module的导出函数,而module可理解为是属于某个application的,这么一来,便可以知道application都间接使用了哪些其他的application。在配置2中,尽管只有app_a设置为include(app_b和app_c的incl_cond值最终为derived),但是由于模块a1会调用模块b1的导出函数,模块b2会调用模块c2的导出函数,即app_b会间接被app_a所使用,app_c又会间接被app_b所使用,因此app_a,app_b,app_c最终都将被打包,同样stdlib,kernel也间接使用了hipe,crypto,因此相关app也会被打包,其他没有被间接使用的则不会被打包。
=======================================================
补充:reltool打包的时候怎么找到所有的application的?全局配置中有个lib_dirs,app的配置中有个lib_dir,有必然联系吗?两个都需要配置吗?
实际上,reltool在打包过程中,首先根据全局配置lib_dirs指定的路径,以及root_dir(该配置项通常不需要配置,默认为code:root_dir()下的lib目录,即安装是otp自带应用的目录位置)指定的路径,罗列出在这些路径下所有的application。然后解析配置文件中的application配置部分,未出现在之前罗列出的application则进行添加,如果已存在,则按需更新路径。因此对于配置1,最终打包结果包含所有otp自带的application也就不难理解了。
与模块包含相关的配置项就是mod_cond,这个配置项可设置的值包括:
derived:与incl_cond中的derived等同,即只有被其他明确为include的模块调用时将被包含打包。
ebin:ebin目录下所有的模块加上被其他明确为include的模块所调用的模块。
app:app文件中指定的模块加上被其他明确为include的模块所调用的模块。
all:所有模块。
mod_cond配置项相对较容易理解,但有一点需要注意:如果将配置1中mod_cond的值修改为derived,最终打包的结果是:所有的应用都包含,但是所有的应用的ebin目录下仅有一个.app文件,任何beam都未被打包,因为所有application的mod_cond最终设置为derived,也就是说没有哪个模块是明确设置为include。因此所有的模块都不会被打包。
另外,当incl_cond设置为derived时,mod_cond配置项会对app的包含关系有一定的影响。例如将前面的配置2做些修改。
配置4:
{sys, [ {lib_dirs, ["./"]}, {rel, "test", "1", [kernel, stdlib, compiler, app_a ]}, {boot_rel, "test"}, {incl_cond, derived}, {mod_cond, derived}, {app, app_a, [{incl_cond, include}, {mod_cond, all}]}, ... ] }.最终打包后发现app_c未被打包。
解释:对于app_a,mod_cond最终为all,即所有模块都将被打包;而对于app_b,它的mod_cond和incl_cond最终都为derived,但是app_a的a1模块会调用b1,因此该应用会被打包,但是只有b1模块;对于app_c,它的mod_cond和incl_cond最终也为derived,仅管app_b的b2会调用c2,但是app_b只有b1会被打包,b2不会被打包,所以app_c的所有模块最终是不被间接使用的,因而该application也是不被间接使用的,它也就不会被打包。
rel配置项内容与release resource file中的内容差不多,描述release的名称,版本,以及release中包含的application,以及这些application的启动类型。
这里需要注意:当app的incl_cond最终为derived时,如果该app又出现在了rel配置项中,那么这个application也还是会被打包。例如对前面的配置4做些修改,将app_c加到rel中,那么最终app_c也被打包了。
另外,如果某个app的incl_cond最终为exclude,而该app又出现在了rel中,那么打包就会出错。例如把配置3中的{app, compiler, [{incl_cond,include}]}一行删除,打包就出现如下错误。
从前面可以看到reltool的配置非常灵活,不同的场景可以设置不同的值来满足需要,比如设置mod_cond为derived保证不需要的模块都不打包以此保证最终的镜像足够精简,incl_cond设置为derived让reltool自动分析application之间的依赖关系等。但是灵活的同时可能会出现很多未知的问题,因此对于一般使用者,推荐将mod_cond设置为all,incl_cond设置为exclude,需要打包的app都手动添加对应的app配置项。