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

152 lines
6.5 KiB

10 년 전
10 년 전
  1. %% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
  2. %% ex: ts=4 sw=4 et
  3. -module(rebar_plugins).
  4. -export([project_plugins_install/1
  5. ,top_level_install/1
  6. ,project_apps_install/1
  7. ,install/2
  8. ,handle_plugins/3
  9. ,handle_plugins/4]).
  10. -include("rebar.hrl").
  11. %% ===================================================================
  12. %% Public API
  13. %% ===================================================================
  14. -spec project_plugins_install(rebar_state:t()) -> rebar_state:t().
  15. project_plugins_install(State) ->
  16. Profiles = rebar_state:current_profiles(State),
  17. State1 = rebar_state:allow_provider_overrides(State, true),
  18. State2 = lists:foldl(fun(Profile, StateAcc) ->
  19. Plugins = rebar_state:get(State, {project_plugins, Profile}, []),
  20. handle_plugins(Profile, Plugins, StateAcc)
  21. end, State1, Profiles),
  22. rebar_state:allow_provider_overrides(State2, false).
  23. -spec top_level_install(rebar_state:t()) -> rebar_state:t().
  24. top_level_install(State) ->
  25. Profiles = rebar_state:current_profiles(State),
  26. lists:foldl(fun(Profile, StateAcc) ->
  27. Plugins = rebar_state:get(State, {plugins, Profile}, []),
  28. handle_plugins(Profile, Plugins, StateAcc)
  29. end, State, Profiles).
  30. -spec project_apps_install(rebar_state:t()) -> rebar_state:t().
  31. project_apps_install(State) ->
  32. Profiles = rebar_state:current_profiles(State),
  33. ProjectApps = rebar_state:project_apps(State),
  34. lists:foldl(fun(Profile, StateAcc) ->
  35. Plugins = rebar_state:get(State, {plugins, Profile}, []),
  36. StateAcc1 = handle_plugins(Profile, Plugins, StateAcc),
  37. lists:foldl(fun(AppInfo, StateAcc2) ->
  38. C = rebar_config:consult(rebar_app_info:dir(AppInfo)),
  39. AppInfo0 = rebar_app_info:update_opts(AppInfo, rebar_app_info:opts(AppInfo), C),
  40. Plugins2 = rebar_app_info:get(AppInfo0, {plugins, Profile}, []),
  41. handle_plugins(Profile, Plugins2, StateAcc2)
  42. end, StateAcc1, ProjectApps)
  43. end, State, Profiles).
  44. -spec install(rebar_state:t(), rebar_app_info:t()) -> rebar_state:t().
  45. install(State, AppInfo) ->
  46. Profiles = rebar_state:current_profiles(State),
  47. %% don't lose the overrides of the dep we are processing plugins for
  48. Overrides = rebar_app_info:get(AppInfo, overrides, []),
  49. StateOverrides = rebar_state:get(State, overrides, []),
  50. AllOverrides = Overrides ++ StateOverrides,
  51. State1 = rebar_state:set(State, overrides, AllOverrides),
  52. State2 = lists:foldl(fun(Profile, StateAcc) ->
  53. Plugins = rebar_app_info:get(AppInfo, {plugins, Profile}, []),
  54. handle_plugins(Profile, Plugins, StateAcc)
  55. end, State1, Profiles),
  56. %% Reset the overrides after processing the dep
  57. rebar_state:set(State2, overrides, StateOverrides).
  58. handle_plugins(Profile, Plugins, State) ->
  59. handle_plugins(Profile, Plugins, State, false).
  60. handle_plugins(Profile, Plugins, State, Upgrade) ->
  61. %% Set deps dir to plugins dir so apps are installed there
  62. Locks = rebar_state:lock(State),
  63. DepsDir = rebar_state:get(State, deps_dir, ?DEFAULT_DEPS_DIR),
  64. State1 = rebar_state:set(State, deps_dir, ?DEFAULT_PLUGINS_DIR),
  65. %% Install each plugin individually so if one fails to install it doesn't effect the others
  66. {_PluginProviders, State2} =
  67. lists:foldl(fun(Plugin, {PluginAcc, StateAcc}) ->
  68. {NewPlugins, NewState} = handle_plugin(Profile, Plugin, StateAcc, Upgrade),
  69. NewState1 = rebar_state:create_logic_providers(NewPlugins, NewState),
  70. {PluginAcc++NewPlugins, NewState1}
  71. end, {[], State1}, Plugins),
  72. %% reset deps dir
  73. State3 = rebar_state:set(State2, deps_dir, DepsDir),
  74. rebar_state:lock(State3, Locks).
  75. handle_plugin(Profile, Plugin, State, Upgrade) ->
  76. try
  77. {Apps, State2} = rebar_prv_install_deps:handle_deps_as_profile(Profile, State, [Plugin], Upgrade),
  78. {no_cycle, Sorted} = rebar_prv_install_deps:find_cycles(Apps),
  79. ToBuild = rebar_prv_install_deps:cull_compile(Sorted, []),
  80. %% Add already built plugin deps to the code path
  81. CodePaths = [rebar_app_info:ebin_dir(A) || A <- Apps -- ToBuild],
  82. code:add_pathsa(CodePaths),
  83. %% Build plugin and its deps
  84. [build_plugin(AppInfo, Apps, State2) || AppInfo <- ToBuild],
  85. %% Add newly built deps and plugin to code path
  86. State3 = rebar_state:update_all_plugin_deps(State2, Apps),
  87. NewCodePaths = [rebar_app_info:ebin_dir(A) || A <- ToBuild],
  88. code:add_pathsa(CodePaths),
  89. %% Store plugin code paths so we can remove them when compiling project apps
  90. State4 = rebar_state:update_code_paths(State3, all_plugin_deps, CodePaths++NewCodePaths),
  91. {plugin_providers(Plugin), State4}
  92. catch
  93. C:T ->
  94. ?DEBUG("~p ~p ~p", [C, T, erlang:get_stacktrace()]),
  95. ?WARN("Plugin ~p not available. It will not be used.", [Plugin]),
  96. {[], State}
  97. end.
  98. build_plugin(AppInfo, Apps, State) ->
  99. Providers = rebar_state:providers(State),
  100. %Providers1 = rebar_state:providers(rebar_app_info:state(AppInfo)),
  101. %rebar_app_info:state_or_new(State, AppInfo)
  102. S = rebar_state:all_deps(State, Apps),
  103. S1 = rebar_state:set(S, deps_dir, ?DEFAULT_PLUGINS_DIR),
  104. rebar_prv_compile:compile(S1, Providers, AppInfo).
  105. plugin_providers({Plugin, _, _, _}) when is_atom(Plugin) ->
  106. validate_plugin(Plugin);
  107. plugin_providers({Plugin, _, _}) when is_atom(Plugin) ->
  108. validate_plugin(Plugin);
  109. plugin_providers({Plugin, _}) when is_atom(Plugin) ->
  110. validate_plugin(Plugin);
  111. plugin_providers(Plugin) when is_atom(Plugin) ->
  112. validate_plugin(Plugin).
  113. validate_plugin(Plugin) ->
  114. _ = application:load(Plugin),
  115. case application:get_env(Plugin, providers) of
  116. {ok, Providers} ->
  117. Providers;
  118. undefined ->
  119. Exports = Plugin:module_info(exports),
  120. case lists:member({init,1}, Exports) of
  121. false ->
  122. ?WARN("Plugin ~p does not export init/1. It will not be used.", [Plugin]),
  123. [];
  124. true ->
  125. [Plugin]
  126. end
  127. end.