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.

448 lines
15 KiB

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