Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

225 рядки
7.9 KiB

  1. -module(rebar_packages).
  2. -export([packages/1
  3. ,close_packages/0
  4. ,load_and_verify_version/1
  5. ,deps/3
  6. ,registry_dir/1
  7. ,package_dir/1
  8. ,registry_checksum/2
  9. ,find_highest_matching/6
  10. ,find_highest_matching/4
  11. ,find_all/3
  12. ,verify_table/1
  13. ,format_error/1]).
  14. -export_type([package/0]).
  15. -include("rebar.hrl").
  16. -include_lib("providers/include/providers.hrl").
  17. -type pkg_name() :: binary() | atom().
  18. -type vsn() :: binary().
  19. -type package() :: pkg_name() | {pkg_name(), vsn()}.
  20. -spec packages(rebar_state:t()) -> ets:tid().
  21. packages(State) ->
  22. catch ets:delete(?PACKAGE_TABLE),
  23. case load_and_verify_version(State) of
  24. true ->
  25. ok;
  26. false ->
  27. ?DEBUG("Error loading package index.", []),
  28. handle_bad_index(State)
  29. end.
  30. handle_bad_index(State) ->
  31. ?ERROR("Bad packages index. Trying to fix by updating the registry.", []),
  32. {ok, State1} = rebar_prv_update:do(State),
  33. case load_and_verify_version(State1) of
  34. true ->
  35. ok;
  36. false ->
  37. %% Still unable to load after an update, create an empty registry
  38. ets:new(?PACKAGE_TABLE, [named_table, public])
  39. end.
  40. close_packages() ->
  41. catch ets:delete(?PACKAGE_TABLE).
  42. load_and_verify_version(State) ->
  43. {ok, RegistryDir} = registry_dir(State),
  44. case ets:file2tab(filename:join(RegistryDir, ?INDEX_FILE)) of
  45. {ok, _} ->
  46. case ets:lookup_element(?PACKAGE_TABLE, package_index_version, 2) of
  47. ?PACKAGE_INDEX_VERSION ->
  48. true;
  49. _ ->
  50. (catch ets:delete(?PACKAGE_TABLE)),
  51. rebar_prv_update:hex_to_index(State)
  52. end;
  53. _ ->
  54. rebar_prv_update:hex_to_index(State)
  55. end.
  56. deps(Name, Vsn, State) ->
  57. try
  58. deps_(Name, Vsn, State)
  59. catch
  60. _:_ ->
  61. handle_missing_package({Name, Vsn}, State, fun(State1) -> deps_(Name, Vsn, State1) end)
  62. end.
  63. deps_(Name, Vsn, State) ->
  64. ?MODULE:verify_table(State),
  65. ets:lookup_element(?PACKAGE_TABLE, {ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)}, 2).
  66. handle_missing_package(Dep, State, Fun) ->
  67. case Dep of
  68. {Name, Vsn} ->
  69. ?INFO("Package ~s-~s not found. Fetching registry updates and trying again...", [Name, Vsn]);
  70. _ ->
  71. ?INFO("Package ~p not found. Fetching registry updates and trying again...", [Dep])
  72. end,
  73. {ok, State1} = rebar_prv_update:do(State),
  74. try
  75. Fun(State1)
  76. catch
  77. _:_ ->
  78. %% Even after an update the package is still missing, time to error out
  79. throw(?PRV_ERROR({missing_package, Dep}))
  80. end.
  81. registry_dir(State) ->
  82. CacheDir = rebar_dir:global_cache_dir(rebar_state:opts(State)),
  83. case rebar_state:get(State, rebar_packages_cdn, ?DEFAULT_CDN) of
  84. ?DEFAULT_CDN ->
  85. RegistryDir = filename:join([CacheDir, "hex", "default"]),
  86. ok = filelib:ensure_dir(filename:join(RegistryDir, "placeholder")),
  87. {ok, RegistryDir};
  88. CDN ->
  89. case rebar_utils:url_append_path(CDN, ?REMOTE_PACKAGE_DIR) of
  90. {ok, Parsed} ->
  91. {ok, {_, _, Host, _, Path, _}} = http_uri:parse(Parsed),
  92. CDNHostPath = lists:reverse(string:tokens(Host, ".")),
  93. CDNPath = tl(filename:split(Path)),
  94. RegistryDir = filename:join([CacheDir, "hex"] ++ CDNHostPath ++ CDNPath),
  95. ok = filelib:ensure_dir(filename:join(RegistryDir, "placeholder")),
  96. {ok, RegistryDir};
  97. _ ->
  98. {uri_parse_error, CDN}
  99. end
  100. end.
  101. package_dir(State) ->
  102. case registry_dir(State) of
  103. {ok, RegistryDir} ->
  104. PackageDir = filename:join([RegistryDir, "packages"]),
  105. ok = filelib:ensure_dir(filename:join(PackageDir, "placeholder")),
  106. {ok, PackageDir};
  107. Error ->
  108. Error
  109. end.
  110. registry_checksum({pkg, Name, Vsn, _Hash}, State) ->
  111. try
  112. ?MODULE:verify_table(State),
  113. ets:lookup_element(?PACKAGE_TABLE, {Name, Vsn}, 3)
  114. catch
  115. _:_ ->
  116. throw(?PRV_ERROR({missing_package, ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)}))
  117. end.
  118. %% Hex supports use of ~> to specify the version required for a dependency.
  119. %% Since rebar3 requires exact versions to choose from we find the highest
  120. %% available version of the dep that passes the constraint.
  121. %% `~>` will never include pre-release versions of its upper bound.
  122. %% It can also be used to set an upper bound on only the major
  123. %% version part. See the table below for `~>` requirements and
  124. %% their corresponding translation.
  125. %% `~>` | Translation
  126. %% :------------- | :---------------------
  127. %% `~> 2.0.0` | `>= 2.0.0 and < 2.1.0`
  128. %% `~> 2.1.2` | `>= 2.1.2 and < 2.2.0`
  129. %% `~> 2.1.3-dev` | `>= 2.1.3-dev and < 2.2.0`
  130. %% `~> 2.0` | `>= 2.0.0 and < 3.0.0`
  131. %% `~> 2.1` | `>= 2.1.0 and < 3.0.0`
  132. find_highest_matching(Dep, Constraint, Table, State) ->
  133. find_highest_matching(undefined, undefined, Dep, Constraint, Table, State).
  134. find_highest_matching(Pkg, PkgVsn, Dep, Constraint, Table, State) ->
  135. try find_highest_matching_(Pkg, PkgVsn, Dep, Constraint, Table, State) of
  136. none ->
  137. handle_missing_package(Dep, State,
  138. fun(State1) ->
  139. find_highest_matching_(Pkg, PkgVsn, Dep, Constraint, Table, State1)
  140. end);
  141. Result ->
  142. Result
  143. catch
  144. _:_ ->
  145. handle_missing_package(Dep, State,
  146. fun(State1) ->
  147. find_highest_matching_(Pkg, PkgVsn, Dep, Constraint, Table, State1)
  148. end)
  149. end.
  150. find_highest_matching_(Pkg, PkgVsn, Dep, Constraint, Table, State) ->
  151. try find_all(Dep, Table, State) of
  152. {ok, [Vsn]} ->
  153. handle_single_vsn(Pkg, PkgVsn, Dep, Vsn, Constraint);
  154. {ok, [HeadVsn | VsnTail]} ->
  155. {ok, handle_vsns(Constraint, HeadVsn, VsnTail)}
  156. catch
  157. error:badarg ->
  158. none
  159. end.
  160. find_all(Dep, Table, State) ->
  161. ?MODULE:verify_table(State),
  162. try ets:lookup_element(Table, Dep, 2) of
  163. [Vsns] when is_list(Vsns)->
  164. {ok, Vsns};
  165. Vsns ->
  166. {ok, Vsns}
  167. catch
  168. error:badarg ->
  169. none
  170. end.
  171. handle_vsns(Constraint, HeadVsn, VsnTail) ->
  172. lists:foldl(fun(Version, Highest) ->
  173. case ec_semver:pes(Version, Constraint) andalso
  174. ec_semver:gt(Version, Highest) of
  175. true ->
  176. Version;
  177. false ->
  178. Highest
  179. end
  180. end, HeadVsn, VsnTail).
  181. handle_single_vsn(Pkg, PkgVsn, Dep, Vsn, Constraint) ->
  182. case ec_semver:pes(Vsn, Constraint) of
  183. true ->
  184. {ok, Vsn};
  185. false ->
  186. case {Pkg, PkgVsn} of
  187. {undefined, undefined} ->
  188. ?WARN("Only existing version of ~s is ~s which does not match constraint ~~> ~s. "
  189. "Using anyway, but it is not guaranteed to work.", [Dep, Vsn, Constraint]);
  190. _ ->
  191. ?WARN("[~s:~s] Only existing version of ~s is ~s which does not match constraint ~~> ~s. "
  192. "Using anyway, but it is not guaranteed to work.", [Pkg, PkgVsn, Dep, Vsn, Constraint])
  193. end,
  194. {ok, Vsn}
  195. end.
  196. format_error({missing_package, {Name, Vsn}}) ->
  197. io_lib:format("Package not found in registry: ~s-~s.", [ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)]);
  198. format_error({missing_package, Dep}) ->
  199. io_lib:format("Package not found in registry: ~p.", [Dep]).
  200. verify_table(State) ->
  201. ets:info(?PACKAGE_TABLE, named_table) =:= true orelse load_and_verify_version(State).