Browse Source

调整与优化

master
AICells 5 years ago
parent
commit
8eac77584f
9 changed files with 411 additions and 539 deletions
  1. +40
    -9
      README.md
  2. +34
    -51
      erlSync.sample.config
  3. +19
    -0
      include/erlSync.hrl
  4. +2
    -17
      src/erlSync.app.src
  5. +19
    -17
      src/erlSync.erl
  6. +0
    -8
      src/erlSync_app.erl
  7. +2
    -3
      src/erlSync_sup.erl
  8. +247
    -331
      src/sync/esScanner.erl
  9. +48
    -103
      src/sync/esUtils.erl

+ 40
- 9
README.md View File

@ -1,9 +1,40 @@
erlSync
=====
An OTP application
Build
-----
$ rebar3 compile
# erlSync
改造自 [sync](https://github.com/rustyio/sync) 有兴趣可以了解一下, 本项目仅仅在此基础做了一些封装改动和优化,工作原理并无差别
# 工作原理
启动后,Sync会收集有关已加载模块,ebin目录,源文件,编译选项等的信息。
然后,Sync会定期检查源文件的最后修改日期。如果自上次扫描以来文件已更改,则Sync会使用上一组编译选项自动重新编译模块。如果编译成功,它将加载更新的模块。否则,它将编译错误输出到控制台。
同步还会定期检查任何梁文件的上次修改日期,如果文件已更改,则会自动重新加载。
扫描过程会在运行的Erlang VM上增加1%到2%的CPU负载。已经采取了很多措施将其保持在较低水平。
但这仅适用于开发模式,请勿在生产中运行。
# 使用
启动自动编译与加载
erlSync:run().
暂停自动编译与加载
erlSync:pause().
启动或者关闭集群同步加载
erlSync:swSyncNode(TrueOrFalse).
设置编译与加载日志提示
erlSync:setLog(Val).
设置加载后的钩子函数(支持匿名函数, {Mod, Fun}(Fun函数只有一个参数)格式, 以及他们的列表组合)
erlSync:setOnsync(FunOrFuns).
# 配置说明
参见erlSync.sample.config
默认配置为
[
{erlSync,
[
{moduleTime, 30000},
{srcDirTime, 5000},
{srcFileTime, 5000},
{compareBeamTime, 3000},
{compareSrcFileTime, 3000},
{srcDirs, undefined}
{log, all},
{descendant, fix},
{onlyMods, []},
{excludedMods, []}
]
}
]

+ 34
- 51
erlSync.sample.config View File

@ -1,61 +1,44 @@
[
{erlSync, [
{moduleTime, 10000},
{srcDirTime, 10000},
%% 扫描module的间隔时间
{moduleTime, 30000},
%% 扫描 源码目录的间隔时间
{srcDirTime, 5000},
%% 扫描 源码文件的间隔时间
{srcFileTime, 5000},
{compareBeamTime, 2000},
%% 对比加载beam 文件的间隔时间
{compareBeamTime, 3000},
%% 对比编译erl hrl文件的间隔时间
{compareSrcFileTime, 1000},
{file_variables, "-*- mode: compilation; mode: auto-revert; buffer-read-only: true; auto-revert-interval: 0.5 -*-\n\n"},
%% Temp file to write output messages.
{out_file, "/tmp/erlSync.out"},
%% List of modules to be excluded from scanning. While using rebar
%% it's not very useful to specify the excludes here as every
%% get/update-deps will override the settings. Instead specify
%% excluded stuff in the node's config file.
{excluded_modules, []},
%% log: Console notifications
%% valid values: all | none | [success | warnings | errors]
%% default: all
%% 编译和加载以及其他一些日志的提示级别设置
%% 有效值: all | none | [success | warnings | errors]
%% 默认值: all
{log, all},
%% non_descendants: How to handle beams whose original source path is
%% not a descendant of the current working directory.
%%
%% valid values: fix | allow | ignore
%% * fix = attempt to find source files under current directory
%% * allow = don't do anything special, use the non-descendant path and
%% watch that file
%% * ignore = don't watch that module at all and ignore any changes to
%% its source path
%% default: fix
{non_descendants, fix},
%% whitelisted_modules: erlSync only these modules
%% default: []
{whitelisted_modules, []},
%% excluded_modules: Ignore any modules listed
%% default: []
{excluded_modules, []},
%% executable: Identify the program that you want run by the "growl" notifications
%% valid values: auto | notifu | 'notify-send' | growlnotify | emacsclient | notification_center
%% * auto = allow erlSync to autodetect which program to run
%% * growlnotify = Use Growl for Mac
%% * notification_center = Use OSX Notification Center
%% * 'notify-send' = Use libnotify for Linux
%% * notifu = The notifu program for Windows
%% * emacsclient = Emacs notifications
%% default: auto
{executable, auto}
%% 这个参数用于设置特殊目录下的文件检查编译与加载
%% 格式:{srcDirs, {strategy(), [srcDirDescr()]}} | {srcDirs, undefined}
%% -type strategy() :: add | only.
%% 如果 strategy() is only, 仅仅扫描指定目录下的文件编译与加载. 如果 strategy() is add, 会扫描添加的指定目录同步编译与加载.
%% -type srcDirDescr() :: { Dir :: file:filename(), [Options :: compile_option()]}.
%% 默认值:undefined 根据当前工作目录 和 已经加载的模块做来得出需要扫描的目录
{srcDirs, {strategy(), [srcDirDescr()]}},
%% 这个参数用来设置 怎么处理 当beam文件的源文件目录不是当前工作的子目录时的情况
%% 有效值: fix | allow | ignore
%% * fix = 尝试在当前目录下查找源文件
%% * allow = 不要做任何特别的事情,使用beam源文件原始路径查找该文件
%% * ignore = 而忽略对其源路径的任何更改
%% 默认值: fix
{descendant, fix},
%% 仅仅同步编译和加载该参数指定的模块
%% default: [] 为空的时候 该参数无效
{onlyMods, []},
%% 不同步编译和加载该参数指定的模块
%% default: [] 为空的时候 该参数无效
{excludedMods, []}
]}
].

+ 19
- 0
include/erlSync.hrl View File

@ -0,0 +1,19 @@
-define(LOG_ON(Val), Val == true; Val == all; Val == skip_success; is_list(Val), Val =/= []).
-define(gTimeout(Type, Time), {{gTimeout, Type}, Time, Type}).
-define(Log, log).
-define(moduleTime, moduleTime).
-define(srcDirTime, srcDirTime).
-define(srcFileTime, srcFileTime).
-define(compareBeamTime, compareBeamTime).
-define(compareSrcFileTime, compareSrcFileTime).
-define(srcDirs, srcDirs).
-define(onlyMods, onlyMods).
-define(excludedMods, excludedMods).
-define(descendant, descendant).
-define(CfgList, [{?Log, all}, {?moduleTime, 30000}, {?srcDirTime, 5000}, {?srcFileTime, 5000}, {?compareBeamTime, 3000}, {?compareSrcFileTime, 3000}, {?srcDirs, undefined}, {?onlyMods, []}, {?excludedMods, []}, {?descendant, fix}]).
-define(esCfgSync, esCfgSync).
-define(esRecompileCnt, '$esRecompileCnt').

+ 2
- 17
src/erlSync.app.src View File

@ -4,23 +4,8 @@
{registered, []},
{mod, {erlSync_app, []}},
{applications, [kernel, stdlib, syntax_tools, compiler]},
{env, [
{moduleTime, 10000},
{srcDirTime, 10000},
{srcFileTime, 5000},
{compareBeamTime, 2000},
{compareSrcFileTime, 1000},
%% http://www.gnu.org/software/emacs/manual/html_node/emacs/File-Variables.html#File-Variables
{file_variables, "-*- mode: compilation; mode: auto-revert; buffer-read-only: true; auto-revert-interval: 0.5 -*-\n\n"},
%% Temp file to write output messages.
{out_file, "/tmp/erlSync.out"},
%% List of modules to be excluded from scanning. While using rebar
%% it's not very useful to specify the excludes here as every
%% get/update-deps will override the settings. Instead specify
%% excluded stuff in the node's config file.
{excluded_modules, []}
]},
{env, []},
{modules, []},
{licenses, ["Apache 2.0"]},
{licenses, ["MIT License"]},
{links, []}
]}.

+ 19
- 17
src/erlSync.erl View File

@ -5,16 +5,15 @@
stop/0,
run/0,
pause/0,
info/0,
log/1,
log/0,
onsync/0,
onsync/1,
curInfo/0,
setLog/1,
getLog/0,
getOnsync/0,
setOnsync/0,
setOnsync/1,
swSyncNode/1
]).
-define(VALID_GROWL_OR_LOG(X), is_boolean(X); is_list(X); X == all; X == none; X == skip_success).
start() ->
application:ensure_all_started(erlSync).
@ -27,8 +26,8 @@ run() ->
esScanner:unpause(),
esScanner:rescan(),
ok;
{error, _Reason} ->
io:format("Err")
{error, Reason} ->
esUtils:logErrors("start erlSync error ~p~n", [Reason])
end.
pause() ->
@ -39,21 +38,24 @@ swSyncNode(IsSync) ->
esScanner:swSyncNode(IsSync),
ok.
info() ->
esScanner:info().
curInfo() ->
esScanner:curInfo().
log(Val) when ?VALID_GROWL_OR_LOG(Val) ->
setLog(Val) ->
esScanner:setLog(Val).
log() ->
getLog() ->
esScanner:getLog().
onsync(Fun) ->
esScanner:setOnsync(Fun).
onsync() ->
getOnsync() ->
esScanner:getOnsync().
setOnsync() ->
esScanner:setOnsync(undefined).
setOnsync(Fun) ->
esScanner:setOnsync(Fun).

+ 0
- 8
src/erlSync_app.erl View File

@ -1,10 +1,4 @@
%%%-------------------------------------------------------------------
%% @doc erlSync public API
%% @end
%%%-------------------------------------------------------------------
-module(erlSync_app).
-behaviour(application).
-export([start/2, stop/1]).
@ -14,5 +8,3 @@ start(_StartType, _StartArgs) ->
stop(_State) ->
ok.
%% internal functions

+ 2
- 3
src/erlSync_sup.erl View File

@ -1,5 +1,4 @@
-module(erlSync_sup).
-behaviour(supervisor).
-export([
@ -23,6 +22,6 @@ start_link() ->
%% type => worker(), % optional
%% modules => modules()} % optional
init([]) ->
SupFlags = #{strategy => rest_for_one, intensity => 1, period => 10},
ChildSpecs = [?ChildSpec(esOptions, worker), ?ChildSpec(esScanner, worker)],
SupFlags = #{strategy => one_for_one, intensity => 5, period => 10},
ChildSpecs = [?ChildSpec(esScanner, worker)],
{ok, {SupFlags, ChildSpecs}}.

+ 247
- 331
src/sync/esScanner.erl View File

@ -1,4 +1,6 @@
-module(esScanner).
-include("erlSync.hrl").
-behaviour(gen_ipc).
-compile(inline).
@ -8,14 +10,14 @@
-export([
start_link/0,
rescan/0,
info/0,
swSyncNode/1,
pause/0,
unpause/0,
setLog/1,
getLog/0,
curInfo/0,
getOnsync/0,
setOnsync/1,
setLog/1,
getLog/0
swSyncNode/1
]).
%% gen_ipc callbacks
@ -29,45 +31,30 @@
]).
-define(SERVER, ?MODULE).
-define(PRINT(Var), io:format("DEBUG: ~p:~p - ~p~n~n ~p~n~n", [?MODULE, ?LINE, ??Var, Var])).
-define(LOG_OR_GROWL_ON(Val), Val == true;Val == all;Val == skip_success;is_list(Val), Val =/= []).
-define(LOG_OR_GROWL_OFF(Val), Val == false;F == none;F == []).
-define(gTimeout(Type, Time), {{gTimeout, Type}, Time, Type}).
-define(esCfgSync, esCfgSync).
-define(Log, log).
-define(moduleTime, moduleTime).
-define(srcDirTime, srcDirTime).
-define(srcFileTime, srcFileTime).
-define(compareBeamTime, compareBeamTime).
-define(compareSrcFileTime, compareSrcFileTime).
-define(CfgList, [{?Log, all}, {?moduleTime, 30000}, {?srcDirTime, 5000}, {?srcFileTime, 5000}, {?compareBeamTime, 3000}, {?compareSrcFileTime, 3000}]).
-type timestamp() :: file:date_time() | 0.
-record(state, {
modules = [] :: [module()],
srcDirs = [] :: [file:filename()],
hrlDirs = [] :: [file:filename()],
srcFiles = [] :: [file:filename()],
hrlFiles = [] :: [file:filename()],
beamTimes = undefined :: [{module(), timestamp()}] | undefined,
srcFileTimes = [] :: [{file:filename(), timestamp()}],
hrlFileTimes = [] :: [{file:filename(), timestamp()}],
onsyncFun = undefined,
patching = false
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
}).
%% ************************************ API start ***************************
rescan() ->
io:format("Scanning source files...~n"),
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),
gen_ipc:cast(?SERVER, miCompareHrlFiles),
esUtils:logSuccess("start Scanning source files..."),
ok.
unpause() ->
@ -75,24 +62,22 @@ unpause() ->
ok.
pause() ->
esUtils:log_success("Pausing erlSync. Call sync:go() to restart~n"),
gen_ipc:cast(?SERVER, miPause),
esUtils:logSuccess("Pausing erlSync. Call erlSync:run() to restart"),
ok.
info() ->
io:format("erlSync Info...~n"),
gen_ipc:call(?SERVER, miInfo),
ok.
curInfo() ->
gen_ipc:call(?SERVER, miCurInfo).
setLog(T) when ?LOG_OR_GROWL_ON(T) ->
setLog(T) when ?LOG_ON(T) ->
esUtils:setEnv(log, T),
loadCfg(),
esUtils:log_success("Console Notifications Enabled~n"),
esUtils:logSuccess("Console Notifications Enabled"),
ok;
setLog(F) when ?LOG_OR_GROWL_OFF(F) ->
setLog(_) ->
esUtils:setEnv(log, none),
loadCfg(),
esUtils:log_success("Console Notifications Disabled~n"),
esUtils:logSuccess("Console Notifications Disabled"),
ok.
getLog() ->
@ -108,27 +93,29 @@ getOnsync() ->
setOnsync(Fun) ->
gen_ipc:call(?SERVER, {miSetOnsync, Fun}).
%% status running | pause
%% ************************************ API end ***************************
start_link() ->
gen_ipc:start_link({local, ?SERVER}, ?MODULE, [], []).
%% status :: running | pause
init([]) ->
erlang:process_flag(trap_exit, true),
rescan(),
startup(),
loadCfg(),
case persistent_term:get(?esRecompileCnt, undefined) of
undefined ->
IndexRef = atomics:new(1, [{signed, false}]),
persistent_term:put(?esRecompileCnt, IndexRef);
_ ->
ignore
end,
{ok, running, #state{}}.
handleCall(miGetOnsync, _, State, _From) ->
OnSync = State#state.onsyncFun,
handleCall(miGetOnsync, _, #state{onsyncFun = OnSync} = State, _From) ->
{reply, OnSync, State};
handleCall({miSetOnsync, Fun}, _, State, _From) ->
State2 = State#state{onsyncFun = Fun},
{reply, ok, State2};
handleCall(miInfo, _, State, _Form) ->
{reply, ok, State#state{onsyncFun = Fun}};
handleCall(miCurInfo, _, State, _Form) ->
{reply, {erlang:get(), State}, State};
handleCall(_Request, _, _State, _From) ->
keepStatusState.
@ -136,119 +123,81 @@ handleCall(_Request, _, _State, _From) ->
handleCast(miPause, _, State) ->
{nextStatus, pause, State};
handleCast(miUnpause, _, State) ->
rescan(),
{nextStatus, running, State};
handleCast(miCollMods, running, State) ->
% Get a list of all loaded non-system modules.
AllModules = (erlang:loaded() -- esUtils:getSystemModules()),
% Delete excluded modules/applications
CollModules = collMods(AllModules),
%% Schedule the next interval...
LastCollMods = filterCollMods(AllModules),
Time = ?esCfgSync:getv(?moduleTime),
{keepStatus, State#state{modules = CollModules}, [?gTimeout(miCollMods, Time)]};
handleCast(miCollSrcDirs, running, State) ->
{keepStatus, State#state{modules = LastCollMods}, [?gTimeout(miCollMods, Time)]};
handleCast(miCollSrcDirs, running, #state{modules = Modules} = State) ->
{USortedSrcDirs, USortedHrlDirs} =
case application:get_env(erlSync, srcDirs) of
case ?esCfgSync:getv(srcDirs) of
undefined ->
collSrcDirs(State, [], []);
{ok, {add, DirsAndOpts}} ->
collSrcDirs(State, dirs(DirsAndOpts), []);
{ok, {replace, DirsAndOpts}} ->
collSrcDirs(State, [], dirs(DirsAndOpts))
collSrcDirs(Modules, [], []);
{add, DirsAndOpts} ->
collSrcDirs(Modules, addSrcDirs(DirsAndOpts), []);
{only, DirsAndOpts} ->
collSrcDirs(Modules, [], addSrcDirs(DirsAndOpts))
end,
Time = ?esCfgSync:getv(?srcDirTime),
{keepStatus, State#state{srcDirs = USortedSrcDirs, hrlDirs = USortedHrlDirs}, [?gTimeout(miCollSrcDirs, Time)]};
handleCast(miCollSrcFiles, running, State) ->
%% For each source dir, get a list of source files...
FErl =
fun(X, Acc) ->
esUtils:wildcard(X, ".*\\.(erl|dtl|lfe|ex)$") ++ Acc
handleCast(miCollSrcFiles, running, #state{hrlDirs = HrlDirs, srcDirs = SrcDirs} = State) ->
FSrc =
fun(Dir, Acc) ->
esUtils:wildcard(Dir, ".*\\.(erl|dtl|lfe|ex)$") ++ Acc
end,
ErlFiles = lists:usort(lists:foldl(FErl, [], State#state.srcDirs)),
%% For each include dir, get a list of hrl files...
SrcFiles = lists:usort(lists:foldl(FSrc, [], SrcDirs)),
FHrl =
fun(X, Acc) ->
esUtils:wildcard(X, ".*\\.hrl$") ++ Acc
fun(Dir, Acc) ->
esUtils:wildcard(Dir, ".*\\.hrl$") ++ Acc
end,
HrlFiles = lists:usort(lists:foldl(FHrl, [], State#state.hrlDirs)),
%% Schedule the next interval...
HrlFiles = lists:usort(lists:foldl(FHrl, [], HrlDirs)),
Time = ?esCfgSync:getv(?srcFileTime),
%% Return with updated files...
{keepStatus, State#state{srcFiles = ErlFiles, hrlFiles = HrlFiles}, [?gTimeout(miCollSrcFiles, Time)]};
handleCast(miCompareBeams, running, #state{onsyncFun = OnsyncFun} = State) ->
%% Create a list of beam file lastmod times, but filter out modules not having
%% a valid beam file reference.
F = fun(X) ->
case code:which(X) of
Beam when is_list(Beam) ->
case filelib:last_modified(Beam) of
0 ->
false; %% file not found
LastMod ->
{true, {X, LastMod}}
end;
_Other ->
false %% non_existing | cover_compiled | preloaded
end
end,
NewBeamLastMod = lists:usort(lists:filtermap(F, State#state.modules)),
%% Compare to previous results, if there are changes, then reload
%% the beam...
reloadChangedMod(State#state.beamTimes, NewBeamLastMod, State#state.patching, OnsyncFun, []),
{keepStatus, 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),
{keepStatus, State#state{beamTimes = NewBeamLastMod}, [?gTimeout(miCompareBeams, Time)]};
handleCast(miCompareSrcFiles, running, State) ->
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...
F =
fun(X) ->
LastMod = filelib:last_modified(X),
{X, LastMod}
end,
NewSrcFileLastMod = lists:usort([F(X) || X <- State#state.srcFiles]),
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(State#state.srcFileTimes, NewSrcFileLastMod, State#state.patching),
%% Schedule the next interval...
recompileChangeSrcFile(SrcFileTimes, NewSrcFileLastMod, SwSyncNode),
case atomics:get(persistent_term:get(?esRecompileCnt), 1) > 0 of
true ->
gen_ipc:cast(?SERVER, miCompareBeams);
_ ->
ignore
end,
Time = ?esCfgSync:getv(?compareSrcFileTime),
{keepStatus, State#state{srcFileTimes = NewSrcFileLastMod}, [?gTimeout(miCompareSrcFiles, Time)]};
handleCast(miCompareHrlFiles, running, State) ->
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...
F =
fun(X) ->
LastMod = filelib:last_modified(X),
{X, LastMod}
end,
NewHrlFileLastMod = lists:usort([F(X) || X <- State#state.hrlFiles]),
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(State#state.hrlFileTimes, NewHrlFileLastMod, State#state.srcFiles, State#state.patching),
%% Schedule the next interval...
recompileChangeHrlFile(HrlFileTimes, NewHrlFileLastMod, SrcFiles, SwSyncNode),
case atomics:get(persistent_term:get(?esRecompileCnt), 1) > 0 of
true ->
gen_ipc:cast(?SERVER, miCompareBeams);
_ ->
ignore
end,
Time = ?esCfgSync:getv(?compareSrcFileTime),
{keepStatus, State#state{hrlFileTimes = NewHrlFileLastMod}, [?gTimeout(miCompareHrlFiles, Time)]};
handleCast({miSyncNode, IsSync}, _, State) ->
case IsSync of
true ->
{keepStatus, State#state{patching = true}};
{keepStatus, State#state{swSyncNode = true}};
_ ->
{keepStatus, State#state{patching = false}}
{keepStatus, State#state{swSyncNode = false}}
end;
handleCast(_Msg, _, _State) ->
keepStatusState.
@ -260,11 +209,15 @@ handleOnevent({gTimeout, _}, Msg, Status, State) ->
handleOnevent(_EventType, _EventContent, _Status, _State) ->
keepStatusState.
terminate(_Reason, _Status, _State) ->
ok.
%% ***********************************PRIVATE FUNCTIONS start *******************************************
dirs(DirsAndOpts) ->
addSrcDirs(DirsAndOpts) ->
[
begin
%% ensure module out path exists & in our code list
%% ensure module out path exists & in our code path list
case proplists:get_value(outdir, Opts) of
undefined ->
true;
@ -274,59 +227,48 @@ dirs(DirsAndOpts) ->
end,
setOptions(Dir, Opts),
Dir
end || {Dir, Opts} <- DirsAndOpts].
terminate(_Reason, _Status, _State) ->
ok.
end || {Dir, Opts} <- DirsAndOpts
].
%%% PRIVATE FUNCTIONS %%%
reloadChangedMod([{Module, LastMod} | T1], [{Module, LastMod} | T2], EnablePatching, OnsyncFun, Acc) ->
%% Beam hasn't changed, do nothing...
reloadChangedMod(T1, T2, EnablePatching, OnsyncFun, Acc);
reloadChangedMod([{Module, _} | T1], [{Module, _} | T2], EnablePatching, OnsyncFun, Acc) ->
%% Beam has changed, reload...
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~n", [Module]),
esUtils:log_errors(Msg),
reloadChangedMod(T1, T2, EnablePatching, OnsyncFun, Acc);
Msg = io_lib:format("Error loading object code for ~p", [Module]),
esUtils:logErrors(Msg),
reloadChangedMod(T1, T2, SwSyncNode, OnsyncFun, Acc);
{Module, Binary, Filename} ->
code:load_binary(Module, Filename, Binary),
%% If patching is enabled, then reload the module across *all* connected
%% erlang VMs, and save the compiled beam to disk.
case EnablePatching of
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} = syncLoadModOnAllNodes(Module),
Msg = io_lib:format("~s: Reloaded on ~p nodes! (Beam changed.)~n", [Module, NumNodes]),
esUtils:log_success(Msg);
{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 ->
%% Print a status message...
Msg = io_lib:format("~s: Reloaded! (Beam changed.)~n", [Module]),
esUtils:log_success(Msg)
ignore
end,
reloadChangedMod(T1, T2, EnablePatching, OnsyncFun, [Module | Acc])
reloadChangedMod(T1, T2, SwSyncNode, OnsyncFun, [Module | Acc])
end;
reloadChangedMod([{Module1, _LastMod1} | T1] = OldLastMods, [{Module2, _LastMod2} | T2] = NewLastMods, EnablePatching, OnsyncFun, Acc) ->
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, EnablePatching, OnsyncFun, Acc);
reloadChangedMod(T1, NewLastMods, SwSyncNode, OnsyncFun, Acc);
false ->
reloadChangedMod(OldLastMods, T2, EnablePatching, OnsyncFun, Acc)
reloadChangedMod(OldLastMods, T2, SwSyncNode, OnsyncFun, Acc)
end;
reloadChangedMod(A, B, _EnablePatching, OnsyncFun, Acc) when A =:= []; B =:= [] ->
% MsgAdd =
% case EnablePatching of
% true -> " on " ++ integer_to_list(length(get_nodes())) ++ " nodes.";
% false -> "."
% end,
%% Done.
reloadChangedMod(A, B, _SwSyncNode, OnsyncFun, Acc) when A =:= []; B =:= [] ->
fireOnsync(OnsyncFun, Acc),
ok;
reloadChangedMod(undefined, _Other, _, _, _) ->
%% First load, do nothing.
ok.
fireOnsync(OnsyncFun, Modules) ->
@ -348,58 +290,67 @@ 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.
%% Get a list of nodes known by this node, plus all attached nodes.
Nodes = getNodes(),
io:format("[~s:~p] DEBUG - Nodes: ~p~n", [?MODULE, ?LINE, Nodes]),
NumNodes = length(Nodes),
{Module, Binary, _} = code:get_object_code(Module),
FSync =
fun(Node) ->
io:format("[~s:~p] DEBUG - Node: ~p~n", [?MODULE, ?LINE, Node]),
Msg = io_lib:format("Reloading '~s' on ~s.~n", [Module, Node]),
esUtils:log_success(Msg),
io:format("[~s:~p] DEBUG - Node: ~p", [?MODULE, ?LINE, Node]),
Msg = io_lib:format("Reloading '~s' on ~s", [Module, Node]),
esUtils:logSuccess(Msg),
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]),
{module, Module} = rpc:call(Node, code, load_file, [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", [Node, Module]),
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.
{module, Module} = rpc:call(Node, code, load_binary, [Module, undefined, Binary])
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", [Node, Module]),
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}.
recompileChangeSrcFile([{File, LastMod} | T1], [{File, LastMod} | T2], EnablePatching) ->
%% Beam hasn't changed, do nothing...
recompileChangeSrcFile(T1, T2, EnablePatching);
recompileChangeSrcFile([{File, _} | T1], [{File, _} | T2], EnablePatching) ->
%% File has changed, recompile...
recompileSrcFile(File, EnablePatching),
recompileChangeSrcFile(T1, T2, EnablePatching);
recompileChangeSrcFile([{File1, _LastMod1} | T1] = OldSrcFiles, [{File2, LastMod2} | T2] = NewSrcFiles, EnablePatching) ->
{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, EnablePatching);
recompileChangeSrcFile(T1, NewSrcFiles, SwSyncNode);
false ->
maybeRecompileSrcFile(File2, LastMod2, EnablePatching),
recompileChangeSrcFile(OldSrcFiles, T2, EnablePatching)
maybeRecompileSrcFile(File2, LastMod2, SwSyncNode),
recompileChangeSrcFile(OldSrcFiles, T2, SwSyncNode)
end;
recompileChangeSrcFile([], [{File, LastMod} | T2], EnablePatching) ->
maybeRecompileSrcFile(File, LastMod, EnablePatching),
recompileChangeSrcFile([], T2, EnablePatching);
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, _) ->
%% First load, do nothing.
ok.
erlydtlCompile(SrcFile, Options) ->
@ -429,20 +380,20 @@ lfe_compile(SrcFile, Options) ->
Compiler = lfe_comp,
Compiler:file(SrcFile, Options).
maybeRecompileSrcFile(File, LastMod, EnablePatching) ->
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, EnablePatching);
recompileSrcFile(File, SwSyncNode);
_ ->
ok
end;
_ ->
%% File is new, recompile...
recompileSrcFile(File, EnablePatching)
recompileSrcFile(File, SwSyncNode)
end.
getCompileFunAndModuleName(SrcFile) ->
@ -463,12 +414,9 @@ getObjectCode(Module) ->
_ -> undefined
end.
reloadIfNecessary(_CompileFun, SrcFile, Module, Binary, Binary, _Options, Warnings) ->
%% Compiling didn't change the beam code. Don't reload...
printResults(Module, SrcFile, [], Warnings),
{ok, [], Warnings};
reloadIfNecessary(CompileFun, SrcFile, Module, _OldBinary, _Binary, Options, Warnings) ->
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...
@ -476,89 +424,66 @@ reloadIfNecessary(CompileFun, SrcFile, Module, _OldBinary, _Binary, Options, War
{module, Module} -> ok;
{error, nofile} -> errorNoFile(Module);
{error, embedded} ->
%% Module is not yet loaded, load it.
case code:load_file(Module) of
case code:load_file(Module) of %% Module is not yet loaded, load it.
{module, Module} -> ok;
{error, nofile} -> errorNoFile(Module)
end
end,
gen_ipc:cast(?SERVER, miCompareBeams),
%% Print the warnings...
printResults(Module, SrcFile, [], Warnings),
{ok, [], Warnings}.
atomics:add(persistent_term:get(?esRecompileCnt), 1, 1).
errorNoFile(Module) ->
Msg = io_lib:format("~p:0: Couldn't load module: nofile~n", [Module]),
esUtils:log_warnings([Msg]).
Msg = io_lib:format("~p Couldn't load module: nofile", [Module]),
esUtils:logWarnings([Msg]).
recompileSrcFile(SrcFile, _EnablePatching) ->
recompileSrcFile(SrcFile, _SwSyncNode) ->
%% Get the module, src dir, and options...
case esUtils:getSrcDir(SrcFile) of
{ok, SrcDir} ->
{CompileFun, Module} = getCompileFunAndModuleName(SrcFile),
%% Get the old binary code...
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, 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, Warnings ++ Warnings2);
reloadIfNecessary(CompileFun, SrcFile, Module, OldBinary, Binary, Options),
printResults(Module, SrcFile, [], Warnings ++ Warnings2),
{ok, [], Warnings ++ Warnings2};
{ok, multiple, Results, Warnings} ->
Reloader =
fun({CompiledModule, Binary}) ->
{ok, _, _} = reloadIfNecessary(CompileFun, SrcFile, CompiledModule, OldBinary, Binary, Options, Warnings)
end,
lists:foreach(Reloader, Results),
[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} ->
%% Compiling failed. Print the warnings and errors...
printResults(Module, SrcFile, Errors, Warnings),
{ok, Errors, Warnings}
end;
undefined ->
Msg = io_lib:format("Unable to determine options for ~p", [SrcFile]),
esUtils:log_errors(Msg)
esUtils:logErrors(Msg)
end;
_ ->
Msg = io_lib:format("not find the file ~p", [SrcFile]),
esUtils:log_errors(Msg)
esUtils:logErrors(Msg)
end.
printResults(Module, SrcFile, [], []) ->
Msg = io_lib:format("~s:0: Recompiled.~n", [SrcFile]),
case code:is_loaded(Module) of
{file, _} ->
ok;
false ->
ignore
end,
esUtils:log_success(lists:flatten(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:0: Recompiled with ~p warnings~n", [SrcFile, length(Warnings)])
],
esUtils:log_warnings(Msg);
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:log_errors(Msg).
esUtils:logErrors(Msg).
%% @private Print error messages in a pretty and user readable way.
@ -568,11 +493,12 @@ formatErrors(File, Errors, Warnings) ->
AllWarnings1 = lists:sort(lists:flatten([X || {_, X} <- Warnings])),
AllWarnings2 = [{Line, "Warning", Module, Description} || {Line, Module, Description} <- AllWarnings1],
Everything = lists:sort(AllErrors2 ++ AllWarnings2),
F = fun({Line, Prefix, Module, ErrorDescription}) ->
Msg = formatError(Module, ErrorDescription),
io_lib:format("~s:~p: ~s: ~s~n", [File, Line, Prefix, Msg])
end,
[F(X) || X <- Everything].
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
@ -580,36 +506,32 @@ formatError(Module, ErrorDescription) ->
false -> io_lib:format("~s", [ErrorDescription])
end.
recompileChangeHrlFile([{File, LastMod} | T1], [{File, LastMod} | T2], SrcFiles, Patching) ->
%% Hrl hasn't changed, do nothing...
recompileChangeHrlFile(T1, T2, SrcFiles, Patching);
recompileChangeHrlFile([{File, _} | T1], [{File, _} | T2], SrcFiles, Patching) ->
%% File has changed, recompile...
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, Patching) || SrcFile <- WhoInclude],
recompileChangeHrlFile(T1, T2, SrcFiles, Patching);
recompileChangeHrlFile([{File1, _LastMod1} | T1] = OldHrlFiles, [{File2, LastMod2} | T2] = NewHrlFiles, SrcFiles, Patching) ->
[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, Patching);
recompileChangeHrlFile(T1, NewHrlFiles, SrcFiles, SwSyncNode);
false ->
%% File is new, look for src that include it
WhoInclude = whoInclude(File2, SrcFiles),
[maybeRecompileSrcFile(SrcFile, LastMod2, Patching) || SrcFile <- WhoInclude],
recompileChangeHrlFile(OldHrlFiles, T2, SrcFiles, Patching)
[maybeRecompileSrcFile(SrcFile, LastMod2, SwSyncNode) || SrcFile <- WhoInclude],
recompileChangeHrlFile(OldHrlFiles, T2, SrcFiles, SwSyncNode)
end;
recompileChangeHrlFile([], [{File, LastMod} | T2], SrcFiles, Patching) ->
%% File is new, look for src that include it
recompileChangeHrlFile([], [{File, LastMod} | T2], SrcFiles, SwSyncNode) ->
WhoInclude = whoInclude(File, SrcFiles),
[maybeRecompileSrcFile(SrcFile, LastMod, Patching) || SrcFile <- WhoInclude],
recompileChangeHrlFile([], T2, SrcFiles, Patching);
recompileChangeHrlFile([{File1, _LastMod1} | T1], [], SrcFiles, Patching) ->
%% Rest of file(s) removed, warn and process next
[maybeRecompileSrcFile(SrcFile, LastMod, SwSyncNode) || SrcFile <- WhoInclude],
recompileChangeHrlFile([], T2, SrcFiles, SwSyncNode);
recompileChangeHrlFile([{File1, _LastMod1} | T1], [], SrcFiles, SwSyncNode) ->
warnDelHrlFiles(File1, SrcFiles),
recompileChangeHrlFile(T1, [], SrcFiles, Patching);
recompileChangeHrlFile(T1, [], SrcFiles, SwSyncNode);
recompileChangeHrlFile([], [], _, _) ->
%% Done
ok;
@ -621,17 +543,18 @@ warnDelHrlFiles(HrlFile, SrcFiles) ->
WhoInclude = whoInclude(HrlFile, SrcFiles),
case WhoInclude of
[] -> ok;
_ -> io:format(
"Warning. Deleted ~p file included in existing src files: ~p~n",
[filename:basename(HrlFile), lists:map(fun(File) -> filename:basename(File) end, WhoInclude)])
_ ->
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,
Pred =
fun(SrcFile) ->
{ok, Forms} = epp_dodger:parse_file(SrcFile),
isInclude(HrlFileBaseName, Forms)
end,
lists:filter(Pred, SrcFiles).
isInclude(_HrlFile, []) ->
@ -645,27 +568,23 @@ isInclude(HrlFile, [{tree, attribute, _, {attribute, _, [{_, _, IncludeFile}]}}
isInclude(HrlFile, [_SomeForm | Forms]) ->
isInclude(HrlFile, Forms).
collMods(Modules) ->
excludeMods(whitelistMods(Modules)).
filterCollMods(Modules) ->
excludeMods(onlyMods(Modules)).
whitelistMods(Modules) ->
case application:get_env(erlSync, whitelistMods) of
{ok, []} ->
onlyMods(Modules) ->
case ?esCfgSync:getv(?onlyMods) of
[] ->
Modules;
{ok, WhitelistMods} ->
[Mod || Mod <- Modules, checkModIsMatch(Mod, WhitelistMods) == true];
_ ->
Modules
OnlyMods ->
[Mod || Mod <- Modules, checkModIsMatch(OnlyMods, Mod) == true]
end.
excludeMods(Modules) ->
case application:get_env(erlSync, excludedMods) of
{ok, []} ->
case ?esCfgSync:getv(?excludedMods) of
[] ->
Modules;
{ok, ExcludedModules} ->
[Mod || Mod <- Modules, checkModIsMatch(Mod, ExcludedModules) == false];
_ ->
Modules
ExcludedModules ->
[Mod || Mod <- Modules, checkModIsMatch(ExcludedModules, Mod) == false]
end.
checkModIsMatch([], _Module) ->
@ -685,23 +604,17 @@ checkModIsMatch([ModOrPattern | T], Module) ->
checkModIsMatch(T, Module)
end.
collSrcDirs(State, ExtraDirs, ReplaceDirs) ->
%% Extract the compile / options / source / dir from each module.
F =
collSrcDirs(Modules, AddDirs, OnlyDirs) ->
FColl =
fun
(X, {SrcAcc, HrlAcc} = Acc) ->
%% Get the dir...
case esUtils:getSrcDirFromMod(X) of
(Mod, {SrcAcc, HrlAcc} = Acc) ->
case esUtils:getModSrcDir(Mod) of
{ok, SrcDir} ->
case isReplaceDir(SrcDir, ReplaceDirs) of
case isOnlyDir(OnlyDirs, SrcDir) of
true ->
%% Get the options, storse under the dir...
{ok, Options} = esUtils:getOptionsFromMod(X),
%% Store the options for later reference...
{ok, Options} = esUtils:getModOptions(Mod),
HrlDir = proplists:get_all_values(i, Options),
setOptions(SrcDir, Options),
%% Return the dir...
{[SrcDir | SrcAcc], HrlDir ++ HrlAcc};
_ ->
Acc
@ -710,28 +623,34 @@ collSrcDirs(State, ExtraDirs, ReplaceDirs) ->
Acc
end
end,
{SrcDirs, HrlDirs} = lists:foldl(F, {ExtraDirs, []}, State#state.modules),
{SrcDirs, HrlDirs} = lists:foldl(FColl, {AddDirs, []}, Modules),
USortedSrcDirs = lists:usort(SrcDirs),
USortedHrlDirs = lists:usort(HrlDirs),
%% InitialDirs = sync_utils:initial_src_dirs(),
%% Return with updated dirs...
{USortedSrcDirs, USortedHrlDirs}.
isReplaceDir(_, []) ->
isOnlyDir([], _) ->
true;
isReplaceDir(SrcDir, ReplaceDirs) ->
lists:foldl(
fun
(Dir, false) ->
case re:run(SrcDir, Dir) of
nomatch -> false;
_ -> true
end;
(_, Acc) -> Acc
end, false, ReplaceDirs).
%% ***********************************misc fun start *******************************************
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 ->
@ -749,11 +668,8 @@ setOptions(SrcDir, Options) ->
erlang:put(SrcDir, NewOptions)
end.
startup() ->
io:format("Growl notifications disabled~n").
loadCfg() ->
KVs = [{Key, esUtils:getEnv(Key, DefVal)} || {Key, DefVal} <- ?CfgList],
esUtils:load(?esCfgSync, KVs).
%% ***********************************misc fun end *********************************************
%% ***********************************PRIVATE FUNCTIONS end *********************************************

+ 48
- 103
src/sync/esUtils.erl View File

@ -1,23 +1,25 @@
-module(esUtils).
-include("erlSync.hrl").
-compile(inline).
-compile({inline_size, 128}).
-export([
getSrcDirFromMod/1,
getOptionsFromMod/1,
getModSrcDir/1,
getModOptions/1,
getFileType/1,
getSrcDir/1,
wildcard/2,
getEnv/2,
setEnv/2,
load/2,
logSuccess/1,
logErrors/1,
logWarnings/1,
getSystemModules/0
]).
-compile([export_all, nowarn_export_all]).
getSrcDirFromMod(Module) ->
getModSrcDir(Module) ->
case code:is_loaded(Module) of
{file, _} ->
try
@ -27,17 +29,15 @@ getSrcDirFromMod(Module) ->
%% 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),
NonDescendant = getEnv(non_descendant, fix),
Descendant = ?esCfgSync:getv(?descendant),
LastSource =
case {IsFile, IsDescendant, NonDescendant} of
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 file, but is a descendant, file is deleted, nothing we can do
{false, true, _} -> undefined;
%% is not a descendant, and we fix non-descendants, so let's fix it
{_, false, fix} -> fixDescendantSource(Source, IsFile);
{_, false, fix} -> fixDescendantSource(Source);
%% Anything else, and we don't know what to do, so let's just bail.
_ -> undefined
end,
@ -56,7 +56,7 @@ getSrcDirFromMod(Module) ->
undefined
end.
getOptionsFromMod(Module) ->
getModOptions(Module) ->
case code:is_loaded(Module) of
{file, _} ->
try
@ -75,7 +75,7 @@ getOptionsFromMod(Module) ->
{ok, Options6}
catch ExType:Error:Stacktrace ->
Msg = [io_lib:format("~p:0: ~p looking for options: ~p. Stack: ~p~n", [Module, ExType, Error, Stacktrace])],
log_warnings(Msg),
logWarnings(Msg),
{ok, []}
end;
_ ->
@ -95,7 +95,7 @@ transformAllIncludes(Module, BeamDir, Options) ->
[transformInclude(Module, BeamDir, Opt) || Opt <- Options].
transformInclude(Module, BeamDir, {i, IncludeDir}) ->
{ok, SrcDir} = getSrcDirFromMod(Module),
{ok, SrcDir} = getModSrcDir(Module),
{ok, IncludeDir2} = determineIncludeDir(IncludeDir, BeamDir, SrcDir),
{i, IncludeDir2};
transformInclude(_, _, Other) ->
@ -115,7 +115,7 @@ addFileType(Module, Options) ->
Type = getFileType(Module),
[{type, Type} | Options].
%% @private This will check if the given module or source file is an ErlyDTL template.
%% 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) ->
@ -135,7 +135,7 @@ getFileType(Source) when is_list(Source) ->
".ex" -> elixir
end.
%% @private This will search back to find an appropriate include directory, by
%% 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
@ -146,17 +146,16 @@ determineIncludeDir(IncludeDir, BeamDir, SrcDir) ->
{ok, D} -> {ok, D};
undefined ->
{ok, Cwd} = file:get_cwd(),
Cwd2 = normalizeCaseWindowsDir(Cwd),
SrcDir2 = normalizeCaseWindowsDir(SrcDir),
IncludeBase2 = normalizeCaseWindowsDir(IncludeBase),
case findIncludeDirFromAncestors(Cwd2, IncludeBase2, SrcDir2) of
% Cwd2 = normalizeCaseWindowsDir(Cwd),
% SrcDir2 = normalizeCaseWindowsDir(SrcDir),
% IncludeBase2 = normalizeCaseWindowsDir(IncludeBase),
case findIncludeDirFromAncestors(Cwd, IncludeBase, SrcDir) of
{ok, D} -> {ok, D};
undefined -> {ok, IncludeDir} %% Failed, just stick with original
end
end.
%% @private First try to see if we have an include file alongside our ebin
%directory, which is typically the case
%% First try to see if we have an include file alongside our ebin directory, which is typically the case
determineIncludeDirFromBeamDir(IncludeBase, BeamDir) ->
BeamBasedIncDir = filename:join(filename:dirname(BeamDir), IncludeBase),
case filelib:is_dir(BeamBasedIncDir) of
@ -164,8 +163,7 @@ determineIncludeDirFromBeamDir(IncludeBase, BeamDir) ->
false -> undefined
end.
%% @private Then we dig back through the parent directories until we find our
%include directory
%% Then we dig back through the parent directories until we find our include directory
findIncludeDirFromAncestors(Cwd, _, Cwd) -> undefined;
findIncludeDirFromAncestors(_, _, "/") -> undefined;
findIncludeDirFromAncestors(_, _, ".") -> undefined;
@ -179,26 +177,26 @@ findIncludeDirFromAncestors(Cwd, IncludeBase, Dir) ->
findIncludeDirFromAncestors(Cwd, IncludeBase, filename:dirname(Dir))
end.
normalizeCaseWindowsDir(Dir) ->
case os:type() of
{win32, _} -> string:to_lower(Dir);
{unix, _} -> Dir
end.
% normalizeCaseWindowsDir(Dir) ->
% case os:type() of
% {win32, _} -> Dir; %string:to_lower(Dir);
% {unix, _} -> Dir
% end.
%% @private This is an attempt to intelligently fix paths in modules when a
%% 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) ->
fixDescendantSource([]) ->
undefined;
fixDescendantSource(Path, IsFile) ->
PathParts = filename:split(Path),
fixDescendantSource(Path) ->
{ok, Cwd} = file:get_cwd(),
PathParts = filename:split(Path),
case makeDescendantSource(Cwd, PathParts) of
undefined -> useOriginalIfExists(Path, IsFile);
undefined -> Path;
FoundPath -> FoundPath
end.
@ -211,14 +209,6 @@ makeDescendantSource(Cwd, [_ | T]) ->
false -> makeDescendantSource(Cwd, T)
end.
useOriginalIfExists(Path, IsFile) ->
case IsFile of
true -> Path;
false -> undefined
end.
%% @private returns true if the provided path is a descendant of the current
%% working directory.
isDescendent(Path) ->
{ok, Cwd} = file:get_cwd(),
lists:sublist(Path, length(Cwd)) == Cwd.
@ -240,11 +230,10 @@ getSrcDir(Dir, Ctr) ->
true -> getSrcDir(filename:dirname(Dir), Ctr - 1)
end.
%% @private Return all files in a directory matching a regex.
%% Return all files in a directory matching a regex.
wildcard(Dir, Regex) ->
filelib:fold_files(Dir, Regex, true, fun(Y, Acc) -> [Y | Acc] end, []).
%% @private Get an environment variable.
getEnv(Var, Default) ->
case application:get_env(erlSync, Var) of
{ok, Value} ->
@ -253,26 +242,19 @@ getEnv(Var, Default) ->
Default
end.
%% @private Set a erlSync environment variable.
setEnv(Var, Val) ->
ok = application:set_env(erlSync, Var, Val).
log_success(Message) ->
can_we_log(success)
andalso error_logger:info_msg(lists:flatten(Message)).
log_errors(Message) ->
can_we_log(errors)
andalso error_logger:error_msg(lists:flatten(Message)).
logSuccess(Message) ->
canLog(success) andalso error_logger:info_msg(lists:flatten(Message)).
log_warnings(Message) ->
can_we_log(warnings)
andalso error_logger:warning_msg(lists:flatten(Message)).
logErrors(Message) ->
canLog(errors) andalso error_logger:error_msg(lists:flatten(Message)).
can_we_log(MsgType) ->
can_we_notify(log, MsgType).
logWarnings(Message) ->
canLog(warnings) andalso error_logger:warning_msg(lists:flatten(Message)).
can_we_notify(_GrowlOrLog, MsgType) ->
canLog(MsgType) ->
case esScanner:getLog() of
true -> true;
all -> true;
@ -283,61 +265,24 @@ can_we_notify(_GrowlOrLog, MsgType) ->
_ -> false
end.
%% @private Return a list of all modules that belong to Erlang rather
%% than whatever application we may be running.
%% 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
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
],
F =
FAppMod =
fun(App) ->
case application:get_key(App, modules) of
{ok, Modules} -> Modules;
_Other -> []
end
end,
lists:flatten([F(X) || X <- Apps]).
lists:flatten([FAppMod(X) || X <- Apps]).
%% map类型的数据不能当做key
-type key() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple().

Loading…
Cancel
Save