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.

341 lines
12 KiB

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