erlang代码热加载

最近用erlang开发项目,用rebar3进行项目的管理,在linux环境下运行,开发过程中要对代码进行测试测试,项目启动后,测试过程中会发现一些小的问题,代码修改后要执行:rebar3 shell,重新启动项目才能把更新后的代码加载到项目中,感觉很麻烦和费时。

想着可以不用重启项目的情况下,部署更新后的代码,于是查资料已便可以找到一个解决方案。主要思路:是在应用启动后,在应用的根目录下,不停止应用执行一个简单编译命令,就可以让变更后的代码生效,最终写了下面的代码:

-module(userc).
-author("pingjianwei").
%% API
-compile(export_all).
%% 宏WORK_DIRS里,列出的目录是项目开发过程中经常改动
%%的代码的目录,这些目录的路径全是相对于项目的根目录。
-define(WORK_DIRS, [
  "src/",
  "src/repo/",
  "src/model/",
  "src/protocol/model/",
  "src/protocol/model/mcht/",
  "src/protocol/model/ums/",
  "src/protocol/model/up/",
  "src/protocol/processor/",
  "src/ums/",
  "src/utils/",
  "src/web_handle/",
  "src/web_rest_api/"
]).
%% 定义头文件的目录
-define(INCLUDE_DIRS, ["/src/include/"]).

cfile(FileName) ->
%%  code:add_path(?OUT_DIR),
  {ok,RootPath} =file:get_cwd(),
  try
    cfile(FileName, ?WORK_DIRS)
  catch
     Exception :ErrorMsg-> io:format("~p:~p",[Exception,ErrorMsg])
  after
%保证无论身么情况下,保证编译完成后,回到应用根目录
    c:cd(RootPath)
  end.

cfile(FileName, []) ->
  {erlang:atom_to_list(FileName) ++ ":not find", ?WORK_DIRS};
cfile(FileName, [Path | Left]) ->
  FileDir = Path ++ erlang:atom_to_list(FileName) ++ ".erl",
  case file:read_file_info(FileDir) of
    {ok, _} ->
%找到要编译erl文件目录,切换file server到该目录下
      c:cd(Path),
      IncludeDirs = [begin {i, Dir} end || Dir <- ?INCLUDE_DIRS],
      io:format("outdir : ~p ~n",[get_out_dir(FileName)]),
%% CompileOptions变量定义了,copile时的一些选项
%  1.{outdir, get_out_dir(FileName)} 指定文件输出目录
%  2.{parse_transform, lager_transform},编译日志输出模块
%  3.{parse_transform, lager_transform},编译生成记录操作函数
      CompileOptions = [{outdir, get_out_dir(FileName)}, {parse_transform, lager_transform},
        {parse_transform, exprecs}, report, verbose] ++ IncludeDirs,

      case compile:file(FileName, CompileOptions) of
%加载编译后的beam文件
        {ok, T} -> c:l(T), {ok, T};
        Err -> Err
      end;
    _ ->
      cfile(FileName, Left)
  end.
%%这个函数是获取runtime时,beam文件的存放的目录
get_out_dir(FileName) when is_atom(FileName) ->
  Path = code:which(FileName),
  PathBin = list_to_binary(Path),
  {Pos,_} =binary:match(PathBin,list_to_binary([atom_to_binary(FileName,utf8),<<".beam">>])),
  OutDirBin =binary:part(PathBin,0,Pos),
  binary_to_list(OutDirBin).

用法说明

1.userc.erl文件放在应用的根目录下
erlang代码热加载_第1张图片
TIM截图20171021223027.png
2.进入linux环境,并进入应用的根目录,rebar3 shell 启动项目
3.执行:c(userc),编译该文件
4.代码调试过程中,修改了某个模块的名字,可以执行:userc:cfile(模块的名字),比如test.erl.可以这么编译:userc:cfile(test)。注意:被编译模块路径必须添加到:?WORK_DIRS宏里,否者就找不到源文件

但是如果一次性,改了多个地方,用这个反而麻烦,还是重启项目好,后期考虑,添加一个函数:usec:all/0, 就能编译指定目录的的所有文件,等测试好了,到时候再和大家分享。

你可能感兴趣的:(erlang代码热加载)