erlang自动编译与加载
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

691 рядки
25 KiB

-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,
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
}).
%% ************************************ 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 :: 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, running, #state{}}.
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) ->
keepStatusState.
handleCast(miPause, _, State) ->
{nextStatus, pause, State};
handleCast(miUnpause, _, State) ->
{nextStatus, running, State};
handleCast(miCollMods, running, State) ->
AllModules = (erlang:loaded() -- esUtils:getSystemModules()),
LastCollMods = filterCollMods(AllModules),
Time = ?esCfgSync:getv(?moduleTime),
{keepStatus, 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),
{keepStatus, 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),
{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{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),
{keepStatus, 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),
{keepStatus, State#state{hrlFileTimes = HrlFileTimeList}, [?gTimeout(miCompareHrlFiles, Time)]};
handleCast({miSyncNode, IsSync}, _, State) ->
case IsSync of
true ->
{keepStatus, State#state{swSyncNode = true}};
_ ->
{keepStatus, State#state{swSyncNode = false}}
end;
handleCast(_Msg, _, _State) ->
keepStatusState.
handleInfo(_Msg, _, _State) ->
keepStatusState.
handleOnevent({gTimeout, _}, Msg, Status, State) ->
handleCast(Msg, Status, State);
handleOnevent(_EventType, _EventContent, _Status, _State) ->
keepStatusState.
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 *********************************************