erlang自动编译与加载
Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

323 Zeilen
11 KiB

vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
vor 5 Jahren
  1. -module(esUtils).
  2. -include("erlSync.hrl").
  3. -compile(inline).
  4. -compile({inline_size, 128}).
  5. -export([
  6. getModSrcDir/1,
  7. getModOptions/1,
  8. getFileType/1,
  9. getSrcDir/1,
  10. wildcard/2,
  11. getEnv/2,
  12. setEnv/2,
  13. load/2,
  14. logSuccess/1,
  15. logErrors/1,
  16. logWarnings/1,
  17. getSystemModules/0
  18. ]).
  19. getModSrcDir(Module) ->
  20. case code:is_loaded(Module) of
  21. {file, _} ->
  22. try
  23. %% Get some module info...
  24. Props = Module:module_info(compile),
  25. Source = proplists:get_value(source, Props, ""),
  26. %% Ensure that the file exists, is a decendent of the tree, and how to deal with that
  27. IsFile = filelib:is_regular(Source),
  28. IsDescendant = isDescendent(Source),
  29. Descendant = ?esCfgSync:getv(?descendant),
  30. LastSource =
  31. case {IsFile, IsDescendant, Descendant} of
  32. %% is file and descendant, we're good to go
  33. {true, true, _} -> Source;
  34. %% is not a descendant, but we allow them, so good to go
  35. {true, false, allow} -> Source;
  36. %% is not a descendant, and we fix non-descendants, so let's fix it
  37. {_, false, fix} -> fixDescendantSource(Source);
  38. %% Anything else, and we don't know what to do, so let's just bail.
  39. _ -> undefined
  40. end,
  41. case LastSource of
  42. undefined ->
  43. undefined;
  44. _ ->
  45. %% Get the source dir...
  46. Dir = filename:dirname(LastSource),
  47. getSrcDir(Dir)
  48. end
  49. catch _ : _ : _ ->
  50. undefined
  51. end;
  52. _ ->
  53. undefined
  54. end.
  55. getModOptions(Module) ->
  56. case code:is_loaded(Module) of
  57. {file, _} ->
  58. try
  59. Props = Module:module_info(compile),
  60. BeamDir = filename:dirname(code:which(Module)),
  61. Options1 = proplists:get_value(options, Props, []),
  62. %% transform `outdir'
  63. Options2 = transformOutdir(BeamDir, Options1),
  64. Options3 = ensureInclude(Options2),
  65. %% transform the include directories
  66. Options4 = transformAllIncludes(Module, BeamDir, Options3),
  67. %% maybe_add_compile_info
  68. Options5 = maybeAddCompileInfo(Options4),
  69. %% add filetype to options (DTL, LFE, erl, etc)
  70. Options6 = addFileType(Module, Options5),
  71. {ok, Options6}
  72. catch ExType:Error:Stacktrace ->
  73. Msg = [io_lib:format("~p:0: ~p looking for options: ~p. Stack: ~p~n", [Module, ExType, Error, Stacktrace])],
  74. logWarnings(Msg),
  75. {ok, []}
  76. end;
  77. _ ->
  78. {ok, []}
  79. end.
  80. transformOutdir(BeamDir, Options) ->
  81. [{outdir, BeamDir} | proplists:delete(outdir, Options)].
  82. ensureInclude(Options) ->
  83. case proplists:get_value(i, Options) of
  84. undefined -> [{i, "include"} | Options];
  85. _ -> Options
  86. end.
  87. transformAllIncludes(Module, BeamDir, Options) ->
  88. [transformInclude(Module, BeamDir, Opt) || Opt <- Options].
  89. transformInclude(Module, BeamDir, {i, IncludeDir}) ->
  90. {ok, SrcDir} = getModSrcDir(Module),
  91. {ok, IncludeDir2} = determineIncludeDir(IncludeDir, BeamDir, SrcDir),
  92. {i, IncludeDir2};
  93. transformInclude(_, _, Other) ->
  94. Other.
  95. maybeAddCompileInfo(Options) ->
  96. case lists:member(predetermined, Options) of
  97. true -> Options;
  98. false -> addCompileInfo(Options)
  99. end.
  100. addCompileInfo(Options) ->
  101. CompInfo = [{K, V} || {K, V} <- Options, lists:member(K, [outdir, i])],
  102. [{compile_info, CompInfo} | Options].
  103. addFileType(Module, Options) ->
  104. Type = getFileType(Module),
  105. [{type, Type} | Options].
  106. %% This will check if the given module or source file is an ErlyDTL template.
  107. %% Currently, this is done by checking if its reported source path ends with
  108. %% ".dtl.erl".
  109. getFileType(Module) when is_atom(Module) ->
  110. Props = Module:module_info(compile),
  111. Source = proplists:get_value(source, Props, ""),
  112. getFileType(Source);
  113. getFileType(Source) when is_list(Source) ->
  114. Ext = filename:extension(Source),
  115. Root = filename:rootname(Source),
  116. SecondExt = filename:extension(Root),
  117. case Ext of
  118. ".erl" when SecondExt =:= ".dtl" -> dtl;
  119. ".dtl" -> dtl;
  120. ".erl" -> erl;
  121. ".lfe" -> lfe;
  122. ".ex" -> elixir
  123. end.
  124. %% This will search back to find an appropriate include directory, by
  125. %% searching further back than "..". Instead, it will extract the basename
  126. %% (probably "include" from the include pathfile, and then search backwards in
  127. %% the directory tree until it finds a directory with the same basename found
  128. %% above.
  129. determineIncludeDir(IncludeDir, BeamDir, SrcDir) ->
  130. IncludeBase = filename:basename(IncludeDir),
  131. case determineIncludeDirFromBeamDir(IncludeBase, BeamDir) of
  132. {ok, D} -> {ok, D};
  133. undefined ->
  134. {ok, Cwd} = file:get_cwd(),
  135. % Cwd2 = normalizeCaseWindowsDir(Cwd),
  136. % SrcDir2 = normalizeCaseWindowsDir(SrcDir),
  137. % IncludeBase2 = normalizeCaseWindowsDir(IncludeBase),
  138. case findIncludeDirFromAncestors(Cwd, IncludeBase, SrcDir) of
  139. {ok, D} -> {ok, D};
  140. undefined -> {ok, IncludeDir} %% Failed, just stick with original
  141. end
  142. end.
  143. %% First try to see if we have an include file alongside our ebin directory, which is typically the case
  144. determineIncludeDirFromBeamDir(IncludeBase, BeamDir) ->
  145. BeamBasedIncDir = filename:join(filename:dirname(BeamDir), IncludeBase),
  146. case filelib:is_dir(BeamBasedIncDir) of
  147. true -> {ok, BeamBasedIncDir};
  148. false -> undefined
  149. end.
  150. %% Then we dig back through the parent directories until we find our include directory
  151. findIncludeDirFromAncestors(Cwd, _, Cwd) -> undefined;
  152. findIncludeDirFromAncestors(_, _, "/") -> undefined;
  153. findIncludeDirFromAncestors(_, _, ".") -> undefined;
  154. findIncludeDirFromAncestors(_, _, "") -> undefined;
  155. findIncludeDirFromAncestors(Cwd, IncludeBase, Dir) ->
  156. AttemptDir = filename:join(filename:dirname(Dir), IncludeBase),
  157. case filelib:is_dir(AttemptDir) of
  158. true ->
  159. {ok, AttemptDir};
  160. false ->
  161. findIncludeDirFromAncestors(Cwd, IncludeBase, filename:dirname(Dir))
  162. end.
  163. % normalizeCaseWindowsDir(Dir) ->
  164. % case os:type() of
  165. % {win32, _} -> Dir; %string:to_lower(Dir);
  166. % {unix, _} -> Dir
  167. % end.
  168. %% This is an attempt to intelligently fix paths in modules when a
  169. %% release is moved. Essentially, it takes a module name and its original path
  170. %% from Module:module_info(compile), say
  171. %% "/some/original/path/site/src/pages/somepage.erl", and then breaks down the
  172. %% path one by one prefixing it with the current working directory until it
  173. %% either finds a match, or fails. If it succeeds, it returns the Path to the
  174. %% new Source file.
  175. fixDescendantSource([]) ->
  176. undefined;
  177. fixDescendantSource(Path) ->
  178. {ok, Cwd} = file:get_cwd(),
  179. PathParts = filename:split(Path),
  180. case makeDescendantSource(Cwd, PathParts) of
  181. undefined -> Path;
  182. FoundPath -> FoundPath
  183. end.
  184. makeDescendantSource(_Cwd, []) ->
  185. undefined;
  186. makeDescendantSource(Cwd, [_ | T]) ->
  187. PathAttempt = filename:join([Cwd | T]),
  188. case filelib:is_regular(PathAttempt) of
  189. true -> PathAttempt;
  190. false -> makeDescendantSource(Cwd, T)
  191. end.
  192. isDescendent(Path) ->
  193. {ok, Cwd} = file:get_cwd(),
  194. lists:sublist(Path, length(Cwd)) == Cwd.
  195. %% @private Find the src directory for the specified Directory; max 15 iterations
  196. getSrcDir(Dir) ->
  197. getSrcDir(Dir, 15).
  198. getSrcDir(_Dir, 0) ->
  199. undefined;
  200. getSrcDir(Dir, Ctr) ->
  201. HasCode = filelib:wildcard("*.erl", Dir) /= [] orelse
  202. filelib:wildcard("*.hrl", Dir) /= [] orelse
  203. filelib:wildcard("*.dtl", Dir) /= [] orelse
  204. filelib:wildcard("*.lfe", Dir) /= [] orelse
  205. filelib:wildcard("*.ex", Dir) /= [],
  206. if
  207. HasCode -> {ok, Dir};
  208. true -> getSrcDir(filename:dirname(Dir), Ctr - 1)
  209. end.
  210. %% Return all files in a directory matching a regex.
  211. wildcard(Dir, Regex) ->
  212. filelib:fold_files(Dir, Regex, true, fun(Y, Acc) -> [Y | Acc] end, []).
  213. getEnv(Var, Default) ->
  214. case application:get_env(erlSync, Var) of
  215. {ok, Value} ->
  216. Value;
  217. _ ->
  218. Default
  219. end.
  220. setEnv(Var, Val) ->
  221. ok = application:set_env(erlSync, Var, Val).
  222. logSuccess(Message) ->
  223. canLog(success) andalso error_logger:info_msg(lists:flatten(Message)).
  224. logErrors(Message) ->
  225. canLog(errors) andalso error_logger:error_msg(lists:flatten(Message)).
  226. logWarnings(Message) ->
  227. canLog(warnings) andalso error_logger:warning_msg(lists:flatten(Message)).
  228. canLog(MsgType) ->
  229. case esScanner:getLog() of
  230. true -> true;
  231. all -> true;
  232. none -> false;
  233. false -> false;
  234. skip_success -> MsgType == errors orelse MsgType == warnings;
  235. L when is_list(L) -> lists:member(MsgType, L);
  236. _ -> false
  237. end.
  238. %% Return a list of all modules that belong to Erlang rather than whatever application we may be running.
  239. getSystemModules() ->
  240. Apps = [
  241. appmon, asn1, common_test, compiler, crypto, debugger,
  242. dialyzer, docbuilder, edoc, erl_interface, erts, et,
  243. eunit, gs, hipe, inets, inets, inviso, jinterface, kernel,
  244. mnesia, observer, orber, os_mon, parsetools, percept, pman,
  245. reltool, runtime_tools, sasl, snmp, ssl, stdlib, syntax_tools,
  246. test_server, toolbar, tools, tv, webtool, wx, xmerl, zlib
  247. ],
  248. FAppMod =
  249. fun(App) ->
  250. case application:get_key(App, modules) of
  251. {ok, Modules} -> Modules;
  252. _Other -> []
  253. end
  254. end,
  255. lists:flatten([FAppMod(X) || X <- Apps]).
  256. %% 注意 map类型的数据不能当做key
  257. -type key() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple().
  258. -type value() :: atom() | binary() | bitstring() | float() | integer() | list() | tuple() | map().
  259. -spec load(term(), [{key(), value()}]) -> ok.
  260. load(Module, KVs) ->
  261. Forms = forms(Module, KVs),
  262. {ok, Module, Bin} = compile:forms(Forms),
  263. code:soft_purge(Module),
  264. {module, Module} = code:load_binary(Module, atom_to_list(Module), Bin),
  265. ok.
  266. forms(Module, KVs) ->
  267. %% -module(Module).
  268. Mod = erl_syntax:attribute(erl_syntax:atom(module), [erl_syntax:atom(Module)]),
  269. %% -export([getv/0]).
  270. ExportList = [erl_syntax:arity_qualifier(erl_syntax:atom(getv), erl_syntax:integer(1))],
  271. Export = erl_syntax:attribute(erl_syntax:atom(export), [erl_syntax:list(ExportList)]),
  272. %% getv(K) -> V
  273. Function = erl_syntax:function(erl_syntax:atom(getv), lookup_clauses(KVs, [])),
  274. [erl_syntax:revert(X) || X <- [Mod, Export, Function]].
  275. lookup_clause(Key, Value) ->
  276. Var = erl_syntax:abstract(Key),
  277. Body = erl_syntax:abstract(Value),
  278. erl_syntax:clause([Var], [], [Body]).
  279. lookup_clause_anon() ->
  280. Var = erl_syntax:variable("_"),
  281. Body = erl_syntax:atom(undefined),
  282. erl_syntax:clause([Var], [], [Body]).
  283. lookup_clauses([], Acc) ->
  284. lists:reverse(lists:flatten([lookup_clause_anon() | Acc]));
  285. lookup_clauses([{Key, Value} | T], Acc) ->
  286. lookup_clauses(T, [lookup_clause(Key, Value) | Acc]).