File 2813-parsetools-Let-Leex-recognize-ERL_COMPILER_OPTIONS.patch of Package erlang
From 31a7aeed186bd6c2113e7d9b5b59d996be4629a3 Mon Sep 17 00:00:00 2001
From: Hans Bolinder <hasse@erlang.org>
Date: Tue, 24 Nov 2020 09:24:24 +0100
Subject: [PATCH 3/3] parsetools: Let Leex recognize ERL_COMPILER_OPTIONS
---
lib/parsetools/doc/src/leex.xml | 16 +++
lib/parsetools/src/leex.erl | 161 +++++++++++++++++------------
lib/parsetools/test/leex_SUITE.erl | 31 +++++-
3 files changed, 142 insertions(+), 66 deletions(-)
diff --git a/lib/parsetools/doc/src/leex.xml b/lib/parsetools/doc/src/leex.xml
index 0e69e44667..153374e16a 100644
--- a/lib/parsetools/doc/src/leex.xml
+++ b/lib/parsetools/doc/src/leex.xml
@@ -241,6 +241,22 @@ io:request(InFile, {get_until,unicode,Prompt,Module,tokens,[Line]})
</func>
</funcs>
+ <section>
+ <title>Default Leex Options</title>
+ <p>The (host operating system) environment variable
+ <c>ERL_COMPILER_OPTIONS</c> can be used to give default Leex
+ options. Its value must be a valid Erlang term. If the value is a
+ list, it is used as is. If it is not a list, it is put
+ into a list.</p>
+
+ <p>The list is appended to any options given to
+ <seemfa marker="#file/2">file/2</seemfa>.</p>
+
+ <p>The list can be retrieved with
+ <seemfa marker="compiler:compile#env_compiler_options/0">
+ compile:env_compiler_options/0</seemfa>.</p>
+ </section>
+
<section>
<title>Input File Format</title>
<p>Erlang style comments starting with a <c>%</c> are allowed in
diff --git a/lib/parsetools/src/leex.erl b/lib/parsetools/src/leex.erl
index f60ad76175..90d1b9f71e 100644
--- a/lib/parsetools/src/leex.erl
+++ b/lib/parsetools/src/leex.erl
@@ -34,9 +34,8 @@
-export([compile/3,file/1,file/2,format_error/1]).
--import(lists, [member/2,reverse/1,sort/1,delete/2,
- keysort/2,keydelete/3,
- map/2,foldl/3,foreach/2,flatmap/2]).
+-import(lists, [member/2,reverse/1,sort/1,keysort/2,
+ map/2,foldl/3,foldr/3,foreach/2,flatmap/2]).
-import(ordsets, [is_element/2,add_element/2,union/2]).
-import(orddict, [store/3]).
@@ -123,12 +122,15 @@ file(File) -> file(File, []).
| 'return_errors' | 'return_warnings' | 'return'
| 'verbose' | 'warnings_as_errors'.
-file(File, Opts0) ->
+file(File, Opts0) when is_list(Opts0) ->
case is_filename(File) of
no -> erlang:error(badarg, [File,Opts0]);
_ -> ok
end,
- Opts = case options(Opts0) of
+ EnvOpts0 = env_default_opts(),
+ EnvOpts = select_recognized_opts(EnvOpts0),
+ Opts1 = Opts0 ++ EnvOpts,
+ Opts = case options(Opts1) of
badarg ->
erlang:error(badarg, [File,Opts0]);
Options ->
@@ -152,7 +154,9 @@ file(File, Opts0) ->
catch #leex{}=St4 ->
St4
end,
- leex_ret(St).
+ leex_ret(St);
+file(File, Opt) ->
+ file(File, [Opt]).
-spec format_error(ErrorDescriptor) -> io_lib:chars() when
ErrorDescriptor :: term().
@@ -196,83 +200,112 @@ strip_extension(File, Ext) ->
_Other -> File
end.
-options(Options0) when is_list(Options0) ->
- try
- Options = flatmap(fun(return) -> short_option(return, true);
- (report) -> short_option(report, true);
- ({return,T}) -> short_option(return, T);
- ({report,T}) -> short_option(report, T);
- (T) -> [T]
- end, Options0),
- options(Options, [scannerfile,includefile,report_errors,
- report_warnings,warnings_as_errors,
- return_errors,return_warnings,
- verbose,dfa_graph], [])
- catch error: _ -> badarg
- end;
-options(Option) ->
- options([Option]).
-
-short_option(return, T) ->
- [{return_errors,T}, {return_warnings,T}];
-short_option(report, T) ->
- [{report_errors,T}, {report_warnings,T}].
-
-options(Options0, [Key|Keys], L) when is_list(Options0) ->
- Options = case member(Key, Options0) of
- true ->
- [atom_option(Key)|delete(Key, Options0)];
- false ->
- Options0
- end,
- V = case lists:keyfind(Key, 1, Options) of
- {Key, Filename0} when Key =:= includefile;
- Key =:= scannerfile ->
- case is_filename(Filename0) of
- no ->
- badarg;
- Filename ->
- {ok,[{Key,Filename}]}
- end;
- {Key, Bool} = KB when is_boolean(Bool) ->
- {ok, [KB]};
- {Key, _} ->
- badarg;
- false ->
- {ok,[{Key,default_option(Key)}]}
- end,
- case V of
+%% Copied from compile.erl.
+env_default_opts() ->
+ Key = "ERL_COMPILER_OPTIONS",
+ case os:getenv(Key) of
+ false -> [];
+ Str when is_list(Str) ->
+ case erl_scan:string(Str) of
+ {ok,Tokens,_} ->
+ Dot = {dot, erl_anno:new(1)},
+ case erl_parse:parse_term(Tokens ++ [Dot]) of
+ {ok,List} when is_list(List) -> List;
+ {ok,Term} -> [Term];
+ {error,_Reason} ->
+ io:format("Ignoring bad term in ~s\n", [Key]),
+ []
+ end;
+ {error, {_,_,_Reason}, _} ->
+ io:format("Ignoring bad term in ~s\n", [Key]),
+ []
+ end
+ end.
+
+select_recognized_opts(Options0) ->
+ Options = preprocess_options(Options0),
+ AllOptions = all_options(),
+ [Option ||
+ {Name, _} = Option <- Options,
+ lists:member(Name, AllOptions)].
+
+options(Options0) ->
+ Options1 = preprocess_options(Options0),
+ AllOptions = all_options(),
+ case check_options(Options1, AllOptions, []) of
badarg ->
badarg;
- {ok,KeyValueL} ->
- NewOptions = keydelete(Key, 1, Options),
- options(NewOptions, Keys, KeyValueL ++ L)
+ OptionValues ->
+ AllOptionValues =
+ [case lists:keyfind(Option, 1, OptionValues) of
+ false ->
+ {Option, default_option(Option)};
+ OptionValue ->
+ OptionValue
+ end || Option <- AllOptions],
+ foldr(fun({_, false}, L) -> L;
+ ({Option, true}, L) -> [Option | L];
+ (OptionValue, L) -> [OptionValue | L]
+ end, [], AllOptionValues)
+ end.
+
+preprocess_options(Options) ->
+ foldr(fun preproc_opt/2, [], Options).
+
+preproc_opt(return, Os) ->
+ [{return_errors, true}, {return_warnings, true} | Os];
+preproc_opt(report, Os) ->
+ [{report_errors, true}, {report_warnings, true} | Os];
+preproc_opt({return, T}, Os) ->
+ [{return_errors, T}, {return_warnings, T} | Os];
+preproc_opt({report, T}, Os) ->
+ [{report_errors, T}, {report_warnings, T} | Os];
+preproc_opt(Option, Os) ->
+ [try atom_option(Option) catch error:_ -> Option end | Os].
+
+check_options([{Option, FileName0} | Options], AllOptions, L)
+ when Option =:= includefile; Option =:= scannerfile ->
+ case is_filename(FileName0) of
+ no ->
+ badarg;
+ Filename ->
+ check_options(Options, AllOptions, [{Option, Filename} | L])
end;
-options([], [], L) ->
- foldl(fun({_,false}, A) -> A;
- ({Tag,true}, A) -> [Tag|A];
- (F,A) -> [F|A]
- end, [], L);
-options(_Options, _, _L) ->
+check_options([{Option, Boolean} | Options], AllOptions, L)
+ when is_boolean(Boolean) ->
+ case lists:member(Option, AllOptions) of
+ true ->
+ check_options(Options, AllOptions, [{Option, Boolean} | L]);
+ false ->
+ badarg
+ end;
+check_options([], _AllOptions, L) ->
+ L;
+check_options(_Options, _, _L) ->
badarg.
+all_options() ->
+ [dfa_graph,includefile,report_errors,report_warnings,
+ return_errors,return_warnings,scannerfile,verbose,
+ warnings_as_errors].
+
default_option(dfa_graph) -> false;
default_option(includefile) -> [];
default_option(report_errors) -> true;
default_option(report_warnings) -> true;
-default_option(warnings_as_errors) -> false;
default_option(return_errors) -> false;
default_option(return_warnings) -> false;
default_option(scannerfile) -> [];
-default_option(verbose) -> false.
+default_option(verbose) -> false;
+default_option(warnings_as_errors) -> false.
atom_option(dfa_graph) -> {dfa_graph,true};
atom_option(report_errors) -> {report_errors,true};
atom_option(report_warnings) -> {report_warnings,true};
atom_option(warnings_as_errors) -> {warnings_as_errors,true};
atom_option(return_errors) -> {return_errors,true};
-atom_option(return_warnings) -> {return_warnings,true};
atom_option(verbose) -> {verbose,true};
+atom_option(return_warnings) -> {return_warnings,true};
atom_option(Key) -> Key.
is_filename(T) ->
diff --git a/lib/parsetools/test/leex_SUITE.erl b/lib/parsetools/test/leex_SUITE.erl
index 16f67f7f26..e8ec376d4b 100644
--- a/lib/parsetools/test/leex_SUITE.erl
+++ b/lib/parsetools/test/leex_SUITE.erl
@@ -45,7 +44,7 @@
pt/1, man/1, ex/1, ex2/1, not_yet/1,
line_wrap/1,
otp_10302/1, otp_11286/1, unicode/1, otp_13916/1, otp_14285/1,
- compiler_warnings/1]).
+ otp_17023/1, compiler_warnings/1]).
% Default timetrap timeout (set in init_per_testcase).
-define(default_timeout, ?t:minutes(1)).
@@ -67,7 +67,7 @@ all() ->
groups() ->
[{checks, [], [file, compile, syntax]},
{examples, [], [pt, man, ex, ex2, not_yet, unicode]},
- {tickets, [], [otp_10302, otp_11286, otp_13916, otp_14285,
+ {tickets, [], [otp_10302, otp_11286, otp_13916, otp_14285, otp_17023,
compiler_warnings]},
{bugs, [], [line_wrap]}].
@@ -1185,6 +1185,32 @@ otp_14285(Config) ->
{ok, compiler_warnings, []} = compile:file(ErlFile, [return]),
ok.
+otp_17023(Config) ->
+ Dir = ?privdir,
+ Filename = filename:join(Dir, "file.xrl"),
+ Ret = [return, {report, true}],
+
+ {'EXIT', {badarg, _}} = (catch leex:file(Filename, [{noopt,true}])),
+ OldEnv = os:getenv("ERL_COMPILER_OPTIONS"),
+ true = os:putenv("ERL_COMPILER_OPTIONS", "strong_validation"),
+ ok = file:write_file(Filename,
+ <<"Definitions.\n"
+ "Rules.\n"
+ "^ : .\n"
+ "Erlang code.\n">>),
+ {error,[{_,[{3,leex,{regexp,_}}]}],[]} =
+ leex:file(Filename, Ret),
+ true = os:putenv("ERL_COMPILER_OPTIONS", "{return, false}"),
+ error = leex:file(Filename, Ret),
+ error = leex:file(Filename, [return | Ret]), % overridden
+ case OldEnv of
+ false ->
+ os:unsetenv("ERL_COMPILER_OPTIONS");
+ _ ->
+ os:putenv("ERL_COMPILER_OPTIONS", OldEnv)
+ end,
+ ok.
+
start_node(Name, Args) ->
[_,Host] = string:tokens(atom_to_list(node()), "@"),
ct:log("Trying to start ~w@~s~n", [Name,Host]),
--
2.26.2