SisMaker 4 роки тому
джерело
коміт
86aa760bca
9 змінених файлів з 726 додано та 841 видалено
  1. +11
    -23
      erlSync.sample.config
  2. +3
    -9
      include/erlSync.hrl
  3. BIN
      priv/fileSync
  4. BIN
      priv/fileSync.exe
  5. +11
    -11
      src/erlSync.erl
  6. +1
    -1
      src/erlSync_sup.erl
  7. +0
    -755
      src/sync/esScanner.erl
  8. +235
    -0
      src/sync/esSyncSrv.erl
  9. +465
    -42
      src/sync/esUtils.erl

+ 11
- 23
erlSync.sample.config Переглянути файл

@ -1,30 +1,26 @@
[
{erlSync, [
%% 扫描module的间隔时间
{moduleTime, 40000},
%% 扫描 源码目录的间隔时间
{srcDirTime, 6000},
%% 扫描 源码文件的间隔时间
{srcFileTime, 6000},
%% 对比加载beam 文件的间隔时间
{compareBeamTime, 2000},
%% 对比编译erl hrl文件的间隔时间
{compareSrcFileTime, 2000},
%% 接受fileSync的监听端口
{listenPort, 12369},
%% 编译命令 支持项目自带的编译命令 也可以用该项目自带的编译逻辑
%% "rebar3 compile" | "start compile.bat" | "make"
{compileCmd, undefined},
%% 编译和加载以及其他一些日志的提示级别设置
%% 有效值: all | none | [success | warnings | errors]
%% 默认值: all
{log, all},
%% 这个参数用于设置特殊目录下的文件检查编译与加载
%% 格式:{srcDirs, {strategy(), [srcDirDescr()]}} | {srcDirs, undefined}
%% -type strategy() :: add | only.
%% 格式:{extraDirs, [{strategy(), [srcDirDescr()]}} | {srcDirs, undefined]}
%% -type strategy() :: add | only | del.
%% 如果 strategy() is only, 仅仅扫描指定目录下的文件编译与加载. 如果 strategy() is add, 会扫描添加的指定目录同步编译与加载.
%% -type srcDirDescr() :: { Dir :: file:filename(), [Options :: compile_option()]}.
%% 默认值:undefined 根据当前工作目录 和 已经加载的模块做来得出需要扫描的目录
{srcDirs, {strategy(), [srcDirDescr()]}},
%%示例: {extraDirs, [{add, [{"./_build/default/lib/erlGbh", []}]}, {only, [{"./", []}]}, {del, [{"./_build", []}]}]}.
%%{extraDirs, [{add, [{"./_build/default/lib/erlGbh", []}, {"./_build/default/lib/erlSync/ebin", []}]}, {only, [{"./", []}]}, {del, [{"./_build", []}]}]}
{extraDirs, [{strategy(), [srcDirDescr()]}]},
%% 这个参数用来设置 怎么处理 当beam文件的源文件目录不是当前工作的子目录时的情况
%% 有效值: fix | allow | ignore
@ -32,15 +28,7 @@
%% * allow = 不要做任何特别的事情,使用beam源文件原始路径查找该文件
%% * ignore = 而忽略对其源路径的任何更改
%% 默认值: fix
{descendant, fix},
%% 仅仅同步编译和加载该参数指定的模块
%% default: [] 为空的时候 该参数无效
{onlyMods, []},
%% 不同步编译和加载该参数指定的模块
%% default: [] 为空的时候 该参数无效
{excludedMods, []}
{descendant, fix}
]}
].

+ 3
- 9
include/erlSync.hrl Переглянути файл

@ -14,17 +14,11 @@
, {exit_on_close, true}]).
-define(Log, log).
-define(moduleTime, moduleTime).
-define(srcDirTime, srcDirTime).
-define(srcFileTime, srcFileTime).
-define(compareBeamTime, compareBeamTime).
-define(compareSrcFileTime, compareSrcFileTime).
-define(listenPort, listenPort).
-define(srcDirs, srcDirs).
-define(onlyMods, onlyMods).
-define(excludedMods, excludedMods).
-define(compileCmd, compileCmd).
-define(extraDirs, extraDirs).
-define(descendant, descendant).
-define(CfgList, [{?Log, all}, {?moduleTime, 30000}, {?srcDirTime, 6000}, {?srcFileTime, 6000}, {?compareBeamTime, 4000}, {?compareSrcFileTime, 4000}, {?listenPort, 12369}, {?srcDirs, undefined}, {?onlyMods, []}, {?excludedMods, []}, {?descendant, fix}]).
-define(CfgList, [{?Log, all}, {?listenPort, 12369}, {?compileCmd, undefined}, {?extraDirs, undefined}, {?descendant, fix}]).
-define(esCfgSync, esCfgSync).


BIN
priv/fileSync.exe Переглянути файл


+ 11
- 11
src/erlSync.erl Переглянути файл

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

+ 1
- 1
src/erlSync_sup.erl Переглянути файл

@ -23,5 +23,5 @@ start_link() ->
%% modules => modules()} % optional
init([]) ->
SupFlags = #{strategy => one_for_one, intensity => 5, period => 10},
ChildSpecs = [?ChildSpec(esScanner, worker)],
ChildSpecs = [?ChildSpec(esSyncSrv, worker)],
{ok, {SupFlags, ChildSpecs}}.

+ 0
- 755
src/sync/esScanner.erl Переглянути файл

@ -1,755 +0,0 @@
-module(esScanner).
-include("erlSync.hrl").
-behaviour(gen_ipc).
-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_ipc callbacks
-export([
init/1,
handleCall/4,
handleAfter/3,
handleCast/3,
handleInfo/3,
handleOnevent/4,
terminate/3
]).
-define(SERVER, ?MODULE).
-type timestamp() :: file:date_time() | 0.
-record(state, {
modules = [] :: [module()]
, hrlDirs = [] :: [file:filename()]
, srcDirs = [] :: [file:filename()]
, hrlFiles = [] :: [file:filename()]
, srcFiles = [] :: [file:filename()]
, beamTimes = undefined :: [{module(), timestamp()}] | undefined
, hrlFileTimes = undefined :: [{file:filename(), timestamp()}] | undefined
, srcFileTimes = undefined :: [{file:filename(), timestamp()}] | undefined
, onsyncFun = undefined
, swSyncNode = false
, sockMod = undefined
, sock = undefined
}).
%% ************************************ API start ***************************
rescan() ->
gen_ipc:cast(?SERVER, miCollMods),
gen_ipc:cast(?SERVER, miCollSrcDirs),
gen_ipc:cast(?SERVER, miCollSrcFiles),
gen_ipc:cast(?SERVER, miCompareHrlFiles),
gen_ipc:cast(?SERVER, miCompareSrcFiles),
gen_ipc:cast(?SERVER, miCompareBeams),
esUtils:logSuccess("start Scanning source files..."),
ok.
unpause() ->
gen_ipc:cast(?SERVER, miUnpause),
ok.
pause() ->
gen_ipc:cast(?SERVER, miPause),
esUtils:logSuccess("Pausing erlSync. Call erlSync:run() to restart"),
ok.
curInfo() ->
gen_ipc:call(?SERVER, miCurInfo).
setLog(T) when ?LOG_ON(T) ->
esUtils:setEnv(log, T),
loadCfg(),
esUtils:logSuccess("Console Notifications Enabled"),
ok;
setLog(_) ->
esUtils:setEnv(log, none),
loadCfg(),
esUtils:logSuccess("Console Notifications Disabled"),
ok.
getLog() ->
?esCfgSync:getv(log).
swSyncNode(IsSync) ->
gen_ipc:cast(?SERVER, {miSyncNode, IsSync}),
ok.
getOnsync() ->
gen_ipc:call(?SERVER, miGetOnsync).
setOnsync(Fun) ->
gen_ipc:call(?SERVER, {miSetOnsync, Fun}).
%% ************************************ API end ***************************
start_link() ->
gen_ipc:start_link({local, ?SERVER}, ?MODULE, [], []).
%% status :: waiting | running | pause
init([]) ->
erlang:process_flag(trap_exit, true),
loadCfg(),
case persistent_term:get(?esRecompileCnt, undefined) of
undefined ->
IndexRef = atomics:new(1, [{signed, false}]),
persistent_term:put(?esRecompileCnt, IndexRef);
_ ->
ignore
end,
{ok, waiting, #state{}, {doAfter, 0}}.
handleAfter(_, waiting, 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),
io:format("IMY******************11111"),
spawn(fun() -> case os:type() of
{win32, _Osname} ->
os:cmd("start ./priv/fileSync.exe ./ " ++ integer_to_list(ListenPort));
_ ->
os:cmd("./priv/fileSync ./ " ++ integer_to_list(ListenPort))
end end),
io:format("IMY******************22222 "),
{kpS, State#state{sockMod = SockMod}, {sTimeout, 2000000, waitConnOver}};
{error, Reason} ->
Msg = io_lib:format("init prim_inet:async_accept error ~p~n", [Reason]),
esUtils:logErrors(Msg),
{kpS, State, {sTimeout, 2000, waitConnOver}}
end;
{error, Reason} ->
Msg = io_lib:format("failed to listen on ~p - ~p (~s) ~n", [ListenPort, Reason, inet:format_error(Reason)]),
esUtils:logErrors(Msg),
{kpS, State, {sTimeout, 2000, waitConnOver}}
end.
handleCall(miGetOnsync, _, #state{onsyncFun = OnSync} = State, _From) ->
{reply, OnSync, State};
handleCall({miSetOnsync, Fun}, _, State, _From) ->
{reply, ok, State#state{onsyncFun = Fun}};
handleCall(miCurInfo, _, State, _Form) ->
{reply, {erlang:get(), State}, State};
handleCall(_Request, _, _State, _From) ->
kpS_S.
handleCast(miPause, _, State) ->
{nextS, pause, State};
handleCast(miUnpause, _, State) ->
{nextS, running, State};
handleCast(miCollMods, running, State) ->
AllModules = (erlang:loaded() -- esUtils:getSystemModules()),
LastCollMods = filterCollMods(AllModules),
Time = ?esCfgSync:getv(?moduleTime),
{kpS, State#state{modules = LastCollMods}, [?gTimeout(miCollMods, Time)]};
handleCast(miCollSrcDirs, running, #state{modules = Modules} = State) ->
{USortedSrcDirs, USortedHrlDirs} =
case ?esCfgSync:getv(srcDirs) of
undefined ->
collSrcDirs(Modules, [], []);
{add, DirsAndOpts} ->
collSrcDirs(Modules, addSrcDirs(DirsAndOpts), []);
{only, DirsAndOpts} ->
collSrcDirs(Modules, [], addSrcDirs(DirsAndOpts))
end,
Time = ?esCfgSync:getv(?srcDirTime),
{kpS, State#state{srcDirs = USortedSrcDirs, hrlDirs = USortedHrlDirs}, [?gTimeout(miCollSrcDirs, Time)]};
handleCast(miCollSrcFiles, running, #state{hrlDirs = HrlDirs, srcDirs = SrcDirs} = State) ->
FSrc =
fun(Dir, Acc) ->
esUtils:wildcard(Dir, ".*\\.(erl|dtl|lfe|ex)$") ++ Acc
end,
SrcFiles = lists:usort(lists:foldl(FSrc, [], SrcDirs)),
FHrl =
fun(Dir, Acc) ->
esUtils:wildcard(Dir, ".*\\.hrl$") ++ Acc
end,
HrlFiles = lists:usort(lists:foldl(FHrl, [], HrlDirs)),
Time = ?esCfgSync:getv(?srcFileTime),
{kpS, State#state{srcFiles = SrcFiles, hrlFiles = HrlFiles}, [?gTimeout(miCollSrcFiles, Time)]};
handleCast(miCompareBeams, running, #state{modules = Modules, beamTimes = BeamTimes, onsyncFun = OnsyncFun, swSyncNode = SwSyncNode} = State) ->
BeamTimeList = [{Mod, LastMod} || Mod <- Modules, LastMod <- [modLastmod(Mod)], LastMod =/= 0],
NewBeamLastMod = lists:usort(BeamTimeList),
reloadChangedMod(BeamTimes, NewBeamLastMod, SwSyncNode, OnsyncFun, []),
Time = ?esCfgSync:getv(?compareBeamTime),
{kpS, State#state{beamTimes = NewBeamLastMod}, [?gTimeout(miCompareBeams, Time)]};
handleCast(miCompareSrcFiles, running, #state{srcFiles = SrcFiles, srcFileTimes = SrcFileTimes, swSyncNode = SwSyncNode} = State) ->
atomics:put(persistent_term:get(?esRecompileCnt), 1, 0),
%% Create a list of file lastmod times...
SrcFileTimeList = [{Src, LastMod} || Src <- SrcFiles, LastMod <- [filelib:last_modified(Src)], LastMod =/= 0],
%% NewSrcFileLastMod = lists:usort(SrcFileTimeList),
%% Compare to previous results, if there are changes, then recompile the file...
recompileChangeSrcFile(SrcFileTimes, SrcFileTimeList, SwSyncNode),
case atomics:get(persistent_term:get(?esRecompileCnt), 1) > 0 of
true ->
gen_ipc:cast(?SERVER, miCompareBeams);
_ ->
ignore
end,
Time = ?esCfgSync:getv(?compareSrcFileTime),
{kpS, State#state{srcFileTimes = SrcFileTimeList}, [?gTimeout(miCompareSrcFiles, Time)]};
handleCast(miCompareHrlFiles, running, #state{hrlFiles = HrlFiles, srcFiles = SrcFiles, hrlFileTimes = HrlFileTimes, swSyncNode = SwSyncNode} = State) ->
atomics:put(persistent_term:get(?esRecompileCnt), 1, 0),
%% Create a list of file lastmod times...
HrlFileTimeList = [{Hrl, LastMod} || Hrl <- HrlFiles, LastMod <- [filelib:last_modified(Hrl)], LastMod =/= 0],
%% NewHrlFileLastMod = lists:usort(HrlFileTimeList),
%% Compare to previous results, if there are changes, then recompile src files that depends
recompileChangeHrlFile(HrlFileTimes, HrlFileTimeList, SrcFiles, SwSyncNode),
case atomics:get(persistent_term:get(?esRecompileCnt), 1) > 0 of
true ->
gen_ipc:cast(?SERVER, miCompareBeams);
_ ->
ignore
end,
Time = ?esCfgSync:getv(?compareSrcFileTime),
{kpS, State#state{hrlFileTimes = HrlFileTimeList}, [?gTimeout(miCompareHrlFiles, Time)]};
handleCast({miSyncNode, IsSync}, _, State) ->
case IsSync of
true ->
{kpS, State#state{swSyncNode = true}};
_ ->
{kpS, State#state{swSyncNode = false}}
end;
handleCast(_Msg, _, _State) ->
kpS_S.
handleInfo({inet_async, LSock, _Ref, Msg}, _, #state{sockMod = SockMod} = State) ->
io:format("IMY************** get inet_async msg ~p~n", [Msg]),
case Msg of
{ok, Sock} ->
%% make it look like gen_tcp:accept
inet_db:register_socket(Sock, SockMod),
io:format("IMY************** socket optinon111 ~p~n", [inet:getopts(Sock, [active, packet])]),
io:format("IMY************** socket optinon222 ~p~n", [inet:getopts(LSock, [active, packet])]),
inet:setopts(Sock, [{active, true}]),
io:format("IMY************** socket optinon111 ~p~n", [inet:getopts(Sock, [active, packet])]),
prim_inet:async_accept(LSock, -1),
{nextS, running, State#state{sock = Sock}};
{error, closed} ->
Msg = io_lib:format("error, closed listen sock error ~p~n",[closed]),
esUtils:logErrors(Msg),
{stop, normal};
{error, Reason} ->
Msg = io_lib:format("listen sock error ~p~n",[Reason]),
esUtils:logErrors(Msg),
{stop, {lsock, Reason}}
end;
handleInfo({tcp, Socket, Data}, running, State) ->
io:format("IMY************** get tcp msg ~p~n", [Data]),
kpS_S;
handleInfo({tcp_closed, Socket}, running, State) ->
io:format("IMY************** get tcp msg ~p~n", [close]),
kpS_S;
handleInfo({tcp_error, Socket, Reason},running, State) ->
io:format("IMY************** get tcp msg ~p~n", [111]),
kpS_S;
handleInfo(_Msg, _, _State) ->
io:format("IMY************** get tcp msg ~p~n", [_Msg]),
kpS_S.
handleOnevent({doSync, _}, Msg, Status, State) ->
handleCast(Msg, Status, State);
handleOnevent(sTimeout, waitConnOver, Status, State) ->
stop;
handleOnevent(_EventType, _EventContent, _Status, _State) ->
kpS_S.
terminate(_Reason, _Status, _State) ->
ok.
%% ***********************************PRIVATE FUNCTIONS start *******************************************
addSrcDirs(DirsAndOpts) ->
[
begin
%% ensure module out path exists & in our code path list
case proplists:get_value(outdir, Opts) of
undefined ->
true;
Path ->
ok = filelib:ensure_dir(Path),
true = code:add_pathz(Path)
end,
setOptions(Dir, Opts),
Dir
end || {Dir, Opts} <- DirsAndOpts
].
reloadChangedMod([{Module, LastMod} | T1], [{Module, LastMod} | T2], SwSyncNode, OnsyncFun, Acc) ->
reloadChangedMod(T1, T2, SwSyncNode, OnsyncFun, Acc);
reloadChangedMod([{Module, _} | T1], [{Module, _} | T2], SwSyncNode, OnsyncFun, Acc) ->
case code:get_object_code(Module) of
error ->
Msg = io_lib:format("Error loading object code for ~p", [Module]),
esUtils:logErrors(Msg),
reloadChangedMod(T1, T2, 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", [Module]),
esUtils:logSuccess(Msg);
{error, What} ->
Msg = io_lib:format("Reloaded(Beam changed) Mod:~s Errors Reason:~p", [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", [Module, NumNodes, Nodes]),
esUtils:logSuccess(MsgNodes);
false ->
ignore
end,
reloadChangedMod(T1, T2, SwSyncNode, OnsyncFun, [Module | Acc])
end;
reloadChangedMod([{Module1, _LastMod1} | T1] = OldLastMods, [{Module2, _LastMod2} | T2] = NewLastMods, SwSyncNode, OnsyncFun, Acc) ->
%% Lists are different, advance the smaller one...
case Module1 < Module2 of
true ->
reloadChangedMod(T1, NewLastMods, SwSyncNode, OnsyncFun, Acc);
false ->
reloadChangedMod(OldLastMods, T2, SwSyncNode, OnsyncFun, Acc)
end;
reloadChangedMod(A, B, _SwSyncNode, OnsyncFun, Acc) when A =:= []; B =:= [] ->
fireOnsync(OnsyncFun, Acc),
ok;
reloadChangedMod(undefined, _Other, _, _, _) ->
ok.
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).
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", [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", [Module, Node]),
esUtils:logSuccess(Msg);
{error, What} ->
Msg = io_lib:format("Reloaded(Beam changed) Mod:~s and write Errors on node:~p Reason:~p", [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", [Module, Node]),
esUtils:logSuccess(Msg);
{error, What} ->
Msg = io_lib:format("Reloaded(Beam changed) Mod:~s Errors on node:~p Reason:~p", [Module, Node, What]),
esUtils:logErrors(Msg)
end
end
end,
[FSync(X) || X <- Nodes],
{ok, NumNodes, Nodes}.
recompileChangeSrcFile([{File, LastMod} | T1], [{File, LastMod} | T2], SwSyncNode) ->
recompileChangeSrcFile(T1, T2, SwSyncNode);
recompileChangeSrcFile([{File, _} | T1], [{File, _} | T2], SwSyncNode) ->
recompileSrcFile(File, SwSyncNode),
recompileChangeSrcFile(T1, T2, SwSyncNode);
recompileChangeSrcFile([{File1, _LastMod1} | T1] = OldSrcFiles, [{File2, LastMod2} | T2] = NewSrcFiles, SwSyncNode) ->
%% Lists are different...
case File1 < File2 of
true ->
%% File was removed, do nothing...
recompileChangeSrcFile(T1, NewSrcFiles, SwSyncNode);
false ->
maybeRecompileSrcFile(File2, LastMod2, SwSyncNode),
recompileChangeSrcFile(OldSrcFiles, T2, SwSyncNode)
end;
recompileChangeSrcFile([], [{File, LastMod} | T2], SwSyncNode) ->
maybeRecompileSrcFile(File, LastMod, SwSyncNode),
recompileChangeSrcFile([], T2, SwSyncNode);
recompileChangeSrcFile(_A, [], _) ->
%% All remaining files, if any, were removed.
ok;
recompileChangeSrcFile(undefined, _Other, _) ->
ok.
erlydtlCompile(SrcFile, Options) ->
F =
fun({outdir, OutDir}, Acc) -> [{out_dir, OutDir} | Acc];
(OtherOption, Acc) -> [OtherOption | Acc]
end,
DtlOptions = lists:foldl(F, [], Options),
Module = list_to_atom(lists:flatten(filename:basename(SrcFile, ".dtl") ++ "_dtl")),
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([list_to_binary(SrcFile)], list_to_binary(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).
maybeRecompileSrcFile(File, LastMod, SwSyncNode) ->
Module = list_to_atom(filename:basename(File, ".erl")),
case code:which(Module) of
BeamFile when is_list(BeamFile) ->
%% check with beam file
case filelib:last_modified(BeamFile) of
BeamLastMod when LastMod > BeamLastMod ->
recompileSrcFile(File, SwSyncNode);
_ ->
ok
end;
_ ->
%% File is new, recompile...
recompileSrcFile(File, SwSyncNode)
end.
getCompileFunAndModuleName(SrcFile) ->
case esUtils:getFileType(SrcFile) of
erl ->
{fun compile:file/2, list_to_atom(filename:basename(SrcFile, ".erl"))};
dtl ->
{fun erlydtlCompile/2, list_to_atom(lists:flatten(filename:basename(SrcFile, ".dtl") ++ "_dtl"))};
lfe ->
{fun lfe_compile/2, list_to_atom(filename:basename(SrcFile, ".lfe"))};
elixir ->
{fun elixir_compile/2, list_to_atom(filename:basename(SrcFile, ".ex"))}
end.
getObjectCode(Module) ->
case code:get_object_code(Module) of
{Module, B, _Filename} -> B;
_ -> undefined
end.
reloadIfNecessary(_CompileFun, _SrcFile, _Module, Binary, Binary, _Options) ->
ok;
reloadIfNecessary(CompileFun, SrcFile, Module, _OldBinary, _Binary, Options) ->
%% Compiling changed the beam code. Compile and reload.
CompileFun(SrcFile, Options),
%% Try to load the module...
case code:ensure_loaded(Module) of
{module, Module} -> ok;
{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,
atomics:add(persistent_term:get(?esRecompileCnt), 1, 1).
errorNoFile(Module) ->
Msg = io_lib:format("~p Couldn't load module: nofile", [Module]),
esUtils:logWarnings([Msg]).
recompileSrcFile(SrcFile, SwSyncNode) ->
%% Get the module, src dir, and options...
case esUtils:getSrcDir(SrcFile) of
{ok, SrcDir} ->
{CompileFun, Module} = getCompileFunAndModuleName(SrcFile),
OldBinary = getObjectCode(Module),
case getOptions(SrcDir) of
{ok, Options} ->
case CompileFun(SrcFile, [binary, return | Options]) of
{ok, Module, Binary, Warnings} ->
reloadIfNecessary(CompileFun, SrcFile, Module, OldBinary, Binary, Options),
printResults(Module, SrcFile, [], Warnings),
{ok, [], Warnings};
{ok, [{ok, Module, Binary, Warnings}], Warnings2} ->
reloadIfNecessary(CompileFun, SrcFile, Module, OldBinary, Binary, Options),
printResults(Module, SrcFile, [], Warnings ++ Warnings2),
{ok, [], Warnings ++ Warnings2};
{ok, multiple, Results, Warnings} ->
[reloadIfNecessary(CompileFun, SrcFile, CompiledModule, OldBinary, Binary, Options) || {CompiledModule, Binary} <- Results],
printResults(Module, SrcFile, [], Warnings),
{ok, [], Warnings};
{ok, OtherModule, _Binary, Warnings} ->
Desc = io_lib:format("Module definition (~p) differs from expected (~s)", [OtherModule, filename:rootname(filename:basename(SrcFile))]),
Errors = [{SrcFile, {0, Module, Desc}}],
printResults(Module, SrcFile, Errors, Warnings),
{ok, Errors, Warnings};
{error, Errors, Warnings} ->
printResults(Module, SrcFile, Errors, Warnings),
{ok, Errors, Warnings}
end;
undefined ->
case esUtils:tryGetModOptions(Module) of
{ok, Options} ->
setOptions(SrcDir, Options),
recompileSrcFile(SrcFile, SwSyncNode);
_ ->
Msg = io_lib:format("Unable to determine options for ~s", [SrcFile]),
esUtils:logErrors(Msg)
end
end;
_ ->
Msg = io_lib:format("not find the file ~s", [SrcFile]),
esUtils:logErrors(Msg)
end.
printResults(_Module, SrcFile, [], []) ->
Msg = io_lib:format("~s Recompiled", [SrcFile]),
esUtils:logSuccess(lists:flatten(Msg));
printResults(_Module, SrcFile, [], Warnings) ->
Msg = [formatErrors(SrcFile, [], Warnings), io_lib:format("~s Recompiled with ~p warnings", [SrcFile, length(Warnings)])],
esUtils:logWarnings(Msg);
printResults(_Module, SrcFile, Errors, Warnings) ->
Msg = [formatErrors(SrcFile, Errors, Warnings)],
esUtils:logErrors(Msg).
%% @private Print error messages in a pretty and user readable way.
formatErrors(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),
io_lib:format("~s:~p: ~s: ~s", [File, Line, Prefix, Msg])
end,
[FPck(X) || X <- Everything].
formatError(Module, ErrorDescription) ->
case erlang:function_exported(Module, format_error, 1) of
true -> Module:format_error(ErrorDescription);
false -> io_lib:format("~s", [ErrorDescription])
end.
recompileChangeHrlFile([{File, LastMod} | T1], [{File, LastMod} | T2], SrcFiles, SwSyncNode) ->
recompileChangeHrlFile(T1, T2, SrcFiles, SwSyncNode);
recompileChangeHrlFile([{File, _} | T1], [{File, _} | T2], SrcFiles, SwSyncNode) ->
WhoInclude = whoInclude(File, SrcFiles),
[recompileSrcFile(SrcFile, SwSyncNode) || SrcFile <- WhoInclude],
recompileChangeHrlFile(T1, T2, SrcFiles, SwSyncNode);
recompileChangeHrlFile([{File1, _LastMod1} | T1] = OldHrlFiles, [{File2, LastMod2} | T2] = NewHrlFiles, SrcFiles, SwSyncNode) ->
%% Lists are different...
case File1 < File2 of
true ->
%% File was removed, do nothing...
warnDelHrlFiles(File1, SrcFiles),
recompileChangeHrlFile(T1, NewHrlFiles, SrcFiles, SwSyncNode);
false ->
%% File is new, look for src that include it
WhoInclude = whoInclude(File2, SrcFiles),
[maybeRecompileSrcFile(SrcFile, LastMod2, SwSyncNode) || SrcFile <- WhoInclude],
recompileChangeHrlFile(OldHrlFiles, T2, SrcFiles, SwSyncNode)
end;
recompileChangeHrlFile([], [{File, LastMod} | T2], SrcFiles, SwSyncNode) ->
WhoInclude = whoInclude(File, SrcFiles),
[maybeRecompileSrcFile(SrcFile, LastMod, SwSyncNode) || SrcFile <- WhoInclude],
recompileChangeHrlFile([], T2, SrcFiles, SwSyncNode);
recompileChangeHrlFile([{File1, _LastMod1} | T1], [], SrcFiles, SwSyncNode) ->
warnDelHrlFiles(File1, SrcFiles),
recompileChangeHrlFile(T1, [], SrcFiles, SwSyncNode);
recompileChangeHrlFile([], [], _, _) ->
%% Done
ok;
recompileChangeHrlFile(undefined, _Other, _, _) ->
%% First load, do nothing
ok.
warnDelHrlFiles(HrlFile, SrcFiles) ->
WhoInclude = whoInclude(HrlFile, SrcFiles),
case WhoInclude of
[] -> ok;
_ ->
Msg = io_lib:format("Warning. Deleted ~p file included in existing src files: ~p", [filename:basename(HrlFile), lists:map(fun(File) ->
filename:basename(File) end, WhoInclude)]),
esUtils:logSuccess(lists:flatten(Msg))
end.
whoInclude(HrlFile, SrcFiles) ->
HrlFileBaseName = filename:basename(HrlFile),
Pred =
fun(SrcFile) ->
{ok, Forms} = epp_dodger:parse_file(SrcFile),
isInclude(HrlFileBaseName, Forms)
end,
lists: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).
filterCollMods(Modules) ->
excludeMods(onlyMods(Modules)).
onlyMods(Modules) ->
case ?esCfgSync:getv(?onlyMods) of
[] ->
Modules;
OnlyMods ->
[Mod || Mod <- Modules, checkModIsMatch(OnlyMods, Mod) == true]
end.
excludeMods(Modules) ->
case ?esCfgSync:getv(?excludedMods) of
[] ->
Modules;
ExcludedModules ->
[Mod || Mod <- Modules, checkModIsMatch(ExcludedModules, Mod) == false]
end.
checkModIsMatch([], _Module) ->
false;
checkModIsMatch([ModOrPattern | T], Module) ->
case ModOrPattern of
Module ->
true;
_ when is_list(ModOrPattern) ->
case re:run(atom_to_list(Module), ModOrPattern) of
{match, _} ->
true;
nomatch ->
checkModIsMatch(T, Module)
end;
_ ->
checkModIsMatch(T, Module)
end.
collSrcDirs(Modules, AddDirs, OnlyDirs) ->
FColl =
fun
(Mod, {SrcAcc, HrlAcc} = Acc) ->
case esUtils:getModSrcDir(Mod) of
{ok, SrcDir} ->
case isOnlyDir(OnlyDirs, SrcDir) of
true ->
{ok, Options} = esUtils:getModOptions(Mod),
HrlDir = proplists:get_all_values(i, Options),
setOptions(SrcDir, Options),
{[SrcDir | SrcAcc], HrlDir ++ HrlAcc};
_ ->
Acc
end;
undefined ->
Acc
end
end,
{SrcDirs, HrlDirs} = lists:foldl(FColl, {AddDirs, []}, Modules),
USortedSrcDirs = lists:usort(SrcDirs),
USortedHrlDirs = lists:usort(HrlDirs),
{USortedSrcDirs, USortedHrlDirs}.
isOnlyDir([], _) ->
true;
isOnlyDir(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.
modLastmod(Mod) ->
case code:which(Mod) of
Beam when is_list(Beam) ->
filelib:last_modified(Beam);
_Other ->
0 %% non_existing | cover_compiled | preloaded
end.
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).
%% ***********************************PRIVATE FUNCTIONS end *********************************************

+ 235
- 0
src/sync/esSyncSrv.erl Переглянути файл

@ -0,0 +1,235 @@
-module(esSyncSrv).
-behaviour(gen_ipc).
-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_ipc callbacks
-export([
init/1,
handleCall/4,
handleAfter/3,
handleCast/3,
handleInfo/3,
handleOnevent/4,
terminate/3
]).
-define(SERVER, ?MODULE).
-define(None, 0).
-record(state, {
srcFiles = #{} :: map()
, onsyncFun = undefined
, swSyncNode = false
, sockMod = undefined
, sock = undefined
}).
%% ************************************ API start ***************************
rescan() ->
gen_ipc:cast(?SERVER, miRescan),
esUtils:logSuccess("start rescaning source files..."),
ok.
unpause() ->
gen_ipc:cast(?SERVER, miUnpause),
ok.
pause() ->
gen_ipc:cast(?SERVER, miPause),
esUtils:logSuccess("Pausing erlSync. Call erlSync:run() to restart"),
ok.
curInfo() ->
gen_ipc: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_ipc:cast(?SERVER, {miSyncNode, IsSync}),
ok.
getOnsync() ->
gen_ipc:call(?SERVER, miGetOnsync).
setOnsync(Fun) ->
gen_ipc:call(?SERVER, {miSetOnsync, Fun}).
%% ************************************ API end ***************************
start_link() ->
gen_ipc:start_link({local, ?SERVER}, ?MODULE, ?None, []).
%% status :: waiting | running | pause
init(_Args) ->
erlang:process_flag(trap_exit, true),
esUtils:loadCfg(),
{ok, waiting, #state{}, {doAfter, ?None}}.
handleAfter(?None, waiting, 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} ->
os:cmd("start ./priv/fileSync.exe ./ " ++ integer_to_list(ListenPort));
_ ->
os:cmd("./priv/fileSync ./ " ++ integer_to_list(ListenPort))
end end),
{kpS, State#state{sockMod = SockMod}, {sTimeout, 4000, waitConnOver}};
{error, Reason} ->
Msg = io_lib:format("init prim_inet:async_accept error ~p~n", [Reason]),
esUtils:logErrors(Msg),
{kpS, State, {sTimeout, 2000, waitConnOver}}
end;
{error, Reason} ->
Msg = io_lib:format("failed to listen on ~p - ~p (~s) ~n", [ListenPort, Reason, inet:format_error(Reason)]),
esUtils:logErrors(Msg),
{kpS, State, {sTimeout, 2000, waitConnOver}}
end.
handleCall(miGetOnsync, _, #state{onsyncFun = OnSync} = State, _From) ->
{reply, OnSync, State};
handleCall({miSetOnsync, Fun}, _, State, _From) ->
{reply, ok, State#state{onsyncFun = Fun}};
handleCall(miCurInfo, _, State, _Form) ->
{reply, {erlang:get(), State}, State};
handleCall(_Request, _, _State, _From) ->
kpS_S.
handleCast(miPause, _, State) ->
{nextS, pause, State};
handleCast(miUnpause, _, State) ->
{nextS, running, State};
handleCast({miSyncNode, IsSync}, _, State) ->
case IsSync of
true ->
{kpS, State#state{swSyncNode = true}};
_ ->
{kpS, State#state{swSyncNode = false}}
end;
handleCast(miRescan, _, State) ->
SrcFiles = esUtils:collSrcFiles(false),
{kpS_S, State#state{srcFiles = SrcFiles}};
handleCast(_Msg, _, _State) ->
kpS_S.
handleInfo({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),
case ?esCfgSync:getv(?compileCmd) of
undefined ->
%% src文件
SrcFiles = esUtils:collSrcFiles(true),
{nextS, running, State#state{sock = Sock, srcFiles = SrcFiles}};
_ ->
{nextS, running, State}
end;
{error, closed} ->
Msg = io_lib:format("error, closed listen sock error ~p~n", [closed]),
esUtils:logErrors(Msg),
{stop, normal};
{error, Reason} ->
Msg = io_lib:format("listen sock error ~p~n", [Reason]),
esUtils:logErrors(Msg),
{stop, {lsock, Reason}}
end;
handleInfo({tcp, _Socket, Data}, running, #state{srcFiles = SrcFiles, onsyncFun = OnsyncFun, swSyncNode = SwSyncNode} = State) ->
FileList = binary:split(Data, <<"\r\n">>, [global]),
%% beam hrl src
{Beams, Hrls, Srcs} = esUtils:dealChangeFile(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),
{kpS, 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,
kpS_S
end;
handleInfo({tcp_closed, _Socket}, running, _State) ->
Msg = io_lib:format("esSyncSrv receive tcp_closed ~n", []),
esUtils:logErrors(Msg),
kpS_S;
handleInfo({tcp_error, _Socket, Reason}, running, _State) ->
Msg = io_lib:format("esSyncSrv receive tcp_error Reason:~p ~n", [Reason]),
esUtils:logErrors(Msg),
kpS_S;
handleInfo(_Msg, _, _State) ->
Msg = io_lib:format("esSyncSrv receive unexpect msg:~p ~n", [_Msg]),
esUtils:logErrors(Msg),
kpS_S.
handleOnevent(sTimeout, waitConnOver, Status, State) ->
Msg = io_lib:format("failed to connect the fileSync to stop stauts:~p state:~p ~n", [Status, State]),
esUtils:logErrors(Msg),
stop;
handleOnevent(_EventType, _EventContent, _Status, _State) ->
kpS_S.
terminate(_Reason, _Status, _State) ->
ok.

+ 465
- 42
src/sync/esUtils.erl Переглянути файл

@ -1,24 +1,10 @@
-module(esUtils).
-include("erlSync.hrl").
-compile(inline).
-compile({inline_size, 128}).
-export([
getModSrcDir/1,
getModOptions/1,
getFileType/1,
getSrcDir/1,
wildcard/2,
getEnv/2,
setEnv/2,
load/2,
logSuccess/1,
logErrors/1,
logWarnings/1,
getSystemModules/0,
tryGetModOptions/1
]).
-compile([export_all, nowarn_export_all]).
getModSrcDir(Module) ->
case code:is_loaded(Module) of
@ -102,6 +88,20 @@ tryGetModOptions(Module) ->
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;
_ ->
case NewDirName =/= SrcDir of
true ->
tryGetSrcOptions(NewDirName);
_ ->
undefined
end
end.
transformOutdir(BeamDir, Options) ->
[{outdir, BeamDir} | proplists:delete(outdir, Options)].
@ -144,11 +144,16 @@ getFileType(Module) when is_atom(Module) ->
Source = proplists:get_value(source, Props, ""),
getFileType(Source);
getFileType(Source) when is_list(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;
@ -193,8 +198,8 @@ determineIncludeDirFromBeamDir(IncludeBase, IncludeDir, BeamDir) ->
%% 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(".", _, _) -> undefined;
findIncludeDirFromAncestors("", _, _) -> undefined;
findIncludeDirFromAncestors(Dir, Cwd, IncludeBase) ->
NewDirName = filename:dirname(Dir),
AttemptDir = filename:join(NewDirName, IncludeBase),
@ -263,9 +268,120 @@ getSrcDir(Dir, Ctr) ->
true -> getSrcDir(filename:dirname(Dir), Ctr - 1)
end.
%% Return all files in a directory matching a regex.
wildcard(Dir, Regex) ->
filelib:fold_files(Dir, Regex, true, fun(Y, Acc) -> [Y | Acc] 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 ->
SrcDir = list_to_binary(filename:dirname(OneFiles)),
case getOptions(SrcDir) of
undefined ->
Mod = list_to_atom(filename:basename(OneFiles, filename:extension(OneFiles))),
{ok, Options} = getModOptions(Mod),
setOptions(SrcDir, Options);
_ ->
ignore
end,
Acc#{list_to_binary(OneFiles) => 1};
_ ->
Acc
end;
_ ->
Acc
end
end, #{}),
FunCollAdds =
fun(OneDir, FilesAcc) ->
filelib:fold_files(OneDir, ".*\\.(erl|dtl|lfe|ex)$", true, fun(OneFiles, Acc) ->
Acc#{list_to_binary(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
@ -288,7 +404,7 @@ logWarnings(Message) ->
canLog(warnings) andalso error_logger:warning_msg(lists:flatten(Message)).
canLog(MsgType) ->
case esScanner:getLog() of
case esSyncSrv:getLog() of
true -> true;
all -> true;
none -> false;
@ -298,25 +414,6 @@ canLog(MsgType) ->
_ -> false
end.
%% Return a list of all modules that belong to Erlang rather than whatever application we may be running.
getSystemModules() ->
Apps = [
appmon, asn1, common_test, compiler, crypto, debugger,
dialyzer, docbuilder, edoc, erl_interface, erts, et,
eunit, gs, hipe, inets, inets, inviso, jinterface, kernel,
mnesia, observer, orber, os_mon, parsetools, percept, pman,
reltool, runtime_tools, sasl, snmp, ssl, stdlib, syntax_tools,
test_server, toolbar, tools, tv, webtool, wx, xmerl, zlib, rebar, rebar3
],
FAppMod =
fun(App) ->
case application:get_key(App, modules) of
{ok, Modules} -> Modules;
_Other -> []
end
end,
lists:flatten([FAppMod(X) || X <- Apps]).
%% map类型的数据不能当做key
-type key() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple().
-type value() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple() | map().
@ -354,3 +451,329 @@ lookup_clauses([], 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", [Module]),
esUtils:logWarnings([Msg]).
printResults(_Module, SrcFile, [], []) ->
Msg = io_lib:format("~s Recompiled", [SrcFile]),
esUtils:logSuccess(lists:flatten(Msg));
printResults(_Module, SrcFile, [], Warnings) ->
Msg = [formatErrors(SrcFile, [], Warnings), io_lib:format("~s Recompiled with ~p warnings", [SrcFile, length(Warnings)])],
esUtils:logWarnings(Msg);
printResults(_Module, SrcFile, Errors, Warnings) ->
Msg = [formatErrors(SrcFile, Errors, Warnings)],
esUtils:logErrors(Msg).
%% @private Print error messages in a pretty and user readable way.
formatErrors(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),
io_lib:format("~s:~p: ~s: ~s", [File, Line, Prefix, Msg])
end,
[FPck(X) || X <- Everything].
formatError(Module, ErrorDescription) ->
case erlang:function_exported(Module, format_error, 1) of
true -> Module:format_error(ErrorDescription);
false -> io_lib:format("~s", [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", [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", [Module]),
esUtils:logSuccess(Msg);
{error, What} ->
Msg = io_lib:format("Reloaded(Beam changed) Mod:~s Errors Reason:~p", [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", [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", [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", [Module, Node]),
esUtils:logSuccess(Msg);
{error, What} ->
Msg = io_lib:format("Reloaded(Beam changed) Mod:~s and write Errors on node:~p Reason:~p", [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", [Module, Node]),
esUtils:logSuccess(Msg);
{error, What} ->
Msg = io_lib:format("Reloaded(Beam changed) Mod:~s Errors on node:~p Reason:~p", [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 = binary_to_atom(lists:flatten(filename:basename(SrcFile, ".dtl") ++ "_dtl")),
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([list_to_binary(SrcFile)], list_to_binary(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, binary_to_atom(filename:basename(SrcFile, <<".erl">>))};
dtl ->
{fun erlydtlCompile/2, list_to_atom(lists:flatten(binary_to_list(filename:basename(SrcFile, <<".dtl">>)) ++ "_dtl"))};
lfe ->
{fun lfe_compile/2, binary_to_atom(filename:basename(SrcFile, <<".lfe">>))};
elixir ->
{fun elixir_compile/2, binary_to_atom(filename:basename(SrcFile, <<".ex">>))}
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", [Module]),
esUtils:logSuccess(Msg);
{error, What} ->
Msg = io_lib:format("Reloaded(Beam changed) Mod:~s Errors Reason:~p", [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...
SrcDir = filename:dirname(SrcFile),
{CompileFun, Module} = getCompileFunAndModuleName(SrcFile),
{OldBinary, Filename} = getObjectCode(Module),
case getOptions(SrcDir) of
{ok, Options} ->
RightFileDir = binary_to_list(filename:join(SrcDir, 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)", [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}
end;
undefined ->
case esUtils:tryGetModOptions(Module) of
{ok, Options} ->
setOptions(SrcDir, Options),
recompileSrcFile(SrcFile, SwSyncNode);
_ ->
case esUtils:tryGetSrcOptions(SrcDir) of
{ok, _Options} ->
recompileSrcFile(SrcFile, SwSyncNode);
_ ->
Msg = io_lib:format("Unable to determine options for ~s", [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).
dealChangeFile([], Beams, Hrls, Srcs) ->
{Beams, Hrls, Srcs};
dealChangeFile([OneFile | LeftFile], Beams, Hrls, Srcs) ->
case filename:extension(OneFile) of
<<".beam">> ->
Module = binary_to_atom(filename:basename(OneFile, <<".beam">>)),
dealChangeFile(LeftFile, [Module | Beams], Hrls, Srcs);
<<".hrl">> ->
dealChangeFile(LeftFile, Beams, [OneFile | Hrls], Srcs);
<<>> ->
dealChangeFile(LeftFile, Beams, Hrls, Srcs);
_ ->
dealChangeFile(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.

Завантаження…
Відмінити
Зберегти