Erlang makefile,运行时生成

Erlang makefile,运行时生成,makefile,erlang,Makefile,Erlang,我为我的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=$

我为我的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
#生成文件
SRC_DIR=SRC
BIN_DIR=ebin
DB_DIR=DB
厄尔=厄尔
ERLC=ERLC
厄尔库旗=
SOURCES=$(通配符${SRC_DIR}/*.erl)
HEADERS=$(通配符${SRC_DIR}/*.hrl)
OBJECTS=$(源:${SRC_DIR}/%.erl=${BIN_DIR}/%.beam)
全部:$(对象)
ebin/%.beam:src/%.erl$(标题)生成文件
${ERLC}$(ERLC_标志)-o${BIN_DIR}/$<
drop_db:
rm-r${DB_DIR}
清洁:
-rm$(对象)
运行:
${ERL}-pa${BIN_DIR}-s god run

Erlang允许在运行时轻松修改应用程序代码,但这并不意味着它对用户是透明的

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

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

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

  • 一个节点中只能同时存在(:o)2个版本的模块。如果您运行一个模块,然后两次加载该模块进行2次修改,最旧的代码将“消失”,并且仍然使用此版本的任何进程都将死亡。您需要同步升级。功能
    code:soft\u purge(模块)
    可帮助您实现这一点

在所有这些警告之后,回到乐趣上来。您可以轻松地进行代码升级,以更加熟悉它。只需要两个步骤和一个条件:

  • 新的beam文件(编译后的代码)必须位于Erlang VM的代码路径中(或者您必须在下一步中使用
    code:load\u abs(Filename)

  • 您必须在VM中加载新代码。如果使用
    c(my_模块)从shell编译代码。
    将自动完成。如果通过其他方式编译,则必须使用例如
    code:load\u file(my\u模块)
    在VM中显式加载新代码

  • 然后,您必须确保使用此模块的每个进程执行对导出函数的完全限定调用,即:
    my\u模块:code\u更改(状态)
    。如果使用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中编译它,启动两个进程并使用它们:

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>
在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已停止。

运行
make run
不会运行新代码,或者您以前运行的shell不会神奇地看到新编译的代码?你正在加载新代码吗?您是否有在erlang代码中切换旧运行代码的代码?编译后如何加载新模块?我已经很久没有使用erlang了,不能肯定这一点,但我认为只需从shell中再次编译它就足够了。在外部编译之后,您可能只需要告诉shell加载它(使用
m()
或类似的方法)。但我真的不知道。我想大多数优秀的erlang指南都会涵盖这一点
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>