aboutsummaryrefslogblamecommitdiffstats
path: root/lib/diameter/bin/diameterc
blob: cba664bfad7a7edd880a6cf1a986fe35387c3879 (plain) (tree)


























































































































































                                                                              
#!/usr/bin/env escript
%% Use -*- erlang -*- mode in Erlang

%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2010. 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(diameterc).

-mode(compile).

-include_lib("kernel/include/file.hrl").

%% The parsed command line.
-record(argv, {file,
               options = [{outdir, "."}],
               output  = [erl, hrl]}).

usage() ->
    io:format(
      "~w [options] file~n"
      "~n"
      "  Compile a diameter dictionary file (.dia) to Erlang source (.erl)~n"
      "  and/or header (.hrl) files.~n"
      "~n"
      "  options:~n"
      "      -h      = print this message~n"
      "      -v      = verbose output~n"
      "      -o dir  = set the output directory (default .)~n"
      "      -i dir  = set an include directory for inherited beams~n"
      "      -E      = no .erl output~n"
      "      -H      = no .hrl output~n"
      "      -d      = write intermediate files (.spec and .forms)~n",
      [?MODULE]).

main(Args) ->
    %% Add the ebin directory relative to the script path.
    BinDir = filename:dirname(escript:script_name()),
    code:add_path(filename:join([BinDir, "..", "ebin"])),
    halt(gen(Args)).

gen(Args) ->
    try parse_args(Args) of
        #argv{} = A ->
            compile(A)
    catch
        throw: usage  ->
            usage(),
            0;
        throw: Reason ->
            error_msg(norm(Reason)),
            1
    end.

compile(#argv{file = File, options = Opts} = A) ->
    try
        Spec = diameter_spec_util:parse(File, Opts),
        maybe_output(A, Spec, Opts, spec), %% the spec file
        maybe_output(A, Spec, Opts, erl),  %% the erl file
        maybe_output(A, Spec, Opts, hrl),  %% The hrl file
        0
    catch
        error: Reason ->
            error_msg({"ERROR: ~p~n  ~p", [Reason, erlang:get_stacktrace()]}),
            2
    end.

maybe_output(#argv{file = File, output = Output}, Spec, Opts, Mode) ->
    lists:member(Mode, Output)
        andalso diameter_codegen:from_spec(File, Spec, Opts, Mode).

error_msg({Fmt, Args}) ->
    io:format(standard_error, Fmt ++ "~n", Args).

norm({_,_} = T) ->
    T;
norm(Str) ->
    {Str, []}.

%% parse_args/1

parse_args(Args)
  when is_list(Args) ->
    arg(Args, #argv{}).

arg(["-h" | _], _) ->
    throw(usage);

arg(["-v" | Args], #argv{options = Opts} = A) ->
    arg(Args, A#argv{options = [verbose | Opts]});

arg(["-o", Dir | Args], #argv{options = Opts} = A) ->
    true = dir_exists(Dir),
    arg(Args, A#argv{options = [{outdir, Dir} | Opts]});

arg(["-i", Dir | Args], #argv{options = Opts} = A) ->
    true = dir_exists(Dir),
    arg(Args, A#argv{options = Opts ++ [{include, Dir}]});

arg(["-E" | Args], #argv{output = Output} = A) ->
    arg(Args, A#argv{output = lists:delete(erl, Output)});

arg(["-H" | Args], #argv{output = Output} = A) ->
    arg(Args, A#argv{output = lists:delete(hrl, Output)});

arg(["-d" | Args], #argv{options = Opts, output = Output} = A) ->
    arg(Args, A#argv{options = [debug | Opts],
                            output = [spec | Output]});

arg([[$- = M, C, H | T] | Args], A)  %% clustered options
  when C /= $i, C /= $o ->
    arg([[M,C], [M,H|T] | Args], A);

arg([File], A) ->
    true = file_exists(File),
    A#argv{file = File};

arg([], _) ->
    throw("No input file");

arg([Bad | _], _) ->
    throw({"Unknown option: ~p", [Bad]}).

%% path_exists/2

path_exists(File, Type) ->
    case file:read_file_info(File) of
	{ok, #file_info{type = Type}} ->
	    true;
	{ok, #file_info{type = WrongType}} ->
	    throw({"Invalid type for file: ~p, ~p", [WrongType, File]});
	_ ->
	    throw({"No such file: ~p", [File]})
    end.
    
file_exists(File) ->
    path_exists(File, regular).

dir_exists(File) ->
    path_exists(File, directory).