Skip to content

Commit 19d402b

Browse files
author
José Valim
committed
Disable native compilation when on_load attributes is present
This is a work around an Erlang bug, see #845 for more information. Closes #845.
1 parent cf6751a commit 19d402b

File tree

3 files changed

+52
-6
lines changed

3 files changed

+52
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* [IO] Add `IO.ANSI` to make it easy to write ANSI escape codes
33
* [Kernel] Better support for Unicode lists
44
* [Kernel] Reduce variables footprint in `case`/`receive` clauses
5+
* [Kernel] Disable native compilation when on_load attributes is present to work around an Erlang bug
56
* [Macro] `Macro.expand` also considers macros from the current `__ENV__` module
67
* [Mix] Improve support for compilation of `.erl` files
78
* [Mix] Add support for compilation of `.yrl` and `.xrl` files

lib/elixir/src/elixir_compiler.erl

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
-module(elixir_compiler).
22
-export([get_opts/0, get_opt/1, get_opt/2, string/2, file/1, file_to_path/2]).
3-
-export([core/0, module/3, eval_forms/4]).
3+
-export([core/0, module/3, eval_forms/4, format_error/1]).
44
-include("elixir.hrl").
55
-compile({parse_transform, elixir_transform}).
66

@@ -89,11 +89,17 @@ module(Forms, S, Callback) ->
8989
end,
9090
module(Forms, S#elixir_scope.file, Options, false, Callback).
9191

92-
module(Forms, File, Options, Bootstrap, Callback) when
93-
is_binary(File), is_list(Forms), is_list(Options), is_boolean(Bootstrap), is_function(Callback) ->
92+
module(Forms, File, RawOptions, Bootstrap, Callback) when
93+
is_binary(File), is_list(Forms), is_list(RawOptions), is_boolean(Bootstrap), is_function(Callback) ->
94+
{ Options, SkipNative } = compile_opts(Forms, RawOptions),
9495
Listname = binary_to_list(File),
95-
case compile:forms([no_auto_import()|Forms], [return,{source,Listname}|Options]) of
96-
{ok, ModuleName, Binary, Warnings} ->
96+
97+
case compile:noenv_forms([no_auto_import()|Forms], [return,{source,Listname}|Options]) of
98+
{ok, ModuleName, Binary, RawWarnings} ->
99+
Warnings = case SkipNative of
100+
true -> [{?MODULE,[{0,?MODULE,{skip_native,ModuleName}}]}|RawWarnings];
101+
false -> RawWarnings
102+
end,
97103
format_warnings(Bootstrap, File, Warnings),
98104
code:load_binary(ModuleName, Listname, Binary),
99105
Callback(ModuleName, Binary);
@@ -112,6 +118,38 @@ core() ->
112118

113119
%% HELPERS
114120

121+
compile_opts(Forms, Options) ->
122+
EnvOptions = env_default_opts(),
123+
SkipNative = lists:member(native, EnvOptions) and contains_on_load(Forms),
124+
case SkipNative or lists:member([{native,false}], Options) of
125+
true -> { Options ++ lists:delete(native, EnvOptions), SkipNative };
126+
false -> { Options ++ EnvOptions, SkipNative }
127+
end.
128+
129+
env_default_opts() ->
130+
Key = "ERL_COMPILER_OPTIONS",
131+
case os:getenv(Key) of
132+
false -> [];
133+
Str when is_list(Str) ->
134+
case erl_scan:string(Str) of
135+
{ok,Tokens,_} ->
136+
case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of
137+
{ok,List} when is_list(List) -> List;
138+
{ok,Term} -> [Term];
139+
{error,_Reason} ->
140+
io:format("Ignoring bad term in ~s\n", [Key]),
141+
[]
142+
end;
143+
{error, {_,_,_Reason}, _} ->
144+
io:format("Ignoring bad term in ~s\n", [Key]),
145+
[]
146+
end
147+
end.
148+
149+
contains_on_load([{ attribute, _, on_load, _ }|_]) -> true;
150+
contains_on_load([_|T]) -> contains_on_load(T);
151+
contains_on_load([]) -> false.
152+
115153
no_auto_import() ->
116154
Bifs = [{ Name, Arity } || { Name, Arity } <- erlang:module_info(exports), erl_internal:bif(Name, Arity)],
117155
{ attribute, 0, compile, { no_auto_import, Bifs } }.
@@ -196,8 +234,12 @@ core_main() ->
196234

197235
%% ERROR HANDLING
198236

237+
format_error({ skip_native, Module }) ->
238+
io_lib:format("skipping native compilation for ~s because it contains on_load attribute",
239+
[elixir_errors:inspect(Module)]).
240+
199241
format_errors(_File, []) ->
200-
exit({nocompile, "compilation failed but no error was raised"});
242+
exit({ nocompile, "compilation failed but no error was raised" });
201243

202244
format_errors(File, Errors) ->
203245
lists:foreach(fun ({_, Each}) ->

lib/elixir/src/elixir_errors.erl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,9 @@ raise(Line, File, Kind, Message) when is_integer(Line) ->
172172
Exception = Kind:new([{description, Message}, {file, iolist_to_binary(File)}, {line, Line}]),
173173
erlang:raise(error, Exception, Stacktrace).
174174

175+
file_format(0, File, Message) ->
176+
io_lib:format("~ts: ~ts~n", [File, Message]);
177+
175178
file_format(Line, File, Message) ->
176179
io_lib:format("~ts:~w: ~ts~n", [File, Line, Message]).
177180

0 commit comments

Comments
 (0)