diff options
Diffstat (limited to 'src/rebar_prv_update.erl')
-rw-r--r-- | src/rebar_prv_update.erl | 106 |
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. |