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.

283 righe
10 KiB

  1. -module(rebar_opts).
  2. -export([get/2,
  3. get/3,
  4. set/3,
  5. erl_opts/1,
  6. apply_overrides/3,
  7. add_to_profile/3,
  8. merge_opts/2,
  9. merge_opts/3]).
  10. -include("rebar.hrl").
  11. get(Opts, Key) ->
  12. {ok, Value} = dict:find(Key, Opts),
  13. Value.
  14. get(Opts, Key, Default) ->
  15. case dict:find(Key, Opts) of
  16. {ok, Value} ->
  17. Value;
  18. error ->
  19. Default
  20. end.
  21. set(Opts, Key, Value) ->
  22. dict:store(Key, Value, Opts).
  23. %% @doc Return list of erl_opts
  24. -spec erl_opts(rebar_dict()) -> list().
  25. erl_opts(Opts) ->
  26. RawErlOpts = filter_defines(?MODULE:get(Opts, erl_opts, []), []),
  27. Defines = [{d, list_to_atom(D)} ||
  28. D <- ?MODULE:get(Opts, defines, [])],
  29. AllOpts = Defines ++ RawErlOpts,
  30. lists:reverse(filter_debug_info(lists:reverse(AllOpts))).
  31. filter_debug_info([]) ->
  32. %% Default == ON
  33. [debug_info];
  34. filter_debug_info([debug_info|_] = L) ->
  35. %% drop no_debug_info and {debug_info_key, _} since those would
  36. %% conflict with a plain debug_info
  37. [debug_info |
  38. lists:filter(fun(K) ->
  39. K =/= no_debug_info andalso K =/= debug_info andalso
  40. not (is_tuple(K) andalso element(1,K) =:= debug_info_key)
  41. end, L)];
  42. filter_debug_info([{debug_info, _} = H | T]) ->
  43. %% custom debug_info field; keep and filter the rest except
  44. %% without no_debug_info. Still have to filter for regular or crypto
  45. %% debug_info.
  46. [H | filter_debug_info(lists:filter(fun(K) -> K =/= no_debug_info end, T))];
  47. filter_debug_info([{debug_info_key, _}=H | T]) ->
  48. %% Drop no_debug_info and regular debug_info
  49. [H | lists:filter(fun(K) ->
  50. K =/= no_debug_info andalso K =/= debug_info andalso
  51. not (is_tuple(K) andalso element(1,K) =:= debug_info_key)
  52. end, T)];
  53. filter_debug_info([no_debug_info|T]) ->
  54. %% Drop all debug info
  55. lists:filter(fun(debug_info) -> false
  56. ; ({debug_info, _}) -> false
  57. ; ({debug_info_key, _}) -> false
  58. ; (no_debug_info) -> false
  59. ; (_Other) -> true
  60. end, T);
  61. filter_debug_info([H|T]) ->
  62. [H|filter_debug_info(T)].
  63. apply_overrides(Opts, Name, Overrides) ->
  64. %% Inefficient. We want the order we get here though.
  65. Opts1 = lists:foldl(fun({override, O}, OptsAcc) ->
  66. override_opt(O, OptsAcc);
  67. (_, OptsAcc) ->
  68. OptsAcc
  69. end, Opts, Overrides),
  70. Opts2 = lists:foldl(fun({add, O}, OptsAcc) ->
  71. add_opt(O, OptsAcc);
  72. (_, OptsAcc) ->
  73. OptsAcc
  74. end, Opts1, Overrides),
  75. Opts3 = lists:foldl(fun({del, O}, OptsAcc) ->
  76. del_opt(O, OptsAcc);
  77. (_, OptsAcc) ->
  78. OptsAcc
  79. end, Opts2, Overrides),
  80. Opts4 = lists:foldl(fun({override, N, O}, OptsAcc) when N =:= Name ->
  81. override_opt(O, OptsAcc);
  82. (_, OptsAcc) ->
  83. OptsAcc
  84. end, Opts3, Overrides),
  85. Opts5 = lists:foldl(fun({add, N, O}, OptsAcc) when N =:= Name ->
  86. add_opt(O, OptsAcc);
  87. (_, OptsAcc) ->
  88. OptsAcc
  89. end, Opts4, Overrides),
  90. Opts6 = lists:foldl(fun({del, N, O}, OptsAcc) when N =:= Name ->
  91. del_opt(O, OptsAcc);
  92. (_, OptsAcc) ->
  93. OptsAcc
  94. end, Opts5, Overrides),
  95. Opts6.
  96. add_to_profile(Opts, Profile, KVs) when is_atom(Profile), is_list(KVs) ->
  97. Profiles = ?MODULE:get(Opts, profiles, []),
  98. ProfileOpts = dict:from_list(proplists:get_value(Profile, Profiles, [])),
  99. NewOpts = merge_opts(Profile, dict:from_list(KVs), ProfileOpts),
  100. NewProfiles = [{Profile, dict:to_list(NewOpts)}|lists:keydelete(Profile, 1, Profiles)],
  101. set(Opts, profiles, NewProfiles).
  102. merge_opts(Profile, NewOpts, OldOpts) ->
  103. Opts = merge_opts(NewOpts, OldOpts),
  104. Opts2 = case dict:find(plugins, NewOpts) of
  105. {ok, Value} ->
  106. dict:store({plugins, Profile}, Value, Opts);
  107. error ->
  108. Opts
  109. end,
  110. case dict:find(deps, NewOpts) of
  111. {ok, Value2} ->
  112. dict:store({deps, Profile}, Value2, Opts2);
  113. error ->
  114. Opts2
  115. end.
  116. merge_opts(NewOpts, OldOpts) ->
  117. dict:merge(fun merge_opt/3, NewOpts, OldOpts).
  118. %% Internal functions
  119. add_opt(Opts1, Opts2) ->
  120. lists:foldl(fun({deps, Value}, OptsAcc) ->
  121. OldValue = ?MODULE:get(OptsAcc, {deps,default}, []),
  122. set(OptsAcc, {deps,default}, Value++OldValue);
  123. ({Key, Value}, OptsAcc) ->
  124. OldValue = ?MODULE:get(OptsAcc, Key, []),
  125. set(OptsAcc, Key, Value++OldValue)
  126. end, Opts2, Opts1).
  127. del_opt(Opts1, Opts2) ->
  128. lists:foldl(fun({deps, Value}, OptsAcc) ->
  129. OldValue = ?MODULE:get(OptsAcc, {deps,default}, []),
  130. set(OptsAcc, {deps,default}, OldValue--Value);
  131. ({Key, Value}, OptsAcc) ->
  132. OldValue = ?MODULE:get(OptsAcc, Key, []),
  133. set(OptsAcc, Key, OldValue--Value)
  134. end, Opts2, Opts1).
  135. override_opt(Opts1, Opts2) ->
  136. lists:foldl(fun({deps, Value}, OptsAcc) ->
  137. set(OptsAcc, {deps,default}, Value);
  138. ({Key, Value}, OptsAcc) ->
  139. set(OptsAcc, Key, Value)
  140. end, Opts2, Opts1).
  141. %%
  142. %% Function for dict:merge/3 (in merge_opts/2) to merge options by priority.
  143. %%
  144. merge_opt(deps, _NewValue, OldValue) ->
  145. OldValue;
  146. merge_opt({deps, _}, NewValue, _OldValue) ->
  147. NewValue;
  148. merge_opt(plugins, NewValue, _OldValue) ->
  149. NewValue;
  150. merge_opt({plugins, _}, NewValue, _OldValue) ->
  151. NewValue;
  152. merge_opt(profiles, NewValue, OldValue) ->
  153. %% Merge up sparse pairs of {Profile, Opts} into a joined up
  154. %% {Profile, OptsNew, OptsOld} list.
  155. ToMerge = normalise_profile_pairs(lists:sort(NewValue),
  156. lists:sort(OldValue)),
  157. [{K,dict:to_list(merge_opts(dict:from_list(New), dict:from_list(Old)))}
  158. || {K,New,Old} <- ToMerge];
  159. merge_opt(erl_first_files, Value, Value) ->
  160. Value;
  161. merge_opt(erl_first_files, NewValue, OldValue) ->
  162. OldValue ++ NewValue;
  163. merge_opt(mib_first_files, Value, Value) ->
  164. Value;
  165. merge_opt(mib_first_files, NewValue, OldValue) ->
  166. OldValue ++ NewValue;
  167. merge_opt(relx, NewValue, OldValue) ->
  168. Partition = fun(C) -> is_tuple(C) andalso element(1, C) =:= overlay end,
  169. {NewOverlays, NewOther} = lists:partition(Partition, NewValue),
  170. {OldOverlays, OldOther} = lists:partition(Partition, OldValue),
  171. rebar_utils:tup_umerge(NewOverlays, OldOverlays)
  172. ++ rebar_utils:tup_umerge(OldOther, NewOther);
  173. merge_opt(Key, NewValue, OldValue)
  174. when Key == erl_opts; Key == eunit_compile_opts; Key == ct_compile_opts ->
  175. merge_erl_opts(lists:reverse(OldValue), NewValue);
  176. merge_opt(_Key, NewValue, OldValue) when is_list(NewValue) ->
  177. case io_lib:printable_list(NewValue) of
  178. true when NewValue =:= [] ->
  179. case io_lib:printable_list(OldValue) of
  180. true ->
  181. NewValue;
  182. false ->
  183. OldValue
  184. end;
  185. true ->
  186. NewValue;
  187. false ->
  188. rebar_utils:tup_umerge(NewValue, OldValue)
  189. end;
  190. merge_opt(_Key, NewValue, _OldValue) ->
  191. NewValue.
  192. %%
  193. %% Merge Erlang compiler options such that the result
  194. %% a) Doesn't contain duplicates.
  195. %% b) Resulting options are ordered by increasing precedence as expected by
  196. %% the compiler.
  197. %% The first parameter is the lower precedence options, in reverse order, to
  198. %% be merged with the higher-precedence options in the second parameter.
  199. %%
  200. merge_erl_opts([Opt | Opts], []) ->
  201. merge_erl_opts(Opts, [Opt]);
  202. merge_erl_opts([Opt | Opts], Merged) ->
  203. case lists:member(Opt, Merged) of
  204. true ->
  205. merge_erl_opts(Opts, Merged);
  206. _ ->
  207. merge_erl_opts(Opts, [Opt | Merged])
  208. end;
  209. merge_erl_opts([], Merged) ->
  210. Merged.
  211. %%
  212. %% Filter a list of erl_opts platform_define options such that only
  213. %% those which match the provided architecture regex are returned.
  214. %%
  215. filter_defines([], Acc) ->
  216. lists:reverse(Acc);
  217. filter_defines([{platform_define, ArchRegex, Key} | Rest], Acc) ->
  218. case rebar_utils:is_arch(ArchRegex) of
  219. true ->
  220. filter_defines(Rest, [{d, Key} | Acc]);
  221. false ->
  222. filter_defines(Rest, Acc)
  223. end;
  224. filter_defines([{platform_define, ArchRegex, Key, Value} | Rest], Acc) ->
  225. case rebar_utils:is_arch(ArchRegex) of
  226. true ->
  227. filter_defines(Rest, [{d, Key, Value} | Acc]);
  228. false ->
  229. filter_defines(Rest, Acc)
  230. end;
  231. filter_defines([Opt | Rest], Acc) ->
  232. filter_defines(Rest, [Opt | Acc]).
  233. %% @private takes two lists of profile tuples and merges them
  234. %% into one list of 3-tuples containing the values of either
  235. %% profiles.
  236. %% Any missing profile in one of the keys is replaced by an
  237. %% empty one.
  238. -spec normalise_profile_pairs([Profile], [Profile]) -> [Pair] when
  239. Profile :: {Name, Opts},
  240. Pair :: {Name, Opts, Opts},
  241. Name :: atom(),
  242. Opts :: [term()].
  243. normalise_profile_pairs([], []) ->
  244. [];
  245. normalise_profile_pairs([{P,V}|Ps], []) ->
  246. [{P,V,[]} | normalise_profile_pairs(Ps, [])];
  247. normalise_profile_pairs([], [{P,V}|Ps]) ->
  248. [{P,[],V} | normalise_profile_pairs([], Ps)];
  249. normalise_profile_pairs([{P,VA}|PAs], [{P,VB}|PBs]) ->
  250. [{P,VA,VB} | normalise_profile_pairs(PAs, PBs)];
  251. normalise_profile_pairs([{PA,VA}|PAs], [{PB,VB}|PBs]) when PA < PB ->
  252. [{PA,VA,[]} | normalise_profile_pairs(PAs, [{PB, VB}|PBs])];
  253. normalise_profile_pairs([{PA,VA}|PAs], [{PB,VB}|PBs]) when PA > PB ->
  254. [{PB,[],VB} | normalise_profile_pairs([{PA,VA}|PAs], PBs)].