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.

364 lines
13 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. -module(rebar_state).
  2. -export([new/0, new/1, new/2, new/3,
  3. get/2, get/3, set/3,
  4. opts/1, opts/2,
  5. default/1, default/2,
  6. escript_path/1, escript_path/2,
  7. lock/1, lock/2,
  8. current_profiles/1,
  9. command_args/1, command_args/2,
  10. command_parsed_args/1, command_parsed_args/2,
  11. add_to_profile/3, apply_profiles/2,
  12. dir/1, dir/2,
  13. create_logic_providers/2,
  14. project_apps/1, project_apps/2,
  15. deps_to_build/1, deps_to_build/2,
  16. all_deps/1, all_deps/2,
  17. namespace/1, namespace/2,
  18. deps_names/1,
  19. overrides/1, overrides/2,
  20. apply_overrides/2,
  21. providers/1, providers/2, add_provider/2]).
  22. -include("rebar.hrl").
  23. -record(state_t, {dir :: file:name(),
  24. opts = dict:new() :: rebar_dict(),
  25. default = dict:new() :: rebar_dict(),
  26. escript_path :: undefined | file:filename_all(),
  27. lock = [],
  28. current_profiles = [default] :: [atom()],
  29. namespace = undefined :: [atom()],
  30. command_args = [],
  31. command_parsed_args = [],
  32. project_apps = [] :: [rebar_app_info:t()],
  33. deps_to_build = [] :: [rebar_app_info:t()],
  34. all_deps = [] :: [rebar_app_info:t()],
  35. overrides = [],
  36. providers = []}).
  37. -export_type([t/0]).
  38. -type t() :: record(state_t).
  39. -spec new() -> t().
  40. new() ->
  41. #state_t{dir = rebar_dir:get_cwd()}.
  42. -spec new(list()) -> t().
  43. new(Config) when is_list(Config) ->
  44. Deps = proplists:get_value(deps, Config, []),
  45. Opts = dict:from_list([{{deps, default}, Deps} | Config]),
  46. #state_t { dir = rebar_dir:get_cwd(),
  47. default = Opts,
  48. opts = Opts }.
  49. -spec new(t() | atom(), list()) -> t().
  50. new(Profile, Config) when is_atom(Profile)
  51. , is_list(Config) ->
  52. Deps = proplists:get_value(deps, Config, []),
  53. Opts = dict:from_list([{{deps, default}, Deps} | Config]),
  54. #state_t { dir = rebar_dir:get_cwd(),
  55. current_profiles = [Profile],
  56. default = Opts,
  57. opts = Opts };
  58. new(ParentState=#state_t{}, Config) ->
  59. %% Load terms from rebar.config, if it exists
  60. Dir = rebar_dir:get_cwd(),
  61. new(ParentState, Config, Dir).
  62. -spec new(t(), list(), file:name()) -> t().
  63. new(ParentState, Config, Dir) ->
  64. Opts = ParentState#state_t.opts,
  65. LocalOpts = case rebar_config:consult_file(filename:join(Dir, ?LOCK_FILE)) of
  66. [D] ->
  67. %% We want the top level deps only from the lock file.
  68. %% This ensures deterministic overrides for configs.
  69. Deps = [X || X <- D, element(3, X) =:= 0],
  70. dict:from_list([{{locks, default}, D}, {{deps, default}, Deps} | Config]);
  71. _ ->
  72. D = proplists:get_value(deps, Config, []),
  73. dict:from_list([{{deps, default}, D} | Config])
  74. end,
  75. NewOpts = merge_opts(LocalOpts, Opts),
  76. ParentState#state_t{dir=Dir
  77. ,opts=NewOpts
  78. ,default=NewOpts}.
  79. get(State, Key) ->
  80. {ok, Value} = dict:find(Key, State#state_t.opts),
  81. Value.
  82. get(State, Key, Default) ->
  83. case dict:find(Key, State#state_t.opts) of
  84. {ok, Value} ->
  85. Value;
  86. error ->
  87. Default
  88. end.
  89. -spec set(t(), any(), any()) -> t().
  90. set(State=#state_t{opts=Opts}, Key, Value) ->
  91. State#state_t{ opts = dict:store(Key, Value, Opts) }.
  92. default(#state_t{default=Opts}) ->
  93. Opts.
  94. default(State, Opts) ->
  95. State#state_t{default=Opts}.
  96. opts(#state_t{opts=Opts}) ->
  97. Opts.
  98. opts(State, Opts) ->
  99. State#state_t{opts=Opts}.
  100. current_profiles(#state_t{current_profiles=Profiles}) ->
  101. Profiles.
  102. lock(#state_t{lock=Lock}) ->
  103. Lock.
  104. lock(State=#state_t{}, Apps) when is_list(Apps) ->
  105. State#state_t{lock=Apps};
  106. lock(State=#state_t{lock=Lock}, App) ->
  107. State#state_t{lock=[App | Lock]}.
  108. escript_path(#state_t{escript_path=EscriptPath}) ->
  109. EscriptPath.
  110. escript_path(State, EscriptPath) ->
  111. State#state_t{escript_path=EscriptPath}.
  112. command_args(#state_t{command_args=CmdArgs}) ->
  113. CmdArgs.
  114. command_args(State, CmdArgs) ->
  115. State#state_t{command_args=CmdArgs}.
  116. command_parsed_args(#state_t{command_parsed_args=CmdArgs}) ->
  117. CmdArgs.
  118. command_parsed_args(State, CmdArgs) ->
  119. State#state_t{command_parsed_args=CmdArgs}.
  120. apply_overrides(State=#state_t{overrides=Overrides}, AppName) ->
  121. Name = binary_to_atom(AppName, utf8),
  122. %% Inefficient. We want the order we get here though.
  123. State1 = lists:foldl(fun({override, O}, StateAcc) ->
  124. lists:foldl(fun({Key, Value}, StateAcc1) ->
  125. rebar_state:set(StateAcc1, Key, Value)
  126. end, StateAcc, O);
  127. (_, StateAcc) ->
  128. StateAcc
  129. end, State, Overrides),
  130. State2 = lists:foldl(fun({override, N, O}, StateAcc) when N =:= Name ->
  131. lists:foldl(fun({Key, Value}, StateAcc1) ->
  132. rebar_state:set(StateAcc1, Key, Value)
  133. end, StateAcc, O);
  134. (_, StateAcc) ->
  135. StateAcc
  136. end, State1, Overrides),
  137. lists:foldl(fun({add, N, O}, StateAcc) when N =:= Name ->
  138. lists:foldl(fun({Key, Value}, StateAcc1) ->
  139. OldValue = rebar_state:get(StateAcc1, Key, []),
  140. rebar_state:set(StateAcc1, Key, Value++OldValue)
  141. end, StateAcc, O);
  142. (_, StateAcc) ->
  143. StateAcc
  144. end, State2, Overrides).
  145. add_to_profile(State, Profile, KVs) when is_atom(Profile), is_list(KVs) ->
  146. Profiles = rebar_state:get(State, profiles, []),
  147. ProfileOpts = dict:from_list(proplists:get_value(Profile, Profiles, [])),
  148. NewOpts = merge_opts(Profile, dict:from_list(KVs), ProfileOpts),
  149. NewProfiles = [{Profile, dict:to_list(NewOpts)}|lists:keydelete(Profile, 1, Profiles)],
  150. rebar_state:set(State, profiles, NewProfiles).
  151. apply_profiles(State, Profile) when not is_list(Profile) ->
  152. apply_profiles(State, [Profile]);
  153. apply_profiles(State, [default]) ->
  154. State;
  155. apply_profiles(State=#state_t{opts=Opts, current_profiles=CurrentProfiles}, Profiles) ->
  156. ConfigProfiles = rebar_state:get(State, profiles, []),
  157. {Profiles1, NewOpts} =
  158. lists:foldl(fun(default, {ProfilesAcc, OptsAcc}) ->
  159. {ProfilesAcc, OptsAcc};
  160. (Profile, {ProfilesAcc, OptsAcc}) ->
  161. ProfileOpts = dict:from_list(proplists:get_value(Profile, ConfigProfiles, [])),
  162. {[Profile]++ProfilesAcc, merge_opts(Profile, ProfileOpts, OptsAcc)}
  163. end, {[], Opts}, Profiles),
  164. State#state_t{current_profiles=CurrentProfiles++Profiles1, opts=NewOpts}.
  165. merge_opts(Profile, NewOpts, OldOpts) ->
  166. Opts = merge_opts(NewOpts, OldOpts),
  167. case dict:find(deps, NewOpts) of
  168. {ok, Value} ->
  169. dict:store({deps, Profile}, Value, Opts);
  170. error ->
  171. Opts
  172. end.
  173. merge_opts(NewOpts, OldOpts) ->
  174. dict:merge(fun(deps, NewValue, _OldValue) ->
  175. NewValue;
  176. ({deps, _}, NewValue, _OldValue) ->
  177. NewValue;
  178. (profiles, NewValue, OldValue) ->
  179. dict:to_list(merge_opts(dict:from_list(NewValue), dict:from_list(OldValue)));
  180. (_Key, NewValue, OldValue) when is_list(NewValue) ->
  181. case io_lib:printable_list(NewValue) of
  182. true when NewValue =:= [] ->
  183. case io_lib:printable_list(OldValue) of
  184. true ->
  185. NewValue;
  186. false ->
  187. OldValue
  188. end;
  189. true ->
  190. NewValue;
  191. false ->
  192. lists:umerge(lists:sort(NewValue), lists:sort(OldValue))
  193. end;
  194. (_Key, NewValue, _OldValue) ->
  195. NewValue
  196. end, NewOpts, OldOpts).
  197. dir(#state_t{dir=Dir}) ->
  198. Dir.
  199. dir(State=#state_t{}, Dir) ->
  200. State#state_t{dir=filename:absname(Dir)}.
  201. deps_names(Deps) when is_list(Deps) ->
  202. lists:map(fun(Dep) when is_tuple(Dep) ->
  203. ec_cnv:to_binary(element(1, Dep));
  204. (Dep) when is_atom(Dep) ->
  205. ec_cnv:to_binary(Dep)
  206. end, Deps);
  207. deps_names(State) ->
  208. Deps = rebar_state:get(State, deps, []),
  209. deps_names(Deps).
  210. overrides(#state_t{overrides=Overrides}) ->
  211. Overrides.
  212. overrides(State=#state_t{}, Overrides) ->
  213. State#state_t{overrides=Overrides}.
  214. project_apps(#state_t{project_apps=Apps}) ->
  215. Apps.
  216. project_apps(State=#state_t{}, NewApps) when is_list(NewApps) ->
  217. State#state_t{project_apps=NewApps};
  218. project_apps(State=#state_t{project_apps=Apps}, App) ->
  219. State#state_t{project_apps=lists:keystore(rebar_app_info:name(App), 2, Apps, App)}.
  220. deps_to_build(#state_t{deps_to_build=Apps}) ->
  221. Apps.
  222. deps_to_build(State=#state_t{deps_to_build=Apps}, NewApps) when is_list(NewApps) ->
  223. State#state_t{deps_to_build=Apps++NewApps};
  224. deps_to_build(State=#state_t{deps_to_build=Apps}, App) ->
  225. State#state_t{deps_to_build=lists:keystore(rebar_app_info:name(App), 2, Apps, App)}.
  226. all_deps(#state_t{all_deps=Apps}) ->
  227. Apps.
  228. all_deps(State=#state_t{}, NewApps) ->
  229. State#state_t{all_deps=NewApps}.
  230. namespace(#state_t{namespace=Namespace}) ->
  231. Namespace.
  232. namespace(State=#state_t{}, Namespace) ->
  233. State#state_t{namespace=Namespace}.
  234. providers(#state_t{providers=Providers}) ->
  235. Providers.
  236. providers(State, NewProviders) ->
  237. State#state_t{providers=NewProviders}.
  238. -spec add_provider(t(), providers:t()) -> t().
  239. add_provider(State=#state_t{providers=Providers}, Provider) ->
  240. State#state_t{providers=[Provider | Providers]}.
  241. create_logic_providers(ProviderModules, State0) ->
  242. try
  243. State1 = lists:foldl(fun(ProviderMod, StateAcc) ->
  244. case providers:new(ProviderMod, StateAcc) of
  245. {error, Reason} ->
  246. ?ERROR(Reason++"~n", []),
  247. StateAcc;
  248. {ok, StateAcc1} ->
  249. StateAcc1
  250. end
  251. end, State0, ProviderModules),
  252. apply_hooks(State1)
  253. catch
  254. C:T ->
  255. ?DEBUG("~p: ~p ~p", [C, T, erlang:get_stacktrace()]),
  256. throw({error, "Failed creating providers. Run with DEBUG=1 for stacktrace."})
  257. end.
  258. apply_hooks(State0) ->
  259. try
  260. Hooks = rebar_state:get(State0, provider_hooks, []),
  261. PreHooks = proplists:get_value(pre, Hooks, []),
  262. PostHooks = proplists:get_value(post, Hooks, []),
  263. State1 = lists:foldl(fun({Target, Hook}, StateAcc) ->
  264. prepend_hook(StateAcc, Target, Hook)
  265. end, State0, PreHooks),
  266. lists:foldl(fun({Target, Hook}, StateAcc) ->
  267. append_hook(StateAcc, Target, Hook)
  268. end, State1, PostHooks)
  269. catch
  270. C:T ->
  271. ?DEBUG("~p: ~p ~p", [C, T, erlang:get_stacktrace()]),
  272. throw({error, "Failed parsing provider hooks. Run with DEBUG=1 for stacktrace."})
  273. end.
  274. %% ===================================================================
  275. %% Internal functions
  276. %% ===================================================================
  277. prepend_hook(State=#state_t{providers=Providers}, Target, Hook) ->
  278. State#state_t{providers=add_hook(pre, Providers, Target, Hook)}.
  279. append_hook(State=#state_t{providers=Providers}, Target, Hook) ->
  280. State#state_t{providers=add_hook(post, Providers, Target, Hook)}.
  281. add_hook(Which, Providers, Target, Hook) ->
  282. Provider = providers:get_provider(Target, Providers),
  283. Hooks = providers:hooks(Provider),
  284. NewHooks = add_hook(Which, Hooks, Hook),
  285. NewProvider = providers:hooks(Provider, NewHooks),
  286. [NewProvider | lists:delete(Provider, Providers)].
  287. add_hook(pre, {PreHooks, PostHooks}, Hook) ->
  288. {[Hook | PreHooks], PostHooks};
  289. add_hook(post, {PreHooks, PostHooks}, Hook) ->
  290. {PreHooks, [Hook | PostHooks]}.