aboutsummaryrefslogblamecommitdiffstats
path: root/lib/diameter/bin/diameterc
blob: 2f5834d359e6f9f4ccdb1ead8869501d5ba7ce9a (plain) (tree)
1
2
3
4
5
6
7





                                    
                                                        



























                                                                         
                           




                                                                             
          


                                                       







                                                                        





















                                                          








                                                                   

                        
                                                                            




                                                                      
                                                                   

                         



                                                          






















                                                        

                                                          

                                                          
 

                                                            
 


                                                              







                                                                 
                                                

                                                         
                                   






















                                                                        
 




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

%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2010-2013. 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] dict~n"
      "~n"
      "  Compile a diameter dictionary file (.dia) to Erlang source (.erl)~n"
      "  and/or header (.hrl) files.~n"
      "~n"
      "  options:~n"
      "~n"
      "      --name name       = set @name~n"
      "      --prefix prefix   = set @prefix~n"
      "      --inherits dict|- = set/clear @inherits~n"
      "~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 diameter_dict_util:parse({path, File}, Opts) of
        {ok, Spec} ->
            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;
        {error, Reason} ->
            error_msg(diameter_dict_util:format_error(Reason), []),
            1
    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_dict(File, Spec, Opts, Mode).

error_msg({Fmt, Args}) ->
    error_msg(Fmt, Args).

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) ->
    arg(Args, A#argv{options = Opts ++ [{include, Dir}]});

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

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

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

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, C /= $- ->
    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).