Introduce and use compat random number module

In order for rebar to work with previous and current OTP releases, we
generate the rebar_rnd module on startup. rebar_rnd is generated
dynamically to make sure that we use the right random module (either
rand if available or else random). It only wraps the common subset of
the API, but that's sufficient for rebar's use.
diff --git a/ebin/rebar.app b/ebin/rebar.app
index b7be991..4d588bd 100644
--- a/ebin/rebar.app
+++ b/ebin/rebar.app
@@ -46,7 +46,8 @@
               rebar_metacmds,
               rebar_getopt,
               rebar_mustache,
-              rmemo ]},
+              rmemo,
+              rebar_rand_compat ]},
   {registered, []},
   {applications,
    [
diff --git a/rebar.config b/rebar.config
index bcd5368..eda5a2c 100644
--- a/rebar.config
+++ b/rebar.config
@@ -30,7 +30,9 @@
       - (\"diameter_codegen\":\"from_dict\"/\"4\")
       - (\"diameter_dict_util\":\"format_error\"/\"1\")
       - (\"diameter_dict_util\":\"parse\"/\"2\")
-      - (\"erlang\":\"timestamp\"/\"0\"))",
+      - (\"erlang\":\"timestamp\"/\"0\")
+      - (\"rebar_rnd\":\"seed\"/\"1\")
+      - (\"rebar_rnd\":\"uniform\"/\"0\"))",
          []}]}.
 
 {dialyzer,
diff --git a/src/rebar.erl b/src/rebar.erl
index 5a809d2..2eb608d 100644
--- a/src/rebar.erl
+++ b/src/rebar.erl
@@ -220,6 +220,9 @@
         {error, {already_started, _}} -> ok
     end,
 
+    %% Make sure rebar_rnd module is generated, compiled, and loaded
+    {ok, rebar_rnd} = rebar_rand_compat:init("rebar_rnd"),
+
     %% Convert command strings to atoms
     CommandAtoms = [list_to_atom(C) || C <- Commands],
 
diff --git a/src/rebar_eunit.erl b/src/rebar_eunit.erl
index 6026740..f4d7b76 100644
--- a/src/rebar_eunit.erl
+++ b/src/rebar_eunit.erl
@@ -293,9 +293,9 @@
     end.
 
 randomize_suites1(Modules, Seed) ->
-    _ = random:seed(35, Seed, 1337),
+    _ = rebar_rnd:seed({35, Seed, 1337}),
     ?CONSOLE("Randomizing suite order with seed ~b~n", [Seed]),
-    [X||{_,X} <- lists:sort([{random:uniform(), M} || M <- Modules])].
+    [X||{_,X} <- lists:sort([{rebar_rnd:uniform(), M} || M <- Modules])].
 
 %%
 %% == get matching tests ==
diff --git a/src/rebar_rand_compat.erl b/src/rebar_rand_compat.erl
new file mode 100644
index 0000000..849ee35
--- /dev/null
+++ b/src/rebar_rand_compat.erl
@@ -0,0 +1,178 @@
+%%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%%% ex: ts=4 sw=4 et
+%%%
+%%% Copyright (c) 2016 Tuncer Ayaz
+%%%
+%%% Permission to use, copy, modify, and/or distribute this software
+%%% for any purpose with or without fee is hereby granted, provided
+%%% that the above copyright notice and this permission notice appear
+%%% in all copies.
+%%%
+%%% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
+%%% WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
+%%% WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
+%%% AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
+%%% CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+%%% LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+%%% NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+%%% CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+-module(rebar_rand_compat).
+
+%% API
+-export([ init/0
+        , init/1
+        ]).
+
+-define(DEFAULT_MODNAME, "rnd").
+
+%%%===================================================================
+%%% API
+%%%===================================================================
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Generate, compile and load rnd module.
+%% @end
+%%--------------------------------------------------------------------
+-spec init() -> {'ok', module()}.
+init() ->
+    init(?DEFAULT_MODNAME).
+
+%%--------------------------------------------------------------------
+%% @doc
+%% Generate, compile and load ModName module.
+%% @end
+%%--------------------------------------------------------------------
+-spec init(string()) -> {'ok', module()}.
+init(ModName) ->
+    %% First, select the right module, then generate the appropriate code as a
+    %% string, and finally compile and load it as ModName.
+    Src = select(ModName),
+    {ok, Mod, Bin, []} = compile(Src),
+    {module, Mod} = code:load_binary(Mod, ModName++".erl", Bin),
+    {ok, Mod}.
+
+%%%===================================================================
+%%% Internal functions
+%%%===================================================================
+
+%% Select right rand module and return wrapper module's source as string
+-spec select(string()) -> string().
+select(ModName) ->
+    case code:which(rand) of
+        non_existing ->
+            src(ModName, fun funs_random/0);
+        _ ->
+            src(ModName, fun funs_rand/0)
+    end.
+
+%% Return module's implementation as a string.
+-spec src(string(), fun(() -> binary())) -> string().
+src(ModName, GenFuns) ->
+    lists:flatten(
+      io_lib:format(
+        <<"
+-module(~s).
+-export([ seed/0
+        , seed/1
+        , uniform/0
+        , uniform/1
+        , uniform_s/1
+        , uniform_s/2
+        ]).
+
+%% Functions
+~s
+">>
+, [ModName, GenFuns()])).
+
+%% random.beam wrapper
+funs_random() ->
+    <<"
+seed()           -> random:seed().
+seed(Exp)        -> random:seed(Exp).
+uniform()        -> random:uniform().
+uniform(N)       -> random:uniform(N).
+uniform_s(St)    -> random:uniform_s(St).
+uniform_s(N, St) -> random:uniform_s(N, St).
+">>.
+
+%% rand.beam wrapper
+funs_rand() ->
+    <<"
+seed()           -> rand:seed(exsplus).
+seed(Exp)        -> rand:seed(exsplus, Exp).
+uniform()        -> rand:uniform().
+uniform(N)       -> rand:uniform(N).
+uniform_s(St)    -> rand:uniform_s(St).
+uniform_s(N, St) -> rand:uniform_s(N, St).
+">>.
+
+compile(String) ->
+    Forms = convert(String ++ eof, []),
+    compile:forms(Forms, [return]).
+
+%% Parse string into forms for compiler.
+convert({done, {eof, _EndLocation}, _LeftOverChars}, Acc)->
+    %% Finished
+    lists:reverse(Acc);
+convert({done, {error, ErrorInfo, _EndLocation}, _LeftOverChars}, _Acc)->
+    ErrorInfo;
+convert({done, {ok, Tokens, _EndLocation}, LeftOverChars}, Acc)->
+    case erl_parse:parse_form(Tokens) of
+        {ok, AbsForm} ->
+            convert(LeftOverChars, [AbsForm|Acc]);
+        {error, AbsForm} ->
+            convert(LeftOverChars, AbsForm)
+    end;
+convert({more, Continuation}, Acc)->
+    convert(erl_scan:tokens(Continuation, [], 1), Acc);
+convert(String, Acc) ->
+    convert(erl_scan:tokens([], String, 1), Acc).
+
+%%%===================================================================
+%%% Tests
+%%%===================================================================
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+init_test() ->
+    DefMod = list_to_atom(?DEFAULT_MODNAME),
+    ok = unload(DefMod),
+    ?assertMatch(false, code:is_loaded(DefMod)),
+    ?assertMatch({ok, DefMod}, init()),
+    ?assertMatch({file, _}, code:is_loaded(DefMod)),
+    check_api(DefMod),
+    CustomMod = foornd,
+    CustomName = "foornd",
+    ok = unload(CustomMod),
+    ?assertMatch(false, code:is_loaded(CustomMod)),
+    ?assertMatch({ok, CustomMod}, init(CustomName)),
+    ?assertMatch({file, _}, code:is_loaded(CustomMod)),
+    check_api(CustomMod).
+
+unload(Mod) ->
+    case code:is_loaded(Mod) of
+        false ->
+            ok;
+        {file, _} ->
+            code:delete(Mod),
+            code:purge(Mod),
+            ok
+    end.
+
+check_api(Mod) ->
+    Exports = [ {seed, 0}
+              , {seed, 1}
+              , {uniform, 0}
+              , {uniform, 1}
+              , {uniform_s, 1}
+              , {uniform_s, 2}
+              , {module_info, 0}
+              , {module_info, 1}
+              ],
+    ?assertMatch(Exports, Mod:module_info(exports)).
+
+-endif.