我为我的erlang项目编写了一个makefile,它可以工作。但是当项目运行时,如果我更改文件并通过makefile创建,则新版本的代码不起作用,我必须退出shell并再次启动它以运行新版本。怎么解决呢?
生成文件:
# Makefile
SRC_DIR = src
BIN_DIR = ebin
DB_DIR = db
ERL = erl
ERLC = erlc
ERLC_FLAGS=
SOURCES=$(wildcard ${SRC_DIR}/*.erl)
HEADERS=$(wildcard ${SRC_DIR}/*.hrl)
OBJECTS=$(SOURCES:${SRC_DIR}/%.erl=${BIN_DIR}/%.beam)
all: $(OBJECTS)
ebin/%.beam : src/%.erl $(HEADERS) Makefile
${ERLC} $(ERLC_FLAGS) -o ${BIN_DIR}/ $<
drop_db:
rm -r ${DB_DIR}
clean:
-rm $(OBJECTS)
run:
${ERL} -pa ${BIN_DIR} -s god run
答案 0 :(得分:3)
Erlang允许轻松修改运行中的应用程序代码,但这并不意味着它对用户是透明的。
希望它不透明,否则该功能无法使用,或仅限于普通用例。以下是我想到的前三个理由,证明代码更改必须由用户负责和控制:
从一个版本到另一个版本,可能需要调整数据(修改记录,修改结构,新数据......)。所以在转换时,新版本的代码必须更新状态数据;它甚至必须验证它能够做到(它是否具有从版本X到版本Y的转换代码)。 OTP行为为此提供了特殊的回调。
应用程序修改可能涉及多个模块的更改,并且模块可能由许多进程(例如Web客户端)运行。但是代码在特定时间(完全合格的调用)在进程级别更新,并且不能同时对所有进程进行更新。因此,您必须控制模块升级的顺序。函数code:load_file(Module)
允许这样做。
仅(:o)模块的2个版本可以在一个节点中同时存在。如果你运行一个模块,然后进行两次修改,加载模块两次,最旧的代码“消失”,任何仍然使用这个版本的进程就会死掉。您需要同步升级。函数code:soft_purge(Module)
可以帮助您实现这一目标。
在所有这些警告回归之后。您可以轻松地使用代码升级来更熟悉它。只需要两个步骤和一个条件:
新的梁文件(已编译的代码)必须位于Erlang VM的代码路径中(或者您必须在下一步中使用code:load_abs(Filename)
)。
您必须在VM中加载新代码。如果使用c(my_module).
从shell编译代码,则会自动完成。如果您通过其他方式编译它,则必须使用例如code:load_file(my_module)
在VM中显式加载新代码。
然后,您必须确保使用此模块的每个进程都对导出的函数执行完全限定的调用,例如:my_module:code_change(State)
。如果您使用OTP行为,则存在此回调,并在参数中提供旧版本的代码。
让我们使用以下模块:
-module(one).
-compile([export_all]).
-define (VERSION,1).
start() ->
rserver,spawn(?MODULE,init,[]).
init() ->
loop(0).
loop(N) ->
receive
print ->
io:format("received ~p message(s) so far~n",[N+1]),
loop(N+1);
load ->
io:format("received ~p message(s) so far~n reload the code~n",[N+1]),
?MODULE:loop(N+1);
version ->
io:format("received ~p message(s) so far~n version is ~p~n",[N+1,?VERSION]),
loop(N+1);
M ->
io:format("received unexpected message ~p: ignored~n",[M]),
loop(N)
end.
我在shell中编译它,启动2个进程并使用它们:
1> c(one).
{ok,one}
2> P1 = one:start().
<0.40.0>
3> P2 = one:start().
<0.42.0>
4> P1 ! print.
received 1 message(s) so far
print
5> P1 ! print.
received 2 message(s) so far
print
6> P1 ! version.
received 3 message(s) so far
version is 1
version
7> P1 ! reset.
received unexpected message reset: ignored
reset
8> P2 ! print.
received 1 message(s) so far
print
9>
现在我将代码修改为:
-module(one).
-compile([export_all]).
-define (VERSION,2).
start() ->
rserver,spawn(?MODULE,init,[]).
init() ->
loop(0).
loop(N) ->
receive
print ->
io:format("received ~p message(s) so far~n",[N+1]),
loop(N+1);
load ->
io:format("received ~p message(s) so far~n reload the code~n",[N+1]),
?MODULE:loop(N+1);
version ->
io:format("received ~p message(s) so far~n version is ~p~n",[N+1,?VERSION]),
loop(N+1);
reset ->
io:format("received ~p message(s) so far, reset message count~n",[N+1]),
loop(0);
M ->
io:format("received unexpected message ~p: ignored~n",[M]),
loop(N)
end.
在VM之外编译并测试:
9> P1 ! version.
received 4 message(s) so far
version is 1
version
10> P1 ! load.
received 5 message(s) so far
reload the code
load
11> P1 ! version.
received 6 message(s) so far
version is 1
version
12> P1 ! reset.
received unexpected message reset: ignored
reset
13> P2 ! print.
received 2 message(s) so far
print
14>
由于我没有加载代码,因此没有更新:如果每次外部呼叫都存在新版本,VM将不会浪费时间在代码路径中进行搜索!
14> code:load_file(one).
{module,one}
15> P1 ! version.
received 7 message(s) so far
version is 1
version
16> P2 ! version.
received 3 message(s) so far
version is 1
version
17> P1 ! load.
received 8 message(s) so far
reload the code
load
18> P1 ! version.
received 9 message(s) so far
version is 2
version
19> P1 ! reset.
received 10 message(s) so far, reset message count
reset
20> P1 ! print.
received 1 message(s) so far
print
21> P2 ! version.
received 4 message(s) so far
version is 1
version
22> P2 ! print.
received 5 message(s) so far
print
23>
加载新代码后,我已经能够升级P1的版本,而P2仍然是版本1.
现在我做了一个新的修改,只需转到版本3并在shell中编译以强制加载代码:
23> c(one).
{ok,one}
24> P1 ! version.
received 2 message(s) so far
version is 2
version
25> P1 ! load.
received 3 message(s) so far
reload the code
load
26> P1 ! version.
received 4 message(s) so far
version is 3
version
27> P2 ! print.
print
28>
我已经能够将流程P1从版本2升级到3.但是仍在使用版本1的流程P2已经死亡。