Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

376 строки
13 KiB

10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
9 лет назад
9 лет назад
9 лет назад
10 лет назад
9 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
10 лет назад
  1. #!/usr/bin/env escript
  2. %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
  3. %% ex: ft=erlang ts=4 sw=4 et
  4. main(_Args) ->
  5. application:start(crypto),
  6. application:start(asn1),
  7. application:start(public_key),
  8. application:start(ssl),
  9. inets:start(),
  10. inets:start(httpc, [{profile, rebar}]),
  11. set_httpc_options(),
  12. %% Fetch and build deps required to build rebar3
  13. BaseDeps = [{providers, []}
  14. ,{getopt, []}
  15. ,{erlware_commons, ["ec_dictionary.erl", "ec_vsn.erl"]}],
  16. Deps = get_deps(),
  17. [fetch_and_compile(Dep, Deps) || Dep <- BaseDeps],
  18. %% Build rebar3 modules with compile:file
  19. bootstrap_rebar3(),
  20. %% Build rebar.app from rebar.app.src
  21. {ok, App} = rebar_app_info:new(rebar, "3.0.0", filename:absname("_build/default/lib/rebar/")),
  22. rebar_otp_app:compile(rebar_state:new(), App),
  23. %% Because we are compiling files that are loaded already we want to silence
  24. %% not_purged errors in rebar_erlc_compiler:opts_changed/1
  25. error_logger:tty(false),
  26. setup_env(),
  27. os:putenv("REBAR_PROFILE", "bootstrap"),
  28. rebar3:run(["update"]),
  29. {ok, State} = rebar3:run(["compile"]),
  30. reset_env(),
  31. os:putenv("REBAR_PROFILE", ""),
  32. DepsPaths = rebar_state:code_paths(State, all_deps),
  33. code:add_pathsa(DepsPaths),
  34. rebar3:run(["clean", "-a"]),
  35. rebar3:run(["escriptize"]),
  36. %% Done with compile, can turn back on error logger
  37. error_logger:tty(true),
  38. %% Finally, update executable perms for our script on *nix,
  39. %% or write out script files on win32.
  40. ec_file:copy("_build/default/bin/rebar3", "./rebar3"),
  41. case os:type() of
  42. {unix,_} ->
  43. [] = os:cmd("chmod u+x rebar3"),
  44. ok;
  45. {win32,_} ->
  46. write_windows_scripts(),
  47. ok;
  48. _ ->
  49. ok
  50. end.
  51. fetch_and_compile({Name, ErlFirstFiles}, Deps) ->
  52. case lists:keyfind(Name, 1, Deps) of
  53. {Name, Vsn} ->
  54. ok = fetch({pkg, atom_to_binary(Name, utf8), list_to_binary(Vsn)}, Name);
  55. {Name, _, Source} ->
  56. ok = fetch(Source, Name)
  57. end,
  58. compile(Name, ErlFirstFiles).
  59. fetch({pkg, Name, Vsn}, App) ->
  60. Dir = filename:join([filename:absname("_build/default/lib/"), App]),
  61. CDN = "https://s3.amazonaws.com/s3.hex.pm/tarballs",
  62. Package = binary_to_list(<<Name/binary, "-", Vsn/binary, ".tar">>),
  63. Url = string:join([CDN, Package], "/"),
  64. case request(Url) of
  65. {ok, Binary} ->
  66. {ok, Contents} = extract(Binary),
  67. ok = erl_tar:extract({binary, Contents}, [{cwd, Dir}, compressed]);
  68. _ ->
  69. io:format("Error: Unable to fetch package ~p ~p~n", [Name, Vsn])
  70. end.
  71. extract(Binary) ->
  72. {ok, Files} = erl_tar:extract({binary, Binary}, [memory]),
  73. {"contents.tar.gz", Contents} = lists:keyfind("contents.tar.gz", 1, Files),
  74. {ok, Contents}.
  75. request(Url) ->
  76. case httpc:request(get, {Url, []},
  77. [{relaxed, true}],
  78. [{body_format, binary}],
  79. rebar) of
  80. {ok, {{_Version, 200, _Reason}, _Headers, Body}} ->
  81. {ok, Body};
  82. Error ->
  83. Error
  84. end.
  85. get_rebar_config() ->
  86. {ok, [[Home]]} = init:get_argument(home),
  87. ConfDir = filename:join(Home, ".config/rebar3"),
  88. case file:consult(filename:join(ConfDir, "rebar.config")) of
  89. {ok, Config} ->
  90. Config;
  91. _ ->
  92. []
  93. end.
  94. get_http_vars(Scheme) ->
  95. proplists:get_value(Scheme, get_rebar_config(), []).
  96. set_httpc_options() ->
  97. set_httpc_options(https_proxy, get_http_vars(https_proxy)),
  98. set_httpc_options(proxy, get_http_vars(http_proxy)).
  99. set_httpc_options(_, []) ->
  100. ok;
  101. set_httpc_options(Scheme, Proxy) ->
  102. {ok, {_, _, Host, Port, _, _}} = http_uri:parse(Proxy),
  103. httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar).
  104. compile(App, FirstFiles) ->
  105. Dir = filename:join(filename:absname("_build/default/lib/"), App),
  106. filelib:ensure_dir(filename:join([Dir, "ebin", "dummy.beam"])),
  107. code:add_path(filename:join(Dir, "ebin")),
  108. FirstFilesPaths = [filename:join([Dir, "src", Module]) || Module <- FirstFiles],
  109. Sources = FirstFilesPaths ++ filelib:wildcard(filename:join([Dir, "src", "*.erl"])),
  110. [compile_file(X, [{i, filename:join(Dir, "include")}
  111. ,debug_info
  112. ,{outdir, filename:join(Dir, "ebin")}
  113. ,return | additional_defines()]) || X <- Sources].
  114. compile_file(File, Opts) ->
  115. case compile:file(File, Opts) of
  116. {ok, _Mod} ->
  117. ok;
  118. {ok, _Mod, []} ->
  119. ok;
  120. {ok, _Mod, Ws} ->
  121. io:format("~s~n", [format_warnings(File, Ws)]),
  122. halt(1);
  123. {error, Es, Ws} ->
  124. io:format("~s ~s~n", [format_errors(File, Es), format_warnings(File, Ws)]),
  125. halt(1)
  126. end.
  127. bootstrap_rebar3() ->
  128. filelib:ensure_dir("_build/default/lib/rebar/ebin/dummy.beam"),
  129. code:add_path("_build/default/lib/rebar/ebin/"),
  130. ok = symlink_or_copy(filename:absname("src"),
  131. filename:absname("_build/default/lib/rebar/src")),
  132. Sources = ["src/rebar_resource.erl" | filelib:wildcard("src/*.erl")],
  133. [compile_file(X, [{outdir, "_build/default/lib/rebar/ebin/"}
  134. ,return | additional_defines()]) || X <- Sources],
  135. code:add_patha(filename:absname("_build/default/lib/rebar/ebin")).
  136. %%rebar.hrl
  137. -define(FMT(Str, Args), lists:flatten(io_lib:format(Str, Args))).
  138. %%/rebar.hrl
  139. %%rebar_file_utils
  140. symlink_or_copy(Source, Target) ->
  141. Link = case os:type() of
  142. {win32, _} ->
  143. Source;
  144. _ ->
  145. make_relative_path(Source, Target)
  146. end,
  147. case file:make_symlink(Link, Target) of
  148. ok ->
  149. ok;
  150. {error, eexist} ->
  151. ok;
  152. {error, _} ->
  153. cp_r([Source], Target)
  154. end.
  155. make_relative_path(Source, Target) ->
  156. do_make_relative_path(filename:split(Source), filename:split(Target)).
  157. do_make_relative_path([H|T1], [H|T2]) ->
  158. do_make_relative_path(T1, T2);
  159. do_make_relative_path(Source, Target) ->
  160. Base = lists:duplicate(max(length(Target) - 1, 0), ".."),
  161. filename:join(Base ++ Source).
  162. cp_r([], _Dest) ->
  163. ok;
  164. cp_r(Sources, Dest) ->
  165. case os:type() of
  166. {unix, _} ->
  167. EscSources = [escape_path(Src) || Src <- Sources],
  168. SourceStr = string:join(EscSources, " "),
  169. os:cmd(?FMT("cp -R ~s \"~s\"", [SourceStr, Dest])),
  170. ok;
  171. {win32, _} ->
  172. lists:foreach(fun(Src) -> ok = cp_r_win32(Src,Dest) end, Sources),
  173. ok
  174. end.
  175. xcopy_win32(Source,Dest)->
  176. R = os:cmd(?FMT("xcopy \"~s\" \"~s\" /q /y /e 2> nul",
  177. [filename:nativename(Source), filename:nativename(Dest)])),
  178. case length(R) > 0 of
  179. %% when xcopy fails, stdout is empty and and error message is printed
  180. %% to stderr (which is redirected to nul)
  181. true -> ok;
  182. false ->
  183. {error, lists:flatten(
  184. io_lib:format("Failed to xcopy from ~s to ~s~n",
  185. [Source, Dest]))}
  186. end.
  187. cp_r_win32({true, SourceDir}, {true, DestDir}) ->
  188. %% from directory to directory
  189. SourceBase = filename:basename(SourceDir),
  190. ok = case file:make_dir(filename:join(DestDir, SourceBase)) of
  191. {error, eexist} -> ok;
  192. Other -> Other
  193. end,
  194. ok = xcopy_win32(SourceDir, filename:join(DestDir, SourceBase));
  195. cp_r_win32({false, Source} = S,{true, DestDir}) ->
  196. %% from file to directory
  197. cp_r_win32(S, {false, filename:join(DestDir, filename:basename(Source))});
  198. cp_r_win32({false, Source},{false, Dest}) ->
  199. %% from file to file
  200. {ok,_} = file:copy(Source, Dest),
  201. ok;
  202. cp_r_win32({true, SourceDir}, {false, DestDir}) ->
  203. case filelib:is_regular(DestDir) of
  204. true ->
  205. %% From directory to file? This shouldn't happen
  206. {error, lists:flatten(
  207. io_lib:format("Cannot copy dir (~p) to file (~p)\n",
  208. [SourceDir, DestDir]))};
  209. false ->
  210. %% Specifying a target directory that doesn't currently exist.
  211. %% So let's attempt to create this directory
  212. case filelib:ensure_dir(filename:join(DestDir, "dummy")) of
  213. ok ->
  214. ok = xcopy_win32(SourceDir, DestDir);
  215. {error, Reason} ->
  216. {error, lists:flatten(
  217. io_lib:format("Unable to create dir ~p: ~p\n",
  218. [DestDir, Reason]))}
  219. end
  220. end;
  221. cp_r_win32(Source,Dest) ->
  222. Dst = {filelib:is_dir(Dest), Dest},
  223. lists:foreach(fun(Src) ->
  224. ok = cp_r_win32({filelib:is_dir(Src), Src}, Dst)
  225. end, filelib:wildcard(Source)),
  226. ok.
  227. escape_path(Str) ->
  228. re:replace(Str, "([ ()?])", "\\\\&", [global, {return, list}]).
  229. %%/rebar_file_utils
  230. setup_env() ->
  231. %% We don't need or want relx providers loaded yet
  232. application:load(rebar),
  233. {ok, Providers} = application:get_env(rebar, providers),
  234. Providers1 = Providers -- [rebar_prv_release,
  235. rebar_prv_relup,
  236. rebar_prv_tar],
  237. application:set_env(rebar, providers, Providers1).
  238. reset_env() ->
  239. %% Reset the env so we get all providers
  240. application:unset_env(rebar, providers),
  241. application:unload(rebar),
  242. application:load(rebar).
  243. write_windows_scripts() ->
  244. CmdScript=
  245. "@echo off\r\n"
  246. "setlocal\r\n"
  247. "set rebarscript=%~f0\r\n"
  248. "escript.exe \"%rebarscript:.cmd=%\" %*\r\n",
  249. ok = file:write_file("rebar3.cmd", CmdScript).
  250. get_deps() ->
  251. case file:consult("rebar.lock") of
  252. {ok, [[]]} ->
  253. %% Something went wrong in a previous build, lock file shouldn't be empty
  254. io:format("Empty list in lock file, deleting rebar.lock~n"),
  255. ok = file:delete("rebar.lock"),
  256. {ok, Config} = file:consult("rebar.config"),
  257. proplists:get_value(deps, Config);
  258. {ok, [Deps]} ->
  259. [{binary_to_atom(Name, utf8), "", Source} || {Name, Source, _Level} <- Deps];
  260. _ ->
  261. {ok, Config} = file:consult("rebar.config"),
  262. proplists:get_value(deps, Config)
  263. end.
  264. format_errors(Source, Errors) ->
  265. format_errors(Source, "", Errors).
  266. format_warnings(Source, Warnings) ->
  267. format_warnings(Source, Warnings, []).
  268. format_warnings(Source, Warnings, Opts) ->
  269. Prefix = case lists:member(warnings_as_errors, Opts) of
  270. true -> "";
  271. false -> "Warning: "
  272. end,
  273. format_errors(Source, Prefix, Warnings).
  274. format_errors(_MainSource, Extra, Errors) ->
  275. [begin
  276. [format_error(Source, Extra, Desc) || Desc <- Descs]
  277. end
  278. || {Source, Descs} <- Errors].
  279. format_error(AbsSource, Extra, {{Line, Column}, Mod, Desc}) ->
  280. ErrorDesc = Mod:format_error(Desc),
  281. io_lib:format("~s:~w:~w: ~s~s~n", [AbsSource, Line, Column, Extra, ErrorDesc]);
  282. format_error(AbsSource, Extra, {Line, Mod, Desc}) ->
  283. ErrorDesc = Mod:format_error(Desc),
  284. io_lib:format("~s:~w: ~s~s~n", [AbsSource, Line, Extra, ErrorDesc]);
  285. format_error(AbsSource, Extra, {Mod, Desc}) ->
  286. ErrorDesc = Mod:format_error(Desc),
  287. io_lib:format("~s: ~s~s~n", [AbsSource, Extra, ErrorDesc]).
  288. additional_defines() ->
  289. [{d, D} || {Re, D} <- [{"^[0-9]+", namespaced_types}, {"^R1[4|5]", deprecated_crypto}], is_otp_release(Re)].
  290. is_otp_release(ArchRegex) ->
  291. case re:run(otp_release(), ArchRegex, [{capture, none}]) of
  292. match ->
  293. true;
  294. nomatch ->
  295. false
  296. end.
  297. otp_release() ->
  298. otp_release1(erlang:system_info(otp_release)).
  299. %% If OTP <= R16, otp_release is already what we want.
  300. otp_release1([$R,N|_]=Rel) when is_integer(N) ->
  301. Rel;
  302. %% If OTP >= 17.x, erlang:system_info(otp_release) returns just the
  303. %% major version number, we have to read the full version from
  304. %% a file. See http://www.erlang.org/doc/system_principles/versions.html
  305. %% Read vsn string from the 'OTP_VERSION' file and return as list without
  306. %% the "\n".
  307. otp_release1(Rel) ->
  308. File = filename:join([code:root_dir(), "releases", Rel, "OTP_VERSION"]),
  309. case file:read_file(File) of
  310. {error, _} ->
  311. Rel;
  312. {ok, Vsn} ->
  313. %% It's fine to rely on the binary module here because we can
  314. %% be sure that it's available when the otp_release string does
  315. %% not begin with $R.
  316. Size = byte_size(Vsn),
  317. %% The shortest vsn string consists of at least two digits
  318. %% followed by "\n". Therefore, it's safe to assume Size >= 3.
  319. case binary:part(Vsn, {Size, -3}) of
  320. <<"**\n">> ->
  321. %% The OTP documentation mentions that a system patched
  322. %% using the otp_patch_apply tool available to licensed
  323. %% customers will leave a '**' suffix in the version as a
  324. %% flag saying the system consists of application versions
  325. %% from multiple OTP versions. We ignore this flag and
  326. %% drop the suffix, given for all intents and purposes, we
  327. %% cannot obtain relevant information from it as far as
  328. %% tooling is concerned.
  329. binary:bin_to_list(Vsn, {0, Size - 3});
  330. _ ->
  331. binary:bin_to_list(Vsn, {0, Size - 1})
  332. end
  333. end.