Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

477 righe
16 KiB

10 anni fa
10 anni fa
10 anni fa
10 anni fa
10 anni fa
10 anni fa
9 anni fa
10 anni fa
10 anni fa
10 anni fa
10 anni fa
10 anni fa
10 anni fa
10 anni fa
10 anni fa
10 anni fa
  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. create_resources/2,
  26. resources/1, resources/2, add_resource/2,
  27. providers/1, providers/2, add_provider/2,
  28. allow_provider_overrides/1, allow_provider_overrides/2
  29. ]).
  30. -include("rebar.hrl").
  31. -include_lib("providers/include/providers.hrl").
  32. -record(state_t, {dir :: file:name(),
  33. opts = dict:new() :: rebar_dict(),
  34. code_paths = dict:new() :: rebar_dict(),
  35. default = dict:new() :: rebar_dict(),
  36. escript_path :: undefined | file:filename_all(),
  37. lock = [],
  38. current_profiles = [default] :: [atom()],
  39. namespace = default :: atom(),
  40. command_args = [],
  41. command_parsed_args = {[], []},
  42. current_app :: undefined | rebar_app_info:t(),
  43. project_apps = [] :: [rebar_app_info:t()],
  44. deps_to_build = [] :: [rebar_app_info:t()],
  45. all_plugin_deps = [] :: [rebar_app_info:t()],
  46. all_deps = [] :: [rebar_app_info:t()],
  47. resources = [],
  48. providers = [],
  49. allow_provider_overrides = false :: boolean()}).
  50. -export_type([t/0]).
  51. -type t() :: #state_t{}.
  52. -spec new() -> t().
  53. new() ->
  54. BaseState = base_state(dict:new()),
  55. BaseState#state_t{dir = rebar_dir:get_cwd()}.
  56. -spec new(list()) -> t().
  57. new(Config) when is_list(Config) ->
  58. Opts = base_opts(Config),
  59. BaseState = base_state(Opts),
  60. BaseState#state_t{dir=rebar_dir:get_cwd(),
  61. default=Opts}.
  62. -spec new(t() | atom(), list()) -> t().
  63. new(Profile, Config) when is_atom(Profile)
  64. , is_list(Config) ->
  65. Opts = base_opts(Config),
  66. BaseState = base_state(Opts),
  67. BaseState#state_t{dir = rebar_dir:get_cwd(),
  68. current_profiles = [Profile],
  69. default = Opts};
  70. new(ParentState=#state_t{}, Config) ->
  71. %% Load terms from rebar.config, if it exists
  72. Dir = rebar_dir:get_cwd(),
  73. new(ParentState, Config, Dir).
  74. -spec new(t(), list(), file:filename_all()) -> t().
  75. new(ParentState, Config, Dir) ->
  76. new(ParentState, Config, deps_from_config(Dir, Config), Dir).
  77. new(ParentState, Config, Deps, Dir) ->
  78. Opts = ParentState#state_t.opts,
  79. Plugins = proplists:get_value(plugins, Config, []),
  80. ProjectPlugins = proplists:get_value(project_plugins, Config, []),
  81. Terms = Deps++[{{project_plugins, default}, ProjectPlugins}, {{plugins, default}, Plugins} | Config],
  82. true = rebar_config:verify_config_format(Terms),
  83. LocalOpts = dict:from_list(Terms),
  84. NewOpts = rebar_opts:merge_opts(LocalOpts, Opts),
  85. ParentState#state_t{dir=Dir
  86. ,opts=NewOpts
  87. ,default=NewOpts}.
  88. deps_from_config(Dir, Config) ->
  89. case rebar_config:consult_lock_file(filename:join(Dir, ?LOCK_FILE)) of
  90. [] ->
  91. [{{deps, default}, proplists:get_value(deps, Config, [])}];
  92. D ->
  93. %% We want the top level deps only from the lock file.
  94. %% This ensures deterministic overrides for configs.
  95. Deps = [X || X <- D, element(3, X) =:= 0],
  96. [{{locks, default}, D}, {{deps, default}, Deps}]
  97. end.
  98. base_state(Opts) ->
  99. #state_t{opts=Opts}.
  100. base_opts(Config) ->
  101. Deps = proplists:get_value(deps, Config, []),
  102. Plugins = proplists:get_value(plugins, Config, []),
  103. ProjectPlugins = proplists:get_value(project_plugins, Config, []),
  104. Terms = [{{deps, default}, Deps}, {{plugins, default}, Plugins},
  105. {{project_plugins, default}, ProjectPlugins} | 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 ~ts", [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. ProvidedProfiles = lists:prefix([default|Profiles], CurrentProfiles),
  196. AppliedProfiles = case Profiles of
  197. %% Head of list global profile is special, only for use by rebar3
  198. %% It does not clash if a user does `rebar3 as global...` but when
  199. %% it is the head we must make sure not to prepend `default`
  200. [global | _] ->
  201. Profiles;
  202. _ when ProvidedProfiles ->
  203. deduplicate(CurrentProfiles);
  204. _ ->
  205. deduplicate(CurrentProfiles ++ Profiles)
  206. end,
  207. ConfigProfiles = rebar_state:get(State, profiles, []),
  208. NewOpts =
  209. lists:foldl(fun(default, OptsAcc) ->
  210. OptsAcc;
  211. (Profile, OptsAcc) ->
  212. case proplists:get_value(Profile, ConfigProfiles, []) of
  213. OptsList when is_list(OptsList) ->
  214. ProfileOpts = dict:from_list(OptsList),
  215. rebar_opts:merge_opts(Profile, ProfileOpts, OptsAcc);
  216. Other ->
  217. throw(?PRV_ERROR({profile_not_list, Profile, Other}))
  218. end
  219. end, Defaults, AppliedProfiles),
  220. State#state_t{current_profiles = AppliedProfiles, opts=NewOpts}.
  221. deduplicate(Profiles) ->
  222. do_deduplicate(lists:reverse(Profiles), []).
  223. do_deduplicate([], Acc) ->
  224. Acc;
  225. do_deduplicate([Head | Rest], Acc) ->
  226. case lists:member(Head, Acc) of
  227. true -> do_deduplicate(Rest, Acc);
  228. false -> do_deduplicate(Rest, [Head | Acc])
  229. end.
  230. dir(#state_t{dir=Dir}) ->
  231. Dir.
  232. dir(State=#state_t{}, Dir) ->
  233. State#state_t{dir=filename:absname(Dir)}.
  234. deps_names(Deps) when is_list(Deps) ->
  235. lists:map(fun(Dep) when is_tuple(Dep) ->
  236. rebar_utils:to_binary(element(1, Dep));
  237. (Dep) when is_atom(Dep) ->
  238. rebar_utils:to_binary(Dep)
  239. end, Deps);
  240. deps_names(State) ->
  241. Deps = rebar_state:get(State, deps, []),
  242. deps_names(Deps).
  243. current_app(#state_t{current_app=CurrentApp}) ->
  244. CurrentApp.
  245. current_app(State=#state_t{}, CurrentApp) ->
  246. State#state_t{current_app=CurrentApp}.
  247. project_apps(#state_t{project_apps=Apps}) ->
  248. Apps.
  249. project_apps(State=#state_t{}, NewApps) when is_list(NewApps) ->
  250. State#state_t{project_apps=NewApps};
  251. project_apps(State=#state_t{project_apps=Apps}, App) ->
  252. State#state_t{project_apps=lists:keystore(rebar_app_info:name(App), 2, Apps, App)}.
  253. deps_to_build(#state_t{deps_to_build=Apps}) ->
  254. Apps.
  255. deps_to_build(State=#state_t{deps_to_build=Apps}, NewApps) when is_list(NewApps) ->
  256. State#state_t{deps_to_build=Apps++NewApps};
  257. deps_to_build(State=#state_t{deps_to_build=Apps}, App) ->
  258. State#state_t{deps_to_build=lists:keystore(rebar_app_info:name(App), 2, Apps, App)}.
  259. all_deps(#state_t{all_deps=Apps}) ->
  260. Apps.
  261. all_deps(State=#state_t{}, NewApps) ->
  262. State#state_t{all_deps=NewApps}.
  263. all_plugin_deps(#state_t{all_plugin_deps=Apps}) ->
  264. Apps.
  265. all_plugin_deps(State=#state_t{}, NewApps) ->
  266. State#state_t{all_plugin_deps=NewApps}.
  267. update_all_plugin_deps(State=#state_t{all_plugin_deps=Apps}, NewApps) ->
  268. State#state_t{all_plugin_deps=Apps++NewApps}.
  269. update_all_deps(State=#state_t{all_deps=Apps}, NewApps) ->
  270. State#state_t{all_deps=Apps++NewApps}.
  271. namespace(#state_t{namespace=Namespace}) ->
  272. Namespace.
  273. namespace(State=#state_t{}, Namespace) ->
  274. State#state_t{namespace=Namespace}.
  275. -spec resources(t()) -> [{rebar_resource_v2:type(), module()}].
  276. resources(#state_t{resources=Resources}) ->
  277. Resources.
  278. -spec resources(t(), [{rebar_resource_v2:type(), module()}]) -> t().
  279. resources(State, NewResources) ->
  280. lists:foldl(fun(Resource, StateAcc) ->
  281. add_resource(StateAcc, Resource)
  282. end, State, NewResources).
  283. -spec add_resource(t(), {rebar_resource_v2:type(), module()}) -> t().
  284. add_resource(State=#state_t{resources=Resources}, {ResourceType, ResourceModule}) ->
  285. _ = code:ensure_loaded(ResourceModule),
  286. Resource = case erlang:function_exported(ResourceModule, init, 2) of
  287. true ->
  288. case ResourceModule:init(ResourceType, State) of
  289. {ok, R=#resource{}} ->
  290. R;
  291. _ ->
  292. %% init didn't return a resource
  293. %% must be an old resource
  294. warn_old_resource(ResourceModule),
  295. rebar_resource:new(ResourceType,
  296. ResourceModule,
  297. #{})
  298. end;
  299. false ->
  300. %% no init, must be initial implementation
  301. warn_old_resource(ResourceModule),
  302. rebar_resource:new(ResourceType,
  303. ResourceModule,
  304. #{})
  305. end,
  306. State#state_t{resources=[Resource | Resources]}.
  307. warn_old_resource(ResourceModule) ->
  308. ?WARN("Using custom resource ~s that implements a deprecated api. "
  309. "It should be upgraded to rebar_resource_v2.", [ResourceModule]).
  310. create_resources(Resources, State) ->
  311. lists:foldl(fun(R, StateAcc) ->
  312. add_resource(StateAcc, R)
  313. end, State, Resources).
  314. providers(#state_t{providers=Providers}) ->
  315. Providers.
  316. providers(State, NewProviders) ->
  317. State#state_t{providers=NewProviders}.
  318. allow_provider_overrides(#state_t{allow_provider_overrides=Allow}) ->
  319. Allow.
  320. allow_provider_overrides(State, Allow) ->
  321. State#state_t{allow_provider_overrides=Allow}.
  322. -spec add_provider(t(), providers:t()) -> t().
  323. add_provider(State=#state_t{providers=Providers, allow_provider_overrides=true}, Provider) ->
  324. State#state_t{providers=[Provider | Providers]};
  325. add_provider(State=#state_t{providers=Providers, allow_provider_overrides=false}, Provider) ->
  326. Name = providers:impl(Provider),
  327. Namespace = providers:namespace(Provider),
  328. Module = providers:module(Provider),
  329. case lists:any(fun(P) ->
  330. case {providers:impl(P), providers:namespace(P)} of
  331. {Name, Namespace} ->
  332. ?DEBUG("Not adding provider ~p ~p from module ~p because it already exists from module ~p",
  333. [Namespace, Name, Module, providers:module(P)]),
  334. true;
  335. _ ->
  336. false
  337. end
  338. end, Providers) of
  339. true ->
  340. State;
  341. false ->
  342. State#state_t{providers=[Provider | Providers]}
  343. end.
  344. create_logic_providers(ProviderModules, State0) ->
  345. try
  346. lists:foldl(fun(ProviderMod, StateAcc) ->
  347. case providers:new(ProviderMod, StateAcc) of
  348. {error, Reason} ->
  349. ?ERROR(Reason++"~n", []),
  350. StateAcc;
  351. {ok, StateAcc1} ->
  352. StateAcc1
  353. end
  354. end, State0, ProviderModules)
  355. catch
  356. ?WITH_STACKTRACE(C,T,S)
  357. ?DEBUG("~p: ~p ~p", [C, T, S]),
  358. ?CRASHDUMP("~p: ~p~n~p~n~n~p", [C, T, S, State0]),
  359. throw({error, "Failed creating providers. Run with DEBUG=1 for stacktrace or consult rebar3.crashdump."})
  360. end.
  361. to_list(#state_t{} = State) ->
  362. Fields = record_info(fields, state_t),
  363. Values = tl(tuple_to_list(State)),
  364. lists:zip(Fields, [reformat(I) || I <- Values]).
  365. reformat({K,V}) when is_list(V) ->
  366. {K, [reformat(I) || I <- V]};
  367. reformat({K,V}) ->
  368. try
  369. {K, [reformat(I) || I <- dict:to_list(V)]}
  370. catch
  371. error:{badrecord,dict} ->
  372. {K,V}
  373. end;
  374. reformat(V) ->
  375. try
  376. [reformat(I) || I <- dict:to_list(V)]
  377. catch
  378. error:{badrecord,dict} ->
  379. V
  380. end.
  381. %% ===================================================================
  382. %% Internal functions
  383. %% ===================================================================