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.

364 line
13 KiB

10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
10 年之前
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. opts/1, opts/2,
  5. default/1, default/2,
  6. escript_path/1, escript_path/2,
  7. lock/1, lock/2,
  8. current_profiles/1,
  9. command_args/1, command_args/2,
  10. command_parsed_args/1, command_parsed_args/2,
  11. add_to_profile/3, apply_profiles/2,
  12. dir/1, dir/2,
  13. create_logic_providers/2,
  14. project_apps/1, project_apps/2,
  15. deps_to_build/1, deps_to_build/2,
  16. all_deps/1, all_deps/2,
  17. namespace/1, namespace/2,
  18. deps_names/1,
  19. overrides/1, overrides/2,
  20. apply_overrides/2,
  21. providers/1, providers/2, add_provider/2]).
  22. -include("rebar.hrl").
  23. -record(state_t, {dir :: file:name(),
  24. opts = dict:new() :: rebar_dict(),
  25. default = dict:new() :: rebar_dict(),
  26. escript_path :: undefined | file:filename_all(),
  27. lock = [],
  28. current_profiles = [default] :: [atom()],
  29. namespace = undefined :: [atom()],
  30. command_args = [],
  31. command_parsed_args = [],
  32. project_apps = [] :: [rebar_app_info:t()],
  33. deps_to_build = [] :: [rebar_app_info:t()],
  34. all_deps = [] :: [rebar_app_info:t()],
  35. overrides = [],
  36. providers = []}).
  37. -export_type([t/0]).
  38. -type t() :: record(state_t).
  39. -spec new() -> t().
  40. new() ->
  41. #state_t{dir = rebar_dir:get_cwd()}.
  42. -spec new(list()) -> t().
  43. new(Config) when is_list(Config) ->
  44. Deps = proplists:get_value(deps, Config, []),
  45. Opts = dict:from_list([{{deps, default}, Deps} | Config]),
  46. #state_t { dir = rebar_dir:get_cwd(),
  47. default = Opts,
  48. opts = Opts }.
  49. -spec new(t() | atom(), list()) -> t().
  50. new(Profile, Config) when is_atom(Profile)
  51. , is_list(Config) ->
  52. Deps = proplists:get_value(deps, Config, []),
  53. Opts = dict:from_list([{{deps, default}, Deps} | Config]),
  54. #state_t { dir = rebar_dir:get_cwd(),
  55. current_profiles = [Profile],
  56. default = Opts,
  57. opts = Opts };
  58. new(ParentState=#state_t{}, Config) ->
  59. %% Load terms from rebar.config, if it exists
  60. Dir = rebar_dir:get_cwd(),
  61. new(ParentState, Config, Dir).
  62. -spec new(t(), list(), file:name()) -> t().
  63. new(ParentState, Config, Dir) ->
  64. Opts = ParentState#state_t.opts,
  65. LocalOpts = case rebar_config:consult_file(filename:join(Dir, ?LOCK_FILE)) of
  66. [D] ->
  67. %% We want the top level deps only from the lock file.
  68. %% This ensures deterministic overrides for configs.
  69. Deps = [X || X <- D, element(3, X) =:= 0],
  70. dict:from_list([{{locks, default}, D}, {{deps, default}, Deps} | Config]);
  71. _ ->
  72. D = proplists:get_value(deps, Config, []),
  73. dict:from_list([{{deps, default}, D} | Config])
  74. end,
  75. NewOpts = merge_opts(LocalOpts, Opts),
  76. ParentState#state_t{dir=Dir
  77. ,opts=NewOpts
  78. ,default=NewOpts}.
  79. get(State, Key) ->
  80. {ok, Value} = dict:find(Key, State#state_t.opts),
  81. Value.
  82. get(State, Key, Default) ->
  83. case dict:find(Key, State#state_t.opts) of
  84. {ok, Value} ->
  85. Value;
  86. error ->
  87. Default
  88. end.
  89. -spec set(t(), any(), any()) -> t().
  90. set(State=#state_t{opts=Opts}, Key, Value) ->
  91. State#state_t{ opts = dict:store(Key, Value, Opts) }.
  92. default(#state_t{default=Opts}) ->
  93. Opts.
  94. default(State, Opts) ->
  95. State#state_t{default=Opts}.
  96. opts(#state_t{opts=Opts}) ->
  97. Opts.
  98. opts(State, Opts) ->
  99. State#state_t{opts=Opts}.
  100. current_profiles(#state_t{current_profiles=Profiles}) ->
  101. Profiles.
  102. lock(#state_t{lock=Lock}) ->
  103. Lock.
  104. lock(State=#state_t{}, Apps) when is_list(Apps) ->
  105. State#state_t{lock=Apps};
  106. lock(State=#state_t{lock=Lock}, App) ->
  107. State#state_t{lock=[App | Lock]}.
  108. escript_path(#state_t{escript_path=EscriptPath}) ->
  109. EscriptPath.
  110. escript_path(State, EscriptPath) ->
  111. State#state_t{escript_path=EscriptPath}.
  112. command_args(#state_t{command_args=CmdArgs}) ->
  113. CmdArgs.
  114. command_args(State, CmdArgs) ->
  115. State#state_t{command_args=CmdArgs}.
  116. command_parsed_args(#state_t{command_parsed_args=CmdArgs}) ->
  117. CmdArgs.
  118. command_parsed_args(State, CmdArgs) ->
  119. State#state_t{command_parsed_args=CmdArgs}.
  120. apply_overrides(State=#state_t{overrides=Overrides}, AppName) ->
  121. Name = binary_to_atom(AppName, utf8),
  122. %% Inefficient. We want the order we get here though.
  123. State1 = lists:foldl(fun({override, O}, StateAcc) ->
  124. lists:foldl(fun({Key, Value}, StateAcc1) ->
  125. rebar_state:set(StateAcc1, Key, Value)
  126. end, StateAcc, O);
  127. (_, StateAcc) ->
  128. StateAcc
  129. end, State, Overrides),
  130. State2 = lists:foldl(fun({override, N, O}, StateAcc) when N =:= Name ->
  131. lists:foldl(fun({Key, Value}, StateAcc1) ->
  132. rebar_state:set(StateAcc1, Key, Value)
  133. end, StateAcc, O);
  134. (_, StateAcc) ->
  135. StateAcc
  136. end, State1, Overrides),
  137. lists:foldl(fun({add, N, O}, StateAcc) when N =:= Name ->
  138. lists:foldl(fun({Key, Value}, StateAcc1) ->
  139. OldValue = rebar_state:get(StateAcc1, Key, []),
  140. rebar_state:set(StateAcc1, Key, Value++OldValue)
  141. end, StateAcc, O);
  142. (_, StateAcc) ->
  143. StateAcc
  144. end, State2, Overrides).
  145. add_to_profile(State, Profile, KVs) when is_atom(Profile), is_list(KVs) ->
  146. Profiles = rebar_state:get(State, profiles, []),
  147. ProfileOpts = dict:from_list(proplists:get_value(Profile, Profiles, [])),
  148. NewOpts = merge_opts(Profile, dict:from_list(KVs), ProfileOpts),
  149. NewProfiles = [{Profile, dict:to_list(NewOpts)}|lists:keydelete(Profile, 1, Profiles)],
  150. rebar_state:set(State, profiles, NewProfiles).
  151. apply_profiles(State, Profile) when not is_list(Profile) ->
  152. apply_profiles(State, [Profile]);
  153. apply_profiles(State, [default]) ->
  154. State;
  155. apply_profiles(State=#state_t{opts=Opts, current_profiles=CurrentProfiles}, Profiles) ->
  156. ConfigProfiles = rebar_state:get(State, profiles, []),
  157. {Profiles1, NewOpts} =
  158. lists:foldl(fun(default, {ProfilesAcc, OptsAcc}) ->
  159. {ProfilesAcc, OptsAcc};
  160. (Profile, {ProfilesAcc, OptsAcc}) ->
  161. ProfileOpts = dict:from_list(proplists:get_value(Profile, ConfigProfiles, [])),
  162. {[Profile]++ProfilesAcc, merge_opts(Profile, ProfileOpts, OptsAcc)}
  163. end, {[], Opts}, Profiles),
  164. State#state_t{current_profiles=CurrentProfiles++Profiles1, opts=NewOpts}.
  165. merge_opts(Profile, NewOpts, OldOpts) ->
  166. Opts = merge_opts(NewOpts, OldOpts),
  167. case dict:find(deps, NewOpts) of
  168. {ok, Value} ->
  169. dict:store({deps, Profile}, Value, Opts);
  170. error ->
  171. Opts
  172. end.
  173. merge_opts(NewOpts, OldOpts) ->
  174. dict:merge(fun(deps, NewValue, _OldValue) ->
  175. NewValue;
  176. ({deps, _}, NewValue, _OldValue) ->
  177. NewValue;
  178. (profiles, NewValue, OldValue) ->
  179. dict:to_list(merge_opts(dict:from_list(NewValue), dict:from_list(OldValue)));
  180. (_Key, NewValue, OldValue) when is_list(NewValue) ->
  181. case io_lib:printable_list(NewValue) of
  182. true when NewValue =:= [] ->
  183. case io_lib:printable_list(OldValue) of
  184. true ->
  185. NewValue;
  186. false ->
  187. OldValue
  188. end;
  189. true ->
  190. NewValue;
  191. false ->
  192. lists:umerge(lists:sort(NewValue), lists:sort(OldValue))
  193. end;
  194. (_Key, NewValue, _OldValue) ->
  195. NewValue
  196. end, NewOpts, OldOpts).
  197. dir(#state_t{dir=Dir}) ->
  198. Dir.
  199. dir(State=#state_t{}, Dir) ->
  200. State#state_t{dir=filename:absname(Dir)}.
  201. deps_names(Deps) when is_list(Deps) ->
  202. lists:map(fun(Dep) when is_tuple(Dep) ->
  203. ec_cnv:to_binary(element(1, Dep));
  204. (Dep) when is_atom(Dep) ->
  205. ec_cnv:to_binary(Dep)
  206. end, Deps);
  207. deps_names(State) ->
  208. Deps = rebar_state:get(State, deps, []),
  209. deps_names(Deps).
  210. overrides(#state_t{overrides=Overrides}) ->
  211. Overrides.
  212. overrides(State=#state_t{}, Overrides) ->
  213. State#state_t{overrides=Overrides}.
  214. project_apps(#state_t{project_apps=Apps}) ->
  215. Apps.
  216. project_apps(State=#state_t{}, NewApps) when is_list(NewApps) ->
  217. State#state_t{project_apps=NewApps};
  218. project_apps(State=#state_t{project_apps=Apps}, App) ->
  219. State#state_t{project_apps=lists:keystore(rebar_app_info:name(App), 2, Apps, App)}.
  220. deps_to_build(#state_t{deps_to_build=Apps}) ->
  221. Apps.
  222. deps_to_build(State=#state_t{deps_to_build=Apps}, NewApps) when is_list(NewApps) ->
  223. State#state_t{deps_to_build=Apps++NewApps};
  224. deps_to_build(State=#state_t{deps_to_build=Apps}, App) ->
  225. State#state_t{deps_to_build=lists:keystore(rebar_app_info:name(App), 2, Apps, App)}.
  226. all_deps(#state_t{all_deps=Apps}) ->
  227. Apps.
  228. all_deps(State=#state_t{}, NewApps) ->
  229. State#state_t{all_deps=NewApps}.
  230. namespace(#state_t{namespace=Namespace}) ->
  231. Namespace.
  232. namespace(State=#state_t{}, Namespace) ->
  233. State#state_t{namespace=Namespace}.
  234. providers(#state_t{providers=Providers}) ->
  235. Providers.
  236. providers(State, NewProviders) ->
  237. State#state_t{providers=NewProviders}.
  238. -spec add_provider(t(), providers:t()) -> t().
  239. add_provider(State=#state_t{providers=Providers}, Provider) ->
  240. State#state_t{providers=[Provider | Providers]}.
  241. create_logic_providers(ProviderModules, State0) ->
  242. try
  243. State1 = lists:foldl(fun(ProviderMod, StateAcc) ->
  244. case providers:new(ProviderMod, StateAcc) of
  245. {error, Reason} ->
  246. ?ERROR(Reason++"~n", []),
  247. StateAcc;
  248. {ok, StateAcc1} ->
  249. StateAcc1
  250. end
  251. end, State0, ProviderModules),
  252. apply_hooks(State1)
  253. catch
  254. C:T ->
  255. ?DEBUG("~p: ~p ~p", [C, T, erlang:get_stacktrace()]),
  256. throw({error, "Failed creating providers. Run with DEBUG=1 for stacktrace."})
  257. end.
  258. apply_hooks(State0) ->
  259. try
  260. Hooks = rebar_state:get(State0, provider_hooks, []),
  261. PreHooks = proplists:get_value(pre, Hooks, []),
  262. PostHooks = proplists:get_value(post, Hooks, []),
  263. State1 = lists:foldl(fun({Target, Hook}, StateAcc) ->
  264. prepend_hook(StateAcc, Target, Hook)
  265. end, State0, PreHooks),
  266. lists:foldl(fun({Target, Hook}, StateAcc) ->
  267. append_hook(StateAcc, Target, Hook)
  268. end, State1, PostHooks)
  269. catch
  270. C:T ->
  271. ?DEBUG("~p: ~p ~p", [C, T, erlang:get_stacktrace()]),
  272. throw({error, "Failed parsing provider hooks. Run with DEBUG=1 for stacktrace."})
  273. end.
  274. %% ===================================================================
  275. %% Internal functions
  276. %% ===================================================================
  277. prepend_hook(State=#state_t{providers=Providers}, Target, Hook) ->
  278. State#state_t{providers=add_hook(pre, Providers, Target, Hook)}.
  279. append_hook(State=#state_t{providers=Providers}, Target, Hook) ->
  280. State#state_t{providers=add_hook(post, Providers, Target, Hook)}.
  281. add_hook(Which, Providers, Target, Hook) ->
  282. Provider = providers:get_provider(Target, Providers),
  283. Hooks = providers:hooks(Provider),
  284. NewHooks = add_hook(Which, Hooks, Hook),
  285. NewProvider = providers:hooks(Provider, NewHooks),
  286. [NewProvider | lists:delete(Provider, Providers)].
  287. add_hook(pre, {PreHooks, PostHooks}, Hook) ->
  288. {[Hook | PreHooks], PostHooks};
  289. add_hook(post, {PreHooks, PostHooks}, Hook) ->
  290. {PreHooks, [Hook | PostHooks]}.