瀏覽代碼

Preserve profile order on mergeable tuple/opts

Rather than using the stdlib lists:umerge, we expand it to allow fuzzy
matching on tuples vs. vals (`key` vs. `{key,val}`) with an overriden
sort order so that two tuples or values comparing equal get a priority
on the newest profile.

This is a partial fix for #287 -- this current patch should be followed
by a relx update to take options in order (as if they were a proplist)
to complete it.
pull/290/head
Fred Hebert 10 年之前
父節點
當前提交
2d430bf3e2
共有 2 個檔案被更改,包括 66 行新增3 行删除
  1. +57
    -1
      src/rebar_state.erl
  2. +9
    -2
      test/rebar_profiles_SUITE.erl

+ 57
- 1
src/rebar_state.erl 查看文件

@ -240,7 +240,7 @@ merge_opts(NewOpts, OldOpts) ->
true ->
NewValue;
false ->
lists:umerge(lists:sort(NewValue), lists:sort(OldValue))
tup_umerge(lists:sort(NewValue), lists:sort(OldValue))
end;
(_Key, NewValue, _OldValue) ->
NewValue
@ -362,3 +362,59 @@ add_hook(pre, {PreHooks, PostHooks}, Hook) ->
{[Hook | PreHooks], PostHooks};
add_hook(post, {PreHooks, PostHooks}, Hook) ->
{PreHooks, [Hook | PostHooks]}.
%% Custom merge functions. The objective is to behave like lists:umerge/2,
%% except that we also compare the merge elements based on the key if they're a
%% tuple, such that `{key, val1}' is always prioritized over `{key, val0}' if
%% the former is from the 'new' list.
%%
%% This lets us apply proper overrides to list of elements according to profile
%% priority.
tup_umerge([], Olds) ->
Olds;
tup_umerge(News, Olds) ->
[ENew|ENews] = expand(News),
EOlds = expand(Olds),
unexpand(lists:reverse(umerge(ENews, EOlds, [], ENew))).
%% Expand values, so they `key' is now `{key, key}', and so that
%% `{key, val}' is now `{key, {key, val}'. This allows us to compare
%% possibly only on the total key or the value itself.
expand([]) -> [];
expand([Tup|T]) when is_tuple(Tup) -> [{element(1, Tup), Tup} | expand(T)];
expand([H|T]) -> [{H,H} | expand(T)].
%% Go back to unexpanded form.
unexpand(List) -> [element(2, X) || X <- List].
%% This is equivalent to umerge2_2 in the stdlib, except we use the expanded
%% value/key only to compare
umerge(News, [{KOld,_}=Old|Olds], Merged, {KCmp, _} = Cmp) when KCmp =< KOld ->
umerge(News, Olds, [Cmp | Merged], Cmp, Old);
umerge(News, [Old|Olds], Merged, Cmp) ->
umerge(News, Olds, [Old | Merged], Cmp);
umerge(News, [], Merged, Cmp) ->
lists:reverse(News, [Cmp | Merged]).
%% Similar to stdlib's umerge2_1 in the stdlib, except that when the expanded
%% value/keys compare equal, we check if the element is a full dupe to clear it
%% (like the stdlib function does) or otherwise keep the duplicate around in
%% an order that prioritizes 'New' elements.
umerge([{KNew,_}=New|News], Olds, Merged, _CmpMerged, {KCmp,_}=Cmp) when KNew =< KCmp ->
umerge(News, Olds, [New | Merged], New, Cmp);
umerge([{KNew,_}=New|News], Olds, Merged, {KCmp,_}=CmpMerged, Cmp) when KNew == KCmp ->
if New == CmpMerged ->
umerge(News, Olds, Merged, New);
New =/= CmpMerged -> % this is where we depart from the stdlib!
umerge(News, Olds, [New | Merged], New, Cmp)
end;
umerge([New|News], Olds, Merged, _CmpMerged, Cmp) -> % >
umerge(News, Olds, [Cmp | Merged], New);
umerge([], Olds, Merged, {KCmpM,_}=CmpMerged, {KCmp,_}=Cmp) when KCmpM =:= KCmp ->
if CmpMerged == Cmp ->
lists:reverse(Olds, Merged);
CmpMerged =/= Cmp -> % We depart from stdlib here too!
lists:reverse(Olds, [Cmp | Merged])
end;
umerge([], Olds, Merged, _CmpMerged, Cmp) ->
lists:reverse(Olds, [Cmp | Merged]).

+ 9
- 2
test/rebar_profiles_SUITE.erl 查看文件

@ -100,12 +100,17 @@ profile_merges(_Config) ->
{test2, "hello"},
{test3, [key3]},
{test4, "oldvalue"},
{test5, [{key5, true}]},
{test6, [{key6, false}]},
{profiles,
[{profile1,
[{test1, [{key3, 5}, key1]}]},
{profile2, [{test2, "goodbye"},
{test3, []},
{test4, []}]}]}],
{test4, []},
{test5, [{key5, false}]},
{test6, [{key6, true}]}
]}]}],
State = rebar_state:new(RebarConfig),
State1 = rebar_state:apply_profiles(State, [profile1, profile2]),
@ -118,7 +123,9 @@ profile_merges(_Config) ->
%% Check that a newvalue of []/"" doesn't override non-string oldvalues
[key3] = rebar_state:get(State1, test3),
[] = rebar_state:get(State1, test4).
[] = rebar_state:get(State1, test4),
[{key5, false}, {key5, true}] = rebar_state:get(State1, test5),
[{key6, true}, {key6, false}] = rebar_state:get(State1, test6).
add_to_profile(_Config) ->
RebarConfig = [{foo, true}, {bar, false}],

Loading…
取消
儲存