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

285 строки
13 KiB

10 лет назад
10 лет назад
9 лет назад
10 лет назад
9 лет назад
9 лет назад
9 лет назад
9 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
  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. find_app/3]).
  9. -include("rebar.hrl").
  10. -include_lib("providers/include/providers.hrl").
  11. do(State, LibDirs) ->
  12. BaseDir = rebar_state:dir(State),
  13. Dirs = [filename:join(BaseDir, LibDir) || LibDir <- LibDirs],
  14. Apps = find_apps(Dirs, all),
  15. ProjectDeps = rebar_state:deps_names(State),
  16. DepsDir = rebar_dir:deps_dir(State),
  17. CurrentProfiles = rebar_state:current_profiles(State),
  18. %% There may be a top level src which is an app and there may not
  19. %% Find it here if there is, otherwise define the deps parent as root
  20. TopLevelApp = define_root_app(Apps, State),
  21. %% Handle top level deps
  22. State1 = lists:foldl(fun(Profile, StateAcc) ->
  23. ProfileDeps = rebar_state:get(StateAcc, {deps, Profile}, []),
  24. ProfileDeps2 = rebar_utils:tup_dedup(ProfileDeps),
  25. StateAcc1 = rebar_state:set(StateAcc, {deps, Profile}, ProfileDeps2),
  26. ParsedDeps = parse_profile_deps(Profile
  27. ,TopLevelApp
  28. ,ProfileDeps2
  29. ,rebar_state:opts(StateAcc1)
  30. ,StateAcc1),
  31. rebar_state:set(StateAcc1, {parsed_deps, Profile}, ParsedDeps)
  32. end, State, lists:reverse(CurrentProfiles)),
  33. %% Handle sub project apps deps
  34. %% Sort apps so we get the same merged deps config everytime
  35. SortedApps = rebar_utils:sort_deps(Apps),
  36. lists:foldl(fun(AppInfo, StateAcc) ->
  37. Name = rebar_app_info:name(AppInfo),
  38. case enable(State, AppInfo) of
  39. true ->
  40. {AppInfo1, StateAcc1} = merge_deps(AppInfo, StateAcc),
  41. OutDir = filename:join(DepsDir, Name),
  42. AppInfo2 = rebar_app_info:out_dir(AppInfo1, OutDir),
  43. ProjectDeps1 = lists:delete(Name, ProjectDeps),
  44. rebar_state:project_apps(StateAcc1
  45. ,rebar_app_info:deps(AppInfo2, ProjectDeps1));
  46. false ->
  47. ?INFO("Ignoring ~s", [Name]),
  48. StateAcc
  49. end
  50. end, State1, SortedApps).
  51. define_root_app(Apps, State) ->
  52. RootDir = rebar_dir:root_dir(State),
  53. case ec_lists:find(fun(X) ->
  54. ec_file:real_dir_path(rebar_app_info:dir(X)) =:=
  55. ec_file:real_dir_path(RootDir)
  56. end, Apps) of
  57. {ok, App} ->
  58. rebar_app_info:name(App);
  59. error ->
  60. root
  61. end.
  62. format_error({module_list, File}) ->
  63. io_lib:format("Error reading module list from ~p~n", [File]);
  64. format_error({missing_module, Module}) ->
  65. io_lib:format("Module defined in app file missing: ~p~n", [Module]).
  66. merge_deps(AppInfo, State) ->
  67. %% These steps make sure that hooks and artifacts are run in the context of
  68. %% the application they are defined at. If an umbrella structure is used and
  69. %% they are deifned at the top level they will instead run in the context of
  70. %% the State and at the top level, not as part of an application.
  71. Default = reset_hooks(rebar_state:default(State)),
  72. {C, State1} = project_app_config(AppInfo, State),
  73. AppInfo0 = rebar_app_info:update_opts(AppInfo, Default, C),
  74. CurrentProfiles = rebar_state:current_profiles(State1),
  75. Name = rebar_app_info:name(AppInfo0),
  76. %% We reset the opts here to default so no profiles are applied multiple times
  77. AppInfo1 = rebar_app_info:apply_overrides(rebar_state:get(State1, overrides, []), AppInfo0),
  78. AppInfo2 = rebar_app_info:apply_profiles(AppInfo1, CurrentProfiles),
  79. %% Will throw an exception if checks fail
  80. rebar_app_info:verify_otp_vsn(AppInfo2),
  81. State2 = lists:foldl(fun(Profile, StateAcc) ->
  82. handle_profile(Profile, Name, AppInfo2, StateAcc)
  83. end, State1, lists:reverse(CurrentProfiles)),
  84. {AppInfo2, State2}.
  85. handle_profile(Profile, Name, AppInfo, State) ->
  86. TopParsedDeps = rebar_state:get(State, {parsed_deps, Profile}, {[], []}),
  87. TopLevelProfileDeps = rebar_state:get(State, {deps, Profile}, []),
  88. AppProfileDeps = rebar_app_info:get(AppInfo, {deps, Profile}, []),
  89. AppProfileDeps2 = rebar_utils:tup_dedup(AppProfileDeps),
  90. ProfileDeps2 = rebar_utils:tup_dedup(rebar_utils:tup_umerge(TopLevelProfileDeps
  91. ,AppProfileDeps2)),
  92. State1 = rebar_state:set(State, {deps, Profile}, ProfileDeps2),
  93. %% Only deps not also specified in the top level config need
  94. %% to be included in the parsed deps
  95. NewDeps = ProfileDeps2 -- TopLevelProfileDeps,
  96. ParsedDeps = parse_profile_deps(Profile, Name, NewDeps, rebar_app_info:opts(AppInfo), State1),
  97. State2 = rebar_state:set(State1, {deps, Profile}, ProfileDeps2),
  98. rebar_state:set(State2, {parsed_deps, Profile}, TopParsedDeps++ParsedDeps).
  99. parse_profile_deps(Profile, Name, Deps, Opts, State) ->
  100. DepsDir = rebar_prv_install_deps:profile_dep_dir(State, Profile),
  101. Locks = rebar_state:get(State, {locks, Profile}, []),
  102. rebar_app_utils:parse_deps(Name
  103. ,DepsDir
  104. ,Deps
  105. ,rebar_state:opts(State, Opts)
  106. ,Locks
  107. ,1).
  108. project_app_config(AppInfo, State) ->
  109. C = rebar_config:consult(rebar_app_info:dir(AppInfo)),
  110. Dir = rebar_app_info:dir(AppInfo),
  111. Opts = maybe_reset_hooks(Dir, rebar_state:opts(State), State),
  112. {C, rebar_state:opts(State, Opts)}.
  113. %% Here we check if the app is at the root of the project.
  114. %% If it is, then drop the hooks from the config so they aren't run twice
  115. maybe_reset_hooks(Dir, Opts, State) ->
  116. case ec_file:real_dir_path(rebar_dir:root_dir(State)) of
  117. Dir ->
  118. reset_hooks(Opts);
  119. _ ->
  120. Opts
  121. end.
  122. reset_hooks(Opts) ->
  123. lists:foldl(fun(Key, OptsAcc) ->
  124. rebar_opts:set(OptsAcc, Key, [])
  125. end, Opts, [post_hooks, pre_hooks, provider_hooks, artifacts]).
  126. -spec all_app_dirs(list(file:name())) -> list(file:name()).
  127. all_app_dirs(LibDirs) ->
  128. lists:flatmap(fun(LibDir) ->
  129. app_dirs(LibDir)
  130. end, LibDirs).
  131. app_dirs(LibDir) ->
  132. Path1 = filename:join([LibDir,
  133. "src",
  134. "*.app.src"]),
  135. Path2 = filename:join([LibDir,
  136. "src",
  137. "*.app.src.script"]),
  138. Path3 = filename:join([LibDir,
  139. "ebin",
  140. "*.app"]),
  141. lists:usort(lists:foldl(fun(Path, Acc) ->
  142. Files = filelib:wildcard(ec_cnv:to_list(Path)),
  143. [app_dir(File) || File <- Files] ++ Acc
  144. end, [], [Path1, Path2, Path3])).
  145. find_unbuilt_apps(LibDirs) ->
  146. find_apps(LibDirs, invalid).
  147. -spec find_apps([file:filename_all()]) -> [rebar_app_info:t()].
  148. find_apps(LibDirs) ->
  149. find_apps(LibDirs, valid).
  150. -spec find_apps([file:filename_all()], valid | invalid | all) -> [rebar_app_info:t()].
  151. find_apps(LibDirs, Validate) ->
  152. rebar_utils:filtermap(fun(AppDir) ->
  153. find_app(AppDir, Validate)
  154. end, all_app_dirs(LibDirs)).
  155. -spec find_app(file:filename_all(), valid | invalid | all) -> {true, rebar_app_info:t()} | false.
  156. find_app(AppDir, Validate) ->
  157. find_app(rebar_app_info:new(), AppDir, Validate).
  158. find_app(AppInfo, AppDir, Validate) ->
  159. AppFile = filelib:wildcard(filename:join([AppDir, "ebin", "*.app"])),
  160. AppSrcFile = filelib:wildcard(filename:join([AppDir, "src", "*.app.src"])),
  161. AppSrcScriptFile = filelib:wildcard(filename:join([AppDir, "src", "*.app.src.script"])),
  162. try_handle_app_file(AppInfo, AppFile, AppDir, AppSrcFile, AppSrcScriptFile, Validate).
  163. app_dir(AppFile) ->
  164. filename:join(rebar_utils:droplast(filename:split(filename:dirname(AppFile)))).
  165. -spec create_app_info(rebar_app_info:t(), file:name(), file:name()) -> rebar_app_info:t().
  166. create_app_info(AppInfo, AppDir, AppFile) ->
  167. [{application, AppName, AppDetails}] = rebar_config:consult_app_file(AppFile),
  168. AppVsn = proplists:get_value(vsn, AppDetails),
  169. Applications = proplists:get_value(applications, AppDetails, []),
  170. IncludedApplications = proplists:get_value(included_applications, AppDetails, []),
  171. AppInfo1 = rebar_app_info:name(
  172. rebar_app_info:original_vsn(
  173. rebar_app_info:dir(AppInfo, AppDir), AppVsn), AppName),
  174. AppInfo2 = rebar_app_info:applications(
  175. rebar_app_info:app_details(AppInfo1, AppDetails),
  176. IncludedApplications++Applications),
  177. Valid = case rebar_app_utils:validate_application_info(AppInfo2) =:= true
  178. andalso rebar_app_info:has_all_artifacts(AppInfo2) =:= true of
  179. true ->
  180. true;
  181. _ ->
  182. false
  183. end,
  184. rebar_app_info:dir(rebar_app_info:valid(AppInfo2, Valid), AppDir).
  185. %% Read in and parse the .app file if it is availabe. Do the same for
  186. %% the .app.src file if it exists.
  187. try_handle_app_file(AppInfo, [], AppDir, [], AppSrcScriptFile, Validate) ->
  188. try_handle_app_src_file(AppInfo, [], AppDir, AppSrcScriptFile, Validate);
  189. try_handle_app_file(AppInfo, [], AppDir, AppSrcFile, _, Validate) ->
  190. try_handle_app_src_file(AppInfo, [], AppDir, AppSrcFile, Validate);
  191. try_handle_app_file(AppInfo0, [File], AppDir, AppSrcFile, _, Validate) ->
  192. try create_app_info(AppInfo0, AppDir, File) of
  193. AppInfo ->
  194. AppInfo1 = rebar_app_info:app_file(AppInfo, File),
  195. AppInfo2 = case AppSrcFile of
  196. [F] ->
  197. rebar_app_info:app_file_src(AppInfo1, F);
  198. [] ->
  199. %% Set to undefined in case AppInfo previous had a .app.src
  200. rebar_app_info:app_file_src(AppInfo1, undefined);
  201. Other when is_list(Other) ->
  202. throw({error, {multiple_app_files, Other}})
  203. end,
  204. case Validate of
  205. valid ->
  206. case rebar_app_utils:validate_application_info(AppInfo2) of
  207. true ->
  208. {true, AppInfo2};
  209. _ ->
  210. false
  211. end;
  212. invalid ->
  213. case rebar_app_utils:validate_application_info(AppInfo2) of
  214. true ->
  215. false;
  216. _ ->
  217. {true, AppInfo2}
  218. end;
  219. all ->
  220. {true, AppInfo2}
  221. end
  222. catch
  223. throw:{error, {Module, Reason}} ->
  224. ?DEBUG("Falling back to app.src file because .app failed: ~s", [Module:format_error(Reason)]),
  225. try_handle_app_src_file(AppInfo0, File, AppDir, AppSrcFile, Validate)
  226. end;
  227. try_handle_app_file(_AppInfo, Other, _AppDir, _AppSrcFile, _, _Validate) ->
  228. throw({error, {multiple_app_files, Other}}).
  229. %% Read in the .app.src file if we aren't looking for a valid (already built) app
  230. try_handle_app_src_file(_AppInfo, _, _AppDir, [], _Validate) ->
  231. false;
  232. try_handle_app_src_file(_AppInfo, _, _AppDir, _AppSrcFile, valid) ->
  233. false;
  234. try_handle_app_src_file(AppInfo, _, AppDir, [File], Validate) when Validate =:= invalid
  235. ; Validate =:= all ->
  236. AppInfo1 = create_app_info(AppInfo, AppDir, File),
  237. case filename:extension(File) of
  238. ".script" ->
  239. {true, rebar_app_info:app_file_src_script(AppInfo1, File)};
  240. _ ->
  241. {true, rebar_app_info:app_file_src(AppInfo1, File)}
  242. end;
  243. try_handle_app_src_file(_AppInfo, _, _AppDir, Other, _Validate) ->
  244. throw({error, {multiple_app_files, Other}}).
  245. enable(State, AppInfo) ->
  246. not lists:member(to_atom(rebar_app_info:name(AppInfo)),
  247. rebar_state:get(State, excluded_apps, [])).
  248. to_atom(Bin) ->
  249. list_to_atom(binary_to_list(Bin)).