%% %% %CopyrightBegin% %% %% Copyright Ericsson AB 1998-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. %% You may obtain a copy of the License at %% %% http://www.apache.org/licenses/LICENSE-2.0 %% %% Unless required by applicable law or agreed to in writing, software %% distributed under the License is distributed on an "AS IS" BASIS, %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %% See the License for the specific language governing permissions and %% limitations under the License. %% %% %CopyrightEnd% %% %% -module(ic_options). -include_lib("ic/src/ic.hrl"). -include_lib("kernel/include/file.hrl"). %%----------------------------------------------------------------- %% External exports %%----------------------------------------------------------------- -export([defaultBe/0, float_to_version/1, get_opt/2, add_opt/3, read_cfg/2, which_opts/1, allowed_opt/2]). %%----------------------------------------------------------------- %% Internal exports %%----------------------------------------------------------------- -export([]). %%----------------------------------------------------------------- %% External functions %%----------------------------------------------------------------- %%-------------------------------------------------------------------- %% %% Option handling %% %% Valid options are: (those with * is NotYetImpl) %% %% pedantic - makes the compiler really nitty-gritty about its input %% %% Wall - those warning options that we feel an IDL programmer should %% care about. Not as picky as pedantic %% %% warn_multi_mod - warn if several modules are declared in the same %% IDL file %% %% warn_nested_mod - warn if there are nested modules. This is not a %% problem but it breakes the rule that modules are put into one file %% each. %% %% warn_name_shadow - warn if identifiers are shadow through inherited %% interfaces. Default is true. %% %% warn_quoted_atom - warn if atoms needs quote, this makes Erlang %% code less nice but is certainly no error. %% %% nowarn - suppress all warning messages. Will still output warnings %% if silent2 option is used %% %% always_outargs - force object server implementation return the %% tuple {RetVal, OutArgs, NewState} even if there are no OutArgs. If %% this option is not set then such an operation implementation is %% assumed to return {RetVal, NewState} %% %% use_proc_dict - use the process dictionary in the client %% stubs. This means that client stubs return RetVal instead of {ok, %% RetVal, OutArgs} and that corba:get_outargs() returns OutArgs. The %% out arguments are stored with the key '$corba_outargs'. %% %% module_group - use the top module as file name for both skeletons %% and stubs. Default value is false which means that each interface %% is put in a separate file. %% %% skel_module_group - group all interfaces in a module in one %% skeleton file as opposed to one skeleton file for each %% interface. Defaults to false. %% %% stub_module_group - group all interface stubs from a module in one %% stub file as opposed to one stub file for each interface. Default %% is false. %% %% *help - prints a small summary of the compiler usage %% %% silent - suppresses all messages from the compiler %% %% silent2 - suppresses all messages from the compiler and returns all %% warnings or errors as lists. Returns {ok, WarnList} or {error, %% WarnList, ErrList} %% %% *noexec - runs the compiler but does not open files or write to %% files. %% %% {serv, } - sets the name of the implementation skeleton %% file. This defaults to ModName_skel. %% %% {impl, } - sets the name of the interface server %% implementation module name. This defaults to InterfaceName_impl %% %% {outdir, Dir} - use Dir as the directory to put all generated %% files. %% %% {servdir, Dir} - put all generated skel files in the directory Dir. %% %% {stubdir, Dir} - put all generated stub files in the directory Dir. %% %% {this, InterfaceOrOpName} - puts the OE_THIS parameter into the %% impl. call. This option can be used both on whole interfaces an on %% distinct operations. Fullscoped names must be used (as in {this, %% "M1::I1::Op"}). The option can be given in 3 ways: {this, Name} %% means this will be added to all matching Name or as {{this, Name}, %% true} or this can explicitly be asked to be left out as in {{this, %% Name}, false} which enables OE_THIS to be passed to all ops of an %% interface except those set by the false flag. %% %% cfgfile - sets the name of the config file that is read at %% startup. The order of the different ways to set options is: default %% setting, configuration file, options given when generator is %% called. Default name for this file is .ic_config %% %% serv_last_call - tells what the last handle_call clause should %% do. It can have the values exception, which makes the last clause %% return a CORBA exception and exit which does not generate a last clause %% (which will make the server crash on an unknown call) %% %% %% -- UNDOCUMENTED -- %% %% debug - prints debug information %% %% tokens - prints the tokens from the tokenizer and then exit %% %% form - prints the form from the parser and then exit %% %% tform - form returned from type check %% %% time - if true then time is measured during compilation %% %% %%-------------------------------------------------------------------- allowed_opt(default_opts, _V) -> true; allowed_opt(debug, V) -> is_bool(V); allowed_opt(tokens, V) -> is_bool(V); allowed_opt(form, V) -> is_bool(V); allowed_opt(tform, V) -> is_bool(V); allowed_opt(time, V) -> is_bool(V); allowed_opt(maxerrs, V) -> is_intorinfinity(V); allowed_opt(maxwarns, V) -> is_intorinfinity(V); allowed_opt(nowarn, V) -> is_bool(V); allowed_opt(show_opts, V) -> is_bool(V); allowed_opt(help, V) -> is_bool(V); allowed_opt('Wall', V) -> is_bool(V); allowed_opt(warn_multi_mod, V) -> is_bool(V); allowed_opt(warn_quoted_atom, V) -> is_bool(V); allowed_opt(warn_nested_mod, V) -> is_bool(V); allowed_opt(warn_name_shadow, V) -> is_bool(V); allowed_opt(module_group, V) -> is_bool(V); allowed_opt(skel_module_group, V) -> is_bool(V); allowed_opt(stub_module_group, V) -> is_bool(V); allowed_opt(always_outargs, V) -> is_bool(V); allowed_opt(pedantic, V) -> is_bool(V); %%allowed_opt(gen_serv, V) -> is_bool(V); %%allowed_opt(gen_stub, V) -> is_bool(V); allowed_opt(gen_hrl, V) -> is_bool(V); allowed_opt(serv_last_call, exception) -> true; allowed_opt(serv_last_call, exit) -> true; allowed_opt(silent, V) -> is_bool(V); allowed_opt(silent2, V) -> is_bool(V); allowed_opt({serv, _}, _V) -> true; allowed_opt({impl, _}, _V) -> true; allowed_opt(outdir, _V) -> true; allowed_opt(servdir, _V) -> true; allowed_opt(stubdir, _V) -> true; allowed_opt(cfgfile, _V) -> true; allowed_opt(use_preproc, V) -> is_bool(V); allowed_opt(preproc_cmd, _V) -> true; allowed_opt(preproc_flags, _V) -> true; allowed_opt(this, _V) -> true; allowed_opt({this, _}, V) -> is_bool(V); allowed_opt(from, _V) -> true; allowed_opt({from, _}, V) -> is_bool(V); allowed_opt(handle_info, _V) -> true; allowed_opt({handle_info, _}, V) -> is_bool(V); allowed_opt(timeout, _V) -> true; allowed_opt({timeout, _}, V) -> is_bool(V); allowed_opt(c_timeout, {V1, V2}) -> is_int(V1) and is_int(V2); allowed_opt(c_timeout, V) -> is_int(V); allowed_opt(c_report, V) -> is_bool(V); allowed_opt(scoped_op_calls, V) -> is_bool(V); % Compatibility option (semantic check limitation) allowed_opt(scl, V) -> is_bool(V); % Added switches for non corba generation allowed_opt(flags, V) -> is_int(V); allowed_opt(be, erl_corba) -> true; allowed_opt(be, erl_template) -> true; allowed_opt(be, erl_genserv) -> true; allowed_opt(be, c_genserv) -> true; allowed_opt(be, erl_plain) -> true; allowed_opt(be, c_server) -> true; allowed_opt(be, c_client) -> true; allowed_opt(be, java) -> true; % Noc backend allowed_opt(be, noc) -> true; allowed_opt({broker,_},{_,transparent}) -> true; allowed_opt({broker,_},{_,Term}) -> is_term(Term); allowed_opt({use_tk,_},V) -> is_bool(V); % % Multiple be allowed_opt(multiple_be, _List) -> true; % allowed_opt(precond, {_M, _F}) -> true; allowed_opt({precond, _}, {_M, _F}) -> true; allowed_opt(postcond, {_M, _F}) -> true; allowed_opt({postcond, _}, {_M, _F}) -> true; allowed_opt(no_codechange, V) -> is_bool(V); allowed_opt(user_protocol, _V) -> true; allowed_opt(light_ifr, V) -> is_bool(V); allowed_opt(_, _) -> false. -define(DEFAULTCFGFILE, ".ic_config"). which_opts(G) -> ets:match(G#genobj.options, {{option, '$1'}, '$2'}). add_opt(G, KList, Val) when is_list(KList) -> lists:foreach(fun({K, V}) -> add_opt(G, K, V); (K) -> add_opt(G, K, Val) end, KList); add_opt(G, servdir, V) -> do_add_opt(G, servdir, assure_directory(G, ic_util:to_list(V))); add_opt(G, stubdir, V) -> do_add_opt(G, stubdir, assure_directory(G, ic_util:to_list(V))); add_opt(G, K, V) -> do_add_opt(G, K, V). assure_directory(_G, Dir) -> Dirs = filename:split(Dir), check_dirs(Dirs, [], filename:pathtype(Dir)). check_dirs([X | Xs], SoFar, Type) -> New = if SoFar == [], Type /= absolute -> X; true -> filename:join(SoFar, X) end, assert_dir(New), check_dirs(Xs, New, Type); check_dirs([], SoFar, _Type) -> SoFar. assert_dir(D) -> case file:read_file_info(D) of {ok, X} when X#file_info.type == directory -> ok; _ -> case file:make_dir(D) of ok -> ok; _ -> exit({could_not_create, D}) end end. do_add_opt(G, handle_info, V) -> ?insert(G#genobj.options, {option, {handle_info, V}}, true); do_add_opt(G, {handle_info, V}, false) -> ?insert(G#genobj.options, {option, {handle_info, V}}, force_false); do_add_opt(G, timeout, V) -> ?insert(G#genobj.options, {option, {timeout, V}}, true); do_add_opt(G, {timeout, V}, false) -> ?insert(G#genobj.options, {option, {timeout, V}}, force_false); do_add_opt(G, this, V) -> ?insert(G#genobj.options, {option, {this, V}}, true); do_add_opt(G, {this, V}, false) -> ?insert(G#genobj.options, {option, {this, V}}, force_false); do_add_opt(G, from, V) -> ?insert(G#genobj.options, {option, {from, V}}, true); do_add_opt(G, {from, V}, false) -> ?insert(G#genobj.options, {option, {from, V}}, force_false); do_add_opt(G, scoped_op_calls, V) when V /= true, V /= false -> ?insert(G#genobj.options, {option, {scoped_op_calls, V}}, false); do_add_opt(G, K, V) -> case allowed_opt(K, V) of true -> case expand_opt(K) of L when is_list(L) -> add_opt(G, L, V); _ -> %%io:format("Add opt: ~p ~p~n", [K, V]), ?insert(G#genobj.options, {option, K}, V) end; _ -> ic_error:warn(G, {illegal_opt, K}) end. get_opt(G, K) -> case ets:lookup(G#genobj.options, {option, K}) of [] -> false; [{{_, K}, V}] -> V end. expand_opt(pedantic) -> [warn_multi_mod, warn_quoted_atom, always_outargs]; expand_opt(module_group) -> [skel_module_group, stub_module_group]; expand_opt('Wall') -> [warn_multi_mod, warn_nested_mod, warn_name_shadow]; expand_opt(outdir) -> [servdir, stubdir]; expand_opt(default_opts) -> ['Wall', gen_hrl, {serv_last_call, exception}, {outdir, []}, use_preproc, {preproc_cmd, "erl"}, {preproc_flags, ""}, {maxerrs, 10}, {maxwarns, infinity}]; %% gcc preproc command {preproc_cmd, "gcc -x c++ -E"} expand_opt(Opt) -> Opt. %% Use this if user not provide %% a backend. defaultBe() -> erl_corba. %% %% Read any config file read_cfg(G, Opts) -> Name = case lists:keysearch(cfgfile, 1, Opts) of {value, {_, N}} -> ic_util:to_list(N); _ -> ?DEFAULTCFGFILE end, case file:consult(Name) of {ok, OptList} -> add_opt(G, OptList, true); _X when Name == ?DEFAULTCFGFILE -> ok; %% {error, X} -> %% ic_error:warn(G, {cfg_open, X, Name}); X -> ic_error:warn(G, {cfg_open, X, Name}) end. float_to_version({_,_,Str}) -> Str. %%----------------------------------------------------------------- %% Internal functions %%----------------------------------------------------------------- is_bool(true) -> true; is_bool(false) -> true; is_bool(_) -> false. is_int(V) when is_integer(V) -> true; is_int(_) -> false. is_intorinfinity(X) when is_integer(X) -> true; is_intorinfinity(infinity) -> true; is_intorinfinity(_X) -> false. is_term(Term) when is_tuple(Term) -> true; is_term(_NoTerm) -> false.