From 777fefe181521b497e2c23b955be7bea48bf2950 Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Thu, 5 Dec 2013 21:58:44 +0100 Subject: [PATCH] Fix #187 (rename getopt to rebar_getopt) While at it, update getopt.erl to git rev 78eda3b. --- ebin/rebar.app | 2 +- src/rebar.erl | 10 ++-- src/{getopt.erl => rebar_getopt.erl} | 90 +++++++++++++++++++++++++--- 3 files changed, 87 insertions(+), 15 deletions(-) rename src/{getopt.erl => rebar_getopt.erl} (91%) diff --git a/ebin/rebar.app b/ebin/rebar.app index 873be18..e679139 100644 --- a/ebin/rebar.app +++ b/ebin/rebar.app @@ -38,7 +38,7 @@ rebar_upgrade, rebar_utils, rebar_xref, - getopt, + rebar_getopt, mustache ]}, {registered, []}, {applications, [kernel, diff --git a/src/rebar.erl b/src/rebar.erl index 00505be..86b3734 100644 --- a/src/rebar.erl +++ b/src/rebar.erl @@ -178,10 +178,10 @@ run_aux(BaseConfig, Commands) -> %% help() -> OptSpecList = option_spec_list(), - getopt:usage(OptSpecList, "rebar", - "[var=value,...] ", - [{"var=value", "rebar global variables (e.g. force=1)"}, - {"command", "Command to run (e.g. compile)"}]), + rebar_getopt:usage(OptSpecList, "rebar", + "[var=value,...] ", + [{"var=value", "rebar global variables (e.g. force=1)"}, + {"command", "Command to run (e.g. compile)"}]), ?CONSOLE( "Type 'rebar help ' for help on specific commands." "~n~n", []), @@ -215,7 +215,7 @@ help() -> parse_args(RawArgs) -> %% Parse getopt options OptSpecList = option_spec_list(), - case getopt:parse(OptSpecList, RawArgs) of + case rebar_getopt:parse(OptSpecList, RawArgs) of {ok, Args} -> Args; {error, {Reason, Data}} -> diff --git a/src/getopt.erl b/src/rebar_getopt.erl similarity index 91% rename from src/getopt.erl rename to src/rebar_getopt.erl index f9852fb..79b871d 100644 --- a/src/getopt.erl +++ b/src/rebar_getopt.erl @@ -8,10 +8,11 @@ %%% a copy of the New BSD license with this software. If not, it can be %%% retrieved from: http://www.opensource.org/licenses/bsd-license.php %%%------------------------------------------------------------------- --module(getopt). +-module(rebar_getopt). -author('juanjo@comellas.org'). --export([parse/2, usage/2, usage/3, usage/4, tokenize/1]). +-export([parse/2, check/2, parse_and_check/2, format_error/2, + usage/2, usage/3, usage/4, tokenize/1]). -export([usage_cmd_line/2]). -define(LINE_LENGTH, 75). @@ -57,11 +58,52 @@ -export_type([arg_type/0, arg_value/0, arg_spec/0, simple_option/0, compound_option/0, option/0, option_spec/0]). -%% @doc Parse the command line options and arguments returning a list of tuples -%% and/or atoms using the Erlang convention for sending options to a -%% function. +%% @doc Parse the command line options and arguments returning a list of tuples +%% and/or atoms using the Erlang convention for sending options to a +%% function. Additionally perform check if all required options (the ones +%% without default values) are present. The function is a combination of +%% two calls: parse/2 and check/2. +-spec parse_and_check([option_spec()], string() | [string()]) -> + {ok, {[option()], [string()]}} | {error, {Reason :: atom(), Data :: term()}}. +parse_and_check(OptSpecList, CmdLine) when is_list(OptSpecList), is_list(CmdLine) -> + case parse(OptSpecList, CmdLine) of + {ok, {Opts, _}} = Result -> + case check(OptSpecList, Opts) of + ok -> Result; + Error -> Error + end; + Error -> + Error + end. + +%% @doc Check the parsed command line arguments returning ok if all required +%% options (i.e. that don't have defaults) are present, and returning +%% error otherwise. +-spec check([option_spec()], [option()]) -> + ok | {error, {Reason :: atom(), Option :: atom()}}. +check(OptSpecList, ParsedOpts) when is_list(OptSpecList), is_list(ParsedOpts) -> + try + RequiredOpts = [Name || {Name, _, _, Arg, _} <- OptSpecList, + not is_tuple(Arg) andalso Arg =/= undefined], + lists:foreach(fun (Option) -> + case proplists:is_defined(Option, ParsedOpts) of + true -> + ok; + false -> + throw({error, {missing_required_option, Option}}) + end + end, RequiredOpts) + catch + _:Error -> + Error + end. + + +%% @doc Parse the command line options and arguments returning a list of tuples +%% and/or atoms using the Erlang convention for sending options to a +%% function. -spec parse([option_spec()], string() | [string()]) -> - {ok, {[option()], [string()]}} | {error, {Reason :: atom(), Data :: any()}}. + {ok, {[option()], [string()]}} | {error, {Reason :: atom(), Data :: term()}}. parse(OptSpecList, CmdLine) when is_list(CmdLine) -> try Args = if @@ -101,6 +143,24 @@ parse(OptSpecList, OptAcc, ArgAcc, _ArgPos, []) -> {ok, {lists:reverse(append_default_options(OptSpecList, OptAcc)), lists:reverse(ArgAcc)}}. +%% @doc Format the error code returned by prior call to parse/2 or check/2. +-spec format_error([option_spec()], {error, {Reason :: atom(), Data :: term()}} | + {Reason :: term(), Data :: term()}) -> string(). +format_error(OptSpecList, {error, Reason}) -> + format_error(OptSpecList, Reason); +format_error(OptSpecList, {missing_required_option, Name}) -> + {_Name, Short, Long, _Type, _Help} = lists:keyfind(Name, 1, OptSpecList), + lists:flatten(["missing required option: -", [Short], " (", to_string(Long), ")"]); +format_error(_OptSpecList, {invalid_option, OptStr}) -> + lists:flatten(["invalid option: ", to_string(OptStr)]); +format_error(_OptSpecList, {invalid_option_arg, {Name, Arg}}) -> + lists:flatten(["option \'", to_string(Name) ++ "\' has invalid argument: ", to_string(Arg)]); +format_error(_OptSpecList, {invalid_option_arg, OptStr}) -> + lists:flatten(["invalid option argument: ", to_string(OptStr)]); +format_error(_OptSpecList, {Reason, Data}) -> + lists:flatten([to_string(Reason), " ", to_string(Data)]). + + %% @doc Parse a long option, add it to the option accumulator and continue %% parsing the rest of the arguments recursively. %% A long option can have the following syntax: @@ -698,7 +758,7 @@ format_usage_line(_MaxOptionLength, _MaxLineLength, {_OptionLength, OptionText, %% @doc Wrap a text line converting it into several text lines so that the -%% length of each one of them is never over HelpLength characters. +%% length of each one of them is never over Length characters. -spec wrap_text_line(Length :: non_neg_integer(), Text :: string()) -> [string()]. wrap_text_line(Length, Text) -> wrap_text_line(Length, Text, [], 0, []). @@ -730,7 +790,7 @@ default_arg_value_to_string(Value) when is_binary(Value) -> default_arg_value_to_string(Value) when is_integer(Value) -> integer_to_list(Value); default_arg_value_to_string(Value) when is_float(Value) -> - float_to_list(Value); + lists:flatten(io_lib:format("~w", [Value])); default_arg_value_to_string(Value) -> Value. @@ -832,7 +892,7 @@ get_env_var(Prefix, Suffix, []) -> Prefix ++ Suffix. --spec line_length() -> non_neg_integer(). +-spec line_length() -> 0..?LINE_LENGTH. line_length() -> case io:columns() of {ok, Columns} when Columns < ?LINE_LENGTH -> @@ -840,3 +900,15 @@ line_length() -> _ -> ?LINE_LENGTH end. + + +-spec to_string(term()) -> string(). +to_string(List) when is_list(List) -> + case io_lib:printable_list(List) of + true -> List; + false -> io_lib:format("~p", [List]) + end; +to_string(Atom) when is_atom(Atom) -> + atom_to_list(Atom); +to_string(Value) -> + io_lib:format("~p", [Value]).