選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

411 行
14 KiB

  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. %% ===================================================================