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

114 行
4.9 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) 2011 Trifork
  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_prv_shell).
  28. -author("Kresten Krab Thorup <krab@trifork.com>").
  29. -behaviour(provider).
  30. -export([init/1,
  31. do/1]).
  32. -include("rebar.hrl").
  33. -define(PROVIDER, shell).
  34. -define(DEPS, [compile]).
  35. %% ===================================================================
  36. %% Public API
  37. %% ===================================================================
  38. -spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
  39. init(State) ->
  40. State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
  41. {module, ?MODULE},
  42. {bare, false},
  43. {deps, ?DEPS},
  44. {example, "rebar shell"},
  45. {short_desc, "Run shell with project apps and deps in path."},
  46. {desc, info()},
  47. {opts, []}])),
  48. {ok, State1}.
  49. -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
  50. do(Config) ->
  51. shell(),
  52. {ok, Config}.
  53. %% NOTE:
  54. %% this is an attempt to replicate `erl -pa ./ebin -pa deps/*/ebin`. it is
  55. %% mostly successful but does stop and then restart the user io system to get
  56. %% around issues with rebar being an escript and starting in `noshell` mode.
  57. %% it also lacks the ctrl-c interrupt handler that `erl` features. ctrl-c will
  58. %% immediately kill the script. ctrl-g, however, works fine
  59. shell() ->
  60. true = code:add_pathz(rebar_utils:ebin_dir()),
  61. %% scan all processes for any with references to the old user and save them to
  62. %% update later
  63. NeedsUpdate = [Pid || Pid <- erlang:processes(),
  64. proplists:get_value(group_leader, erlang:process_info(Pid)) == whereis(user)
  65. ],
  66. %% terminate the current user
  67. ok = supervisor:terminate_child(kernel_sup, user),
  68. %% start a new shell (this also starts a new user under the correct group)
  69. _ = user_drv:start(),
  70. %% wait until user_drv and user have been registered (max 3 seconds)
  71. ok = wait_until_user_started(3000),
  72. %% set any process that had a reference to the old user's group leader to the
  73. %% new user process
  74. _ = [erlang:group_leader(whereis(user), Pid) || Pid <- NeedsUpdate],
  75. %% enable error_logger's tty output
  76. ok = error_logger:swap_handler(tty),
  77. %% disable the simple error_logger (which may have been added multiple
  78. %% times). removes at most the error_logger added by init and the
  79. %% error_logger added by the tty handler
  80. ok = remove_error_handler(3),
  81. %% this call never returns (until user quits shell)
  82. timer:sleep(infinity).
  83. info() ->
  84. "Start a shell with project and deps preloaded similar to~n'erl -pa ebin -pa deps/*/ebin'.~n".
  85. remove_error_handler(0) ->
  86. ?WARN("Unable to remove simple error_logger handler~n", []);
  87. remove_error_handler(N) ->
  88. case gen_event:delete_handler(error_logger, error_logger, []) of
  89. {error, module_not_found} -> ok;
  90. {error_logger, _} -> remove_error_handler(N-1)
  91. end.
  92. %% Timeout is a period to wait before giving up
  93. wait_until_user_started(0) ->
  94. ?ABORT("Timeout exceeded waiting for `user` to register itself~n", []),
  95. erlang:error(timeout);
  96. wait_until_user_started(Timeout) ->
  97. case whereis(user) of
  98. %% if user is not yet registered wait a tenth of a second and try again
  99. undefined -> timer:sleep(100), wait_until_user_started(Timeout - 100);
  100. _ -> ok
  101. end.