diff options
-rw-r--r-- | THANKS | 1 | ||||
-rw-r--r-- | src/rebar_app_discover.erl | 4 | ||||
-rw-r--r-- | src/rebar_prv_install_deps.erl | 3 | ||||
-rw-r--r-- | src/rebar_prv_plugins_upgrade.erl | 17 | ||||
-rw-r--r-- | src/rebar_prv_upgrade.erl | 13 | ||||
-rw-r--r-- | src/rebar_utils.erl | 69 | ||||
-rw-r--r-- | test/rebar_utils_SUITE.erl | 69 |
7 files changed, 149 insertions, 27 deletions
@@ -135,3 +135,4 @@ Pierre Fenoll David Kubecka Stefan Grundmann Carlos Eduardo de Paula +Derek Brown
\ No newline at end of file diff --git a/src/rebar_app_discover.erl b/src/rebar_app_discover.erl index f55a4d5..95b3273 100644 --- a/src/rebar_app_discover.erl +++ b/src/rebar_app_discover.erl @@ -51,6 +51,10 @@ merge_deps(AppInfo, State) -> rebar_state:apply_profiles( rebar_state:new(reset_hooks(rebar_state:opts(State, Default)), C, rebar_app_info:dir(AppInfo)), CurrentProfiles), Name), + + rebar_utils:check_min_otp_version(rebar_state:get(AppState, minimum_otp_vsn, undefined)), + rebar_utils:check_blacklisted_otp_versions(rebar_state:get(AppState, blacklisted_otp_vsns, [])), + AppState1 = rebar_state:set(AppState, artifacts, []), AppInfo1 = rebar_app_info:state(AppInfo, AppState1), diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl index 0276f5b..c4fd985 100644 --- a/src/rebar_prv_install_deps.erl +++ b/src/rebar_prv_install_deps.erl @@ -386,6 +386,9 @@ handle_dep(State, Profile, DepsDir, AppInfo, Locks, Level) -> S4 = rebar_state:set(S3, {plugins, Profile}, Plugins), AppInfo1 = rebar_app_info:state(AppInfo, S4), + rebar_utils:check_min_otp_version(rebar_state:get(S4, minimum_otp_vsn, undefined)), + rebar_utils:check_blacklisted_otp_versions(rebar_state:get(S4, blacklisted_otp_vsns, [])), + %% Dep may have plugins to install. Find and install here. S5 = rebar_plugins:install(S4), AppInfo2 = rebar_app_info:state(AppInfo1, S5), diff --git a/src/rebar_prv_plugins_upgrade.erl b/src/rebar_prv_plugins_upgrade.erl index f67b7dc..dfc9990 100644 --- a/src/rebar_prv_plugins_upgrade.erl +++ b/src/rebar_prv_plugins_upgrade.erl @@ -79,7 +79,7 @@ upgrade(Plugin, State) -> find_plugin(Plugin, Profiles, State) -> ec_lists:search(fun(Profile) -> Plugins = rebar_state:get(State, {plugins, Profile}, []), - case find(list_to_atom(Plugin), Plugins) of + case rebar_utils:tup_find(list_to_atom(Plugin), Plugins) of false -> not_found; P -> @@ -87,21 +87,6 @@ find_plugin(Plugin, Profiles, State) -> end end, Profiles). -find(_Plugin, []) -> - false; -find(Plugin, [Plugin | _Plugins]) -> - Plugin; -find(Plugin, [Plugin1 | Plugins]) when is_tuple(Plugin1) -> - case element(1, Plugin1) =:= Plugin of - true -> - Plugin1; - false -> - find(Plugin, Plugins) - end; -find(Plugin, [_Plugin | Plugins]) -> - find(Plugin, Plugins). - - build_plugin(AppInfo, Apps, State) -> Providers = rebar_state:providers(State), AppDir = rebar_app_info:dir(AppInfo), diff --git a/src/rebar_prv_upgrade.erl b/src/rebar_prv_upgrade.erl index 49d5674..46aed9e 100644 --- a/src/rebar_prv_upgrade.erl +++ b/src/rebar_prv_upgrade.erl @@ -94,21 +94,16 @@ prepare_locks([Name|Names], Deps, Locks, Unlocks) -> AtomName = binary_to_atom(Name, utf8), case lists:keyfind(Name, 1, Locks) of {_, _, 0} = Lock -> - case {lists:keyfind(AtomName, 1, Deps), lists:member(AtomName, Deps)} of - {false, false} -> + case rebar_utils:tup_find(AtomName, Deps) of + false -> ?PRV_ERROR({unknown_dependency, Name}); - {Dep, false} -> - {Source, NewLocks, NewUnlocks} = prepare_lock(Dep, Lock, Locks), - prepare_locks(Names, Deps, NewLocks, - [{Name, Source, 0} | NewUnlocks ++ Unlocks]); - {false, true} -> % package as a single atom - Dep = AtomName, + Dep -> {Source, NewLocks, NewUnlocks} = prepare_lock(Dep, Lock, Locks), prepare_locks(Names, Deps, NewLocks, [{Name, Source, 0} | NewUnlocks ++ Unlocks]) end; {_, _, Level} = Lock when Level > 0 -> - case lists:keyfind(AtomName, 1, Deps) of + case rebar_utils:tup_find(AtomName, Deps) of false -> ?PRV_ERROR({transitive_dependency, Name}); Dep -> % Dep has been promoted diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index 6eb4f4b..c729b58 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -56,11 +56,14 @@ wordsize/0, tup_umerge/2, tup_sort/1, + tup_find/2, line_count/1, set_httpc_options/0, escape_chars/1, escape_double_quotes/1, - escape_double_quotes_weak/1]). + escape_double_quotes_weak/1, + check_min_otp_version/1, + check_blacklisted_otp_versions/1]). %% for internal use only -export([otp_release/0]). @@ -264,6 +267,20 @@ tup_umerge([], Olds) -> tup_umerge([New|News], Olds) -> lists:reverse(umerge(News, Olds, [], New)). +tup_find(_Elem, []) -> + false; +tup_find(Elem, [Elem | _Elems]) -> + Elem; +tup_find(Elem, [Elem1 | Elems]) when is_tuple(Elem1) -> + case element(1, Elem1) =:= Elem of + true -> + Elem1; + false -> + tup_find(Elem, Elems) + end; +tup_find(Elem, [_Elem | Elems]) -> + tup_find(Elem, Elems). + %% This is equivalent to umerge2_2 in the stdlib, except we use the expanded %% value/key only to compare umerge(News, [Old|Olds], Merged, Cmp) when element(1, Cmp) == element(1, Old); @@ -299,9 +316,57 @@ line_count(PatchLines) -> Tokenized = string:tokens(PatchLines, "\n"), {ok, length(Tokenized)}. +check_min_otp_version(undefined) -> + ok; +check_min_otp_version(MinOtpVersion) -> + %% Fully-qualify with ?MODULE so the function can be meck'd in rebar_utils_SUITE + OtpRelease = ?MODULE:otp_release(), + ParsedMin = version_tuple(MinOtpVersion), + ParsedVsn = version_tuple(OtpRelease), + + case ParsedVsn >= ParsedMin of + true -> + ?DEBUG("~s satisfies the requirement for minimum OTP version ~s", + [OtpRelease, MinOtpVersion]); + false -> + ?ABORT("OTP release ~s or later is required. Version in use: ~s", + [MinOtpVersion, OtpRelease]) + end. + +check_blacklisted_otp_versions(undefined) -> + ok; +check_blacklisted_otp_versions(BlacklistedRegexes) -> + %% Fully-qualify with ?MODULE so the function can be meck'd in rebar_utils_SUITE + OtpRelease = ?MODULE:otp_release(), + lists:foreach( + fun(BlacklistedRegex) -> abort_if_blacklisted(BlacklistedRegex, OtpRelease) end, + BlacklistedRegexes). + +abort_if_blacklisted(BlacklistedRegex, OtpRelease) -> + case re:run(OtpRelease, BlacklistedRegex, [{capture, none}]) of + match -> + ?ABORT("OTP release ~s matches blacklisted version ~s", + [OtpRelease, BlacklistedRegex]); + nomatch -> + ?DEBUG("~s does not match blacklisted OTP version ~s", + [OtpRelease, BlacklistedRegex]) + end. + + %% ==================================================================== %% Internal functions %% ==================================================================== +version_tuple(OtpRelease) -> + case re:run(OtpRelease, "R?(\\d+)B?.?-?(\\d+)?.?-?(\\d+)?", [{capture, all, list}]) of + {match, [_Full, Maj, Min, Patch]} -> + {list_to_integer(Maj), list_to_integer(Min), list_to_integer(Patch)}; + {match, [_Full, Maj, Min]} -> + {list_to_integer(Maj), list_to_integer(Min), 0}; + {match, [_Full, Maj]} -> + {list_to_integer(Maj), 0, 0}; + nomatch -> + ?ABORT("Minimum OTP release unable to be parsed: ~s", [OtpRelease]) + end. otp_release() -> otp_release1(erlang:system_info(otp_release)). @@ -689,6 +754,8 @@ set_httpc_options(Scheme, Proxy) -> httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar). %% escape\ as\ a\ shell\? +escape_chars(Str) when is_atom(Str) -> + escape_chars(atom_to_list(Str)); escape_chars(Str) -> re:replace(Str, "([ ()?`!$])", "\\\\&", [global, {return, list}]). diff --git a/test/rebar_utils_SUITE.erl b/test/rebar_utils_SUITE.erl index f04ab63..24e8afe 100644 --- a/test/rebar_utils_SUITE.erl +++ b/test/rebar_utils_SUITE.erl @@ -1,6 +1,8 @@ -module(rebar_utils_SUITE). -export([all/0, + init_per_testcase/2, + end_per_testcase/2, groups/0, init_per_group/2, end_per_group/2, @@ -22,12 +24,23 @@ task_with_flag_with_commas/1, task_with_multiple_flags/1, special_task_do/1, + valid_otp_version/1, + valid_old_format_otp_version/1, + valid_otp_version_equal/1, + invalid_otp_version/1, + nonblacklisted_otp_version/1, + blacklisted_otp_version/1, sh_does_not_miss_messages/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). -include_lib("kernel/include/file.hrl"). +init_per_testcase(_, Config) -> + rebar_test_utils:init_rebar_state(Config). + +end_per_testcase(_, _Config) -> + catch meck:unload(). all() -> [{group, args_to_tasks}, @@ -51,7 +64,14 @@ groups() -> task_with_flag_with_trailing_comma, task_with_flag_with_commas, task_with_multiple_flags, - special_task_do]}]. + special_task_do, + valid_otp_version, + valid_old_format_otp_version, + valid_otp_version_equal, + invalid_otp_version, + nonblacklisted_otp_version, + blacklisted_otp_version + ]}]. init_per_group(_, Config) -> Config. end_per_group(_, Config) -> Config. @@ -120,6 +140,53 @@ special_task_do(_Config) -> "do", "bar,", "baz"]). + +valid_otp_version(_Config) -> + meck:new(rebar_utils, [passthrough]), + meck:expect(rebar_utils, otp_release, fun() -> "42.4" end), + rebar_utils:check_min_otp_version("42.3"), + meck:unload(rebar_utils). + +valid_old_format_otp_version(_Config) -> + meck:new(rebar_utils, [passthrough]), + meck:expect(rebar_utils, otp_release, fun() -> "R15B03-1" end), + rebar_utils:check_min_otp_version("14"), + + meck:expect(rebar_utils, otp_release, fun() -> "R16B03" end), + rebar_utils:check_min_otp_version("16.0"), + + meck:expect(rebar_utils, otp_release, fun() -> "18.0.1" end), + rebar_utils:check_min_otp_version("17.5.4"), + + meck:expect(rebar_utils, otp_release, fun() -> "18.0-rc1" end), + ?assertException(throw, rebar_abort, rebar_utils:check_min_otp_version("19")), + + meck:unload(rebar_utils). + +valid_otp_version_equal(_Config) -> + meck:new(rebar_utils, [passthrough]), + meck:expect(rebar_utils, otp_release, fun() -> "42.3" end), + rebar_utils:check_min_otp_version("42.3"), + meck:unload(rebar_utils). + +invalid_otp_version(_Config) -> + meck:new(rebar_utils, [passthrough]), + meck:expect(rebar_utils, otp_release, fun() -> "17.4" end), + ?assertException(throw, rebar_abort, rebar_utils:check_min_otp_version("42.3")), + meck:unload(rebar_utils). + +nonblacklisted_otp_version(_Config) -> + meck:new(rebar_utils, [passthrough]), + meck:expect(rebar_utils, otp_release, fun() -> "42.4" end), + rebar_utils:check_blacklisted_otp_versions(["1\\.2", "42\\.3"]), + meck:unload(rebar_utils). + +blacklisted_otp_version(_Config) -> + meck:new(rebar_utils, [passthrough]), + meck:expect(rebar_utils, otp_release, fun() -> "42.4" end), + ?assertException(throw, rebar_abort, rebar_utils:check_blacklisted_otp_versions(["1\\.2", "42\\.[1-4]"])), + meck:unload(rebar_utils). + sh_does_not_miss_messages(_Config) -> Source = "~nmain(_) ->~n io:format(\"donotmissme\").~n", file:write_file("do_not_miss_messages", io_lib:format(Source,[])), |