运送用 Erlang 编写的命令行工具的惯用方法
Idiomatic way to ship command line tools written in Erlang
问题
我能找到的大多数关于 Erlang 的文章和书籍都专注于创建长 运行 类服务器应用程序,而没有涵盖命令行工具的创建过程。
我有一个包含 3 个应用程序的多应用程序 rebar3 项目:
myweb
- 基于 Cowboy 的网络服务;
mycli
- 为 myweb
; 准备资产的命令行工具
mylib
- myweb
和 mycli
都使用的库,取决于 NIF。
作为构建的结果,我想获得这样的工件:
- 将为 HTTP 请求提供服务的 Web 部件的可执行文件;
- 用于资产准备的可执行命令行工具;
- 上面用到的一组库
要求
- CLI 的行为应该像一个健全的非交互式命令行工具:处理参数、处理 stdin/stdout、return 错误时的非零退出代码等;
- 服务器和 CLI 都应该能够使用 NIF;
- 将工件打包为一组
deb
/rpm
包应该很容易,因此服务器和 CLI 都应该重用公共依赖项。
到目前为止尝试过的东西
建立 escript
我在野外看到的一种方法是创建一个独立的 escript
文件。至少 rebar3 和 relx
这样做。所以我试了一下。
优点:
- 支持命令行参数;
- 如果出现错误,它 return 是非零退出代码。
缺点:
- 将所有依赖项嵌入到单个文件中,因此无法重复使用
mylib
;
- 由于
*.so
文件嵌入到生成的 escript
文件中,因此无法在运行时加载它们,因此 NIF 不起作用(参见 erlang rebar escriptize & nifs);
rebar3 escriptize
不能很好地处理依赖关系(参见 bug 1139)。
未知:
- CLI 应用程序是否应该成为合适的 OTP 应用程序?
- 它应该有监督树吗?
- 是否应该开始?
- 如果是这样,资产处理完成后如何停止?
构建版本
Fred Hebert 在 How I start: Erlang 文章中描述了另一种构建命令行工具的方法。
优点:
- 每个依赖应用程序都有自己的目录,便于共享和打包。
缺点:
- 没有像
escript
的 main/1
; 那样定义的入口点
- 因此必须手动处理命令行参数和退出代码。
未知:
- 如何以非交互方式对 CLI OTP 应用建模?
- 如何在资产处理完成后停止应用程序?
以上两种方法似乎都不适合我。
最好能两全其美:获得 escript
提供的基础设施,例如 main/1
入口点、命令行参数和退出代码处理,同时仍然拥有一个很好的目录结构,易于打包并且不妨碍使用 NIF。
然后从 'conventional' 模块进入代码的小脚本可能是一个解决方案。
例如,Concuerror is expected to be used as a command line tool and uses an escript as its entry point. It handles command-line arguments via getopt。所有主要代码都在常规的 Erlang 模块中,这些模块包含在带有简单参数的 escript 路径中。
据我了解,然后可以使用常规 -onload
属性加载 NIF(Concuerror 不使用 NIF)。
无论您是在 Erlang 中启动一个 long-运行ning 类似守护进程的应用程序,还是一个 CLI 命令,您始终需要以下内容:
erts
application - 特定版本的虚拟机和内核
- Erlang OTP 应用程序
- 您的应用程序的依赖项
- CLI 入口点
然后在任何一种情况下,CLI 入口点都必须启动 Erlang VM 并执行它应该在给定情况下执行的代码。然后它将退出或继续 运行ning - 后者用于长期 运行ning 应用程序。
CLI 入口点可以是启动 Erlang VM 的任何东西,例如escript
脚本、sh
、bash
等。escript
相对于通用 shell 的明显优势是 escript
已经在执行Erlang 虚拟机的上下文,因此无需处理 starting/stopping 虚拟机。
您可以通过两种方式启动 Erlang VM:
- 使用系统范围的 Erlang VM
- 使用 embedded Erlang 版本
在第一种情况下,您既不提供 erts
也不随包提供任何 OTP 应用程序,您只需将特定的 Erlang 版本作为您的应用程序的依赖项。在第二种情况下,您提供 erts
和所有必需的 OTP 应用程序以及您的程序包中的应用程序依赖项。
在第二种情况下,您还需要在启动 VM 时正确设置 code root。但这很容易,请参阅 Erlang 用于启动系统范围 VM 的 erl
脚本:
# location: /usr/local/lib/erlang/bin/erl
ROOTDIR="/usr/local/lib/erlang"
BINDIR=$ROOTDIR/erts-7.2.1/bin
EMU=beam
PROGNAME=`echo [=10=] | sed 's/.*\///'`
export EMU
export ROOTDIR
export BINDIR
export PROGNAME
exec "$BINDIR/erlexec" ${1+"$@"}
这可以通过脚本来处理,例如 node_package
tool that Basho uses to package their Riak database for all major operating systems. I am maintaining my own fork of it which I am using with my own build tool called builderl
。我只是这么说,所以你知道如果我设法定制它,你也能做到:)
启动 Erlang VM 后,您的应用程序应该能够加载和启动任何应用程序,无论是随 Erlang 提供的还是您的应用程序(包括您提到的 mylib
库)。以下是如何实现这一目标的一些示例:
escript
例子
参见 this builderl.esh
example how I handle loading other Erlang applications from builderl
. That escript
script assumes that the Erlang installation is relative to the folder from which it's executed. When it's a part of another application, like for example humbundee
, the load_builderl.hrl
include file compiles and loads bld_load
, which in turn loads all remaining modules with bld_load:boot/3
. Notice how I can use standard OTP applications without specifying where they are - builderl
is being executed by escript
and so all the applications are loaded from where they were installed (/usr/local/lib/erlang/lib/
on my system). If libraries used by your application, e.g. mylib
, are installed somewhere else, all you need to do is add that location to the Erlang path, e.g. with code:add_path
。 Erlang 将自动从添加到代码路径列表的文件夹中加载代码中使用的模块。
嵌入式Erlang
但是,如果应用程序是独立于系统范围的 Erlang 安装的正确 OTP 版本,则同样适用。那是因为在那种情况下,脚本由属于该嵌入式 Erlang 版本的 escript
执行,而不是系统范围的版本(即使它已安装)。因此它知道属于该版本的所有应用程序(包括您的应用程序)的位置。例如 riak
正是这样做的——在他们的包中,他们提供了一个 embedded Erlang release,其中包含它自己的 erts
和所有依赖的 Erlang 应用程序。这样 riak
就可以在主机操作系统上什至没有安装 Erlang 的情况下启动。这是 FreeBSD 上 riak
软件包的摘录:
% tar -tf riak2-2.1.1_1.txz
/usr/local/sbin/riak
/usr/local/lib/riak/releases/start_erl.data
/usr/local/lib/riak/releases/2.1.0/riak.rel
/usr/local/lib/riak/releases/RELEASES
/usr/local/lib/riak/erts-5.10.3/bin/erl
/usr/local/lib/riak/erts-5.10.3/bin/beam
/usr/local/lib/riak/erts-5.10.3/bin/erlc
/usr/local/lib/riak/lib/stdlib-1.19.3/ebin/re.beam
/usr/local/lib/riak/lib/ssl-5.3.1/ebin/tls_v1.beam
/usr/local/lib/riak/lib/crypto-3.1/ebin/crypto.beam
/usr/local/lib/riak/lib/inets-5.9.6/ebin/inets.beam
/usr/local/lib/riak/lib/bitcask-1.7.0/ebin/bitcask.app
/usr/local/lib/riak/lib/bitcask-1.7.0/ebin/bitcask.beam
(...)
sh
/bash
除了在启动 Erlang VM 时必须显式调用您要执行的函数(入口点或您调用的 main
函数之外,这在原则上与上述没有太大区别).
考虑 builderl
生成的这个脚本来启动 Erlang 应用程序只是为了执行指定的任务(生成 RELEASES
文件),之后节点关闭:
#!/bin/sh
START_ERL=`cat releases/start_erl.data`
APP_VSN=${START_ERL#* }
run_erl -daemon ../hbd/shell/ ../hbd/log "exec erl ../hbd releases releases/start_erl.data -config releases/$APP_VSN/hbd.config -args_file ../hbd/etc/vm.args -boot releases/$APP_VSN/humbundee -noshell -noinput -eval \"{ok, Cwd} = file:get_cwd(), release_handler:create_RELEASES(Cwd, \\"releases\\", \\"releases/$APP_VSN/humbundee.rel\\", []), init:stop()\""
这是一个类似的脚本,但不启动任何特定代码或应用程序。相反,它会启动一个正确的 OTP 版本,因此启动哪些应用程序以及启动顺序取决于版本(由 -boot
选项指定)。
#!/bin/sh
START_ERL=`cat releases/start_erl.data`
APP_VSN=${START_ERL#* }
run_erl -daemon ../hbd/shell/ ../hbd/log "exec erl ../hbd releases releases/start_erl.data -config releases/$APP_VSN/hbd.config -args_file ../hbd/etc/vm.args -boot releases/$APP_VSN/humbundee"
在 vm.args
文件中,您可以根据需要提供应用程序的其他路径,例如:
-pa lib/humbundee/ebin lib/yolf/ebin deps/goldrush/ebin deps/lager/ebin deps/yajler/ebin
在此示例中,这些是相对的,但如果您的应用程序安装在标准的众所周知的位置,则可以是绝对的。此外,仅当您使用系统范围的 Erlang 安装并需要添加额外的路径来定位您的 Erlang 应用程序时,或者如果您的 Erlang 应用程序位于非标准位置(例如不在 lib
文件夹,正如 Erlang OTP 所要求的那样)。在适当的嵌入式 Erlang 版本中,应用程序位于 code root/lib
文件夹中,Erlang 能够加载这些应用程序而无需指定任何其他路径。
总结及其他注意事项
Erlang 应用程序的部署与其他用脚本语言编写的项目没有太大区别,例如ruby 或 python 个项目。所有这些项目都必须处理类似的问题,我相信每个操作系统的包管理都会以一种或另一种方式处理它们:
了解您的操作系统如何处理具有 运行 时间依赖性的打包项目。
查看其他 Erlang 应用程序是如何为您的操作系统打包的,其中有很多通常由所有主要系统分发:RabbitMQ、Ejabberd、Riak 等。只需下载压缩包并将其解压到一个文件夹中,然后您将看到所有文件的位置。
编辑 - 参考要求
回到您的要求,您有以下选择:
将 Erlang 安装为系统范围内的 OTP 版本,作为嵌入式 Erlang,或作为应用程序包安装在一些随机文件夹中(抱歉 Rebar)
您可以有多个入口点,形式为 sh
或 escript
脚本,执行从已安装版本中选择的应用程序。只要您正确配置了这些应用程序的代码根和路径(如上所述),两者都可以工作。
那么您的每个应用程序:myweb
和 mycli
都需要在其自己的新上下文中执行,例如启动一个新的 VM 实例并执行所需的应用程序(来自相同的 Erlang 版本)。在 myweb
的情况下,入口点可以是 sh
脚本,根据版本启动一个新节点(类似于 Riak)。在 mycli
的情况下,入口点可以是 escript
,一旦任务完成,它就会结束执行。
但是完全有可能创建一个 运行ning 任务来退出 VM,即使它是从 sh
启动的 - 请参见上面的示例。在这种情况下,mycli
将需要单独的发布文件 - script
和 boot
来启动 VM。当然也可以从 escript
.
启动一个 long-运行ning Erlang VM
我提供了一个同时使用所有这些方法的示例项目,humbundee。编译后,它提供了三个访问点:
cmd
发布。
humbundee
发布。
-
builder.esh
escript
.
第一个用于启动节点进行安装,然后将其关闭。第二个用于启动一个 long-运行ning Erlang 应用程序。第三个是 install/configure 节点的构建工具。这是创建发布后项目的样子:
$:~/work/humbundee/tmp/rel % ls | tr " " "\n"
bin
erts-7.3
etc
lib
releases
$:~/work/humbundee/tmp/rel % ls bin | tr " " "\n"
builderl.esh
cmd.boot
humbundee.boot
epmd
erl
escript
run_erl
to_erl
(...)
$:~/work/humbundee/tmp/rel % ls lib | tr " " "\n"
builderl-0.2.7
compiler-6.0.3
deploy-0.0.1
goldrush-0.1.7
humbundee-0.0.1
kernel-4.2
lager-3.0.1
mnesia-4.13.3
sasl-2.7
stdlib-2.8
syntax_tools-1.7
yajler-0.0.1
yolf-0.1.1
$:~/work/humbundee/tmp/rel % ls releases/hbd-0.0.1 | tr " " "\n"
builderl.config
cmd.boot
cmd.rel
cmd.script
humbundee.boot
humbundee.rel
humbundee.script
sys.config.src
cmd
入口点将使用应用程序 deploy-0.0.1
和 builderl-0.2.7
以及发布文件 cmd.boot
、cmd.script
和一些 OTP 应用程序。标准 humbundee
入口点将使用除 builderl
和 deploy
之外的所有应用程序。然后 builderl.esh
脚本将使用应用程序 deploy-0.0.1
和 builderl-0.2.7
。全部来自相同的嵌入式 Erlang OTP 安装。
问题
我能找到的大多数关于 Erlang 的文章和书籍都专注于创建长 运行 类服务器应用程序,而没有涵盖命令行工具的创建过程。
我有一个包含 3 个应用程序的多应用程序 rebar3 项目:
myweb
- 基于 Cowboy 的网络服务;mycli
- 为myweb
; 准备资产的命令行工具
mylib
-myweb
和mycli
都使用的库,取决于 NIF。
作为构建的结果,我想获得这样的工件:
- 将为 HTTP 请求提供服务的 Web 部件的可执行文件;
- 用于资产准备的可执行命令行工具;
- 上面用到的一组库
要求
- CLI 的行为应该像一个健全的非交互式命令行工具:处理参数、处理 stdin/stdout、return 错误时的非零退出代码等;
- 服务器和 CLI 都应该能够使用 NIF;
- 将工件打包为一组
deb
/rpm
包应该很容易,因此服务器和 CLI 都应该重用公共依赖项。
到目前为止尝试过的东西
建立 escript
我在野外看到的一种方法是创建一个独立的 escript
文件。至少 rebar3 和 relx
这样做。所以我试了一下。
优点:
- 支持命令行参数;
- 如果出现错误,它 return 是非零退出代码。
缺点:
- 将所有依赖项嵌入到单个文件中,因此无法重复使用
mylib
; - 由于
*.so
文件嵌入到生成的escript
文件中,因此无法在运行时加载它们,因此 NIF 不起作用(参见 erlang rebar escriptize & nifs); rebar3 escriptize
不能很好地处理依赖关系(参见 bug 1139)。
未知:
- CLI 应用程序是否应该成为合适的 OTP 应用程序?
- 它应该有监督树吗?
- 是否应该开始?
- 如果是这样,资产处理完成后如何停止?
构建版本
Fred Hebert 在 How I start: Erlang 文章中描述了另一种构建命令行工具的方法。
优点:
- 每个依赖应用程序都有自己的目录,便于共享和打包。
缺点:
- 没有像
escript
的main/1
; 那样定义的入口点
- 因此必须手动处理命令行参数和退出代码。
未知:
- 如何以非交互方式对 CLI OTP 应用建模?
- 如何在资产处理完成后停止应用程序?
以上两种方法似乎都不适合我。
最好能两全其美:获得 escript
提供的基础设施,例如 main/1
入口点、命令行参数和退出代码处理,同时仍然拥有一个很好的目录结构,易于打包并且不妨碍使用 NIF。
然后从 'conventional' 模块进入代码的小脚本可能是一个解决方案。
例如,Concuerror is expected to be used as a command line tool and uses an escript as its entry point. It handles command-line arguments via getopt。所有主要代码都在常规的 Erlang 模块中,这些模块包含在带有简单参数的 escript 路径中。
据我了解,然后可以使用常规 -onload
属性加载 NIF(Concuerror 不使用 NIF)。
无论您是在 Erlang 中启动一个 long-运行ning 类似守护进程的应用程序,还是一个 CLI 命令,您始终需要以下内容:
erts
application - 特定版本的虚拟机和内核- Erlang OTP 应用程序
- 您的应用程序的依赖项
- CLI 入口点
然后在任何一种情况下,CLI 入口点都必须启动 Erlang VM 并执行它应该在给定情况下执行的代码。然后它将退出或继续 运行ning - 后者用于长期 运行ning 应用程序。
CLI 入口点可以是启动 Erlang VM 的任何东西,例如escript
脚本、sh
、bash
等。escript
相对于通用 shell 的明显优势是 escript
已经在执行Erlang 虚拟机的上下文,因此无需处理 starting/stopping 虚拟机。
您可以通过两种方式启动 Erlang VM:
- 使用系统范围的 Erlang VM
- 使用 embedded Erlang 版本
在第一种情况下,您既不提供 erts
也不随包提供任何 OTP 应用程序,您只需将特定的 Erlang 版本作为您的应用程序的依赖项。在第二种情况下,您提供 erts
和所有必需的 OTP 应用程序以及您的程序包中的应用程序依赖项。
在第二种情况下,您还需要在启动 VM 时正确设置 code root。但这很容易,请参阅 Erlang 用于启动系统范围 VM 的 erl
脚本:
# location: /usr/local/lib/erlang/bin/erl
ROOTDIR="/usr/local/lib/erlang"
BINDIR=$ROOTDIR/erts-7.2.1/bin
EMU=beam
PROGNAME=`echo [=10=] | sed 's/.*\///'`
export EMU
export ROOTDIR
export BINDIR
export PROGNAME
exec "$BINDIR/erlexec" ${1+"$@"}
这可以通过脚本来处理,例如 node_package
tool that Basho uses to package their Riak database for all major operating systems. I am maintaining my own fork of it which I am using with my own build tool called builderl
。我只是这么说,所以你知道如果我设法定制它,你也能做到:)
启动 Erlang VM 后,您的应用程序应该能够加载和启动任何应用程序,无论是随 Erlang 提供的还是您的应用程序(包括您提到的 mylib
库)。以下是如何实现这一目标的一些示例:
escript
例子
参见 this builderl.esh
example how I handle loading other Erlang applications from builderl
. That escript
script assumes that the Erlang installation is relative to the folder from which it's executed. When it's a part of another application, like for example humbundee
, the load_builderl.hrl
include file compiles and loads bld_load
, which in turn loads all remaining modules with bld_load:boot/3
. Notice how I can use standard OTP applications without specifying where they are - builderl
is being executed by escript
and so all the applications are loaded from where they were installed (/usr/local/lib/erlang/lib/
on my system). If libraries used by your application, e.g. mylib
, are installed somewhere else, all you need to do is add that location to the Erlang path, e.g. with code:add_path
。 Erlang 将自动从添加到代码路径列表的文件夹中加载代码中使用的模块。
嵌入式Erlang
但是,如果应用程序是独立于系统范围的 Erlang 安装的正确 OTP 版本,则同样适用。那是因为在那种情况下,脚本由属于该嵌入式 Erlang 版本的 escript
执行,而不是系统范围的版本(即使它已安装)。因此它知道属于该版本的所有应用程序(包括您的应用程序)的位置。例如 riak
正是这样做的——在他们的包中,他们提供了一个 embedded Erlang release,其中包含它自己的 erts
和所有依赖的 Erlang 应用程序。这样 riak
就可以在主机操作系统上什至没有安装 Erlang 的情况下启动。这是 FreeBSD 上 riak
软件包的摘录:
% tar -tf riak2-2.1.1_1.txz
/usr/local/sbin/riak
/usr/local/lib/riak/releases/start_erl.data
/usr/local/lib/riak/releases/2.1.0/riak.rel
/usr/local/lib/riak/releases/RELEASES
/usr/local/lib/riak/erts-5.10.3/bin/erl
/usr/local/lib/riak/erts-5.10.3/bin/beam
/usr/local/lib/riak/erts-5.10.3/bin/erlc
/usr/local/lib/riak/lib/stdlib-1.19.3/ebin/re.beam
/usr/local/lib/riak/lib/ssl-5.3.1/ebin/tls_v1.beam
/usr/local/lib/riak/lib/crypto-3.1/ebin/crypto.beam
/usr/local/lib/riak/lib/inets-5.9.6/ebin/inets.beam
/usr/local/lib/riak/lib/bitcask-1.7.0/ebin/bitcask.app
/usr/local/lib/riak/lib/bitcask-1.7.0/ebin/bitcask.beam
(...)
sh
/bash
除了在启动 Erlang VM 时必须显式调用您要执行的函数(入口点或您调用的 main
函数之外,这在原则上与上述没有太大区别).
考虑 builderl
生成的这个脚本来启动 Erlang 应用程序只是为了执行指定的任务(生成 RELEASES
文件),之后节点关闭:
#!/bin/sh
START_ERL=`cat releases/start_erl.data`
APP_VSN=${START_ERL#* }
run_erl -daemon ../hbd/shell/ ../hbd/log "exec erl ../hbd releases releases/start_erl.data -config releases/$APP_VSN/hbd.config -args_file ../hbd/etc/vm.args -boot releases/$APP_VSN/humbundee -noshell -noinput -eval \"{ok, Cwd} = file:get_cwd(), release_handler:create_RELEASES(Cwd, \\"releases\\", \\"releases/$APP_VSN/humbundee.rel\\", []), init:stop()\""
这是一个类似的脚本,但不启动任何特定代码或应用程序。相反,它会启动一个正确的 OTP 版本,因此启动哪些应用程序以及启动顺序取决于版本(由 -boot
选项指定)。
#!/bin/sh
START_ERL=`cat releases/start_erl.data`
APP_VSN=${START_ERL#* }
run_erl -daemon ../hbd/shell/ ../hbd/log "exec erl ../hbd releases releases/start_erl.data -config releases/$APP_VSN/hbd.config -args_file ../hbd/etc/vm.args -boot releases/$APP_VSN/humbundee"
在 vm.args
文件中,您可以根据需要提供应用程序的其他路径,例如:
-pa lib/humbundee/ebin lib/yolf/ebin deps/goldrush/ebin deps/lager/ebin deps/yajler/ebin
在此示例中,这些是相对的,但如果您的应用程序安装在标准的众所周知的位置,则可以是绝对的。此外,仅当您使用系统范围的 Erlang 安装并需要添加额外的路径来定位您的 Erlang 应用程序时,或者如果您的 Erlang 应用程序位于非标准位置(例如不在 lib
文件夹,正如 Erlang OTP 所要求的那样)。在适当的嵌入式 Erlang 版本中,应用程序位于 code root/lib
文件夹中,Erlang 能够加载这些应用程序而无需指定任何其他路径。
总结及其他注意事项
Erlang 应用程序的部署与其他用脚本语言编写的项目没有太大区别,例如ruby 或 python 个项目。所有这些项目都必须处理类似的问题,我相信每个操作系统的包管理都会以一种或另一种方式处理它们:
了解您的操作系统如何处理具有 运行 时间依赖性的打包项目。
查看其他 Erlang 应用程序是如何为您的操作系统打包的,其中有很多通常由所有主要系统分发:RabbitMQ、Ejabberd、Riak 等。只需下载压缩包并将其解压到一个文件夹中,然后您将看到所有文件的位置。
编辑 - 参考要求
回到您的要求,您有以下选择:
将 Erlang 安装为系统范围内的 OTP 版本,作为嵌入式 Erlang,或作为应用程序包安装在一些随机文件夹中(抱歉 Rebar)
您可以有多个入口点,形式为
sh
或escript
脚本,执行从已安装版本中选择的应用程序。只要您正确配置了这些应用程序的代码根和路径(如上所述),两者都可以工作。
那么您的每个应用程序:myweb
和 mycli
都需要在其自己的新上下文中执行,例如启动一个新的 VM 实例并执行所需的应用程序(来自相同的 Erlang 版本)。在 myweb
的情况下,入口点可以是 sh
脚本,根据版本启动一个新节点(类似于 Riak)。在 mycli
的情况下,入口点可以是 escript
,一旦任务完成,它就会结束执行。
但是完全有可能创建一个 运行ning 任务来退出 VM,即使它是从 sh
启动的 - 请参见上面的示例。在这种情况下,mycli
将需要单独的发布文件 - script
和 boot
来启动 VM。当然也可以从 escript
.
我提供了一个同时使用所有这些方法的示例项目,humbundee。编译后,它提供了三个访问点:
cmd
发布。humbundee
发布。-
builder.esh
escript
.
第一个用于启动节点进行安装,然后将其关闭。第二个用于启动一个 long-运行ning Erlang 应用程序。第三个是 install/configure 节点的构建工具。这是创建发布后项目的样子:
$:~/work/humbundee/tmp/rel % ls | tr " " "\n"
bin
erts-7.3
etc
lib
releases
$:~/work/humbundee/tmp/rel % ls bin | tr " " "\n"
builderl.esh
cmd.boot
humbundee.boot
epmd
erl
escript
run_erl
to_erl
(...)
$:~/work/humbundee/tmp/rel % ls lib | tr " " "\n"
builderl-0.2.7
compiler-6.0.3
deploy-0.0.1
goldrush-0.1.7
humbundee-0.0.1
kernel-4.2
lager-3.0.1
mnesia-4.13.3
sasl-2.7
stdlib-2.8
syntax_tools-1.7
yajler-0.0.1
yolf-0.1.1
$:~/work/humbundee/tmp/rel % ls releases/hbd-0.0.1 | tr " " "\n"
builderl.config
cmd.boot
cmd.rel
cmd.script
humbundee.boot
humbundee.rel
humbundee.script
sys.config.src
cmd
入口点将使用应用程序 deploy-0.0.1
和 builderl-0.2.7
以及发布文件 cmd.boot
、cmd.script
和一些 OTP 应用程序。标准 humbundee
入口点将使用除 builderl
和 deploy
之外的所有应用程序。然后 builderl.esh
脚本将使用应用程序 deploy-0.0.1
和 builderl-0.2.7
。全部来自相同的嵌入式 Erlang OTP 安装。