diff options
51 files changed, 1141 insertions, 218 deletions
diff --git a/.travis.yml b/.travis.yml index f5e3d99..191d337 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,8 @@ language: erlang otp_release: + - R16B - R15B01 - R15B - R14B04 - R14B03 -script: "make debug xref clean all deps test" +script: "make travis" @@ -24,7 +24,7 @@ dialyzer: dialyzer_warnings @diff -U0 dialyzer_reference dialyzer_warnings dialyzer_warnings: - -@dialyzer -q -n ebin -Wunmatched_returns -Werror_handling \ + -@dialyzer -q -nn -n ebin -Wunmatched_returns -Werror_handling \ -Wrace_conditions > dialyzer_warnings binary: VSN = $(shell ./rebar -V) @@ -38,4 +38,6 @@ deps: test: @$(REBAR) eunit - @$(RETEST) inttest + @$(RETEST) -v inttest + +travis: clean debug xref clean all deps test diff --git a/dialyzer_reference b/dialyzer_reference index a50bb88..fccada2 100644 --- a/dialyzer_reference +++ b/dialyzer_reference @@ -1,3 +1,3 @@ -rebar_eunit.erl:351: Call to missing or unexported function eunit_test:function_wrapper/2 +rebar_eunit.erl:388: Call to missing or unexported function eunit_test:function_wrapper/2 rebar_utils.erl:162: Call to missing or unexported function escript:foldl/3 diff --git a/inttest/depplugins/depplugins_rt.erl b/inttest/depplugins/depplugins_rt.erl new file mode 100644 index 0000000..7b106eb --- /dev/null +++ b/inttest/depplugins/depplugins_rt.erl @@ -0,0 +1,50 @@ +%%% @doc Plugin handling test +%%% +%%% This test checks if plugins are loaded correctly. +%%% +%%% It has three applications: +%%% <ol> +%%% <li>fish. top-level module, has one dependency: `dependsonplugin'.</li> +%%% <li>dependsonplugin. This depends on some pre-compile actions by the +%%% plugin. In the test the plugin creates a file `pre.compile' in the +%%% top-level folder of this application.</li> +%%% <li>testplugin. This is a plugin application which creates the file.</li> +%%% </ol> + +-module(depplugins_rt). +-compile(export_all). + +-include_lib("eunit/include/eunit.hrl"). + +files() -> + [ + {copy, "../../rebar", "rebar"}, + {copy, "rebar.config", "rebar.config"}, + {create, "ebin/fish.app", app(fish, [])}, + + {create, "deps/dependsonplugin/ebin/dependsonplugin.app", + app(dependsonplugin, [])}, + {copy, "rebar_dependsonplugin.config", + "deps/dependsonplugin/rebar.config"}, + {copy, "testplugin_mod.erl", + "deps/testplugin/plugins/testplugin_mod.erl"}, + {create, "deps/testplugin/ebin/testplugin.app", + app(testplugin, [])} + ]. + +run(_Dir) -> + ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])), + ?assertEqual(true, filelib:is_regular("deps/dependsonplugin/pre.compile")), + ok. + +%% +%% Generate the contents of a simple .app file +%% +app(Name, Modules) -> + App = {application, Name, + [{description, atom_to_list(Name)}, + {vsn, "1"}, + {modules, Modules}, + {registered, []}, + {applications, [kernel, stdlib]}]}, + io_lib:format("~p.\n", [App]). diff --git a/inttest/depplugins/rebar.config b/inttest/depplugins/rebar.config new file mode 100644 index 0000000..3a2e34e --- /dev/null +++ b/inttest/depplugins/rebar.config @@ -0,0 +1 @@ +{deps, [dependsonplugin]}. diff --git a/inttest/depplugins/rebar_dependsonplugin.config b/inttest/depplugins/rebar_dependsonplugin.config new file mode 100644 index 0000000..df36213 --- /dev/null +++ b/inttest/depplugins/rebar_dependsonplugin.config @@ -0,0 +1,2 @@ +{deps, [testplugin]}. +{plugins, [testplugin_mod]}. diff --git a/inttest/depplugins/testplugin_mod.erl b/inttest/depplugins/testplugin_mod.erl new file mode 100644 index 0000000..055bbc7 --- /dev/null +++ b/inttest/depplugins/testplugin_mod.erl @@ -0,0 +1,6 @@ +-module(testplugin_mod). +-compile(export_all). + +pre_compile(Config, _) -> + ok = file:write_file("pre.compile", <<"Yadda!">>), + rebar_log:log(info, "Wrote ~p/pre.compile~n", [rebar_utils:get_cwd()]). diff --git a/inttest/tdeps1/a.rebar.config b/inttest/tdeps1/a.rebar.config index 01609ee..991ea5a 100644 --- a/inttest/tdeps1/a.rebar.config +++ b/inttest/tdeps1/a.rebar.config @@ -1 +1 @@ -{deps, [{b, "1", {hg, "../repo/b", "tip"}}]}. +{deps, [{b, "1", {git, "../repo/b"}}]}. diff --git a/inttest/tdeps1/b.rebar.config b/inttest/tdeps1/b.rebar.config index 59c8987..ffbd0db 100644 --- a/inttest/tdeps1/b.rebar.config +++ b/inttest/tdeps1/b.rebar.config @@ -1 +1 @@ -{deps, [{c, "1", {hg, "../repo/c", "tip"}}]}. +{deps, [{c, "1", {git, "../repo/c"}}]}. diff --git a/inttest/tdeps1/tdeps1_rt.erl b/inttest/tdeps1/tdeps1_rt.erl index 43184c6..3de1a2b 100644 --- a/inttest/tdeps1/tdeps1_rt.erl +++ b/inttest/tdeps1/tdeps1_rt.erl @@ -23,12 +23,23 @@ files() -> {copy, "c.hrl", "repo/c/include/c.hrl"} ]. +apply_cmds([], _Params) -> + ok; +apply_cmds([Cmd | Rest], Params) -> + io:format("Running: ~s (~p)\n", [Cmd, Params]), + {ok, _} = retest_sh:run(Cmd, Params), + apply_cmds(Rest, Params). + run(_Dir) -> - %% Initialize the b/c apps as mercurial repos so that dependencies pull + %% Initialize the b/c apps as git repos so that dependencies pull %% properly - HgCmd = "/bin/sh -c \"hg init && hg add && hg commit -m 'Initial commit'\"", - {ok, _} = retest_sh:run(HgCmd, [{dir, "repo/b"}]), - {ok, _} = retest_sh:run(HgCmd, [{dir, "repo/c"}]), + GitCmds = ["git init", + "git add -A", + "git config user.email 'tdeps@example.com'", + "git config user.name 'tdeps'", + "git commit -a -m 'Initial Commit'"], + apply_cmds(GitCmds, [{dir, "repo/b"}]), + apply_cmds(GitCmds, [{dir, "repo/c"}]), {ok, _} = retest_sh:run("./rebar get-deps compile", []), diff --git a/inttest/tdeps2/a.rebar.config b/inttest/tdeps2/a.rebar.config index 01609ee..991ea5a 100644 --- a/inttest/tdeps2/a.rebar.config +++ b/inttest/tdeps2/a.rebar.config @@ -1 +1 @@ -{deps, [{b, "1", {hg, "../repo/b", "tip"}}]}. +{deps, [{b, "1", {git, "../repo/b"}}]}. diff --git a/inttest/tdeps2/b.rebar.config b/inttest/tdeps2/b.rebar.config index 59c8987..ffbd0db 100644 --- a/inttest/tdeps2/b.rebar.config +++ b/inttest/tdeps2/b.rebar.config @@ -1 +1 @@ -{deps, [{c, "1", {hg, "../repo/c", "tip"}}]}. +{deps, [{c, "1", {git, "../repo/c"}}]}. diff --git a/inttest/tdeps2/tdeps2_rt.erl b/inttest/tdeps2/tdeps2_rt.erl index bad546e..987567e 100644 --- a/inttest/tdeps2/tdeps2_rt.erl +++ b/inttest/tdeps2/tdeps2_rt.erl @@ -31,12 +31,23 @@ files() -> {copy, "c.hrl", "repo/c/include/c.hrl"} ]. +apply_cmds([], _Params) -> + ok; +apply_cmds([Cmd | Rest], Params) -> + io:format("Running: ~s (~p)\n", [Cmd, Params]), + {ok, _} = retest_sh:run(Cmd, Params), + apply_cmds(Rest, Params). + run(_Dir) -> - %% Initialize the b/c apps as mercurial repos so that dependencies pull + %% Initialize the b/c apps as git repos so that dependencies pull %% properly - HgCmd = "/bin/sh -c \"hg init && hg add && hg commit -m 'Initial commit'\"", - {ok, _} = retest_sh:run(HgCmd, [{dir, "repo/b"}]), - {ok, _} = retest_sh:run(HgCmd, [{dir, "repo/c"}]), + GitCmds = ["git init", + "git add -A", + "git config user.email 'tdeps@example.com'", + "git config user.name 'tdeps'", + "git commit -a -m 'Initial Commit'"], + ok = apply_cmds(GitCmds, [{dir, "repo/b"}]), + ok = apply_cmds(GitCmds, [{dir, "repo/c"}]), {ok, _} = retest_sh:run("./rebar -v get-deps compile", []), ok. diff --git a/priv/templates/simplenode.erl.script b/priv/templates/simplenode.erl.script index 6f65e3f..f4c63af 100644 --- a/priv/templates/simplenode.erl.script +++ b/priv/templates/simplenode.erl.script @@ -1,16 +1,26 @@ #!/bin/sh -## This script replaces the default "erl" in erts-VSN/bin. This is necessary -## as escript depends on erl and in turn, erl depends on having access to a -## bootscript (start.boot). Note that this script is ONLY invoked as a side-effect -## of running escript -- the embedded node bypasses erl and uses erlexec directly -## (as it should). +# /bin/sh on Solaris is not a POSIX compatible shell, but /usr/bin/ksh is. +if [ `uname -s` = 'SunOS' -a "${POSIX_SHELL}" != "true" ]; then + POSIX_SHELL="true" + export POSIX_SHELL + exec /usr/bin/ksh $0 "$@" +fi + +# clear it so if we invoke other scripts, they run as ksh as well +unset POSIX_SHELL + +## This script replaces the default "erl" in erts-VSN/bin. This is +## necessary as escript depends on erl and in turn, erl depends on +## having access to a bootscript (start.boot). Note that this script +## is ONLY invoked as a side-effect of running escript -- the embedded +## node bypasses erl and uses erlexec directly (as it should). ## -## Note that this script makes the assumption that there is a start_clean.boot -## file available in $ROOTDIR/release/VSN. +## Note that this script makes the assumption that there is a +## start_clean.boot file available in $ROOTDIR/release/VSN. # Determine the abspath of where this script is executing from. -ERTS_BIN_DIR=$(cd ${0%/*} && pwd) +ERTS_BIN_DIR=$(cd ${0%/*} && pwd -P) # Now determine the root directory -- this script runs from erts-VSN/bin, # so we simply need to strip off two dirs from the end of the ERTS_BIN_DIR diff --git a/priv/templates/simplenode.nodetool b/priv/templates/simplenode.nodetool index eb08fa4..ce06c6a 100644..100755 --- a/priv/templates/simplenode.nodetool +++ b/priv/templates/simplenode.nodetool @@ -1,3 +1,4 @@ +#!/usr/bin/env escript %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ft=erlang ts=4 sw=4 et %% ------------------------------------------------------------------- @@ -5,25 +6,51 @@ %% nodetool: Helper Script for interacting with live nodes %% %% ------------------------------------------------------------------- - main(Args) -> ok = start_epmd(), %% Extract the args {RestArgs, TargetNode} = process_args(Args, [], undefined), + %% any commands that don't need a running node + case RestArgs of + ["chkconfig", File] -> + case file:consult(File) of + {ok, _} -> + io:format("ok\n"), + halt(0); + {error, {Line, Mod, Term}} -> + io:format(standard_error, ["Error on line ", + file:format_error({Line, Mod, Term}), "\n"], []), + halt(1); + {error, R} -> + io:format(standard_error, ["Error reading config file: ", + file:format_error(R), "\n"], []), + halt(1) + end; + _ -> + ok + end, + %% See if the node is currently running -- if it's not, we'll bail - case {net_kernel:hidden_connect_node(TargetNode), net_adm:ping(TargetNode)} of + case {net_kernel:hidden_connect_node(TargetNode), + net_adm:ping(TargetNode)} of {true, pong} -> ok; + {false,pong} -> + io:format("Failed to connect to node ~p .\n", [TargetNode]), + halt(1); {_, pang} -> io:format("Node ~p not responding to pings.\n", [TargetNode]), halt(1) end, case RestArgs of + ["getpid"] -> + io:format("~p\n", + [list_to_integer(rpc:call(TargetNode, os, getpid, []))]); ["ping"] -> - %% If we got this far, the node already responsed to a ping, so just dump - %% a "pong" + %% If we got this far, the node already responsed to a + %% ping, so just dump a "pong" io:format("pong\n"); ["stop"] -> io:format("~p\n", [rpc:call(TargetNode, init, stop, [], 60000)]); @@ -32,7 +59,9 @@ main(Args) -> ["reboot"] -> io:format("~p\n", [rpc:call(TargetNode, init, reboot, [], 60000)]); ["rpc", Module, Function | RpcArgs] -> - case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), + case rpc:call(TargetNode, + list_to_atom(Module), + list_to_atom(Function), [RpcArgs], 60000) of ok -> ok; @@ -42,8 +71,23 @@ main(Args) -> _ -> halt(1) end; + ["rpc_infinity", Module, Function | RpcArgs] -> + case rpc:call(TargetNode, + list_to_atom(Module), + list_to_atom(Function), + [RpcArgs], infinity) of + ok -> + ok; + {badrpc, Reason} -> + io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), + halt(1); + _ -> + halt(1) + end; ["rpcterms", Module, Function, ArgsAsString] -> - case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), + case rpc:call(TargetNode, + list_to_atom(Module), + list_to_atom(Function), consult(ArgsAsString), 60000) of {badrpc, Reason} -> io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), @@ -53,7 +97,7 @@ main(Args) -> end; Other -> io:format("Other: ~p\n", [Other]), - io:format("Usage: nodetool {ping|stop|restart|reboot}\n") + io:format("Usage: nodetool {chkconfig|getpid|ping|stop|restart|reboot|rpc|rpc_infinity|rpcterms}\n") end, net_kernel:stop(). diff --git a/priv/templates/simplenode.reltool.config b/priv/templates/simplenode.reltool.config index b580c2a..bac7270 100644 --- a/priv/templates/simplenode.reltool.config +++ b/priv/templates/simplenode.reltool.config @@ -1,3 +1,5 @@ +%% -*- mode: erlang -*- +%% ex: ft=erlang {sys, [ {lib_dirs, []}, {erts, [{mod_cond, derived}, {app_file, strip}]}, @@ -17,9 +19,9 @@ {boot_rel, "{{nodeid}}"}, {profile, embedded}, {incl_cond, derived}, - {mod_cond, derived}, {excl_archive_filters, [".*"]}, %% Do not archive built libs - {excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)", + {excl_sys_filters, ["^bin/(?!start_clean.boot)", + "^erts.*/bin/(dialyzer|typer)", "^erts.*/(doc|info|include|lib|man|src)"]}, {excl_app_filters, ["\.gitignore"]}, {app, {{nodeid}}, [{mod_cond, app}, {incl_cond, include}]} @@ -31,6 +33,8 @@ {mkdir, "log/sasl"}, {copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"}, {copy, "files/nodetool", "\{\{erts_vsn\}\}/bin/nodetool"}, + {copy, "{{nodeid}}/bin/start_clean.boot", + "\{\{erts_vsn\}\}/bin/start_clean.boot"}, {copy, "files/{{nodeid}}", "bin/{{nodeid}}"}, {copy, "files/{{nodeid}}.cmd", "bin/{{nodeid}}.cmd"}, {copy, "files/start_erl.cmd", "bin/start_erl.cmd"}, diff --git a/priv/templates/simplenode.runner b/priv/templates/simplenode.runner index 43d90bc..c2ef258 100755 --- a/priv/templates/simplenode.runner +++ b/priv/templates/simplenode.runner @@ -2,7 +2,20 @@ # -*- tab-width:4;indent-tabs-mode:nil -*- # ex: ts=4 sw=4 et -RUNNER_SCRIPT_DIR=$(cd ${0%/*} && pwd) +# /bin/sh on Solaris is not a POSIX compatible shell, but /usr/bin/ksh is. +if [ `uname -s` = 'SunOS' -a "${POSIX_SHELL}" != "true" ]; then + POSIX_SHELL="true" + export POSIX_SHELL + # To support 'whoami' add /usr/ucb to path + PATH=/usr/ucb:$PATH + export PATH + exec /usr/bin/ksh $0 "$@" +fi + +# clear it so if we invoke other scripts, they run as ksh +unset POSIX_SHELL + +RUNNER_SCRIPT_DIR=$(cd ${0%/*} && pwd -P) CALLER_DIR=$PWD @@ -11,10 +24,17 @@ RUNNER_ETC_DIR=$RUNNER_BASE_DIR/etc # Note the trailing slash on $PIPE_DIR/ PIPE_DIR=/tmp/$RUNNER_BASE_DIR/ RUNNER_USER= +WHOAMI=$(whoami) # Make sure this script is running as the appropriate user -if [ ! -z "$RUNNER_USER" ] && [ `whoami` != "$RUNNER_USER" ]; then - exec sudo -u $RUNNER_USER -i $0 $@ +if ([ "$RUNNER_USER" ] && [ "x$WHOAMI" != "x$RUNNER_USER" ]); then + type sudo > /dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "sudo doesn't appear to be installed and your EUID isn't $RUNNER_USER" 1>&2 + exit 1 + fi + echo "Attempting to restart script through sudo -H -u $RUNNER_USER" >&2 + exec sudo -H -u $RUNNER_USER -i $RUNNER_SCRIPT_DIR/$RUNNER_SCRIPT $@ fi # Identify the script name @@ -25,7 +45,8 @@ START_ERL=`cat $RUNNER_BASE_DIR/releases/start_erl.data` ERTS_VSN=${START_ERL% *} APP_VSN=${START_ERL#* } -# Use $CWD/vm.args if exists, otherwise releases/APP_VSN/vm.args, or else etc/vm.args +# Use $CWD/vm.args if exists, otherwise releases/APP_VSN/vm.args, or +# else etc/vm.args if [ -e "$CALLER_DIR/vm.args" ]; then VMARGS_PATH=$CALLER_DIR/vm.args USE_DIR=$CALLER_DIR @@ -54,7 +75,7 @@ else fi # Extract the target node name from node.args -NAME_ARG=`egrep '^-s?name' $VMARGS_PATH` +NAME_ARG=`egrep '^\-s?name' $VMARGS_PATH` if [ -z "$NAME_ARG" ]; then echo "vm.args needs to have either -name or -sname parameter." exit 1 @@ -64,12 +85,13 @@ fi REMSH_TYPE=`echo $NAME_ARG | awk '{print $1}'` REMSH_NAME=`echo $NAME_ARG | awk '{print $2}'` -# Note the `date +%s`, used to allow multiple remsh to the same node transparently +# Note the `date +%s`, used to allow multiple remsh to the same node +# transparently REMSH_NAME_ARG="$REMSH_TYPE remsh`date +%s`@`echo $REMSH_NAME | awk -F@ '{print $2}'`" REMSH_REMSH_ARG="-remsh $REMSH_NAME" # Extract the target cookie -COOKIE_ARG=`grep '^-setcookie' $VMARGS_PATH` +COOKIE_ARG=`grep '^\-setcookie' $VMARGS_PATH` if [ -z "$COOKIE_ARG" ]; then echo "vm.args needs to have a -setcookie parameter." exit 1 @@ -81,7 +103,6 @@ cd $USE_DIR # Make sure log directory exists mkdir -p $USE_DIR/log - # Add ERTS bin dir to our path ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin @@ -91,11 +112,35 @@ NODETOOL="$ERTS_PATH/escript $ERTS_PATH/nodetool $NAME_ARG $COOKIE_ARG" # Setup remote shell command to control node REMSH="$ERTS_PATH/erl $REMSH_NAME_ARG $REMSH_REMSH_ARG $COOKIE_ARG" +# Common functions + +# Ping node without allowing nodetool to take stdin +ping_node() { + $NODETOOL ping < /dev/null +} + +# Set the PID global variable, return 1 on error +get_pid() { + PID=`$NODETOOL getpid < /dev/null` + ES=$? + if [ "$ES" -ne 0 ]; then + echo "Node is not running!" + return 1 + fi + + # don't allow empty or init pid's + if [ -z $PID ] || [ "$PID" -le 1 ]; then + return 1 + fi + + return 0 +} + # Check the first argument for instructions case "$1" in start|start_boot) # Make sure there is not already a node running - RES=`$NODETOOL ping` + RES=`ping_node` if [ "$RES" = "pong" ]; then echo "Node is already running!" exit 1 @@ -122,27 +167,28 @@ case "$1" in stop) # Wait for the node to completely stop... case `uname -s` in - Linux|Darwin|FreeBSD|DragonFly|NetBSD|OpenBSD) - # PID COMMAND - PID=`ps ax -o pid= -o command=|\ - grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $1}'` - ;; - SunOS) - # PID COMMAND - PID=`ps -ef -o pid= -o args=|\ - grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $1}'` - ;; - CYGWIN*) - # UID PID PPID TTY STIME COMMAND - PID=`ps -efW|grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $2}'` - ;; + Darwin) + # Make sure we explicitly set this because iTerm.app doesn't for + # some reason. + COMMAND_MODE=unix2003 esac + + # Get the PID from nodetool + get_pid + GPR=$? + if [ "$GPR" -ne 0 ] || [ -z $PID ]; then + exit $GPR + fi + + # Tell nodetool to initiate a stop $NODETOOL stop ES=$? if [ "$ES" -ne 0 ]; then exit $ES fi - while `kill -0 $PID 2>/dev/null`; + + # Wait for the node to completely stop... + while `kill -s 0 $PID 2>/dev/null` do sleep 1 done @@ -168,7 +214,7 @@ case "$1" in ping) ## See if the VM is alive - $NODETOOL ping + ping_node ES=$? if [ "$ES" -ne 0 ]; then exit $ES @@ -176,8 +222,8 @@ case "$1" in ;; attach) - # Make sure a node IS running - RES=`$NODETOOL ping` + # Make sure a node is running + ping_node ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" @@ -189,8 +235,8 @@ case "$1" in ;; remote_console) - # Make sure a node IS running - RES=`$NODETOOL ping` + # Make sure a node is running + ping_node ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" @@ -210,7 +256,7 @@ case "$1" in fi # Make sure a node IS running - RES=`$NODETOOL ping` + ping_node ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" @@ -283,8 +329,17 @@ case "$1" in # Start the VM exec $CMD -- ${1+"$@"} ;; + getpid) + # Get the PID from nodetool + get_pid + ES=$? + if [ "$ES" -ne 0 ] || [ -z $PID ]; then + exit $ES + fi + echo $PID + ;; *) - echo "Usage: $SCRIPT {start|start_boot <file>|foreground|stop|restart|reboot|ping|console|console_clean|console_boot <file>|attach|remote_console|upgrade}" + echo "Usage: $SCRIPT {start|start_boot <file>|foreground|stop|restart|reboot|ping|console|getpid|console_clean|console_boot <file>|attach|remote_console|upgrade}" exit 1 ;; esac diff --git a/priv/templates/simplenode.windows.runner.cmd b/priv/templates/simplenode.windows.runner.cmd index 16da5b9..d45f438 100644 --- a/priv/templates/simplenode.windows.runner.cmd +++ b/priv/templates/simplenode.windows.runner.cmd @@ -30,6 +30,7 @@ @set epmd="%erts_bin%\epmd.exe" @set escript="%erts_bin%\escript.exe" @set werl="%erts_bin%\werl.exe" +@set nodetool="%erts_bin%\nodetool" @if "%1"=="usage" @goto usage @if "%1"=="install" @goto install @@ -38,13 +39,14 @@ @if "%1"=="stop" @goto stop @if "%1"=="restart" @call :stop && @goto start @if "%1"=="console" @goto console +@if "%1"=="ping" @goto ping @if "%1"=="query" @goto query @if "%1"=="attach" @goto attach @if "%1"=="upgrade" @goto upgrade @echo Unknown command: "%1" :usage -@echo Usage: %~n0 [install^|uninstall^|start^|stop^|restart^|console^|query^|attach^|upgrade] +@echo Usage: %~n0 [install^|uninstall^|start^|stop^|restart^|console^|ping^|query^|attach^|upgrade] @goto :EOF :install @@ -71,6 +73,11 @@ @start "%node_name% console" %werl% -boot "%node_boot_script%" -config "%sys_config%" -args_file "%vm_args%" -sname %node_name% @goto :EOF +:ping +@%escript% %nodetool% ping -sname "%node_name%" -setcookie "%erlang_cookie%" +@exit %ERRORLEVEL% +@goto :EOF + :query @%erlsrv% list %service_name% @exit %ERRORLEVEL% diff --git a/rebar.config.sample b/rebar.config.sample index a6fb03f..d8d61fc 100644 --- a/rebar.config.sample +++ b/rebar.config.sample @@ -156,7 +156,7 @@ {deps, [application_name, {application_name, "1.0.*"}, {application_name, "1.0.*", - {git, "git://github.com/basho/rebar.git", {branch, "master"}}}, + {git, "git://github.com/rebar/rebar.git", {branch, "master"}}}, %% Dependencies can be marked as 'raw'. Rebar does not require %% such dependencies to have a standard Erlang/OTP layout %% which assumes the presence of either @@ -171,7 +171,7 @@ %% 'raw' subdirectories: get-deps, update-deps, check-deps, %% list-deps and delete-deps. {application_name, "", - {git, "git://github.com/basho/rebar.git", {branch, "master"}}, + {git, "git://github.com/rebar/rebar.git", {branch, "master"}}, [raw]}]}. %% == Subdirectories == @@ -213,9 +213,9 @@ %% Optional custom xref queries (xref manual has details) specified as %% {xref_queries, [{query_string(), expected_query_result()},...]} -%% The following for example removes all references to ejabberd:*_msg/4 +%% The following for example removes all references to mod:*foo/4 %% functions from undefined external function calls as those are in a %% generated module {xref_queries, [{"(XC - UC) || (XU - X - B" - " - (\"ejabberd_logger\":\".*_msg\"/\"4\"))",[]}]}. + " - (\"mod\":\".*foo\"/\"4\"))",[]}]}. diff --git a/src/rebar.erl b/src/rebar.erl index c872ade..ded5ebe 100644 --- a/src/rebar.erl +++ b/src/rebar.erl @@ -76,8 +76,18 @@ run(BaseConfig, Commands) -> %% Internal functions %% ==================================================================== +run(["help"|RawCmds]) when RawCmds =/= [] -> + ok = load_rebar_app(), + Cmds = unabbreviate_command_names(RawCmds), + Args = parse_args(Cmds), + BaseConfig = init_config(Args), + {BaseConfig1, _} = save_options(BaseConfig, Args), + BaseConfig2 = init_config1(BaseConfig1), + rebar_core:help(BaseConfig2, [list_to_atom(C) || C <- Cmds]); run(["help"]) -> help(); +run(["info"|_]) -> + help(); run(["version"]) -> ok = load_rebar_app(), %% Display vsn and build time info @@ -138,6 +148,16 @@ init_config({Options, _NonOptArgs}) -> %% Initialize vsn cache rebar_config:set_xconf(BaseConfig1, vsn_cache, dict:new()). +init_config1(BaseConfig) -> + %% Determine the location of the rebar executable; important for pulling + %% resources out of the escript + ScriptName = filename:absname(escript:script_name()), + BaseConfig1 = rebar_config:set_xconf(BaseConfig, escript, ScriptName), + ?DEBUG("Rebar location: ~p\n", [ScriptName]), + %% Note the top-level directory for reference + AbsCwd = filename:absname(rebar_utils:get_cwd()), + rebar_config:set_xconf(BaseConfig1, base_dir, AbsCwd). + run_aux(BaseConfig, Commands) -> %% Make sure crypto is running case crypto:start() of @@ -148,18 +168,10 @@ run_aux(BaseConfig, Commands) -> %% 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 - ScriptName = filename:absname(escript:script_name()), - BaseConfig1 = rebar_config:set_xconf(BaseConfig, escript, ScriptName), - ?DEBUG("Rebar location: ~p\n", [ScriptName]), - - %% Note the top-level directory for reference - AbsCwd = filename:absname(rebar_utils:get_cwd()), - BaseConfig2 = rebar_config:set_xconf(BaseConfig1, base_dir, AbsCwd), + BaseConfig1 = init_config1(BaseConfig), %% Process each command, resetting any state between each one - rebar_core:process_commands(CommandAtoms, BaseConfig2). + rebar_core:process_commands(CommandAtoms, BaseConfig1). %% %% print help/usage string @@ -169,7 +181,29 @@ help() -> getopt:usage(OptSpecList, "rebar", "[var=value,...] <command,...>", [{"var=value", "rebar global variables (e.g. force=1)"}, - {"command", "Command to run (e.g. compile)"}]). + {"command", "Command to run (e.g. compile)"}]), + ?CONSOLE( + "Core rebar.config options:~n" + " ~p~n" + " ~p~n" + " ~p~n" + " ~p~n" + " ~p~n" + " ~p~n", + [ + {lib_dirs, []}, + {sub_dirs, ["dir1", "dir2"]}, + {plugins, [plugin1, plugin2]}, + {plugin_dir, "some_other_directory"}, + {pre_hooks, [{clean, "./prepare_package_files.sh"}, + {"linux", compile, "c_src/build_linux.sh"}, + {compile, "escript generate_headers"}, + {compile, "escript check_headers"}]}, + {post_hooks, [{clean, "touch file1.out"}, + {"freebsd", compile, "c_src/freebsd_tweaks.sh"}, + {eunit, "touch file2.out"}, + {compile, "touch postcompile.out"}]} + ]). %% %% Parse command line arguments using getopt and also filtering out any diff --git a/src/rebar_abnfc_compiler.erl b/src/rebar_abnfc_compiler.erl index 0e6749a..37731b5 100644 --- a/src/rebar_abnfc_compiler.erl +++ b/src/rebar_abnfc_compiler.erl @@ -47,6 +47,9 @@ -export([compile/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). %% =================================================================== @@ -62,11 +65,23 @@ compile(Config, _AppFile) -> option(module_ext, DtlOpts) ++ ".erl", fun compile_abnfc/3). - %% =================================================================== %% Internal functions %% =================================================================== +info(help, compile) -> + ?CONSOLE( + "Build ABNF (*.abnf) sources.~n" + "~n" + "Valid rebar.config options:~n" + " ~p~n", + [ + {abnfc_opts, [{doc_root, "src"}, + {out_dir, "src"}, + {source_ext, ".abnfc"}, + {module_ext, ""}]} + ]). + abnfc_opts(Config) -> rebar_config:get(Config, abnfc_opts, []). diff --git a/src/rebar_appups.erl b/src/rebar_appups.erl index 0aeccb6..722f161 100644 --- a/src/rebar_appups.erl +++ b/src/rebar_appups.erl @@ -31,6 +31,9 @@ -export(['generate-appups'/2]). +%% for internal use only +-export([info/2]). + -define(APPUPFILEFORMAT, "%% appup generated for ~p by rebar (~p)~n" "{~p, [{~p, ~p}], [{~p, []}]}.~n"). @@ -82,6 +85,13 @@ %% Internal functions %% =================================================================== +info(help, 'generate-appups') -> + ?CONSOLE("Generate appup files.~n" + "~n" + "Valid command line options:~n" + " previous_release=path~n", + []). + get_apps(Name, OldVerPath, NewVerPath) -> OldApps = rebar_rel_utils:get_rel_apps(Name, OldVerPath), ?DEBUG("Old Version Apps: ~p~n", [OldApps]), diff --git a/src/rebar_asn1_compiler.erl b/src/rebar_asn1_compiler.erl index d93a2e8..25e3fd3 100644 --- a/src/rebar_asn1_compiler.erl +++ b/src/rebar_asn1_compiler.erl @@ -30,6 +30,9 @@ -export([compile/2, clean/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). %% =================================================================== @@ -48,6 +51,23 @@ clean(_Config, _AppFile) -> ok = rebar_file_utils:delete_each(GeneratedFiles), ok. +%% =================================================================== +%% Internal functions +%% =================================================================== + +info(help, compile) -> + info_help("Build ASN.1 (*.asn1) sources"); +info(help, clean) -> + info_help("Delete ASN.1 (*.asn1) results"). + +info_help(Description) -> + ?CONSOLE( + "~s.~n" + "~n" + "Valid rebar.config options:~n" + " {asn1_opts, []} (see asn1ct:compile/2 documentation)~n", + [Description]). + -spec compile_asn1(file:filename(), file:filename(), rebar_config:config()) -> ok. compile_asn1(Source, Target, Config) -> diff --git a/src/rebar_base_compiler.erl b/src/rebar_base_compiler.erl index 260cdaf..a0dec30 100644 --- a/src/rebar_base_compiler.erl +++ b/src/rebar_base_compiler.erl @@ -226,6 +226,8 @@ format_warnings(Config, Source, Warnings, Opts) -> maybe_report([{error, {error, _Es, _Ws}=ErrorsAndWarnings}, {source, _}]) -> maybe_report(ErrorsAndWarnings); +maybe_report([{error, E}, {source, S}]) -> + report(["unexpected error compiling " ++ S, io_lib:fwrite("~n~p~n", [E])]); maybe_report({error, Es, Ws}) -> report(Es), report(Ws); diff --git a/src/rebar_cleaner.erl b/src/rebar_cleaner.erl index 9ddeb8a..7a762f5 100644 --- a/src/rebar_cleaner.erl +++ b/src/rebar_cleaner.erl @@ -28,6 +28,9 @@ -export([clean/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). %% =================================================================== @@ -37,3 +40,17 @@ clean(Config, _AppFile) -> %% Get a list of files to delete from config and remove them FilesToClean = rebar_config:get(Config, clean_files, []), lists:foreach(fun (F) -> rebar_file_utils:rm_rf(F) end, FilesToClean). + +%% =================================================================== +%% Internal functions +%% =================================================================== + +info(help, clean) -> + ?CONSOLE( + "Delete list of files.~n" + "~n" + "Valid rebar.config options:~n" + " ~p~n", + [ + {clean_files, ["file", "file2"]} + ]). diff --git a/src/rebar_core.erl b/src/rebar_core.erl index 5396dd5..3172d64 100644 --- a/src/rebar_core.erl +++ b/src/rebar_core.erl @@ -26,7 +26,7 @@ %% ------------------------------------------------------------------- -module(rebar_core). --export([process_commands/2]). +-export([process_commands/2, help/2]). -include("rebar.hrl"). @@ -34,6 +34,35 @@ %% Internal functions %% =================================================================== +help(ParentConfig, Commands) -> + %% get all core modules + {ok, AnyDirModules} = application:get_env(rebar, any_dir_modules), + {ok, RawCoreModules} = application:get_env(rebar, modules), + AppDirModules = proplists:get_value(app_dir, RawCoreModules), + RelDirModules = proplists:get_value(rel_dir, RawCoreModules), + CoreModules = AnyDirModules ++ AppDirModules ++ RelDirModules, + + %% get plugin modules + Predirs = [], + Dir = rebar_utils:get_cwd(), + PredirsAssoc = remember_cwd_predirs(Dir, Predirs), + Config = maybe_load_local_config(Dir, ParentConfig), + {ok, PluginModules} = plugin_modules(Config, PredirsAssoc), + + AllModules = CoreModules ++ PluginModules, + + lists:foreach( + fun(Cmd) -> + ?CONSOLE("==> help ~p~n~n", [Cmd]), + CmdModules = select_modules(AllModules, Cmd, []), + Modules = select_modules(CmdModules, info, []), + lists:foreach(fun(M) -> + ?CONSOLE("=== ~p:~p ===~n", [M, Cmd]), + M:info(help, Cmd), + ?CONSOLE("~n", []) + end, Modules) + end, Commands). + process_commands([], ParentConfig) -> AbortTrapped = rebar_config:get_xconf(ParentConfig, abort_trapped, false), case {get_operations(ParentConfig), AbortTrapped} of @@ -110,21 +139,21 @@ process_dir(Dir, ParentConfig, Command, DirSet) -> {ok, AvailModuleSets} = application:get_env(rebar, modules), ModuleSet = choose_module_set(AvailModuleSets, Dir), skip_or_process_dir(ModuleSet, Config, CurrentCodePath, - Dir, Command, DirSet) + Dir, Command, DirSet) end. skip_or_process_dir({[], undefined}=ModuleSet, Config, CurrentCodePath, - Dir, Command, DirSet) -> + Dir, Command, DirSet) -> process_dir1(Dir, Command, DirSet, Config, CurrentCodePath, ModuleSet); skip_or_process_dir({_, ModuleSetFile}=ModuleSet, Config, CurrentCodePath, - Dir, Command, DirSet) -> + Dir, Command, DirSet) -> case lists:suffix(".app.src", ModuleSetFile) orelse lists:suffix(".app", ModuleSetFile) of true -> %% .app or .app.src file, check if is_skipped_app skip_or_process_dir1(ModuleSetFile, ModuleSet, - Config, CurrentCodePath, Dir, - Command, DirSet); + Config, CurrentCodePath, Dir, + Command, DirSet); false -> %% not an app dir, no need to consider apps=/skip_apps= process_dir1(Dir, Command, DirSet, Config, @@ -132,7 +161,7 @@ skip_or_process_dir({_, ModuleSetFile}=ModuleSet, Config, CurrentCodePath, end. skip_or_process_dir1(AppFile, ModuleSet, Config, CurrentCodePath, - Dir, Command, DirSet) -> + Dir, Command, DirSet) -> case rebar_app_utils:is_skipped_app(Config, AppFile) of {Config1, {true, SkippedApp}} -> ?DEBUG("Skipping app: ~p~n", [SkippedApp]), @@ -158,11 +187,12 @@ process_dir1(Dir, Command, DirSet, Config0, CurrentCodePath, {Config1, Predirs} = acc_modules(Modules, preprocess, Config0, ModuleSetFile), - SubdirAssoc = remember_cwd_subdir(Dir, Predirs), + %% Remember associated pre-dirs (used for plugin lookup) + PredirsAssoc = remember_cwd_predirs(Dir, Predirs), %% Get the list of plug-in modules from rebar.config. These %% modules may participate in preprocess and postprocess. - {ok, PluginModules} = plugin_modules(Config1, SubdirAssoc), + {ok, PluginModules} = plugin_modules(Config1, PredirsAssoc), {Config2, PluginPredirs} = acc_modules(PluginModules, preprocess, Config1, ModuleSetFile), @@ -224,7 +254,7 @@ process_dir1(Dir, Command, DirSet, Config0, CurrentCodePath, %% Return the updated {config, dirset} as result Res. -remember_cwd_subdir(Cwd, Subdirs) -> +remember_cwd_predirs(Cwd, Predirs) -> Store = fun(Dir, Dict) -> case dict:find(Dir, Dict) of error -> @@ -235,11 +265,10 @@ remember_cwd_subdir(Cwd, Subdirs) -> ?ABORT("Internal consistency assertion failed.~n" "sub_dir ~s already associated with ~s.~n" "Duplicate sub_dirs or deps entries?", - [Dir, Existing]), - Dict + [Dir, Existing]) end end, - lists:foldl(Store, dict:new(), Subdirs). + lists:foldl(Store, dict:new(), Predirs). maybe_load_local_config(Dir, ParentConfig) -> %% We need to ensure we don't overwrite custom @@ -459,9 +488,9 @@ acc_modules([Module | Rest], Command, Config, File, Acc) -> %% %% Return a flat list of rebar plugin modules. %% -plugin_modules(Config, SubdirAssoc) -> +plugin_modules(Config, PredirsAssoc) -> Modules = lists:flatten(rebar_config:get_all(Config, plugins)), - plugin_modules(Config, SubdirAssoc, ulist(Modules)). + plugin_modules(Config, PredirsAssoc, ulist(Modules)). ulist(L) -> ulist(L, []). @@ -476,16 +505,16 @@ ulist([H | T], Acc) -> ulist(T, [H | Acc]) end. -plugin_modules(_Config, _SubdirAssoc, []) -> +plugin_modules(_Config, _PredirsAssoc, []) -> {ok, []}; -plugin_modules(Config, SubdirAssoc, Modules) -> +plugin_modules(Config, PredirsAssoc, Modules) -> FoundModules = [M || M <- Modules, code:which(M) =/= non_existing], - plugin_modules(Config, SubdirAssoc, FoundModules, Modules -- FoundModules). + plugin_modules(Config, PredirsAssoc, FoundModules, Modules -- FoundModules). -plugin_modules(_Config, _SubdirAssoc, FoundModules, []) -> +plugin_modules(_Config, _PredirsAssoc, FoundModules, []) -> {ok, FoundModules}; -plugin_modules(Config, SubdirAssoc, FoundModules, MissingModules) -> - {Loaded, NotLoaded} = load_plugin_modules(Config, SubdirAssoc, +plugin_modules(Config, PredirsAssoc, FoundModules, MissingModules) -> + {Loaded, NotLoaded} = load_plugin_modules(Config, PredirsAssoc, MissingModules), AllViablePlugins = FoundModules ++ Loaded, case NotLoaded =/= [] of @@ -499,37 +528,43 @@ plugin_modules(Config, SubdirAssoc, FoundModules, MissingModules) -> end, {ok, AllViablePlugins}. -load_plugin_modules(Config, SubdirAssoc, Modules) -> +load_plugin_modules(Config, PredirsAssoc, Modules) -> Cwd = rebar_utils:get_cwd(), - PluginDir = case rebar_config:get_local(Config, plugin_dir, undefined) of - undefined -> - filename:join(Cwd, "plugins"); - Dir -> - Dir - end, + PluginDirs = case rebar_config:get_local(Config, plugin_dir, undefined) of + undefined -> + % Plugin can be in the project's "plugins" folder + [filename:join(Cwd, "plugins")]; + Dir -> + [Dir] + end ++ + % We also want to include this case: + % Plugin can be in "plugins" directory of the plugin base directory. For + % example, Cwd depends on Plugin, and deps/Plugin/plugins/Plugin.erl is the + % plugin. + [ + filename:join(Dir, "plugins") || + Dir <- get_plugin_base_dirs(Cwd, PredirsAssoc) + ], %% Find relevant sources in base_dir and plugin_dir Erls = string:join([atom_to_list(M)++"\\.erl" || M <- Modules], "|"), RE = "^" ++ Erls ++ "\$", - BaseDir = get_plugin_base_dir(Cwd, SubdirAssoc), %% If a plugin is found both in base_dir and plugin_dir, the clash %% will provoke an error and we'll abort. - Sources = rebar_utils:find_files(PluginDir, RE, false) - ++ rebar_utils:find_files(BaseDir, RE, false), + Sources = [rebar_utils:find_files(PD, RE, false) || PD <- PluginDirs], %% Compile and load plugins - Loaded = [load_plugin(Src) || Src <- Sources], + Loaded = [load_plugin(Src) || Src <- lists:append(Sources)], FilterMissing = is_missing_plugin(Loaded), NotLoaded = [V || V <- Modules, FilterMissing(V)], {Loaded, NotLoaded}. -get_plugin_base_dir(Cwd, SubdirAssoc) -> - case dict:find(Cwd, SubdirAssoc) of - {ok, BaseDir} -> - BaseDir; - error -> - Cwd - end. +%% @doc PredirsAssoc is a dictionary of plugindir -> 'parent' pairs +%% 'parent' in this case depends on plugin; therefore we have to give +%% all plugins that Cwd ('parent' in this case) depends on. +get_plugin_base_dirs(Cwd, PredirsAssoc) -> + [PluginDir || {PluginDir, Master} <- dict:to_list(PredirsAssoc), + Master =:= Cwd]. is_missing_plugin(Loaded) -> fun(Mod) -> not lists:member(Mod, Loaded) end. diff --git a/src/rebar_ct.erl b/src/rebar_ct.erl index 749d025..9951f8e 100644 --- a/src/rebar_ct.erl +++ b/src/rebar_ct.erl @@ -39,6 +39,9 @@ -export([ct/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). %% =================================================================== @@ -53,6 +56,26 @@ ct(Config, File) -> %% =================================================================== %% Internal functions %% =================================================================== + +info(help, ct) -> + ?CONSOLE( + "Run common_test suites.~n" + "~n" + "Valid rebar.config options:~n" + " ~p~n" + " ~p~n" + " ~p~n" + " ~p~n" + "Valid command line options:~n" + " suites=foo,bar - run <test>/foo_SUITE and <test>/bar_SUITE~n" + " case=\"mycase\" - run individual test case foo_SUITE:mycase~n", + [ + {ct_dir, "itest"}, + {ct_log_dir, "test/logs"}, + {ct_extra_params, "-boot start_sasl -s myapp"}, + {ct_use_short_names, true} + ]). + run_test_if_present(TestDir, LogDir, Config, File) -> case filelib:is_dir(TestDir) of false -> @@ -85,8 +108,16 @@ run_test(TestDir, LogDir, Config, _File) -> " 2>&1 | tee -a " ++ RawLog end, - rebar_utils:sh(Cmd ++ Output, [{env,[{"TESTDIR", TestDir}]}]), - check_log(Config, RawLog). + case rebar_utils:sh(Cmd ++ Output, [{env,[{"TESTDIR", TestDir}]}, return_on_error]) of + {ok,_} -> + %% in older versions of ct_run, this could have been a failure + %% that returned a non-0 code. Check for that! + check_success_log(Config, RawLog); + {error,Res} -> + %% In newer ct_run versions, this may be a sign of a good compile + %% that failed cases. In older version, it's a worse error. + check_fail_log(Config, RawLog, Cmd ++ Output, Res) + end. clear_log(LogDir, RawLog) -> case filelib:ensure_dir(filename:join(LogDir, "index.html")) of @@ -101,7 +132,16 @@ clear_log(LogDir, RawLog) -> %% calling ct with erl does not return non-zero on failure - have to check %% log results -check_log(Config, RawLog) -> +check_success_log(Config, RawLog) -> + check_log(Config, RawLog, fun(Msg) -> ?CONSOLE("DONE.\n~s\n", [Msg]) end). + +check_fail_log(Config, RawLog, Command, {Rc, Output}) -> + check_log(Config, RawLog, fun(_Msg) -> + ?ABORT("~s failed with error: ~w and output:~n~s~n", + [Command, Rc, Output]) + end). + +check_log(Config,RawLog,Fun) -> {ok, Msg} = rebar_utils:sh("grep -e 'TEST COMPLETE' -e '{error,make_failed}' " ++ RawLog, [{use_stdout, false}]), @@ -119,9 +159,10 @@ check_log(Config, RawLog) -> ?FAIL; true -> - ?CONSOLE("DONE.\n~s\n", [Msg]) + Fun(Msg) end. + %% Show the log if it hasn't already been shown because verbose was on show_log(Config, RawLog) -> ?CONSOLE("Showing log\n", []), diff --git a/src/rebar_deps.erl b/src/rebar_deps.erl index 074e929..313deaa 100644 --- a/src/rebar_deps.erl +++ b/src/rebar_deps.erl @@ -38,6 +38,8 @@ 'delete-deps'/2, 'list-deps'/2]). +%% for internal use only +-export([info/2]). -record(dep, { dir, app, @@ -203,6 +205,40 @@ do_check_deps(Config) -> %% Internal functions %% =================================================================== +info(help, compile) -> + info_help("Display to be fetched dependencies"); +info(help, 'check-deps') -> + info_help("Display to be fetched dependencies"); +info(help, 'get-deps') -> + info_help("Fetch dependencies"); +info(help, 'update-deps') -> + info_help("Update fetched dependencies"); +info(help, 'delete-deps') -> + info_help("Delete fetched dependencies"); +info(help, 'list-deps') -> + info_help("List dependencies"). + +info_help(Description) -> + ?CONSOLE( + "~s.~n" + "~n" + "Valid rebar.config options:~n" + " ~p~n" + " ~p~n" + "Valid command line options:~n" + " deps_dir=\"deps\" (override default or rebar.config deps_dir)~n", + [ + Description, + {deps_dir, "deps"}, + {deps, [application_name, + {application_name, "1.0.*"}, + {application_name, "1.0.*", + {git, "git://github.com/rebar/rebar.git", {branch, "master"}}}, + {application_name, "", + {git, "git://github.com/rebar/rebar.git", {branch, "master"}}, + [raw]}]} + ]). + %% Added because of trans deps, %% need all deps in same dir and should be the one set by the root rebar.config %% Sets a default if root config has no deps_dir set diff --git a/src/rebar_dia_compiler.erl b/src/rebar_dia_compiler.erl index 51c075b..f81c734 100644 --- a/src/rebar_dia_compiler.erl +++ b/src/rebar_dia_compiler.erl @@ -28,6 +28,9 @@ -export([compile/2, clean/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). %% =================================================================== @@ -46,6 +49,23 @@ clean(_Config, _AppFile) -> ok = rebar_file_utils:delete_each(GeneratedFiles), ok. +%% =================================================================== +%% Internal functions +%% =================================================================== + +info(help, compile) -> + info_help("Build Diameter (*.dia) sources"); +info(help, clean) -> + info_help("Delete generated Diameter files"). + +info_help(Description) -> + ?CONSOLE( + "~s.~n" + "~n" + "Valid rebar.config options:~n" + " {dia_opts, []} (see diameter_codegen:from_dict/4 documentation)~n", + [Description]). + -spec compile_dia(file:filename(), file:filename(), rebar_config:config()) -> ok. compile_dia(Source, Target, Config) -> diff --git a/src/rebar_edoc.erl b/src/rebar_edoc.erl index cf0239c..c828d27 100644 --- a/src/rebar_edoc.erl +++ b/src/rebar_edoc.erl @@ -39,6 +39,9 @@ -export([doc/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). %% =================================================================== @@ -71,6 +74,14 @@ doc(Config, File) -> %% Internal functions %% =================================================================== +info(help, doc) -> + ?CONSOLE( + "Generate Erlang program documentation.~n" + "~n" + "Valid rebar.config options:~n" + " {edoc_opts, []} (see edoc:application/3 documentation)~n", + []). + setup_code_path() -> %% Setup code path prior to calling edoc so that edown, asciiedoc, %% and the like can work properly when generating their own diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index 91f8354..caef0d2 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -29,8 +29,9 @@ -export([compile/2, clean/2]). -%% for internal use by only eunit and qc --export([test_compile/3]). +%% for internal use only +-export([test_compile/3, + info/2]). -include("rebar.hrl"). @@ -116,8 +117,6 @@ clean(_Config, _AppFile) -> test_compile(Config, Cmd, OutDir) -> %% Obtain all the test modules for inclusion in the compile stage. - %% Notice: this could also be achieved with the following - %% rebar.config option: {test_compile_opts, [{src_dirs, ["src", "test"]}]} TestErls = rebar_utils:find_files("test", ".*\\.erl\$"), %% Copy source files to eunit dir for cover in case they are not directly @@ -165,6 +164,42 @@ test_compile(Config, Cmd, OutDir) -> %% Internal functions %% =================================================================== +info(help, compile) -> + info_help("Build *.erl, *.yrl, *.xrl, and *.mib sources"); +info(help, clean) -> + info_help("Delete *.erl, *.yrl, *.xrl, and *.mib build results"). + +info_help(Description) -> + ?CONSOLE( + "~s.~n" + "~n" + "Valid rebar.config options:~n" + " ~p~n" + " ~p~n" + " ~p~n" + " ~p~n" + " ~p~n" + " ~p~n" + " ~p~n" + " ~p~n", + [ + Description, + {erl_opts, [no_debug_info, + {i, "myinclude"}, + {src_dirs, ["src", "src2", "src3"]}, + {platform_define, + "(linux|solaris|freebsd|darwin)", 'HAVE_SENDFILE'}, + {platform_define, "(linux|freebsd)", 'BACKLOG', 128}, + {platform_define, "R13", 'old_inets'}]}, + {erl_first_files, ["mymib1", "mymib2"]}, + {mib_opts, []}, + {mib_first_files, []}, + {xrl_opts, []}, + {xrl_first_files, []}, + {yrl_opts, []}, + {yrl_first_files, []} + ]). + test_compile_config(Config, Cmd) -> {Config1, TriqOpts} = triq_opts(Config), {Config2, PropErOpts} = proper_opts(Config1), diff --git a/src/rebar_erlydtl_compiler.erl b/src/rebar_erlydtl_compiler.erl index aef41c5..4449be6 100644 --- a/src/rebar_erlydtl_compiler.erl +++ b/src/rebar_erlydtl_compiler.erl @@ -96,6 +96,9 @@ -export([compile/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). %% =================================================================== @@ -123,11 +126,24 @@ compile(Config, _AppFile) -> true = code:set_path(OrigPath), Result. - %% =================================================================== %% Internal functions %% =================================================================== +info(help, compile) -> + ?CONSOLE( + "Build ErlyDtl (*.dtl) sources.~n" + "~n" + "Valid rebar.config options:~n" + " ~p~n", + [ + {erlydtl_opts, [{doc_root, "templates"}, + {out_dir, "ebin"}, + {source_ext, ".dtl"}, + {module_ext, "_dtl"}, + {recursive, true}]} + ]). + erlydtl_opts(Config) -> Opts = rebar_config:get(Config, erlydtl_opts, []), Tuples = [{K,V} || {K,V} <- Opts], @@ -135,11 +151,14 @@ erlydtl_opts(Config) -> [] -> [lists:keysort(1, Tuples)]; Lists -> - lists:map(fun(L) -> - lists:keysort(1, lists:foldl(fun({K,T}, Acc) -> - lists:keystore(K, 1, Acc, {K, T}) - end, Tuples, L)) - end, Lists) + lists:map( + fun(L) -> + lists:keysort(1, + lists:foldl( + fun({K,T}, Acc) -> + lists:keystore(K, 1, Acc, {K, T}) + end, Tuples, L)) + end, Lists) end. option(Opt, DtlOpts) -> diff --git a/src/rebar_escripter.erl b/src/rebar_escripter.erl index 706cf7c..0cc43ef 100644 --- a/src/rebar_escripter.erl +++ b/src/rebar_escripter.erl @@ -29,6 +29,9 @@ -export([escriptize/2, clean/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). -include_lib("kernel/include/file.hrl"). @@ -108,6 +111,30 @@ clean(Config0, AppFile) -> %% Internal functions %% =================================================================== +info(help, escriptize) -> + info_help("Generate escript archive"); +info(help, clean) -> + info_help("Delete generated escript archive"). + +info_help(Description) -> + ?CONSOLE( + "~s.~n" + "~n" + "Valid rebar.config options:~n" + " ~p~n" + " ~p~n" + " ~p~n" + " ~p~n" + " ~p~n", + [ + Description, + {escript_name, "application"}, + {escript_incl_apps, []}, + {escript_shebang, "#!/usr/bin/env escript\n"}, + {escript_comment, "%%\n"}, + {escript_emu_args, "%%! -pa application/application/ebin\n"} + ]). + get_app_beams([], Acc) -> Acc; get_app_beams([App | Rest], Acc) -> diff --git a/src/rebar_eunit.erl b/src/rebar_eunit.erl index a7f4aca..cdc0787 100644 --- a/src/rebar_eunit.erl +++ b/src/rebar_eunit.erl @@ -61,7 +61,7 @@ %% Additionally, for projects that have separate folders for the core %% implementation, and for the unit tests, then the following %% <code>rebar.config</code> option can be provided: -%% <code>{test_compile_opts, [{src_dirs, ["dir"]}]}.</code>. +%% <code>{eunit_compile_opts, [{src_dirs, ["src", "dir"]}]}.</code>. %% @copyright 2009, 2010 Dave Smith %% ------------------------------------------------------------------- -module(rebar_eunit). @@ -69,6 +69,9 @@ -export([eunit/2, clean/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). -define(EUNIT_DIR, ".eunit"). @@ -100,6 +103,40 @@ clean(_Config, _File) -> %% Internal functions %% =================================================================== +info(help, eunit) -> + info_help("Run eunit tests"); +info(help, clean) -> + Description = ?FMT("Delete eunit test dir (~s)", [?EUNIT_DIR]), + info_help(Description). + +info_help(Description) -> + ?CONSOLE( + "~s.~n" + "~n" + "Valid rebar.config options:~n" + " ~p~n" + " ~p~n" + " ~p~n" + " ~p~n" + " ~p~n" + " ~p~n" + "Valid command line options:~n" + " suites=\"foo,bar\" (Run tests in foo.erl, test/foo_tests.erl and~n" + " tests in bar.erl, test/bar_tests.erl)~n" + " tests=\"baz\" (For every existing suite, run the first test whose~n" + " name starts with bar and, if no such test exists,~n" + " run the test whose name starts with bar in the~n" + " suite's _tests module)~n", + [ + Description, + {eunit_opts, []}, + {eunit_compile_opts, []}, + {eunit_first_files, []}, + {cover_enabled, false}, + {cover_print_enabled, false}, + {cover_export_enabled, false} + ]). + run_eunit(Config, CodePath, SrcErls) -> %% Build a list of all the .beams in ?EUNIT_DIR -- use this for %% cover and eunit testing. Normally you can just tell cover diff --git a/src/rebar_lfe_compiler.erl b/src/rebar_lfe_compiler.erl index d288ca5..2a047d8 100644 --- a/src/rebar_lfe_compiler.erl +++ b/src/rebar_lfe_compiler.erl @@ -30,6 +30,9 @@ -export([compile/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). %% =================================================================== @@ -45,6 +48,14 @@ compile(Config, _AppFile) -> %% Internal functions %% =================================================================== +info(help, compile) -> + ?CONSOLE( + "Build Lisp Flavoured Erlang (*.lfe) sources.~n" + "~n" + "Valid rebar.config options:~n" + " erl_opts is reused.'~n", + []). + compile_lfe(Source, _Target, Config) -> case code:which(lfe_comp) of non_existing -> diff --git a/src/rebar_neotoma_compiler.erl b/src/rebar_neotoma_compiler.erl index 33f32e3..5607aa4 100644 --- a/src/rebar_neotoma_compiler.erl +++ b/src/rebar_neotoma_compiler.erl @@ -42,6 +42,9 @@ -export([compile/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). %% ============================================================================ @@ -60,6 +63,19 @@ compile(Config, _AppFile) -> %% Internal functions %% ============================================================================ +info(help, compile) -> + ?CONSOLE( + "Build Neotoma (*.peg) sources.~n" + "~n" + "Valid rebar.config options:~n" + " ~p~n", + [ + {neotom_opts, [{doc_root, "src"}, + {out_dir, "src"}, + {source_ext, ".peg"}, + {module_ext, ""}]} + ]). + neotoma_opts(Config) -> rebar_config:get(Config, neotoma_opts, []). diff --git a/src/rebar_otp_app.erl b/src/rebar_otp_app.erl index a62f584..6c52336 100644 --- a/src/rebar_otp_app.erl +++ b/src/rebar_otp_app.erl @@ -29,6 +29,9 @@ -export([compile/2, clean/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). %% =================================================================== @@ -82,11 +85,26 @@ clean(_Config, File) -> ok end. - %% =================================================================== %% Internal functions %% =================================================================== +info(help, compile) -> + info_help("Validate .app file"); +info(help, clean) -> + info_help("Delete .app file if generated from .app.src"). + +info_help(Description) -> + ?CONSOLE( + "~s.~n" + "~n" + "Valid rebar.config options:~n" + " ~p~n", + [ + Description, + {validate_app_modules, true} + ]). + preprocess(Config, AppSrcFile) -> case rebar_app_utils:load_app_file(Config, AppSrcFile) of {ok, Config1, AppName, AppData} -> diff --git a/src/rebar_port_compiler.erl b/src/rebar_port_compiler.erl index 06a79bc..0abb044 100644 --- a/src/rebar_port_compiler.erl +++ b/src/rebar_port_compiler.erl @@ -27,8 +27,11 @@ -module(rebar_port_compiler). -export([compile/2, - clean/2, - setup_env/1]). + clean/2]). + +%% for internal use only +-export([setup_env/1, + info/2]). -include("rebar.hrl"). @@ -97,7 +100,8 @@ compile(Config, AppFile) -> [] -> ok; Specs -> - SharedEnv = rebar_config:get_env(Config, ?MODULE), + SharedEnv = rebar_config:get_env(Config, rebar_deps) ++ + rebar_config:get_env(Config, ?MODULE), %% Compile each of the sources NewBins = compile_sources(Config, Specs, SharedEnv), @@ -149,6 +153,27 @@ setup_env(Config) -> %% Internal functions %% =================================================================== +info(help, compile) -> + info_help("Build port sources"); +info(help, clean) -> + info_help("Delete port build results"). + +info_help(Description) -> + ?CONSOLE( + "~s.~n" + "~n" + "Valid rebar.config options:~n" + " ~p~n" + " ~p~n", + [ + Description, + {port_env, [{"CFLAGS", "$CFLAGS -Ifoo"}, + {"freebsd", "LDFLAGS", "$LDFLAGS -lfoo"}]}, + {port_specs, [{"priv/so_name.so", ["c_src/*.c"]}, + {"linux", "priv/hello_linux", ["c_src/hello_linux.c"]}, + {"linux", "priv/hello_linux", ["c_src/*.c"], [{env, []}]}]} + ]). + setup_env(Config, ExtraEnv) -> %% Extract environment values from the config (if specified) and %% merge with the default for this operating system. This enables diff --git a/src/rebar_protobuffs_compiler.erl b/src/rebar_protobuffs_compiler.erl index 7ef58d6..579ecfb 100644 --- a/src/rebar_protobuffs_compiler.erl +++ b/src/rebar_protobuffs_compiler.erl @@ -29,6 +29,9 @@ -export([compile/2, clean/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). %% =================================================================== @@ -57,7 +60,6 @@ compile(Config, _AppFile) -> end end. - clean(_Config, _AppFile) -> %% Get a list of generated .beam and .hrl files and then delete them Protos = rebar_utils:find_files("src", ".*\\.proto$"), @@ -71,11 +73,24 @@ clean(_Config, _AppFile) -> delete_each(Targets) end. - %% =================================================================== %% Internal functions %% =================================================================== +info(help, compile) -> + info_help("Build Protobuffs (*.proto) sources"); +info(help, clean) -> + info_help("Delete Protobuffs (*.proto) build results"). + +info_help(Description) -> + ?CONSOLE( + "~s.~n" + "~n" + "Valid rebar.config options:~n" + " erl_opts is passed as compile_flags to " + "protobuffs_compile:scan_file/2~n", + [Description]). + protobuffs_is_present() -> code:which(protobuffs_compile) =/= non_existing. @@ -115,7 +130,7 @@ compile_each(Config, [{Proto, Beam, Hrl} | Rest]) -> ok = rebar_file_utils:mv(Hrl, "include"), ok; Other -> - ?ERROR("Protobuff compile of ~s failed: ~p\n", + ?ERROR("Protobuffs compile of ~s failed: ~p\n", [Proto, Other]), ?FAIL end; diff --git a/src/rebar_qc.erl b/src/rebar_qc.erl index 09e48e4..53a6f52 100644 --- a/src/rebar_qc.erl +++ b/src/rebar_qc.erl @@ -28,6 +28,9 @@ -export([qc/2, triq/2, eqc/2, clean/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). -define(QC_DIR, ".qc"). @@ -57,6 +60,19 @@ clean(_Config, _File) -> %% Internal functions %% =================================================================== +info(help, qc) -> + ?CONSOLE( + "Test QuickCheck properties.~n" + "~n" + "Valid rebar.config options:~n" + " {qc_opts, [{qc_mod, module()}, Options]}~n" + " ~p~n" + " ~p~n", + [ + {qc_compile_opts, []}, + {qc_first_files, []} + ]). + -define(TRIQ_MOD, triq). -define(EQC_MOD, eqc). @@ -159,6 +175,7 @@ qc_module(QC=triq, _QCOpts, M) -> Failed -> [Failed] end; +qc_module(QC=eqc, [], M) -> QC:module(M); qc_module(QC=eqc, QCOpts, M) -> QC:module(QCOpts, M). find_prop_mods() -> diff --git a/src/rebar_reltool.erl b/src/rebar_reltool.erl index 3c9b728..18b0702 100644 --- a/src/rebar_reltool.erl +++ b/src/rebar_reltool.erl @@ -30,6 +30,9 @@ overlay/2, clean/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). -include_lib("kernel/include/file.hrl"). @@ -80,6 +83,32 @@ clean(Config, ReltoolFile) -> %% Internal functions %% =================================================================== +info(help, generate) -> + info_help("Build release with reltool"); +info(help, clean) -> + info_help("Delete release"); +info(help, overlay) -> + info_help("Run reltool overlays only"). + +info_help(Description) -> + ?CONSOLE( + "~s.~n" + "~n" + "Valid rebar.config options:~n" + " ~n" + "Valid reltool.config options:~n" + " {sys, []}~n" + " {target_dir, \"target\"}~n" + " {overlay_vars, \"overlay\"}~n" + " {overlay, []}~n" + "Valid command line options:~n" + " target_dir=target~n" + " overlay_vars=VarsFile~n" + " dump_spec=1 (write reltool target spec to reltool.spec)~n", + [ + Description + ]). + check_vsn() -> %% TODO: use application:load and application:get_key once we require %% R14A or newer. There's no reltool.app before R14A. diff --git a/src/rebar_require_vsn.erl b/src/rebar_require_vsn.erl index 9a0a005..385f55c 100644 --- a/src/rebar_require_vsn.erl +++ b/src/rebar_require_vsn.erl @@ -33,6 +33,9 @@ -export([compile/2, eunit/2]). +%% for internal use only +-export([info/2]). + %% =================================================================== %% Public API %% =================================================================== @@ -47,6 +50,25 @@ eunit(Config, _) -> %% Internal functions %% ==================================================================== +info(help, compile) -> + info_help(); +info(help, eunit) -> + info_help(). + +info_help() -> + ?CONSOLE( + "Check required ERTS or OTP release version.~n" + "~n" + "Valid rebar.config options:~n" + " ~p~n" + " ~p~n" + " ~p~n", + [ + {require_erts_vsn, ".*"}, + {require_otp_vsn, ".*"}, + {require_min_otp_vsn, ".*"} + ]). + check_versions(Config) -> ErtsRegex = rebar_config:get(Config, require_erts_vsn, ".*"), ReOpts = [{capture, none}], diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl index 0e1eef1..e997975 100644 --- a/src/rebar_templater.erl +++ b/src/rebar_templater.erl @@ -35,6 +35,9 @@ -export([resolve_variables/2, render/2]). +%% for internal use only +-export([info/2]). + -include("rebar.hrl"). -define(TEMPLATE_RE, ".*\\.template\$"). @@ -81,6 +84,9 @@ resolve_variables([], Dict) -> resolve_variables([{Key, Value0} | Rest], Dict) when is_list(Value0) -> Value = render(list_to_binary(Value0), Dict), resolve_variables(Rest, dict:store(Key, Value, Dict)); +resolve_variables([{Key, {list, Dicts}} | Rest], Dict) when is_list(Dicts) -> + %% just un-tag it so mustache can use it + resolve_variables(Rest, dict:store(Key, Dicts, Dict)); resolve_variables([_Pair | Rest], Dict) -> resolve_variables(Rest, Dict). @@ -98,6 +104,27 @@ render(Bin, Context) -> %% Internal functions %% =================================================================== +info(help, create) -> + ?CONSOLE( + "Create skel based on template and vars.~n" + "~n" + "Valid command line options:~n" + " template= [var=foo,...]~n", []); +info(help, 'create-app') -> + ?CONSOLE( + "Create simple app skel.~n" + "~n" + "Valid command line options:~n" + " [appid=myapp]~n", []); +info(help, 'create-node') -> + ?CONSOLE( + "Create simple node skel.~n" + "~n" + "Valid command line options:~n" + " [nodeid=mynode]~n", []); +info(help, 'list-templates') -> + ?CONSOLE("List available templates.~n", []). + create1(Config, TemplateId) -> {AvailTemplates, Files} = find_templates(Config), ?DEBUG("Available templates: ~p\n", [AvailTemplates]), @@ -134,14 +161,14 @@ create1(Config, TemplateId) -> undefined -> Context0; File -> - case file:consult(File) of - {ok, Terms} -> - %% TODO: Cleanup/merge with similar code in rebar_reltool - M = fun(_Key, _Base, Override) -> Override end, - dict:merge(M, Context0, dict:from_list(Terms)); + case consult(load_file([], file, File)) of {error, Reason} -> ?ABORT("Unable to load template_vars from ~s: ~p\n", - [File, Reason]) + [File, Reason]); + Terms -> + %% TODO: Cleanup/merge with similar code in rebar_reltool + M = fun(_Key, _Base, Override) -> Override end, + dict:merge(M, Context0, dict:from_list(Terms)) end end, @@ -275,7 +302,7 @@ consult(Cont, Str, Acc) -> case Result of {ok, Tokens, _} -> {ok, Term} = erl_parse:parse_term(Tokens), - consult([], Remaining, [Term | Acc]); + consult([], Remaining, [maybe_dict(Term) | Acc]); {eof, _Other} -> lists:reverse(Acc); {error, Info, _} -> @@ -286,6 +313,13 @@ consult(Cont, Str, Acc) -> end. +maybe_dict({Key, {list, Dicts}}) -> + %% this is a 'list' element; a list of lists representing dicts + {Key, {list, [dict:from_list(D) || D <- Dicts]}}; +maybe_dict(Term) -> + Term. + + write_file(Output, Data, Force) -> %% determine if the target file already exists FileExists = filelib:is_regular(Output), diff --git a/src/rebar_upgrade.erl b/src/rebar_upgrade.erl index 14ea758..d18603c 100644 --- a/src/rebar_upgrade.erl +++ b/src/rebar_upgrade.erl @@ -32,6 +32,9 @@ -export(['generate-upgrade'/2]). +%% for internal use only +-export([info/2]). + -define(TMP, "_tmp"). %% ==================================================================== @@ -80,6 +83,13 @@ %% Internal functions %% ================================================================== +info(help, 'generate-upgrade') -> + ?CONSOLE("Build an upgrade package.~n" + "~n" + "Valid command line options:~n" + " previous_release=path~n", + []). + run_checks(Config, OldVerPath, ReltoolConfig) -> true = rebar_utils:prop_check(filelib:is_dir(OldVerPath), "Release directory doesn't exist (~p)~n", diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index bb58460..fd93f98 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -195,7 +195,7 @@ expand_env_variable(InStr, VarName, RawVarValue) -> %% Use a regex to match/replace: %% Given variable "FOO": match $FOO\s | $FOOeol | ${FOO} RegEx = io_lib:format("\\\$(~s(\\s|$)|{~s})", [VarName, VarName]), - ReOpts = [global, {return, list}], + ReOpts = [global, {return, list}, unicode], re:replace(InStr, RegEx, [VarValue, "\\2"], ReOpts) end. diff --git a/src/rebar_xref.erl b/src/rebar_xref.erl index a55d71d..8c0a872 100644 --- a/src/rebar_xref.erl +++ b/src/rebar_xref.erl @@ -37,6 +37,9 @@ -export([xref/2]). +%% for internal use only +-export([info/2]). + %% =================================================================== %% Public API %% =================================================================== @@ -100,6 +103,22 @@ xref(Config, _) -> %% Internal functions %% =================================================================== +info(help, xref) -> + ?CONSOLE( + "Run cross reference analysis.~n" + "~n" + "Valid rebar.config options:~n" + " ~p~n" + " ~p~n" + " ~p~n", + [ + {xref_warnings, false}, + {xref_checks, [exports_not_used, undefined_function_calls]}, + {xref_queries, + [{"(xc - uc) || (xu - x - b" + " - (\"mod\":\".*foo\"/\"4\"))",[]}]} + ]). + check_exports_not_used() -> {ok, UnusedExports0} = xref:analyze(xref, exports_not_used), UnusedExports = filter_away_ignored(UnusedExports0), diff --git a/test/upgrade_project/rel/files/dummy b/test/upgrade_project/rel/files/dummy index cfbfd4f..c2ef258 100755 --- a/test/upgrade_project/rel/files/dummy +++ b/test/upgrade_project/rel/files/dummy @@ -2,25 +2,41 @@ # -*- tab-width:4;indent-tabs-mode:nil -*- # ex: ts=4 sw=4 et -RUNNER_SCRIPT_DIR=$(cd ${0%/*} && pwd) +# /bin/sh on Solaris is not a POSIX compatible shell, but /usr/bin/ksh is. +if [ `uname -s` = 'SunOS' -a "${POSIX_SHELL}" != "true" ]; then + POSIX_SHELL="true" + export POSIX_SHELL + # To support 'whoami' add /usr/ucb to path + PATH=/usr/ucb:$PATH + export PATH + exec /usr/bin/ksh $0 "$@" +fi + +# clear it so if we invoke other scripts, they run as ksh +unset POSIX_SHELL + +RUNNER_SCRIPT_DIR=$(cd ${0%/*} && pwd -P) + +CALLER_DIR=$PWD RUNNER_BASE_DIR=${RUNNER_SCRIPT_DIR%/*} RUNNER_ETC_DIR=$RUNNER_BASE_DIR/etc -RUNNER_LOG_DIR=$RUNNER_BASE_DIR/log # Note the trailing slash on $PIPE_DIR/ PIPE_DIR=/tmp/$RUNNER_BASE_DIR/ RUNNER_USER= +WHOAMI=$(whoami) # Make sure this script is running as the appropriate user -if [ ! -z "$RUNNER_USER" ] && [ `whoami` != "$RUNNER_USER" ]; then - exec sudo -u $RUNNER_USER -i $0 $@ +if ([ "$RUNNER_USER" ] && [ "x$WHOAMI" != "x$RUNNER_USER" ]); then + type sudo > /dev/null 2>&1 + if [ $? -ne 0 ]; then + echo "sudo doesn't appear to be installed and your EUID isn't $RUNNER_USER" 1>&2 + exit 1 + fi + echo "Attempting to restart script through sudo -H -u $RUNNER_USER" >&2 + exec sudo -H -u $RUNNER_USER -i $RUNNER_SCRIPT_DIR/$RUNNER_SCRIPT $@ fi -# Make sure CWD is set to runner base dir -cd $RUNNER_BASE_DIR - -# Make sure log directory exists -mkdir -p $RUNNER_LOG_DIR # Identify the script name SCRIPT=`basename $0` @@ -29,22 +45,37 @@ START_ERL=`cat $RUNNER_BASE_DIR/releases/start_erl.data` ERTS_VSN=${START_ERL% *} APP_VSN=${START_ERL#* } -# Use releases/VSN/vm.args if it exists otherwise use etc/vm.args -if [ -e "$RUNNER_BASE_DIR/releases/$APP_VSN/vm.args" ]; then - VMARGS_PATH="$RUNNER_BASE_DIR/releases/$APP_VSN/vm.args" +# Use $CWD/vm.args if exists, otherwise releases/APP_VSN/vm.args, or +# else etc/vm.args +if [ -e "$CALLER_DIR/vm.args" ]; then + VMARGS_PATH=$CALLER_DIR/vm.args + USE_DIR=$CALLER_DIR else - VMARGS_PATH="$RUNNER_ETC_DIR/vm.args" + USE_DIR=$RUNNER_BASE_DIR + if [ -e "$RUNNER_BASE_DIR/releases/$APP_VSN/vm.args" ]; then + VMARGS_PATH="$RUNNER_BASE_DIR/releases/$APP_VSN/vm.args" + else + VMARGS_PATH="$RUNNER_ETC_DIR/vm.args" + fi fi +RUNNER_LOG_DIR=$USE_DIR/log +# Make sure log directory exists +mkdir -p $RUNNER_LOG_DIR + # Use releases/VSN/sys.config if it exists otherwise use etc/app.config -if [ -e "$RUNNER_BASE_DIR/releases/$APP_VSN/sys.config" ]; then - CONFIG_PATH="$RUNNER_BASE_DIR/releases/$APP_VSN/sys.config" +if [ -e "$USE_DIR/sys.config" ]; then + CONFIG_PATH="$USE_DIR/sys.config" else - CONFIG_PATH="$RUNNER_ETC_DIR/app.config" + if [ -e "$RUNNER_BASE_DIR/releases/$APP_VSN/sys.config" ]; then + CONFIG_PATH="$RUNNER_BASE_DIR/releases/$APP_VSN/sys.config" + else + CONFIG_PATH="$RUNNER_ETC_DIR/app.config" + fi fi # Extract the target node name from node.args -NAME_ARG=`egrep '^-s?name' $VMARGS_PATH` +NAME_ARG=`egrep '^\-s?name' $VMARGS_PATH` if [ -z "$NAME_ARG" ]; then echo "vm.args needs to have either -name or -sname parameter." exit 1 @@ -54,17 +85,24 @@ fi REMSH_TYPE=`echo $NAME_ARG | awk '{print $1}'` REMSH_NAME=`echo $NAME_ARG | awk '{print $2}'` -# Note the `date +%s`, used to allow multiple remsh to the same node transparently -REMSH_NAME_ARG="$REMSH_TYPE attach`date +%s`@`echo $REMSH_NAME | awk -F@ '{print $2}'`" +# Note the `date +%s`, used to allow multiple remsh to the same node +# transparently +REMSH_NAME_ARG="$REMSH_TYPE remsh`date +%s`@`echo $REMSH_NAME | awk -F@ '{print $2}'`" REMSH_REMSH_ARG="-remsh $REMSH_NAME" # Extract the target cookie -COOKIE_ARG=`grep '^-setcookie' $VMARGS_PATH` +COOKIE_ARG=`grep '^\-setcookie' $VMARGS_PATH` if [ -z "$COOKIE_ARG" ]; then echo "vm.args needs to have a -setcookie parameter." exit 1 fi +# Make sure CWD is set to the right dir +cd $USE_DIR + +# Make sure log directory exists +mkdir -p $USE_DIR/log + # Add ERTS bin dir to our path ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin @@ -74,47 +112,83 @@ NODETOOL="$ERTS_PATH/escript $ERTS_PATH/nodetool $NAME_ARG $COOKIE_ARG" # Setup remote shell command to control node REMSH="$ERTS_PATH/erl $REMSH_NAME_ARG $REMSH_REMSH_ARG $COOKIE_ARG" +# Common functions + +# Ping node without allowing nodetool to take stdin +ping_node() { + $NODETOOL ping < /dev/null +} + +# Set the PID global variable, return 1 on error +get_pid() { + PID=`$NODETOOL getpid < /dev/null` + ES=$? + if [ "$ES" -ne 0 ]; then + echo "Node is not running!" + return 1 + fi + + # don't allow empty or init pid's + if [ -z $PID ] || [ "$PID" -le 1 ]; then + return 1 + fi + + return 0 +} + # Check the first argument for instructions case "$1" in - start) + start|start_boot) # Make sure there is not already a node running - RES=`$NODETOOL ping` + RES=`ping_node` if [ "$RES" = "pong" ]; then echo "Node is already running!" exit 1 fi - shift # remove $1 + case "$1" in + start) + shift + START_OPTION="console" + HEART_OPTION="start" + ;; + start_boot) + shift + START_OPTION="console_boot" + HEART_OPTION="start_boot" + ;; + esac RUN_PARAM=$(printf "\'%s\' " "$@") - HEART_COMMAND="$RUNNER_BASE_DIR/bin/$SCRIPT start $RUN_PARAM" + HEART_COMMAND="$RUNNER_BASE_DIR/bin/$SCRIPT $HEART_OPTION $RUN_PARAM" export HEART_COMMAND mkdir -p $PIPE_DIR - $ERTS_PATH/run_erl -daemon $PIPE_DIR $RUNNER_LOG_DIR "exec $RUNNER_BASE_DIR/bin/$SCRIPT console $RUN_PARAM" 2>&1 + $ERTS_PATH/run_erl -daemon $PIPE_DIR $RUNNER_LOG_DIR "exec $RUNNER_BASE_DIR/bin/$SCRIPT $START_OPTION $RUN_PARAM" 2>&1 ;; stop) # Wait for the node to completely stop... case `uname -s` in - Linux|Darwin|FreeBSD|DragonFly|NetBSD|OpenBSD) - # PID COMMAND - PID=`ps ax -o pid= -o command=|\ - grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $1}'` - ;; - SunOS) - # PID COMMAND - PID=`ps -ef -o pid= -o args=|\ - grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $1}'` - ;; - CYGWIN*) - # UID PID PPID TTY STIME COMMAND - PID=`ps -efW|grep "$RUNNER_BASE_DIR/.*/[b]eam"|awk '{print $2}'` - ;; + Darwin) + # Make sure we explicitly set this because iTerm.app doesn't for + # some reason. + COMMAND_MODE=unix2003 esac + + # Get the PID from nodetool + get_pid + GPR=$? + if [ "$GPR" -ne 0 ] || [ -z $PID ]; then + exit $GPR + fi + + # Tell nodetool to initiate a stop $NODETOOL stop ES=$? if [ "$ES" -ne 0 ]; then exit $ES fi - while `kill -0 $PID 2>/dev/null`; + + # Wait for the node to completely stop... + while `kill -s 0 $PID 2>/dev/null` do sleep 1 done @@ -140,7 +214,7 @@ case "$1" in ping) ## See if the VM is alive - $NODETOOL ping + ping_node ES=$? if [ "$ES" -ne 0 ]; then exit $ES @@ -148,8 +222,8 @@ case "$1" in ;; attach) - # Make sure a node IS running - RES=`$NODETOOL ping` + # Make sure a node is running + ping_node ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" @@ -161,8 +235,8 @@ case "$1" in ;; remote_console) - # Make sure a node IS running - RES=`$NODETOOL ping` + # Make sure a node is running + ping_node ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" @@ -182,7 +256,7 @@ case "$1" in fi # Make sure a node IS running - RES=`$NODETOOL ping` + ping_node ES=$? if [ "$ES" -ne 0 ]; then echo "Node is not running!" @@ -195,12 +269,18 @@ case "$1" in $ERTS_PATH/escript $RUNNER_BASE_DIR/bin/install_upgrade.escript $node_name $erlang_cookie $2 ;; - console|console_clean) + console|console_clean|console_boot) # .boot file typically just $SCRIPT (ie, the app name) - # however, for debugging, sometimes start_clean.boot is useful: + # however, for debugging, sometimes start_clean.boot is useful. + # For e.g. 'setup', one may even want to name another boot script. case "$1" in console) BOOTFILE=$SCRIPT ;; console_clean) BOOTFILE=start_clean ;; + console_boot) + shift + BOOTFILE="$1" + shift + ;; esac # Setup beam-required vars ROOTDIR=$RUNNER_BASE_DIR @@ -249,8 +329,17 @@ case "$1" in # Start the VM exec $CMD -- ${1+"$@"} ;; + getpid) + # Get the PID from nodetool + get_pid + ES=$? + if [ "$ES" -ne 0 ] || [ -z $PID ]; then + exit $ES + fi + echo $PID + ;; *) - echo "Usage: $SCRIPT {start|foreground|stop|restart|reboot|ping|console|console_clean|attach|remote_console|upgrade}" + echo "Usage: $SCRIPT {start|start_boot <file>|foreground|stop|restart|reboot|ping|console|getpid|console_clean|console_boot <file>|attach|remote_console|upgrade}" exit 1 ;; esac diff --git a/test/upgrade_project/rel/files/erl b/test/upgrade_project/rel/files/erl index 6f65e3f..f4c63af 100755 --- a/test/upgrade_project/rel/files/erl +++ b/test/upgrade_project/rel/files/erl @@ -1,16 +1,26 @@ #!/bin/sh -## This script replaces the default "erl" in erts-VSN/bin. This is necessary -## as escript depends on erl and in turn, erl depends on having access to a -## bootscript (start.boot). Note that this script is ONLY invoked as a side-effect -## of running escript -- the embedded node bypasses erl and uses erlexec directly -## (as it should). +# /bin/sh on Solaris is not a POSIX compatible shell, but /usr/bin/ksh is. +if [ `uname -s` = 'SunOS' -a "${POSIX_SHELL}" != "true" ]; then + POSIX_SHELL="true" + export POSIX_SHELL + exec /usr/bin/ksh $0 "$@" +fi + +# clear it so if we invoke other scripts, they run as ksh as well +unset POSIX_SHELL + +## This script replaces the default "erl" in erts-VSN/bin. This is +## necessary as escript depends on erl and in turn, erl depends on +## having access to a bootscript (start.boot). Note that this script +## is ONLY invoked as a side-effect of running escript -- the embedded +## node bypasses erl and uses erlexec directly (as it should). ## -## Note that this script makes the assumption that there is a start_clean.boot -## file available in $ROOTDIR/release/VSN. +## Note that this script makes the assumption that there is a +## start_clean.boot file available in $ROOTDIR/release/VSN. # Determine the abspath of where this script is executing from. -ERTS_BIN_DIR=$(cd ${0%/*} && pwd) +ERTS_BIN_DIR=$(cd ${0%/*} && pwd -P) # Now determine the root directory -- this script runs from erts-VSN/bin, # so we simply need to strip off two dirs from the end of the ERTS_BIN_DIR diff --git a/test/upgrade_project/rel/files/nodetool b/test/upgrade_project/rel/files/nodetool index eb08fa4..ce06c6a 100644 --- a/test/upgrade_project/rel/files/nodetool +++ b/test/upgrade_project/rel/files/nodetool @@ -1,3 +1,4 @@ +#!/usr/bin/env escript %% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*- %% ex: ft=erlang ts=4 sw=4 et %% ------------------------------------------------------------------- @@ -5,25 +6,51 @@ %% nodetool: Helper Script for interacting with live nodes %% %% ------------------------------------------------------------------- - main(Args) -> ok = start_epmd(), %% Extract the args {RestArgs, TargetNode} = process_args(Args, [], undefined), + %% any commands that don't need a running node + case RestArgs of + ["chkconfig", File] -> + case file:consult(File) of + {ok, _} -> + io:format("ok\n"), + halt(0); + {error, {Line, Mod, Term}} -> + io:format(standard_error, ["Error on line ", + file:format_error({Line, Mod, Term}), "\n"], []), + halt(1); + {error, R} -> + io:format(standard_error, ["Error reading config file: ", + file:format_error(R), "\n"], []), + halt(1) + end; + _ -> + ok + end, + %% See if the node is currently running -- if it's not, we'll bail - case {net_kernel:hidden_connect_node(TargetNode), net_adm:ping(TargetNode)} of + case {net_kernel:hidden_connect_node(TargetNode), + net_adm:ping(TargetNode)} of {true, pong} -> ok; + {false,pong} -> + io:format("Failed to connect to node ~p .\n", [TargetNode]), + halt(1); {_, pang} -> io:format("Node ~p not responding to pings.\n", [TargetNode]), halt(1) end, case RestArgs of + ["getpid"] -> + io:format("~p\n", + [list_to_integer(rpc:call(TargetNode, os, getpid, []))]); ["ping"] -> - %% If we got this far, the node already responsed to a ping, so just dump - %% a "pong" + %% If we got this far, the node already responsed to a + %% ping, so just dump a "pong" io:format("pong\n"); ["stop"] -> io:format("~p\n", [rpc:call(TargetNode, init, stop, [], 60000)]); @@ -32,7 +59,9 @@ main(Args) -> ["reboot"] -> io:format("~p\n", [rpc:call(TargetNode, init, reboot, [], 60000)]); ["rpc", Module, Function | RpcArgs] -> - case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), + case rpc:call(TargetNode, + list_to_atom(Module), + list_to_atom(Function), [RpcArgs], 60000) of ok -> ok; @@ -42,8 +71,23 @@ main(Args) -> _ -> halt(1) end; + ["rpc_infinity", Module, Function | RpcArgs] -> + case rpc:call(TargetNode, + list_to_atom(Module), + list_to_atom(Function), + [RpcArgs], infinity) of + ok -> + ok; + {badrpc, Reason} -> + io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), + halt(1); + _ -> + halt(1) + end; ["rpcterms", Module, Function, ArgsAsString] -> - case rpc:call(TargetNode, list_to_atom(Module), list_to_atom(Function), + case rpc:call(TargetNode, + list_to_atom(Module), + list_to_atom(Function), consult(ArgsAsString), 60000) of {badrpc, Reason} -> io:format("RPC to ~p failed: ~p\n", [TargetNode, Reason]), @@ -53,7 +97,7 @@ main(Args) -> end; Other -> io:format("Other: ~p\n", [Other]), - io:format("Usage: nodetool {ping|stop|restart|reboot}\n") + io:format("Usage: nodetool {chkconfig|getpid|ping|stop|restart|reboot|rpc|rpc_infinity|rpcterms}\n") end, net_kernel:stop(). diff --git a/test/upgrade_project/rel/files/sys.config b/test/upgrade_project/rel/files/sys.config index d7677e7..3b7f6bd 100644 --- a/test/upgrade_project/rel/files/sys.config +++ b/test/upgrade_project/rel/files/sys.config @@ -8,3 +8,4 @@ {error_logger_mf_maxfiles, 5} % 5 files max ]} ]. + diff --git a/test/upgrade_project/rel/files/vm.args b/test/upgrade_project/rel/files/vm.args index 2d64e25..a9aeb64 100644 --- a/test/upgrade_project/rel/files/vm.args +++ b/test/upgrade_project/rel/files/vm.args @@ -9,11 +9,11 @@ ##-heart ## Enable kernel poll and a few async threads -+K true -+A 5 +##+K true +##+A 5 ## Increase number of concurrent ports/sockets --env ERL_MAX_PORTS 4096 +##-env ERL_MAX_PORTS 4096 ## Tweak GC to run more often --env ERL_FULLSWEEP_AFTER 10 +##-env ERL_FULLSWEEP_AFTER 10 |