-module(esSyncSrv). -behaviour(es_gen_ipc). %%%%%%%%%%%%%%%%%%%%%%%% eSync.hrl start %%%%%%%%%%%%%%%%%%%%%% -define(LOG_ON(Val), Val == true; Val == all; Val == skip_success; is_list(Val), Val =/= []). -define(Log, log). -define(compileCmd, compileCmd). -define(extraDirs, extraDirs). -define(descendant, descendant). -define(onMSyncFun, onMSyncFun). -define(onCSyncFun, onCSyncFun). -define(swSyncNode, swSyncNode). -define(isJustMem, isJustMem). -define(DefCfgList, [{?Log, all}, {?compileCmd, undefined}, {?extraDirs, undefined}, {?descendant, fix}, {?onMSyncFun, undefined}, {?onCSyncFun, undefined}, {?swSyncNode, false}, {?isJustMem, false}]). -define(esCfgSync, esCfgSync). -define(rootSrcDir, <<"src">>). %%%%%%%%%%%%%%%%%%%%%%%% eSync.hrl end %%%%%%%%%%%%%%%%%%%%%% -compile(inline). -compile({inline_size, 128}). %% API -export([ start_link/0, rescan/0, pause/0, unpause/0, setLog/1, getLog/0, curInfo/0, getOnMSync/0, setOnMSync/1, getOnCSync/0, setOnCSync/1, swSyncNode/1 ]). %% es_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() , hrlFiles = #{} :: map() , configs = #{} :: map() , beams = #{} :: map() , onMSyncFun = undefined , onCSyncFun = undefined , swSyncNode = false , port = undefined }). %% ************************************ API start *************************** rescan() -> es_gen_ipc:cast(?SERVER, miRescan), esUtils:logSuccess("start rescaning source files..."), ok. unpause() -> es_gen_ipc:cast(?SERVER, miUnpause), ok. pause() -> es_gen_ipc:cast(?SERVER, miPause), esUtils:logSuccess("Pausing eSync. Call eSync:run() to restart"), ok. curInfo() -> es_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) -> es_gen_ipc:cast(?SERVER, {miSyncNode, IsSync}), ok. getOnMSync() -> es_gen_ipc:call(?SERVER, miGetOnMSync). setOnMSync(Fun) -> es_gen_ipc:call(?SERVER, {miSetOnMSync, Fun}). getOnCSync() -> es_gen_ipc:call(?SERVER, miGetOnCSync). setOnCSync(Fun) -> es_gen_ipc:call(?SERVER, {miSetOnCSync, Fun}). %% ************************************ API end *************************** start_link() -> es_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{onMSyncFun = ?esCfgSync:getv(?onMSyncFun), onCSyncFun = ?esCfgSync:getv(?onCSyncFun), swSyncNode = ?esCfgSync:getv(?swSyncNode)}, {doAfter, ?None}}. handleAfter(?None, waiting, State) -> %% 启动port 发送监听目录信息 PortName = esUtils:fileSyncPath("fileSync"), Opts = [{packet, 4}, binary, exit_status, use_stdio], Port = erlang:open_port({spawn_executable, PortName}, Opts), {kpS, State#state{port = Port}, {sTimeout, 4000, waitConnOver}}. handleCall(miGetOnMSync, _, #state{onMSyncFun = OnMSyncFun} = State, _From) -> {reply, OnMSyncFun, State}; handleCall({miSetOnMSync, Fun}, _, State, _From) -> {reply, ok, State#state{onMSyncFun = Fun}}; handleCall(miGetOnCSync, _, #state{onCSyncFun = OnCSyncFun} = State, _From) -> {reply, OnCSyncFun, State}; handleCall({miSetOnCSync, Fun}, _, State, _From) -> {reply, ok, State#state{onCSyncFun = Fun}}; handleCall(miCurInfo, Status, State, _Form) -> {reply, {Status, erlang:get(), State}, State}; handleCall(_Request, _, _State, _From) -> kpS_S. handleCast(miPause, running, State) -> {nextS, pause, State}; handleCast(miUnpause, pause, 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) -> {Srcs, Hrls, Configs, Beams} = esUtils:collSrcFiles(false), {kpS_S, State#state{srcFiles = Srcs, hrlFiles = Hrls, configs = Configs, beams = Beams}}; handleCast(_Msg, _, _State) -> kpS_S. handleInfo({Port, {data, Data}}, Status, #state{srcFiles = Srcs, hrlFiles = Hrls, configs = Configs, beams = Beams, onMSyncFun = OnMSyncFun, onCSyncFun = OnCSyncFun, swSyncNode = SwSyncNode} = State) -> case Status of running -> FileList = binary:split(Data, <<"\r\n">>, [global]), %% 收集改动了beam hrl src 文件 然后执行相应的逻辑 {CBeams, CConfigs, CHrls, CSrcs, NewSrcs, NewHrls, NewConfigs, NewBeams} = esUtils:classifyChangeFile(FileList, [], [], #{}, #{}, Srcs, Hrls, Configs, Beams), esUtils:fireOnSync(OnCSyncFun, CConfigs), esUtils:reloadChangedMod(CBeams, SwSyncNode, OnMSyncFun, []), case ?esCfgSync:getv(?compileCmd) of undefined -> LastCHrls = esUtils:collIncludeCHrls(maps:keys(CHrls), NewHrls, CHrls, #{}), NReSrcs = esUtils:collIncludeCErls(maps:keys(LastCHrls), NewSrcs, CSrcs, #{}), esUtils:recompileChangeSrcFile(maps:iterator(NReSrcs), SwSyncNode), {kpS, State#state{srcFiles = NewSrcs, hrlFiles = NewHrls, configs = NewConfigs, beams = NewBeams}}; CmdStr -> case maps:size(CSrcs) > 0 orelse CHrls =/= [] of true -> RetStr = os:cmd(CmdStr), RetList = string:split(RetStr, "\n", all), esUtils:logSuccess("compile cmd:~p ~n", [CmdStr]), esUtils:logSuccess("the result: ~n ", []), [ begin esUtils:logSuccess("~p ~n", [OneRet]) end || OneRet <- RetList, OneRet =/= [] ], ok; _ -> ignore end, kpS_S end; _ -> case Data of <<"init">> -> %% port启动成功 先发送监听目录配置 {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"), erlang:port_command(Port, AllStr), esUtils:logSuccess("eSync connect fileSync success..."), %% 然后收集一下监听目录下的src文件 {BSrcs, BHrls, BConfigs, BBeams} = esUtils:collSrcFiles(true), {nextS, running, State#state{srcFiles = BSrcs, hrlFiles = BHrls, configs = BConfigs, beams = BBeams}}; _ -> esUtils:logErrors("error, esSyncSrv receive unexpect port msg ~p~n", [Data]), kpS_S end end; handleInfo({Port, closed}, running, #state{port = Port} = _State) -> esUtils:logErrors("esSyncSrv receive port closed ~n"), {nextS, port_close, _State}; handleInfo({'EXIT', Port, Reason}, running, #state{port = Port} = _State) -> esUtils:logErrors("esSyncSrv receive port exit Reason:~p ~n", [Reason]), {nextS, {port_EXIT, Reason}, _State}; handleInfo({Port, {exit_status, Status}}, running, #state{port = Port} = _State) -> esUtils:logErrors("esSyncSrv receive port exit_status Status:~p ~p ~n", [Status, Port]), {nextS, {port_exit_status, Status}, _State}; handleInfo({'EXIT', _Pid, _Reason}, running, _State) -> kpS_S; handleInfo(_Msg, _, _State) -> esUtils:logErrors("esSyncSrv receive unexpect msg:~p ~n", [_Msg]), kpS_S. handleOnevent(sTimeout, waitConnOver, Status, State) -> esUtils:logErrors("failed to connect the fileSync to stop stauts:~p state:~p ~n", [Status, State]), stop; handleOnevent(_EventType, _EventContent, _Status, _State) -> kpS_S. terminate(_Reason, _Status, _State) -> ok.