Erlang makefile,make while 运行

Erlang makefile, make while running

我为我的 erlang 项目编写了一个 makefile,它可以工作。但是当项目是 运行ning 时,如果我更改一个文件并通过 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

Erlang允许修改运行中的应用程序代码,但并不意味着它对用户是透明的。

希望它不是透明的,否则无法使用该功能,或者仅限于琐碎的用例。以下是我想到的证明代码更改必须由用户负责和控制的前 3 个理由:

  • 从一个版本到另一个版本,数据可能需要适配(修改记录,修改结构,新数据...)。所以在转换时,新版本的代码必须更新状态数据;它甚至必须验证它是否能够做到这一点(它是否具有从版本 X 到版本 Y 的转换代码)。 OTP 行为为此提供了特殊的回调。

  • 一个应用程序的修改可能涉及多个模块的更改,一个模块可能运行被多个进程(例如web客户端)。但是代码在非常特定的时间(完全合格的调用)在进程级别更新,并且不能同时发生在所有进程中。所以必须控制模块升级的顺序。函数 code:load_file(Module) 允许这样做。

  • 只有 (:o) 一个模块的 2 个版本可以同时存在于一个节点中。如果你 运行 一个模块然后进行 2 次修改加载模块两次,最旧的代码 "disappears" 和任何仍在使用这个版本的进程都会死掉。您需要同步升级。函数 code:soft_purge(Module) 可以帮助您。

在所有这些警告之后又回到了乐趣。您可以轻松玩代码升级以更加熟悉它。只需2个步骤和一个条件:

  • 新的 beam 文件(编译后的代码)必须在您的 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.

在虚拟机外编译并测试:

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 已经死了。