Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge remote-tracking branch 'upstream/master'

  • Loading branch information...
commit 867a915ead4dde5e18ee6d5d4ecb256fe12faa99 2 parents 6fb336d + 2e36109
@mdaguete mdaguete authored
Showing with 1,831 additions and 768 deletions.
  1. +4 −0 .gitignore
  2. +14 −5 Makefile
  3. +29 −11 README.md
  4. +16 −0 THANKS
  5. +13 −1 bootstrap
  6. +2 −0  dialyzer_reference
  7. +0 −3  include/rebar.hrl
  8. +2 −1  inttest/ct1/ct1_rt.erl
  9. +2 −0  inttest/ct1/rebar.config
  10. +2 −2 priv/shell-completion/bash/rebar
  11. +3 −1 priv/templates/basicnif.c
  12. +0 −4 priv/templates/simplemod.erl
  13. +44 −0 priv/templates/simplenode.install_upgrade.escript
  14. +14 −11 priv/templates/simplenode.reltool.config
  15. +79 −7 priv/templates/simplenode.runner
  16. +1 −0  priv/templates/simplenode.template
  17. +4 −4 priv/templates/simplenode.vm.args
  18. +33 −5 priv/templates/simplenode.windows.runner.cmd
  19. +1 −1  priv/templates/simplesrv.erl
  20. +0 −4 rebar.bat
  21. +10 −1 rebar.config
  22. +27 −15 rebar.config.sample
  23. +206 −93 src/getopt.erl
  24. +5 −0 src/mustache.erl
  25. +28 −8 src/rebar.erl
  26. +8 −9 src/rebar_abnfc_compiler.erl
  27. +45 −54 src/rebar_app_utils.erl
  28. +1 −1  src/rebar_appups.erl
  29. +6 −2 src/rebar_asn1_compiler.erl
  30. +60 −11 src/rebar_config.erl
  31. +199 −98 src/rebar_core.erl
  32. +25 −32 src/rebar_ct.erl
  33. +32 −14 src/rebar_deps.erl
  34. +7 −1 src/rebar_erlc_compiler.erl
  35. +20 −16 src/rebar_erlydtl_compiler.erl
  36. +69 −41 src/rebar_eunit.erl
  37. +19 −0 src/rebar_file_utils.erl
  38. +9 −11 src/rebar_lfe_compiler.erl
  39. +14 −7 src/rebar_log.erl
  40. +18 −19 src/rebar_neotoma_compiler.erl
  41. +352 −192 src/rebar_port_compiler.erl
  42. +68 −14 src/rebar_rel_utils.erl
  43. +5 −3 src/rebar_reltool.erl
  44. +14 −5 src/rebar_subdirs.erl
  45. +2 −2 src/rebar_templater.erl
  46. +43 −19 src/rebar_upgrade.erl
  47. +175 −30 src/rebar_utils.erl
  48. +19 −3 src/rebar_xref.erl
  49. +1 −0  test/upgrade_project/README.md
  50. +79 −7 test/upgrade_project/rel/files/dummy
  51. +2 −0  test/upgrade_project/rel/reltool.config
View
4 .gitignore
@@ -6,3 +6,7 @@ rebar
rt.work
.hgignore
.eunit
+dialyzer_warnings
+xref_warnings
+rebar.cmd
+rebar.ps1
View
19 Makefile
@@ -1,3 +1,5 @@
+.PHONY: dialyzer_warnings xref_warnings
+
all:
./bootstrap
@@ -5,9 +7,16 @@ clean:
@rm -rf rebar ebin/*.beam inttest/rt.work
debug:
- ./bootstrap debug
+ @./bootstrap debug
+
+check: debug xref dialyzer
+
+xref:
+ @./rebar xref
+
+dialyzer: dialyzer_warnings
+ @diff -U0 dialyzer_reference dialyzer_warnings
-check: debug
- -@./rebar xref
- -@dialyzer ebin --verbose -Wunmatched_returns -Werror_handling \
- -Wrace_conditions
+dialyzer_warnings:
+ -@dialyzer -q -n ebin -Wunmatched_returns -Werror_handling \
+ -Wrace_conditions > dialyzer_warnings
View
40 README.md
@@ -14,8 +14,9 @@ locations (git, hg, etc).
Building
--------
-Information on building and installing Erlang/OTP can be found
-in the `INSTALL.md` document.
+Information on building and installing [Erlang/OTP](http://www.erlang.org)
+can be found [here](https://github.com/erlang/otp/wiki/Installation)
+([more info](https://github.com/erlang/otp/blob/master/INSTALL.md)).
### Dependencies
@@ -28,7 +29,9 @@ Should you want to clone the rebar repository, you will also require git.
Clone the git repository:
- $ git clone git://github.com/basho/rebar.git
+```sh
+$ git clone git://github.com/basho/rebar.git
+```
#### Building rebar
@@ -69,7 +72,8 @@ Do not mix spaces and tabs.
Do not introduce lines longer than 80 characters.
[erlang-mode (emacs)](http://www.erlang.org/doc/man/erlang.el.html) indentation is preferred.
-vi-only users are encouraged to give [Vim emulation](http://emacswiki.org/emacs/Evil) ([more info](https://gitorious.org/evil/pages/Home)) a try.
+vi-only users are encouraged to give [Vim emulation](http://emacswiki.org/emacs/Evil)
+([more info](https://gitorious.org/evil/pages/Home)) a try.
Writing Commit Messages
-----------------------
@@ -106,18 +110,32 @@ Longer description (wrap at 72 characters)
Dialyzer and Tidier
-------------------
-Before you submit a patch check for discrepancies with
-[Dialyzer](http://www.erlang.org/doc/man/dialyzer.html):
+Before you submit a patch check for
+[xref](http://www.erlang.org/doc/man/xref.html) and
+[Dialyzer](http://www.erlang.org/doc/man/dialyzer.html)
+warnings.
+
+A successful run of ``make check`` looks like:
```sh
$ make check
+Recompile: src/rebar_core
+==> rebar (compile)
+Command 'debug' not understood or not applicable
+Congratulations! You now have a self-contained script called "rebar" in
+your current working directory. Place this script anywhere in your path
+and you can use rebar to build OTP-compliant apps.
+==> rebar (xref)
+make: [dialyzer_warnings] Error 2 (ignored)
```
-The following discrepancies are known and safe to ignore:
-
-```
-rebar_utils.erl:147: Call to missing or unexported function escript:foldl/3
-```
+[xref](http://www.erlang.org/doc/man/xref.html) and
+[Dialyzer](http://www.erlang.org/doc/man/dialyzer.html) warnings are compared
+against a set of safe-to-ignore warnings
+found in
+[dialyzer_reference](https://raw.github.com/tuncer/rebar/maint/dialyzer_reference)
+and
+[xref_reference](https://raw.github.com/tuncer/rebar/maint/xref_reference).
It is **strongly recommended** to check the code with
[Tidier](http://tidier.softlab.ntua.gr:20000/tidier/getstarted).
View
16 THANKS
@@ -75,3 +75,19 @@ Anton Lavrik
Jan Vincent Liwanag
Przemyslaw Dabek
Fabian Linzberger
+Smith Winston
+Jesse Gumm
+Torbjorn Tornkvist
+Ali Sabil
+Tomas Abrahamsson
+Francis Joanis
+fisher@yun.io
+Yurin Slava
+Phillip Toland
+Mike Lazar
+Loic Hoguin
+Ali Yakout
+Adam Schepis
+Amit Kapoor
+Ulf Wiger
+Nick Vatamaniuc
View
14 bootstrap
@@ -79,11 +79,15 @@ main(Args) ->
halt(1)
end,
- %% Finally, update executable perms for our script
+ %% Finally, update executable perms for our script on *nix,
+ %% or write out script files on win32.
case os:type() of
{unix,_} ->
[] = os:cmd("chmod u+x rebar"),
ok;
+ {win32,_} ->
+ write_windows_scripts(),
+ ok;
_ ->
ok
end,
@@ -126,3 +130,11 @@ vcs_info([{Id, Dir, Cmd} | Rest]) ->
false ->
vcs_info(Rest)
end.
+
+write_windows_scripts() ->
+ CmdScript=
+ "@echo off\r\n"
+ "setlocal\r\n"
+ "set rebarscript=%~f0\r\n"
+ "escript.exe \"%rebarscript:.cmd=%\" %*\r\n",
+ ok = file:write_file("rebar.cmd", CmdScript).
View
2  dialyzer_reference
@@ -0,0 +1,2 @@
+
+rebar_utils.erl:154: Call to missing or unexported function escript:foldl/3
View
3  include/rebar.hrl
@@ -10,6 +10,3 @@
-define(ERROR(Str, Args), rebar_log:log(error, Str, Args)).
-define(FMT(Str, Args), lists:flatten(io_lib:format(Str, Args))).
-
--define(DEPRECATED(Key, Old, New, Opts, When),
- rebar_utils:deprecated(Key, Old, New, Opts, When)).
View
3  inttest/ct1/ct1_rt.erl
@@ -6,7 +6,8 @@
files() ->
[{create, "ebin/a1.app", app(a1)},
{copy, "../../rebar", "rebar"},
- {copy, "test_SUITE.erl", "test/test_SUITE.erl"}].
+ {copy, "rebar.config", "rebar.config"},
+ {copy, "test_SUITE.erl", "itest/test_SUITE.erl"}].
run(_Dir) ->
{ok, _} = retest:sh("./rebar compile ct"),
View
2  inttest/ct1/rebar.config
@@ -0,0 +1,2 @@
+
+{ct_dir, "itest"}.
View
4 priv/shell-completion/bash/rebar
@@ -11,8 +11,8 @@ _rebar()
cmdsnvars="check-deps clean compile create create-app create-node \
ct doc delete-deps eunit get-deps generate generate-upgrade \
help list-deps list-templates update-deps version xref overlay \
- case= force=1 jobs= suite= verbose=1 appid= previous_release= \
- skip_deps=true skip_app= template= template_dir="
+ apps= case= force=1 jobs= suites= verbose=1 appid= previous_release= \
+ nodeid= root_dir= skip_deps=true skip_apps= template= template_dir="
if [[ ${cur} == --* ]] ; then
COMPREPLY=( $(compgen -W "${lopts}" -- ${cur}) )
View
4 priv/templates/basicnif.c
@@ -1,6 +1,6 @@
#include "erl_nif.h"
-static ErlNifResourceType* {{module}}_RESOURCE;
+static ErlNifResourceType* {{module}}_RESOURCE = NULL;
typedef struct
{
@@ -51,6 +51,8 @@ static int on_load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info)
if (rt == NULL)
return -1;
+ {{module}}_RESOURCE = rt;
+
return 0;
}
View
4 priv/templates/simplemod.erl
@@ -2,9 +2,5 @@
-export([my_func/0]).
--ifdef(TEST).
--compile(export_all).
--endif.
-
my_func() ->
ok.
View
44 priv/templates/simplenode.install_upgrade.escript
@@ -0,0 +1,44 @@
+#!/usr/bin/env escript
+%%! -noshell -noinput
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ft=erlang ts=4 sw=4 et
+
+-define(TIMEOUT, 60000).
+-define(INFO(Fmt,Args), io:format(Fmt,Args)).
+
+main([NodeName, Cookie, ReleasePackage]) ->
+ TargetNode = start_distribution(NodeName, Cookie),
+ {ok, Vsn} = rpc:call(TargetNode, release_handler, unpack_release,
+ [ReleasePackage], ?TIMEOUT),
+ ?INFO("Unpacked Release ~p~n", [Vsn]),
+ {ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler,
+ check_install_release, [Vsn], ?TIMEOUT),
+ {ok, OtherVsn, Desc} = rpc:call(TargetNode, release_handler,
+ install_release, [Vsn], ?TIMEOUT),
+ ?INFO("Installed Release ~p~n", [Vsn]),
+ ok = rpc:call(TargetNode, release_handler, make_permanent, [Vsn], ?TIMEOUT),
+ ?INFO("Made Release ~p Permanent~n", [Vsn]);
+main(_) ->
+ init:stop(1).
+
+start_distribution(NodeName, Cookie) ->
+ MyNode = make_script_node(NodeName),
+ {ok, _Pid} = net_kernel:start([MyNode, shortnames]),
+ erlang:set_cookie(node(), list_to_atom(Cookie)),
+ TargetNode = make_target_node(NodeName),
+ case {net_kernel:hidden_connect_node(TargetNode),
+ net_adm:ping(TargetNode)} of
+ {true, pong} ->
+ ok;
+ {_, pang} ->
+ io:format("Node ~p not responding to pings.\n", [TargetNode]),
+ init:stop(1)
+ end,
+ TargetNode.
+
+make_target_node(Node) ->
+ [_, Host] = string:tokens(atom_to_list(node()), "@"),
+ list_to_atom(lists:concat([Node, "@", Host])).
+
+make_script_node(Node) ->
+ list_to_atom(lists:concat([Node, "_upgrader_", os:getpid()])).
View
25 priv/templates/simplenode.reltool.config
@@ -1,10 +1,13 @@
{sys, [
{lib_dirs, []},
+ {erts, [{mod_cond, derived}, {app_file, strip}]},
+ {app_file, strip},
{rel, "{{nodeid}}", "1",
[
kernel,
stdlib,
- sasl
+ sasl,
+ {{nodeid}}
]},
{rel, "start_clean", "",
[
@@ -13,16 +16,15 @@
]},
{boot_rel, "{{nodeid}}"},
{profile, embedded},
+ {incl_cond, exclude},
{excl_archive_filters, [".*"]}, %% Do not archive built libs
- {excl_sys_filters, ["^bin/.*",
- "^erts.*/bin/(dialyzer|typer)"]},
-
- %% Including HiPE can cause issues generating your first upgrade.
- %% If you plan to distribute HiPE in your release remove the
- %% following line.
- {app, hipe, [{incl_cond, exclude}]},
-
- {app, sasl, [{incl_cond, include}]}
+ {excl_sys_filters, ["^bin/.*", "^erts.*/bin/(dialyzer|typer)",
+ "^erts.*/(doc|info|include|lib|man|src)"]},
+ {excl_app_filters, ["\.gitignore"]},
+ {app, sasl, [{incl_cond, include}]},
+ {app, stdlib, [{incl_cond, include}]},
+ {app, kernel, [{incl_cond, include}]},
+ {app, {{nodeid}}, [{incl_cond, include}]}
]}.
{target_dir, "{{nodeid}}"}.
@@ -32,8 +34,9 @@
{copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"},
{copy, "files/nodetool", "\{\{erts_vsn\}\}/bin/nodetool"},
{copy, "files/{{nodeid}}", "bin/{{nodeid}}"},
- {copy, "files/sys.config", "releases/\{\{rel_vsn\}\}/sys.config"},
{copy, "files/{{nodeid}}.cmd", "bin/{{nodeid}}.cmd"},
{copy, "files/start_erl.cmd", "bin/start_erl.cmd"},
+ {copy, "files/install_upgrade.escript", "bin/install_upgrade.escript"},
+ {copy, "files/sys.config", "releases/\{\{rel_vsn\}\}/sys.config"},
{copy, "files/vm.args", "releases/\{\{rel_vsn\}\}/vm.args"}
]}.
View
86 priv/templates/simplenode.runner
@@ -50,6 +50,14 @@ if [ -z "$NAME_ARG" ]; then
exit 1
fi
+# Extract the name type and name from the NAME_ARG for REMSH
+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 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`
if [ -z "$COOKIE_ARG" ]; then
@@ -63,6 +71,9 @@ ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin
# Setup command to control the node
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"
+
# Check the first argument for instructions
case "$1" in
start)
@@ -72,11 +83,12 @@ case "$1" in
echo "Node is already running!"
exit 1
fi
- HEART_COMMAND="$RUNNER_BASE_DIR/bin/$SCRIPT start"
+ shift # remove $1
+ RUN_PARAM=$(printf "\'%s\' " "$@")
+ HEART_COMMAND="$RUNNER_BASE_DIR/bin/$SCRIPT start $RUN_PARAM"
export HEART_COMMAND
mkdir -p $PIPE_DIR
- shift # remove $1
- $ERTS_PATH/run_erl -daemon $PIPE_DIR $RUNNER_LOG_DIR "exec $RUNNER_BASE_DIR/bin/$SCRIPT console $@" 2>&1
+ $ERTS_PATH/run_erl -daemon $PIPE_DIR $RUNNER_LOG_DIR "exec $RUNNER_BASE_DIR/bin/$SCRIPT console $RUN_PARAM" 2>&1
;;
stop)
@@ -148,6 +160,41 @@ case "$1" in
exec $ERTS_PATH/to_erl $PIPE_DIR
;;
+ remote_console)
+ # Make sure a node IS running
+ RES=`$NODETOOL ping`
+ ES=$?
+ if [ "$ES" -ne 0 ]; then
+ echo "Node is not running!"
+ exit $ES
+ fi
+
+ shift
+ exec $REMSH
+ ;;
+
+ upgrade)
+ if [ -z "$2" ]; then
+ echo "Missing upgrade package argument"
+ echo "Usage: $SCRIPT upgrade {package base name}"
+ echo "NOTE {package base name} MUST NOT include the .tar.gz suffix"
+ exit 1
+ fi
+
+ # Make sure a node IS running
+ RES=`$NODETOOL ping`
+ ES=$?
+ if [ "$ES" -ne 0 ]; then
+ echo "Node is not running!"
+ exit $ES
+ fi
+
+ node_name=`echo $NAME_ARG | awk '{print $2}'`
+ erlang_cookie=`echo $COOKIE_ARG | awk '{print $2}'`
+
+ $ERTS_PATH/escript $RUNNER_BASE_DIR/bin/install_upgrade.escript $node_name $erlang_cookie $2
+ ;;
+
console|console_clean)
# .boot file typically just $SCRIPT (ie, the app name)
# however, for debugging, sometimes start_clean.boot is useful:
@@ -160,25 +207,50 @@ case "$1" in
BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin
EMU=beam
PROGNAME=`echo $0 | sed 's/.*\\///'`
- CMD="$BINDIR/erlexec -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -mode embedded -config $CONFIG_PATH -args_file $VMARGS_PATH -- ${1+"$@"}"
+ CMD="$BINDIR/erlexec -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -mode embedded -config $CONFIG_PATH -args_file $VMARGS_PATH"
export EMU
export ROOTDIR
export BINDIR
export PROGNAME
# Dump environment info for logging purposes
- echo "Exec: $CMD"
+ echo "Exec: $CMD" -- ${1+"$@"}
echo "Root: $ROOTDIR"
# Log the startup
logger -t "$SCRIPT[$$]" "Starting up"
# Start the VM
- exec $CMD
+ exec $CMD -- ${1+"$@"}
;;
+ foreground)
+ # start up the release in the foreground for use by runit
+ # or other supervision services
+
+ BOOTFILE=$SCRIPT
+ FOREGROUNDOPTIONS="-noinput +Bd"
+
+ # Setup beam-required vars
+ ROOTDIR=$RUNNER_BASE_DIR
+ BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin
+ EMU=beam
+ PROGNAME=`echo $0 | sed 's/.*\///'`
+ CMD="$BINDIR/erlexec $FOREGROUNDOPTIONS -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -config $CONFIG_PATH -args_file $VMARGS_PATH"
+ export EMU
+ export ROOTDIR
+ export BINDIR
+ export PROGNAME
+
+ # Dump environment info for logging purposes
+ echo "Exec: $CMD" -- ${1+"$@"}
+ echo "Root: $ROOTDIR"
+
+ # Start the VM
+ exec $CMD -- ${1+"$@"}
+ ;;
*)
- echo "Usage: $SCRIPT {start|stop|restart|reboot|ping|console|console_clean|attach}"
+ echo "Usage: $SCRIPT {start|foreground|stop|restart|reboot|ping|console|console_clean|attach|remote_console|upgrade}"
exit 1
;;
esac
View
1  priv/templates/simplenode.template
@@ -10,3 +10,4 @@
{template, "simplenode.vm.args", "files/vm.args"}.
{template, "simplenode.windows.runner.cmd", "files/{{nodeid}}.cmd"}.
{file, "simplenode.windows.start_erl.cmd", "files/start_erl.cmd"}.
+{file, "simplenode.install_upgrade.escript", "files/install_upgrade.escript"}.
View
8 priv/templates/simplenode.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
View
38 priv/templates/simplenode.windows.runner.cmd
@@ -2,7 +2,7 @@
@set node_name={{nodeid}}
-@rem Get the abolute path to the parent directory,
+@rem Get the absolute path to the parent directory,
@rem which is assumed to be the node root.
@for /F "delims=" %%I in ("%~dp0..") do @set node_root=%%~fI
@@ -14,24 +14,32 @@
@call :set_trim release_version %%J
)
+@rem extract erlang cookie from vm.args
+@set vm_args=%releases_dir%\%release_version%\vm.args
+@for /f "usebackq tokens=1-2" %%I in (`findstr /b \-setcookie %vm_args%`) do @set erlang_cookie=%%J
+
@set erts_bin=%node_root%\erts-%erts_version%\bin
@set service_name=%node_name%_%release_version%
+@if "%1"=="usage" @goto usage
@if "%1"=="install" @goto install
@if "%1"=="uninstall" @goto uninstall
@if "%1"=="start" @goto start
@if "%1"=="stop" @goto stop
@if "%1"=="restart" @call :stop && @goto start
@if "%1"=="console" @goto console
-@rem TODO: attach, ping, restart and reboot
+@if "%1"=="query" @goto query
+@if "%1"=="attach" @goto attach
+@if "%1"=="upgrade" @goto upgrade
+@echo Unknown command: "%1"
:usage
-@echo Usage: %0 {install|uninstall|start|stop|restart|console}
+@echo Usage: %~n0 [install^|uninstall^|start^|stop^|restart^|console^|query^|attach^|upgrade]
@goto :EOF
:install
-@%erts_bin%\erlsrv.exe add %service_name% -c "Erlang node %node_name% in %node_root%" -w %node_root% -m %node_root%\bin\start_erl.cmd -args " ++ %node_name% ++ %node_root%" -stopaction "init:stop()."
+@%erts_bin%\erlsrv.exe add %service_name% -c "Erlang node %node_name% in %node_root%" -sname %node_name% -w %node_root% -m %node_root%\bin\start_erl.cmd -args " ++ %node_name% ++ %node_root%" -stopaction "init:stop()."
@goto :EOF
:uninstall
@@ -48,7 +56,27 @@
@goto :EOF
:console
-@start %erts_bin%\werl.exe -boot %releases_dir%\%release_version%\%node_name%
+@start %erts_bin%\werl.exe -boot %releases_dir%\%release_version%\%node_name% -config %releases_dir%\%release_version%\sys.config -args_file %vm_args% -sname %node_name%
+@goto :EOF
+
+:query
+@%erts_bin%\erlsrv.exe list %service_name%
+@exit /b %ERRORLEVEL%
+@goto :EOF
+
+:attach
+@for /f "usebackq" %%I in (`hostname`) do @set hostname=%%I
+start %erts_bin%\werl.exe -boot %releases_dir%\%release_version%\start_clean -remsh %node_name%@%hostname% -sname console -setcookie %erlang_cookie%
+@goto :EOF
+
+:upgrade
+@if "%2"=="" (
+ @echo Missing upgrade package argument
+ @echo Usage: %~n0 upgrade {package base name}
+ @echo NOTE {package base name} MUST NOT include the .tar.gz suffix
+ @goto :EOF
+)
+@%erts_bin%\escript.exe %node_root%\bin\install_upgrade.escript %node_name% %erlang_cookie% %2
@goto :EOF
:set_trim
View
2  priv/templates/simplesrv.erl
@@ -30,7 +30,7 @@ init(Args) ->
{ok, Args}.
handle_call(_Request, _From, State) ->
- {noreply, ok, State}.
+ {reply, ok, State}.
handle_cast(_Msg, State) ->
{noreply, State}.
View
4 rebar.bat
@@ -1,4 +0,0 @@
-@echo off
-setlocal
-set rebarscript=%~f0
-escript.exe "%rebarscript:.bat=%" %*
View
11 rebar.config
@@ -3,4 +3,13 @@
{app_bin, ["priv/rebar"]}.
{erl_opts, [warnings_as_errors]}.
-{xref_checks, [undefined_function_calls]}.
+{xref_checks, []}.
+{xref_queries,
+ [{"(XC - UC) || (XU - X - B
+ - (\"escript\":\"foldl\"/\"3\")
+ - (\"abnfc\":\"file\"/\"2\")
+ - (\"erlydtl\":\"compile\"/\"3\")
+ - (\"lfe_comp\":\"file\"/\"2\")
+ - (\"neotoma\":\"file\"/\"2\")
+ - (\"protobuffs_compile\":\"scan_file\"/\"1\"))",
+ []}]}.
View
42 rebar.config.sample
@@ -18,7 +18,7 @@
%% Erlang compiler options
{erl_opts, [no_debug_info, {i, "myinclude"}, {src_dirs, ["src1", "src2"]},
{platform_define,
- "(linux|solaris|freebsd|darwin)", 'HAVE_SENDFILE'},
+ "(linux|solaris|freebsd|darwin)", 'HAVE_SENDFILE'},
{platform_define, "(linux|freebsd)", 'BACKLOG', 128},
{platform_define, "R13", 'old_inets'}]}.
@@ -35,22 +35,18 @@
%% == Port Compiler ==
-%% List of filenames or wildcards to be compiled. May also contain a tuple
-%% consisting of a regular expression to be applied against the system
-%% architecture and a list of filenames or wildcards to include should the
-%% expression pass. Default is `"c_src/*.c"'
-{port_sources, ["c_src/*.c", {"R14", ["c_src/*.c"]}]}.
-
%% Port compilation environment variables. See rebar_port_compiler.erl for
%% more info. Default is `[]'
-{port_envs, []}.
+{port_env, [{"CFLAGS", "$CFLAGS -Ifoo"},
+ {"freebsd", "LDFLAGS", "$LDFLAGS -lfoo"}]}.
-%% Custom name of the port driver .so file. Defaults to `<Application>_drv.so'.
-{so_name, "driver.so"}.
-
-%% so_specs - useful for building multiple *.so files
-%% from one or more object files
-{so_specs, [{"priv/so_name.so", ["c_src/object_file_name.o"]}]}.
+%% port_specs
+%% List of filenames or wildcards to be compiled. May also contain a tuple
+%% consisting of a regular expression to be applied against the system
+%% architecture as a filter.
+{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, []}]}}.
%% == LFE Compiler ==
@@ -83,6 +79,9 @@
%% == Common Test ==
+%% Override the default "test" directory in which SUITEs are located
+{ct_dir, "itest"}.
+
%% Option to pass extra parameters when launching Common Test
{ct_extra_params, "-boot start_sasl -s myapp"}.
@@ -139,14 +138,27 @@
%% == Pre/Post Command Hooks ==
{pre_hooks, [{clean, "./prepare_package_files.sh"},
- {compile, "escript generate_headers"}]}.
+ {"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"}]}.
%% == xref ==
{xref_warnings, false}.
+
%% xref checks to run
{xref_checks, [exports_not_used, undefined_function_calls]}.
+
+%% 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
+%% functions from undefined external function calls as those are in a
+%% generated module
+{xref_queries,
+ [{"(XC - UC) || (XU - X - B"
+ " - (\"ejabberd_logger\":\".*_msg\"/\"4\"))",[]}]}.
View
299 src/getopt.erl
@@ -53,6 +53,8 @@
ArgSpec :: arg_spec(),
Help :: string() | undefined
}.
+%% Output streams
+-type output_stream() :: 'standard_io' | 'standard_error'.
%% @doc Parse the command line options and arguments returning a list of tuples
@@ -79,26 +81,25 @@ parse(OptSpecList, CmdLine) ->
{ok, {[option()], [string()]}}.
%% Process the option terminator.
parse(OptSpecList, OptAcc, ArgAcc, _ArgPos, ["--" | Tail]) ->
- % Any argument present after the terminator is not considered an option.
+ %% Any argument present after the terminator is not considered an option.
{ok, {lists:reverse(append_default_options(OptSpecList, OptAcc)), lists:reverse(ArgAcc, Tail)}};
%% Process long options.
parse(OptSpecList, OptAcc, ArgAcc, ArgPos, ["--" ++ OptArg = OptStr | Tail]) ->
- parse_option_long(OptSpecList, OptAcc, ArgAcc, ArgPos, Tail, OptStr, OptArg);
+ parse_long_option(OptSpecList, OptAcc, ArgAcc, ArgPos, Tail, OptStr, OptArg);
%% Process short options.
parse(OptSpecList, OptAcc, ArgAcc, ArgPos, ["-" ++ ([_Char | _] = OptArg) = OptStr | Tail]) ->
- parse_option_short(OptSpecList, OptAcc, ArgAcc, ArgPos, Tail, OptStr, OptArg);
+ parse_short_option(OptSpecList, OptAcc, ArgAcc, ArgPos, Tail, OptStr, OptArg);
%% Process non-option arguments.
parse(OptSpecList, OptAcc, ArgAcc, ArgPos, [Arg | Tail]) ->
case find_non_option_arg(OptSpecList, ArgPos) of
{value, OptSpec} when ?IS_OPT_SPEC(OptSpec) ->
- parse(OptSpecList, [convert_option_arg(OptSpec, Arg) | OptAcc],
- ArgAcc, ArgPos + 1, Tail);
+ parse(OptSpecList, add_option_with_arg(OptSpec, Arg, OptAcc), ArgAcc, ArgPos + 1, Tail);
false ->
parse(OptSpecList, OptAcc, [Arg | ArgAcc], ArgPos, Tail)
end;
parse(OptSpecList, OptAcc, ArgAcc, _ArgPos, []) ->
- % Once we have completed gathering the options we add the ones that were
- % not present but had default arguments in the specification.
+ %% Once we have completed gathering the options we add the ones that were
+ %% not present but had default arguments in the specification.
{ok, {lists:reverse(append_default_options(OptSpecList, OptAcc)), lists:reverse(ArgAcc)}}.
@@ -108,14 +109,14 @@ parse(OptSpecList, OptAcc, ArgAcc, _ArgPos, []) ->
%% --foo Single option 'foo', no argument
%% --foo=bar Single option 'foo', argument "bar"
%% --foo bar Single option 'foo', argument "bar"
--spec parse_option_long([option_spec()], [option()], [string()], integer(), [string()], string(), string()) ->
+-spec parse_long_option([option_spec()], [option()], [string()], integer(), [string()], string(), string()) ->
{ok, {[option()], [string()]}}.
-parse_option_long(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, OptArg) ->
+parse_long_option(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, OptArg) ->
case split_assigned_arg(OptArg) of
{Long, Arg} ->
- % Get option that has its argument within the same string
- % separated by an equal ('=') character (e.g. "--port=1000").
- parse_option_assigned_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, Long, Arg);
+ %% Get option that has its argument within the same string
+ %% separated by an equal ('=') character (e.g. "--port=1000").
+ parse_long_option_assigned_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, Long, Arg);
Long ->
case lists:keyfind(Long, ?OPT_LONG, OptSpecList) of
@@ -123,9 +124,10 @@ parse_option_long(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, OptArg) ->
parse(OptSpecList, [Name | OptAcc], ArgAcc, ArgPos, Args);
{_Name, _Short, Long, _ArgSpec, _Help} = OptSpec ->
- % The option argument string is empty, but the option requires
- % an argument, so we look into the next string in the list.
- parse_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptSpec);
+ %% The option argument string is empty, but the option requires
+ %% an argument, so we look into the next string in the list.
+ %% e.g ["--port", "1000"]
+ parse_long_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptSpec);
false ->
throw({error, {invalid_option, OptStr}})
end
@@ -135,17 +137,17 @@ parse_option_long(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, OptArg) ->
%% @doc Parse an option where the argument is 'assigned' in the same string using
%% the '=' character, add it to the option accumulator and continue parsing the
%% rest of the arguments recursively. This syntax is only valid for long options.
--spec parse_option_assigned_arg([option_spec()], [option()], [string()], integer(),
- [string()], string(), string(), string()) ->
- {ok, {[option()], [string()]}}.
-parse_option_assigned_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, Long, Arg) ->
+-spec parse_long_option_assigned_arg([option_spec()], [option()], [string()], integer(),
+ [string()], string(), string(), string()) ->
+ {ok, {[option()], [string()]}}.
+parse_long_option_assigned_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, Long, Arg) ->
case lists:keyfind(Long, ?OPT_LONG, OptSpecList) of
{_Name, _Short, Long, ArgSpec, _Help} = OptSpec ->
case ArgSpec of
undefined ->
throw({error, {invalid_option_arg, OptStr}});
_ ->
- parse(OptSpecList, [convert_option_arg(OptSpec, Arg) | OptAcc], ArgAcc, ArgPos, Args)
+ parse(OptSpecList, add_option_with_assigned_arg(OptSpec, Arg, OptAcc), ArgAcc, ArgPos, Args)
end;
false ->
throw({error, {invalid_option, OptStr}})
@@ -166,6 +168,25 @@ split_assigned_arg(OptStr, [], _Acc) ->
OptStr.
+%% @doc Retrieve the argument for an option from the next string in the list of
+%% command-line parameters or set the value of the argument from the argument
+%% specification (for boolean and integer arguments), if possible.
+parse_long_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, {Name, _Short, _Long, ArgSpec, _Help} = OptSpec) ->
+ ArgSpecType = arg_spec_type(ArgSpec),
+ case Args =:= [] orelse is_implicit_arg(ArgSpecType, hd(Args)) of
+ true ->
+ parse(OptSpecList, add_option_with_implicit_arg(OptSpec, OptAcc), ArgAcc, ArgPos, Args);
+ false ->
+ [Arg | Tail] = Args,
+ try
+ parse(OptSpecList, [{Name, to_type(ArgSpecType, Arg)} | OptAcc], ArgAcc, ArgPos, Tail)
+ catch
+ error:_ ->
+ throw({error, {invalid_option_arg, {Name, Arg}}})
+ end
+ end.
+
+
%% @doc Parse a short option, add it to the option accumulator and continue
%% parsing the rest of the arguments recursively.
%% A short option can have the following syntax:
@@ -174,55 +195,62 @@ split_assigned_arg(OptStr, [], _Acc) ->
%% -afoo Single option 'a', argument "foo"
%% -abc Multiple options: 'a'; 'b'; 'c'
%% -bcafoo Multiple options: 'b'; 'c'; 'a' with argument "foo"
--spec parse_option_short([option_spec()], [option()], [string()], integer(), [string()], string(), string()) ->
+%% -aaa Multiple repetitions of option 'a' (only valid for options with integer arguments)
+-spec parse_short_option([option_spec()], [option()], [string()], integer(), [string()], string(), string()) ->
{ok, {[option()], [string()]}}.
-parse_option_short(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, [Short | Arg]) ->
+parse_short_option(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, OptArg) ->
+ parse_short_option(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, first, OptArg).
+
+parse_short_option(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptStr, OptPos, [Short | Arg]) ->
case lists:keyfind(Short, ?OPT_SHORT, OptSpecList) of
{Name, Short, _Long, undefined, _Help} ->
- parse_option_short(OptSpecList, [Name | OptAcc], ArgAcc, ArgPos, Args, OptStr, Arg);
+ parse_short_option(OptSpecList, [Name | OptAcc], ArgAcc, ArgPos, Args, OptStr, first, Arg);
{_Name, Short, _Long, ArgSpec, _Help} = OptSpec ->
+ %% The option has a specification, so it requires an argument.
case Arg of
[] ->
- % The option argument string is empty, but the option requires
- % an argument, so we look into the next string in the list.
- parse_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptSpec);
+ %% The option argument string is empty, but the option requires
+ %% an argument, so we look into the next string in the list.
+ parse_short_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, OptSpec, OptPos);
_ ->
case is_valid_arg(ArgSpec, Arg) of
true ->
- parse(OptSpecList, [convert_option_arg(OptSpec, Arg) | OptAcc], ArgAcc, ArgPos, Args);
+ parse(OptSpecList, add_option_with_arg(OptSpec, Arg, OptAcc), ArgAcc, ArgPos, Args);
_ ->
- parse_option_short(OptSpecList, [convert_option_no_arg(OptSpec) | OptAcc], ArgAcc, ArgPos, Args, OptStr, Arg)
+ NewOptAcc = case OptPos of
+ first -> add_option_with_implicit_arg(OptSpec, OptAcc);
+ _ -> add_option_with_implicit_incrementable_arg(OptSpec, OptAcc)
+ end,
+ parse_short_option(OptSpecList, NewOptAcc, ArgAcc, ArgPos, Args, OptStr, next, Arg)
end
end;
false ->
throw({error, {invalid_option, OptStr}})
end;
-parse_option_short(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, _OptStr, []) ->
+parse_short_option(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, _OptStr, _OptPos, []) ->
parse(OptSpecList, OptAcc, ArgAcc, ArgPos, Args).
%% @doc Retrieve the argument for an option from the next string in the list of
-%% command-line parameters.
-parse_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, [Arg | Tail] = Args, {Name, _Short, _Long, ArgSpec, _Help} = OptSpec) ->
- % Special case for booleans: when the next string is an option we assume
- % the value is 'true'.
- case (arg_spec_type(ArgSpec) =:= boolean) andalso not is_boolean_arg(Arg) of
+%% command-line parameters or set the value of the argument from the argument
+%% specification (for boolean and integer arguments), if possible.
+parse_short_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, Args, {Name, _Short, _Long, ArgSpec, _Help} = OptSpec, OptPos) ->
+ case Args =:= [] orelse is_implicit_arg(ArgSpec, hd(Args)) of
+ true when OptPos =:= first ->
+ parse(OptSpecList, add_option_with_implicit_arg(OptSpec, OptAcc), ArgAcc, ArgPos, Args);
true ->
- parse(OptSpecList, [{Name, true} | OptAcc], ArgAcc, ArgPos, Args);
- _ ->
- parse(OptSpecList, [convert_option_arg(OptSpec, Arg) | OptAcc], ArgAcc, ArgPos, Tail)
- end;
-parse_option_next_arg(OptSpecList, OptAcc, ArgAcc, ArgPos, [] = Args, {Name, _Short, _Long, ArgSpec, _Help}) ->
- % Special case for booleans: when the next string is missing we assume the
- % value is 'true'.
- case arg_spec_type(ArgSpec) of
- boolean ->
- parse(OptSpecList, [{Name, true} | OptAcc], ArgAcc, ArgPos, Args);
- _ ->
- throw({error, {missing_option_arg, Name}})
+ parse(OptSpecList, add_option_with_implicit_incrementable_arg(OptSpec, OptAcc), ArgAcc, ArgPos, Args);
+ false ->
+ [Arg | Tail] = Args,
+ try
+ parse(OptSpecList, [{Name, to_type(ArgSpec, Arg)} | OptAcc], ArgAcc, ArgPos, Tail)
+ catch
+ error:_ ->
+ throw({error, {invalid_option_arg, {Name, Arg}}})
+ end
end.
@@ -257,32 +285,79 @@ append_default_options([], OptAcc) ->
OptAcc.
--spec convert_option_no_arg(option_spec()) -> compound_option().
-convert_option_no_arg({Name, _Short, _Long, ArgSpec, _Help}) ->
- case ArgSpec of
- % Special case for booleans: if there is no argument we assume
- % the value is 'true'.
- {boolean, _DefaultValue} ->
- {Name, true};
- boolean ->
- {Name, true};
- _ ->
- throw({error, {missing_option_arg, Name}})
+%% @doc Add an option with argument converting it to the data type indicated by the
+%% argument specification.
+-spec add_option_with_arg(option_spec(), string(), [option()]) -> [option()].
+add_option_with_arg({Name, _Short, _Long, ArgSpec, _Help} = OptSpec, Arg, OptAcc) ->
+ case is_valid_arg(ArgSpec, Arg) of
+ true ->
+ try
+ [{Name, to_type(ArgSpec, Arg)} | OptAcc]
+ catch
+ error:_ ->
+ throw({error, {invalid_option_arg, {Name, Arg}}})
+ end;
+ false ->
+ add_option_with_implicit_arg(OptSpec, OptAcc)
end.
-%% @doc Convert the argument passed in the command line to the data type
-%% indicated by the argument specification.
--spec convert_option_arg(option_spec(), string()) -> compound_option().
-convert_option_arg({Name, _Short, _Long, ArgSpec, _Help}, Arg) ->
+%% @doc Add an option with argument that was part of an assignment expression
+%% (e.g. "--verbose=3") converting it to the data type indicated by the
+%% argument specification.
+-spec add_option_with_assigned_arg(option_spec(), string(), [option()]) -> [option()].
+add_option_with_assigned_arg({Name, _Short, _Long, ArgSpec, _Help}, Arg, OptAcc) ->
try
- {Name, to_type(arg_spec_type(ArgSpec), Arg)}
+ [{Name, to_type(ArgSpec, Arg)} | OptAcc]
catch
error:_ ->
throw({error, {invalid_option_arg, {Name, Arg}}})
end.
+%% @doc Add an option that required an argument but did not have one. Some data
+%% types (boolean, integer) allow implicit or assumed arguments.
+-spec add_option_with_implicit_arg(option_spec(), [option()]) -> [option()].
+add_option_with_implicit_arg({Name, _Short, _Long, ArgSpec, _Help}, OptAcc) ->
+ case arg_spec_type(ArgSpec) of
+ boolean ->
+ %% Special case for boolean arguments: if there is no argument we
+ %% set the value to 'true'.
+ [{Name, true} | OptAcc];
+ integer ->
+ %% Special case for integer arguments: if the option had not been set
+ %% before we set the value to 1. This is needed to support options like
+ %% "-v" to return something like {verbose, 1}.
+ [{Name, 1} | OptAcc];
+ _ ->
+ throw({error, {missing_option_arg, Name}})
+ end.
+
+
+%% @doc Add an option with an implicit or assumed argument.
+-spec add_option_with_implicit_incrementable_arg(option_spec() | arg_spec(), [option()]) -> [option()].
+add_option_with_implicit_incrementable_arg({Name, _Short, _Long, ArgSpec, _Help}, OptAcc) ->
+ case arg_spec_type(ArgSpec) of
+ boolean ->
+ %% Special case for boolean arguments: if there is no argument we
+ %% set the value to 'true'.
+ [{Name, true} | OptAcc];
+ integer ->
+ %% Special case for integer arguments: if the option had not been set
+ %% before we set the value to 1; if not we increment the previous value
+ %% the option had. This is needed to support options like "-vvv" to
+ %% return something like {verbose, 3}.
+ case OptAcc of
+ [{Name, Count} | Tail] ->
+ [{Name, Count + 1} | Tail];
+ _ ->
+ [{Name, 1} | OptAcc]
+ end;
+ _ ->
+ throw({error, {missing_option_arg, Name}})
+ end.
+
+
%% @doc Retrieve the data type form an argument specification.
-spec arg_spec_type(arg_spec()) -> arg_type() | undefined.
arg_spec_type({Type, _DefaultArg}) ->
@@ -292,7 +367,9 @@ arg_spec_type(Type) when is_atom(Type) ->
%% @doc Convert an argument string to its corresponding data type.
--spec to_type(arg_type(), string()) -> arg_value().
+-spec to_type(arg_spec() | arg_type(), string()) -> arg_value().
+to_type({Type, _DefaultArg}, Arg) ->
+ to_type(Type, Arg);
to_type(binary, Arg) ->
list_to_binary(Arg);
to_type(atom, Arg) ->
@@ -340,13 +417,24 @@ is_valid_arg({Type, _DefaultArg}, Arg) ->
is_valid_arg(boolean, Arg) ->
is_boolean_arg(Arg);
is_valid_arg(integer, Arg) ->
- is_integer_arg(Arg);
+ is_non_neg_integer_arg(Arg);
is_valid_arg(float, Arg) ->
- is_float_arg(Arg);
+ is_non_neg_float_arg(Arg);
is_valid_arg(_Type, _Arg) ->
true.
+-spec is_implicit_arg(arg_spec(), nonempty_string()) -> boolean().
+is_implicit_arg({Type, _DefaultArg}, Arg) ->
+ is_implicit_arg(Type, Arg);
+is_implicit_arg(boolean, Arg) ->
+ not is_boolean_arg(Arg);
+is_implicit_arg(integer, Arg) ->
+ not is_integer_arg(Arg);
+is_implicit_arg(_Type, _Arg) ->
+ false.
+
+
-spec is_boolean_arg(string()) -> boolean().
is_boolean_arg(Arg) ->
LowerArg = string:to_lower(Arg),
@@ -354,51 +442,76 @@ is_boolean_arg(Arg) ->
-spec is_integer_arg(string()) -> boolean().
-is_integer_arg([Head | Tail]) when Head >= $0, Head =< $9 ->
- is_integer_arg(Tail);
-is_integer_arg([_Head | _Tail]) ->
+is_integer_arg("-" ++ Tail) ->
+ is_non_neg_integer_arg(Tail);
+is_integer_arg(Arg) ->
+ is_non_neg_integer_arg(Arg).
+
+
+-spec is_non_neg_integer_arg(string()) -> boolean().
+is_non_neg_integer_arg([Head | Tail]) when Head >= $0, Head =< $9 ->
+ is_non_neg_integer_arg(Tail);
+is_non_neg_integer_arg([_Head | _Tail]) ->
false;
-is_integer_arg([]) ->
+is_non_neg_integer_arg([]) ->
true.
--spec is_float_arg(string()) -> boolean().
-is_float_arg([Head | Tail]) when (Head >= $0 andalso Head =< $9) orelse Head =:= $. ->
- is_float_arg(Tail);
-is_float_arg([_Head | _Tail]) ->
+-spec is_non_neg_float_arg(string()) -> boolean().
+is_non_neg_float_arg([Head | Tail]) when (Head >= $0 andalso Head =< $9) orelse Head =:= $. ->
+ is_non_neg_float_arg(Tail);
+is_non_neg_float_arg([_Head | _Tail]) ->
false;
-is_float_arg([]) ->
+is_non_neg_float_arg([]) ->
true.
-%% @doc Show a message on stdout indicating the command line options and
+%% @doc Show a message on standard_error indicating the command line options and
%% arguments that are supported by the program.
-spec usage([option_spec()], string()) -> ok.
usage(OptSpecList, ProgramName) ->
- io:format("Usage: ~s~s~n~n~s~n",
- [ProgramName, usage_cmd_line(OptSpecList), usage_options(OptSpecList)]).
+ usage(OptSpecList, ProgramName, standard_error).
-%% @doc Show a message on stdout indicating the command line options and
+%% @doc Show a message on standard_error or standard_io indicating the command line options and
+%% arguments that are supported by the program.
+-spec usage([option_spec()], string(), output_stream() | string()) -> ok.
+usage(OptSpecList, ProgramName, OutputStream) when is_atom(OutputStream) ->
+ io:format(OutputStream, "Usage: ~s~s~n~n~s~n",
+ [ProgramName, usage_cmd_line(OptSpecList), usage_options(OptSpecList)]);
+%% @doc Show a message on standard_error indicating the command line options and
%% arguments that are supported by the program. The CmdLineTail argument
%% is a string that is added to the end of the usage command line.
--spec usage([option_spec()], string(), string()) -> ok.
usage(OptSpecList, ProgramName, CmdLineTail) ->
- io:format("Usage: ~s~s ~s~n~n~s~n",
- [ProgramName, usage_cmd_line(OptSpecList), CmdLineTail, usage_options(OptSpecList)]).
+ usage(OptSpecList, ProgramName, CmdLineTail, standard_error).
-%% @doc Show a message on stdout indicating the command line options and
+%% @doc Show a message on standard_error or standard_io indicating the command line options and
+%% arguments that are supported by the program. The CmdLineTail argument
+%% is a string that is added to the end of the usage command line.
+-spec usage([option_spec()], string(), string(), output_stream() | [{string(), string()}]) -> ok.
+usage(OptSpecList, ProgramName, CmdLineTail, OutputStream) when is_atom(OutputStream) ->
+ io:format(OutputStream, "Usage: ~s~s ~s~n~n~s~n",
+ [ProgramName, usage_cmd_line(OptSpecList), CmdLineTail, usage_options(OptSpecList)]);
+%% @doc Show a message on standard_error indicating the command line options and
%% arguments that are supported by the program. The CmdLineTail and OptionsTail
%% arguments are a string that is added to the end of the usage command line
%% and a list of tuples that are added to the end of the options' help lines.
--spec usage([option_spec()], string(), string(), [{string(), string()}]) -> ok.
usage(OptSpecList, ProgramName, CmdLineTail, OptionsTail) ->
+ usage(OptSpecList, ProgramName, CmdLineTail, OptionsTail, standard_error).
+
+
+%% @doc Show a message on standard_error or standard_io indicating the command line options and
+%% arguments that are supported by the program. The CmdLineTail and OptionsTail
+%% arguments are a string that is added to the end of the usage command line
+%% and a list of tuples that are added to the end of the options' help lines.
+-spec usage([option_spec()], string(), string(), [{string(), string()}], output_stream()) -> ok.
+usage(OptSpecList, ProgramName, CmdLineTail, OptionsTail, OutputStream) ->
UsageOptions = lists:foldl(
fun ({Prefix, Help}, Acc) ->
add_option_help(Prefix, Help, Acc)
end, usage_options_reverse(OptSpecList, []), OptionsTail),
- io:format("Usage: ~s~s ~s~n~n~s~n",
+ io:format(OutputStream, "Usage: ~s~s ~s~n~n~s~n",
[ProgramName, usage_cmd_line(OptSpecList), CmdLineTail,
lists:flatten(lists:reverse(UsageOptions))]).
@@ -414,10 +527,10 @@ usage_cmd_line([{Name, Short, Long, ArgSpec, _Help} | Tail], Acc) ->
case ArgSpec of
undefined ->
if
- % For options with short form and no argument.
+ %% For options with short form and no argument.
Short =/= undefined ->
[$\s, $[, $-, Short, $]];
- % For options with only long form and no argument.
+ %% For options with only long form and no argument.
Long =/= undefined ->
[$\s, $[, $-, $-, Long, $]];
true ->
@@ -425,13 +538,13 @@ usage_cmd_line([{Name, Short, Long, ArgSpec, _Help} | Tail], Acc) ->
end;
_ ->
if
- % For options with short form and argument.
+ %% For options with short form and argument.
Short =/= undefined ->
[$\s, $[, $-, Short, $\s, $<, atom_to_list(Name), $>, $]];
- % For options with only long form and argument.
+ %% For options with only long form and argument.
Long =/= undefined ->
[$\s, $[, $-, $-, Long, $\s, $<, atom_to_list(Name), $>, $]];
- % For options with neither short nor long form and argument.
+ %% For options with neither short nor long form and argument.
true ->
[$\s, $<, atom_to_list(Name), $>]
end
@@ -452,19 +565,19 @@ usage_options_reverse([{Name, Short, Long, _ArgSpec, Help} | Tail], Acc) ->
case Long of
undefined ->
case Short of
- % Neither short nor long form (non-option argument).
+ %% Neither short nor long form (non-option argument).
undefined ->
[$<, atom_to_list(Name), $>];
- % Only short form.
+ %% Only short form.
_ ->
[$-, Short]
end;
_ ->
case Short of
- % Only long form.
+ %% Only long form.
undefined ->
[$-, $- | Long];
- % Both short and long form.
+ %% Both short and long form.
_ ->
[$-, Short, $,, $\s, $-, $- | Long]
end
View
5 src/mustache.erl
@@ -226,4 +226,9 @@ simple_test() ->
Result = render("Hello {{name}}!", Ctx),
?assertEqual("Hello world!", Result).
+integer_values_too_test() ->
+ Ctx = dict:from_list([{name, "Chris"}, {value, 10000}]),
+ Result = render("Hello {{name}}~nYou have just won ${{value}}!", Ctx),
+ ?assertEqual("Hello Chris~nYou have just won $10000!", Result).
+
-endif.
View
36 src/rebar.erl
@@ -96,6 +96,9 @@ run_aux(Commands) ->
%% Initialize logging system
rebar_log:init(),
+ %% Initialize vsn cache
+ _VsnCacheTab = ets:new(rebar_vsn_cache,[named_table, public]),
+
%% Convert command strings to atoms
CommandAtoms = [list_to_atom(C) || C <- Commands],
@@ -156,7 +159,7 @@ parse_args(Args) ->
proplists:get_bool(profile, Options)),
%% Set global variables based on getopt options
- set_global_flag(Options, verbose),
+ set_log_level(Options),
set_global_flag(Options, force),
DefJobs = rebar_config:get_jobs(),
case proplists:get_value(jobs, Options, DefJobs) of
@@ -183,6 +186,18 @@ parse_args(Args) ->
end.
%%
+%% set log level based on getopt option
+%%
+set_log_level(Options) ->
+ LogLevel = case proplists:get_all_values(verbose, Options) of
+ [] ->
+ rebar_log:default_level();
+ Verbosities ->
+ lists:last(Verbosities)
+ end,
+ rebar_config:set_global(verbose, LogLevel).
+
+%%
%% show version information and halt
%%
version() ->
@@ -223,9 +238,7 @@ show_info_maybe_halt(O, Opts, F) ->
case proplists:get_bool(O, Opts) of
true ->
F(),
- halt(0),
- %% workaround to delay exit until all output is written
- receive after infinity -> ok end;
+ rebar_utils:delayed_halt(0);
false ->
false
end.
@@ -256,10 +269,10 @@ overlay Run reltool overlays only
generate-upgrade previous_release=path Build an upgrade package
-generate-appups previous_release=path Generate appup files
+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
+ct [suites=] [case=] Run common_test suites in ./test
xref Run cross reference analysis
@@ -276,11 +289,12 @@ option_spec_list() ->
JobsHelp = io_lib:format(
"Number of concurrent workers a command may use. Default: ~B",
[Jobs]),
+ VerboseHelp = "Verbosity level (-v, -vv, -vvv, --verbose 3). Default: 0",
[
%% {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"},
+ {verbose, $v, "verbose", integer, VerboseHelp},
{version, $V, "version", undefined, "Show version information"},
{force, $f, "force", undefined, "Force"},
{defines, $D, undefined, string, "Define compiler macro"},
@@ -299,8 +313,14 @@ filter_flags([Item | Rest], Commands) ->
case string:tokens(Item, "=") of
[Command] ->
filter_flags(Rest, [Command | Commands]);
- [KeyStr, Value] ->
+ [KeyStr, RawValue] ->
Key = list_to_atom(KeyStr),
+ Value = case Key of
+ verbose ->
+ list_to_integer(RawValue);
+ _ ->
+ RawValue
+ end,
rebar_config:set_global(Key, Value),
filter_flags(Rest, Commands);
Other ->
View
17 src/rebar_abnfc_compiler.erl
@@ -84,13 +84,12 @@ abnfc_is_present() ->
compile_abnfc(Source, _Target, Config) ->
case abnfc_is_present() of
false ->
- ?CONSOLE(
- <<"~n===============================================~n"
- " You need to install abnfc to compile ABNF grammars~n"
- " Download the latest tarball release from github~n"
- " https://github.com/nygge/abnfc~n"
- " and install it into your erlang library dir~n"
- "===============================================~n~n">>, []),
+ ?ERROR("~n===============================================~n"
+ " You need to install abnfc to compile ABNF grammars~n"
+ " Download the latest tarball release from github~n"
+ " https://github.com/nygge/abnfc~n"
+ " and install it into your erlang library dir~n"
+ "===============================================~n~n", []),
?FAIL;
true ->
AbnfcOpts = abnfc_opts(Config),
@@ -102,8 +101,8 @@ compile_abnfc(Source, _Target, Config) ->
case abnfc:file(Source, Opts) of
ok -> ok;
Error ->
- ?CONSOLE("Compiling grammar ~s failed:~n ~p~n",
- [Source, Error]),
+ ?ERROR("Compiling grammar ~s failed:~n ~p~n",
+ [Source, Error]),
?FAIL
end
end.
View
99 src/rebar_app_utils.erl
@@ -31,7 +31,8 @@
app_src_to_app/1,
app_name/1,
app_applications/1,
- app_vsn/1]).
+ app_vsn/1,
+ is_skipped_app/1]).
-export([load_app_file/1]). % TEMPORARY
@@ -98,12 +99,33 @@ app_vsn(AppFile) ->
case load_app_file(AppFile) of
{ok, _, AppInfo} ->
AppDir = filename:dirname(filename:dirname(AppFile)),
- vcs_vsn(get_value(vsn, AppInfo, AppFile), AppDir);
+ rebar_utils:vcs_vsn(get_value(vsn, AppInfo, AppFile), AppDir);
{error, Reason} ->
?ABORT("Failed to extract vsn from ~s: ~p\n",
[AppFile, Reason])
end.
+is_skipped_app(AppFile) ->
+ ThisApp = app_name(AppFile),
+ %% Check for apps global parameter; this is a comma-delimited list
+ %% of apps on which we want to run commands
+ case get_apps() of
+ undefined ->
+ %% No apps parameter specified, check the skip_apps list..
+ case get_skip_apps() of
+ undefined ->
+ %% No skip_apps list, run everything..
+ false;
+ SkipApps ->
+ TargetApps = [list_to_atom(A) ||
+ A <- string:tokens(SkipApps, ",")],
+ is_skipped_app(ThisApp, TargetApps)
+ end;
+ Apps ->
+ %% run only selected apps
+ TargetApps = [list_to_atom(A) || A <- string:tokens(Apps, ",")],
+ is_selected_app(ThisApp, TargetApps)
+ end.
%% ===================================================================
%% Internal functions
@@ -134,57 +156,26 @@ get_value(Key, AppInfo, AppFile) ->
Value
end.
-vcs_vsn(Vcs, Dir) ->
- case vcs_vsn_cmd(Vcs) of
- {unknown, VsnString} ->
- ?DEBUG("vcs_vsn: Unknown VCS atom in vsn field: ~p\n", [Vcs]),
- VsnString;
- {cmd, CmdString} ->
- vcs_vsn_invoke(CmdString, Dir);
- Cmd ->
- %% If there is a valid VCS directory in the application directory,
- %% use that version info
- Extension = lists:concat([".", Vcs]),
- case filelib:is_dir(filename:join(Dir, Extension)) of
- true ->
- ?DEBUG("vcs_vsn: Primary vcs used for ~s\n", [Dir]),
- vcs_vsn_invoke(Cmd, Dir);
- false ->
- %% No VCS directory found for the app. Depending on source
- %% tree structure, there may be one higher up, but that can
- %% yield unexpected results when used with deps. So, we
- %% fallback to searching for a priv/vsn.Vcs file.
- VsnFile = filename:join([Dir, "priv", "vsn" ++ Extension]),
- case file:read_file(VsnFile) of
- {ok, VsnBin} ->
- ?DEBUG("vcs_vsn: Read ~s from priv/vsn.~p\n",
- [VsnBin, Vcs]),
- string:strip(binary_to_list(VsnBin), right, $\n);
- {error, enoent} ->
- ?DEBUG("vcs_vsn: Fallback to vcs for ~s\n", [Dir]),
- vcs_vsn_invoke(Cmd, Dir)
- end
- end
+%% apps= for selecting apps
+is_selected_app(ThisApp, TargetApps) ->
+ case lists:member(ThisApp, TargetApps) of
+ false ->
+ {true, ThisApp};
+ true ->
+ false
end.
-vcs_vsn_cmd(git) ->
- %% Explicitly git-describe a committish to accommodate for projects
- %% in subdirs which don't have a GIT_DIR. In that case we will
- %% get a description of the last commit that touched the subdir.
- case os:type() of
- {win32,nt} ->
- "FOR /F \"usebackq tokens=* delims=\" %i in "
- "(`git log -n 1 \"--pretty=format:%h\" .`) do "
- "@git describe --always --tags %i";
- _ ->
- "git describe --always --tags `git log -n 1 --pretty=format:%h .`"
- end;
-vcs_vsn_cmd(hg) -> "hg identify -i";
-vcs_vsn_cmd(bzr) -> "bzr revno";
-vcs_vsn_cmd(svn) -> "svnversion";
-vcs_vsn_cmd({cmd, _Cmd}=Custom) -> Custom;
-vcs_vsn_cmd(Version) -> {unknown, Version}.
-
-vcs_vsn_invoke(Cmd, Dir) ->
- {ok, VsnString} = rebar_utils:sh(Cmd, [{cd, Dir}, {use_stdout, false}]),
- string:strip(VsnString, right, $\n).
+%% skip_apps= for filtering apps
+is_skipped_app(ThisApp, TargetApps) ->
+ case lists:member(ThisApp, TargetApps) of
+ false ->
+ false;
+ true ->
+ {true, ThisApp}
+ end.
+
+get_apps() ->
+ rebar_utils:get_deprecated_global(app, apps, "soon").
+
+get_skip_apps() ->
+ rebar_utils:get_deprecated_global(skip_app, skip_apps, "soon").
View
2  src/rebar_appups.erl
@@ -44,7 +44,7 @@
TargetParentDir = rebar_rel_utils:get_target_parent_dir(ReltoolConfig),
OldVerPath = filename:join([TargetParentDir,
- rebar_rel_utils:get_previous_release_path()]),
+ rebar_rel_utils:get_previous_release_path()]),
%% Get the new and old release name and versions
{Name, _Ver} = rebar_rel_utils:get_reltool_release_info(ReltoolConfig),
View
8 src/rebar_asn1_compiler.erl
@@ -58,8 +58,12 @@ compile_asn1(Source, Target, Config) ->
ok ->
Asn1 = filename:basename(Source, ".asn1"),
HrlFile = filename:join("src", Asn1 ++ ".hrl"),
- ok = rebar_file_utils:mv(HrlFile, "include"),
- ok;
+ case filelib:is_regular(HrlFile) of
+ true ->
+ ok = rebar_file_utils:mv(HrlFile, "include");
+ false ->
+ ok
+ end;
{error, _Reason} ->
?FAIL
end.
View
71 src/rebar_config.erl
@@ -26,17 +26,19 @@
%% -------------------------------------------------------------------
-module(rebar_config).
--export([new/0, new/1, base_config/1,
+-export([new/0, new/1, base_config/1, consult_file/1,
get/3, get_local/3, get_list/3,
get_all/2,
set/3,
set_global/2, get_global/2,
- is_verbose/0, get_jobs/0]).
+ is_verbose/0, get_jobs/0,
+ set_env/3, get_env/2]).
-include("rebar.hrl").
-record(config, { dir :: file:filename(),
- opts :: list() }).
+ opts = [] :: list(),
+ envs = new_env() :: dict() }).
%% Types that can be used from other modules -- alphabetically ordered.
-export_type([config/0]).
@@ -53,8 +55,7 @@ base_config(#config{opts=Opts0}) ->
new(Opts0, ConfName).
new() ->
- #config { dir = rebar_utils:get_cwd(),
- opts = [] }.
+ #config{dir = rebar_utils:get_cwd()}.
new(ConfigFile) when is_list(ConfigFile) ->
case consult_file(ConfigFile) of
@@ -88,7 +89,7 @@ new(Opts0, ConfName) ->
?ABORT("Failed to load ~s: ~p\n", [ConfigFile, Other])
end,
- #config { dir = Dir, opts = Opts }.
+ #config{dir = Dir, opts = Opts}.
get(Config, Key, Default) ->
proplists:get_value(Key, Config#config.opts, Default).
@@ -109,7 +110,7 @@ set(Config, Key, Value) ->
set_global(jobs=Key, Value) when is_list(Value) ->
set_global(Key, list_to_integer(Value));
set_global(jobs=Key, Value) when is_integer(Value) ->
- application:set_env(rebar_global, Key, erlang:max(1,Value));
+ application:set_env(rebar_global, Key, erlang:max(1, Value));
set_global(Key, Value) ->
application:set_env(rebar_global, Key, Value).
@@ -122,18 +123,63 @@ get_global(Key, Default) ->
end.
is_verbose() ->
- get_global(verbose, "0") =:= "1".
+ DefaulLevel = rebar_log:default_level(),
+ get_global(verbose, DefaulLevel) > DefaulLevel.
get_jobs() ->
get_global(jobs, 3).
+consult_file(File) ->
+ case filename:extension(File) of
+ ".script" ->
+ consult_and_eval(remove_script_ext(File), File);
+ _ ->
+ Script = File ++ ".script",
+ case filelib:is_regular(Script) of
+ true ->
+ consult_and_eval(File, Script);
+ false ->
+ ?DEBUG("Consult config file ~p~n", [File]),
+ file:consult(File)
+ end
+ end.
+
+set_env(Config, Mod, Env) ->
+ OldEnvs = Config#config.envs,
+ NewEnvs = dict:store(Mod, Env, OldEnvs),
+ Config#config{envs=NewEnvs}.
+
+get_env(Config, Mod) ->
+ dict:fetch(Mod, Config#config.envs).
+
%% ===================================================================
%% Internal functions
%% ===================================================================
-consult_file(File) ->
- ?DEBUG("Consult config file ~p~n", [File]),
- file:consult(File).
+consult_and_eval(File, Script) ->
+ ?DEBUG("Evaluating config script ~p~n", [Script]),
+ ConfigData = try_consult(File),
+ file:script(Script, bs([{'CONFIG', ConfigData}, {'SCRIPT', Script}])).
+
+
+remove_script_ext(F) ->
+ "tpircs." ++ Rev = lists:reverse(F),
+ lists:reverse(Rev).
+
+try_consult(File) ->
+ case file:consult(File) of
+ {ok, Terms} ->
+ ?DEBUG("Consult config file ~p~n", [File]),
+ Terms;
+ {error, enoent} -> [];
+ {error, Reason} ->
+ ?ABORT("Failed to read config file ~s: ~p~n", [File, Reason])
+ end.
+
+bs(Vars) ->
+ lists:foldl(fun({K,V}, Bs) ->
+ erl_eval:add_binding(K, V, Bs)
+ end, erl_eval:new_bindings(), Vars).
local_opts([], Acc) ->
lists:reverse(Acc);
@@ -141,3 +187,6 @@ local_opts([local | _Rest], Acc) ->
lists:reverse(Acc);
local_opts([Item | Rest], Acc) ->
local_opts(Rest, [Item | Acc]).
+
+new_env() ->
+ dict:new().
View
297 src/rebar_core.erl
@@ -90,6 +90,9 @@ process_commands([Command | Rest], ParentConfig) ->
_ ->
ok
end,
+ %% Wipe out vsn cache to avoid invalid hits when
+ %% dependencies are updated
+ ets:delete_all_objects(rebar_vsn_cache),
process_commands(Rest, ParentConfig).
@@ -100,7 +103,14 @@ process_dir(Dir, ParentConfig, Command, DirSet) ->
DirSet;
true ->
- ?DEBUG("Entering ~s\n", [Dir]),
+ AbsDir = filename:absname(Dir),
+ case processing_base_dir(Dir) of
+ false ->
+ ?CONSOLE("==> Entering directory `~s'\n", [AbsDir]);
+ true ->
+ ok
+ end,
+
ok = file:set_cwd(Dir),
Config = maybe_load_local_config(Dir, ParentConfig),
@@ -114,82 +124,148 @@ process_dir(Dir, ParentConfig, Command, DirSet) ->
%% CWD to see if it's a fit -- if it is, use that set of modules
%% to process this dir.
{ok, AvailModuleSets} = application:get_env(rebar, modules),
- {DirModules, ModuleSetFile} = choose_module_set(AvailModuleSets,
- Dir),
-
- %% Get the list of modules for "any dir". This is a catch-all list
- %% of modules that are processed in addition to modules associated
- %% with this directory type. These any_dir modules are processed
- %% FIRST.
- {ok, AnyDirModules} = application:get_env(rebar, any_dir_modules),
-
- Modules = AnyDirModules ++ DirModules,
-
- %% Invoke 'preprocess' on the modules -- this yields a list of other
- %% directories that should be processed _before_ the current one.
- Predirs = acc_modules(Modules, preprocess, Config, ModuleSetFile),
-
- %% Get the list of plug-in modules from rebar.config. These
- %% modules may participate in preprocess and postprocess.
- {ok, PluginModules} = plugin_modules(Config),
-
- PluginPredirs = acc_modules(PluginModules, preprocess,
- Config, ModuleSetFile),
-
- AllPredirs = Predirs ++ PluginPredirs,
-
- ?DEBUG("Predirs: ~p\n", [AllPredirs]),
- DirSet2 = process_each(AllPredirs, Command, Config,
- ModuleSetFile, DirSet),
-
- %% Make sure the CWD is reset properly; processing the dirs may have
- %% caused it to change
- ok = file:set_cwd(Dir),
-
- %% Check that this directory is not on the skip list
- case is_skip_dir(Dir) of
- true ->
- %% Do not execute the command on the directory, as some
- %% module as requested a skip on it.
- ?INFO("Skipping ~s in ~s\n", [Command, Dir]);
+ ModuleSet = choose_module_set(AvailModuleSets, Dir),
+ Res = maybe_process_dir(ModuleSet, Config, CurrentCodePath,
+ Dir, Command, DirSet),
+ case processing_base_dir(Dir) of
false ->
- %% Execute any before_command plugins on this directory
- execute_pre(Command, PluginModules,
- Config, ModuleSetFile),
-
- %% Execute the current command on this directory
- execute(Command, Modules ++ PluginModules,
- Config, ModuleSetFile),
-
- %% Execute any after_command plugins on this directory
- execute_post(Command, PluginModules,
- Config, ModuleSetFile)
+ ?CONSOLE("==> Leaving directory `~s'\n", [AbsDir]);
+ true ->
+ ok
end,
- %% Mark the current directory as processed
- DirSet3 = sets:add_element(Dir, DirSet2),
-
- %% Invoke 'postprocess' on the modules. This yields a list of other
- %% directories that should be processed _after_ the current one.
- Postdirs = acc_modules(Modules ++ PluginModules, postprocess,
- Config, ModuleSetFile),
- ?DEBUG("Postdirs: ~p\n", [Postdirs]),
- DirSet4 = process_each(Postdirs, Command, Config,
- ModuleSetFile, DirSet3),
-
- %% Make sure the CWD is reset properly; processing the dirs may have
- %% caused it to change
- ok = file:set_cwd(Dir),
+ Res
+ end.
- %% Once we're all done processing, reset the code path to whatever
- %% the parent initialized it to
- restore_code_path(CurrentCodePath),
+maybe_process_dir({[], undefined}=ModuleSet, Config, CurrentCodePath,
+ Dir, Command, DirSet) ->
+ process_dir0(Dir, Command, DirSet, Config, CurrentCodePath, ModuleSet);
+maybe_process_dir({_, ModuleSetFile}=ModuleSet, Config, CurrentCodePath,
+ 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
+ maybe_process_dir0(ModuleSetFile, ModuleSet,
+ Config, CurrentCodePath, Dir,
+ Command, DirSet);
+ false ->
+ %% not an app dir, no need to consider apps=/skip_apps=
+ process_dir0(Dir, Command, DirSet, Config,
+ CurrentCodePath, ModuleSet)
+ end.
- %% Return the updated dirset as our result
- DirSet4
+maybe_process_dir0(AppFile, ModuleSet, Config, CurrentCodePath,
+ Dir, Command, DirSet) ->
+ case rebar_app_utils:is_skipped_app(AppFile) of
+ {true, SkippedApp} ->
+ ?DEBUG("Skipping app: ~p~n", [SkippedApp]),
+ increment_operations(),
+ DirSet;
+ false ->
+ process_dir0(Dir, Command, DirSet, Config,
+ CurrentCodePath, ModuleSet)
end.
+process_dir0(Dir, Command, DirSet, Config0, CurrentCodePath,
+ {DirModules, ModuleSetFile}) ->
+ %% Get the list of modules for "any dir". This is a catch-all list
+ %% of modules that are processed in addition to modules associated
+ %% with this directory type. These any_dir modules are processed
+ %% FIRST.
+ {ok, AnyDirModules} = application:get_env(rebar, any_dir_modules),
+
+ Modules = AnyDirModules ++ DirModules,
+
+ %% Invoke 'preprocess' on the modules -- this yields a list of other
+ %% directories that should be processed _before_ the current one.
+ Predirs = acc_modules(Modules, preprocess, Config0, ModuleSetFile),
+
+ SubdirAssoc = remember_cwd_subdir(Dir, Predirs),
+
+ %% Get the list of plug-in modules from rebar.config. These
+ %% modules may participate in preprocess and postprocess.
+ {ok, PluginModules} = plugin_modules(Config0, SubdirAssoc),
+
+ PluginPredirs = acc_modules(PluginModules, preprocess,
+ Config0, ModuleSetFile),
+
+ AllPredirs = Predirs ++ PluginPredirs,
+
+ ?DEBUG("Predirs: ~p\n", [AllPredirs]),
+ DirSet2 = process_each(AllPredirs, Command, Config0,
+ ModuleSetFile, DirSet),
+
+ %% Make sure the CWD is reset properly; processing the dirs may have
+ %% caused it to change
+ ok = file:set_cwd(Dir),
+
+ %% Check that this directory is not on the skip list
+ Config = case is_skip_dir(Dir) of
+ true ->
+ %% Do not execute the command on the directory, as some
+ %% module has requested a skip on it.
+ ?INFO("Skipping ~s in ~s\n", [Command, Dir]),
+ Config0;
+
+ false ->
+ %% Check for and get command specific environments
+ {Config1, Env} = setup_envs(Config0, Modules),
+
+ %% Execute any before_command plugins on this directory
+ execute_pre(Command, PluginModules,
+ Config1, ModuleSetFile, Env),
+
+ %% Execute the current command on this directory
+ execute(Command, Modules ++ PluginModules,
+ Config1, ModuleSetFile, Env),
+
+ %% Execute any after_command plugins on this directory
+ execute_post(Command, PluginModules,
+ Config1, ModuleSetFile, Env),
+
+ Config1
+ end,
+
+ %% Mark the current directory as processed
+ DirSet3 = sets:add_element(Dir, DirSet2),
+
+ %% Invoke 'postprocess' on the modules. This yields a list of other
+ %% directories that should be processed _after_ the current one.
+ Postdirs = acc_modules(Modules ++ PluginModules, postprocess,
+ Config, ModuleSetFile),
+ ?DEBUG("Postdirs: ~p\n", [Postdirs]),
+ DirSet4 = process_each(Postdirs, Command, Config,
+ ModuleSetFile, DirSet3),
+
+ %% Make sure the CWD is reset properly; processing the dirs may have
+ %% caused it to change
+ ok = file:set_cwd(Dir),
+
+ %% Once we're all done processing, reset the code path to whatever
+ %% the parent initialized it to
+ restore_code_path(CurrentCodePath),
+
+ %% Return the updated dirset as our result
+ DirSet4.
+
+remember_cwd_subdir(Cwd, Subdirs) ->
+ Store = fun(Dir, Dict) ->
+ case dict:find(Dir, Dict) of
+ error ->
+ ?DEBUG("Associate sub_dir ~s with ~s~n", [Dir, Cwd]),
+ dict:store(Dir, Cwd, Dict);
+ {ok, Existing} ->
+ ?ABORT("Internal consistency assertion failed.~n"
+ "sub_dir ~s already associated with ~s.~n"
+ "Duplicate sub_dirs or deps entries?",
+ [Dir, Existing]),
+ Dict
+ end
+ end,
+ lists:foldl(Store, dict:new(), Subdirs).
+
maybe_load_local_config(Dir, ParentConfig) ->
%% We need to ensure we don't overwrite custom
%% config when we are dealing with base_dir.
@@ -241,22 +317,22 @@ is_dir_type(rel_dir, Dir) ->
is_dir_type(_, _) ->
false.
-execute_pre(Command, Modules, Config, ModuleFile) ->
+execute_pre(Command, Modules, Config, ModuleFile, Env) ->
execute_plugin_hook("pre_", Command, Modules,
- Config, ModuleFile).
+ Config, ModuleFile, Env).
-execute_post(Command, Modules, Config, ModuleFile) ->
+execute_post(Command, Modules, Config, ModuleFile, Env) ->
execute_plugin_hook("post_", Command, Modules,
- Config, ModuleFile).
+ Config, ModuleFile, Env).
-execute_plugin_hook(Hook, Command, Modules, Config, ModuleFile) ->
+execute_plugin_hook(Hook, Command, Modules, Config, ModuleFile, Env) ->
HookFunction = list_to_atom(Hook ++ atom_to_list(Command)),
- execute(HookFunction, Modules, Config, ModuleFile).
+ execute(HookFunction, Modules, Config, ModuleFile, Env).
%%
%% Execute a command across all applicable modules
%%
-execute(Command, Modules, Config, ModuleFile) ->
+execute(Command, Modules, Config, ModuleFile, Env) ->
case select_modules(Modules, Command, []) of
[] ->
Cmd = atom_to_list(Command),
@@ -274,12 +350,7 @@ execute(Command, Modules, Config, ModuleFile) ->
Dir = rebar_utils:get_cwd(),
?CONSOLE("==> ~s (~s)\n", [filename:basename(Dir), Command]),
- %% Increment the count of operations, since some module
- %% responds to this command
- erlang:put(operations, erlang:get(operations) + 1),
-
- %% Check for and get command specific environments
- Env = setup_envs(Config, Modules),
+ increment_operations(),
%% Run the available modules
apply_hooks(pre_hooks, Config, Command, Env),
@@ -300,6 +371,11 @@ execute(Command, Modules, Config, ModuleFile) ->
end
end.
+%% Increment the count of operations, since some module
+%% responds to this command
+increment_operations() ->
+ erlang:put(operations, erlang:get(operations) + 1).
+
update_code_path(Config) ->
case rebar_config:get_local(Config, lib_dirs, []) of
@@ -354,16 +430,32 @@ run_modules([Module | Rest], Command, Config, File) ->
apply_hooks(Mode, Config, Command, Env) ->
Hooks = rebar_config:get_local(Config, Mode, []),
lists:foreach(fun apply_hook/1,
- [{Env, Hook} || Hook <- Hooks, element(1, Hook) =:= Command]).
+ [{Env, Hook} || Hook <- Hooks,
+ element(1, Hook) =:= Command orelse
+ element(2, Hook) =:= Command]).
+apply_hook({Env, {Arch, Command, Hook}}) ->
+ case rebar_utils:is_arch(Arch) of
+ true ->
+ apply_hook({Env, {Command, Hook}});
+ false ->
+ ok
+ end;
apply_hook({Env, {Command, Hook}}) ->
Msg = lists:flatten(io_lib:format("Command [~p] failed!~n", [Command])),
rebar_utils:sh(Hook, [{env, Env}, {abort_on_error, Msg}]).
setup_envs(Config, Modules) ->
- lists:flatten([M:setup_env(Config) ||
- M <- Modules,
- erlang:function_exported(M, setup_env, 1)]).
+ lists:foldl(fun(M, {C,E}=T) ->
+ case erlang:function_exported(M, setup_env, 1) of
+ true ->
+ Env = M:setup_env(C),
+ C1 = rebar_config:set_env(C, M, Env),
+ {C1, E++Env};
+ false ->
+ T
+ end
+ end, {Config, []}, Modules).
acc_modules(Modules, Command, Config, File) ->
acc_modules(select_modules(Modules, Command, []),
@@ -378,9 +470,9 @@ acc_modules([Module | Rest], Command, Config, File, Acc) ->
%%
%% Return a flat list of rebar plugin modules.
%%
-plugin_modules(Config) ->
+plugin_modules(Config, SubdirAssoc) ->
Modules = lists:flatten(rebar_config:get_all(Config, plugins)),
- plugin_modules(Config, ulist(Modules)).
+ plugin_modules(Config, SubdirAssoc, ulist(Modules)).
ulist(L) ->
ulist(L, []).
@@ -395,16 +487,16 @@ ulist([H | T], Acc) ->
ulist(T, [H | Acc])
end.
-plugin_modules(_Config, []) ->
+plugin_modules(