Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

248 строки
9.4 KiB

10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
  1. -module(rebar_prv_compile).
  2. -behaviour(provider).
  3. -export([init/1,
  4. do/1,
  5. format_error/1]).
  6. -export([compile/2,
  7. compile/3]).
  8. -include_lib("providers/include/providers.hrl").
  9. -include("rebar.hrl").
  10. -define(PROVIDER, compile).
  11. -define(DEPS, [lock]).
  12. %% ===================================================================
  13. %% Public API
  14. %% ===================================================================
  15. -spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
  16. init(State) ->
  17. State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
  18. {module, ?MODULE},
  19. {bare, true},
  20. {deps, ?DEPS},
  21. {example, "rebar3 compile"},
  22. {short_desc, "Compile apps .app.src and .erl files."},
  23. {desc, "Compile apps .app.src and .erl files."},
  24. {opts, []}])),
  25. {ok, State1}.
  26. -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
  27. do(State) ->
  28. DepsPaths = rebar_state:code_paths(State, all_deps),
  29. PluginDepsPaths = rebar_state:code_paths(State, all_plugin_deps),
  30. rebar_utils:remove_from_code_path(PluginDepsPaths),
  31. code:add_pathsa(DepsPaths),
  32. ProjectApps = rebar_state:project_apps(State),
  33. Providers = rebar_state:providers(State),
  34. Deps = rebar_state:deps_to_build(State),
  35. Cwd = rebar_state:dir(State),
  36. build_apps(State, Providers, Deps),
  37. {ok, ProjectApps1} = rebar_digraph:compile_order(ProjectApps),
  38. %% Run top level hooks *before* project apps compiled but *after* deps are
  39. rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State),
  40. ProjectApps2 = build_apps(State, Providers, ProjectApps1),
  41. State2 = rebar_state:project_apps(State, ProjectApps2),
  42. %% projects with structures like /apps/foo,/apps/bar,/test
  43. build_extra_dirs(State, ProjectApps2),
  44. State3 = update_code_paths(State2, ProjectApps2, DepsPaths),
  45. rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State2),
  46. case rebar_state:has_all_artifacts(State3) of
  47. {false, File} ->
  48. throw(?PRV_ERROR({missing_artifact, File}));
  49. true ->
  50. true
  51. end,
  52. rebar_utils:cleanup_code_path(rebar_state:code_paths(State3, default)
  53. ++ rebar_state:code_paths(State, all_plugin_deps)),
  54. {ok, State3}.
  55. -spec format_error(any()) -> iolist().
  56. format_error({missing_artifact, File}) ->
  57. io_lib:format("Missing artifact ~s", [File]);
  58. format_error(Reason) ->
  59. io_lib:format("~p", [Reason]).
  60. build_apps(State, Providers, Apps) ->
  61. [build_app(State, Providers, AppInfo) || AppInfo <- Apps].
  62. build_app(State, Providers, AppInfo) ->
  63. AppDir = rebar_app_info:dir(AppInfo),
  64. OutDir = rebar_app_info:out_dir(AppInfo),
  65. copy_app_dirs(AppInfo, AppDir, OutDir),
  66. compile(State, Providers, AppInfo).
  67. build_extra_dirs(State, Apps) ->
  68. BaseDir = rebar_state:dir(State),
  69. F = fun(App) -> rebar_app_info:dir(App) == BaseDir end,
  70. %% check that this app hasn't already been dealt with
  71. case lists:any(F, Apps) of
  72. false ->
  73. ProjOpts = rebar_state:opts(State),
  74. Extras = rebar_dir:extra_src_dirs(ProjOpts, []),
  75. [build_extra_dir(State, Dir) || Dir <- Extras];
  76. true -> ok
  77. end.
  78. build_extra_dir(_State, []) -> ok;
  79. build_extra_dir(State, Dir) ->
  80. case ec_file:is_dir(filename:join([rebar_state:dir(State), Dir])) of
  81. true ->
  82. BaseDir = filename:join([rebar_dir:base_dir(State), "extras"]),
  83. OutDir = filename:join([BaseDir, Dir]),
  84. filelib:ensure_dir(filename:join([OutDir, "dummy.beam"])),
  85. copy(rebar_state:dir(State), BaseDir, Dir),
  86. rebar_erlc_compiler:compile_dir(State, BaseDir, OutDir);
  87. false -> ok
  88. end.
  89. compile(State, AppInfo) ->
  90. compile(State, rebar_state:providers(State), AppInfo).
  91. compile(State, Providers, AppInfo) ->
  92. ?INFO("Compiling ~s", [rebar_app_info:name(AppInfo)]),
  93. AppDir = rebar_app_info:dir(AppInfo),
  94. AppInfo1 = rebar_hooks:run_all_hooks(AppDir, pre, ?PROVIDER, Providers, AppInfo, State),
  95. rebar_erlc_compiler:compile(AppInfo1),
  96. case rebar_otp_app:compile(State, AppInfo1) of
  97. {ok, AppInfo2} ->
  98. AppInfo3 = rebar_hooks:run_all_hooks(AppDir, post, ?PROVIDER, Providers, AppInfo2, State),
  99. has_all_artifacts(AppInfo3),
  100. AppInfo3;
  101. Error ->
  102. throw(Error)
  103. end.
  104. %% ===================================================================
  105. %% Internal functions
  106. %% ===================================================================
  107. update_code_paths(State, ProjectApps, DepsPaths) ->
  108. ProjAppsPaths = paths_for_apps(ProjectApps),
  109. ExtrasPaths = paths_for_extras(State, ProjectApps),
  110. rebar_state:code_paths(State, all_deps, DepsPaths ++ ProjAppsPaths ++ ExtrasPaths).
  111. paths_for_apps(Apps) -> paths_for_apps(Apps, []).
  112. paths_for_apps([], Acc) -> Acc;
  113. paths_for_apps([App|Rest], Acc) ->
  114. {_SrcDirs, ExtraDirs} = resolve_src_dirs(rebar_app_info:opts(App)),
  115. Paths = [filename:join([rebar_app_info:out_dir(App), Dir]) || Dir <- ["ebin"|ExtraDirs]],
  116. FilteredPaths = lists:filter(fun ec_file:is_dir/1, Paths),
  117. paths_for_apps(Rest, Acc ++ FilteredPaths).
  118. paths_for_extras(State, Apps) ->
  119. F = fun(App) -> rebar_app_info:dir(App) == rebar_state:dir(State) end,
  120. %% check that this app hasn't already been dealt with
  121. case lists:any(F, Apps) of
  122. false -> paths_for_extras(State);
  123. true -> []
  124. end.
  125. paths_for_extras(State) ->
  126. {_SrcDirs, ExtraDirs} = resolve_src_dirs(rebar_state:opts(State)),
  127. Paths = [filename:join([rebar_dir:base_dir(State), "extras", Dir]) || Dir <- ExtraDirs],
  128. lists:filter(fun ec_file:is_dir/1, Paths).
  129. has_all_artifacts(AppInfo1) ->
  130. case rebar_app_info:has_all_artifacts(AppInfo1) of
  131. {false, File} ->
  132. throw(?PRV_ERROR({missing_artifact, File}));
  133. true ->
  134. true
  135. end.
  136. copy_app_dirs(AppInfo, OldAppDir, AppDir) ->
  137. case ec_cnv:to_binary(filename:absname(OldAppDir)) =/=
  138. ec_cnv:to_binary(filename:absname(AppDir)) of
  139. true ->
  140. EbinDir = filename:join([OldAppDir, "ebin"]),
  141. %% copy all files from ebin if it exists
  142. case filelib:is_dir(EbinDir) of
  143. true ->
  144. OutEbin = filename:join([AppDir, "ebin"]),
  145. filelib:ensure_dir(filename:join([OutEbin, "dummy.beam"])),
  146. rebar_file_utils:cp_r(filelib:wildcard(filename:join([EbinDir, "*"])), OutEbin);
  147. false ->
  148. ok
  149. end,
  150. filelib:ensure_dir(filename:join([AppDir, "dummy"])),
  151. %% link or copy mibs if it exists
  152. case filelib:is_dir(filename:join([OldAppDir, "mibs"])) of
  153. true ->
  154. %% If mibs exist it means we must ensure priv exists.
  155. %% mibs files are compiled to priv/mibs/
  156. filelib:ensure_dir(filename:join([OldAppDir, "priv", "dummy"])),
  157. symlink_or_copy(OldAppDir, AppDir, "mibs");
  158. false ->
  159. ok
  160. end,
  161. {SrcDirs, ExtraDirs} = resolve_src_dirs(rebar_app_info:opts(AppInfo)),
  162. %% link to src_dirs to be adjacent to ebin is needed for R15 use of cover/xref
  163. [symlink_or_copy(OldAppDir, AppDir, Dir) || Dir <- ["priv", "include"] ++ SrcDirs],
  164. %% copy all extra_src_dirs as they build into themselves and linking means they
  165. %% are shared across profiles
  166. [copy(OldAppDir, AppDir, Dir) || Dir <- ExtraDirs];
  167. false ->
  168. ok
  169. end.
  170. symlink_or_copy(OldAppDir, AppDir, Dir) ->
  171. Source = filename:join([OldAppDir, Dir]),
  172. Target = filename:join([AppDir, Dir]),
  173. rebar_file_utils:symlink_or_copy(Source, Target).
  174. copy(OldAppDir, AppDir, Dir) ->
  175. Source = filename:join([OldAppDir, Dir]),
  176. Target = filename:join([AppDir, Dir]),
  177. case ec_file:is_dir(Source) of
  178. true -> copy(Source, Target);
  179. false -> ok
  180. end.
  181. %% TODO: use ec_file:copy/2 to do this, it preserves timestamps and
  182. %% may prevent recompilation of files in extra dirs
  183. copy(Source, Target) ->
  184. %% important to do this so no files are copied onto themselves
  185. %% which truncates them to zero length on some platforms
  186. ok = delete_if_symlink(Target),
  187. ok = filelib:ensure_dir(filename:join([Target, "dummy.beam"])),
  188. {ok, Files} = rebar_utils:list_dir(Source),
  189. case [filename:join([Source, F]) || F <- Files] of
  190. [] -> ok;
  191. Paths -> rebar_file_utils:cp_r(Paths, Target)
  192. end.
  193. delete_if_symlink(Path) ->
  194. case ec_file:is_symlink(Path) of
  195. true -> file:delete(Path);
  196. false -> ok
  197. end.
  198. resolve_src_dirs(Opts) ->
  199. SrcDirs = rebar_dir:src_dirs(Opts, ["src"]),
  200. ExtraDirs = rebar_dir:extra_src_dirs(Opts, []),
  201. normalize_src_dirs(SrcDirs, ExtraDirs).
  202. %% remove duplicates and make sure no directories that exist
  203. %% in src_dirs also exist in extra_src_dirs
  204. normalize_src_dirs(SrcDirs, ExtraDirs) ->
  205. S = lists:usort(SrcDirs),
  206. E = lists:usort(ExtraDirs),
  207. {S, lists:subtract(E, S)}.