Skip to content

Commit a061528

Browse files
author
José Valim
committed
Ensure IEx runs only after Elixir's CLI process its options
1 parent 9e030a6 commit a061528

File tree

12 files changed

+159
-197
lines changed

12 files changed

+159
-197
lines changed

bin/mix

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ readlink_f () {
1111

1212
SELF=$(readlink_f "$0")
1313
SCRIPT_PATH=$(dirname "$SELF")
14-
ERL_OPTS=""
14+
EXECUTABLE="elixir"
1515

16-
if [ "$1" = "iex" ]; then ERL_OPTS="-user Elixir-IEx-CLI -iex wait true"; fi
17-
exec "$SCRIPT_PATH"/elixir --erl "$ERL_OPTS" -e "Mix.start; Mix.CLI.run" -- "$@"
16+
if [ "$1" = "iex" ]; then EXECUTABLE="iex"; fi
17+
exec "$SCRIPT_PATH"/$EXECUTABLE -e "Mix.start; Mix.CLI.run" -- "$@"

lib/elixir/lib/kernel/cli.ex

Lines changed: 38 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,45 @@
1-
defrecord Kernel.CLI.Config, commands: [], output: ".",
2-
compile: [], halt: true, compiler_options: []
3-
41
defmodule Kernel.CLI do
5-
@moduledoc false
6-
7-
@doc """
8-
Invoked directly from erlang boot process. It parses all argv
9-
options and execute them in the order they are specified.
2+
@moduledoc """
3+
Module responsible for controlling Elixir's CLI
104
"""
11-
def process_argv(options) do
12-
{ config, argv } = process_options(options, Kernel.CLI.Config.new)
5+
6+
defrecord Config, commands: [], output: ".",
7+
compile: [], halt: true, compiler_options: []
8+
9+
# This is the API invoked by Elixir boot process.
10+
@doc false
11+
def main(options) do
12+
{ config, argv } = process_argv(options, Kernel.CLI.Config.new)
1313

1414
argv = lc arg inlist argv, do: list_to_binary(arg)
1515
:gen_server.call(:elixir_code_server, { :argv, argv })
1616

17-
all_commands = Enum.reverse(config.commands)
17+
run fn ->
18+
Enum.map Enum.reverse(config.commands), process_command(&1, config)
19+
:gen_server.cast(:elixir_code_server, :finished)
20+
end, config.halt
21+
end
1822

19-
run fn -> Enum.map all_commands, process_command(&1, config) end, config.halt
23+
@doc """
24+
Wait until the CLI finishes procesing options.
25+
"""
26+
def wait_until_finished do
27+
case :gen_server.call(:elixir_code_server, { :wait_until_finished, self }) do
28+
:wait ->
29+
receive do
30+
{ :elixir_code_server, :finished } -> :ok
31+
end
32+
:ok -> :ok
33+
end
2034
end
2135

2236
@doc """
23-
Runs the given function and wraps any exception or
24-
error in messages useful for the command line.
37+
Runs the given function by catching any failure
38+
and printing them to stdout. `at_exit` hooks are
39+
also invoked before exiting.
40+
41+
This function is used by Elixir's CLI and also
42+
by escripts generated by Elixir.
2543
"""
2644
def run(fun, halt // true) do
2745
try do
@@ -141,15 +159,15 @@ defmodule Kernel.CLI do
141159

142160
# Process init options
143161

144-
def process_options(['--'|t], config) do
162+
defp process_argv(['--'|t], config) do
145163
{ config, t }
146164
end
147165

148-
def process_options(['--compile'|t], config) do
166+
defp process_argv(['--compile'|t], config) do
149167
process_compiler t, config
150168
end
151169

152-
def process_options(['-S',h|t], config) do
170+
defp process_argv(['-S',h|t], config) do
153171
exec = System.find_executable(h)
154172
if exec do
155173
{ config.prepend_commands([require: list_to_binary(exec)]), t }
@@ -159,17 +177,17 @@ defmodule Kernel.CLI do
159177
end
160178
end
161179

162-
def process_options([h|t] = list, config) do
180+
defp process_argv([h|t] = list, config) do
163181
case h do
164182
'-' ++ _ ->
165-
shared_option? list, config, process_options(&1, &2)
183+
shared_option? list, config, process_argv(&1, &2)
166184
_ ->
167185
h = list_to_binary(h)
168186
{ config.prepend_commands([require: h]), t }
169187
end
170188
end
171189

172-
def process_options([], config) do
190+
defp process_argv([], config) do
173191
{ config, [] }
174192
end
175193

lib/elixir/src/elixir.erl

Lines changed: 11 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
-module(elixir).
22
-behaviour(application).
3-
-export([main/1, start_cli/0, start_app/0,
3+
-export([main/1, start_cli/0,
44
scope_for_eval/1, eval/2, eval/3, eval/4,
55
eval_quoted/2, eval_quoted/3, eval_quoted/4,
66
eval_forms/3]).
@@ -16,6 +16,12 @@
1616
-export([start/2, stop/1, config_change/3]).
1717

1818
start(_Type, _Args) ->
19+
%% Set the shell to unicode so printing inside scripts work
20+
%% Those can take a while, so let's do it in a new process
21+
spawn(fun() ->
22+
io:setopts(standard_io, [{encoding,unicode}]),
23+
io:setopts(standard_error, [{encoding,unicode}])
24+
end),
1925
elixir_sup:start_link([]).
2026

2127
stop(_S) ->
@@ -27,29 +33,14 @@ config_change(_Changed, _New, _Remove) ->
2733
%% escript entry point
2834

2935
main(Args) ->
30-
start_app(),
31-
'Elixir.Kernel.CLI':process_argv(Args).
32-
33-
%% ELIXIR ENTRY POINTS
34-
35-
% Start the Elixir app. This is the proper way to boot Elixir from
36-
% inside an Erlang process.
37-
38-
start_app() ->
39-
case lists:keyfind(?MODULE, 1, application:loaded_applications()) of
40-
false ->
41-
application:start(?MODULE),
42-
%% Set the shell to unicode so printing inside scripts work
43-
io:setopts(standard_io, [{encoding,unicode}]),
44-
io:setopts(standard_error, [{encoding,unicode}]);
45-
_ -> ok
46-
end.
36+
application:start(?MODULE),
37+
'Elixir.Kernel.CLI':main(Args).
4738

4839
% Boot and process given options. Invoked by Elixir's script.
4940

5041
start_cli() ->
51-
start_app(),
52-
'Elixir.Kernel.CLI':process_argv(init:get_plain_arguments()).
42+
application:start(?MODULE),
43+
'Elixir.Kernel.CLI':main(init:get_plain_arguments()).
5344

5445
%% EVAL HOOKS
5546

lib/elixir/src/elixir_code_server.erl

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
at_exit=[],
99
pool=[],
1010
counter=0,
11-
compiler_options=[{docs,true},{debug_info,true}]
11+
compiler_options=[{docs,true},{debug_info,true}],
12+
waiting=[]
1213
}).
1314

1415
start_link() ->
@@ -17,6 +18,13 @@ start_link() ->
1718
init(_args) ->
1819
{ ok, #elixir_code_server{} }.
1920

21+
handle_call({ wait_until_finished, Pid }, _, Config) ->
22+
Waiting = Config#elixir_code_server.waiting,
23+
case is_list(Waiting) of
24+
true -> { reply, wait, Config#elixir_code_server{waiting=[Pid|Waiting]} };
25+
false -> { reply, ok, Config }
26+
end;
27+
2028
handle_call({ acquire, Path }, From, Config) ->
2129
Current = Config#elixir_code_server.loaded,
2230
case orddict:find(Path, Current) of
@@ -67,6 +75,11 @@ handle_call(retrieve_module_name, _From, Config) ->
6775
handle_call(_Request, _From, Config) ->
6876
{ reply, undef, Config }.
6977

78+
handle_cast(finished, Config) ->
79+
Waiting = Config#elixir_code_server.waiting,
80+
[Pid ! { elixir_code_server, finished } || Pid <- lists:reverse(Waiting)],
81+
{ noreply, Config#elixir_code_server{waiting=done} };
82+
7083
handle_cast({ loaded, Path }, Config) ->
7184
Current = Config#elixir_code_server.loaded,
7285
case orddict:find(Path, Current) of

lib/elixir/src/elixir_compiler.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ module(Forms, File, Options, Bootstrap, Callback) when
114114
%% Invoked from the Makefile.
115115

116116
core() ->
117-
elixir:start_app(),
117+
application:start(elixir),
118118
gen_server:call(elixir_code_server, { compiler_options, [{docs,false},{internal,true}] }),
119119
[core_file(File) || File <- core_main()].
120120

lib/elixir/test/elixir/kernel/cli_test.exs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,6 @@ defmodule Kernel.CLI.OptionParsingTest do
2929
# pz
3030
assert File.expand_path('lib/list', root) inlist path
3131
end
32-
33-
test :require do
34-
options = ['-r', fixture_path('../../../lib/list/*') /> to_char_list, '-r', '/never/gonna/*/up']
35-
{ config, _argv } = Kernel.CLI.process_options(options, Kernel.CLI.Config.new)
36-
assert {:require, fixture_path "../../../lib/list/chars.ex"} inlist config.commands
37-
end
3832
end
3933

4034
defmodule Kernel.CLI.AtExitTest do

lib/elixir/test/erlang/test_helper.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
-export([test/0, run_and_remove/2, throw_elixir/1, throw_erlang/1]).
44

55
test() ->
6-
elixir:start_app(),
6+
application:start(elixir),
77
eunit:test([
88
atom_test,
99
conditionals_test,

0 commit comments

Comments
 (0)