#!/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 (.D and .F)~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).