1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
|
-module(rebar_packages).
-export([get_packages/1
,registry/1
,package_dir/1
,check_registry/3
,registry_checksum/2
,find_highest_matching/3]).
-export_type([package/0]).
-include("rebar.hrl").
-type pkg_name() :: binary() | atom().
-type vsn() :: binary().
-type package() :: pkg_name() | {pkg_name(), vsn()}.
-spec get_packages(rebar_state:t()) -> {rebar_dict(), rebar_digraph()}.
get_packages(State) ->
RegistryDir = package_dir(State),
DictFile = filename:join(RegistryDir, "dict"),
Edges = filename:join(RegistryDir, "edges"),
Vertices = filename:join(RegistryDir, "vertices"),
Neighbors = filename:join(RegistryDir, "neighbors"),
case lists:all(fun(X) -> filelib:is_file(X) end, [DictFile, Edges, Vertices, Neighbors]) of
true ->
try
{ok, DictBinary} = file:read_file(DictFile),
Dict = binary_to_term(DictBinary),
{ok, EdgesTab} = ets:file2tab(Edges),
{ok, VerticesTab} = ets:file2tab(Vertices),
{ok, NeighborsTab} = ets:file2tab(Neighbors),
{Dict, {digraph, EdgesTab, VerticesTab, NeighborsTab, true}}
catch
_:_ ->
?ERROR("Bad packages index, try to fix with `rebar3 update`", []),
{dict:new(), digraph:new()}
end;
false ->
?ERROR("Bad packages index, try to fix with `rebar3 update`", []),
{dict:new(), digraph:new()}
end.
registry(State) ->
RegistryDir = package_dir(State),
HexFile = filename:join(RegistryDir, "registry"),
case ets:file2tab(HexFile) of
{ok, T} ->
{ok, T};
{error, Reason} ->
?DEBUG("Error loading registry: ~p", [Reason]),
error
end.
package_dir(State) ->
CacheDir = rebar_dir:global_cache_dir(State),
CDN = rebar_state:get(State, rebar_packages_cdn, ?DEFAULT_CDN),
{ok, {_, _, Host, _, Path, _}} = http_uri:parse(CDN),
CDNHostPath = lists:reverse(string:tokens(Host, ".")),
CDNPath = tl(filename:split(Path)),
PackageDir = filename:join([CacheDir, "hex"] ++ CDNHostPath ++ CDNPath ++ ["packages"]),
ok = filelib:ensure_dir(filename:join(PackageDir, "placeholder")),
PackageDir.
check_registry(Pkg, Vsn, State) ->
case rebar_state:registry(State) of
{ok, T} ->
case ets:lookup(T, Pkg) of
[{Pkg, [Vsns]}] ->
lists:member(Vsn, Vsns);
_ ->
false
end;
error ->
false
end.
registry_checksum({pkg, Name, Vsn}, State) ->
{ok, Registry} = registry(State),
case ets:lookup(Registry, {Name, Vsn}) of
[{{_, _}, [_, Checksum | _]}] ->
Checksum;
[] ->
none
end.
%% 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 ->
{ok, 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]),
{ok, Vsn}
end;
[{Dep, [[HeadVsn | VsnTail]]}] ->
{ok, 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)};
[] ->
?WARN("Missing registry entry for package ~s", [Dep]),
none
end.
|