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

232 lines
8.1 KiB

15 년 전
15 년 전
15 년 전
15 년 전
15 년 전
15 년 전
15 년 전
15 년 전
  1. %% -------------------------------------------------------------------
  2. %%
  3. %% rebar: Erlang Build Tools
  4. %%
  5. %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com)
  6. %%
  7. %% Permission is hereby granted, free of charge, to any person obtaining a copy
  8. %% of this software and associated documentation files (the "Software"), to deal
  9. %% in the Software without restriction, including without limitation the rights
  10. %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  11. %% copies of the Software, and to permit persons to whom the Software is
  12. %% furnished to do so, subject to the following conditions:
  13. %%
  14. %% The above copyright notice and this permission notice shall be included in
  15. %% all copies or substantial portions of the Software.
  16. %%
  17. %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  18. %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  19. %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  20. %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  21. %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  22. %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  23. %% THE SOFTWARE.
  24. %% -------------------------------------------------------------------
  25. -module(rebar_erlc_compiler).
  26. -export([compile/2,
  27. clean/2]).
  28. %% make available for rebar_eunit until there is a better option
  29. -export([do_compile/8, compile_opts/2, list_hrls/2]).
  30. -include("rebar.hrl").
  31. %% ===================================================================
  32. %% Public API
  33. %% ===================================================================
  34. compile(Config, _AppFile) ->
  35. do_compile(Config, "src/*.erl", "ebin", ".erl", ".beam",
  36. fun list_hrls/2, fun compile_erl/2,
  37. rebar_config:get_list(Config, erl_first_files, [])),
  38. do_compile(Config, "mibs/*.mib", "priv/mibs", ".mib", ".bin",
  39. undefined, fun compile_mib/2,
  40. rebar_config:get_list(Config, mib_first_files, [])).
  41. clean(_Config, _AppFile) ->
  42. %% TODO: This would be more portable if it used Erlang to traverse
  43. %% the dir structure and delete each file; however it would also
  44. %% much slower.
  45. [] = os:cmd("rm -f ebin/*.beam priv/mibs/*.bin"),
  46. ok.
  47. %% ===================================================================
  48. %% Internal functions
  49. %% ===================================================================
  50. do_compile(Config, SrcWildcard, OutDir, InExt, OutExt,
  51. IncludeFn, CompileFn, FirstFiles) ->
  52. case filelib:wildcard(SrcWildcard) of
  53. [] ->
  54. ok;
  55. FoundFiles when is_list(FoundFiles) ->
  56. %% Ensure that the FirstFiles are compiled first; drop them from the
  57. %% FoundFiles and compile them in sequence
  58. FirstTargets = [{Fs, target_file(Fs, OutDir, InExt, OutExt)} || Fs <- FirstFiles],
  59. RestTargets = [{Fs, target_file(Fs, OutDir, InExt, OutExt)} ||
  60. Fs <- drop_each(FirstFiles, FoundFiles)],
  61. %% Make sure target directory exists
  62. ok = filelib:ensure_dir(target_file(hd(FoundFiles), OutDir, InExt, OutExt)),
  63. %% Compile first targets in sequence
  64. compile_each(FirstTargets, Config, IncludeFn, CompileFn),
  65. %% Spin up workers
  66. Self = self(),
  67. Pids = [spawn_monitor(fun() -> compile_worker(Self) end) || _I <- lists:seq(1,3)],
  68. %% Process rest of targets
  69. compile_queue(Pids, RestTargets, Config, IncludeFn, CompileFn)
  70. end.
  71. drop_each([], List) ->
  72. List;
  73. drop_each([Member | Rest], List) ->
  74. drop_each(Rest, lists:delete(Member, List)).
  75. compile_each([], _Config, _IncludeFn, _CompileFn) ->
  76. ok;
  77. compile_each([{Src, Target} | Rest], Config, IncludeFn, CompileFn) ->
  78. case needs_compile(Src, Target, IncludeFn, Config) of
  79. true ->
  80. ?CONSOLE("Compiling ~s\n", [Src]),
  81. CompileFn(Src, Config);
  82. false ->
  83. ?INFO("Skipping ~s\n", [Src]),
  84. ok
  85. end,
  86. compile_each(Rest, Config, IncludeFn, CompileFn).
  87. needs_compile(Src, Target, IncludeFn, Config) ->
  88. TargetLM = filelib:last_modified(Target),
  89. case TargetLM < filelib:last_modified(Src) of
  90. true ->
  91. true;
  92. false ->
  93. if is_function(IncludeFn) ->
  94. lists:any(fun(I) ->
  95. TargetLM < filelib:last_modified(I)
  96. end,
  97. IncludeFn(Src, Config));
  98. true ->
  99. false
  100. end
  101. end.
  102. target_file(F, TargetDir, InExt, OutExt) ->
  103. filename:join([TargetDir, filename:basename(F, InExt) ++ OutExt]).
  104. compile_opts(Config, Key) ->
  105. rebar_config:get_list(Config, Key, []).
  106. list_hrls(Src, Config) ->
  107. case epp:open(Src, include_path(Src, Config)) of
  108. {ok, Epp} ->
  109. %% check include for erlang files
  110. extract_includes(Epp, Src);
  111. _ ->
  112. false
  113. end.
  114. include_path(Src, Config) ->
  115. [filename:dirname(Src)|compile_opts(Config, i)].
  116. extract_includes(Epp, Src) ->
  117. case epp:parse_erl_form(Epp) of
  118. {ok, {attribute, 1, file, {Src, 1}}} ->
  119. extract_includes(Epp, Src);
  120. {ok, {attribute, 1, file, {IncFile, 1}}} ->
  121. [IncFile|extract_includes(Epp, Src)];
  122. {ok, _} ->
  123. extract_includes(Epp, Src);
  124. {eof, _} ->
  125. epp:close(Epp),
  126. [];
  127. {error, _Error} ->
  128. extract_includes(Epp, Src)
  129. end.
  130. compile_erl(Source, Config) ->
  131. Opts = [{i, "include"}, {outdir, "ebin"}, report, return] ++ compile_opts(Config, erl_opts),
  132. case compile:file(Source, Opts) of
  133. {ok, _, []} ->
  134. ok;
  135. {ok, _, _Warnings} ->
  136. %% We got at least one warning -- if fail_on_warning is in options, fail
  137. case lists:member(fail_on_warning, Opts) of
  138. true ->
  139. ?FAIL;
  140. false ->
  141. ok
  142. end;
  143. _ ->
  144. ?FAIL
  145. end.
  146. compile_mib(Source, Config) ->
  147. Opts = [{outdir, "priv/mibs"}, {i, ["priv/mibs"]}] ++ compile_opts(Config, mib_opts),
  148. case snmpc:compile(Source, Opts) of
  149. {ok, _} ->
  150. ok;
  151. {error, compilation_failed} ->
  152. ?FAIL
  153. end.
  154. compile_queue([], [], _Config, _IncludeFn, _CompileFn) ->
  155. ok;
  156. compile_queue(Pids, Targets, Config, IncludeFn, CompileFn) ->
  157. receive
  158. {next, Worker} ->
  159. case Targets of
  160. [] ->
  161. Worker ! empty,
  162. compile_queue(Pids, Targets, Config, IncludeFn, CompileFn);
  163. [{Src, Target} | Rest] ->
  164. Worker ! {compile, Src, Target, Config, IncludeFn, CompileFn},
  165. compile_queue(Pids, Rest, Config, IncludeFn, CompileFn)
  166. end;
  167. {fail, Error} ->
  168. ?DEBUG("Worker compilation failed: ~p\n", [Error]),
  169. ?FAIL;
  170. {compiled, Source} ->
  171. ?CONSOLE("Compiled ~s\n", [Source]),
  172. compile_queue(Pids, Targets, Config, IncludeFn, CompileFn);
  173. {'DOWN', Mref, _, Pid, normal} ->
  174. ?DEBUG("Worker exited cleanly\n", []),
  175. Pids2 = lists:delete({Pid, Mref}, Pids),
  176. compile_queue(Pids2, Targets, Config, IncludeFn, CompileFn);
  177. {'DOWN', _Mref, _, _Pid, Info} ->
  178. ?DEBUG("Worker failed: ~p\n", [Info]),
  179. ?FAIL
  180. end.
  181. compile_worker(QueuePid) ->
  182. QueuePid ! {next, self()},
  183. receive
  184. {compile, Src, Target, Config, IncludeFn, CompileFn} ->
  185. case needs_compile(Src, Target, IncludeFn, Config) of
  186. true ->
  187. case catch(CompileFn(Src, Config)) of
  188. ok ->
  189. QueuePid ! {compiled, Src},
  190. compile_worker(QueuePid);
  191. Error ->
  192. QueuePid ! {fail, Error},
  193. ok
  194. end;
  195. false ->
  196. ?INFO("Skipping ~s\n", [Src]),
  197. compile_worker(QueuePid)
  198. end;
  199. empty ->
  200. ok
  201. end.