You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1202 line
43 KiB

15 年之前
15 年之前
support for hex v2, multiple repository fetching, private organizations (#1884) * update to hex_core for hex-v2 repo support (#1865) * update to hex_core for hex-v2 repo support This patch adds only single repo hex-v2 support through hex_core. Packages no longer filtered out by buildtool metadata and the package index is updated per-package instead of fetched as one large ets dump. * tell travis to also build hex_core branch * support list of repos for hex packages (#1866) * support list of repos for hex packages repos are defined under the hex key in rebar configs. They can be defined at the top level of a project or globally, but not in profiles and the repos configured in dependencies are also ignored. Searching for packages involves first checking for a match in the local repo index cache, in the order repos are defined. If not found each repo is checked through the hex api for any known versions of the package and the first repo with a version that fits the constraint is used. * add {repos, replace, []} for overriding the global & default repos * add hex auth handling for repos (#1874) auth token are kept in a hex.config file that is modified by the rebar3 hex plugin. Repo names that have a : separating a parent and child are considered organizations. The parent repo's auth will be included with the child. So an organization named hexpm:rebar3_test will include any hexpm auth tokens found in the rebar3_test organization's configuration. * move packages to top level of of hexpm cache dir (#1876) * move packages to top level of of hexpm cache dir * append organization name to parent's repo_url when parsing repos * only eval config scripts and apply overrides once per app (#1879) * only eval config scripts and apply overrides once per app * move new resource behaviour to rebar_resource_v2 and keep v1 * cleanup use of rebar_resource module and unused functions * cleanup error messages and unused code * when discovering apps support mix packages as unbuilt apps (#1882) * use hex_core tarball unpacking support in pkg resource (#1883) * use hex_core tarball unpacking support in pkg resource * ignore etag if package doesn't exist and delete if checksum fails * add back tests for bad package checksums * improve bad registry checksum error message
6 年之前
support for hex v2, multiple repository fetching, private organizations (#1884) * update to hex_core for hex-v2 repo support (#1865) * update to hex_core for hex-v2 repo support This patch adds only single repo hex-v2 support through hex_core. Packages no longer filtered out by buildtool metadata and the package index is updated per-package instead of fetched as one large ets dump. * tell travis to also build hex_core branch * support list of repos for hex packages (#1866) * support list of repos for hex packages repos are defined under the hex key in rebar configs. They can be defined at the top level of a project or globally, but not in profiles and the repos configured in dependencies are also ignored. Searching for packages involves first checking for a match in the local repo index cache, in the order repos are defined. If not found each repo is checked through the hex api for any known versions of the package and the first repo with a version that fits the constraint is used. * add {repos, replace, []} for overriding the global & default repos * add hex auth handling for repos (#1874) auth token are kept in a hex.config file that is modified by the rebar3 hex plugin. Repo names that have a : separating a parent and child are considered organizations. The parent repo's auth will be included with the child. So an organization named hexpm:rebar3_test will include any hexpm auth tokens found in the rebar3_test organization's configuration. * move packages to top level of of hexpm cache dir (#1876) * move packages to top level of of hexpm cache dir * append organization name to parent's repo_url when parsing repos * only eval config scripts and apply overrides once per app (#1879) * only eval config scripts and apply overrides once per app * move new resource behaviour to rebar_resource_v2 and keep v1 * cleanup use of rebar_resource module and unused functions * cleanup error messages and unused code * when discovering apps support mix packages as unbuilt apps (#1882) * use hex_core tarball unpacking support in pkg resource (#1883) * use hex_core tarball unpacking support in pkg resource * ignore etag if package doesn't exist and delete if checksum fails * add back tests for bad package checksums * improve bad registry checksum error message
6 年之前
15 年之前
9 年之前
9 年之前
9 年之前
9 年之前
9 年之前
support for hex v2, multiple repository fetching, private organizations (#1884) * update to hex_core for hex-v2 repo support (#1865) * update to hex_core for hex-v2 repo support This patch adds only single repo hex-v2 support through hex_core. Packages no longer filtered out by buildtool metadata and the package index is updated per-package instead of fetched as one large ets dump. * tell travis to also build hex_core branch * support list of repos for hex packages (#1866) * support list of repos for hex packages repos are defined under the hex key in rebar configs. They can be defined at the top level of a project or globally, but not in profiles and the repos configured in dependencies are also ignored. Searching for packages involves first checking for a match in the local repo index cache, in the order repos are defined. If not found each repo is checked through the hex api for any known versions of the package and the first repo with a version that fits the constraint is used. * add {repos, replace, []} for overriding the global & default repos * add hex auth handling for repos (#1874) auth token are kept in a hex.config file that is modified by the rebar3 hex plugin. Repo names that have a : separating a parent and child are considered organizations. The parent repo's auth will be included with the child. So an organization named hexpm:rebar3_test will include any hexpm auth tokens found in the rebar3_test organization's configuration. * move packages to top level of of hexpm cache dir (#1876) * move packages to top level of of hexpm cache dir * append organization name to parent's repo_url when parsing repos * only eval config scripts and apply overrides once per app (#1879) * only eval config scripts and apply overrides once per app * move new resource behaviour to rebar_resource_v2 and keep v1 * cleanup use of rebar_resource module and unused functions * cleanup error messages and unused code * when discovering apps support mix packages as unbuilt apps (#1882) * use hex_core tarball unpacking support in pkg resource (#1883) * use hex_core tarball unpacking support in pkg resource * ignore etag if package doesn't exist and delete if checksum fails * add back tests for bad package checksums * improve bad registry checksum error message
6 年之前
support for hex v2, multiple repository fetching, private organizations (#1884) * update to hex_core for hex-v2 repo support (#1865) * update to hex_core for hex-v2 repo support This patch adds only single repo hex-v2 support through hex_core. Packages no longer filtered out by buildtool metadata and the package index is updated per-package instead of fetched as one large ets dump. * tell travis to also build hex_core branch * support list of repos for hex packages (#1866) * support list of repos for hex packages repos are defined under the hex key in rebar configs. They can be defined at the top level of a project or globally, but not in profiles and the repos configured in dependencies are also ignored. Searching for packages involves first checking for a match in the local repo index cache, in the order repos are defined. If not found each repo is checked through the hex api for any known versions of the package and the first repo with a version that fits the constraint is used. * add {repos, replace, []} for overriding the global & default repos * add hex auth handling for repos (#1874) auth token are kept in a hex.config file that is modified by the rebar3 hex plugin. Repo names that have a : separating a parent and child are considered organizations. The parent repo's auth will be included with the child. So an organization named hexpm:rebar3_test will include any hexpm auth tokens found in the rebar3_test organization's configuration. * move packages to top level of of hexpm cache dir (#1876) * move packages to top level of of hexpm cache dir * append organization name to parent's repo_url when parsing repos * only eval config scripts and apply overrides once per app (#1879) * only eval config scripts and apply overrides once per app * move new resource behaviour to rebar_resource_v2 and keep v1 * cleanup use of rebar_resource module and unused functions * cleanup error messages and unused code * when discovering apps support mix packages as unbuilt apps (#1882) * use hex_core tarball unpacking support in pkg resource (#1883) * use hex_core tarball unpacking support in pkg resource * ignore etag if package doesn't exist and delete if checksum fails * add back tests for bad package checksums * improve bad registry checksum error message
6 年之前
support for hex v2, multiple repository fetching, private organizations (#1884) * update to hex_core for hex-v2 repo support (#1865) * update to hex_core for hex-v2 repo support This patch adds only single repo hex-v2 support through hex_core. Packages no longer filtered out by buildtool metadata and the package index is updated per-package instead of fetched as one large ets dump. * tell travis to also build hex_core branch * support list of repos for hex packages (#1866) * support list of repos for hex packages repos are defined under the hex key in rebar configs. They can be defined at the top level of a project or globally, but not in profiles and the repos configured in dependencies are also ignored. Searching for packages involves first checking for a match in the local repo index cache, in the order repos are defined. If not found each repo is checked through the hex api for any known versions of the package and the first repo with a version that fits the constraint is used. * add {repos, replace, []} for overriding the global & default repos * add hex auth handling for repos (#1874) auth token are kept in a hex.config file that is modified by the rebar3 hex plugin. Repo names that have a : separating a parent and child are considered organizations. The parent repo's auth will be included with the child. So an organization named hexpm:rebar3_test will include any hexpm auth tokens found in the rebar3_test organization's configuration. * move packages to top level of of hexpm cache dir (#1876) * move packages to top level of of hexpm cache dir * append organization name to parent's repo_url when parsing repos * only eval config scripts and apply overrides once per app (#1879) * only eval config scripts and apply overrides once per app * move new resource behaviour to rebar_resource_v2 and keep v1 * cleanup use of rebar_resource module and unused functions * cleanup error messages and unused code * when discovering apps support mix packages as unbuilt apps (#1882) * use hex_core tarball unpacking support in pkg resource (#1883) * use hex_core tarball unpacking support in pkg resource * ignore etag if package doesn't exist and delete if checksum fails * add back tests for bad package checksums * improve bad registry checksum error message
6 年之前
support for hex v2, multiple repository fetching, private organizations (#1884) * update to hex_core for hex-v2 repo support (#1865) * update to hex_core for hex-v2 repo support This patch adds only single repo hex-v2 support through hex_core. Packages no longer filtered out by buildtool metadata and the package index is updated per-package instead of fetched as one large ets dump. * tell travis to also build hex_core branch * support list of repos for hex packages (#1866) * support list of repos for hex packages repos are defined under the hex key in rebar configs. They can be defined at the top level of a project or globally, but not in profiles and the repos configured in dependencies are also ignored. Searching for packages involves first checking for a match in the local repo index cache, in the order repos are defined. If not found each repo is checked through the hex api for any known versions of the package and the first repo with a version that fits the constraint is used. * add {repos, replace, []} for overriding the global & default repos * add hex auth handling for repos (#1874) auth token are kept in a hex.config file that is modified by the rebar3 hex plugin. Repo names that have a : separating a parent and child are considered organizations. The parent repo's auth will be included with the child. So an organization named hexpm:rebar3_test will include any hexpm auth tokens found in the rebar3_test organization's configuration. * move packages to top level of of hexpm cache dir (#1876) * move packages to top level of of hexpm cache dir * append organization name to parent's repo_url when parsing repos * only eval config scripts and apply overrides once per app (#1879) * only eval config scripts and apply overrides once per app * move new resource behaviour to rebar_resource_v2 and keep v1 * cleanup use of rebar_resource module and unused functions * cleanup error messages and unused code * when discovering apps support mix packages as unbuilt apps (#1882) * use hex_core tarball unpacking support in pkg resource (#1883) * use hex_core tarball unpacking support in pkg resource * ignore etag if package doesn't exist and delete if checksum fails * add back tests for bad package checksums * improve bad registry checksum error message
6 年之前
support for hex v2, multiple repository fetching, private organizations (#1884) * update to hex_core for hex-v2 repo support (#1865) * update to hex_core for hex-v2 repo support This patch adds only single repo hex-v2 support through hex_core. Packages no longer filtered out by buildtool metadata and the package index is updated per-package instead of fetched as one large ets dump. * tell travis to also build hex_core branch * support list of repos for hex packages (#1866) * support list of repos for hex packages repos are defined under the hex key in rebar configs. They can be defined at the top level of a project or globally, but not in profiles and the repos configured in dependencies are also ignored. Searching for packages involves first checking for a match in the local repo index cache, in the order repos are defined. If not found each repo is checked through the hex api for any known versions of the package and the first repo with a version that fits the constraint is used. * add {repos, replace, []} for overriding the global & default repos * add hex auth handling for repos (#1874) auth token are kept in a hex.config file that is modified by the rebar3 hex plugin. Repo names that have a : separating a parent and child are considered organizations. The parent repo's auth will be included with the child. So an organization named hexpm:rebar3_test will include any hexpm auth tokens found in the rebar3_test organization's configuration. * move packages to top level of of hexpm cache dir (#1876) * move packages to top level of of hexpm cache dir * append organization name to parent's repo_url when parsing repos * only eval config scripts and apply overrides once per app (#1879) * only eval config scripts and apply overrides once per app * move new resource behaviour to rebar_resource_v2 and keep v1 * cleanup use of rebar_resource module and unused functions * cleanup error messages and unused code * when discovering apps support mix packages as unbuilt apps (#1882) * use hex_core tarball unpacking support in pkg resource (#1883) * use hex_core tarball unpacking support in pkg resource * ignore etag if package doesn't exist and delete if checksum fails * add back tests for bad package checksums * improve bad registry checksum error message
6 年之前
support for hex v2, multiple repository fetching, private organizations (#1884) * update to hex_core for hex-v2 repo support (#1865) * update to hex_core for hex-v2 repo support This patch adds only single repo hex-v2 support through hex_core. Packages no longer filtered out by buildtool metadata and the package index is updated per-package instead of fetched as one large ets dump. * tell travis to also build hex_core branch * support list of repos for hex packages (#1866) * support list of repos for hex packages repos are defined under the hex key in rebar configs. They can be defined at the top level of a project or globally, but not in profiles and the repos configured in dependencies are also ignored. Searching for packages involves first checking for a match in the local repo index cache, in the order repos are defined. If not found each repo is checked through the hex api for any known versions of the package and the first repo with a version that fits the constraint is used. * add {repos, replace, []} for overriding the global & default repos * add hex auth handling for repos (#1874) auth token are kept in a hex.config file that is modified by the rebar3 hex plugin. Repo names that have a : separating a parent and child are considered organizations. The parent repo's auth will be included with the child. So an organization named hexpm:rebar3_test will include any hexpm auth tokens found in the rebar3_test organization's configuration. * move packages to top level of of hexpm cache dir (#1876) * move packages to top level of of hexpm cache dir * append organization name to parent's repo_url when parsing repos * only eval config scripts and apply overrides once per app (#1879) * only eval config scripts and apply overrides once per app * move new resource behaviour to rebar_resource_v2 and keep v1 * cleanup use of rebar_resource module and unused functions * cleanup error messages and unused code * when discovering apps support mix packages as unbuilt apps (#1882) * use hex_core tarball unpacking support in pkg resource (#1883) * use hex_core tarball unpacking support in pkg resource * ignore etag if package doesn't exist and delete if checksum fails * add back tests for bad package checksums * improve bad registry checksum error message
6 年之前
support for hex v2, multiple repository fetching, private organizations (#1884) * update to hex_core for hex-v2 repo support (#1865) * update to hex_core for hex-v2 repo support This patch adds only single repo hex-v2 support through hex_core. Packages no longer filtered out by buildtool metadata and the package index is updated per-package instead of fetched as one large ets dump. * tell travis to also build hex_core branch * support list of repos for hex packages (#1866) * support list of repos for hex packages repos are defined under the hex key in rebar configs. They can be defined at the top level of a project or globally, but not in profiles and the repos configured in dependencies are also ignored. Searching for packages involves first checking for a match in the local repo index cache, in the order repos are defined. If not found each repo is checked through the hex api for any known versions of the package and the first repo with a version that fits the constraint is used. * add {repos, replace, []} for overriding the global & default repos * add hex auth handling for repos (#1874) auth token are kept in a hex.config file that is modified by the rebar3 hex plugin. Repo names that have a : separating a parent and child are considered organizations. The parent repo's auth will be included with the child. So an organization named hexpm:rebar3_test will include any hexpm auth tokens found in the rebar3_test organization's configuration. * move packages to top level of of hexpm cache dir (#1876) * move packages to top level of of hexpm cache dir * append organization name to parent's repo_url when parsing repos * only eval config scripts and apply overrides once per app (#1879) * only eval config scripts and apply overrides once per app * move new resource behaviour to rebar_resource_v2 and keep v1 * cleanup use of rebar_resource module and unused functions * cleanup error messages and unused code * when discovering apps support mix packages as unbuilt apps (#1882) * use hex_core tarball unpacking support in pkg resource (#1883) * use hex_core tarball unpacking support in pkg resource * ignore etag if package doesn't exist and delete if checksum fails * add back tests for bad package checksums * improve bad registry checksum error message
6 年之前
  1. %% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
  2. %% ex: ts=4 sw=4 et
  3. %% -------------------------------------------------------------------
  4. %%
  5. %% rebar: Erlang Build Tools
  6. %%
  7. %% Copyright (c) 2009, 2010 Dave Smith (dizzyd@dizzyd.com)
  8. %%
  9. %% Permission is hereby granted, free of charge, to any person obtaining a copy
  10. %% of this software and associated documentation files (the "Software"), to deal
  11. %% in the Software without restriction, including without limitation the rights
  12. %% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  13. %% copies of the Software, and to permit persons to whom the Software is
  14. %% furnished to do so, subject to the following conditions:
  15. %%
  16. %% The above copyright notice and this permission notice shall be included in
  17. %% all copies or substantial portions of the Software.
  18. %%
  19. %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  20. %% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  21. %% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  22. %% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  23. %% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  24. %% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  25. %% THE SOFTWARE.
  26. %% -------------------------------------------------------------------
  27. -module(rebar_utils).
  28. -export([sort_deps/1,
  29. droplast/1,
  30. filtermap/2,
  31. is_arch/1,
  32. sh/2,
  33. sh_send/3,
  34. abort/0,
  35. abort/2,
  36. escript_foldl/3,
  37. find_files/2,
  38. find_files/3,
  39. find_files_in_dirs/3,
  40. find_source/3,
  41. beam_to_mod/1,
  42. erl_to_mod/1,
  43. beams/1,
  44. find_executable/1,
  45. vcs_vsn/3,
  46. deprecated/3,
  47. deprecated/4,
  48. indent/1,
  49. update_code/1,
  50. update_code/2,
  51. remove_from_code_path/1,
  52. cleanup_code_path/1,
  53. args_to_tasks/1,
  54. expand_env_variable/3,
  55. get_arch/0,
  56. wordsize/0,
  57. deps_to_binary/1,
  58. to_binary/1,
  59. to_list/1,
  60. tup_dedup/1,
  61. tup_umerge/2,
  62. tup_sort/1,
  63. tup_find/2,
  64. line_count/1,
  65. set_httpc_options/0,
  66. url_append_path/2,
  67. escape_chars/1,
  68. escape_double_quotes/1,
  69. escape_double_quotes_weak/1,
  70. check_min_otp_version/1,
  71. check_blacklisted_otp_versions/1,
  72. info_useless/2,
  73. list_dir/1,
  74. user_agent/0,
  75. reread_config/1, reread_config/2,
  76. get_proxy_auth/0,
  77. is_list_of_strings/1,
  78. ssl_opts/1]).
  79. %% for internal use only
  80. -export([otp_release/0]).
  81. -include("rebar.hrl").
  82. -include_lib("public_key/include/OTP-PUB-KEY.hrl").
  83. -define(ONE_LEVEL_INDENT, " ").
  84. -define(APP_NAME_INDEX, 2).
  85. %% ====================================================================
  86. %% Public API
  87. %% ====================================================================
  88. sort_deps(Deps) ->
  89. %% We need a sort stable, based on the name. So that for multiple deps on
  90. %% the same level with the same name, we keep the order the parents had.
  91. %% `lists:keysort/2' is documented as stable in the stdlib.
  92. %% The list of deps is revered when we get it. For the proper stable
  93. %% result, re-reverse it.
  94. lists:keysort(?APP_NAME_INDEX, lists:reverse(Deps)).
  95. droplast(L) ->
  96. lists:reverse(tl(lists:reverse(L))).
  97. %% @doc filtermap takes in a function that is either or both
  98. %% a predicate and a map, and returns the matching and valid elements.
  99. -spec filtermap(F, [In]) -> [Out] when
  100. F :: fun((In) -> boolean() | {true, Out}),
  101. In :: term(),
  102. Out :: term().
  103. filtermap(F, [Hd|Tail]) ->
  104. case F(Hd) of
  105. true ->
  106. [Hd|filtermap(F, Tail)];
  107. {true,Val} ->
  108. [Val|filtermap(F, Tail)];
  109. false ->
  110. filtermap(F, Tail)
  111. end;
  112. filtermap(F, []) when is_function(F, 1) -> [].
  113. is_arch(ArchRegex) ->
  114. case re:run(get_arch(), ArchRegex, [{capture, none}, unicode]) of
  115. match ->
  116. true;
  117. nomatch ->
  118. false
  119. end.
  120. %% @doc returns the sytem architecture, in strings like
  121. %% `"19.0.4-x86_64-unknown-linux-gnu-64"'.
  122. -spec get_arch() -> string().
  123. get_arch() ->
  124. Words = wordsize(),
  125. otp_release() ++ "-"
  126. ++ erlang:system_info(system_architecture) ++ "-" ++ Words.
  127. %% @doc returns the size of a word on the system, as a string
  128. -spec wordsize() -> string().
  129. wordsize() ->
  130. try erlang:system_info({wordsize, external}) of
  131. Val ->
  132. integer_to_list(8 * Val)
  133. catch
  134. error:badarg ->
  135. integer_to_list(8 * erlang:system_info(wordsize))
  136. end.
  137. sh_send(Command0, String, Options0) ->
  138. ?INFO("sh_send info:\n\tcwd: ~p\n\tcmd: ~ts < ~ts\n",
  139. [rebar_dir:get_cwd(), Command0, String]),
  140. ?DEBUG("\topts: ~p\n", [Options0]),
  141. DefaultOptions = [use_stdout, abort_on_error],
  142. Options = [expand_sh_flag(V)
  143. || V <- proplists:compact(Options0 ++ DefaultOptions)],
  144. Command = lists:flatten(patch_on_windows(Command0, proplists:get_value(env, Options0, []))),
  145. PortSettings = proplists:get_all_values(port_settings, Options) ++
  146. [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide],
  147. Port = open_port({spawn, Command}, PortSettings),
  148. try
  149. %% allow us to send some data to the shell command's STDIN
  150. %% Erlang doesn't let us get any reply after sending an EOF, though...
  151. Port ! {self(), {command, String}}
  152. after
  153. port_close(Port)
  154. end.
  155. %%
  156. %% Options = [Option] -- defaults to [use_stdout, abort_on_error]
  157. %% Option = ErrorOption | OutputOption | {cd, string()} | {env, Env}
  158. %% ErrorOption = return_on_error | abort_on_error | {abort_on_error, string()}
  159. %% OutputOption = use_stdout | {use_stdout, bool()}
  160. %% Env = [{string(), Val}]
  161. %% Val = string() | false
  162. %%
  163. sh(Command0, Options0) ->
  164. ?DEBUG("sh info:\n\tcwd: ~p\n\tcmd: ~ts\n", [rebar_dir:get_cwd(), Command0]),
  165. ?DEBUG("\topts: ~p\n", [Options0]),
  166. DefaultOptions = [{use_stdout, false}, debug_and_abort_on_error],
  167. Options = [expand_sh_flag(V)
  168. || V <- proplists:compact(Options0 ++ DefaultOptions)],
  169. ErrorHandler = proplists:get_value(error_handler, Options),
  170. OutputHandler = proplists:get_value(output_handler, Options),
  171. Command = lists:flatten(patch_on_windows(Command0, proplists:get_value(env, Options0, []))),
  172. PortSettings = proplists:get_all_values(port_settings, Options) ++
  173. [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide, eof, binary],
  174. ?DEBUG("Port Cmd: ~ts\nPort Opts: ~p\n", [Command, PortSettings]),
  175. Port = open_port({spawn, Command}, PortSettings),
  176. try
  177. case sh_loop(Port, OutputHandler, []) of
  178. {ok, _Output} = Ok ->
  179. Ok;
  180. {error, {_Rc, _Output}=Err} ->
  181. ErrorHandler(Command, Err)
  182. end
  183. after
  184. port_close(Port)
  185. end.
  186. find_files(Dir, Regex) ->
  187. find_files(Dir, Regex, true).
  188. find_files_in_dirs([], _Regex, _Recursive) ->
  189. [];
  190. find_files_in_dirs([Dir | T], Regex, Recursive) ->
  191. find_files(Dir, Regex, Recursive) ++ find_files_in_dirs(T, Regex, Recursive).
  192. find_files(Dir, Regex, Recursive) ->
  193. filelib:fold_files(Dir, Regex, Recursive,
  194. fun(F, Acc) -> [F | Acc] end, []).
  195. find_executable(Name) ->
  196. case os:find_executable(Name) of
  197. false -> false;
  198. Path ->
  199. "\"" ++ filename:nativename(Path) ++ "\""
  200. end.
  201. deprecated(Old, New, Opts, When) when is_list(Opts) ->
  202. case lists:member(Old, Opts) of
  203. true ->
  204. deprecated(Old, New, When);
  205. false ->
  206. ok
  207. end;
  208. deprecated(Old, New, Config, When) ->
  209. case rebar_state:get(Config, Old, undefined) of
  210. undefined ->
  211. ok;
  212. _ ->
  213. deprecated(Old, New, When)
  214. end.
  215. deprecated(Old, New, When) ->
  216. io:format(
  217. <<"WARNING: deprecated ~p option used~n"
  218. "Option '~p' has been deprecated~n"
  219. "in favor of '~p'.~n"
  220. "'~p' will be removed ~ts.~n">>,
  221. [Old, Old, New, Old, When]).
  222. %% for use by `do` task
  223. %% note: this does not handle the case where you have an argument that
  224. %% was enclosed in quotes and might have commas but should not be split.
  225. args_to_tasks(Args) -> new_task(Args, []).
  226. deps_to_binary([]) ->
  227. [];
  228. deps_to_binary([{Name, _, Source} | T]) ->
  229. [{to_binary(Name), Source} | deps_to_binary(T)];
  230. deps_to_binary([{Name, Source} | T]) ->
  231. [{to_binary(Name), Source} | deps_to_binary(T)];
  232. deps_to_binary([Name | T]) ->
  233. [to_binary(Name) | deps_to_binary(T)].
  234. to_binary(A) when is_atom(A) -> atom_to_binary(A, unicode);
  235. to_binary(Str) -> unicode:characters_to_binary(Str).
  236. to_list(A) when is_atom(A) -> atom_to_list(A);
  237. to_list(Str) -> unicode:characters_to_list(Str).
  238. tup_dedup(List) ->
  239. tup_dedup_(tup_sort(List)).
  240. tup_dedup_([]) ->
  241. [];
  242. tup_dedup_([A]) ->
  243. [A];
  244. tup_dedup_([A,B|T]) when element(1, A) =:= element(1, B) ->
  245. tup_dedup_([A | T]);
  246. tup_dedup_([A,B|T]) when element(1, A) =:= B ->
  247. tup_dedup_([A | T]);
  248. tup_dedup_([A,B|T]) when A =:= element(1, B) ->
  249. tup_dedup_([A | T]);
  250. tup_dedup_([A,A|T]) ->
  251. [A|tup_dedup_(T)];
  252. tup_dedup_([A|T]) ->
  253. [A|tup_dedup_(T)].
  254. %% Sort the list in proplist-order, meaning that `{a,b}' and `{a,c}'
  255. %% both compare as usual, and `a' and `b' do the same, but `a' and `{a,b}' will
  256. %% compare based on the first element of the key, and in order. So the following
  257. %% list will sort as:
  258. %% - `[native, {native,o3}, check]' -> `[check, native, {native, o3}]'
  259. %% - `[native, {native,o3}, {native, o2}, check]' -> `[check,native,{native,o3},{native,o2}]'
  260. %% Meaning that:
  261. %% a) no deduplication takes place
  262. %% b) the key of a tuple is what counts in being sorted, but atoms are seen as {atom}
  263. %% as far as comparison is concerned (departing from lists:ukeysort/2)
  264. %% c) order is preserved for similar keys and tuples no matter their size (sort is stable)
  265. %%
  266. %% These properties let us merge proplists fairly easily.
  267. tup_sort(List) ->
  268. lists:sort(fun(A, B) when is_tuple(A), is_tuple(B) -> element(1, A) =< element(1, B)
  269. ; (A, B) when is_tuple(A) -> element(1, A) =< B
  270. ; (A, B) when is_tuple(B) -> A =< element(1, B)
  271. ; (A, B) -> A =< B
  272. end, List).
  273. %% Custom merge functions. The objective is to behave like lists:umerge/2,
  274. %% except that we also compare the merge elements based on the key if they're a
  275. %% tuple, such that `{key, val1}' is always prioritized over `{key, val0}' if
  276. %% the former is from the 'new' list.
  277. %%
  278. %% This lets us apply proper overrides to list of elements according to profile
  279. %% priority. This function depends on a stable proplist sort.
  280. tup_umerge(NewList, OldList) ->
  281. tup_umerge_(tup_sort(NewList), tup_sort(OldList)).
  282. tup_umerge_([], Olds) ->
  283. Olds;
  284. tup_umerge_([New|News], Olds) ->
  285. tup_umerge_dedup_(umerge(new, News, Olds, [], New), []).
  286. %% removes 100% identical duplicate elements so that
  287. %% `[a,{a,b},a,{a,c},a]' returns `[a,{a,b},{a,c}]'.
  288. %% Operates on a reverted list that gets reversed as part of this pass
  289. tup_umerge_dedup_([], Acc) ->
  290. Acc;
  291. tup_umerge_dedup_([H|T], Acc) ->
  292. case lists:member(H,T) of
  293. true -> tup_umerge_dedup_(T, Acc);
  294. false -> tup_umerge_dedup_(T, [H|Acc])
  295. end.
  296. tup_find(_Elem, []) ->
  297. false;
  298. tup_find(Elem, [Elem | _Elems]) ->
  299. Elem;
  300. tup_find(Elem, [Elem1 | Elems]) when is_tuple(Elem1) ->
  301. case element(1, Elem1) =:= Elem of
  302. true ->
  303. Elem1;
  304. false ->
  305. tup_find(Elem, Elems)
  306. end;
  307. tup_find(Elem, [_Elem | Elems]) ->
  308. tup_find(Elem, Elems).
  309. -spec umerge(new|old, News, Olds, Acc, Current) -> Merged when
  310. News :: [term()],
  311. Olds :: [term()],
  312. Acc :: [term()],
  313. Current :: term(),
  314. Merged :: [term()].
  315. umerge(_, [], [], Acc, Current) ->
  316. [Current | Acc];
  317. umerge(new, News, [], Acc, Current) ->
  318. %% only news left
  319. lists:reverse(News, [Current|Acc]);
  320. umerge(old, [], Olds, Acc, Current) ->
  321. %% only olds left
  322. lists:reverse(Olds, [Current|Acc]);
  323. umerge(new, News, [Old|Olds], Acc, Current) ->
  324. {Dir, Merged, NewCurrent} = compare({new, Current}, {old, Old}),
  325. umerge(Dir, News, Olds, [Merged|Acc], NewCurrent);
  326. umerge(old, [New|News], Olds, Acc, Current) ->
  327. {Dir, Merged, NewCurrent} = compare({new, New}, {old, Current}),
  328. umerge(Dir, News, Olds, [Merged|Acc], NewCurrent).
  329. -spec compare({Priority, term()}, {Secondary, term()}) ->
  330. {NextPriority, Merged, Larger} when
  331. Priority :: new | old,
  332. Secondary :: new | old,
  333. NextPriority :: new | old,
  334. Merged :: term(),
  335. Larger :: term().
  336. compare({Priority, A}, {Secondary, B}) when is_tuple(A), is_tuple(B) ->
  337. KA = element(1,A),
  338. KB = element(1,B),
  339. if KA == KB -> {Secondary, A, B};
  340. KA < KB -> {Secondary, A, B};
  341. KA > KB -> {Priority, B, A}
  342. end;
  343. compare({Priority, A}, {Secondary, B}) when not is_tuple(A), not is_tuple(B) ->
  344. if A == B -> {Secondary, A, B};
  345. A < B -> {Secondary, A, B};
  346. A > B -> {Priority, B, A}
  347. end;
  348. compare({Priority, A}, {Secondary, B}) when is_tuple(A), not is_tuple(B) ->
  349. KA = element(1,A),
  350. if KA == B -> {Secondary, A, B};
  351. KA < B -> {Secondary, A, B};
  352. KA > B -> {Priority, B, A}
  353. end;
  354. compare({Priority, A}, {Secondary, B}) when not is_tuple(A), is_tuple(B) ->
  355. KB = element(1,B),
  356. if A == KB -> {Secondary, A, B};
  357. A < KB -> {Secondary, A, B};
  358. A > KB -> {Priority, B, A}
  359. end.
  360. %% Implements wc -l functionality used to determine patchcount from git output
  361. line_count(PatchLines) ->
  362. Tokenized = rebar_string:lexemes(PatchLines, "\n"),
  363. {ok, length(Tokenized)}.
  364. check_min_otp_version(undefined) ->
  365. ok;
  366. check_min_otp_version(MinOtpVersion) ->
  367. %% Fully-qualify with ?MODULE so the function can be meck'd in rebar_utils_SUITE
  368. OtpRelease = ?MODULE:otp_release(),
  369. ParsedMin = version_tuple(MinOtpVersion),
  370. ParsedVsn = version_tuple(OtpRelease),
  371. case ParsedVsn >= ParsedMin of
  372. true ->
  373. ?DEBUG("~ts satisfies the requirement for minimum OTP version ~ts",
  374. [OtpRelease, MinOtpVersion]);
  375. false ->
  376. ?ABORT("OTP release ~ts or later is required. Version in use: ~ts",
  377. [MinOtpVersion, OtpRelease])
  378. end.
  379. check_blacklisted_otp_versions(undefined) ->
  380. ok;
  381. check_blacklisted_otp_versions(BlacklistedRegexes) ->
  382. %% Fully-qualify with ?MODULE so the function can be meck'd in rebar_utils_SUITE
  383. OtpRelease = ?MODULE:otp_release(),
  384. lists:foreach(
  385. fun(BlacklistedRegex) -> abort_if_blacklisted(BlacklistedRegex, OtpRelease) end,
  386. BlacklistedRegexes).
  387. abort_if_blacklisted(BlacklistedRegex, OtpRelease) ->
  388. case re:run(OtpRelease, BlacklistedRegex, [{capture, none}]) of
  389. match ->
  390. ?ABORT("OTP release ~ts matches blacklisted version ~ts",
  391. [OtpRelease, BlacklistedRegex]);
  392. nomatch ->
  393. ?DEBUG("~ts does not match blacklisted OTP version ~ts",
  394. [OtpRelease, BlacklistedRegex])
  395. end.
  396. user_agent() ->
  397. {ok, Vsn} = application:get_key(rebar, vsn),
  398. ?FMT("Rebar/~ts (OTP/~ts)", [Vsn, otp_release()]).
  399. reread_config(ConfigList) ->
  400. %% Default to not re-configuring the logger for now;
  401. %% this can leak logs in CT redirection when setting up hooks
  402. %% for example. If we want to turn it on by default, we may
  403. %% want to disable it in CT at the same time or figure out a
  404. %% way to silence it.
  405. %% The same pattern may apply to other tasks, so let's enable
  406. %% case-by-case.
  407. reread_config(ConfigList, []).
  408. reread_config(ConfigList, Opts) ->
  409. UpdateLoggerConfig = erlang:function_exported(logger, module_info, 0) andalso
  410. proplists:get_value(update_logger, Opts, false),
  411. %% NB: we attempt to mimic -config here, which survives app reload,
  412. %% hence {persistent, true}.
  413. SetEnv = case version_tuple(?MODULE:otp_release()) of
  414. {X, _, _} when X < 17 ->
  415. fun application:set_env/3;
  416. _ ->
  417. fun (App, Key, Val) -> application:set_env(App, Key, Val, [{persistent, true}]) end
  418. end,
  419. try
  420. Res =
  421. [SetEnv(Application, Key, Val)
  422. || Config <- ConfigList,
  423. {Application, Items} <- Config,
  424. {Key, Val} <- Items],
  425. case UpdateLoggerConfig of
  426. true -> reread_logger_config();
  427. false -> ok
  428. end,
  429. Res
  430. catch _:_ ->
  431. ?ERROR("The configuration file submitted could not be read "
  432. "and will be ignored.", [])
  433. end.
  434. %% @private since the kernel app is already booted, re-reading its config
  435. %% requires doing some magic to dynamically patch running handlers to
  436. %% deal with the current value.
  437. reread_logger_config() ->
  438. KernelCfg = application:get_all_env(kernel),
  439. LogCfg = proplists:get_value(logger, KernelCfg),
  440. case LogCfg of
  441. undefined ->
  442. ok;
  443. _ ->
  444. %% Extract and apply settings related to primary configuration
  445. %% -- primary config is used for settings shared across handlers
  446. LogLvlPrimary = proplists:get_value(logger_level, KernelCfg, all),
  447. {FilterDefault, Filters} =
  448. case lists:keyfind(filters, 1, KernelCfg) of
  449. false -> {log, []};
  450. {filters, FoundDef, FoundFilter} -> {FoundDef, FoundFilter}
  451. end,
  452. Primary = #{level => LogLvlPrimary,
  453. filter_default => FilterDefault,
  454. filters => Filters},
  455. lists:foreach(fun maybe_reset_logger_handler/1, LogCfg),
  456. logger:set_primary_config(Primary),
  457. ok
  458. end.
  459. %% @private add or update handlers based on their individual config,
  460. %% also remove default handler if needed.
  461. maybe_reset_logger_handler({handler, Id, Mod, Cfg}) ->
  462. case logger:add_handler(Id, Mod, Cfg) of
  463. {error, {already_exist, Id}} ->
  464. logger:update_handler_config(Id, Cfg);
  465. _ ->
  466. ok
  467. end;
  468. maybe_reset_logger_handler({handler, default, undefined}) ->
  469. _ = logger:remove_handler(default),
  470. ok;
  471. maybe_reset_logger_handler(_) ->
  472. ok.
  473. %% @doc Given env. variable `FOO' we want to expand all references to
  474. %% it in `InStr'. References can have two forms: `$FOO' and `${FOO}'
  475. %% The end of form `$FOO' is delimited with whitespace or EOL
  476. -spec expand_env_variable(string(), string(), term()) -> string().
  477. expand_env_variable(InStr, VarName, RawVarValue) ->
  478. case rebar_string:chr(InStr, $$) of
  479. 0 ->
  480. %% No variables to expand
  481. InStr;
  482. _ ->
  483. ReOpts = [global, unicode, {return, list}],
  484. VarValue = re:replace(RawVarValue, "\\\\", "\\\\\\\\", ReOpts),
  485. %% Use a regex to match/replace:
  486. %% Given variable "FOO": match $FOO\s | $FOOeol | ${FOO}
  487. RegEx = io_lib:format("\\\$(~ts(\\W|$)|{~ts})", [VarName, VarName]),
  488. re:replace(InStr, RegEx, [VarValue, "\\2"], ReOpts)
  489. end.
  490. %% ====================================================================
  491. %% Internal functions
  492. %% ====================================================================
  493. version_tuple(OtpRelease) ->
  494. case re:run(OtpRelease, "R?(\\d+)B?.?-?(\\d+)?.?-?(\\d+)?", [{capture, all, list}]) of
  495. {match, [_Full, Maj, Min, Patch]} ->
  496. {list_to_integer(Maj), list_to_integer(Min), list_to_integer(Patch)};
  497. {match, [_Full, Maj, Min]} ->
  498. {list_to_integer(Maj), list_to_integer(Min), 0};
  499. {match, [_Full, Maj]} ->
  500. {list_to_integer(Maj), 0, 0};
  501. nomatch ->
  502. ?ABORT("Minimum OTP release unable to be parsed: ~ts", [OtpRelease])
  503. end.
  504. otp_release() ->
  505. otp_release1(erlang:system_info(otp_release)).
  506. %% If OTP <= R16, otp_release is already what we want.
  507. otp_release1([$R,N|_]=Rel) when is_integer(N) ->
  508. Rel;
  509. %% If OTP >= 17.x, erlang:system_info(otp_release) returns just the
  510. %% major version number, we have to read the full version from
  511. %% a file. See http://www.erlang.org/doc/system_principles/versions.html
  512. %% Read vsn string from the 'OTP_VERSION' file and return as list without
  513. %% the "\n".
  514. otp_release1(Rel) ->
  515. File = filename:join([code:root_dir(), "releases", Rel, "OTP_VERSION"]),
  516. case file:read_file(File) of
  517. {error, _} ->
  518. Rel;
  519. {ok, Vsn} ->
  520. %% It's fine to rely on the binary module here because we can
  521. %% be sure that it's available when the otp_release string does
  522. %% not begin with $R.
  523. %% The shortest vsn string consists of at least two digits
  524. %% followed by "\n". Therefore, it's safe to assume Size >= 3.
  525. case binary:match(Vsn, <<"**">>) of
  526. {Pos, _} ->
  527. %% The OTP documentation mentions that a system patched
  528. %% using the otp_patch_apply tool available to licensed
  529. %% customers will leave a '**' suffix in the version as a
  530. %% flag saying the system consists of application versions
  531. %% from multiple OTP versions. We ignore this flag and
  532. %% drop the suffix, given for all intents and purposes, we
  533. %% cannot obtain relevant information from it as far as
  534. %% tooling is concerned.
  535. binary:bin_to_list(Vsn, {0, Pos});
  536. nomatch ->
  537. rebar_string:trim(binary:bin_to_list(Vsn), trailing, "\n")
  538. end
  539. end.
  540. %% We do the shell variable substitution ourselves on Windows and hope that the
  541. %% command doesn't use any other shell magic.
  542. patch_on_windows(Cmd, Env) ->
  543. case os:type() of
  544. {win32,nt} ->
  545. Cmd1 = "cmd /q /c "
  546. ++ lists:foldl(fun({Key, Value}, Acc) ->
  547. expand_env_variable(Acc, Key, Value)
  548. end, Cmd, Env),
  549. %% Remove left-over vars
  550. re:replace(Cmd1, "\\\$\\w+|\\\${\\w+}", "",
  551. [global, {return, list}, unicode]);
  552. _ ->
  553. Cmd
  554. end.
  555. expand_sh_flag(return_on_error) ->
  556. {error_handler,
  557. fun(_Command, Err) ->
  558. {error, Err}
  559. end};
  560. expand_sh_flag(abort_on_error) ->
  561. {error_handler,
  562. fun log_and_abort/2};
  563. expand_sh_flag({abort_on_error, Message}) ->
  564. {error_handler,
  565. log_msg_and_abort(Message)};
  566. expand_sh_flag({debug_abort_on_error, Message}) ->
  567. {error_handler,
  568. debug_log_msg_and_abort(Message)};
  569. expand_sh_flag(debug_and_abort_on_error) ->
  570. {error_handler,
  571. fun debug_and_abort/2};
  572. expand_sh_flag(use_stdout) ->
  573. {output_handler,
  574. fun(Line, Acc) ->
  575. %% Line already has a newline so don't use ?CONSOLE which adds one
  576. io:format("~ts", [Line]),
  577. [Line | Acc]
  578. end};
  579. expand_sh_flag({use_stdout, false}) ->
  580. {output_handler,
  581. fun(Line, Acc) ->
  582. [Line | Acc]
  583. end};
  584. expand_sh_flag({cd, _CdArg} = Cd) ->
  585. {port_settings, Cd};
  586. expand_sh_flag({env, _EnvArg} = Env) ->
  587. {port_settings, Env}.
  588. -type err_handler() :: fun((string(), {integer(), string()}) -> no_return()).
  589. -spec log_msg_and_abort(string()) -> err_handler().
  590. log_msg_and_abort(Message) ->
  591. fun(_Command, {_Rc, _Output}) ->
  592. ?ABORT(Message, [])
  593. end.
  594. -spec debug_log_msg_and_abort(string()) -> err_handler().
  595. debug_log_msg_and_abort(Message) ->
  596. fun(Command, {Rc, Output}) ->
  597. ?DEBUG("sh(~ts)~n"
  598. "failed with return code ~w and the following output:~n"
  599. "~ts", [Command, Rc, Output]),
  600. ?ABORT(Message, [])
  601. end.
  602. -spec log_and_abort(string(), {integer(), string()}) -> no_return().
  603. log_and_abort(Command, {Rc, Output}) ->
  604. ?ABORT("sh(~ts)~n"
  605. "failed with return code ~w and the following output:~n"
  606. "~ts", [Command, Rc, Output]).
  607. -spec debug_and_abort(string(), {integer(), string()}) -> no_return().
  608. debug_and_abort(Command, {Rc, Output}) ->
  609. ?DEBUG("sh(~ts)~n"
  610. "failed with return code ~w and the following output:~n"
  611. "~ts", [Command, Rc, Output]),
  612. throw(rebar_abort).
  613. sh_loop(Port, Fun, Acc) ->
  614. receive
  615. {Port, {data, {eol, Line}}} ->
  616. sh_loop(Port, Fun, Fun(unicode:characters_to_list(Line) ++ "\n", Acc));
  617. {Port, {data, {noeol, Line}}} ->
  618. sh_loop(Port, Fun, Fun(unicode:characters_to_list(Line), Acc));
  619. {Port, eof} ->
  620. Data = lists:flatten(lists:reverse(Acc)),
  621. receive
  622. {Port, {exit_status, 0}} ->
  623. {ok, Data};
  624. {Port, {exit_status, Rc}} ->
  625. {error, {Rc, Data}}
  626. end
  627. end.
  628. beam_to_mod(Filename) ->
  629. list_to_atom(filename:basename(Filename, ".beam")).
  630. erl_to_mod(Filename) ->
  631. list_to_atom(filename:rootname(filename:basename(Filename))).
  632. beams(Dir) ->
  633. filelib:wildcard(filename:join(Dir, "*.beam")).
  634. -spec abort() -> no_return().
  635. abort() ->
  636. throw(rebar_abort).
  637. -spec abort(string(), [term()]) -> no_return().
  638. abort(String, Args) ->
  639. ?ERROR(String, Args),
  640. abort().
  641. escript_foldl(Fun, Acc, File) ->
  642. case escript:extract(File, [compile_source]) of
  643. {ok, [_Shebang, _Comment, _EmuArgs, Body]} ->
  644. case Body of
  645. {source, BeamCode} ->
  646. GetInfo = fun() -> file:read_file_info(File) end,
  647. GetBin = fun() -> BeamCode end,
  648. {ok, Fun(".", GetInfo, GetBin, Acc)};
  649. {beam, BeamCode} ->
  650. GetInfo = fun() -> file:read_file_info(File) end,
  651. GetBin = fun() -> BeamCode end,
  652. {ok, Fun(".", GetInfo, GetBin, Acc)};
  653. {archive, ArchiveBin} ->
  654. zip:foldl(Fun, Acc, {File, ArchiveBin})
  655. end;
  656. {error, _} = Error ->
  657. Error
  658. end.
  659. %% TODO: this is just for rebar3_hex and maybe other plugins
  660. %% but eventually it should be dropped
  661. vcs_vsn(OriginalVsn, Dir, Resources) when is_list(Dir) ,
  662. is_list(Resources) ->
  663. ?WARN("Using deprecated rebar_utils:vcs_vsn/3. Please upgrade your plugins.", []),
  664. FakeState = rebar_state:new(),
  665. {ok, AppInfo} = rebar_app_info:new(fake, OriginalVsn, Dir),
  666. vcs_vsn(AppInfo, OriginalVsn,
  667. rebar_state:set_resources(FakeState, Resources));
  668. vcs_vsn(AppInfo, Vcs, State) ->
  669. case vcs_vsn_cmd(AppInfo, Vcs, State) of
  670. {plain, VsnString} ->
  671. VsnString;
  672. {cmd, CmdString} ->
  673. cmd_vsn_invoke(CmdString, rebar_app_info:dir(AppInfo));
  674. unknown ->
  675. ?ABORT("vcs_vsn: Unknown vsn format: ~p", [Vcs]);
  676. {error, Reason} ->
  677. ?ABORT("vcs_vsn: ~ts", [Reason])
  678. end.
  679. %% Temp work around for repos like relx that use "semver"
  680. vcs_vsn_cmd(_, Vsn, _) when is_binary(Vsn) ->
  681. {plain, Vsn};
  682. vcs_vsn_cmd(AppInfo, VCS, State) when VCS =:= semver ; VCS =:= "semver" ->
  683. vcs_vsn_cmd(AppInfo, git, State);
  684. vcs_vsn_cmd(_AppInfo, {cmd, _Cmd}=Custom, _) ->
  685. Custom;
  686. vcs_vsn_cmd(AppInfo, {file, File}, _) ->
  687. Path = filename:join(rebar_app_info:dir(AppInfo), File),
  688. {ok, Vsn} = file:read_file(Path),
  689. {plain, to_list(rebar_string:trim(Vsn))};
  690. vcs_vsn_cmd(AppInfo, VCS, State) when is_atom(VCS) ->
  691. rebar_resource_v2:make_vsn(AppInfo, VCS, State);
  692. vcs_vsn_cmd(AppInfo, {VCS, _}=V, State) when is_atom(VCS) ->
  693. rebar_resource_v2:make_vsn(AppInfo, V, State);
  694. vcs_vsn_cmd(AppInfo, VCS, State) when is_list(VCS) ->
  695. try list_to_existing_atom(VCS) of
  696. AVCS ->
  697. case vcs_vsn_cmd(AppInfo, AVCS, State) of
  698. unknown -> {plain, VCS};
  699. Other -> Other
  700. end
  701. catch
  702. error:badarg ->
  703. {plain, VCS}
  704. end;
  705. vcs_vsn_cmd(_, _, _) ->
  706. unknown.
  707. cmd_vsn_invoke(Cmd, Dir) ->
  708. {ok, VsnString} = rebar_utils:sh(Cmd, [{cd, Dir}, {use_stdout, false}]),
  709. rebar_string:trim(VsnString, trailing, "\n").
  710. %% @doc ident to the level specified
  711. -spec indent(non_neg_integer()) -> iolist().
  712. indent(Amount) when erlang:is_integer(Amount) ->
  713. [?ONE_LEVEL_INDENT || _ <- lists:seq(1, Amount)].
  714. %% Replace code paths with new paths for existing apps and
  715. %% purge code of the old modules from those apps.
  716. update_code(Paths) -> update_code(Paths, []).
  717. update_code(Paths, Opts) ->
  718. lists:foreach(fun(Path) ->
  719. Name = filename:basename(Path, "/ebin"),
  720. App = list_to_atom(Name),
  721. application:load(App),
  722. case application:get_key(App, modules) of
  723. undefined ->
  724. code:add_patha(Path),
  725. ok;
  726. {ok, Modules} ->
  727. %% replace_path causes problems when running
  728. %% tests in projects like erlware_commons that rebar3
  729. %% also includes
  730. %code:replace_path(App, Path),
  731. code:del_path(App),
  732. code:add_patha(Path),
  733. case lists:member(soft_purge, Opts) of
  734. true ->
  735. [begin code:soft_purge(M), code:delete(M) end || M <- Modules];
  736. false ->
  737. [begin code:purge(M), code:delete(M) end || M <- Modules]
  738. end
  739. end
  740. end, Paths).
  741. remove_from_code_path(Paths) ->
  742. lists:foreach(fun(Path) ->
  743. Name = filename:basename(Path, "/ebin"),
  744. App = list_to_atom(Name),
  745. application:load(App),
  746. case application:get_key(App, modules) of
  747. undefined ->
  748. application:unload(App),
  749. ok;
  750. {ok, Modules} ->
  751. application:unload(App),
  752. [case erlang:check_process_code(self(), M) of
  753. false ->
  754. code:purge(M), code:delete(M);
  755. _ ->
  756. ?DEBUG("~p can't purge ~p safely, doing a soft purge", [self(), M]),
  757. code:soft_purge(M) andalso code:delete(M)
  758. end || M <- Modules]
  759. end,
  760. code:del_path(Path)
  761. end, lists:usort(Paths)).
  762. %% @doc Revert to only having the beams necessary for running rebar3 and
  763. %% plugins in the path
  764. -spec cleanup_code_path([string()]) -> true | {error, term()}.
  765. cleanup_code_path(OrigPath) ->
  766. CurrentPath = code:get_path(),
  767. AddedPaths = CurrentPath -- OrigPath,
  768. %% If someone has removed paths, it's hard to get them back into
  769. %% the right order, but since this is currently rare, we can just
  770. %% fall back to code:set_path/1.
  771. case CurrentPath -- AddedPaths of
  772. OrigPath ->
  773. _ = [code:del_path(Path) || Path <- AddedPaths],
  774. true;
  775. _ ->
  776. code:set_path(OrigPath)
  777. end.
  778. new_task([], Acc) -> lists:reverse(Acc);
  779. new_task([TaskList|Rest], Acc) ->
  780. case re:split(TaskList, ",", [{return, list}, {parts, 2}, unicode]) of
  781. %% `do` consumes all remaining args
  782. ["do" = Task] ->
  783. lists:reverse([{Task, Rest}|Acc]);
  784. %% single task terminated by a comma
  785. [Task, ""] -> new_task(Rest, [{Task, []}|Acc]);
  786. %% sequence of two or more tasks
  787. [Task, More] -> new_task([More|Rest], [{Task, []}|Acc]);
  788. %% single task not terminated by a comma
  789. [Task] -> arg_or_flag(Rest, [{Task, []}|Acc])
  790. end.
  791. arg_or_flag([], [{Task, Args}|Acc]) ->
  792. lists:reverse([{Task, lists:reverse(Args)}|Acc]);
  793. %% case where you have `foo , bar`
  794. arg_or_flag([","|Rest], Acc) -> new_task(Rest, Acc);
  795. %% case where you have `foo ,bar`
  796. arg_or_flag(["," ++ Task|Rest], Acc) -> new_task([Task|Rest], Acc);
  797. %% a flag
  798. arg_or_flag(["-" ++ _ = Flag|Rest], [{Task, Args}|Acc]) ->
  799. case maybe_ends_in_comma(Flag) of
  800. false -> arg_or_flag(Rest, [{Task, [Flag|Args]}|Acc]);
  801. NewFlag -> new_task(Rest, [{Task,
  802. lists:reverse([NewFlag|Args])}|Acc])
  803. end;
  804. %% an argument or a sequence of arguments
  805. arg_or_flag([ArgList|Rest], [{Task, Args}|Acc]) ->
  806. case re:split(ArgList, ",", [{return, list}, {parts, 2}, unicode]) of
  807. %% single arg terminated by a comma
  808. [Arg, ""] -> new_task(Rest, [{Task,
  809. lists:reverse([Arg|Args])}|Acc]);
  810. %% sequence of two or more args/tasks
  811. [Arg, More] -> new_task([More|Rest], [{Task,
  812. lists:reverse([Arg|Args])}|Acc]);
  813. %% single arg not terminated by a comma
  814. [Arg] -> arg_or_flag(Rest, [{Task, [Arg|Args]}|Acc])
  815. end.
  816. maybe_ends_in_comma(H) ->
  817. case lists:reverse(H) of
  818. "," ++ Flag -> lists:reverse(Flag);
  819. _ -> false
  820. end.
  821. get_http_vars(Scheme) ->
  822. OS = case os:getenv(atom_to_list(Scheme)) of
  823. Str when is_list(Str) -> Str;
  824. _ -> []
  825. end,
  826. GlobalConfigFile = rebar_dir:global_config(),
  827. Config = rebar_config:consult_file(GlobalConfigFile),
  828. proplists:get_value(Scheme, Config, OS).
  829. -ifdef (OTP_RELEASE).
  830. -if(?OTP_RELEASE >= 23).
  831. -compile({nowarn_deprecated_function, [{http_uri, parse, 1},
  832. {http_uri, decode, 1}]}).
  833. -endif.
  834. -endif.
  835. set_httpc_options() ->
  836. set_httpc_options(https_proxy, get_http_vars(https_proxy)),
  837. set_httpc_options(proxy, get_http_vars(http_proxy)).
  838. set_httpc_options(_, []) ->
  839. ok;
  840. set_httpc_options(Scheme, Proxy) ->
  841. URI = normalise_proxy(Scheme, Proxy),
  842. {ok, {_, UserInfo, Host, Port, _, _}} = http_uri:parse(URI),
  843. httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar),
  844. proxy_ipfamily(Host, inet:gethostbyname(Host)),
  845. set_proxy_auth(UserInfo).
  846. proxy_ipfamily(_Host, {ok, _}) ->
  847. ok;
  848. proxy_ipfamily(Host, {error, nxdomain}) ->
  849. maybe_proxy_family(Host, inet_db:res_option(inet6)).
  850. maybe_proxy_family(Host, true) ->
  851. maybe_set_ipfamily(inet:gethostbyname(Host, inet), inet);
  852. maybe_proxy_family(Host, false) ->
  853. maybe_set_ipfamily(inet:gethostbyname(Host, inet6), inet6).
  854. maybe_set_ipfamily({ok, _}, Family) ->
  855. httpc:set_options([{ipfamily, Family}], rebar);
  856. maybe_set_ipfamily(_, _Family) ->
  857. ok.
  858. normalise_proxy(Scheme, URI) ->
  859. case re:run(URI, "://", [unicode]) of
  860. nomatch when Scheme =:= https_proxy -> "https://" ++ URI;
  861. nomatch when Scheme =:= proxy -> "http://" ++ URI;
  862. _ -> URI
  863. end.
  864. url_append_path(Url, ExtraPath) ->
  865. case http_uri:parse(Url) of
  866. {ok, {Scheme, UserInfo, Host, Port, Path, Query}} ->
  867. {ok, lists:append([atom_to_list(Scheme), "://", UserInfo, Host, ":", integer_to_list(Port),
  868. filename:join(Path, ExtraPath), Query])};
  869. _ ->
  870. error
  871. end.
  872. %% escape\ as\ a\ shell\?
  873. escape_chars(Str) when is_atom(Str) ->
  874. escape_chars(atom_to_list(Str));
  875. escape_chars(Str) ->
  876. re:replace(Str, "([ ()?`!$&;\"\'])", "\\\\&",
  877. [global, {return, list}, unicode]).
  878. %% "escape inside these"
  879. escape_double_quotes(Str) ->
  880. re:replace(Str, "([\"\\\\`!$&*;])", "\\\\&",
  881. [global, {return, list}, unicode]).
  882. %% "escape inside these" but allow *
  883. escape_double_quotes_weak(Str) ->
  884. re:replace(Str, "([\"\\\\`!$&;])", "\\\\&",
  885. [global, {return, list}, unicode]).
  886. info_useless(Old, New) ->
  887. [?INFO("App ~ts is no longer needed and can be deleted.", [Name])
  888. || Name <- Old,
  889. not lists:member(Name, New)],
  890. ok.
  891. list_dir(Dir) ->
  892. %% `list_dir_all` returns raw files which are unsupported
  893. %% prior to R16 so just fall back to `list_dir` if running
  894. %% on an earlier vm
  895. case erlang:function_exported(file, list_dir_all, 1) of
  896. true -> file:list_dir_all(Dir);
  897. false -> file:list_dir(Dir)
  898. end.
  899. set_proxy_auth([]) ->
  900. ok;
  901. set_proxy_auth(UserInfo) ->
  902. [Username, Password] = re:split(UserInfo, ":",
  903. [{return, list}, {parts,2}, unicode]),
  904. %% password may contain url encoded characters, need to decode them first
  905. application:set_env(rebar, proxy_auth, [{proxy_auth, {Username, http_uri:decode(Password)}}]).
  906. get_proxy_auth() ->
  907. case application:get_env(rebar, proxy_auth) of
  908. undefined -> [];
  909. {ok, ProxyAuth} -> ProxyAuth
  910. end.
  911. -spec is_list_of_strings(term()) -> boolean().
  912. is_list_of_strings(List) when not is_list(hd(List)) ->
  913. false;
  914. is_list_of_strings(List) when is_list(hd(List)) ->
  915. true;
  916. is_list_of_strings(List) when is_list(List) ->
  917. true.
  918. %%------------------------------------------------------------------------------
  919. %% @doc
  920. %% Return the SSL options adequate for the project based on
  921. %% its configuration, including for validation of certs.
  922. %% @end
  923. %%------------------------------------------------------------------------------
  924. -spec ssl_opts(Url) -> Res when
  925. Url :: string(),
  926. Res :: proplists:proplist().
  927. ssl_opts(Url) ->
  928. case get_ssl_config() of
  929. ssl_verify_enabled ->
  930. ssl_opts(ssl_verify_enabled, Url);
  931. ssl_verify_disabled ->
  932. [{verify, verify_none}]
  933. end.
  934. %%------------------------------------------------------------------------------
  935. %% @doc
  936. %% Return the SSL options adequate for the project based on
  937. %% its configuration, including for validation of certs.
  938. %% @end
  939. %%------------------------------------------------------------------------------
  940. -spec ssl_opts(Enabled, Url) -> Res when
  941. Enabled :: atom(),
  942. Url :: string(),
  943. Res :: proplists:proplist().
  944. ssl_opts(ssl_verify_enabled, Url) ->
  945. case check_ssl_version() of
  946. true ->
  947. {ok, {_, _, Hostname, _, _, _}} =
  948. http_uri:parse(rebar_utils:to_list(Url)),
  949. VerifyFun = {fun ssl_verify_hostname:verify_fun/3,
  950. [{check_hostname, Hostname}]},
  951. CACerts = certifi:cacerts(),
  952. [{verify, verify_peer}, {depth, 2}, {cacerts, CACerts},
  953. {partial_chain, fun partial_chain/1}, {verify_fun, VerifyFun}];
  954. false ->
  955. ?WARN("Insecure HTTPS request (peer verification disabled), "
  956. "please update to OTP 17.4 or later", []),
  957. [{verify, verify_none}]
  958. end.
  959. -spec partial_chain(Certs) -> Res when
  960. Certs :: list(any()),
  961. Res :: unknown_ca | {trusted_ca, any()}.
  962. partial_chain(Certs) ->
  963. Certs1 = [{Cert, public_key:pkix_decode_cert(Cert, otp)} || Cert <- Certs],
  964. CACerts = certifi:cacerts(),
  965. CACerts1 = [public_key:pkix_decode_cert(Cert, otp) || Cert <- CACerts],
  966. case ec_lists:find(fun({_, Cert}) ->
  967. check_cert(CACerts1, Cert)
  968. end, Certs1) of
  969. {ok, Trusted} ->
  970. {trusted_ca, element(1, Trusted)};
  971. _ ->
  972. unknown_ca
  973. end.
  974. -spec extract_public_key_info(Cert) -> Res when
  975. Cert :: #'OTPCertificate'{tbsCertificate::#'OTPTBSCertificate'{}},
  976. Res :: any().
  977. extract_public_key_info(Cert) ->
  978. ((Cert#'OTPCertificate'.tbsCertificate)#'OTPTBSCertificate'.subjectPublicKeyInfo).
  979. -spec check_cert(CACerts, Cert) -> Res when
  980. CACerts :: list(any()),
  981. Cert :: any(),
  982. Res :: boolean().
  983. check_cert(CACerts, Cert) ->
  984. lists:any(fun(CACert) ->
  985. extract_public_key_info(CACert) == extract_public_key_info(Cert)
  986. end, CACerts).
  987. -spec check_ssl_version() ->
  988. boolean().
  989. check_ssl_version() ->
  990. case application:get_key(ssl, vsn) of
  991. {ok, Vsn} ->
  992. parse_vsn(Vsn) >= {5, 3, 6};
  993. _ ->
  994. false
  995. end.
  996. -spec get_ssl_config() ->
  997. ssl_verify_disabled | ssl_verify_enabled.
  998. get_ssl_config() ->
  999. GlobalConfigFile = rebar_dir:global_config(),
  1000. Config = rebar_config:consult_file(GlobalConfigFile),
  1001. case proplists:get_value(ssl_verify, Config, []) of
  1002. false ->
  1003. ssl_verify_disabled;
  1004. _ ->
  1005. ssl_verify_enabled
  1006. end.
  1007. -spec parse_vsn(Vsn) -> Res when
  1008. Vsn :: string(),
  1009. Res :: {integer(), integer(), integer()}.
  1010. parse_vsn(Vsn) ->
  1011. version_pad(rebar_string:lexemes(Vsn, ".-")).
  1012. -spec version_pad(list(nonempty_string())) -> Res when
  1013. Res :: {integer(), integer(), integer()}.
  1014. version_pad([Major]) ->
  1015. {list_to_integer(Major), 0, 0};
  1016. version_pad([Major, Minor]) ->
  1017. {list_to_integer(Major), list_to_integer(Minor), 0};
  1018. version_pad([Major, Minor, Patch]) ->
  1019. {list_to_integer(Major), list_to_integer(Minor), list_to_integer(Patch)};
  1020. version_pad([Major, Minor, Patch | _]) ->
  1021. {list_to_integer(Major), list_to_integer(Minor), list_to_integer(Patch)}.
  1022. -ifdef(filelib_find_source).
  1023. find_source(Filename, Dir, Rules) ->
  1024. filelib:find_source(Filename, Dir, Rules).
  1025. -else.
  1026. %% Looks for a file relative to a given directory
  1027. -type find_file_rule() :: {ObjDirSuffix::string(), SrcDirSuffix::string()}.
  1028. %% Looks for a source file relative to the object file name and directory
  1029. -type find_source_rule() :: {ObjExtension::string(), SrcExtension::string(),
  1030. [find_file_rule()]}.
  1031. keep_suffix_search_rules(Rules) ->
  1032. [T || {_,_,_}=T <- Rules].
  1033. -spec find_source(file:filename(), file:filename(), [find_source_rule()]) ->
  1034. {ok, file:filename()} | {error, not_found}.
  1035. find_source(Filename, Dir, Rules) ->
  1036. try_suffix_rules(keep_suffix_search_rules(Rules), Filename, Dir).
  1037. try_suffix_rules(Rules, Filename, Dir) ->
  1038. Ext = filename:extension(Filename),
  1039. try_suffix_rules(Rules, filename:rootname(Filename, Ext), Dir, Ext).
  1040. try_suffix_rules([{Ext,Src,Rules}|Rest], Root, Dir, Ext)
  1041. when is_list(Src), is_list(Rules) ->
  1042. case try_dir_rules(add_local_search(Rules), Root ++ Src, Dir) of
  1043. {ok, File} -> {ok, File};
  1044. _Other ->
  1045. try_suffix_rules(Rest, Root, Dir, Ext)
  1046. end;
  1047. try_suffix_rules([_|Rest], Root, Dir, Ext) ->
  1048. try_suffix_rules(Rest, Root, Dir, Ext);
  1049. try_suffix_rules([], _Root, _Dir, _Ext) ->
  1050. {error, not_found}.
  1051. %% ensuring we check the directory of the object file before any other directory
  1052. add_local_search(Rules) ->
  1053. Local = {"",""},
  1054. [Local] ++ lists:filter(fun (X) -> X =/= Local end, Rules).
  1055. try_dir_rules([{From, To}|Rest], Filename, Dir)
  1056. when is_list(From), is_list(To) ->
  1057. case try_dir_rule(Dir, Filename, From, To) of
  1058. {ok, File} -> {ok, File};
  1059. error -> try_dir_rules(Rest, Filename, Dir)
  1060. end;
  1061. try_dir_rules([], _Filename, _Dir) ->
  1062. {error, not_found}.
  1063. try_dir_rule(Dir, Filename, From, To) ->
  1064. case lists:suffix(From, Dir) of
  1065. true ->
  1066. NewDir = lists:sublist(Dir, 1, length(Dir)-length(From))++To,
  1067. Src = filename:join(NewDir, Filename),
  1068. case filelib:is_regular(Src) of
  1069. true -> {ok, Src};
  1070. false -> find_regular_file(filelib:wildcard(Src))
  1071. end;
  1072. false ->
  1073. error
  1074. end.
  1075. find_regular_file([]) ->
  1076. error;
  1077. find_regular_file([File|Files]) ->
  1078. case filelib:is_regular(File) of
  1079. true -> {ok, File};
  1080. false -> find_regular_file(Files)
  1081. end.
  1082. -endif.