- -module(rebar_opts).
-
- -export([get/2,
- get/3,
- set/3,
-
- erl_opts/1,
-
- apply_overrides/3,
- add_to_profile/3,
- merge_opts/2,
- merge_opts/3]).
-
- -include("rebar.hrl").
-
- get(Opts, Key) ->
- {ok, Value} = dict:find(Key, Opts),
- Value.
-
- get(Opts, Key, Default) ->
- case dict:find(Key, Opts) of
- {ok, Value} ->
- Value;
- error ->
- Default
- end.
-
- set(Opts, Key, Value) ->
- dict:store(Key, Value, Opts).
-
- %% @doc Return list of erl_opts
- -spec erl_opts(rebar_dict()) -> list().
- erl_opts(Opts) ->
- RawErlOpts = filter_defines(?MODULE:get(Opts, erl_opts, []), []),
- Defines = [{d, list_to_atom(D)} ||
- D <- ?MODULE:get(Opts, defines, [])],
- AllOpts = Defines ++ RawErlOpts,
- lists:reverse(filter_debug_info(lists:reverse(AllOpts))).
-
- filter_debug_info([]) ->
- %% Default == ON
- [debug_info];
- filter_debug_info([debug_info|_] = L) ->
- %% drop no_debug_info and {debug_info_key, _} since those would
- %% conflict with a plain debug_info
- [debug_info |
- lists:filter(fun(K) ->
- K =/= no_debug_info andalso K =/= debug_info andalso
- not (is_tuple(K) andalso element(1,K) =:= debug_info_key)
- end, L)];
- filter_debug_info([{debug_info, _} = H | T]) ->
- %% custom debug_info field; keep and filter the rest except
- %% without no_debug_info. Still have to filter for regular or crypto
- %% debug_info.
- [H | filter_debug_info(lists:filter(fun(K) -> K =/= no_debug_info end, T))];
- filter_debug_info([{debug_info_key, _}=H | T]) ->
- %% Drop no_debug_info and regular debug_info
- [H | lists:filter(fun(K) ->
- K =/= no_debug_info andalso K =/= debug_info andalso
- not (is_tuple(K) andalso element(1,K) =:= debug_info_key)
- end, T)];
- filter_debug_info([no_debug_info|T]) ->
- %% Drop all debug info
- lists:filter(fun(debug_info) -> false
- ; ({debug_info, _}) -> false
- ; ({debug_info_key, _}) -> false
- ; (no_debug_info) -> false
- ; (_Other) -> true
- end, T);
- filter_debug_info([H|T]) ->
- [H|filter_debug_info(T)].
-
- apply_overrides(Opts, Name, Overrides) ->
- %% Inefficient. We want the order we get here though.
- Opts1 = lists:foldl(fun({override, O}, OptsAcc) ->
- override_opt(O, OptsAcc);
- (_, OptsAcc) ->
- OptsAcc
- end, Opts, Overrides),
-
- Opts2 = lists:foldl(fun({add, O}, OptsAcc) ->
- add_opt(O, OptsAcc);
- (_, OptsAcc) ->
- OptsAcc
- end, Opts1, Overrides),
-
- Opts3 = lists:foldl(fun({del, O}, OptsAcc) ->
- del_opt(O, OptsAcc);
- (_, OptsAcc) ->
- OptsAcc
- end, Opts2, Overrides),
-
- Opts4 = lists:foldl(fun({override, N, O}, OptsAcc) when N =:= Name ->
- override_opt(O, OptsAcc);
- (_, OptsAcc) ->
- OptsAcc
- end, Opts3, Overrides),
-
- Opts5 = lists:foldl(fun({add, N, O}, OptsAcc) when N =:= Name ->
- add_opt(O, OptsAcc);
- (_, OptsAcc) ->
- OptsAcc
- end, Opts4, Overrides),
-
- Opts6 = lists:foldl(fun({del, N, O}, OptsAcc) when N =:= Name ->
- del_opt(O, OptsAcc);
- (_, OptsAcc) ->
- OptsAcc
- end, Opts5, Overrides),
-
- Opts6.
-
- add_to_profile(Opts, Profile, KVs) when is_atom(Profile), is_list(KVs) ->
- Profiles = ?MODULE:get(Opts, profiles, []),
- ProfileOpts = dict:from_list(proplists:get_value(Profile, Profiles, [])),
- NewOpts = merge_opts(Profile, dict:from_list(KVs), ProfileOpts),
- NewProfiles = [{Profile, dict:to_list(NewOpts)}|lists:keydelete(Profile, 1, Profiles)],
- set(Opts, profiles, NewProfiles).
-
- merge_opts(Profile, NewOpts, OldOpts) ->
- Opts = merge_opts(NewOpts, OldOpts),
-
- Opts2 = case dict:find(plugins, NewOpts) of
- {ok, Value} ->
- dict:store({plugins, Profile}, Value, Opts);
- error ->
- Opts
- end,
-
- case dict:find(deps, NewOpts) of
- {ok, Value2} ->
- dict:store({deps, Profile}, Value2, Opts2);
- error ->
- Opts2
- end.
-
- merge_opts(NewOpts, OldOpts) ->
- dict:merge(fun merge_opt/3, NewOpts, OldOpts).
-
- %% Internal functions
-
- add_opt(Opts1, Opts2) ->
- lists:foldl(fun({deps, Value}, OptsAcc) ->
- OldValue = ?MODULE:get(OptsAcc, {deps,default}, []),
- set(OptsAcc, {deps,default}, Value++OldValue);
- ({Key, Value}, OptsAcc) ->
- OldValue = ?MODULE:get(OptsAcc, Key, []),
- set(OptsAcc, Key, Value++OldValue)
- end, Opts2, Opts1).
-
- del_opt(Opts1, Opts2) ->
- lists:foldl(fun({deps, Value}, OptsAcc) ->
- OldValue = ?MODULE:get(OptsAcc, {deps,default}, []),
- set(OptsAcc, {deps,default}, OldValue--Value);
- ({Key, Value}, OptsAcc) ->
- OldValue = ?MODULE:get(OptsAcc, Key, []),
- set(OptsAcc, Key, OldValue--Value)
- end, Opts2, Opts1).
-
- override_opt(Opts1, Opts2) ->
- lists:foldl(fun({deps, Value}, OptsAcc) ->
- set(OptsAcc, {deps,default}, Value);
- ({Key, Value}, OptsAcc) ->
- set(OptsAcc, Key, Value)
- end, Opts2, Opts1).
-
- %%
- %% Function for dict:merge/3 (in merge_opts/2) to merge options by priority.
- %%
- merge_opt(deps, _NewValue, OldValue) ->
- OldValue;
- merge_opt({deps, _}, NewValue, _OldValue) ->
- NewValue;
- merge_opt(plugins, NewValue, _OldValue) ->
- NewValue;
- merge_opt({plugins, _}, NewValue, _OldValue) ->
- NewValue;
- merge_opt(profiles, NewValue, OldValue) ->
- %% Merge up sparse pairs of {Profile, Opts} into a joined up
- %% {Profile, OptsNew, OptsOld} list.
- ToMerge = normalise_profile_pairs(lists:sort(NewValue),
- lists:sort(OldValue)),
- [{K,dict:to_list(merge_opts(dict:from_list(New), dict:from_list(Old)))}
- || {K,New,Old} <- ToMerge];
- merge_opt(erl_first_files, Value, Value) ->
- Value;
- merge_opt(erl_first_files, NewValue, OldValue) ->
- OldValue ++ NewValue;
- merge_opt(mib_first_files, Value, Value) ->
- Value;
- merge_opt(mib_first_files, NewValue, OldValue) ->
- OldValue ++ NewValue;
- merge_opt(relx, NewValue, OldValue) ->
- Partition = fun(C) -> is_tuple(C) andalso element(1, C) =:= overlay end,
- {NewOverlays, NewOther} = lists:partition(Partition, NewValue),
- {OldOverlays, OldOther} = lists:partition(Partition, OldValue),
- rebar_utils:tup_umerge(NewOverlays, OldOverlays)
- ++ rebar_utils:tup_umerge(OldOther, NewOther);
- merge_opt(Key, NewValue, OldValue)
- when Key == erl_opts; Key == eunit_compile_opts; Key == ct_compile_opts ->
- merge_erl_opts(lists:reverse(OldValue), NewValue);
- merge_opt(_Key, NewValue, OldValue) when is_list(NewValue) ->
- case io_lib:printable_list(NewValue) of
- true when NewValue =:= [] ->
- case io_lib:printable_list(OldValue) of
- true ->
- NewValue;
- false ->
- OldValue
- end;
- true ->
- NewValue;
- false ->
- rebar_utils:tup_umerge(NewValue, OldValue)
- end;
- merge_opt(_Key, NewValue, _OldValue) ->
- NewValue.
-
- %%
- %% Merge Erlang compiler options such that the result
- %% a) Doesn't contain duplicates.
- %% b) Resulting options are ordered by increasing precedence as expected by
- %% the compiler.
- %% The first parameter is the lower precedence options, in reverse order, to
- %% be merged with the higher-precedence options in the second parameter.
- %%
- merge_erl_opts([Opt | Opts], []) ->
- merge_erl_opts(Opts, [Opt]);
- merge_erl_opts([Opt | Opts], Merged) ->
- case lists:member(Opt, Merged) of
- true ->
- merge_erl_opts(Opts, Merged);
- _ ->
- merge_erl_opts(Opts, [Opt | Merged])
- end;
- merge_erl_opts([], Merged) ->
- Merged.
-
- %%
- %% Filter a list of erl_opts platform_define options such that only
- %% those which match the provided architecture regex are returned.
- %%
- filter_defines([], Acc) ->
- lists:reverse(Acc);
- filter_defines([{platform_define, ArchRegex, Key} | Rest], Acc) ->
- case rebar_utils:is_arch(ArchRegex) of
- true ->
- filter_defines(Rest, [{d, Key} | Acc]);
- false ->
- filter_defines(Rest, Acc)
- end;
- filter_defines([{platform_define, ArchRegex, Key, Value} | Rest], Acc) ->
- case rebar_utils:is_arch(ArchRegex) of
- true ->
- filter_defines(Rest, [{d, Key, Value} | Acc]);
- false ->
- filter_defines(Rest, Acc)
- end;
- filter_defines([Opt | Rest], Acc) ->
- filter_defines(Rest, [Opt | Acc]).
-
- %% @private takes two lists of profile tuples and merges them
- %% into one list of 3-tuples containing the values of either
- %% profiles.
- %% Any missing profile in one of the keys is replaced by an
- %% empty one.
- -spec normalise_profile_pairs([Profile], [Profile]) -> [Pair] when
- Profile :: {Name, Opts},
- Pair :: {Name, Opts, Opts},
- Name :: atom(),
- Opts :: [term()].
- normalise_profile_pairs([], []) ->
- [];
- normalise_profile_pairs([{P,V}|Ps], []) ->
- [{P,V,[]} | normalise_profile_pairs(Ps, [])];
- normalise_profile_pairs([], [{P,V}|Ps]) ->
- [{P,[],V} | normalise_profile_pairs([], Ps)];
- normalise_profile_pairs([{P,VA}|PAs], [{P,VB}|PBs]) ->
- [{P,VA,VB} | normalise_profile_pairs(PAs, PBs)];
- normalise_profile_pairs([{PA,VA}|PAs], [{PB,VB}|PBs]) when PA < PB ->
- [{PA,VA,[]} | normalise_profile_pairs(PAs, [{PB, VB}|PBs])];
- normalise_profile_pairs([{PA,VA}|PAs], [{PB,VB}|PBs]) when PA > PB ->
- [{PB,[],VB} | normalise_profile_pairs([{PA,VA}|PAs], PBs)].
|