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.

204 rivejä
8.2 KiB

10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
10 vuotta sitten
  1. -module(rebar_app_discover).
  2. -export([do/2,
  3. format_error/1,
  4. find_unbuilt_apps/1,
  5. find_apps/1,
  6. find_apps/2,
  7. find_app/2]).
  8. -include_lib("providers/include/providers.hrl").
  9. do(State, LibDirs) ->
  10. BaseDir = rebar_state:dir(State),
  11. Dirs = [filename:join(BaseDir, LibDir) || LibDir <- LibDirs],
  12. Apps = find_apps(Dirs, all),
  13. ProjectDeps = rebar_state:deps_names(State),
  14. DepsDir = rebar_dir:deps_dir(State),
  15. %% Sort apps so we get the same merged deps config everytime
  16. SortedApps = rebar_utils:sort_deps(Apps),
  17. lists:foldl(fun(AppInfo, StateAcc) ->
  18. {AppInfo1, StateAcc1} = merge_deps(AppInfo, StateAcc),
  19. Name = rebar_app_info:name(AppInfo),
  20. OutDir = filename:join(DepsDir, Name),
  21. AppInfo2 = rebar_app_info:out_dir(AppInfo1, OutDir),
  22. ProjectDeps1 = lists:delete(Name, ProjectDeps),
  23. rebar_state:project_apps(StateAcc1
  24. ,rebar_app_info:deps(AppInfo2, ProjectDeps1))
  25. end, State, SortedApps).
  26. format_error({module_list, File}) ->
  27. io_lib:format("Error reading module list from ~p~n", [File]);
  28. format_error({missing_module, Module}) ->
  29. io_lib:format("Module defined in app file missing: ~p~n", [Module]).
  30. merge_deps(AppInfo, State) ->
  31. Default = rebar_state:default(State),
  32. CurrentProfiles = rebar_state:current_profiles(State),
  33. Name = rebar_app_info:name(AppInfo),
  34. C = project_app_config(AppInfo, State),
  35. %% We reset the opts here to default so no profiles are applied multiple times
  36. AppState = rebar_state:apply_overrides(
  37. rebar_state:apply_profiles(
  38. rebar_state:new(reset_hooks(rebar_state:opts(State, Default)), C,
  39. rebar_app_info:dir(AppInfo)), CurrentProfiles), Name),
  40. AppInfo1 = rebar_app_info:state(AppInfo, AppState),
  41. State1 = lists:foldl(fun(Profile, StateAcc) ->
  42. AppProfDeps = rebar_state:get(AppState, {deps, Profile}, []),
  43. TopLevelProfDeps = rebar_state:get(StateAcc, {deps, Profile}, []),
  44. ProfDeps2 = dedup(rebar_utils:tup_umerge(
  45. rebar_utils:tup_sort(TopLevelProfDeps)
  46. ,rebar_utils:tup_sort(AppProfDeps))),
  47. rebar_state:set(StateAcc, {deps, Profile}, ProfDeps2)
  48. end, State, lists:reverse(CurrentProfiles)),
  49. {AppInfo1, State1}.
  50. project_app_config(AppInfo, State) ->
  51. C = rebar_config:consult(rebar_app_info:dir(AppInfo)),
  52. Dir = rebar_app_info:dir(AppInfo),
  53. maybe_reset_hooks(C, Dir, State).
  54. %% Here we check if the app is at the root of the project.
  55. %% If it is, then drop the hooks from the config so they aren't run twice
  56. maybe_reset_hooks(C, Dir, State) ->
  57. case ec_file:real_dir_path(rebar_dir:root_dir(State)) of
  58. Dir ->
  59. C1 = proplists:delete(provider_hooks, C),
  60. proplists:delete(post_hooks, proplists:delete(pre_hooks, C1));
  61. _ ->
  62. C
  63. end.
  64. reset_hooks(State) ->
  65. lists:foldl(fun(Key, StateAcc) ->
  66. rebar_state:set(StateAcc, Key, [])
  67. end, State, [post_hooks, pre_hooks, provider_hooks]).
  68. -spec all_app_dirs(list(file:name())) -> list(file:name()).
  69. all_app_dirs(LibDirs) ->
  70. lists:flatmap(fun(LibDir) ->
  71. app_dirs(LibDir)
  72. end, LibDirs).
  73. app_dirs(LibDir) ->
  74. Path1 = filename:join([LibDir,
  75. "*",
  76. "src",
  77. "*.app.src"]),
  78. Path2 = filename:join([LibDir,
  79. "src",
  80. "*.app.src"]),
  81. Path3 = filename:join([LibDir,
  82. "*",
  83. "ebin",
  84. "*.app"]),
  85. Path4 = filename:join([LibDir,
  86. "ebin",
  87. "*.app"]),
  88. lists:usort(lists:foldl(fun(Path, Acc) ->
  89. Files = filelib:wildcard(ec_cnv:to_list(Path)),
  90. [app_dir(File) || File <- Files] ++ Acc
  91. end, [], [Path1, Path2, Path3, Path4])).
  92. find_unbuilt_apps(LibDirs) ->
  93. find_apps(LibDirs, invalid).
  94. -spec find_apps([file:filename_all()]) -> [rebar_app_info:t()].
  95. find_apps(LibDirs) ->
  96. find_apps(LibDirs, valid).
  97. -spec find_apps([file:filename_all()], valid | invalid | all) -> [rebar_app_info:t()].
  98. find_apps(LibDirs, Validate) ->
  99. rebar_utils:filtermap(fun(AppDir) ->
  100. find_app(AppDir, Validate)
  101. end, all_app_dirs(LibDirs)).
  102. -spec find_app(file:filename_all(), valid | invalid | all) -> {true, rebar_app_info:t()} | false.
  103. find_app(AppDir, Validate) ->
  104. AppFile = filelib:wildcard(filename:join([AppDir, "ebin", "*.app"])),
  105. AppSrcFile = filelib:wildcard(filename:join([AppDir, "src", "*.app.src"])),
  106. case AppFile of
  107. [File] ->
  108. AppInfo = create_app_info(AppDir, File),
  109. AppInfo1 = rebar_app_info:app_file(AppInfo, File),
  110. AppInfo2 = case AppSrcFile of
  111. [F] ->
  112. rebar_app_info:app_file_src(AppInfo1, F);
  113. [] ->
  114. AppInfo1;
  115. Other when is_list(Other) ->
  116. throw({error, {multiple_app_files, Other}})
  117. end,
  118. case Validate of
  119. valid ->
  120. case rebar_app_utils:validate_application_info(AppInfo2) of
  121. true ->
  122. {true, AppInfo2};
  123. _ ->
  124. false
  125. end;
  126. invalid ->
  127. case rebar_app_utils:validate_application_info(AppInfo2) of
  128. true ->
  129. false;
  130. _ ->
  131. {true, AppInfo2}
  132. end;
  133. all ->
  134. {true, AppInfo2}
  135. end;
  136. [] ->
  137. case AppSrcFile of
  138. [File] ->
  139. case Validate of
  140. V when V =:= invalid ; V =:= all ->
  141. AppInfo = create_app_info(AppDir, File),
  142. case AppInfo of
  143. {error, Reason} ->
  144. throw({error, {invalid_app_file, File, Reason}});
  145. _ ->
  146. {true, rebar_app_info:app_file_src(AppInfo, File)}
  147. end;
  148. valid ->
  149. false
  150. end;
  151. [] ->
  152. false;
  153. Other when is_list(Other) ->
  154. throw({error, {multiple_app_files, Other}})
  155. end;
  156. Other when is_list(Other) ->
  157. throw({error, {multiple_app_files, Other}})
  158. end.
  159. app_dir(AppFile) ->
  160. filename:join(rebar_utils:droplast(filename:split(filename:dirname(AppFile)))).
  161. -spec create_app_info(file:name(), file:name()) -> rebar_app_info:t() | {error, term()}.
  162. create_app_info(AppDir, AppFile) ->
  163. [{application, AppName, AppDetails}] = rebar_file_utils:try_consult(AppFile),
  164. AppVsn = proplists:get_value(vsn, AppDetails),
  165. Applications = proplists:get_value(applications, AppDetails, []),
  166. IncludedApplications = proplists:get_value(included_applications, AppDetails, []),
  167. {ok, AppInfo} = rebar_app_info:new(AppName, AppVsn, AppDir, []),
  168. AppInfo1 = rebar_app_info:applications(
  169. rebar_app_info:app_details(AppInfo, AppDetails),
  170. IncludedApplications++Applications),
  171. Valid = case rebar_app_utils:validate_application_info(AppInfo1) of
  172. true ->
  173. true;
  174. _ ->
  175. false
  176. end,
  177. rebar_app_info:dir(rebar_app_info:valid(AppInfo1, Valid), AppDir).
  178. dedup([]) -> [];
  179. dedup([A]) -> [A];
  180. dedup([H,H|T]) -> dedup([H|T]);
  181. dedup([H|T]) -> [H|dedup(T)].