Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

477 Zeilen
16 KiB

vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 9 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
vor 10 Jahren
  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. %% ===================================================================