summaryrefslogtreecommitdiff
path: root/src/rebar_prv_update.erl
diff options
context:
space:
mode:
authorTristan Sloughter <tristan.sloughter@gmail.com>2015-02-17 10:35:50 -0600
committerTristan Sloughter <tristan.sloughter@gmail.com>2015-02-17 10:35:50 -0600
commit43a18bf4fa55a7ef23a6da837ba4450996fe8725 (patch)
tree14a08750177ecf866353583dd8d7549f0ba30864 /src/rebar_prv_update.erl
parentd65e6da7dae7a205050befb76752b58721660ff9 (diff)
parent501b9b42dfa4c06bc1d537e810f2e4d730e5d8f7 (diff)
Merge pull request #145 from tsloughter/hex
Replace package management with hex.pm
Diffstat (limited to 'src/rebar_prv_update.erl')
-rw-r--r--src/rebar_prv_update.erl106
1 files changed, 91 insertions, 15 deletions
diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl
index 524a684..f4985f3 100644
--- a/src/rebar_prv_update.erl
+++ b/src/rebar_prv_update.erl
@@ -34,18 +34,22 @@ init(State) ->
do(State) ->
?INFO("Updating package index...", []),
try
- Url = url(State),
+ Url = rebar_state:get(State, rebar_packages_cdn, "https://s3.amazonaws.com/s3.hex.pm/registry.ets.gz"),
TmpDir = ec_file:insecure_mkdtemp(),
- TmpFile = filename:join(TmpDir, "packages"),
- Home = rebar_dir:home_dir(),
- PackagesFile = filename:join([Home, ?CONFIG_DIR, "packages"]),
- filelib:ensure_dir(PackagesFile),
- {ok, _RequestId} = httpc:request(get, {Url, [{"Accept", "application/erlang"}]},
- [], [{stream, TmpFile}, {sync, true}]),
- ok = ec_file:copy(TmpFile, PackagesFile)
+ TmpFile = filename:join(TmpDir, "packages.gz"),
+ HexFile = filename:join(TmpDir, "packages"),
+ {ok, _RequestId} = httpc:request(get, {Url, []},
+ [], [{stream, TmpFile}, {sync, true}]),
+ {ok, Data} = file:read_file(TmpFile),
+ Unzipped = zlib:gunzip(Data),
+ ok = file:write_file(HexFile, Unzipped),
+ {Dict, Graph} = hex_to_graph(HexFile),
+ write_registry(Dict, Graph, State),
+ ok
catch
- _:_ ->
- {error, {?MODULE, package_index_write}}
+ E:C ->
+ io:format("E C ~p ~p~n", [E, C]),
+ throw({error, {?MODULE, package_index_write}})
end,
{ok, State}.
@@ -54,9 +58,81 @@ do(State) ->
format_error(package_index_write) ->
"Failed to write package index.".
-url(State) ->
- ErtsVsn = erlang:system_info(version),
+write_registry(Dict, {digraph, Edges, Vertices, Neighbors, _}, State) ->
+ Dir = rebar_dir:global_config_dir(State),
+ RegistryDir = filename:join(Dir, "packages"),
+ filelib:ensure_dir(filename:join(RegistryDir, "dummy")),
+ ets:tab2file(Edges, filename:join(RegistryDir, "edges")),
+ ets:tab2file(Vertices, filename:join(RegistryDir, "vertices")),
+ ets:tab2file(Neighbors, filename:join(RegistryDir, "neighbors")),
+ file:write_file(filename:join(RegistryDir, "dict"), term_to_binary(Dict)).
- Qs = [io_lib:format("~s=~s", [X, Y]) || {X, Y} <- [{"erts", ErtsVsn}]],
- Url = rebar_state:get(State, rebar_packages_url, "http://packages.rebar3.org/packages"),
- Url ++ "?" ++ string:join(Qs, "&").
+hex_to_graph(Filename) ->
+ {ok, T} = ets:file2tab(Filename),
+ Graph = digraph:new(),
+ ets:foldl(fun({Pkg, [Versions]}, ok) when is_binary(Pkg), is_list(Versions) ->
+ lists:foreach(fun(Version) ->
+ digraph:add_vertex(Graph, {Pkg, Version}, 1)
+ end, Versions);
+ (_, ok) ->
+ ok
+ end, ok, T),
+
+ Dict1 = ets:foldl(fun({{Pkg, PkgVsn}, [Deps | _]}, Dict) ->
+ DepsList = update_graph(Pkg, PkgVsn, Deps, T, Graph),
+ dict:store({Pkg, PkgVsn}, DepsList, Dict);
+ (_, Dict) ->
+ Dict
+ end, dict:new(), T),
+ {Dict1, Graph}.
+
+update_graph(Pkg, PkgVsn, Deps, HexRegistry, Graph) ->
+ lists:foldl(fun([Dep, DepVsn, _, _], DepsListAcc) ->
+ case DepVsn of
+ <<"~> ", Vsn/binary>> ->
+ HighestDepVsn = find_highest_matching(Dep, Vsn, HexRegistry),
+ digraph:add_edge(Graph, {Pkg, PkgVsn}, {Dep, HighestDepVsn}),
+ [{Dep, DepVsn} | DepsListAcc];
+ Vsn ->
+ digraph:add_edge(Graph, {Pkg, PkgVsn}, {Dep, Vsn}),
+ [{Dep, Vsn} | DepsListAcc]
+ end
+ end, [], Deps).
+
+%% Hex supports use of ~> to specify the version required for a dependency.
+%% Since rebar3 requires exact versions to choose from we find the highest
+%% available version of the dep that passes the constraint.
+
+%% `~>` will never include pre-release versions of its upper bound.
+%% It can also be used to set an upper bound on only the major
+%% version part. See the table below for `~>` requirements and
+%% their corresponding translation.
+%% `~>` | Translation
+%% :------------- | :---------------------
+%% `~> 2.0.0` | `>= 2.0.0 and < 2.1.0`
+%% `~> 2.1.2` | `>= 2.1.2 and < 2.2.0`
+%% `~> 2.1.3-dev` | `>= 2.1.3-dev and < 2.2.0`
+%% `~> 2.0` | `>= 2.0.0 and < 3.0.0`
+%% `~> 2.1` | `>= 2.1.0 and < 3.0.0`
+find_highest_matching(Dep, Constraint, T) ->
+ case ets:lookup(T, Dep) of
+ [{Dep, [[Vsn]]}] ->
+ case ec_semver:pes(Vsn, Constraint) of
+ true ->
+ Vsn;
+ false ->
+ ?WARN("Only existing version of ~s is ~s which does not match constraint ~~> ~s. "
+ "Using anyway, but it is not guarenteed to work.", [Dep, Vsn, Constraint]),
+ Vsn
+ end;
+ [{Dep, [[HeadVsn | VsnTail]]}] ->
+ lists:foldl(fun(Version, Highest) ->
+ case ec_semver:pes(Version, Constraint) andalso
+ ec_semver:gt(Version, Highest) of
+ true ->
+ Version;
+ false ->
+ Highest
+ end
+ end, HeadVsn, VsnTail)
+ end.