Browse Source

ft: 修改

master
SisMaker 3 years ago
parent
commit
3aeda583db
12 changed files with 1350 additions and 1271 deletions
  1. +26
    -15
      README.md
  2. +24
    -12
      erlSync.sample.config
  3. +0
    -23
      include/erlSync.hrl
  4. BIN
      priv/fileSync
  5. BIN
      priv/fileSync.exe
  6. +3
    -3
      src/eSync.app.src
  7. +1297
    -0
      src/eSync.erl
  8. +0
    -61
      src/erlSync.erl
  9. +0
    -10
      src/erlSync_app.erl
  10. +0
    -27
      src/erlSync_sup.erl
  11. +0
    -234
      src/sync/esSyncSrv.erl
  12. +0
    -886
      src/sync/esUtils.erl

+ 26
- 15
README.md View File

@ -1,39 +1,50 @@
# erlSync
otp21.2+
# eSync
Erlang即时重新编译和重新加载!
## 基于 [fsnotify](https://github.com/fsnotify/fsnotify) 跨平台文件系统通知。
## 改造自 [sync](https://github.com/rustyio/sync)
## 封装的监听文件项目[fileSync](https://github.com/SisMaker/fileSync) 如果要自己构建执行文件, 拉取监听文件项目, 然后 go build 复制执行文件到该工程的 priv 目录即可
# 特点
本项目实现了自带编译与加载功能,另外支持额外的编译命令,但是执行额外的编译命令是通过os:cmd(),会阻塞VM不是很建议使用.
启动后,erlSync会收集监听目录下的源文件和编译选项等的信息。
启动后,eSync会收集监听目录下的源文件和编译选项等的信息。
不仅适用于开发模式,也可以在生产环境中运行。
注意:linux下拉取项目后 需要给priv目录下的执行文件添加执行权限
# 使用
启动自动编译与加载
erlSync:run().
eSync:run().
暂停自动编译与加载
erlSync:pause().
eSync:pause().
停止自动编译应用
eSync:stop().
启动或者关闭集群同步加载
erlSync:swSyncNode(TrueOrFalse).
eSync:swSyncNode(TrueOrFalse).
设置编译与加载日志提示
erlSync:setLog(Val).
eSync:setLog(Val).
设置加载后的钩子函数(支持匿名函数, {Mod, Fun}(Fun函数只有一个参数)格式, 以及他们的列表组合)
erlSync:setOnsync(FunOrFuns).
eSync:setOnMSync(FunOrFuns).
eSync:setOnCSync(FunOrFuns).
# 配置说明
参见erlSync.sample.config
参见eSync.sample.config
默认配置为
[
{erlSync,
{eSync,
[
{listenPort, 12369},
{compileCmd, undefined},
{extraDirs, undefined}
{log, all},
{descendant, fix}
{descendant, fix},
{onMSyncFun, undefined},
{onCSyncFun, undefined},
{swSyncNode, false},
{isJustMem, false},
{debugInfoKeyFun, undefined}
]
}
]

+ 24
- 12
erlSync.sample.config View File

@ -1,8 +1,5 @@
[
{erlSync, [
%% 接受fileSync的监听端口
{listenPort, 12369},
{eSync, [
%% 编译命令 支持项目自带的编译命令 也可以用该项目自带的编译逻辑
%% "rebar3 compile" | "start compile.bat" | "make"
{compileCmd, undefined},
@ -13,15 +10,16 @@
{log, all},
%% 这个参数用于设置特殊目录下的文件检查编译与加载
%% 格式:{extraDirs, [{strategy(), [srcDirDescr()]}} | {srcDirs, undefined]}
%% -type strategy() :: add | only | del.
%% 如果 strategy() when add, 会无条件监听添加的指定目录及其子目录同步编译与加载.
%% 格式:{extraDirs, [{strategy(), [srcDirDescr()]}] | undefined}
%% -type strategy() :: only | del | addExtra | addOnly.
%% 如果 strategy() when addExtra, 会无条件监听添加的指定目录及其子目录同步编译与加载.
%% 如果 strategy() when addOnly, 会无条件监听添加的指定目录(不含子目录)同步编译与加载.
%% 如果 strategy() when only, 仅仅监听指定目录及其子目录下的文件编译与加载.
%% 如果 strategy() when del, 则不会监听该目录及其子目录下的文件.
%% -type srcDirDescr() :: { Dir :: file:filename(), [Options :: compile_option()]}.
%% 默认值:undefined 根据当前工作目录 和 已经加载的模块做来得出需要扫描的目录
%%示例: {extraDirs, [{add, [{"./_build/default/lib/erlGbh", []}]}, {only, [{"./", []}]}, {del, [{"./_build", []}]}]}.
%%{extraDirs, [{add, [{"./_build/default/lib/erlGbh", []}, {"./_build/default/lib/erlSync/ebin", []}]}, {only, [{"./", []}]}, {del, [{"./_build", []}]}]}
%%示例: {extraDirs, [{addExtra, [{"./_build/default/lib/erlGbh", []}]}, {only, [{"./", []}]}, {del, [{"./_build", []}]}]}.
%%{extraDirs, [{add, [{"./_build/default/lib/erlGbh", []}, {"./_build/default/lib/eSync/ebin", []}]}, {only, [{"./", []}]}, {del, [{"./_build", []}]}]}
{extraDirs, [{strategy(), [srcDirDescr()]}]},
%% 这个参数用来设置 怎么处理 当beam文件的源文件目录不是当前工作的子目录时的情况
@ -30,8 +28,22 @@
%% * allow = 不要做任何特别的事情,使用beam源文件原始路径查找该文件
%% * ignore = 而忽略对其源路径的任何更改
%% 默认值: fix
{descendant, fix}
]}
].
{descendant, fix},
%% Beam更新回调函数 格式: undefined | {Mondule, Fun} | [{Mondule, Fun}, ...], Fun函数只有一个参数
{onMSyncFun, undefined},
%% config更新回调函数 格式: undefined | {Mondule, Fun} | [{Mondule, Fun}, ...], Fun函数只有一个参数
{onCSyncFun, undefined},
%% 是否开启集群同步加载
{swSyncNode, false},
%% 仅仅内存编译还是编译写入到磁盘去
{isJustMem, false},
%% 如果存在debug_info_key 需要用户提供获取debug_info_key的函数 格式: undefined | {Mondule, Fun}
%% this fun need return: {debug_info_key, xxx}
{debugInfoKeyFun, undefined}
]}
].

+ 0
- 23
include/erlSync.hrl View File

@ -1,23 +0,0 @@
-define(LOG_ON(Val), Val == true; Val == all; Val == skip_success; is_list(Val), Val =/= []).
-define(TCP_DEFAULT_OPTIONS, [
binary
, {packet, 4}
, {active, true}
, {reuseaddr, true}
, {nodelay, false}
, {delay_send, true}
, {send_timeout, 15000}
, {keepalive, true}
, {exit_on_close, true}]).
-define(Log, log).
-define(listenPort, listenPort).
-define(compileCmd, compileCmd).
-define(extraDirs, extraDirs).
-define(descendant, descendant).
-define(CfgList, [{?Log, all}, {?listenPort, 12369}, {?compileCmd, undefined}, {?extraDirs, undefined}, {?descendant, fix}]).
-define(esCfgSync, esCfgSync).
-define(rootSrcDir, <<"src">>).
-define(esRecompileCnt, '$esRecompileCnt').

BIN
priv/fileSync View File


BIN
priv/fileSync.exe View File


src/erlSync.app.src → src/eSync.app.src View File

@ -1,11 +1,11 @@
{application, erlSync,
{application, eSync,
[{description, "erlang code auto compile and loader"},
{vsn, "0.1.0"},
{registered, []},
{mod, {erlSync_app, []}},
{mod, {eSync, []}},
{applications, [kernel, stdlib, syntax_tools, compiler]},
{env, []},
{modules, []},
{licenses, ["MIT License"]},
{licenses, ["MIT"]},
{links, []}
]}.

+ 1297
- 0
src/eSync.erl
File diff suppressed because it is too large
View File


+ 0
- 61
src/erlSync.erl View File

@ -1,61 +0,0 @@
-module(erlSync).
-export([
start/0,
stop/0,
run/0,
pause/0,
curInfo/0,
setLog/1,
getLog/0,
getOnsync/0,
setOnsync/0,
setOnsync/1,
swSyncNode/1
]).
start() ->
application:ensure_all_started(erlSync).
stop() ->
application:stop(erlSync).
run() ->
case start() of
{ok, _Started} ->
esSyncSrv:unpause(),
ok;
{error, Reason} ->
Msg = io_lib:format("start erlSync error:~p~n", [Reason]),
esUtils:logErrors(Msg)
end.
pause() ->
esSyncSrv:pause().
swSyncNode(IsSync) ->
run(),
esSyncSrv:swSyncNode(IsSync),
ok.
curInfo() ->
esSyncSrv:curInfo().
setLog(Val) ->
esSyncSrv:setLog(Val).
getLog() ->
esSyncSrv:getLog().
getOnsync() ->
esSyncSrv:getOnsync().
setOnsync() ->
esSyncSrv:setOnsync(undefined).
setOnsync(Fun) ->
esSyncSrv:setOnsync(Fun).

+ 0
- 10
src/erlSync_app.erl View File

@ -1,10 +0,0 @@
-module(erlSync_app).
-behaviour(application).
-export([start/2, stop/1]).
start(_StartType, _StartArgs) ->
erlSync_sup:start_link().
stop(_State) ->
ok.

+ 0
- 27
src/erlSync_sup.erl View File

@ -1,27 +0,0 @@
-module(erlSync_sup).
-behaviour(supervisor).
-export([
start_link/0
, init/1
]).
-define(SERVER, ?MODULE).
-define(ChildSpec(I, Type), #{id => I, start => {I, start_link, []}, restart => permanent, shutdown => 5000, type => Type, modules => [I]}).
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
%% sup_flags() = #{strategy => strategy(), % optional
%% intensity => non_neg_integer(), % optional
%% period => pos_integer()} % optional
%% child_spec() = #{id => child_id(), % mandatory
%% start => mfargs(), % mandatory
%% restart => restart(), % optional
%% shutdown => shutdown(), % optional
%% type => worker(), % optional
%% modules => modules()} % optional
init([]) ->
SupFlags = #{strategy => one_for_one, intensity => 5, period => 10},
ChildSpecs = [?ChildSpec(esSyncSrv, worker)],
{ok, {SupFlags, ChildSpecs}}.

+ 0
- 234
src/sync/esSyncSrv.erl View File

@ -1,234 +0,0 @@
-module(esSyncSrv).
-behaviour(gen_server).
-include("erlSync.hrl").
-compile(inline).
-compile({inline_size, 128}).
%% API
-export([
start_link/0,
rescan/0,
pause/0,
unpause/0,
setLog/1,
getLog/0,
curInfo/0,
getOnsync/0,
setOnsync/1,
swSyncNode/1
]).
%% gen_server callbacks
-export([
init/1,
handle_call/3,
handle_cast/2,
handle_info/2,
terminate/2
]).
-define(SERVER, ?MODULE).
-record(state, {
status = running :: running | pause
, srcFiles = #{} :: map()
, onsyncFun = undefined
, swSyncNode = false
, sockMod = undefined
, sock = undefined
}).
%% ************************************ API start ***************************
rescan() ->
gen_server:cast(?SERVER, miRescan),
esUtils:logSuccess("start rescaning source files..."),
ok.
unpause() ->
gen_server:cast(?SERVER, miUnpause),
ok.
pause() ->
gen_server:cast(?SERVER, miPause),
esUtils:logSuccess("Pausing erlSync. Call erlSync:run() to restart"),
ok.
curInfo() ->
gen_server:call(?SERVER, miCurInfo).
setLog(T) when ?LOG_ON(T) ->
esUtils:setEnv(log, T),
esUtils:loadCfg(),
esUtils:logSuccess("Console Notifications Enabled"),
ok;
setLog(_) ->
esUtils:setEnv(log, none),
esUtils:loadCfg(),
esUtils:logSuccess("Console Notifications Disabled"),
ok.
getLog() ->
?esCfgSync:getv(log).
swSyncNode(IsSync) ->
gen_server:cast(?SERVER, {miSyncNode, IsSync}),
ok.
getOnsync() ->
gen_server:call(?SERVER, miGetOnsync).
setOnsync(Fun) ->
gen_server:call(?SERVER, {miSetOnsync, Fun}).
%% ************************************ API end ***************************
start_link() ->
gen_server:start_link({local, ?SERVER}, ?MODULE, [], []).
init([]) ->
erlang:process_flag(trap_exit, true),
esUtils:loadCfg(),
erlang:send_after(0, self(), doAfter),
{ok, #state{}}.
handle_call(miGetOnsync, _, #state{onsyncFun = OnSync} = State) ->
{reply, OnSync, State};
handle_call({miSetOnsync, Fun}, _, State) ->
{reply, ok, State#state{onsyncFun = Fun}};
handle_call(miCurInfo, _, State) ->
{reply, {erlang:get(), State}, State};
handle_call(_Request, _, State) ->
{reply, ok, State}.
handle_cast(miPause, State) ->
{noreply, State#state{status = pause}};
handle_cast(miUnpause, State) ->
{noreply, State#state{status = running}};
handle_cast({miSyncNode, IsSync}, State) ->
case IsSync of
true ->
{noreply, State#state{swSyncNode = true}};
_ ->
{noreply, State#state{swSyncNode = false}}
end;
handle_cast(miRescan, State) ->
SrcFiles = esUtils:collSrcFiles(false),
{noreply, State#state{srcFiles = SrcFiles}};
handle_cast(_Msg, State) ->
esUtils:logSuccess("recv unexpect cast msg..."),
{noreply, State}.
handle_info({tcp, _Socket, Data}, #state{status = running, srcFiles = SrcFiles, onsyncFun = OnsyncFun, swSyncNode = SwSyncNode} = State) ->
FileList = binary:split(Data, <<"\r\n">>, [global]),
%% beam hrl src
{Beams, Hrls, Srcs} = esUtils:classifyChangeFile(FileList, [], [], []),
esUtils:reloadChangedMod(Beams, SwSyncNode, OnsyncFun, []),
case ?esCfgSync:getv(?compileCmd) of
undefined ->
esUtils:recompileChangeHrlFile(Hrls, SrcFiles, SwSyncNode),
esUtils:recompileChangeSrcFile(Srcs, SwSyncNode),
NewSrcFiles = esUtils:addNewFile(Srcs, SrcFiles),
{noreply, State#state{srcFiles = NewSrcFiles}};
CmdStr ->
case Srcs =/= [] orelse Hrls =/= [] of
true ->
RetStr = os:cmd(CmdStr),
RetList = string:split(RetStr, "\n", all),
CmdMsg = io_lib:format("compile cmd:~p ~n", [CmdStr]),
esUtils:logSuccess(CmdMsg),
RetMsg = io_lib:format("the result: ~n ", []),
esUtils:logSuccess(RetMsg),
[
begin
OneMsg = io_lib:format("~p ~n", [OneRet]),
esUtils:logSuccess(OneMsg)
end || OneRet <- RetList, OneRet =/= []
],
ok;
_ ->
ignore
end,
{noreply, State}
end;
handle_info({inet_async, LSock, _Ref, Msg}, #state{sockMod = SockMod} = State) ->
case Msg of
{ok, Sock} ->
%% make it look like gen_tcp:accept
inet_db:register_socket(Sock, SockMod),
inet:setopts(Sock, [{active, true}]),
prim_inet:async_accept(LSock, -1),
%%
{AddSrcDirs, OnlySrcDirs, DelSrcDirs} = esUtils:mergeExtraDirs(false),
AddStr = string:join([filename:nativename(OneDir) || OneDir <- AddSrcDirs], "|"),
OnlyStr = string:join([filename:nativename(OneDir) || OneDir <- OnlySrcDirs], "|"),
DelStr = string:join([filename:nativename(OneDir) || OneDir <- DelSrcDirs], "|"),
AllStr = string:join([AddStr, OnlyStr, DelStr], "\r\n"),
gen_tcp:send(Sock, AllStr),
esUtils:logSuccess("erlSync connect fileSync success... ~n"),
case ?esCfgSync:getv(?compileCmd) of
undefined ->
%% src文件
SrcFiles = esUtils:collSrcFiles(true),
{noreply, State#state{status = running, sock = Sock, srcFiles = SrcFiles}};
_ ->
{noreply, State#state{status = running}}
end;
{error, closed} ->
Msg = io_lib:format("error, closed listen sock error ~p~n", [closed]),
esUtils:logErrors(Msg),
{stop, normal, State};
{error, Reason} ->
Msg = io_lib:format("listen sock error ~p~n", [Reason]),
esUtils:logErrors(Msg),
{stop, Reason, State}
end;
handle_info({tcp_closed, _Socket}, _State) ->
Msg = io_lib:format("esSyncSrv receive tcp_closed ~n", []),
esUtils:logErrors(Msg),
{noreply, _State};
handle_info({tcp_error, _Socket, Reason}, _State) ->
Msg = io_lib:format("esSyncSrv receive tcp_error Reason:~p ~n", [Reason]),
esUtils:logErrors(Msg),
{noreply, _State};
handle_info(doAfter, State) ->
%% tcp
ListenPort = ?esCfgSync:getv(?listenPort),
case gen_tcp:listen(ListenPort, ?TCP_DEFAULT_OPTIONS) of
{ok, LSock} ->
case prim_inet:async_accept(LSock, -1) of
{ok, _Ref} ->
{ok, SockMod} = inet_db:lookup_socket(LSock),
spawn(fun() ->
case os:type() of
{win32, _Osname} ->
CmtStr = "start " ++ esUtils:fileSyncPath("fileSync.exe") ++ " ./ " ++ integer_to_list(ListenPort),
os:cmd(CmtStr);
_ ->
CmtStr = esUtils:fileSyncPath("fileSync") ++ " ./ " ++ integer_to_list(ListenPort),
os:cmd(CmtStr)
end end),
erlang:send_after(4000, self(), waitConnOver),
{noreply, State#state{sockMod = SockMod}};
{error, Reason} ->
Msg = io_lib:format("init prim_inet:async_accept error ~p~n", [Reason]),
esUtils:logErrors(Msg),
{stop, waitConnOver, State}
end;
{error, Reason} ->
Msg = io_lib:format("failed to listen on ~p - ~p (~s) ~n", [ListenPort, Reason, inet:format_error(Reason)]),
esUtils:logErrors(Msg),
{stop, waitConnOver, State}
end;
handle_info(waitConnOver, #state{sock = undefined} = State) ->
Msg = io_lib:format("failed to start fileSync~n", []),
esUtils:logErrors(Msg),
{stop, waitConnOver, State};
handle_info(_Msg, State) ->
{noreply, State}.
terminate(_Reason, _State) ->
ok.

+ 0
- 886
src/sync/esUtils.erl View File

@ -1,886 +0,0 @@
-module(esUtils).
-include("erlSync.hrl").
-compile(inline).
-compile({inline_size, 128}).
-compile([export_all, nowarn_export_all]).
getModSrcDir(Module) ->
case code:is_loaded(Module) of
{file, _} ->
try
%% Get some module info...
Props = Module:module_info(compile),
Source = proplists:get_value(source, Props, ""),
%% Ensure that the file exists, is a decendent of the tree, and how to deal with that
IsFile = filelib:is_regular(Source),
IsDescendant = isDescendent(Source),
Descendant = ?esCfgSync:getv(?descendant),
LastSource =
case {IsFile, IsDescendant, Descendant} of
%% is file and descendant, we're good to go
{true, true, _} -> Source;
%% is not a descendant, but we allow them, so good to go
{true, false, allow} -> Source;
%% is not a descendant, and we fix non-descendants, so let's fix it
{_, false, fix} -> fixDescendantSource(Source, IsFile);
%% Anything else, and we don't know what to do, so let's just bail.
_ -> undefined
end,
case LastSource of
undefined ->
undefined;
_ ->
%% Get the source dir...
Dir = filename:dirname(LastSource),
getSrcDir(Dir)
end
catch _ : _ ->
undefined
end;
_ ->
undefined
end.
getModOptions(Module) ->
case code:is_loaded(Module) of
{file, _} ->
try
Props = Module:module_info(compile),
BeamDir = filename:dirname(code:which(Module)),
Options1 = proplists:get_value(options, Props, []),
%% transform `outdir'
Options2 = transformOutdir(BeamDir, Options1),
Options3 = ensureInclude(Options2),
%% transform the include directories
Options4 = transformAllIncludes(Module, BeamDir, Options3),
%% maybe_add_compile_info
Options5 = maybeAddCompileInfo(Options4),
%% add filetype to options (DTL, LFE, erl, etc)
Options6 = addFileType(Module, Options5),
{ok, Options6}
catch ExType:Error ->
Msg = [io_lib:format("~p:0: ~p looking for options: ~p. ~n", [Module, ExType, Error])],
logWarnings(Msg),
undefined
end;
_ ->
undefined
end.
tryGetModOptions(Module) ->
try
Props = Module:module_info(compile),
BeamDir = filename:dirname(code:which(Module)),
Options1 = proplists:get_value(options, Props, []),
%% transform `outdir'
Options2 = transformOutdir(BeamDir, Options1),
Options3 = ensureInclude(Options2),
%% transform the include directories
Options4 = transformAllIncludes(Module, BeamDir, Options3),
%% maybe_add_compile_info
Options5 = maybeAddCompileInfo(Options4),
%% add filetype to options (DTL, LFE, erl, etc)
Options6 = addFileType(Module, Options5),
{ok, Options6}
catch _ExType:_Error ->
undefiend
end.
tryGetSrcOptions(SrcDir) ->
%% Then we dig back through the parent directories until we find our include directory
NewDirName = filename:dirname(SrcDir),
case getOptions(NewDirName) of
{ok, _Options} = Opts ->
Opts;
_ ->
BaseName = filename:basename(SrcDir),
IsBaseSrcDir = BaseName == ?rootSrcDir,
case NewDirName =/= SrcDir andalso not IsBaseSrcDir of
true ->
tryGetSrcOptions(NewDirName);
_ when IsBaseSrcDir ->
try filelib:fold_files(SrcDir, ".*\\.(erl|dtl|lfe|ex)$", true,
fun(OneFiles, Acc) ->
Mod = erlang:binary_to_atom(filename:basename(OneFiles, filename:extension(OneFiles)), utf8),
case tryGetModOptions(Mod) of
{ok, _Options} = Opts ->
throw(Opts);
_ ->
Acc
end
end, undefined)
catch
{ok, _Options} = Opts ->
Opts;
_ExType:_Error ->
Msg = [io_lib:format("looking src options error ~p:~p. ~n", [_ExType, _Error])],
logWarnings(Msg),
undefined
end;
_ ->
undefined
end
end.
transformOutdir(BeamDir, Options) ->
[{outdir, BeamDir} | proplists:delete(outdir, Options)].
ensureInclude(Options) ->
case proplists:get_value(i, Options) of
undefined -> [{i, "include"} | Options];
_ -> Options
end.
transformAllIncludes(Module, BeamDir, Options) ->
[begin
case Opt of
{i, IncludeDir} ->
{ok, SrcDir} = getModSrcDir(Module),
{ok, IncludeDir2} = determineIncludeDir(IncludeDir, BeamDir, SrcDir),
{i, IncludeDir2};
_ ->
Opt
end
end || Opt <- Options].
maybeAddCompileInfo(Options) ->
case lists:member(compile_info, Options) of
true -> Options;
false -> addCompileInfo(Options)
end.
addCompileInfo(Options) ->
CompInfo = [{K, V} || {K, V} <- Options, lists:member(K, [outdir, i])],
[{compile_info, CompInfo} | Options].
addFileType(Module, Options) ->
Type = getFileType(Module),
[{type, Type} | Options].
%% This will check if the given module or source file is an ErlyDTL template.
%% Currently, this is done by checking if its reported source path ends with
%% ".dtl.erl".
getFileType(Module) when is_atom(Module) ->
Props = Module:module_info(compile),
Source = proplists:get_value(source, Props, ""),
getFileType(Source);
getFileType(Source) ->
Ext = filename:extension(Source),
Root = filename:rootname(Source),
SecondExt = filename:extension(Root),
case Ext of
<<".erl">> when SecondExt =:= <<".dtl">> -> dtl;
<<".dtl">> -> dtl;
<<".erl">> -> erl;
<<".lfe">> -> lfe;
<<".ex">> -> elixir;
".erl" when SecondExt =:= ".dtl" -> dtl;
".dtl" -> dtl;
".erl" -> erl;
".lfe" -> lfe;
".ex" -> elixir
end.
%% This will search back to find an appropriate include directory, by
%% searching further back than "..". Instead, it will extract the basename
%% (probably "include" from the include pathfile, and then search backwards in
%% the directory tree until it finds a directory with the same basename found
%% above.
determineIncludeDir(IncludeDir, BeamDir, SrcDir) ->
IncludeBase = filename:basename(IncludeDir),
case determineIncludeDirFromBeamDir(IncludeBase, IncludeDir, BeamDir) of
{ok, _Dir} = RetD -> RetD;
undefined ->
{ok, Cwd} = file:get_cwd(),
% Cwd2 = normalizeCaseWindowsDir(Cwd),
% SrcDir2 = normalizeCaseWindowsDir(SrcDir),
% IncludeBase2 = normalizeCaseWindowsDir(IncludeBase),
case findIncludeDirFromAncestors(SrcDir, Cwd, IncludeBase) of
{ok, _Dir} = RetD -> RetD;
undefined -> {ok, IncludeDir} %% Failed, just stick with original
end
end.
%% First try to see if we have an include file alongside our ebin directory, which is typically the case
determineIncludeDirFromBeamDir(IncludeBase, IncludeDir, BeamDir) ->
BeamBasedIncDir = filename:join(filename:dirname(BeamDir), IncludeBase),
case filelib:is_dir(BeamBasedIncDir) of
true -> {ok, BeamBasedIncDir};
false ->
BeamBasedIncDir2 = filename:join(filename:dirname(BeamDir), IncludeDir),
case filelib:is_dir(BeamBasedIncDir2) of
true -> {ok, BeamBasedIncDir2};
_ ->
undefined
end
end.
%% get the src dir
getRootSrcDirFromSrcDir(SrcDir) ->
NewDirName = filename:dirname(SrcDir),
BaseName = filename:basename(SrcDir),
case BaseName of
?rootSrcDir ->
NewDirName;
_ ->
case NewDirName =/= SrcDir of
true ->
getRootSrcDirFromSrcDir(NewDirName);
_ ->
undefined
end
end.
%% Then we dig back through the parent directories until we find our include directory
findIncludeDirFromAncestors(Cwd, Cwd, _) -> undefined;
findIncludeDirFromAncestors("/", _, _) -> undefined;
findIncludeDirFromAncestors(".", _, _) -> undefined;
findIncludeDirFromAncestors("", _, _) -> undefined;
findIncludeDirFromAncestors(Dir, Cwd, IncludeBase) ->
NewDirName = filename:dirname(Dir),
AttemptDir = filename:join(NewDirName, IncludeBase),
case filelib:is_dir(AttemptDir) of
true ->
{ok, AttemptDir};
false ->
case NewDirName =/= Dir of
true ->
findIncludeDirFromAncestors(NewDirName, Cwd, IncludeBase);
_ ->
undefined
end
end.
% normalizeCaseWindowsDir(Dir) ->
% case os:type() of
% {win32, _} -> Dir; %string:to_lower(Dir);
% {unix, _} -> Dir
% end.
%% This is an attempt to intelligently fix paths in modules when a
%% release is moved. Essentially, it takes a module name and its original path
%% from Module:module_info(compile), say
%% "/some/original/path/site/src/pages/somepage.erl", and then breaks down the
%% path one by one prefixing it with the current working directory until it
%% either finds a match, or fails. If it succeeds, it returns the Path to the
%% new Source file.
fixDescendantSource([], _IsFile) ->
undefined;
fixDescendantSource(Path, IsFile) ->
{ok, Cwd} = file:get_cwd(),
PathParts = filename:split(Path),
case makeDescendantSource(PathParts, Cwd) of
undefined -> case IsFile of true -> Path; _ -> undefined end;
FoundPath -> FoundPath
end.
makeDescendantSource([], _Cwd) ->
undefined;
makeDescendantSource([_ | T], Cwd) ->
PathAttempt = filename:join([Cwd | T]),
case filelib:is_regular(PathAttempt) of
true -> PathAttempt;
false -> makeDescendantSource(T, Cwd)
end.
isDescendent(Path) ->
{ok, Cwd} = file:get_cwd(),
lists:sublist(Path, length(Cwd)) == Cwd.
%% @private Find the src directory for the specified Directory; max 15 iterations
getSrcDir(Dir) ->
getSrcDir(Dir, 15).
getSrcDir(_Dir, 0) ->
undefined;
getSrcDir(Dir, Ctr) ->
HasCode = filelib:wildcard("*.erl", Dir) /= [] orelse
filelib:wildcard("*.hrl", Dir) /= [] orelse
filelib:wildcard("*.dtl", Dir) /= [] orelse
filelib:wildcard("*.lfe", Dir) /= [] orelse
filelib:wildcard("*.ex", Dir) /= [],
if
HasCode -> {ok, Dir};
true -> getSrcDir(filename:dirname(Dir), Ctr - 1)
end.
mergeExtraDirs(IsAddPath) ->
case ?esCfgSync:getv(?extraDirs) of
undefined ->
{[], [], []};
ExtraList ->
FunMerge =
fun(OneExtra, {AddDirs, OnlyDirs, DelDirs} = AllAcc) ->
case OneExtra of
{add, DirsAndOpts} ->
Adds =
[
begin
case IsAddPath of
true ->
case proplists:get_value(outdir, Opts) of
undefined ->
true;
Path ->
ok = filelib:ensure_dir(Path),
true = code:add_pathz(Path)
end;
_ ->
ignore
end,
filename:absname(Dir)
end || {Dir, Opts} <- DirsAndOpts
],
setelement(1, AllAcc, Adds ++ AddDirs);
{only, DirsAndOpts} ->
Onlys =
[
begin
case IsAddPath of
true ->
case proplists:get_value(outdir, Opts) of
undefined ->
true;
Path ->
ok = filelib:ensure_dir(Path),
true = code:add_pathz(Path)
end;
_ ->
ignore
end,
filename:absname(Dir)
end || {Dir, Opts} <- DirsAndOpts
],
setelement(2, AllAcc, Onlys ++ OnlyDirs);
{del, DirsAndOpts} ->
Dels =
[
begin
filename:absname(Dir)
end || {Dir, _Opts} <- DirsAndOpts
],
setelement(3, AllAcc, Dels ++ DelDirs)
end
end,
lists:foldl(FunMerge, {[], [], []}, ExtraList)
end.
collSrcFiles(IsAddPath) ->
{AddSrcDirs, OnlySrcDirs, DelSrcDirs} = mergeExtraDirs(IsAddPath),
CollFiles = filelib:fold_files(filename:absname(<<"./">>), ".*\\.(erl|dtl|lfe|ex)$", true,
fun(OneFiles, Acc) ->
case isOnlyDir(OnlySrcDirs, OneFiles) of
true ->
case isDelDir(DelSrcDirs, OneFiles) of
false ->
RootSrcDir =
case getRootSrcDirFromSrcDir(OneFiles) of
undefined ->
filename:dirname(OneFiles);
RetSrcDir ->
RetSrcDir
end,
case getOptions(RootSrcDir) of
undefined ->
Mod = erlang:binary_to_atom(filename:basename(OneFiles, filename:extension(OneFiles)), utf8),
case getModOptions(Mod) of
{ok, Options} ->
setOptions(RootSrcDir, Options);
_ ->
ignore
end;
_ ->
ignore
end,
Acc#{OneFiles => 1};
_ ->
Acc
end;
_ ->
Acc
end
end, #{}),
FunCollAdds =
fun(OneDir, FilesAcc) ->
filelib:fold_files(case is_list(OneDir) of true -> list_to_binary(OneDir); _ ->
OneDir end, ".*\\.(erl|dtl|lfe|ex)$", true, fun(OneFiles, Acc) ->
Acc#{OneFiles => 1} end, FilesAcc)
end,
lists:foldl(FunCollAdds, CollFiles, AddSrcDirs).
isOnlyDir([], _) ->
true;
isOnlyDir(ReplaceDirs, SrcDir) ->
isMatchDir(ReplaceDirs, SrcDir).
isDelDir([], _) ->
false;
isDelDir(ReplaceDirs, SrcDir) ->
isMatchDir(ReplaceDirs, SrcDir).
isMatchDir([], _SrcDir) ->
false;
isMatchDir([SrcDir | _ReplaceDirs], SrcDir) ->
true;
isMatchDir([OneDir | ReplaceDirs], SrcDir) ->
case re:run(SrcDir, OneDir) of
nomatch -> isMatchDir(ReplaceDirs, SrcDir);
_ -> true
end.
getEnv(Var, Default) ->
case application:get_env(erlSync, Var) of
{ok, Value} ->
Value;
_ ->
Default
end.
setEnv(Var, Val) ->
ok = application:set_env(erlSync, Var, Val).
logSuccess(Message) ->
canLog(success) andalso error_logger:info_msg(lists:flatten(Message)).
logErrors(Message) ->
canLog(errors) andalso error_logger:error_msg(lists:flatten(Message)).
logWarnings(Message) ->
canLog(warnings) andalso error_logger:warning_msg(lists:flatten(Message)).
canLog(MsgType) ->
case esSyncSrv:getLog() of
true -> true;
all -> true;
none -> false;
false -> false;
skip_success -> MsgType == errors orelse MsgType == warnings;
L when is_list(L) -> lists:member(MsgType, L);
_ -> false
end.
%% map类型的数据不能当做key
-type key() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple().
-type value() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple() | map().
-spec load(term(), [{key(), value()}]) -> ok.
load(Module, KVs) ->
Forms = forms(Module, KVs),
{ok, Module, Bin} = compile:forms(Forms),
code:soft_purge(Module),
{module, Module} = code:load_binary(Module, atom_to_list(Module), Bin),
ok.
forms(Module, KVs) ->
%% -module(Module).
Mod = erl_syntax:attribute(erl_syntax:atom(module), [erl_syntax:atom(Module)]),
%% -export([getv/0]).
ExportList = [erl_syntax:arity_qualifier(erl_syntax:atom(getv), erl_syntax:integer(1))],
Export = erl_syntax:attribute(erl_syntax:atom(export), [erl_syntax:list(ExportList)]),
%% getv(K) -> V
Function = erl_syntax:function(erl_syntax:atom(getv), lookup_clauses(KVs, [])),
[erl_syntax:revert(X) || X <- [Mod, Export, Function]].
lookup_clause(Key, Value) ->
Var = erl_syntax:abstract(Key),
Body = erl_syntax:abstract(Value),
erl_syntax:clause([Var], [], [Body]).
lookup_clause_anon() ->
Var = erl_syntax:variable("_"),
Body = erl_syntax:atom(undefined),
erl_syntax:clause([Var], [], [Body]).
lookup_clauses([], Acc) ->
lists:reverse(lists:flatten([lookup_clause_anon() | Acc]));
lookup_clauses([{Key, Value} | T], Acc) ->
lookup_clauses(T, [lookup_clause(Key, Value) | Acc]).
getOptions(SrcDir) ->
case erlang:get(SrcDir) of
undefined ->
undefined;
Options ->
{ok, Options}
end.
setOptions(SrcDir, Options) ->
case erlang:get(SrcDir) of
undefined ->
erlang:put(SrcDir, Options);
OldOptions ->
NewOptions =
case lists:keytake(compile_info, 1, Options) of
{value, {compile_info, ValList1}, Options1} ->
case lists:keytake(compile_info, 1, OldOptions) of
{value, {compile_info, ValList2}, Options2} ->
[{compile_info, lists:usort(ValList1 ++ ValList2)} | lists:usort(Options1 ++ Options2)];
_ ->
lists:usort(Options ++ OldOptions)
end;
_ ->
lists:usort(Options ++ OldOptions)
end,
erlang:put(SrcDir, NewOptions)
end.
loadCfg() ->
KVs = [{Key, esUtils:getEnv(Key, DefVal)} || {Key, DefVal} <- ?CfgList],
esUtils:load(?esCfgSync, KVs).
%% ******************************* **********************************************************************
errorNoFile(Module) ->
Msg = io_lib:format("~p Couldn't load module: nofile ~n", [Module]),
esUtils:logWarnings([Msg]).
printResults(_Module, SrcFile, [], []) ->
Msg = io_lib:format("~s Recompiled ~n", [SrcFile]),
esUtils:logSuccess(lists:flatten(Msg));
printResults(_Module, SrcFile, [], Warnings) ->
formatErrors(fun esUtils:logWarnings/1, SrcFile, [], Warnings), io_lib:format("~s Recompiled with ~p warnings~n", [SrcFile, length(Warnings)]);
printResults(_Module, SrcFile, Errors, Warnings) ->
formatErrors(fun esUtils:logErrors/1, SrcFile, Errors, Warnings).
%% @private Print error messages in a pretty and user readable way.
formatErrors(LogFun, File, Errors, Warnings) ->
AllErrors1 = lists:sort(lists:flatten([X || {_, X} <- Errors])),
AllErrors2 = [{Line, "Error", Module, Description} || {Line, Module, Description} <- AllErrors1],
AllWarnings1 = lists:sort(lists:flatten([X || {_, X} <- Warnings])),
AllWarnings2 = [{Line, "Warning", Module, Description} || {Line, Module, Description} <- AllWarnings1],
Everything = lists:sort(AllErrors2 ++ AllWarnings2),
FPck =
fun({Line, Prefix, Module, ErrorDescription}) ->
Msg = formatError(Module, ErrorDescription),
LogMsg = io_lib:format("~s:~p: ~s: ~s~n", [File, Line, Prefix, Msg]),
LogFun(LogMsg)
end,
[FPck(X) || X <- Everything],
ok.
formatError(Module, ErrorDescription) ->
case erlang:function_exported(Module, format_error, 1) of
true -> Module:format_error(ErrorDescription);
false -> io_lib:format("~s ~n", [ErrorDescription])
end.
fireOnsync(OnsyncFun, Modules) ->
case OnsyncFun of
undefined -> ok;
Funs when is_list(Funs) -> onsyncApplyList(Funs, Modules);
Fun -> onsyncApply(Fun, Modules)
end.
onsyncApplyList(Funs, Modules) ->
[onsyncApply(Fun, Modules) || Fun <- Funs].
onsyncApply({M, F}, Modules) ->
erlang:apply(M, F, [Modules]);
onsyncApply(Fun, Modules) when is_function(Fun) ->
Fun(Modules).
reloadChangedMod([], _SwSyncNode, OnsyncFun, Acc) ->
fireOnsync(OnsyncFun, Acc);
reloadChangedMod([Module | LeftMod], SwSyncNode, OnsyncFun, Acc) ->
case code:get_object_code(Module) of
error ->
Msg = io_lib:format("Error loading object code for ~p~n", [Module]),
esUtils:logErrors(Msg),
reloadChangedMod(LeftMod, SwSyncNode, OnsyncFun, Acc);
{Module, Binary, Filename} ->
case code:load_binary(Module, Filename, Binary) of
{module, Module} ->
Msg = io_lib:format("Reloaded(Beam changed) Mod:~s Success~n", [Module]),
esUtils:logSuccess(Msg);
{error, What} ->
Msg = io_lib:format("Reloaded(Beam changed) Mod:~s Errors Reason:~p~n", [Module, What]),
esUtils:logErrors(Msg)
end,
case SwSyncNode of
true ->
{ok, NumNodes, Nodes} = syncLoadModOnAllNodes(Module),
MsgNodes = io_lib:format("Reloaded(Beam changed) Mod:~s on ~p nodes:~p~n", [Module, NumNodes, Nodes]),
esUtils:logSuccess(MsgNodes);
false ->
ignore
end,
reloadChangedMod(LeftMod, SwSyncNode, OnsyncFun, [Module | Acc])
end.
getNodes() ->
lists:usort(lists:flatten(nodes() ++ [rpc:call(X, erlang, nodes, []) || X <- nodes()])) -- [node()].
syncLoadModOnAllNodes(Module) ->
%% Get a list of nodes known by this node, plus all attached nodes.
Nodes = getNodes(),
NumNodes = length(Nodes),
{Module, Binary, _} = code:get_object_code(Module),
FSync =
fun(Node) ->
MsgNode = io_lib:format("Reloading '~s' on ~p~n", [Module, Node]),
esUtils:logSuccess(MsgNode),
rpc:call(Node, code, ensure_loaded, [Module]),
case rpc:call(Node, code, which, [Module]) of
Filename when is_binary(Filename) orelse is_list(Filename) ->
%% File exists, overwrite and load into VM.
ok = rpc:call(Node, file, write_file, [Filename, Binary]),
rpc:call(Node, code, purge, [Module]),
case rpc:call(Node, code, load_file, [Module]) of
{module, Module} ->
Msg = io_lib:format("Reloaded(Beam changed) Mod:~s and write Success on node:~p~n", [Module, Node]),
esUtils:logSuccess(Msg);
{error, What} ->
Msg = io_lib:format("Reloaded(Beam changed) Mod:~s and write Errors on node:~p Reason:~p~n", [Module, Node, What]),
esUtils:logErrors(Msg)
end;
_ ->
%% File doesn't exist, just load into VM.
case rpc:call(Node, code, load_binary, [Module, undefined, Binary]) of
{module, Module} ->
Msg = io_lib:format("Reloaded(Beam changed) Mod:~s Success on node:~p~n", [Module, Node]),
esUtils:logSuccess(Msg);
{error, What} ->
Msg = io_lib:format("Reloaded(Beam changed) Mod:~s Errors on node:~p Reason:~p~n", [Module, Node, What]),
esUtils:logErrors(Msg)
end
end
end,
[FSync(X) || X <- Nodes],
{ok, NumNodes, Nodes}.
recompileChangeSrcFile([], _SwSyncNode) ->
ok;
recompileChangeSrcFile([File | LeftFile], SwSyncNode) ->
recompileSrcFile(File, SwSyncNode),
recompileChangeSrcFile(LeftFile, SwSyncNode).
erlydtlCompile(SrcFile, Options) ->
F =
fun({outdir, OutDir}, Acc) -> [{out_dir, OutDir} | Acc];
(OtherOption, Acc) -> [OtherOption | Acc]
end,
DtlOptions = lists:foldl(F, [], Options),
Module = erlang:binary_to_atom(lists:flatten(filename:basename(SrcFile, ".dtl") ++ "_dtl"), utf8),
Compiler = erlydtl,
Compiler:compile(SrcFile, Module, DtlOptions).
elixir_compile(SrcFile, Options) ->
Outdir = proplists:get_value(outdir, Options),
Compiler = ':Elixir.Kernel.ParallelCompiler',
Modules = Compiler:files_to_path([SrcFile], Outdir),
Loader =
fun(Module) ->
Outfile = code:which(Module),
Binary = file:read_file(Outfile),
{Module, Binary}
end,
Results = lists:map(Loader, Modules),
{ok, multiple, Results, []}.
lfe_compile(SrcFile, Options) ->
Compiler = lfe_comp,
Compiler:file(SrcFile, Options).
getCompileFunAndModuleName(SrcFile) ->
case esUtils:getFileType(SrcFile) of
erl ->
{fun compile:file/2, erlang:binary_to_atom(filename:basename(SrcFile, <<".erl">>), utf8)};
dtl ->
{fun erlydtlCompile/2, list_to_atom(lists:flatten(binary_to_list(filename:basename(SrcFile, <<".dtl">>)) ++ "_dtl"))};
lfe ->
{fun lfe_compile/2, erlang:binary_to_atom(filename:basename(SrcFile, <<".lfe">>), utf8)};
elixir ->
{fun elixir_compile/2, erlang:binary_to_atom(filename:basename(SrcFile, <<".ex">>), utf8)}
end.
getObjectCode(Module) ->
case code:get_object_code(Module) of
{Module, B, Filename} -> {B, Filename};
_ -> {undefined, undefined}
end.
reloadIfNecessary(Module, OldBinary, Binary, Filename) ->
case Binary =/= OldBinary of
true ->
%% Try to load the module...
case code:ensure_loaded(Module) of
{module, Module} ->
case code:load_binary(Module, Filename, Binary) of
{module, Module} ->
Msg = io_lib:format("Reloaded(Beam changed) Mod:~s Success~n", [Module]),
esUtils:logSuccess(Msg);
{error, What} ->
Msg = io_lib:format("Reloaded(Beam changed) Mod:~s Errors Reason:~p~n", [Module, What]),
esUtils:logErrors(Msg)
end;
{error, nofile} -> errorNoFile(Module);
{error, embedded} ->
case code:load_file(Module) of %% Module is not yet loaded, load it.
{module, Module} -> ok;
{error, nofile} -> errorNoFile(Module)
end
end;
_ ->
ignore
end.
recompileSrcFile(SrcFile, SwSyncNode) ->
%% Get the module, src dir, and options...
RootSrcDir =
case getRootSrcDirFromSrcDir(SrcFile) of
undefined ->
filename:dirname(SrcFile);
RetSrcDir ->
RetSrcDir
end,
CurSrcDir = filename:dirname(SrcFile),
{CompileFun, Module} = getCompileFunAndModuleName(SrcFile),
{OldBinary, Filename} = getObjectCode(Module),
case getOptions(RootSrcDir) of
{ok, Options} ->
RightFileDir = binary_to_list(filename:join(CurSrcDir, filename:basename(SrcFile))),
case CompileFun(RightFileDir, [binary, return | Options]) of
{ok, Module, Binary, Warnings} ->
printResults(Module, RightFileDir, [], Warnings),
reloadIfNecessary(Module, OldBinary, Binary, Filename),
{ok, [], Warnings};
{ok, [{ok, Module, Binary, Warnings}], Warnings2} ->
printResults(Module, RightFileDir, [], Warnings ++ Warnings2),
reloadIfNecessary(Module, OldBinary, Binary, Filename),
{ok, [], Warnings ++ Warnings2};
{ok, multiple, Results, Warnings} ->
printResults(Module, RightFileDir, [], Warnings),
[reloadIfNecessary(CompiledModule, OldBinary, Binary, Filename) || {CompiledModule, Binary} <- Results],
{ok, [], Warnings};
{ok, OtherModule, _Binary, Warnings} ->
Desc = io_lib:format("Module definition (~p) differs from expected (~s) ~n", [OtherModule, filename:rootname(filename:basename(RightFileDir))]),
Errors = [{RightFileDir, {0, Module, Desc}}],
printResults(Module, RightFileDir, Errors, Warnings),
{ok, Errors, Warnings};
{error, Errors, Warnings} ->
printResults(Module, RightFileDir, Errors, Warnings),
{ok, Errors, Warnings};
_Err ->
Msg = io_lib:format("compile Mod:~s Errors Reason:~p ~n", [Module, _Err]),
esUtils:logErrors(Msg)
end;
undefined ->
case esUtils:tryGetModOptions(Module) of
{ok, Options} ->
setOptions(RootSrcDir, Options),
recompileSrcFile(SrcFile, SwSyncNode);
_ ->
case esUtils:tryGetSrcOptions(CurSrcDir) of
{ok, Options} ->
setOptions(RootSrcDir, Options),
recompileSrcFile(SrcFile, SwSyncNode);
_ ->
Msg = io_lib:format("Unable to determine options for ~s~n", [SrcFile]),
esUtils:logErrors(Msg)
end
end
end.
recompileChangeHrlFile([], _SrcFiles, _SwSyncNode) ->
ok;
recompileChangeHrlFile([Hrl | LeftHrl], SrcFiles, SwSyncNode) ->
WhoInclude = whoInclude(Hrl, SrcFiles),
[recompileSrcFile(SrcFile, SwSyncNode) || {SrcFile, _} <- maps:to_list(WhoInclude)],
recompileChangeHrlFile(LeftHrl, SrcFiles, SwSyncNode).
%%
%% whoInclude(HrlFile, SrcFiles) ->
%% HrlFileBaseName = filename:basename(HrlFile),
%% Pred =
%% fun(SrcFile, _) ->
%% {ok, Forms} = epp_dodger:parse_file(SrcFile),
%% isInclude(binary_to_list(HrlFileBaseName), Forms)
%% end,
%% maps:filter(Pred, SrcFiles).
%% isInclude(_HrlFile, []) ->
%% false;
%% isInclude(HrlFile, [{tree, attribute, _, {attribute, _, [{_, _, IncludeFile}]}} | Forms]) when is_list(IncludeFile) ->
%% IncludeFileBaseName = filename:basename(IncludeFile),
%% case IncludeFileBaseName of
%% HrlFile -> true;
%% _ -> isInclude(HrlFile, Forms)
%% end;
%% isInclude(HrlFile, [_SomeForm | Forms]) ->
%% isInclude(HrlFile, Forms).
whoInclude(HrlFile, SrcFiles) ->
HrlFileBaseName = filename:basename(HrlFile),
Pred =
fun(SrcFile, _) ->
case file:open(SrcFile, [read, binary]) of
{ok, IoDevice} ->
IsInclude = doMathEveryLine(IoDevice, HrlFileBaseName),
file:close(IoDevice),
IsInclude;
_ ->
false
end
end,
maps:filter(Pred, SrcFiles).
doMathEveryLine(IoDevice, HrlFileBaseName) ->
case file:read_line(IoDevice) of
{ok, Data} ->
case re:run(Data, HrlFileBaseName) of
nomatch ->
case re:run(Data, <<"->">>) of
nomatch ->
doMathEveryLine(IoDevice, HrlFileBaseName);
_ ->
false
end;
_ ->
true
end;
_ ->
false
end.
classifyChangeFile([], Beams, Hrls, Srcs) ->
{Beams, Hrls, Srcs};
classifyChangeFile([OneFile | LeftFile], Beams, Hrls, Srcs) ->
case filename:extension(OneFile) of
<<".beam">> ->
Module = erlang:binary_to_atom(filename:basename(OneFile, <<".beam">>), utf8),
classifyChangeFile(LeftFile, [Module | Beams], Hrls, Srcs);
<<".hrl">> ->
classifyChangeFile(LeftFile, Beams, [OneFile | Hrls], Srcs);
<<>> ->
classifyChangeFile(LeftFile, Beams, Hrls, Srcs);
_ ->
classifyChangeFile(LeftFile, Beams, Hrls, [OneFile | Srcs])
end.
addNewFile([], SrcFiles) ->
SrcFiles;
addNewFile([OneFile | LeftFile], SrcFiles) ->
case SrcFiles of
#{OneFile := _value} ->
addNewFile(LeftFile, SrcFiles);
_ ->
addNewFile(LeftFile, SrcFiles#{OneFile => 1})
end.
fileSyncPath(ExecName) ->
case code:priv_dir(?MODULE) of
{error, _} ->
case code:which(?MODULE) of
Filename when is_list(Filename) ->
filename:join([filename:dirname(filename:dirname(Filename)), "priv", ExecName]);
_ ->
filename:join("../priv", ExecName)
end;
Dir ->
filename:join(Dir, ExecName)
end.

Loading…
Cancel
Save