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