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.

411 lines
14 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
9 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. format_error/1,
  5. has_all_artifacts/1,
  6. code_paths/2, code_paths/3, update_code_paths/3,
  7. opts/1, opts/2,
  8. default/1, default/2,
  9. escript_path/1, escript_path/2,
  10. lock/1, lock/2,
  11. current_profiles/1, current_profiles/2,
  12. command_args/1, command_args/2,
  13. command_parsed_args/1, command_parsed_args/2,
  14. add_to_profile/3, apply_profiles/2,
  15. dir/1, dir/2,
  16. create_logic_providers/2,
  17. current_app/1, current_app/2,
  18. project_apps/1, project_apps/2,
  19. deps_to_build/1, deps_to_build/2,
  20. all_plugin_deps/1, all_plugin_deps/2, update_all_plugin_deps/2,
  21. all_deps/1, all_deps/2, update_all_deps/2,
  22. namespace/1, namespace/2,
  23. deps_names/1,
  24. resources/1, resources/2, add_resource/2,
  25. providers/1, providers/2, add_provider/2]).
  26. -include("rebar.hrl").
  27. -include_lib("providers/include/providers.hrl").
  28. -record(state_t, {dir :: file:name(),
  29. opts = dict:new() :: rebar_dict(),
  30. code_paths = dict:new() :: rebar_dict(),
  31. default = dict:new() :: rebar_dict(),
  32. escript_path :: undefined | file:filename_all(),
  33. lock = [],
  34. current_profiles = [default] :: [atom()],
  35. namespace = default :: atom(),
  36. command_args = [],
  37. command_parsed_args = {[], []},
  38. current_app :: rebar_app_info:t(),
  39. project_apps = [] :: [rebar_app_info:t()],
  40. deps_to_build = [] :: [rebar_app_info:t()],
  41. all_plugin_deps = [] :: [rebar_app_info:t()],
  42. all_deps = [] :: [rebar_app_info:t()],
  43. resources = [],
  44. providers = []}).
  45. -export_type([t/0]).
  46. -type t() :: #state_t{}.
  47. -spec new() -> t().
  48. new() ->
  49. BaseState = base_state(),
  50. BaseState#state_t{dir = rebar_dir:get_cwd()}.
  51. -spec new(list()) -> t().
  52. new(Config) when is_list(Config) ->
  53. BaseState = base_state(),
  54. Opts = base_opts(Config),
  55. BaseState#state_t { dir = rebar_dir:get_cwd(),
  56. default = Opts,
  57. opts = Opts }.
  58. -spec new(t() | atom(), list()) -> t().
  59. new(Profile, Config) when is_atom(Profile)
  60. , is_list(Config) ->
  61. BaseState = base_state(),
  62. Opts = base_opts(Config),
  63. BaseState#state_t { dir = rebar_dir:get_cwd(),
  64. current_profiles = [Profile],
  65. default = Opts,
  66. opts = Opts };
  67. new(ParentState=#state_t{}, Config) ->
  68. %% Load terms from rebar.config, if it exists
  69. Dir = rebar_dir:get_cwd(),
  70. new(ParentState, Config, Dir).
  71. -spec new(t(), list(), file:filename_all()) -> t().
  72. new(ParentState, Config, Dir) ->
  73. new(ParentState, Config, deps_from_config(Dir, Config), Dir).
  74. new(ParentState, Config, Deps, Dir) ->
  75. Opts = ParentState#state_t.opts,
  76. Plugins = proplists:get_value(plugins, Config, []),
  77. Terms = Deps++[{{plugins, default}, Plugins} | Config],
  78. true = rebar_config:verify_config_format(Terms),
  79. LocalOpts = dict:from_list(Terms),
  80. NewOpts = rebar_opts:merge_opts(LocalOpts, Opts),
  81. ParentState#state_t{dir=Dir
  82. ,opts=NewOpts
  83. ,default=NewOpts}.
  84. deps_from_config(Dir, Config) ->
  85. case rebar_config:consult_lock_file(filename:join(Dir, ?LOCK_FILE)) of
  86. [] ->
  87. [{{deps, default}, proplists:get_value(deps, Config, [])}];
  88. D ->
  89. %% We want the top level deps only from the lock file.
  90. %% This ensures deterministic overrides for configs.
  91. Deps = [X || X <- D, element(3, X) =:= 0],
  92. [{{locks, default}, D}, {{deps, default}, Deps}]
  93. end.
  94. base_state() ->
  95. case application:get_env(rebar, resources) of
  96. undefined ->
  97. Resources = [];
  98. {ok, Resources} ->
  99. Resources
  100. end,
  101. #state_t{resources=Resources}.
  102. base_opts(Config) ->
  103. Deps = proplists:get_value(deps, Config, []),
  104. Plugins = proplists:get_value(plugins, Config, []),
  105. Terms = [{{deps, default}, Deps}, {{plugins, default}, Plugins} | Config],
  106. true = rebar_config:verify_config_format(Terms),
  107. dict:from_list(Terms).
  108. get(State, Key) ->
  109. {ok, Value} = dict:find(Key, State#state_t.opts),
  110. Value.
  111. get(State, Key, Default) ->
  112. case dict:find(Key, State#state_t.opts) of
  113. {ok, Value} ->
  114. Value;
  115. error ->
  116. Default
  117. end.
  118. -spec set(t(), any(), any()) -> t().
  119. set(State=#state_t{opts=Opts}, Key, Value) ->
  120. State#state_t{ opts = dict:store(Key, Value, Opts) }.
  121. default(#state_t{default=Opts}) ->
  122. Opts.
  123. default(State, Opts) ->
  124. State#state_t{default=Opts}.
  125. format_error({profile_not_list, Profile, Other}) ->
  126. io_lib:format("Profile config must be a list but for profile '~p' config given as:~n~p", [Profile, Other]).
  127. -spec has_all_artifacts(#state_t{}) -> true | {false, file:filename()}.
  128. has_all_artifacts(State) ->
  129. Artifacts = rebar_state:get(State, artifacts, []),
  130. Dir = rebar_dir:base_dir(State),
  131. all(Dir, Artifacts).
  132. all(_, []) ->
  133. true;
  134. all(Dir, [File|Artifacts]) ->
  135. case filelib:is_regular(filename:join(Dir, File)) of
  136. false ->
  137. ?DEBUG("Missing artifact ~s", [filename:join(Dir, File)]),
  138. {false, File};
  139. true ->
  140. all(Dir, Artifacts)
  141. end.
  142. -spec code_paths(#state_t{}, atom()) -> [file:filename()].
  143. code_paths(#state_t{code_paths=CodePaths}, Key) ->
  144. case dict:find(Key, CodePaths) of
  145. {ok, CodePath} ->
  146. CodePath;
  147. _ ->
  148. []
  149. end.
  150. -spec code_paths(#state_t{}, atom(), [file:filename()]) -> #state_t{}.
  151. code_paths(State=#state_t{code_paths=CodePaths}, Key, CodePath) ->
  152. State#state_t{code_paths=dict:store(Key, CodePath, CodePaths)}.
  153. -spec update_code_paths(#state_t{}, atom(), [file:filename()]) -> #state_t{}.
  154. update_code_paths(State=#state_t{code_paths=CodePaths}, Key, CodePath) ->
  155. case dict:is_key(Key, CodePaths) of
  156. true ->
  157. State#state_t{code_paths=dict:append_list(Key, CodePath, CodePaths)};
  158. false ->
  159. State#state_t{code_paths=dict:store(Key, CodePath, CodePaths)}
  160. end.
  161. opts(#state_t{opts=Opts}) ->
  162. Opts.
  163. opts(State, Opts) ->
  164. State#state_t{opts=Opts}.
  165. current_profiles(#state_t{current_profiles=Profiles}) ->
  166. Profiles.
  167. current_profiles(State, Profiles) ->
  168. State#state_t{current_profiles=Profiles}.
  169. lock(#state_t{lock=Lock}) ->
  170. Lock.
  171. lock(State=#state_t{}, Apps) when is_list(Apps) ->
  172. State#state_t{lock=Apps};
  173. lock(State=#state_t{lock=Lock}, App) ->
  174. State#state_t{lock=[App | Lock]}.
  175. escript_path(#state_t{escript_path=EscriptPath}) ->
  176. EscriptPath.
  177. escript_path(State, EscriptPath) ->
  178. State#state_t{escript_path=EscriptPath}.
  179. command_args(#state_t{command_args=CmdArgs}) ->
  180. CmdArgs.
  181. command_args(State, CmdArgs) ->
  182. State#state_t{command_args=CmdArgs}.
  183. command_parsed_args(#state_t{command_parsed_args=CmdArgs}) ->
  184. CmdArgs.
  185. command_parsed_args(State, CmdArgs) ->
  186. State#state_t{command_parsed_args=CmdArgs}.
  187. add_to_profile(State, Profile, KVs) when is_atom(Profile), is_list(KVs) ->
  188. Opts = rebar_opts:add_to_profile(opts(State), Profile, KVs),
  189. State#state_t{opts=Opts}.
  190. apply_profiles(State, Profile) when not is_list(Profile) ->
  191. apply_profiles(State, [Profile]);
  192. apply_profiles(State, [default]) ->
  193. State;
  194. apply_profiles(State=#state_t{default = Defaults, current_profiles=CurrentProfiles}, Profiles) ->
  195. AppliedProfiles = case Profiles of
  196. %% Head of list global profile is special, only for use by rebar3
  197. %% It does not clash if a user does `rebar3 as global...` but when
  198. %% it is the head we must make sure not to prepend `default`
  199. [global | _] ->
  200. Profiles;
  201. _ ->
  202. deduplicate(CurrentProfiles ++ Profiles)
  203. end,
  204. ConfigProfiles = rebar_state:get(State, profiles, []),
  205. NewOpts =
  206. lists:foldl(fun(default, OptsAcc) ->
  207. OptsAcc;
  208. (Profile, OptsAcc) ->
  209. case proplists:get_value(Profile, ConfigProfiles, []) of
  210. OptsList when is_list(OptsList) ->
  211. ProfileOpts = dict:from_list(OptsList),
  212. rebar_opts:merge_opts(Profile, ProfileOpts, OptsAcc);
  213. Other ->
  214. throw(?PRV_ERROR({profile_not_list, Profile, Other}))
  215. end
  216. end, Defaults, AppliedProfiles),
  217. State#state_t{current_profiles = AppliedProfiles, opts=NewOpts}.
  218. deduplicate(Profiles) ->
  219. do_deduplicate(lists:reverse(Profiles), []).
  220. do_deduplicate([], Acc) ->
  221. Acc;
  222. do_deduplicate([Head | Rest], Acc) ->
  223. case lists:member(Head, Acc) of
  224. true -> do_deduplicate(Rest, Acc);
  225. false -> do_deduplicate(Rest, [Head | Acc])
  226. end.
  227. dir(#state_t{dir=Dir}) ->
  228. Dir.
  229. dir(State=#state_t{}, Dir) ->
  230. State#state_t{dir=filename:absname(Dir)}.
  231. deps_names(Deps) when is_list(Deps) ->
  232. lists:map(fun(Dep) when is_tuple(Dep) ->
  233. ec_cnv:to_binary(element(1, Dep));
  234. (Dep) when is_atom(Dep) ->
  235. ec_cnv:to_binary(Dep)
  236. end, Deps);
  237. deps_names(State) ->
  238. Deps = rebar_state:get(State, deps, []),
  239. deps_names(Deps).
  240. current_app(#state_t{current_app=CurrentApp}) ->
  241. CurrentApp.
  242. current_app(State=#state_t{}, CurrentApp) ->
  243. State#state_t{current_app=CurrentApp}.
  244. project_apps(#state_t{project_apps=Apps}) ->
  245. Apps.
  246. project_apps(State=#state_t{}, NewApps) when is_list(NewApps) ->
  247. State#state_t{project_apps=NewApps};
  248. project_apps(State=#state_t{project_apps=Apps}, App) ->
  249. State#state_t{project_apps=lists:keystore(rebar_app_info:name(App), 2, Apps, App)}.
  250. deps_to_build(#state_t{deps_to_build=Apps}) ->
  251. Apps.
  252. deps_to_build(State=#state_t{deps_to_build=Apps}, NewApps) when is_list(NewApps) ->
  253. State#state_t{deps_to_build=Apps++NewApps};
  254. deps_to_build(State=#state_t{deps_to_build=Apps}, App) ->
  255. State#state_t{deps_to_build=lists:keystore(rebar_app_info:name(App), 2, Apps, App)}.
  256. all_deps(#state_t{all_deps=Apps}) ->
  257. Apps.
  258. all_deps(State=#state_t{}, NewApps) ->
  259. State#state_t{all_deps=NewApps}.
  260. all_plugin_deps(#state_t{all_plugin_deps=Apps}) ->
  261. Apps.
  262. all_plugin_deps(State=#state_t{}, NewApps) ->
  263. State#state_t{all_plugin_deps=NewApps}.
  264. update_all_plugin_deps(State=#state_t{all_plugin_deps=Apps}, NewApps) ->
  265. State#state_t{all_plugin_deps=Apps++NewApps}.
  266. update_all_deps(State=#state_t{all_deps=Apps}, NewApps) ->
  267. State#state_t{all_deps=Apps++NewApps}.
  268. namespace(#state_t{namespace=Namespace}) ->
  269. Namespace.
  270. namespace(State=#state_t{}, Namespace) ->
  271. State#state_t{namespace=Namespace}.
  272. -spec resources(t()) -> [{rebar_resource:type(), module()}].
  273. resources(#state_t{resources=Resources}) ->
  274. Resources.
  275. -spec resources(t(), [{rebar_resource:type(), module()}]) -> t().
  276. resources(State, NewResources) ->
  277. State#state_t{resources=NewResources}.
  278. -spec add_resource(t(), {rebar_resource:type(), module()}) -> t().
  279. add_resource(State=#state_t{resources=Resources}, Resource) ->
  280. State#state_t{resources=[Resource | Resources]}.
  281. providers(#state_t{providers=Providers}) ->
  282. Providers.
  283. providers(State, NewProviders) ->
  284. State#state_t{providers=NewProviders}.
  285. -spec add_provider(t(), providers:t()) -> t().
  286. add_provider(State=#state_t{providers=Providers}, Provider) ->
  287. Name = providers:impl(Provider),
  288. Namespace = providers:namespace(Provider),
  289. Module = providers:module(Provider),
  290. case lists:any(fun(P) ->
  291. case {providers:impl(P), providers:namespace(P)} of
  292. {Name, Namespace} ->
  293. ?DEBUG("Not adding provider ~p ~p from module ~p because it already exists from module ~p",
  294. [Namespace, Name, providers:module(P), Module]),
  295. true;
  296. _ ->
  297. false
  298. end
  299. end, Providers) of
  300. true ->
  301. State;
  302. false ->
  303. State#state_t{providers=[Provider | Providers]}
  304. end.
  305. create_logic_providers(ProviderModules, State0) ->
  306. try
  307. lists:foldl(fun(ProviderMod, StateAcc) ->
  308. case providers:new(ProviderMod, StateAcc) of
  309. {error, Reason} ->
  310. ?ERROR(Reason++"~n", []),
  311. StateAcc;
  312. {ok, StateAcc1} ->
  313. StateAcc1
  314. end
  315. end, State0, ProviderModules)
  316. catch
  317. C:T ->
  318. ?DEBUG("~p: ~p ~p", [C, T, erlang:get_stacktrace()]),
  319. throw({error, "Failed creating providers. Run with DEBUG=1 for stacktrace."})
  320. end.
  321. %% ===================================================================
  322. %% Internal functions
  323. %% ===================================================================