summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--THANKS1
-rw-r--r--src/rebar_app_discover.erl4
-rw-r--r--src/rebar_prv_install_deps.erl3
-rw-r--r--src/rebar_prv_plugins_upgrade.erl17
-rw-r--r--src/rebar_prv_upgrade.erl13
-rw-r--r--src/rebar_utils.erl69
-rw-r--r--test/rebar_utils_SUITE.erl69
7 files changed, 149 insertions, 27 deletions
diff --git a/THANKS b/THANKS
index d813970..cf59ef1 100644
--- a/THANKS
+++ b/THANKS
@@ -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,[])),