erlang自动编译与加载
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1298 lines
50 KiB

5 년 전
  1. -module(eSync).
  2. -behaviour(es_gen_ipc).
  3. -compile(inline).
  4. -compile({inline_size, 128}).
  5. -define(IIF(Cond, Ret1, Ret2), (case Cond of true -> Ret1; _ -> Ret2 end)).
  6. -define(LOG_ON(Val), Val == true; Val == all; Val == skip_success; is_list(Val), Val =/= []).
  7. -define(Log, log).
  8. -define(compileCmd, compileCmd).
  9. -define(extraDirs, extraDirs).
  10. -define(descendant, descendant).
  11. -define(onMSyncFun, onMSyncFun).
  12. -define(onCSyncFun, onCSyncFun).
  13. -define(swSyncNode, swSyncNode).
  14. -define(isJustMem, isJustMem).
  15. -define(debugInfoKeyFun, debugInfoKeyFun).
  16. -define(DefCfgList, [{?Log, all}, {?compileCmd, undefined}, {?extraDirs, undefined}, {?descendant, fix}, {?onMSyncFun, undefined}, {?onCSyncFun, undefined}, {?swSyncNode, false}, {?isJustMem, false}, {?debugInfoKeyFun, undefined}]).
  17. -define(esCfgSync, esCfgSync).
  18. -define(rootSrcDir, <<"src">>).
  19. -define(logSuccess(Format), canLog(success) andalso error_logger:info_msg("eSync[~p:~p|~p] " ++ Format, [?MODULE, ?FUNCTION_NAME, ?LINE])).
  20. -define(logSuccess(Format, Args), canLog(success) andalso error_logger:info_msg("eSync[~p:~p|~p] " ++ Format, [?MODULE, ?FUNCTION_NAME, ?LINE] ++ Args)).
  21. -define(logErrors(Format), canLog(errors) andalso error_logger:info_msg("eSync[~p:~p|~p] " ++ Format, [?MODULE, ?FUNCTION_NAME, ?LINE])).
  22. -define(logErrors(Format, Args), canLog(errors) andalso error_logger:info_msg("eSync[~p:~p|~p] " ++ Format, [?MODULE, ?FUNCTION_NAME, ?LINE] ++ Args)).
  23. -define(logWarnings(Format), canLog(warnings) andalso error_logger:info_msg("eSync[~p:~p|~p] " ++ Format, [?MODULE, ?FUNCTION_NAME, ?LINE])).
  24. -define(logWarnings(Format, Args), canLog(warnings) andalso error_logger:info_msg("eSync[~p:~p|~p] " ++ Format, [?MODULE, ?FUNCTION_NAME, ?LINE] ++ Args)).
  25. -export([
  26. start/2,
  27. start/0,
  28. stop/0,
  29. run/0
  30. ]).
  31. %% API
  32. -export([
  33. start_link/0,
  34. rescan/0,
  35. pause/0,
  36. unpause/0,
  37. setLog/1,
  38. getLog/0,
  39. curInfo/0,
  40. getOnMSync/0,
  41. setOnMSync/1,
  42. getOnCSync/0,
  43. setOnCSync/1,
  44. swSyncNode/1
  45. ]).
  46. %% es_gen_ipc callbacks
  47. -export([
  48. init/1,
  49. handleCall/4,
  50. handleAfter/3,
  51. handleCast/3,
  52. handleInfo/3,
  53. handleOnevent/4,
  54. terminate/3
  55. ]).
  56. start(_StartType, _StartArgs) ->
  57. start_link().
  58. start() ->
  59. application:ensure_all_started(eSync).
  60. stop() ->
  61. application:stop(eSync).
  62. run() ->
  63. case start() of
  64. {ok, _Started} ->
  65. unpause(),
  66. ok;
  67. {error, Reason} ->
  68. ?logErrors("start eSync error:~p~n", [Reason])
  69. end.
  70. -define(SERVER, ?MODULE).
  71. -define(None, 0).
  72. -record(state, {
  73. port = undefined
  74. , onMSyncFun = undefined
  75. , onCSyncFun = undefined
  76. , swSyncNode = false
  77. , srcFiles = #{} :: map()
  78. , hrlFiles = #{} :: map()
  79. , configs = #{} :: map()
  80. , beams = #{} :: map()
  81. }).
  82. %% ************************************ API start ***************************
  83. rescan() ->
  84. es_gen_ipc:cast(?SERVER, miRescan),
  85. ?logSuccess("start rescaning source files..."),
  86. ok.
  87. unpause() ->
  88. es_gen_ipc:cast(?SERVER, miUnpause),
  89. ok.
  90. pause() ->
  91. es_gen_ipc:cast(?SERVER, miPause),
  92. ?logSuccess("Pausing eSync. Call eSync:run() to restart"),
  93. ok.
  94. curInfo() ->
  95. es_gen_ipc:call(?SERVER, miCurInfo).
  96. setLog(T) when ?LOG_ON(T) ->
  97. setEnv(log, T),
  98. loadCfg(),
  99. ?logSuccess("Console Notifications Enabled"),
  100. ok;
  101. setLog(_) ->
  102. setEnv(log, none),
  103. loadCfg(),
  104. ?logSuccess("Console Notifications Disabled"),
  105. ok.
  106. getLog() ->
  107. ?esCfgSync:getv(log).
  108. swSyncNode(IsSync) ->
  109. es_gen_ipc:cast(?SERVER, {miSyncNode, IsSync}),
  110. ok.
  111. getOnMSync() ->
  112. es_gen_ipc:call(?SERVER, miGetOnMSync).
  113. setOnMSync(Fun) ->
  114. es_gen_ipc:call(?SERVER, {miSetOnMSync, Fun}).
  115. getOnCSync() ->
  116. es_gen_ipc:call(?SERVER, miGetOnCSync).
  117. setOnCSync(Fun) ->
  118. es_gen_ipc:call(?SERVER, {miSetOnCSync, Fun}).
  119. %% ************************************ API end ***************************
  120. start_link() ->
  121. es_gen_ipc:start_link({local, ?SERVER}, ?MODULE, ?None, []).
  122. %% status :: waiting | running | pause
  123. init(_Args) ->
  124. erlang:process_flag(trap_exit, true),
  125. loadCfg(),
  126. {ok, waiting, #state{onMSyncFun = ?esCfgSync:getv(?onMSyncFun), onCSyncFun = ?esCfgSync:getv(?onCSyncFun), swSyncNode = ?esCfgSync:getv(?swSyncNode)}, {doAfter, ?None}}.
  127. handleAfter(?None, waiting, State) ->
  128. %% 启动port 发送监听目录信息
  129. PortName = fileSyncPath("fileSync"),
  130. Opts = [{packet, 4}, binary, exit_status, use_stdio],
  131. Port = erlang:open_port({spawn_executable, PortName}, Opts),
  132. {kpS, State#state{port = Port}, {sTimeout, 4000, waitConnOver}}.
  133. handleCall(miGetOnMSync, _, #state{onMSyncFun = OnMSyncFun} = State, _From) ->
  134. {reply, OnMSyncFun, State};
  135. handleCall({miSetOnMSync, Fun}, _, State, _From) ->
  136. {reply, ok, State#state{onMSyncFun = Fun}};
  137. handleCall(miGetOnCSync, _, #state{onCSyncFun = OnCSyncFun} = State, _From) ->
  138. {reply, OnCSyncFun, State};
  139. handleCall({miSetOnCSync, Fun}, _, State, _From) ->
  140. {reply, ok, State#state{onCSyncFun = Fun}};
  141. handleCall(miCurInfo, Status, State, _Form) ->
  142. {reply, {Status, erlang:get(), State}, State};
  143. handleCall(_Request, _, _State, _From) ->
  144. kpS_S.
  145. handleCast(miPause, running, State) ->
  146. {nextS, pause, State};
  147. handleCast(miUnpause, pause, State) ->
  148. {nextS, running, State};
  149. handleCast({miSyncNode, IsSync}, _, State) ->
  150. case IsSync of
  151. true ->
  152. {kpS, State#state{swSyncNode = true}};
  153. _ ->
  154. {kpS, State#state{swSyncNode = false}}
  155. end;
  156. handleCast(miRescan, _, State) ->
  157. {Srcs, Hrls, Configs, Beams} = collSrcFiles(false),
  158. {kpS_S, State#state{srcFiles = Srcs, hrlFiles = Hrls, configs = Configs, beams = Beams}};
  159. handleCast(_Msg, _, _State) ->
  160. kpS_S.
  161. handleInfo({Port, {data, Data}}, Status, #state{srcFiles = Srcs, hrlFiles = Hrls, configs = Configs, beams = Beams, onMSyncFun = OnMSyncFun, onCSyncFun = OnCSyncFun, swSyncNode = SwSyncNode} = State) ->
  162. case Status of
  163. running ->
  164. FileList = binary:split(Data, <<"\r\n">>, [global]),
  165. %% 收集改动了beam hrl src 文件 然后执行相应的逻辑
  166. {CBeams, CConfigs, CHrls, CSrcs, NewSrcs, NewHrls, NewConfigs, NewBeams} = classifyChangeFile(FileList, [], [], #{}, #{}, Srcs, Hrls, Configs, Beams),
  167. fireOnSync(OnCSyncFun, CConfigs),
  168. reloadChangedMod(CBeams, SwSyncNode, OnMSyncFun, []),
  169. case ?esCfgSync:getv(?compileCmd) of
  170. undefined ->
  171. LastCHrls = collIncludeCHrls(maps:keys(CHrls), NewHrls, CHrls, #{}),
  172. NReSrcs = collIncludeCErls(maps:keys(LastCHrls), NewSrcs, CSrcs, #{}),
  173. recompileChangeSrcFile(maps:iterator(NReSrcs), SwSyncNode),
  174. {kpS, State#state{srcFiles = NewSrcs, hrlFiles = NewHrls, configs = NewConfigs, beams = NewBeams}};
  175. CmdStr ->
  176. case maps:size(CSrcs) > 0 orelse CHrls =/= [] of
  177. true ->
  178. RetStr = os:cmd(CmdStr),
  179. RetList = string:split(RetStr, "\n", all),
  180. ?logSuccess("compile cmd:~p ~n", [CmdStr]),
  181. ?logSuccess("the result: ~n ", []),
  182. [
  183. begin
  184. ?logSuccess("~p ~n", [OneRet])
  185. end || OneRet <- RetList, OneRet =/= []
  186. ],
  187. ok;
  188. _ ->
  189. ignore
  190. end,
  191. kpS_S
  192. end;
  193. _ ->
  194. case Data of
  195. <<"init">> ->
  196. %% port启动成功 先发送监听目录配置
  197. {AddExtraSrcDirs, AddOnlySrcDirs, OnlySrcDirs, DelSrcDirs} = mergeExtraDirs(false),
  198. AddExtraStr = string:join([filename:nativename(OneDir) || OneDir <- AddExtraSrcDirs], "|"),
  199. AddOnlyStr = string:join([filename:nativename(OneDir) || OneDir <- AddOnlySrcDirs], "|"),
  200. OnlyStr = string:join([filename:nativename(OneDir) || OneDir <- OnlySrcDirs], "|"),
  201. DelStr = string:join([filename:nativename(OneDir) || OneDir <- DelSrcDirs], "|"),
  202. AllStr = string:join([AddExtraStr, AddOnlyStr, OnlyStr, DelStr], "\r\n"),
  203. erlang:port_command(Port, AllStr),
  204. ?logSuccess("eSync connect fileSync success..."),
  205. %% 然后收集一下监听目录下的src文件
  206. {BSrcs, BHrls, BConfigs, BBeams} = collSrcFiles(true),
  207. {nextS, running, State#state{srcFiles = BSrcs, hrlFiles = BHrls, configs = BConfigs, beams = BBeams}, {isHib, true}};
  208. _ ->
  209. ?logErrors("error, receive unexpect port msg ~p~n", [Data]),
  210. kpS_S
  211. end
  212. end;
  213. handleInfo({Port, closed}, running, #state{port = Port} = _State) ->
  214. ?logErrors("receive port closed ~n"),
  215. {nextS, port_close, _State};
  216. handleInfo({'EXIT', Port, Reason}, running, #state{port = Port} = _State) ->
  217. ?logErrors("receive port exit Reason:~p ~n", [Reason]),
  218. {nextS, {port_EXIT, Reason}, _State};
  219. handleInfo({Port, {exit_status, Status}}, running, #state{port = Port} = _State) ->
  220. ?logErrors("receive port exit_status Status:~p ~p ~n", [Status, Port]),
  221. {nextS, {port_exit_status, Status}, _State};
  222. handleInfo({'EXIT', _Pid, _Reason}, running, _State) ->
  223. kpS_S;
  224. handleInfo(_Msg, _, _State) ->
  225. ?logErrors("receive unexpect msg:~p ~n", [_Msg]),
  226. kpS_S.
  227. handleOnevent(sTimeout, waitConnOver, Status, State) ->
  228. ?logErrors("failed to connect the fileSync to stop stauts:~p state:~p ~n", [Status, State]),
  229. stop;
  230. handleOnevent(_EventType, _EventContent, _Status, _State) ->
  231. kpS_S.
  232. terminate(_Reason, _Status, _State) ->
  233. ok.
  234. %% ************************************************* utils start *******************************************************
  235. getModSrcDir(Module) ->
  236. case code:is_loaded(Module) of
  237. {file, _} ->
  238. try
  239. %% Get some module info...
  240. Props = Module:module_info(compile),
  241. Source = proplists:get_value(source, Props, ""),
  242. %% Ensure that the file exists, is a decendent of the tree, and how to deal with that
  243. IsFile = filelib:is_regular(Source),
  244. IsDescendant = isDescendent(Source),
  245. Descendant = ?esCfgSync:getv(?descendant),
  246. LastSource =
  247. case {IsFile, IsDescendant, Descendant} of
  248. %% is file and descendant, we're good to go
  249. {true, true, _} -> Source;
  250. %% is not a descendant, but we allow them, so good to go
  251. {true, false, allow} -> Source;
  252. %% is not a descendant, and we fix non-descendants, so let's fix it
  253. {_, false, fix} -> fixDescendantSource(Source, IsFile);
  254. %% Anything else, and we don't know what to do, so let's just bail.
  255. _ -> undefined
  256. end,
  257. case LastSource of
  258. undefined ->
  259. undefined;
  260. _ ->
  261. %% Get the source dir...
  262. Dir = filename:dirname(LastSource),
  263. getSrcDir(Dir)
  264. end
  265. catch _ : _ ->
  266. undefined
  267. end;
  268. _ ->
  269. undefined
  270. end.
  271. getModOpts(Module) ->
  272. case code:is_loaded(Module) of
  273. {file, _} ->
  274. try
  275. Props = Module:module_info(compile),
  276. BeamDir = filename:dirname(code:which(Module)),
  277. Options1 = proplists:get_value(options, Props, []),
  278. %% transform `outdir'
  279. Options2 = transformOutdir(BeamDir, Options1),
  280. Options3 = ensureInclude(Options2),
  281. %% transform the include directories
  282. Options4 = transformAllIncludes(Module, BeamDir, Options3),
  283. %% maybe_add_compile_info
  284. Options5 = maybeAddCompileInfo(Options4),
  285. %% add filetype to options (DTL, LFE, erl, etc)
  286. Options6 = addFileType(Module, Options5),
  287. Options7 = lists:keyreplace(debug_info_key, 1, Options6, debugInfoKeyFun()),
  288. {ok, Options7}
  289. catch ExType:Error ->
  290. ?logWarnings("~p:0: ~p looking for options: ~p. ~n", [Module, ExType, Error]),
  291. undefined
  292. end;
  293. _ ->
  294. undefined
  295. end.
  296. tryGetModOpts(Module) ->
  297. try
  298. Props = Module:module_info(compile),
  299. BeamDir = filename:dirname(code:which(Module)),
  300. Options1 = proplists:get_value(options, Props, []),
  301. %% transform `outdir'
  302. Options2 = transformOutdir(BeamDir, Options1),
  303. Options3 = ensureInclude(Options2),
  304. %% transform the include directories
  305. Options4 = transformAllIncludes(Module, BeamDir, Options3),
  306. %% maybe_add_compile_info
  307. Options5 = maybeAddCompileInfo(Options4),
  308. %% add filetype to options (DTL, LFE, erl, etc)
  309. Options6 = addFileType(Module, Options5),
  310. Options7 = lists:keyreplace(debug_info_key, 1, Options6, debugInfoKeyFun()),
  311. {ok, Options7}
  312. catch _ExType:_Error ->
  313. undefiend
  314. end.
  315. tryGetSrcOpts(SrcDir) ->
  316. %% Then we dig back through the parent directories until we find our include directory
  317. NewDirName = filename:dirname(SrcDir),
  318. case getOptions(NewDirName) of
  319. {ok, _Options} = Opts ->
  320. Opts;
  321. _ ->
  322. BaseName = filename:basename(SrcDir),
  323. IsBaseSrcDir = BaseName == ?rootSrcDir,
  324. case NewDirName =/= SrcDir andalso not IsBaseSrcDir of
  325. true ->
  326. tryGetSrcOpts(NewDirName);
  327. _ when IsBaseSrcDir ->
  328. try filelib:fold_files(SrcDir, ".*\\.(erl|dtl|lfe|ex)$", true,
  329. fun(OneFile, Acc) ->
  330. Mod = binary_to_atom(filename:basename(OneFile, filename:extension(OneFile))),
  331. case tryGetModOpts(Mod) of
  332. {ok, _Options} = Opts ->
  333. throw(Opts);
  334. _ ->
  335. Acc
  336. end
  337. end, undefined)
  338. catch
  339. {ok, _Options} = Opts ->
  340. Opts;
  341. _ExType:_Error ->
  342. ?logWarnings("looking src options error ~p:~p. ~n", [_ExType, _Error]),
  343. undefined
  344. end;
  345. _ ->
  346. undefined
  347. end
  348. end.
  349. transformOutdir(BeamDir, Options) ->
  350. [{outdir, BeamDir} | proplists:delete(outdir, Options)].
  351. ensureInclude(Options) ->
  352. case proplists:get_value(i, Options) of
  353. undefined -> [{i, "include"} | Options];
  354. _ -> Options
  355. end.
  356. transformAllIncludes(Module, BeamDir, Options) ->
  357. [begin
  358. case Opt of
  359. {i, IncludeDir} ->
  360. {ok, SrcDir} = getModSrcDir(Module),
  361. {ok, IncludeDir2} = determineIncludeDir(IncludeDir, BeamDir, SrcDir),
  362. {i, IncludeDir2};
  363. _ ->
  364. Opt
  365. end
  366. end || Opt <- Options].
  367. maybeAddCompileInfo(Options) ->
  368. case lists:member(compile_info, Options) of
  369. true -> Options;
  370. false -> addCompileInfo(Options)
  371. end.
  372. addCompileInfo(Options) ->
  373. CompInfo = [{K, V} || {K, V} <- Options, lists:member(K, [outdir, i])],
  374. [{compile_info, CompInfo} | Options].
  375. addFileType(Module, Options) ->
  376. Type = getFileType(Module),
  377. [{type, Type} | Options].
  378. %% This will check if the given module or source file is an ErlyDTL template.
  379. %% Currently, this is done by checking if its reported source path ends with
  380. %% ".dtl.erl".
  381. getFileType(Module) when is_atom(Module) ->
  382. Props = Module:module_info(compile),
  383. Source = proplists:get_value(source, Props, ""),
  384. getFileType(Source);
  385. getFileType(Source) ->
  386. Ext = filename:extension(Source),
  387. Root = filename:rootname(Source),
  388. SecondExt = filename:extension(Root),
  389. case Ext of
  390. <<".erl">> when SecondExt =:= <<".dtl">> -> dtl;
  391. <<".dtl">> -> dtl;
  392. <<".erl">> -> erl;
  393. <<".lfe">> -> lfe;
  394. <<".ex">> -> elixir;
  395. ".erl" when SecondExt =:= ".dtl" -> dtl;
  396. ".dtl" -> dtl;
  397. ".erl" -> erl;
  398. ".lfe" -> lfe;
  399. ".ex" -> elixir
  400. end.
  401. %% This will search back to find an appropriate include directory, by
  402. %% searching further back than "..". Instead, it will extract the basename
  403. %% (probably "include" from the include pathfile, and then search backwards in
  404. %% the directory tree until it finds a directory with the same basename found
  405. %% above.
  406. determineIncludeDir(IncludeDir, BeamDir, SrcDir) ->
  407. IncludeBase = filename:basename(IncludeDir),
  408. case determineIncludeDirFromBeamDir(IncludeBase, IncludeDir, BeamDir) of
  409. {ok, _Dir} = RetD -> RetD;
  410. undefined ->
  411. {ok, Cwd} = file:get_cwd(),
  412. % Cwd2 = normalizeCaseWindowsDir(Cwd),
  413. % SrcDir2 = normalizeCaseWindowsDir(SrcDir),
  414. % IncludeBase2 = normalizeCaseWindowsDir(IncludeBase),
  415. case findIncludeDirFromAncestors(SrcDir, Cwd, IncludeBase) of
  416. {ok, _Dir} = RetD -> RetD;
  417. undefined -> {ok, IncludeDir} %% Failed, just stick with original
  418. end
  419. end.
  420. %% First try to see if we have an include file alongside our ebin directory, which is typically the case
  421. determineIncludeDirFromBeamDir(IncludeBase, IncludeDir, BeamDir) ->
  422. BeamBasedIncDir = filename:join(filename:dirname(BeamDir), IncludeBase),
  423. case filelib:is_dir(BeamBasedIncDir) of
  424. true -> {ok, BeamBasedIncDir};
  425. false ->
  426. BeamBasedIncDir2 = filename:join(filename:dirname(BeamDir), IncludeDir),
  427. case filelib:is_dir(BeamBasedIncDir2) of
  428. true -> {ok, BeamBasedIncDir2};
  429. _ ->
  430. undefined
  431. end
  432. end.
  433. %% get the src dir
  434. getRootSrcDirFromSrcDir(SrcDir) ->
  435. NewDirName = filename:dirname(SrcDir),
  436. BaseName = filename:basename(SrcDir),
  437. case BaseName of
  438. ?rootSrcDir ->
  439. NewDirName;
  440. _ ->
  441. case NewDirName =/= SrcDir of
  442. true ->
  443. getRootSrcDirFromSrcDir(NewDirName);
  444. _ ->
  445. undefined
  446. end
  447. end.
  448. %% Then we dig back through the parent directories until we find our include directory
  449. findIncludeDirFromAncestors(Cwd, Cwd, _) -> undefined;
  450. findIncludeDirFromAncestors("/", _, _) -> undefined;
  451. findIncludeDirFromAncestors(".", _, _) -> undefined;
  452. findIncludeDirFromAncestors("", _, _) -> undefined;
  453. findIncludeDirFromAncestors(Dir, Cwd, IncludeBase) ->
  454. NewDirName = filename:dirname(Dir),
  455. AttemptDir = filename:join(NewDirName, IncludeBase),
  456. case filelib:is_dir(AttemptDir) of
  457. true ->
  458. {ok, AttemptDir};
  459. false ->
  460. case NewDirName =/= Dir of
  461. true ->
  462. findIncludeDirFromAncestors(NewDirName, Cwd, IncludeBase);
  463. _ ->
  464. undefined
  465. end
  466. end.
  467. % normalizeCaseWindowsDir(Dir) ->
  468. % case os:type() of
  469. % {win32, _} -> Dir; %string:to_lower(Dir);
  470. % {unix, _} -> Dir
  471. % end.
  472. %% This is an attempt to intelligently fix paths in modules when a
  473. %% release is moved. Essentially, it takes a module name and its original path
  474. %% from Module:module_info(compile), say
  475. %% "/some/original/path/site/src/pages/somepage.erl", and then breaks down the
  476. %% path one by one prefixing it with the current working directory until it
  477. %% either finds a match, or fails. If it succeeds, it returns the Path to the
  478. %% new Source file.
  479. fixDescendantSource([], _IsFile) ->
  480. undefined;
  481. fixDescendantSource(Path, IsFile) ->
  482. {ok, Cwd} = file:get_cwd(),
  483. PathParts = filename:split(Path),
  484. case makeDescendantSource(PathParts, Cwd) of
  485. undefined -> case IsFile of true -> Path; _ -> undefined end;
  486. FoundPath -> FoundPath
  487. end.
  488. makeDescendantSource([], _Cwd) ->
  489. undefined;
  490. makeDescendantSource([_ | T], Cwd) ->
  491. PathAttempt = filename:join([Cwd | T]),
  492. case filelib:is_regular(PathAttempt) of
  493. true -> PathAttempt;
  494. false -> makeDescendantSource(T, Cwd)
  495. end.
  496. isDescendent(Path) ->
  497. {ok, Cwd} = file:get_cwd(),
  498. lists:sublist(Path, length(Cwd)) == Cwd.
  499. %% @private Find the src directory for the specified Directory; max 15 iterations
  500. getSrcDir(Dir) ->
  501. getSrcDir(Dir, 15).
  502. getSrcDir(_Dir, 0) ->
  503. undefined;
  504. getSrcDir(Dir, Ctr) ->
  505. HasCode = filelib:wildcard("*.erl", Dir) /= [] orelse
  506. filelib:wildcard("*.hrl", Dir) /= [] orelse
  507. filelib:wildcard("*.dtl", Dir) /= [] orelse
  508. filelib:wildcard("*.lfe", Dir) /= [] orelse
  509. filelib:wildcard("*.ex", Dir) /= [],
  510. if
  511. HasCode -> {ok, Dir};
  512. true -> getSrcDir(filename:dirname(Dir), Ctr - 1)
  513. end.
  514. mergeExtraDirs(IsAddPath) ->
  515. case ?esCfgSync:getv(?extraDirs) of
  516. undefined ->
  517. {[], [], [], []};
  518. ExtraList ->
  519. FunMerge =
  520. fun(OneExtra, {AddExtraDirs, AddOnlyDirs, OnlyDirs, DelDirs} = AllAcc) ->
  521. case OneExtra of
  522. {addExtra, DirsAndOpts} ->
  523. Adds =
  524. [
  525. begin
  526. case IsAddPath of
  527. true ->
  528. case proplists:get_value(outdir, Opts) of
  529. undefined ->
  530. true;
  531. Path ->
  532. ok = filelib:ensure_dir(Path),
  533. true = code:add_pathz(Path)
  534. end;
  535. _ ->
  536. ignore
  537. end,
  538. filename:absname(Dir)
  539. end || {Dir, Opts} <- DirsAndOpts
  540. ],
  541. setelement(1, AllAcc, Adds ++ AddExtraDirs);
  542. {addOnly, DirsAndOpts} ->
  543. Adds =
  544. [
  545. begin
  546. case IsAddPath of
  547. true ->
  548. case proplists:get_value(outdir, Opts) of
  549. undefined ->
  550. true;
  551. Path ->
  552. ok = filelib:ensure_dir(Path),
  553. true = code:add_pathz(Path)
  554. end;
  555. _ ->
  556. ignore
  557. end,
  558. filename:absname(Dir)
  559. end || {Dir, Opts} <- DirsAndOpts
  560. ],
  561. setelement(2, AllAcc, Adds ++ AddOnlyDirs);
  562. {only, DirsAndOpts} ->
  563. Onlys =
  564. [
  565. begin
  566. case IsAddPath of
  567. true ->
  568. case proplists:get_value(outdir, Opts) of
  569. undefined ->
  570. true;
  571. Path ->
  572. ok = filelib:ensure_dir(Path),
  573. true = code:add_pathz(Path)
  574. end;
  575. _ ->
  576. ignore
  577. end,
  578. filename:absname(Dir)
  579. end || {Dir, Opts} <- DirsAndOpts
  580. ],
  581. setelement(3, AllAcc, Onlys ++ OnlyDirs);
  582. {del, DirsAndOpts} ->
  583. Dels =
  584. [
  585. begin
  586. filename:absname(Dir)
  587. end || {Dir, _Opts} <- DirsAndOpts
  588. ],
  589. setelement(4, AllAcc, Dels ++ DelDirs)
  590. end
  591. end,
  592. lists:foldl(FunMerge, {[], [], [], []}, ExtraList)
  593. end.
  594. -define(RegExp, <<".*\\.(erl|hrl|beam|config|dtl|lfe|ex)$">>).
  595. collSrcFiles(IsAddPath) ->
  596. {AddExtraSrcDirs, AddOnlySrcDirs, OnlySrcDirs, DelSrcDirs} = mergeExtraDirs(IsAddPath),
  597. CollFiles = filelib:fold_files(filename:absname(<<"./">>), ?RegExp, true,
  598. fun(OneFile, {Srcs, Hrls, Configs, Beams} = Acc) ->
  599. case isOnlyDir(OnlySrcDirs, OneFile) andalso (not isDelDir(DelSrcDirs, OneFile)) of
  600. true ->
  601. MTimeSec = dateTimeToSec(filelib:last_modified(OneFile)),
  602. case filename:extension(OneFile) of
  603. <<".beam">> ->
  604. BeamMod = binary_to_atom(filename:basename(OneFile, <<".beam">>)),
  605. setelement(4, Acc, Beams#{BeamMod => MTimeSec});
  606. <<".config">> ->
  607. setelement(3, Acc, Configs#{OneFile => MTimeSec});
  608. <<".hrl">> ->
  609. setelement(2, Acc, Hrls#{OneFile => MTimeSec});
  610. <<>> ->
  611. Acc;
  612. _ ->
  613. RootSrcDir =
  614. case getRootSrcDirFromSrcDir(OneFile) of
  615. undefined ->
  616. filename:dirname(OneFile);
  617. RetSrcDir ->
  618. RetSrcDir
  619. end,
  620. case getOptions(RootSrcDir) of
  621. undefined ->
  622. Mod = binary_to_atom(filename:basename(OneFile, filename:extension(OneFile))),
  623. case getModOpts(Mod) of
  624. {ok, Options} ->
  625. setOptions(RootSrcDir, Options);
  626. _ ->
  627. ignore
  628. end;
  629. _ ->
  630. ignore
  631. end,
  632. setelement(1, Acc, Srcs#{OneFile => MTimeSec})
  633. end;
  634. _ ->
  635. Acc
  636. end
  637. end, {#{}, #{}, #{}, #{}}),
  638. FunCollAddExtra =
  639. fun(OneDir, FilesAcc) ->
  640. filelib:fold_files(?IIF(is_list(OneDir), list_to_binary(OneDir), OneDir), ?RegExp, true,
  641. fun(OneFile, {Srcs, Hrls, Configs, Beams} = Acc) ->
  642. MTimeSec = dateTimeToSec(filelib:last_modified(OneFile)),
  643. case filename:extension(OneFile) of
  644. <<".beam">> ->
  645. BeamMod = binary_to_atom(filename:basename(OneFile, <<".beam">>)),
  646. setelement(4, Acc, Beams#{BeamMod => MTimeSec});
  647. <<".config">> ->
  648. setelement(3, Acc, Configs#{OneFile => MTimeSec});
  649. <<".hrl">> ->
  650. setelement(2, Acc, Hrls#{OneFile => MTimeSec});
  651. <<>> ->
  652. Acc;
  653. _ ->
  654. setelement(1, Acc, Srcs#{OneFile => MTimeSec})
  655. end
  656. end, FilesAcc)
  657. end,
  658. AddExtraCollFiles = lists:foldl(FunCollAddExtra, CollFiles, AddExtraSrcDirs),
  659. FunCollAddOnly =
  660. fun(OneDir, FilesAcc) ->
  661. filelib:fold_files(?IIF(is_list(OneDir), list_to_binary(OneDir), OneDir), ?RegExp, false,
  662. fun(OneFile, {Srcs, Hrls, Configs, Beams} = Acc) ->
  663. MTimeSec = dateTimeToSec(filelib:last_modified(OneFile)),
  664. case filename:extension(OneFile) of
  665. <<".beam">> ->
  666. BeamMod = binary_to_atom(filename:basename(OneFile, <<".beam">>)),
  667. setelement(4, Acc, Beams#{BeamMod => MTimeSec});
  668. <<".config">> ->
  669. setelement(3, Acc, Configs#{OneFile => MTimeSec});
  670. <<".hrl">> ->
  671. setelement(2, Acc, Hrls#{OneFile => MTimeSec});
  672. <<>> ->
  673. Acc;
  674. _ ->
  675. setelement(1, Acc, Srcs#{OneFile => MTimeSec})
  676. end
  677. end, FilesAcc)
  678. end,
  679. lists:foldl(FunCollAddOnly, AddExtraCollFiles, AddOnlySrcDirs).
  680. isOnlyDir([], _) ->
  681. true;
  682. isOnlyDir(ReplaceDirs, SrcDir) ->
  683. isMatchDir(ReplaceDirs, SrcDir).
  684. isDelDir([], _) ->
  685. false;
  686. isDelDir(ReplaceDirs, SrcDir) ->
  687. isMatchDir(ReplaceDirs, SrcDir).
  688. isMatchDir([], _SrcDir) ->
  689. false;
  690. isMatchDir([SrcDir | _ReplaceDirs], SrcDir) ->
  691. true;
  692. isMatchDir([OneDir | ReplaceDirs], SrcDir) ->
  693. case re:run(SrcDir, OneDir) of
  694. nomatch -> isMatchDir(ReplaceDirs, SrcDir);
  695. _ -> true
  696. end.
  697. getEnv(Var, Default) ->
  698. case application:get_env(eSync, Var) of
  699. {ok, Value} ->
  700. Value;
  701. _ ->
  702. Default
  703. end.
  704. setEnv(Var, Val) ->
  705. ok = application:set_env(eSync, Var, Val).
  706. canLog(MsgType) ->
  707. case eSync:getLog() of
  708. true -> true;
  709. all -> true;
  710. none -> false;
  711. false -> false;
  712. skip_success -> MsgType == errors orelse MsgType == warnings;
  713. L when is_list(L) -> lists:member(MsgType, L);
  714. _ -> false
  715. end.
  716. %% 注意 map类型的数据不能当做key
  717. -type key() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple().
  718. -type value() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple() | map().
  719. -spec load(term(), [{key(), value()}]) -> ok.
  720. load(Module, KVs) ->
  721. Forms = forms(Module, KVs),
  722. {ok, Module, Bin} = compile:forms(Forms),
  723. code:soft_purge(Module),
  724. {module, Module} = code:load_binary(Module, atom_to_list(Module), Bin),
  725. ok.
  726. forms(Module, KVs) ->
  727. %% -module(Module).
  728. Mod = erl_syntax:attribute(erl_syntax:atom(module), [erl_syntax:atom(Module)]),
  729. %% -export([getv/0]).
  730. ExportList = [erl_syntax:arity_qualifier(erl_syntax:atom(getv), erl_syntax:integer(1))],
  731. Export = erl_syntax:attribute(erl_syntax:atom(export), [erl_syntax:list(ExportList)]),
  732. %% getv(K) -> V
  733. Function = erl_syntax:function(erl_syntax:atom(getv), lookup_clauses(KVs, [])),
  734. [erl_syntax:revert(X) || X <- [Mod, Export, Function]].
  735. lookup_clause(Key, Value) ->
  736. Var = erl_syntax:abstract(Key),
  737. Body = erl_syntax:abstract(Value),
  738. erl_syntax:clause([Var], [], [Body]).
  739. lookup_clause_anon() ->
  740. Var = erl_syntax:variable("_"),
  741. Body = erl_syntax:atom(undefined),
  742. erl_syntax:clause([Var], [], [Body]).
  743. lookup_clauses([], Acc) ->
  744. lists:reverse(lists:flatten([lookup_clause_anon() | Acc]));
  745. lookup_clauses([{Key, Value} | T], Acc) ->
  746. lookup_clauses(T, [lookup_clause(Key, Value) | Acc]).
  747. getOptions(SrcDir) ->
  748. case erlang:get(SrcDir) of
  749. undefined ->
  750. undefined;
  751. Options ->
  752. {ok, Options}
  753. end.
  754. setOptions(SrcDir, Options) ->
  755. case erlang:get(SrcDir) of
  756. undefined ->
  757. erlang:put(SrcDir, Options);
  758. OldOptions ->
  759. NewOptions =
  760. case lists:keytake(compile_info, 1, Options) of
  761. {value, {compile_info, ValList1}, Options1} ->
  762. case lists:keytake(compile_info, 1, OldOptions) of
  763. {value, {compile_info, ValList2}, Options2} ->
  764. [{compile_info, lists:usort(ValList1 ++ ValList2)} | lists:usort(Options1 ++ Options2)];
  765. _ ->
  766. lists:usort(Options ++ OldOptions)
  767. end;
  768. _ ->
  769. lists:usort(Options ++ OldOptions)
  770. end,
  771. erlang:put(SrcDir, NewOptions)
  772. end.
  773. loadCfg() ->
  774. KVs = [{Key, getEnv(Key, DefVal)} || {Key, DefVal} <- ?DefCfgList],
  775. load(?esCfgSync, KVs).
  776. %% ******************************* 加载与编译相关 **********************************************************************
  777. printResults(_Module, SrcFile, [], []) ->
  778. ?logSuccess("~s Recompiled", [SrcFile]);
  779. printResults(_Module, SrcFile, [], Warnings) ->
  780. formatErrors(logWarnings, SrcFile, [], Warnings);
  781. printResults(_Module, SrcFile, Errors, Warnings) ->
  782. formatErrors(logErrors, SrcFile, Errors, Warnings).
  783. %% @private Print error messages in a pretty and user readable way.
  784. formatErrors(LogFun, File, Errors, Warnings) ->
  785. AllErrors1 = lists:sort(lists:flatten([X || {_, X} <- Errors])),
  786. AllErrors2 = [{Line, "Error", Module, Description} || {Line, Module, Description} <- AllErrors1],
  787. AllWarnings1 = lists:sort(lists:flatten([X || {_, X} <- Warnings])),
  788. AllWarnings2 = [{Line, "Warning", Module, Description} || {Line, Module, Description} <- AllWarnings1],
  789. Everything = lists:sort(AllErrors2 ++ AllWarnings2),
  790. FPck =
  791. fun({Line, Prefix, Module, ErrorDescription}) ->
  792. Msg = formatError(Module, ErrorDescription),
  793. case LogFun of
  794. logWarnings ->
  795. ?logWarnings("~s: ~p: ~s: ~s", [File, Line, Prefix, Msg]);
  796. logErrors ->
  797. ?logErrors("~s: ~p: ~s: ~s", [File, Line, Prefix, Msg])
  798. end
  799. end,
  800. [FPck(X) || X <- Everything],
  801. ok.
  802. formatError(Module, ErrorDescription) ->
  803. case erlang:function_exported(Module, format_error, 1) of
  804. true -> Module:format_error(ErrorDescription);
  805. false -> io_lib:format("~s", [ErrorDescription])
  806. end.
  807. fireOnSync(OnSyncFun, Modules) ->
  808. case OnSyncFun of
  809. undefined -> ok;
  810. Funs when is_list(Funs) -> onSyncApplyList(Funs, Modules);
  811. Fun -> onSyncApply(Fun, Modules)
  812. end.
  813. onSyncApplyList(Funs, Modules) ->
  814. [onSyncApply(Fun, Modules) || Fun <- Funs].
  815. onSyncApply({M, F}, Modules) ->
  816. try erlang:apply(M, F, [Modules])
  817. catch
  818. C:R:S ->
  819. ?logErrors("apply sync fun ~p:~p(~p) error ~p", [M, F, Modules, {C, R, S}])
  820. end;
  821. onSyncApply(Fun, Modules) when is_function(Fun) ->
  822. try Fun(Modules)
  823. catch
  824. C:R:S ->
  825. ?logErrors("apply sync fun ~p(~p) error ~p", [Fun, Modules, {C, R, S}])
  826. end.
  827. reloadChangedMod([], _SwSyncNode, OnSyncFun, Acc) ->
  828. fireOnSync(OnSyncFun, Acc);
  829. reloadChangedMod([Module | LeftMod], SwSyncNode, OnSyncFun, Acc) ->
  830. case Module == es_gen_ipc orelse code:get_object_code(Module) of
  831. true ->
  832. ignore;
  833. error ->
  834. ?logErrors("Error loading object code for ~p", [Module]),
  835. reloadChangedMod(LeftMod, SwSyncNode, OnSyncFun, Acc);
  836. {Module, Binary, Filename} ->
  837. case code:load_binary(Module, Filename, Binary) of
  838. {module, Module} ->
  839. ?logSuccess("Reloaded(Beam changed) Mod: ~s Success", [Module]),
  840. syncLoadModOnAllNodes(SwSyncNode, Module, Binary, changed);
  841. {error, What} ->
  842. ?logErrors("Reloaded(Beam changed) Mod: ~s Errors Reason:~p", [Module, What])
  843. end,
  844. reloadChangedMod(LeftMod, SwSyncNode, OnSyncFun, [Module | Acc])
  845. end.
  846. getNodes() ->
  847. lists:usort(lists:flatten(nodes() ++ [erpc:call(X, erlang, nodes, []) || X <- nodes()])) -- [node()].
  848. syncLoadModOnAllNodes(false, _Module, _Binary, _Reason) ->
  849. ignore;
  850. syncLoadModOnAllNodes(true, Module, Binary, Reason) ->
  851. %% Get a list of nodes known by this node, plus all attached nodes.
  852. Nodes = getNodes(),
  853. NumNodes = length(Nodes),
  854. [
  855. begin
  856. ?logSuccess("Do Reloading '~s' on ~p", [Module, Node]),
  857. erpc:call(Node, code, ensure_loaded, [Module]),
  858. case erpc:call(Node, code, which, [Module]) of
  859. Filename when is_binary(Filename) orelse is_list(Filename) ->
  860. %% File exists, overwrite and load into VM.
  861. ok = erpc:call(Node, file, write_file, [Filename, Binary]),
  862. erpc:call(Node, code, purge, [Module]),
  863. case erpc:call(Node, code, load_file, [Module]) of
  864. {module, Module} ->
  865. ?logSuccess("Reloaded(Beam ~p) Mod:~s and write Success on node:~p", [Reason, Module, Node]);
  866. {error, What} ->
  867. ?logErrors("Reloaded(Beam ~p) Mod:~s and write Errors on node:~p Reason:~p", [Module, Node, What])
  868. end;
  869. _ ->
  870. %% File doesn't exist, just load into VM.
  871. case erpc:call(Node, code, load_binary, [Module, undefined, Binary]) of
  872. {module, Module} ->
  873. ?logSuccess("Reloaded(Beam ~p) Mod:~s Success on node:~p", [Reason, Module, Node]);
  874. {error, What} ->
  875. ?logErrors("Reloaded(Beam ~p) Mod:~s Errors on node:~p Reason:~p", [Reason, Module, Node, What])
  876. end
  877. end
  878. end || Node <- Nodes
  879. ],
  880. ?logSuccess("Reloaded(Beam changed) Mod: ~s on ~p nodes:~p", [Module, NumNodes, Nodes]).
  881. recompileChangeSrcFile(Iterator, SwSyncNode) ->
  882. case maps:next(Iterator) of
  883. {File, _V, NextIterator} ->
  884. recompileSrcFile(File, SwSyncNode),
  885. recompileChangeSrcFile(NextIterator, SwSyncNode);
  886. _ ->
  887. ok
  888. end.
  889. erlydtlCompile(SrcFile, Options) ->
  890. F =
  891. fun({outdir, OutDir}, Acc) -> [{out_dir, OutDir} | Acc];
  892. (OtherOption, Acc) -> [OtherOption | Acc]
  893. end,
  894. DtlOptions = lists:foldl(F, [], Options),
  895. Module = binary_to_atom(lists:flatten(filename:basename(SrcFile, ".dtl") ++ "_dtl")),
  896. Compiler = erlydtl,
  897. Compiler:compile(SrcFile, Module, DtlOptions).
  898. elixir_compile(SrcFile, Options) ->
  899. Outdir = proplists:get_value(outdir, Options),
  900. Compiler = ':Elixir.Kernel.ParallelCompiler',
  901. Modules = Compiler:files_to_path([SrcFile], Outdir),
  902. Loader =
  903. fun(Module) ->
  904. Outfile = code:which(Module),
  905. Binary = file:read_file(Outfile),
  906. {Module, Binary}
  907. end,
  908. Results = lists:map(Loader, Modules),
  909. {ok, multiple, Results, []}.
  910. lfe_compile(SrcFile, Options) ->
  911. Compiler = lfe_comp,
  912. Compiler:file(SrcFile, Options).
  913. getCompileFunAndModuleName(SrcFile) ->
  914. case getFileType(SrcFile) of
  915. erl ->
  916. {fun compile:file/2, binary_to_atom(filename:basename(SrcFile, <<".erl">>))};
  917. dtl ->
  918. {fun erlydtlCompile/2, list_to_atom(lists:flatten(binary_to_list(filename:basename(SrcFile, <<".dtl">>)) ++ "_dtl"))};
  919. lfe ->
  920. {fun lfe_compile/2, binary_to_atom(filename:basename(SrcFile, <<".lfe">>))};
  921. elixir ->
  922. {fun elixir_compile/2, binary_to_atom(filename:basename(SrcFile, <<".ex">>))}
  923. end.
  924. getObjectCode(Module) ->
  925. case code:get_object_code(Module) of
  926. {Module, B, Filename} -> {B, Filename};
  927. _ -> {undefined, undefined}
  928. end.
  929. reloadIfNecessary(Module, OldBinary, Binary, Filename, SwSyncNode) ->
  930. case Binary =/= OldBinary of
  931. true ->
  932. %% Try to load the module...
  933. case code:ensure_loaded(Module) of
  934. {module, Module} ->
  935. case code:load_binary(Module, Filename, Binary) of
  936. {module, Module} ->
  937. ?logSuccess("Reloaded(Beam recompiled) Mod:~s Success", [Module]),
  938. syncLoadModOnAllNodes(SwSyncNode, Module, Binary, recompiled);
  939. {error, What} ->
  940. ?logErrors("Reloaded(Beam recompiled) Mod:~s Errors Reason:~p", [Module, What])
  941. end;
  942. {error, nofile} ->
  943. case code:load_binary(Module, Filename, Binary) of
  944. {module, Module} ->
  945. ?logSuccess("Reloaded(Beam recompiled) Mod:~s Success", [Module]),
  946. syncLoadModOnAllNodes(SwSyncNode, Module, Binary, recompiled);
  947. {error, What} ->
  948. ?logErrors("Reloaded(Beam recompiled) Mod:~s Errors Reason:~p", [Module, What])
  949. end;
  950. {error, embedded} ->
  951. case code:load_file(Module) of %% Module is not yet loaded, load it.
  952. {module, Module} -> ok;
  953. {error, nofile} ->
  954. case code:load_binary(Module, Filename, Binary) of
  955. {module, Module} ->
  956. ?logSuccess("Reloaded(Beam recompiled) Mod:~s Success", [Module]),
  957. syncLoadModOnAllNodes(SwSyncNode, Module, Binary, recompiled);
  958. {error, What} ->
  959. ?logErrors("Reloaded(Beam recompiled) Mod:~s Errors Reason:~p", [Module, What])
  960. end
  961. end
  962. end;
  963. _ ->
  964. ignore
  965. end.
  966. recompileSrcFile(SrcFile, SwSyncNode) ->
  967. %% Get the module, src dir, and options...
  968. RootSrcDir =
  969. case getRootSrcDirFromSrcDir(SrcFile) of
  970. undefined ->
  971. filename:dirname(SrcFile);
  972. RetSrcDir ->
  973. RetSrcDir
  974. end,
  975. CurSrcDir = filename:dirname(SrcFile),
  976. {CompileFun, Module} = getCompileFunAndModuleName(SrcFile),
  977. {OldBinary, Filename} = getObjectCode(Module),
  978. case Module == es_gen_ipc orelse getOptions(RootSrcDir) of
  979. true ->
  980. ignore;
  981. {ok, Options} ->
  982. RightFileDir = binary_to_list(filename:join(CurSrcDir, filename:basename(SrcFile))),
  983. LastOptions = ?IIF(?esCfgSync:getv(?isJustMem), [binary, return | Options], [return | Options]),
  984. case CompileFun(RightFileDir, LastOptions) of
  985. {ok, Module, Binary, Warnings} ->
  986. printResults(Module, RightFileDir, [], Warnings),
  987. reloadIfNecessary(Module, OldBinary, Binary, Filename, SwSyncNode),
  988. {ok, [], Warnings};
  989. {ok, [{ok, Module, Binary, Warnings}], Warnings2} ->
  990. printResults(Module, RightFileDir, [], Warnings ++ Warnings2),
  991. reloadIfNecessary(Module, OldBinary, Binary, Filename, SwSyncNode),
  992. {ok, [], Warnings ++ Warnings2};
  993. {ok, multiple, Results, Warnings} ->
  994. printResults(Module, RightFileDir, [], Warnings),
  995. [reloadIfNecessary(CompiledModule, OldBinary, Binary, Filename, SwSyncNode) || {CompiledModule, Binary} <- Results],
  996. {ok, [], Warnings};
  997. {ok, OtherModule, _Binary, Warnings} ->
  998. Desc = io_lib:format("Module definition (~p) differs from expected (~s)", [OtherModule, filename:rootname(filename:basename(RightFileDir))]),
  999. Errors = [{RightFileDir, {0, Module, Desc}}],
  1000. printResults(Module, RightFileDir, Errors, Warnings),
  1001. {ok, Errors, Warnings};
  1002. {error, Errors, Warnings} ->
  1003. printResults(Module, RightFileDir, Errors, Warnings),
  1004. {ok, Errors, Warnings};
  1005. {ok, Module, Warnings} ->
  1006. printResults(Module, RightFileDir, [], Warnings),
  1007. {ok, [], Warnings};
  1008. _Err ->
  1009. ?logErrors("compile Mod:~s Errors Reason:~p", [Module, _Err])
  1010. end;
  1011. undefined ->
  1012. case tryGetModOpts(Module) of
  1013. {ok, Options} ->
  1014. setOptions(RootSrcDir, Options),
  1015. recompileSrcFile(SrcFile, SwSyncNode);
  1016. _ ->
  1017. case tryGetSrcOpts(CurSrcDir) of
  1018. {ok, Options} ->
  1019. setOptions(RootSrcDir, Options),
  1020. recompileSrcFile(SrcFile, SwSyncNode);
  1021. _ ->
  1022. ?logErrors("Unable to determine options for ~s", [SrcFile])
  1023. end
  1024. end
  1025. end.
  1026. collIncludeCHrls([], AllHrls, CHrls, NewAddMap) ->
  1027. case maps:size(NewAddMap) > 0 of
  1028. true ->
  1029. collIncludeCHrls(maps:keys(NewAddMap), AllHrls, CHrls, #{});
  1030. _ ->
  1031. CHrls
  1032. end;
  1033. collIncludeCHrls([OneHrl | LeftCHrls], AllHrls, CHrls, NewAddMap) ->
  1034. {NewCHrls, NNewAddMap} = whoInclude(OneHrl, AllHrls, CHrls, NewAddMap),
  1035. collIncludeCHrls(LeftCHrls, AllHrls, NewCHrls, NNewAddMap).
  1036. collIncludeCErls([], _SrcFiles, CSrcs, _NewAddMap) ->
  1037. CSrcs;
  1038. collIncludeCErls([Hrl | LeftHrl], SrcFiles, CSrcs, NewAddMap) ->
  1039. {NewCSrcs, NNewAddMap} = whoInclude(Hrl, SrcFiles, CSrcs, NewAddMap),
  1040. collIncludeCErls(LeftHrl, SrcFiles, NewCSrcs, NNewAddMap).
  1041. whoInclude(HrlFile, AllFiles, CFiles, NewAddMap) ->
  1042. HrlFileBaseName = filename:basename(HrlFile),
  1043. QuoteHrlFileBaseName = <<"\"", HrlFileBaseName/binary, "\"">>,
  1044. doMathEveryFile(maps:iterator(AllFiles), QuoteHrlFileBaseName, CFiles, NewAddMap).
  1045. doMathEveryFile(Iterator, HrlFileBaseName, CFiles, NewAddMap) ->
  1046. case maps:next(Iterator) of
  1047. {OneFile, _V, NextIterator} ->
  1048. case file:open(OneFile, [read, binary]) of
  1049. {ok, IoDevice} ->
  1050. IsInclude = doMathEveryLine(IoDevice, HrlFileBaseName),
  1051. file:close(IoDevice),
  1052. case IsInclude of
  1053. true ->
  1054. case maps:is_key(OneFile, CFiles) of
  1055. true ->
  1056. doMathEveryFile(NextIterator, HrlFileBaseName, CFiles, NewAddMap);
  1057. _ ->
  1058. doMathEveryFile(NextIterator, HrlFileBaseName, CFiles#{OneFile => 1}, NewAddMap#{OneFile => 1})
  1059. end;
  1060. _ ->
  1061. doMathEveryFile(NextIterator, HrlFileBaseName, CFiles, NewAddMap)
  1062. end;
  1063. _ ->
  1064. doMathEveryFile(NextIterator, HrlFileBaseName, CFiles, NewAddMap)
  1065. end;
  1066. _ ->
  1067. {CFiles, NewAddMap}
  1068. end.
  1069. %% 注释
  1070. %% whoInclude(HrlFile, SrcFiles) ->
  1071. %% HrlFileBaseName = filename:basename(HrlFile),
  1072. %% Pred =
  1073. %% fun(SrcFile, _) ->
  1074. %% {ok, Forms} = epp_dodger:parse_file(SrcFile),
  1075. %% isInclude(binary_to_list(HrlFileBaseName), Forms)
  1076. %% end,
  1077. %% maps:filter(Pred, SrcFiles).
  1078. %% isInclude(_HrlFile, []) ->
  1079. %% false;
  1080. %% isInclude(HrlFile, [{tree, attribute, _, {attribute, _, [{_, _, IncludeFile}]}} | Forms]) when is_list(IncludeFile) ->
  1081. %% IncludeFileBaseName = filename:basename(IncludeFile),
  1082. %% case IncludeFileBaseName of
  1083. %% HrlFile -> true;
  1084. %% _ -> isInclude(HrlFile, Forms)
  1085. %% end;
  1086. %% isInclude(HrlFile, [_SomeForm | Forms]) ->
  1087. %% isInclude(HrlFile, Forms).
  1088. doMathEveryLine(IoDevice, HrlFileBaseName) ->
  1089. case file:read_line(IoDevice) of
  1090. {ok, Data} ->
  1091. case re:run(Data, HrlFileBaseName) of
  1092. nomatch ->
  1093. case re:run(Data, <<"->">>) of
  1094. nomatch ->
  1095. doMathEveryLine(IoDevice, HrlFileBaseName);
  1096. _ ->
  1097. false
  1098. end;
  1099. _ ->
  1100. true
  1101. end;
  1102. _ ->
  1103. false
  1104. end.
  1105. classifyChangeFile([], Beams, Configs, Hrls, Srcs, ColSrcs, ColHrls, ColConfigs, ColBeams) ->
  1106. {Beams, Configs, Hrls, Srcs, ColSrcs, ColHrls, ColConfigs, ColBeams};
  1107. classifyChangeFile([OneFile | LeftFile], Beams, Configs, Hrls, Srcs, ColSrcs, ColHrls, ColConfigs, ColBeams) ->
  1108. CurMTimeSec = dateTimeToSec(filelib:last_modified(OneFile)),
  1109. case filename:extension(OneFile) of
  1110. <<".beam">> ->
  1111. BinMod = binary_to_atom(filename:basename(OneFile, <<".beam">>)),
  1112. case ColBeams of
  1113. #{BinMod := OldMTimeSec} ->
  1114. case CurMTimeSec =/= OldMTimeSec andalso CurMTimeSec =/= 0 of
  1115. true ->
  1116. classifyChangeFile(LeftFile, [BinMod | Beams], Configs, Hrls, Srcs, ColSrcs, ColHrls, ColConfigs, ColBeams#{BinMod := CurMTimeSec});
  1117. _ ->
  1118. classifyChangeFile(LeftFile, Beams, Configs, Hrls, Srcs, ColSrcs, ColHrls, ColConfigs, ColBeams)
  1119. end;
  1120. _ ->
  1121. classifyChangeFile(LeftFile, [BinMod | Beams], Configs, Hrls, Srcs, ColSrcs, ColHrls, ColConfigs, ColBeams#{BinMod => CurMTimeSec})
  1122. end;
  1123. <<".config">> ->
  1124. AbsFile = filename:absname(OneFile),
  1125. case ColConfigs of
  1126. #{AbsFile := OldMTimeSec} ->
  1127. case CurMTimeSec =/= OldMTimeSec andalso CurMTimeSec =/= 0 of
  1128. true ->
  1129. CfgMod = erlang:binary_to_atom(filename:basename(AbsFile, <<".config">>), utf8),
  1130. classifyChangeFile(LeftFile, Beams, [CfgMod | Configs], Hrls, Srcs, ColSrcs, ColHrls, ColConfigs#{AbsFile := CurMTimeSec}, ColBeams);
  1131. _ ->
  1132. classifyChangeFile(LeftFile, Beams, Configs, Hrls, Srcs, ColSrcs, ColHrls, ColConfigs, ColBeams)
  1133. end;
  1134. _ ->
  1135. CfgMod = erlang:binary_to_atom(filename:basename(AbsFile, <<".config">>), utf8),
  1136. classifyChangeFile(LeftFile, Beams, [CfgMod | Configs], Hrls, Srcs, ColSrcs, ColHrls, ColConfigs#{AbsFile => CurMTimeSec}, ColBeams)
  1137. end;
  1138. <<".hrl">> ->
  1139. AbsFile = filename:absname(OneFile),
  1140. case ColHrls of
  1141. #{AbsFile := OldMTimeSec} ->
  1142. case CurMTimeSec =/= OldMTimeSec andalso CurMTimeSec =/= 0 of
  1143. true ->
  1144. classifyChangeFile(LeftFile, Beams, Configs, Hrls#{AbsFile => 1}, Srcs, ColSrcs, ColHrls#{AbsFile := CurMTimeSec}, ColConfigs, ColBeams);
  1145. _ ->
  1146. classifyChangeFile(LeftFile, Beams, Configs, Hrls, Srcs, ColSrcs, ColHrls, ColConfigs, ColBeams)
  1147. end;
  1148. _ ->
  1149. classifyChangeFile(LeftFile, Beams, Configs, Hrls#{AbsFile => 1}, Srcs, ColSrcs, ColHrls#{AbsFile => CurMTimeSec}, ColConfigs, ColBeams)
  1150. end;
  1151. <<>> ->
  1152. classifyChangeFile(LeftFile, Beams, Configs, Hrls, Srcs, ColSrcs, ColHrls, ColConfigs, ColBeams);
  1153. _ ->
  1154. AbsFile = filename:absname(OneFile),
  1155. case ColSrcs of
  1156. #{AbsFile := OldMTimeSec} ->
  1157. case CurMTimeSec =/= OldMTimeSec andalso CurMTimeSec =/= 0 of
  1158. true ->
  1159. classifyChangeFile(LeftFile, Beams, Configs, Hrls, Srcs#{AbsFile => 1}, ColSrcs#{AbsFile := CurMTimeSec}, ColHrls, ColConfigs, ColBeams);
  1160. _ ->
  1161. classifyChangeFile(LeftFile, Beams, Configs, Hrls, Srcs, ColSrcs, ColHrls, ColConfigs, ColBeams)
  1162. end;
  1163. _ ->
  1164. classifyChangeFile(LeftFile, Beams, Configs, Hrls, Srcs#{AbsFile => 1}, ColSrcs#{AbsFile => CurMTimeSec}, ColHrls, ColConfigs, ColBeams)
  1165. end
  1166. end.
  1167. fileSyncPath(ExecName) ->
  1168. case code:priv_dir(?MODULE) of
  1169. {error, _} ->
  1170. case code:which(?MODULE) of
  1171. Filename when is_list(Filename) ->
  1172. filename:join([filename:dirname(filename:dirname(Filename)), "priv", ExecName]);
  1173. _ ->
  1174. filename:join("../priv", ExecName)
  1175. end;
  1176. Dir ->
  1177. filename:join(Dir, ExecName)
  1178. end.
  1179. dateTimeToSec(DateTime) ->
  1180. if
  1181. DateTime == 0 ->
  1182. 0;
  1183. true ->
  1184. erlang:universaltime_to_posixtime(DateTime)
  1185. end.
  1186. debugInfoKeyFun() ->
  1187. case ?esCfgSync:getv(?debugInfoKeyFun) of
  1188. undefined ->
  1189. {debug_info_key, undefined};
  1190. {Mod, Fun} ->
  1191. Mod:Fun()
  1192. end.
  1193. %% ************************************************* utils end *******************************************************