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.

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