25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

448 lines
15 KiB

10 년 전
10 년 전
10 년 전
10 년 전
10 년 전
10 년 전
9 년 전
10 년 전
10 년 전
10 년 전
10 년 전
10 년 전
10 년 전
10 년 전
10 년 전
10 년 전
  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. %% ===================================================================