您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

296 行
11 KiB

  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 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(rebar3).
  28. -export([main/1,
  29. run/1,
  30. run/2,
  31. global_option_spec_list/0,
  32. init_config/0,
  33. set_options/2,
  34. parse_args/1,
  35. version/0,
  36. log_level/0]).
  37. -include("rebar.hrl").
  38. %% ====================================================================
  39. %% Public API
  40. %% ====================================================================
  41. %% escript Entry point
  42. -spec main(list()) -> no_return().
  43. main(Args) ->
  44. try run(Args) of
  45. {ok, _State} ->
  46. erlang:halt(0);
  47. Error ->
  48. handle_error(Error)
  49. catch
  50. _:Error ->
  51. handle_error(Error)
  52. end.
  53. %% Erlang-API entry point
  54. run(BaseState, Commands) ->
  55. start_and_load_apps(),
  56. BaseState1 = rebar_state:set(BaseState, task, Commands),
  57. BaseState2 = rebar_state:set(BaseState1, caller, api),
  58. Verbosity = log_level(),
  59. ok = rebar_log:init(api, Verbosity),
  60. run_aux(BaseState2, Commands).
  61. %% ====================================================================
  62. %% Internal functions
  63. %% ====================================================================
  64. run(RawArgs) ->
  65. start_and_load_apps(),
  66. BaseState = init_config(),
  67. BaseState1 = rebar_state:set(BaseState, caller, command_line),
  68. case erlang:system_info(version) of
  69. "6.1" ->
  70. ?WARN("Due to a filelib bug in Erlang 17.1 it is recommended"
  71. "you update to a newer release.", []);
  72. _ ->
  73. ok
  74. end,
  75. {BaseState2, _Args1} = set_options(BaseState1, {[], []}),
  76. run_aux(BaseState2, RawArgs).
  77. run_aux(State, RawArgs) ->
  78. State2 = case os:getenv("REBAR_PROFILE") of
  79. false ->
  80. State;
  81. "" ->
  82. State;
  83. Profile ->
  84. rebar_state:apply_profiles(State, [list_to_atom(Profile)])
  85. end,
  86. %% Process each command, resetting any state between each one
  87. BaseDir = rebar_state:get(State, base_dir, ?DEFAULT_BASE_DIR),
  88. State3 = rebar_state:set(State2, base_dir,
  89. filename:join(filename:absname(rebar_state:dir(State2)), BaseDir)),
  90. {ok, Providers} = application:get_env(rebar, providers),
  91. %% Providers can modify profiles stored in opts, so set default after initializing providers
  92. State4 = rebar_state:create_logic_providers(Providers, State3),
  93. rebar_packages:packages(State4),
  94. State5 = rebar_plugins:project_apps_install(State4),
  95. State6 = rebar_state:default(State5, rebar_state:opts(State5)),
  96. {Task, Args} = parse_args(RawArgs),
  97. State7 = rebar_state:code_paths(State6, default, code:get_path()),
  98. rebar_core:init_command(rebar_state:command_args(State7, Args), Task).
  99. init_config() ->
  100. %% Initialize logging system
  101. Verbosity = log_level(),
  102. ok = rebar_log:init(command_line, Verbosity),
  103. Config = case os:getenv("REBAR_CONFIG") of
  104. false ->
  105. rebar_config:consult_file(?DEFAULT_CONFIG_FILE);
  106. ConfigFile ->
  107. rebar_config:consult_file(ConfigFile)
  108. end,
  109. Config1 = rebar_config:merge_locks(Config, rebar_config:consult_lock_file(?LOCK_FILE)),
  110. %% If $HOME/.config/rebar3/rebar.config exists load and use as global config
  111. GlobalConfigFile = rebar_dir:global_config(),
  112. State = case filelib:is_regular(GlobalConfigFile) of
  113. true ->
  114. ?DEBUG("Load global config file ~s", [GlobalConfigFile]),
  115. try state_from_global_config(Config1, GlobalConfigFile)
  116. catch
  117. _:_ ->
  118. ?WARN("Global config ~s exists but can not be read. Ignoring global config values.", [GlobalConfigFile]),
  119. rebar_state:new(Config1)
  120. end;
  121. false ->
  122. rebar_state:new(Config1)
  123. end,
  124. %% Determine the location of the rebar executable; important for pulling
  125. %% resources out of the escript
  126. State1 = try
  127. ScriptName = filename:absname(escript:script_name()),
  128. rebar_state:escript_path(State, ScriptName)
  129. catch
  130. _:_ ->
  131. State
  132. end,
  133. %% TODO: Do we need this still? I think it may still be used.
  134. %% Initialize vsn cache
  135. rebar_state:set(State1, vsn_cache, dict:new()).
  136. parse_args([]) ->
  137. parse_args(["help"]);
  138. parse_args([H | Rest]) when H =:= "-h"
  139. ; H =:= "--help" ->
  140. parse_args(["help" | Rest]);
  141. parse_args([H | Rest]) when H =:= "-v"
  142. ; H =:= "--version" ->
  143. parse_args(["version" | Rest]);
  144. parse_args([Task | RawRest]) ->
  145. {list_to_atom(Task), RawRest}.
  146. set_options(State, {Options, NonOptArgs}) ->
  147. GlobalDefines = proplists:get_all_values(defines, Options),
  148. State1 = rebar_state:set(State, defines, GlobalDefines),
  149. %% Set global variables based on getopt options
  150. State2 = set_global_flag(State1, Options, force),
  151. Task = proplists:get_value(task, Options, "help"),
  152. {rebar_state:set(State2, task, Task), NonOptArgs}.
  153. %%
  154. %% get log level based on getopt option
  155. %%
  156. log_level() ->
  157. case os:getenv("QUIET") of
  158. false ->
  159. DefaultLevel = rebar_log:default_level(),
  160. case os:getenv("DEBUG") of
  161. false ->
  162. DefaultLevel;
  163. _ ->
  164. DefaultLevel + 3
  165. end;
  166. _ ->
  167. rebar_log:error_level()
  168. end.
  169. %%
  170. %% show version information and halt
  171. %%
  172. version() ->
  173. {ok, Vsn} = application:get_key(rebar, vsn),
  174. ?CONSOLE("rebar ~s on Erlang/OTP ~s Erts ~s",
  175. [Vsn, erlang:system_info(otp_release), erlang:system_info(version)]).
  176. %% TODO: Actually make it 'global'
  177. %%
  178. %% set global flag based on getopt option boolean value
  179. %%
  180. set_global_flag(State, Options, Flag) ->
  181. Value = case proplists:get_bool(Flag, Options) of
  182. true ->
  183. "1";
  184. false ->
  185. "0"
  186. end,
  187. rebar_state:set(State, Flag, Value).
  188. %%
  189. %% options accepted via getopt
  190. %%
  191. global_option_spec_list() ->
  192. [
  193. %% {Name, ShortOpt, LongOpt, ArgSpec, HelpMsg}
  194. {help, $h, "help", undefined, "Print this help."},
  195. {version, $v, "version", undefined, "Show version information."},
  196. %{config, $C, "config", string, "Rebar config file to use."},
  197. {task, undefined, undefined, string, "Task to run."}
  198. ].
  199. handle_error(rebar_abort) ->
  200. erlang:halt(1);
  201. handle_error({error, rebar_abort}) ->
  202. erlang:halt(1);
  203. handle_error({error, {Module, Reason}}) ->
  204. case code:which(Module) of
  205. non_existing ->
  206. ?ERROR("Uncaught error in rebar_core. Run with DEBUG=1 to see stacktrace", []),
  207. ?DEBUG("Uncaught error: ~p ~p", [Module, Reason]),
  208. ?INFO("When submitting a bug report, please include the output of `rebar3 report \"your command\"`", []);
  209. _ ->
  210. ?ERROR(Module:format_error(Reason), [])
  211. end,
  212. erlang:halt(1);
  213. handle_error({error, Error}) when is_list(Error) ->
  214. ?ERROR(Error, []),
  215. erlang:halt(1);
  216. handle_error(Error) ->
  217. %% Nothing should percolate up from rebar_core;
  218. %% Dump this error to console
  219. ?ERROR("Uncaught error in rebar_core. Run with DEBUG=1 to see stacktrace", []),
  220. ?DEBUG("Uncaught error: ~p", [Error]),
  221. case erlang:get_stacktrace() of
  222. [] -> ok;
  223. Trace ->
  224. ?DEBUG("Stack trace to the error location: ~p", [Trace])
  225. end,
  226. ?INFO("When submitting a bug report, please include the output of `rebar3 report \"your command\"`", []),
  227. erlang:halt(1).
  228. start_and_load_apps() ->
  229. _ = application:load(rebar),
  230. %% Make sure crypto is running
  231. case crypto:start() of
  232. ok -> ok;
  233. {error,{already_started,crypto}} -> ok
  234. end,
  235. application:start(asn1),
  236. application:start(public_key),
  237. application:start(ssl),
  238. inets:start(),
  239. inets:start(httpc, [{profile, rebar}]).
  240. state_from_global_config(Config, GlobalConfigFile) ->
  241. rebar_utils:set_httpc_options(),
  242. GlobalConfigTerms = rebar_config:consult_file(GlobalConfigFile),
  243. GlobalConfig = rebar_state:new(GlobalConfigTerms),
  244. %% We don't want to worry about global plugin install state effecting later
  245. %% usage. So we throw away the global profile state used for plugin install.
  246. GlobalConfigThrowAway = rebar_state:current_profiles(GlobalConfig, [global]),
  247. GlobalState = case rebar_state:get(GlobalConfigThrowAway, plugins, []) of
  248. [] ->
  249. GlobalConfigThrowAway;
  250. GlobalPluginsToInstall ->
  251. rebar_packages:packages(GlobalConfigThrowAway),
  252. rebar_plugins:handle_plugins(global,
  253. GlobalPluginsToInstall,
  254. GlobalConfigThrowAway)
  255. end,
  256. GlobalPlugins = rebar_state:providers(GlobalState),
  257. GlobalConfig2 = rebar_state:set(GlobalConfig, plugins, []),
  258. GlobalConfig3 = rebar_state:set(GlobalConfig2, {plugins, global}, rebar_state:get(GlobalConfigThrowAway, plugins, [])),
  259. rebar_state:providers(rebar_state:new(GlobalConfig3, Config), GlobalPlugins).