Merge pull request #214 from tuncer/compile_only

Document compile_only=true
diff --git a/ebin/rebar.app b/ebin/rebar.app
index 873be18..c2f3ef7 100644
--- a/ebin/rebar.app
+++ b/ebin/rebar.app
@@ -38,8 +38,8 @@
               rebar_upgrade,
               rebar_utils,
               rebar_xref,
-              getopt,
-              mustache ]},
+              rebar_getopt,
+              rebar_mustache ]},
   {registered, []},
   {applications, [kernel,
                   stdlib,
diff --git a/src/rebar.erl b/src/rebar.erl
index 00505be..ea3a2ab 100644
--- a/src/rebar.erl
+++ b/src/rebar.erl
@@ -87,7 +87,9 @@
 run(["help"]) ->
     help();
 run(["info"|_]) ->
-    help();
+    %% Catch calls to 'rebar info' to avoid treating plugins' info/2 functions
+    %% as commands.
+    ?CONSOLE("Command 'info' not understood or not applicable~n", []);
 run(["version"]) ->
     ok = load_rebar_app(),
     %% Display vsn and build time info
@@ -178,10 +180,10 @@
 %%
 help() ->
     OptSpecList = option_spec_list(),
-    getopt:usage(OptSpecList, "rebar",
-                 "[var=value,...] <command,...>",
-                 [{"var=value", "rebar global variables (e.g. force=1)"},
-                  {"command", "Command to run (e.g. compile)"}]),
+    rebar_getopt:usage(OptSpecList, "rebar",
+                       "[var=value,...] <command,...>",
+                       [{"var=value", "rebar global variables (e.g. force=1)"},
+                        {"command", "Command to run (e.g. compile)"}]),
     ?CONSOLE(
        "Type 'rebar help <CMD1> <CMD2>' for help on specific commands."
        "~n~n", []),
@@ -215,7 +217,7 @@
 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/rebar_erlydtl_compiler.erl b/src/rebar_erlydtl_compiler.erl
index 6172879..fea8ea6 100644
--- a/src/rebar_erlydtl_compiler.erl
+++ b/src/rebar_erlydtl_compiler.erl
@@ -210,6 +210,10 @@
                          Opts) of
         ok ->
             ok;
+        {ok, _Mod, Ws} ->
+            rebar_base_compiler:ok_tuple(Config, Source, Ws);
+        {ok, _Mod, _Bin, Ws} ->
+            rebar_base_compiler:ok_tuple(Config, Source, Ws);
         error ->
             rebar_base_compiler:error_tuple(Config, Source, [], [], Opts);
         {error, {_File, _Msgs} = Error} ->
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 @@
     {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 @@
 
 
 %% @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_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 @@
     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
     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]).
diff --git a/src/mustache.erl b/src/rebar_mustache.erl
similarity index 95%
rename from src/mustache.erl
rename to src/rebar_mustache.erl
index f6963cd..9016c0f 100644
--- a/src/mustache.erl
+++ b/src/rebar_mustache.erl
@@ -23,7 +23,7 @@
 %% See the README at http://github.com/mojombo/mustache.erl for additional
 %% documentation and usage examples.
 
--module(mustache).  %% v0.1.0
+-module(rebar_mustache).  %% v0.1.0
 -author("Tom Preston-Werner").
 -export([compile/1, compile/2, render/1, render/2, render/3, get/2, get/3, escape/1, start/1]).
 
@@ -31,6 +31,8 @@
                  section_re = undefined,
                  tag_re = undefined}).
 
+-define(MUSTACHE_STR, "rebar_mustache").
+
 compile(Body) when is_list(Body) ->
   State = #mstate{},
   CompiledTemplate = pre_compile(Body, State),
@@ -108,7 +110,7 @@
   Mod = State#mstate.mod,
   Result = compiler(Content, State),
   "fun() -> " ++
-    "case mustache:get(" ++ Name ++ ", Ctx, " ++ atom_to_list(Mod) ++ ") of " ++
+    "case " ++ ?MUSTACHE_STR ++ ":get(" ++ Name ++ ", Ctx, " ++ atom_to_list(Mod) ++ ") of " ++
       "\"true\" -> " ++
         Result ++ "; " ++
       "\"false\" -> " ++
@@ -143,10 +145,10 @@
 
 compile_tag(none, Content, State) ->
   Mod = State#mstate.mod,
-  "mustache:escape(mustache:get(" ++ Content ++ ", Ctx, " ++ atom_to_list(Mod) ++ "))";
+  ?MUSTACHE_STR ++ ":escape(" ++ ?MUSTACHE_STR ++ ":get(" ++ Content ++ ", Ctx, " ++ atom_to_list(Mod) ++ "))";
 compile_tag("{", Content, State) ->
   Mod = State#mstate.mod,
-  "mustache:get(" ++ Content ++ ", Ctx, " ++ atom_to_list(Mod) ++ ")";
+  ?MUSTACHE_STR ++ ":get(" ++ Content ++ ", Ctx, " ++ atom_to_list(Mod) ++ ")";
 compile_tag("!", _Content, _State) ->
   "[]".
 
diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl
index c21daa3..43bb8da 100644
--- a/src/rebar_templater.erl
+++ b/src/rebar_templater.erl
@@ -103,7 +103,7 @@
     ReOpts = [global, {return, list}],
     Str0 = re:replace(Bin, "\\\\", "\\\\\\", ReOpts),
     Str1 = re:replace(Str0, "\"", "\\\\\"", ReOpts),
-    mustache:render(Str1, Context).
+    rebar_mustache:render(Str1, Context).
 
 %% ===================================================================
 %% Internal functions
diff --git a/src/rebar_upgrade.erl b/src/rebar_upgrade.erl
index 5814e51..3a38a08 100644
--- a/src/rebar_upgrade.erl
+++ b/src/rebar_upgrade.erl
@@ -87,7 +87,8 @@
     ?CONSOLE("Build an upgrade package.~n"
              "~n"
              "Valid command line options:~n"
-             "  previous_release=path~n",
+             "  previous_release=path~n"
+             "  target_dir=target_dir (optional)~n",
              []).
 
 run_checks(Config, OldVerPath, ReltoolConfig) ->
@@ -97,10 +98,7 @@
 
     {Name, Ver} = rebar_rel_utils:get_reltool_release_info(ReltoolConfig),
 
-    NewVerPath =
-        filename:join(
-          [rebar_rel_utils:get_target_parent_dir(Config, ReltoolConfig),
-           Name]),
+    NewVerPath = rebar_rel_utils:get_target_dir(Config, ReltoolConfig),
     true = rebar_utils:prop_check(filelib:is_dir(NewVerPath),
                                   "Release directory doesn't exist (~p)~n",
                                   [NewVerPath]),