aboutsummaryrefslogtreecommitdiffstats
path: root/lib/stdlib/src/erl_compile.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/stdlib/src/erl_compile.erl')
-rw-r--r--lib/stdlib/src/erl_compile.erl233
1 files changed, 233 insertions, 0 deletions
diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl
new file mode 100644
index 0000000000..d9d15e05f8
--- /dev/null
+++ b/lib/stdlib/src/erl_compile.erl
@@ -0,0 +1,233 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 1997-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(erl_compile).
+
+-include("erl_compile.hrl").
+-include("file.hrl").
+
+-export([compile_cmdline/1]).
+
+%% Mapping from extension to {M,F} to run the correct compiler.
+
+compiler(".erl") -> {compile, compile};
+compiler(".S") -> {compile, compile_asm};
+compiler(".beam") -> {compile, compile_beam};
+compiler(".core") -> {compile, compile_core};
+compiler(".mib") -> {snmpc, compile};
+compiler(".bin") -> {snmpc, mib_to_hrl};
+compiler(".xrl") -> {leex, compile};
+compiler(".yrl") -> {yecc, compile};
+compiler(".script") -> {systools, script2boot};
+compiler(".rel") -> {systools, compile_rel};
+compiler(".idl") -> {ic, compile};
+compiler(".asn1") -> {asn1ct, compile_asn1};
+compiler(".asn") -> {asn1ct, compile_asn};
+compiler(".py") -> {asn1ct, compile_py};
+compiler(".xml") -> {xmerl_scan, process};
+compiler(_) -> no.
+
+%% Entry from command line.
+
+-type cmd_line_arg() :: atom() | string().
+
+-spec compile_cmdline([cmd_line_arg()]) -> no_return().
+
+compile_cmdline(List) ->
+ case compile(List) of
+ ok -> my_halt(0);
+ error -> my_halt(1);
+ _ -> my_halt(2)
+ end.
+
+my_halt(Reason) ->
+ case process_info(group_leader(), status) of
+ {_,waiting} ->
+ %% Now all output data is down in the driver.
+ %% Give the driver some extra time before halting.
+ receive after 1 -> ok end,
+ halt(Reason);
+ _ ->
+ %% Probably still processing I/O requests.
+ erlang:yield(),
+ my_halt(Reason)
+ end.
+
+%% Run the the compiler in a separate process, trapping EXITs.
+
+compile(List) ->
+ process_flag(trap_exit, true),
+ Pid = spawn_link(fun() -> compiler_runner(List) end),
+ receive
+ {'EXIT', Pid, {compiler_result, Result}} ->
+ Result;
+ {'EXIT', Pid, Reason} ->
+ io:format("Runtime error: ~p~n", [Reason]),
+ error
+ end.
+
+-spec compiler_runner([cmd_line_arg()]) -> no_return().
+
+compiler_runner(List) ->
+ %% We don't want the current directory in the code path.
+ %% Remove it.
+ Path = [D || D <- code:get_path(), D =/= "."],
+ true = code:set_path(Path),
+ exit({compiler_result, compile1(List)}).
+
+%% Parses the first part of the option list.
+
+compile1(['@cwd', Cwd|Rest]) ->
+ CwdL = atom_to_list(Cwd),
+ compile1(Rest, CwdL, #options{outdir=CwdL, cwd=CwdL});
+compile1(Args) ->
+ %% From R13B02, the @cwd argument is optional.
+ {ok, Cwd} = file:get_cwd(),
+ compile1(Args, Cwd, #options{outdir=Cwd, cwd=Cwd}).
+
+%% Parses all options.
+
+compile1(['@i', Dir|Rest], Cwd, Opts) ->
+ AbsDir = filename:absname(Dir, Cwd),
+ compile1(Rest, Cwd, Opts#options{includes=[AbsDir|Opts#options.includes]});
+compile1(['@outdir', Dir|Rest], Cwd, Opts) ->
+ AbsName = filename:absname(Dir, Cwd),
+ case file_or_directory(AbsName) of
+ file ->
+ compile1(Rest, Cwd, Opts#options{outfile=AbsName});
+ directory ->
+ compile1(Rest, Cwd, Opts#options{outdir=AbsName})
+ end;
+compile1(['@d', Name|Rest], Cwd, Opts) ->
+ Defines = Opts#options.defines,
+ compile1(Rest, Cwd, Opts#options{defines=[Name|Defines]});
+compile1(['@dv', Name, Term|Rest], Cwd, Opts) ->
+ Defines = Opts#options.defines,
+ Value = make_term(atom_to_list(Term)),
+ compile1(Rest, Cwd, Opts#options{defines=[{Name, Value}|Defines]});
+compile1(['@warn', Level0|Rest], Cwd, Opts) ->
+ case catch list_to_integer(atom_to_list(Level0)) of
+ Level when is_integer(Level) ->
+ compile1(Rest, Cwd, Opts#options{warning=Level});
+ _ ->
+ compile1(Rest, Cwd, Opts)
+ end;
+compile1(['@verbose', false|Rest], Cwd, Opts) ->
+ compile1(Rest, Cwd, Opts#options{verbose=false});
+compile1(['@verbose', true|Rest], Cwd, Opts) ->
+ compile1(Rest, Cwd, Opts#options{verbose=true});
+compile1(['@optimize', Atom|Rest], Cwd, Opts) ->
+ Term = make_term(atom_to_list(Atom)),
+ compile1(Rest, Cwd, Opts#options{optimize=Term});
+compile1(['@option', Atom|Rest], Cwd, Opts) ->
+ Term = make_term(atom_to_list(Atom)),
+ Specific = Opts#options.specific,
+ compile1(Rest, Cwd, Opts#options{specific=[Term|Specific]});
+compile1(['@output_type', OutputType|Rest], Cwd, Opts) ->
+ compile1(Rest, Cwd, Opts#options{output_type=OutputType});
+compile1(['@files'|Rest], Cwd, Opts) ->
+ Includes = lists:reverse(Opts#options.includes),
+ compile2(Rest, Cwd, Opts#options{includes=Includes}).
+
+compile2(Files, Cwd, Opts) ->
+ case {Opts#options.outfile, length(Files)} of
+ {"", _} ->
+ compile3(Files, Cwd, Opts);
+ {[_|_], 1} ->
+ compile3(Files, Cwd, Opts);
+ {[_|_], _N} ->
+ io:format("Output file name given, but more than one input file.~n"),
+ error
+ end.
+
+%% Compiles the list of files, until done or compilation fails.
+
+compile3([File|Rest], Cwd, Options) ->
+ Ext = filename:extension(File),
+ Root = filename:rootname(File),
+ InFile = filename:absname(Root, Cwd),
+ OutFile =
+ case Options#options.outfile of
+ "" ->
+ filename:join(Options#options.outdir, filename:basename(Root));
+ Outfile ->
+ filename:rootname(Outfile)
+ end,
+ case compile_file(Ext, InFile, OutFile, Options) of
+ ok ->
+ compile3(Rest, Cwd, Options);
+ Other ->
+ Other
+ end;
+compile3([], _Cwd, _Options) -> ok.
+
+%% Invokes the appropriate compiler, depending on the file extension.
+
+compile_file("", Input, _Output, _Options) ->
+ io:format("File has no extension: ~s~n", [Input]),
+ error;
+compile_file(Ext, Input, Output, Options) ->
+ case compiler(Ext) of
+ no ->
+ io:format("Unknown extension: '~s'\n", [Ext]),
+ error;
+ {M, F} ->
+ case catch M:F(Input, Output, Options) of
+ ok -> ok;
+ error -> error;
+ {'EXIT',Reason} ->
+ io:format("Compiler function ~w:~w/3 failed:\n~p~n",
+ [M,F,Reason]),
+ error;
+ Other ->
+ io:format("Compiler function ~w:~w/3 returned:\n~p~n",
+ [M,F,Other]),
+ error
+ end
+ end.
+
+%% Guesses if a give name refers to a file or a directory.
+
+file_or_directory(Name) ->
+ case file:read_file_info(Name) of
+ {ok, #file_info{type=regular}} ->
+ file;
+ {ok, _} ->
+ directory;
+ {error, _} ->
+ case filename:extension(Name) of
+ [] -> directory;
+ _Other -> file
+ end
+ end.
+
+%% Makes an Erlang term given a string.
+
+make_term(Str) ->
+ case erl_scan:string(Str) of
+ {ok, Tokens, _} ->
+ case erl_parse:parse_term(Tokens ++ [{dot, 1}]) of
+ {ok, Term} -> Term;
+ {error, {_,_,Reason}} ->
+ io:format("~s: ~s~n", [Reason, Str]),
+ throw(error)
+ end;
+ {error, {_,_,Reason}, _} ->
+ io:format("~s: ~s~n", [Reason, Str]),
+ throw(error)
+ end.