summaryrefslogtreecommitdiff
path: root/src/rebar_opts.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/rebar_opts.erl')
-rw-r--r--src/rebar_opts.erl252
1 files changed, 187 insertions, 65 deletions
diff --git a/src/rebar_opts.erl b/src/rebar_opts.erl
index b02a504..8195a77 100644
--- a/src/rebar_opts.erl
+++ b/src/rebar_opts.erl
@@ -35,46 +35,80 @@ erl_opts(Opts) ->
Defines = [{d, list_to_atom(D)} ||
D <- ?MODULE:get(Opts, defines, [])],
AllOpts = Defines ++ RawErlOpts,
- case proplists:is_defined(no_debug_info, AllOpts) of
- true ->
- [O || O <- AllOpts, O =/= no_debug_info];
- false ->
- [debug_info|AllOpts]
- end.
+ 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) ->
- lists:foldl(fun({deps, Value}, OptsAcc1) ->
- set(OptsAcc1, {deps,default}, Value);
- ({Key, Value}, OptsAcc1) ->
- set(OptsAcc1, Key, Value)
- end, OptsAcc, O);
+ override_opt(O, OptsAcc);
(_, OptsAcc) ->
OptsAcc
- end, Opts, Overrides),
-
- Opts2 = lists:foldl(fun({override, N, O}, OptsAcc) when N =:= Name ->
- lists:foldl(fun({deps, Value}, OptsAcc1) ->
- set(OptsAcc1, {deps,default}, Value);
- ({Key, Value}, OptsAcc1) ->
- set(OptsAcc1, Key, Value)
- end, OptsAcc, O);
+ 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, Opts1, Overrides),
-
- lists:foldl(fun({add, N, O}, OptsAcc) when N =:= Name ->
- lists:foldl(fun({deps, Value}, OptsAcc1) ->
- OldValue = ?MODULE:get(OptsAcc1, {deps,default}, []),
- set(OptsAcc1, {deps,default}, Value++OldValue);
- ({Key, Value}, OptsAcc1) ->
- OldValue = ?MODULE:get(OptsAcc1, Key, []),
- set(OptsAcc1, Key, Value++OldValue)
- end, OptsAcc, O);
- (_, OptsAcc) ->
- OptsAcc
- end, Opts2, Overrides).
+ 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, []),
@@ -101,42 +135,107 @@ merge_opts(Profile, NewOpts, OldOpts) ->
end.
merge_opts(NewOpts, OldOpts) ->
- dict:merge(fun(deps, _NewValue, OldValue) ->
- OldValue;
- ({deps, _}, NewValue, _OldValue) ->
- NewValue;
- (plugins, NewValue, _OldValue) ->
- NewValue;
- ({plugins, _}, NewValue, _OldValue) ->
- NewValue;
- (profiles, NewValue, OldValue) ->
- dict:to_list(merge_opts(dict:from_list(NewValue), dict:from_list(OldValue)));
- (mib_first_files, Value, Value) ->
- Value;
- (mib_first_files, NewValue, OldValue) ->
- OldValue ++ NewValue;
- (relx, NewValue, OldValue) ->
- rebar_utils:tup_umerge(OldValue, NewValue);
- (_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;
- (_Key, NewValue, _OldValue) ->
- NewValue
- end, 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.
@@ -159,3 +258,26 @@ filter_defines([{platform_define, ArchRegex, Key, Value} | 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)].