From d1ff83a8989cd11fbbed794a97c6e13cf6388e21 Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Mon, 24 Jan 2011 16:40:50 +0100 Subject: Move command line handling funs into rebar.erl --- src/rebar.erl | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 184 insertions(+), 1 deletion(-) (limited to 'src/rebar.erl') diff --git a/src/rebar.erl b/src/rebar.erl index 74812f5..673a4c1 100644 --- a/src/rebar.erl +++ b/src/rebar.erl @@ -26,7 +26,16 @@ %% ------------------------------------------------------------------- -module(rebar). --export([main/1]). +-export([main/1, + help/0, + parse_args/1, + version/0]). + +-include("rebar.hrl"). + +%% ==================================================================== +%% Public API +%% ==================================================================== main(Args) -> case catch(rebar_core:run(Args)) of @@ -39,3 +48,177 @@ main(Args) -> io:format("Uncaught error in rebar_core: ~p\n", [Error]), halt(1) end. + +%% +%% print help/usage string +%% +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)"}]). + +%% +%% Parse command line arguments using getopt and also filtering out any +%% key=value pairs. What's left is the list of commands to run +%% +parse_args(Args) -> + %% Parse getopt options + OptSpecList = option_spec_list(), + case getopt:parse(OptSpecList, Args) of + {ok, {Options, NonOptArgs}} -> + %% Check options and maybe halt execution + ok = show_info_maybe_halt(Options, NonOptArgs), + + %% Set global variables based on getopt options + set_global_flag(Options, verbose), + set_global_flag(Options, force), + DefJobs = rebar_config:get_jobs(), + case proplists:get_value(jobs, Options, DefJobs) of + DefJobs -> + ok; + Jobs -> + rebar_config:set_global(jobs, Jobs) + end, + + %% Set the rebar config to use + case proplists:get_value(config, Options) of + undefined -> ok; + Conf -> rebar_config:set_global(config, Conf) + end, + + %% Filter all the flags (i.e. strings of form key=value) from the + %% command line arguments. What's left will be the commands to run. + filter_flags(NonOptArgs, []); + + {error, {Reason, Data}} -> + ?ERROR("Error: ~s ~p~n~n", [Reason, Data]), + rebar:help(), + halt(1) + end. + +%% +%% show version information and halt +%% +version() -> + {ok, Vsn} = application:get_key(rebar, vsn), + ?CONSOLE("rebar version: ~s date: ~s vcs: ~s\n", [Vsn, ?BUILD_TIME, ?VCS_INFO]). + + +%% ==================================================================== +%% Internal functions +%% ==================================================================== + +%% +%% set global flag based on getopt option boolean value +%% +set_global_flag(Options, Flag) -> + Value = case proplists:get_bool(Flag, Options) of + true -> + "1"; + false -> + "0" + end, + rebar_config:set_global(Flag, Value). + +%% +%% show info and maybe halt execution +%% +show_info_maybe_halt(Opts, NonOptArgs) -> + false = show_info_maybe_halt(help, Opts, fun help/0), + false = show_info_maybe_halt(commands, Opts, fun commands/0), + false = show_info_maybe_halt(version, Opts, fun version/0), + case NonOptArgs of + [] -> + ?CONSOLE("No command to run specified!~n",[]), + help(), + halt(1); + _ -> + ok + end. + +show_info_maybe_halt(O, Opts, F) -> + case proplists:get_bool(O, Opts) of + true -> + F(), + halt(0); + false -> + false + end. + +%% +%% print known commands +%% +commands() -> + S = <<" +dialyze Analyze with Dialyzer +build-plt Build Dialyzer PLT +check-plt Check Dialyzer PLT + +clean Clean +compile Compile sources + +create template= [var=foo,...] Create skel based on template and vars +create-app [appid=myapp] Create simple app skel +create-node [nodeid=mynode] Create simple node skel +list-templates List available templates + +doc Generate Erlang program documentation + +check-deps Display to be fetched dependencies +get-deps Fetch dependencies +update-deps Update fetched dependencies +delete-deps Delete fetched dependencies + +generate [dump_spec=0/1] Build release with reltool + +eunit [suite=foo] Run eunit [test/foo_tests.erl] tests +ct [suite=] [case=] Run common_test suites in ./test + +xref Run cross reference analysis + +help Show the program options +version Show version information +">>, + io:put_chars(S), + %% workaround to delay exit until all output is written + timer:sleep(300). + +%% +%% options accepted via getopt +%% +option_spec_list() -> + Jobs = rebar_config:get_jobs(), + JobsHelp = io_lib:format( + "Number of concurrent workers a command may use. Default: ~B", + [Jobs]), + [ + %% {Name, ShortOpt, LongOpt, ArgSpec, HelpMsg} + {help, $h, "help", undefined, "Show the program options"}, + {commands, $c, "commands", undefined, "Show available commands"}, + {verbose, $v, "verbose", undefined, "Be verbose about what gets done"}, + {version, $V, "version", undefined, "Show version information"}, + {force, $f, "force", undefined, "Force"}, + {jobs, $j, "jobs", integer, JobsHelp}, + {config, $C, "config", string, "Rebar config file to use"} + ]. + +%% +%% Seperate all commands (single-words) from flags (key=value) and store +%% values into the rebar_config global storage. +%% +filter_flags([], Commands) -> + lists:reverse(Commands); +filter_flags([Item | Rest], Commands) -> + case string:tokens(Item, "=") of + [Command] -> + filter_flags(Rest, [Command | Commands]); + [KeyStr, Value] -> + Key = list_to_atom(KeyStr), + rebar_config:set_global(Key, Value), + filter_flags(Rest, Commands); + Other -> + ?CONSOLE("Ignoring command line argument: ~p\n", [Other]), + filter_flags(Rest, Commands) + end. -- cgit v1.1 From 6978504d43de4d12d791db974e4748e2cb4c2092 Mon Sep 17 00:00:00 2001 From: Klas Johansson Date: Mon, 24 Jan 2011 16:55:18 +0100 Subject: Add support for abbreviated command names This change makes it possible to type the beginning (the prefix) of a command name and rebar will guess the full name of the command, thereby saving the user precious keystrokes. As long as the prefix matches only one command, rebar runs that command, otherwise rebar prints a list of candidate command names. The "-" character is considered to be a word separator and the prefix matching is done per word. Example prefix matches: co ==> compile cl ==> clean create ==> create create-a ==> create-app c-a ==> create-app c-app ==> create-app --- src/rebar.erl | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) (limited to 'src/rebar.erl') diff --git a/src/rebar.erl b/src/rebar.erl index 673a4c1..69725b3 100644 --- a/src/rebar.erl +++ b/src/rebar.erl @@ -90,7 +90,7 @@ parse_args(Args) -> %% Filter all the flags (i.e. strings of form key=value) from the %% command line arguments. What's left will be the commands to run. - filter_flags(NonOptArgs, []); + unabbreviate_command_names(filter_flags(NonOptArgs, [])); {error, {Reason, Data}} -> ?ERROR("Error: ~s ~p~n~n", [Reason, Data]), @@ -222,3 +222,67 @@ filter_flags([Item | Rest], Commands) -> ?CONSOLE("Ignoring command line argument: ~p\n", [Other]), filter_flags(Rest, Commands) end. + +command_names() -> + ["build-plt", "check-deps", "check-plt", "clean", "compile", "create", + "create-app", "create-node", "ct", "delete-deps", "dialyze", "doc", + "eunit", "generate", "get-deps", "help", "list-templates", "update-deps", + "version", "xref"]. + +unabbreviate_command_names([]) -> + []; +unabbreviate_command_names([Command | Commands]) -> + case get_command_name_candidates(Command) of + [] -> + %% let the rest of the code detect that the command doesn't exist + %% (this would perhaps be a good place to fail) + [Command | unabbreviate_command_names(Commands)]; + [FullCommand] -> + [FullCommand | unabbreviate_command_names(Commands)]; + Candidates -> + ?ABORT("Found more than one match for abbreviated command name " + " '~s',~nplease be more specific. Possible candidates:~n" + " ~s~n", + [Command, string:join(Candidates, ", ")]) + end. + +get_command_name_candidates(Command) -> + %% Get the command names which match the given (abbreviated) command name. + %% * "c" matches commands like compile, clean and create-app + %% * "create" matches command create only, since it's unique + %% * "create-" matches commands starting with create- + %% * "c-a" matches create-app + %% * "create-a" matches create-app + %% * "c-app" matches create-app + Candidates = [Candidate || Candidate <- command_names(), + is_command_name_candidate(Command, Candidate)], + %% Is there a complete match? If so return only that, return a + %% list of candidates otherwise + case Candidates of + [Command] = Match -> Match; + _ -> Candidates + end. + +is_command_name_candidate(Command, Candidate) -> + lists:prefix(Command, Candidate) + orelse is_command_name_sub_word_candidate(Command, Candidate). + +is_command_name_sub_word_candidate(Command, Candidate) -> + %% Allow for parts of commands to be abbreviated, i.e. create-app + %% can be shortened to "create-a", "c-a" or "c-app" (but not + %% "create-" since that would be ambiguous). + CommandSubWords = re:split(Command, "-", [{return, list}]), + CandidateSubWords = re:split(Candidate, "-", [{return, list}]), + is_command_name_sub_word_candidate_aux(CommandSubWords, CandidateSubWords). + +is_command_name_sub_word_candidate_aux([CmdSW | CmdSWs], [CandSW | CandSWs]) -> + case lists:prefix(CmdSW, CandSW) of + true -> + is_command_name_sub_word_candidate_aux(CmdSWs, CandSWs); + false -> + false + end; +is_command_name_sub_word_candidate_aux([], []) -> + true; +is_command_name_sub_word_candidate_aux(_CmdSWs, _CandSWs) -> + false. -- cgit v1.1 From a8066b35a9d0bdbc360e57db829d8fab65a70d15 Mon Sep 17 00:00:00 2001 From: Dave Smith Date: Tue, 25 Jan 2011 21:29:08 -0700 Subject: Use an order independent method for exact matches when looking for command candidates --- src/rebar.erl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/rebar.erl') diff --git a/src/rebar.erl b/src/rebar.erl index 69725b3..5121f59 100644 --- a/src/rebar.erl +++ b/src/rebar.erl @@ -258,9 +258,9 @@ get_command_name_candidates(Command) -> is_command_name_candidate(Command, Candidate)], %% Is there a complete match? If so return only that, return a %% list of candidates otherwise - case Candidates of - [Command] = Match -> Match; - _ -> Candidates + case lists:member(Command, Candidates) of + true -> [Command]; + false -> Candidates end. is_command_name_candidate(Command, Candidate) -> -- cgit v1.1 From 3fd3bfc89a614728c21360ecb91d8a5029f7d0b3 Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Thu, 27 Jan 2011 15:52:48 +0100 Subject: Fix circular dependency --- src/rebar.erl | 50 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 44 insertions(+), 6 deletions(-) (limited to 'src/rebar.erl') diff --git a/src/rebar.erl b/src/rebar.erl index 5121f59..1b502b1 100644 --- a/src/rebar.erl +++ b/src/rebar.erl @@ -38,7 +38,7 @@ %% ==================================================================== main(Args) -> - case catch(rebar_core:run(Args)) of + case catch(run(Args)) of ok -> ok; {error, failed} -> @@ -49,6 +49,48 @@ main(Args) -> halt(1) end. +%% ==================================================================== +%% Internal functions +%% ==================================================================== + +run(RawArgs) -> + %% Pre-load the rebar app so that we get default configuration + ok = application:load(rebar), + %% Parse out command line arguments -- what's left is a list of commands to + %% run -- and start running commands + run_aux(parse_args(RawArgs)). + +run_aux(["help"]) -> + help(), + ok; +run_aux(["version"]) -> + %% Display vsn and build time info + version(), + ok; +run_aux(Commands) -> + %% Make sure crypto is running + ok = crypto:start(), + + %% Initialize logging system + rebar_log:init(), + + %% Convert command strings to atoms + CommandAtoms = [list_to_atom(C) || C <- Commands], + + %% Determine the location of the rebar executable; important for pulling + %% resources out of the escript + rebar_config:set_global(escript, filename:absname(escript:script_name())), + ?DEBUG("Rebar location: ~p\n", [rebar_config:get_global(escript, undefined)]), + + %% Note the top-level directory for reference + rebar_config:set_global(base_dir, filename:absname(rebar_utils:get_cwd())), + + %% Keep track of how many operations we do, so we can detect bad commands + erlang:put(operations, 0), + + %% Process each command, resetting any state between each one + rebar_core:process_commands(CommandAtoms). + %% %% print help/usage string %% @@ -94,7 +136,7 @@ parse_args(Args) -> {error, {Reason, Data}} -> ?ERROR("Error: ~s ~p~n~n", [Reason, Data]), - rebar:help(), + help(), halt(1) end. @@ -106,10 +148,6 @@ version() -> ?CONSOLE("rebar version: ~s date: ~s vcs: ~s\n", [Vsn, ?BUILD_TIME, ?VCS_INFO]). -%% ==================================================================== -%% Internal functions -%% ==================================================================== - %% %% set global flag based on getopt option boolean value %% -- cgit v1.1 From 5298e93a180e6db87a33f26eb6a2db06e8065dc7 Mon Sep 17 00:00:00 2001 From: joewilliams Date: Thu, 27 Jan 2011 18:15:25 +0100 Subject: Add 'generate-upgrade' command To support OTP release upgrades I have added support for building upgrade packages. Support for this is included in the rebar_upgrade module, specifically generate_upgrade/2. It requires one variable to be set on the command line 'previous_release' which is the absolute path or relative path from 'rel/' to the previous release one is upgrading from. Running an upgrade will create the needed files, including a relup and result in a tarball containing the upgrade being written to 'rel/'. When done it cleans up the temporary files systools created. Usage: $ rebar generate-upgrade previous_release=/path/to/old/version This also includes a dummy application that can be used to test upgrades as well as an example. Special thanks to Daniel Reverri, Jesper Louis Andersen and Richard Jones for comments and patches. --- src/rebar.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/rebar.erl') diff --git a/src/rebar.erl b/src/rebar.erl index 1b502b1..6c99ab5 100644 --- a/src/rebar.erl +++ b/src/rebar.erl @@ -211,6 +211,8 @@ delete-deps Delete fetched dependencies generate [dump_spec=0/1] Build release with reltool +generate-upgrade [previous_release=path] Build an upgrade package + eunit [suite=foo] Run eunit [test/foo_tests.erl] tests ct [suite=] [case=] Run common_test suites in ./test @@ -264,8 +266,8 @@ filter_flags([Item | Rest], Commands) -> command_names() -> ["build-plt", "check-deps", "check-plt", "clean", "compile", "create", "create-app", "create-node", "ct", "delete-deps", "dialyze", "doc", - "eunit", "generate", "get-deps", "help", "list-templates", "update-deps", - "version", "xref"]. + "eunit", "generate", "generate-upgrade", "get-deps", "help", + "list-templates", "update-deps", "version", "xref"]. unabbreviate_command_names([]) -> []; -- cgit v1.1 From 8f5bfea9c479d05298780ec51e1ab3b0f8af42cc Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Fri, 28 Jan 2011 19:05:20 +0100 Subject: Fix help: previous_release is mandatory --- src/rebar.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/rebar.erl') diff --git a/src/rebar.erl b/src/rebar.erl index 6c99ab5..e84fb44 100644 --- a/src/rebar.erl +++ b/src/rebar.erl @@ -211,7 +211,7 @@ delete-deps Delete fetched dependencies generate [dump_spec=0/1] Build release with reltool -generate-upgrade [previous_release=path] Build an upgrade package +generate-upgrade previous_release=path Build an upgrade package eunit [suite=foo] Run eunit [test/foo_tests.erl] tests ct [suite=] [case=] Run common_test suites in ./test -- cgit v1.1 From 7ac3a5aa9b03e8e1a1c5b3d8fcdfeedd6819c074 Mon Sep 17 00:00:00 2001 From: Tim Watson Date: Wed, 26 Jan 2011 02:15:27 +0000 Subject: Move BUILD_TIME and VCS_INFO macros I have moved these macros from rebar_core.erl to rebar.erl in order to prevent eunit tests from failing (which they currently are). --- src/rebar.erl | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/rebar.erl') diff --git a/src/rebar.erl b/src/rebar.erl index e84fb44..554278e 100644 --- a/src/rebar.erl +++ b/src/rebar.erl @@ -33,6 +33,14 @@ -include("rebar.hrl"). +-ifndef(BUILD_TIME). +-define(BUILD_TIME, "undefined"). +-endif. + +-ifndef(VCS_INFO). +-define(VCS_INFO, "undefined"). +-endif. + %% ==================================================================== %% Public API %% ==================================================================== -- cgit v1.1 From c466076ffb5a1ea4c00d49fefff0dcfbceb58236 Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Mon, 31 Jan 2011 17:43:31 +0100 Subject: Clean up emacs file local variables --- src/rebar.erl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/rebar.erl') diff --git a/src/rebar.erl b/src/rebar.erl index 554278e..d9e302a 100644 --- a/src/rebar.erl +++ b/src/rebar.erl @@ -1,4 +1,4 @@ -%% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ts=4 sw=4 et %% ------------------------------------------------------------------- %% -- cgit v1.1 From 63de05d914f3c2bef6dcfc6cf966400d93c9c80d Mon Sep 17 00:00:00 2001 From: Tuncer Ayaz Date: Fri, 28 Jan 2011 16:08:27 +0100 Subject: Clean up code --- src/rebar.erl | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'src/rebar.erl') diff --git a/src/rebar.erl b/src/rebar.erl index d9e302a..22f2080 100644 --- a/src/rebar.erl +++ b/src/rebar.erl @@ -52,7 +52,8 @@ main(Args) -> {error, failed} -> halt(1); Error -> - %% Nothing should percolate up from rebar_core; dump this error to console + %% Nothing should percolate up from rebar_core; + %% Dump this error to console io:format("Uncaught error in rebar_core: ~p\n", [Error]), halt(1) end. @@ -88,7 +89,8 @@ run_aux(Commands) -> %% Determine the location of the rebar executable; important for pulling %% resources out of the escript rebar_config:set_global(escript, filename:absname(escript:script_name())), - ?DEBUG("Rebar location: ~p\n", [rebar_config:get_global(escript, undefined)]), + ?DEBUG("Rebar location: ~p\n", + [rebar_config:get_global(escript, undefined)]), %% Note the top-level directory for reference rebar_config:set_global(base_dir, filename:absname(rebar_utils:get_cwd())), @@ -153,7 +155,8 @@ parse_args(Args) -> %% version() -> {ok, Vsn} = application:get_key(rebar, vsn), - ?CONSOLE("rebar version: ~s date: ~s vcs: ~s\n", [Vsn, ?BUILD_TIME, ?VCS_INFO]). + ?CONSOLE("rebar version: ~s date: ~s vcs: ~s\n", + [Vsn, ?BUILD_TIME, ?VCS_INFO]). %% @@ -239,17 +242,17 @@ version Show version information option_spec_list() -> Jobs = rebar_config:get_jobs(), JobsHelp = io_lib:format( - "Number of concurrent workers a command may use. Default: ~B", - [Jobs]), + "Number of concurrent workers a command may use. Default: ~B", + [Jobs]), [ %% {Name, ShortOpt, LongOpt, ArgSpec, HelpMsg} - {help, $h, "help", undefined, "Show the program options"}, - {commands, $c, "commands", undefined, "Show available commands"}, - {verbose, $v, "verbose", undefined, "Be verbose about what gets done"}, - {version, $V, "version", undefined, "Show version information"}, - {force, $f, "force", undefined, "Force"}, - {jobs, $j, "jobs", integer, JobsHelp}, - {config, $C, "config", string, "Rebar config file to use"} + {help, $h, "help", undefined, "Show the program options"}, + {commands, $c, "commands", undefined, "Show available commands"}, + {verbose, $v, "verbose", undefined, "Be verbose about what gets done"}, + {version, $V, "version", undefined, "Show version information"}, + {force, $f, "force", undefined, "Force"}, + {jobs, $j, "jobs", integer, JobsHelp}, + {config, $C, "config", string, "Rebar config file to use"} ]. %% @@ -303,7 +306,7 @@ get_command_name_candidates(Command) -> %% * "create-a" matches create-app %% * "c-app" matches create-app Candidates = [Candidate || Candidate <- command_names(), - is_command_name_candidate(Command, Candidate)], + is_command_name_candidate(Command, Candidate)], %% Is there a complete match? If so return only that, return a %% list of candidates otherwise case lists:member(Command, Candidates) of -- cgit v1.1 From 11bf6b4aab33c273f0db94bded8763af68cbc167 Mon Sep 17 00:00:00 2001 From: joewilliams Date: Thu, 10 Feb 2011 13:27:29 -0800 Subject: Add 'generate-appups' command To further support OTP releases I have added support for generating application appup files. These include instructions that systools uses to generate a relup file which contains the low level instructions needed to perform a hot code upgrade. My goal with this module is to produce "good enough" appup files or at least a skeleton to help one get started with something more complex. If an appup file already exists for an application this command will not attempt to create a new one. Usage: $ rebar generate-appups previous_release=/path/to/old/version Generally this command will be run just before 'generate-upgrade'. --- src/rebar.erl | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/rebar.erl') diff --git a/src/rebar.erl b/src/rebar.erl index 22f2080..b83a1f7 100644 --- a/src/rebar.erl +++ b/src/rebar.erl @@ -224,6 +224,8 @@ generate [dump_spec=0/1] Build release with reltool generate-upgrade previous_release=path Build an upgrade package +generate-appups previous_release=path Generate appup files + eunit [suite=foo] Run eunit [test/foo_tests.erl] tests ct [suite=] [case=] Run common_test suites in ./test @@ -277,8 +279,8 @@ filter_flags([Item | Rest], Commands) -> command_names() -> ["build-plt", "check-deps", "check-plt", "clean", "compile", "create", "create-app", "create-node", "ct", "delete-deps", "dialyze", "doc", - "eunit", "generate", "generate-upgrade", "get-deps", "help", - "list-templates", "update-deps", "version", "xref"]. + "eunit", "generate", "generate-appups", "generate-upgrade", "get-deps", + "help", "list-templates", "update-deps", "version", "xref"]. unabbreviate_command_names([]) -> []; -- cgit v1.1