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

325 рядки
12 KiB

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