You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

160 lines
6.4 KiB

15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
15 years ago
  1. %% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*-
  2. %% ex: ts=4 sw=4 et
  3. %% -------------------------------------------------------------------
  4. %%
  5. %% rebar: Erlang Build Tools
  6. %%
  7. %% Copyright (c) 2009 Dave Smith (dizzyd@dizzyd.com)
  8. %%
  9. %% Permission is hereby granted, free of charge, to any person obtaining a copy
  10. %% of this software and associated documentation files (the "Software"), to deal
  11. %% in the Software without restriction, including without limitation the rights
  12. %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. %% copies of the Software, and to permit persons to whom the Software is
  14. %% furnished to do so, subject to the following conditions:
  15. %%
  16. %% The above copyright notice and this permission notice shall be included in
  17. %% all copies or substantial portions of the Software.
  18. %%
  19. %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. %% THE SOFTWARE.
  26. %% -------------------------------------------------------------------
  27. -module(rebar_erlc_compiler).
  28. -export([compile/2,
  29. clean/2]).
  30. -export([doterl_compile/2]).
  31. -include("rebar.hrl").
  32. %% ===================================================================
  33. %% Public API
  34. %% ===================================================================
  35. compile(Config, _AppFile) ->
  36. doterl_compile(Config, "ebin"),
  37. rebar_base_compiler:run(Config, rebar_config:get_list(Config, mib_first_files, []),
  38. "mibs", ".mib", "priv/mibs", ".bin",
  39. fun compile_mib/3).
  40. clean(_Config, _AppFile) ->
  41. %% TODO: This would be more portable if it used Erlang to traverse
  42. %% the dir structure and delete each file; however it would also
  43. %% much slower.
  44. ok = rebar_file_utils:rm_rf("ebin/*.beam priv/mibs/*.bin"),
  45. %% Erlang compilation is recursive, so it's possible that we have a nested
  46. %% directory structure in ebin with .beam files within. As such, we want
  47. %% to scan whatever is left in the ebin/ directory for sub-dirs which
  48. %% satisfy our criteria. TODO: Is there a better way to do this?
  49. BeamFiles = filelib:fold_files("ebin", "^.*\\.beam\$", true,
  50. fun(F, BeamFiles) -> BeamFiles ++ [F] end, []),
  51. rebar_file_utils:delete_each(BeamFiles),
  52. ok.
  53. %% ===================================================================
  54. %% .erl Compilation API (externally used by only eunit)
  55. %% ===================================================================
  56. doterl_compile(Config, Outdir) ->
  57. FirstErls = rebar_config:get_list(Config, erl_first_files, []),
  58. RestErls = [Source || Source <- rebar_utils:find_files("src", ".*.erl"),
  59. lists:member(Source, FirstErls) == false],
  60. rebar_base_compiler:run(Config, FirstErls, RestErls,
  61. fun(S, C) -> internal_erl_compile(S, C, Outdir) end).
  62. %% ===================================================================
  63. %% Internal functions
  64. %% ===================================================================
  65. include_path(Source, Config) ->
  66. ErlOpts = rebar_config:get(Config, erl_opts, []),
  67. [filename:dirname(Source)] ++ proplists:get_all_values(i, ErlOpts).
  68. inspect(Source, IncludePath) ->
  69. ModuleDefault = filename:basename(Source, ".erl"),
  70. case epp:open(Source, IncludePath) of
  71. {ok, Epp} ->
  72. inspect_epp(Epp, ModuleDefault, []);
  73. {error, Reason} ->
  74. ?DEBUG("Failed to inspect ~s: ~p\n", [Source, Reason]),
  75. {ModuleDefault, []}
  76. end.
  77. inspect_epp(Epp, Module, Includes) ->
  78. case epp:parse_erl_form(Epp) of
  79. {ok, {attribute, _, module, ActualModule}} when is_list(ActualModule) ->
  80. %% If the module name includes package info, we get a list of atoms...
  81. case is_list(ActualModule) of
  82. true ->
  83. ActualModuleStr = string:join([atom_to_list(P) || P <- ActualModule], ".");
  84. false ->
  85. ActualModuleStr = atom_to_list(ActualModule)
  86. end,
  87. inspect_epp(Epp, ActualModuleStr, Includes);
  88. {ok, {attribute, 1, file, {Module, 1}}} ->
  89. inspect_epp(Epp, Module, Includes);
  90. {ok, {attribute, 1, file, {IncFile, 1}}} ->
  91. inspect_epp(Epp, Module, [IncFile | Includes]);
  92. {eof, _} ->
  93. epp:close(Epp),
  94. {Module, Includes};
  95. _ ->
  96. inspect_epp(Epp, Module, Includes)
  97. end.
  98. needs_compile(Source, Target, Hrls) ->
  99. TargetLastMod = filelib:last_modified(Target),
  100. lists:any(fun(I) -> TargetLastMod < filelib:last_modified(I) end,
  101. [Source] ++ Hrls).
  102. internal_erl_compile(Source, Config, Outdir) ->
  103. %% Determine the target name and includes list by inspecting the source file
  104. {Module, Hrls} = inspect(Source, include_path(Source, Config)),
  105. %% Construct the target filename
  106. Target = filename:join([Outdir | string:tokens(Module, ".")]) ++ ".beam",
  107. %% If the file needs compilation, based on last mod date of includes or
  108. %% the target,
  109. case needs_compile(Source, Target, Hrls) of
  110. true ->
  111. Opts = [{i, "include"}, {outdir, filename:dirname(Target)}, report, return] ++
  112. rebar_config:get(Config, erl_opts, []),
  113. case compile:file(Source, Opts) of
  114. {ok, _, []} ->
  115. ok;
  116. {ok, _, _Warnings} ->
  117. %% We got at least one warning -- if fail_on_warning is in options, fail
  118. case lists:member(fail_on_warning, Opts) of
  119. true ->
  120. ?FAIL;
  121. false ->
  122. ok
  123. end;
  124. _ ->
  125. ?FAIL
  126. end;
  127. false ->
  128. skipped
  129. end.
  130. compile_mib(Source, _Target, Config) ->
  131. Opts = [{outdir, "priv/mibs"}, {i, ["priv/mibs"]}] ++
  132. rebar_config:get(Config, mib_opts, []),
  133. case snmpc:compile(Source, Opts) of
  134. {ok, _} ->
  135. ok;
  136. {error, compilation_failed} ->
  137. ?FAIL
  138. end.