diff options
| author | Fred Hebert <mononcqc@ferd.ca> | 2015-03-23 15:34:01 -0700 | 
|---|---|---|
| committer | Fred Hebert <mononcqc@ferd.ca> | 2015-03-23 15:34:01 -0700 | 
| commit | 75549d3a9180bfe83649cc7bbaffc93f932f12fc (patch) | |
| tree | 7595ff387956e4ebcb67f963cd9b91dabf1bed8c | |
| parent | cafd62e10185e2fc05490c387484dfdb2b615803 (diff) | |
| parent | 48aa18be4fa202e803eb55418c6c53d9afb17685 (diff) | |
Merge pull request #290 from ferd/fancy-profile-merge-fun
Fancy profile merge fun
| -rw-r--r-- | src/rebar_prv_release.erl | 2 | ||||
| -rw-r--r-- | src/rebar_state.erl | 58 | ||||
| -rw-r--r-- | test/rebar_profiles_SUITE.erl | 11 | ||||
| -rw-r--r-- | test/rebar_release_SUITE.erl | 46 | ||||
| -rw-r--r-- | test/rebar_test_utils.erl | 11 | 
5 files changed, 120 insertions, 8 deletions
| diff --git a/src/rebar_prv_release.erl b/src/rebar_prv_release.erl index 766bfe4..db012d5 100644 --- a/src/rebar_prv_release.erl +++ b/src/rebar_prv_release.erl @@ -47,7 +47,7 @@ do(State) ->                            ,{caller, Caller}], AllOptions);              Config ->                  relx:main([{lib_dirs, LibDirs} -                          ,{config, Config} +                          ,{config, lists:reverse(Config)}                            ,{output_dir, OutputDir}                            ,{caller, Caller}], AllOptions)          end, diff --git a/src/rebar_state.erl b/src/rebar_state.erl index 008f202..eced383 100644 --- a/src/rebar_state.erl +++ b/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]). diff --git a/test/rebar_profiles_SUITE.erl b/test/rebar_profiles_SUITE.erl index a4f926e..ab37255 100644 --- a/test/rebar_profiles_SUITE.erl +++ b/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}], diff --git a/test/rebar_release_SUITE.erl b/test/rebar_release_SUITE.erl index 92219a5..3809106 100644 --- a/test/rebar_release_SUITE.erl +++ b/test/rebar_release_SUITE.erl @@ -3,7 +3,10 @@  -include_lib("common_test/include/ct.hrl").  -include_lib("eunit/include/eunit.hrl"). -all() -> [release, tar]. +all() -> [release, +          dev_mode_release, +          profile_dev_mode_override_release, +          tar].  init_per_testcase(Case, Config0) ->      Config = rebar_test_utils:init_rebar_state(Config0), @@ -33,9 +36,46 @@ release(Config) ->      rebar_test_utils:run_and_check(        Config, RebarConfig,        ["release"], -      {ok, [{release, list_to_atom(Name), Vsn}]} +      {ok, [{release, list_to_atom(Name), Vsn, false}]}       ). +dev_mode_release(Config) -> +    AppDir = ?config(apps, Config), +    Name = ?config(name, Config), +    Vsn = "1.0.0", +    {ok, RebarConfig} = +        file:consult(rebar_test_utils:create_config(AppDir, +                                                    [{relx, [{release, {list_to_atom(Name), Vsn}, +                                                              [list_to_atom(Name)]}, +                                                             {lib_dirs, [AppDir]}, +                                                             {dev_mode, true}]}])), +    rebar_test_utils:run_and_check( +      Config, RebarConfig, +      ["release"], +      {ok, [{release, list_to_atom(Name), Vsn, true}]} +     ). + + +profile_dev_mode_override_release(Config) -> +    AppDir = ?config(apps, Config), +    Name = ?config(name, Config), +    Vsn = "1.0.0", +    {ok, RebarConfig} = +        file:consult(rebar_test_utils:create_config(AppDir, +                                                    [{relx, [{release, {list_to_atom(Name), Vsn}, +                                                              [list_to_atom(Name)]}, +                                                             {lib_dirs, [AppDir]}, +                                                             {dev_mode, true}]}, +                                                     {profiles, +                                                      [{ct, +                                                        [{relx, [{dev_mode, false}]}]}]}])), +    rebar_test_utils:run_and_check( +      Config, RebarConfig, +      ["as", "ct", "release"], +      {ok, [{release, list_to_atom(Name), Vsn, false}]} +     ). + +  tar(Config) ->      AppDir = ?config(apps, Config),      Name = ?config(name, Config), @@ -48,5 +88,5 @@ tar(Config) ->      rebar_test_utils:run_and_check(        Config, RebarConfig,        ["tar"], -      {ok, [{release, list_to_atom(Name), Vsn}, {tar, Name, Vsn}]} +      {ok, [{release, list_to_atom(Name), Vsn, false}, {tar, Name, Vsn}]}       ). diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl index 7c52a18..c035b91 100644 --- a/test/rebar_test_utils.erl +++ b/test/rebar_test_utils.erl @@ -217,7 +217,7 @@ check_results(AppDir, Expected) ->                          ?assertEqual(iolist_to_binary(Vsn),                                       iolist_to_binary(LockVsn))                  end -        ;  ({release, Name, Vsn}) -> +        ;  ({release, Name, Vsn, ExpectedDevMode}) ->                  ct:pal("Release: ~p-~s", [Name, Vsn]),                  {ok, Cwd} = file:get_cwd(),                  try @@ -228,6 +228,15 @@ check_results(AppDir, Expected) ->                      {ok, RelxState2} = rlx_prv_app_discover:do(RelxState1),                      {ok, RelxState3} = rlx_prv_rel_discover:do(RelxState2), +                    LibDir = filename:join([ReleaseDir, Name, "lib"]), +                    {ok, RelLibs} = file:list_dir(LibDir), +                    IsSymLinkFun = +                        fun(X) -> +                                ec_file:is_symlink(filename:join(LibDir, X)) +                        end, +                    DevMode = lists:all(IsSymLinkFun, RelLibs), +                    ?assertEqual(ExpectedDevMode, DevMode), +                      %% throws not_found if it doesn't exist                      rlx_state:get_realized_release(RelxState3, Name, Vsn)                  catch | 
