Merge pull request #260 from dgud/dgud/windows-fix-paths

Quote include/lib paths
diff --git a/.gitignore b/.gitignore
index 165948e..15668a7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,12 +1,11 @@
-*.beam
-rebar
+/ebin/*.beam
+/rebar
 *~
 *.orig
 .*.swp
-rt.work
-.test
-dialyzer_warnings
-rebar.cmd
-.eunit
-deps
-.rebar/*
+/rt.work
+/dialyzer_warnings
+/rebar.cmd
+/.eunit
+/deps
+/.rebar
diff --git a/.travis.yml b/.travis.yml
index a7eedb4..602266b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,4 +7,5 @@
   - R15B
   - R14B04
   - R14B03
+  - 17.0
 script: "make travis"
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 30693d8..e0de0eb 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -36,6 +36,37 @@
 
 Provide a clean branch without merge commits.
 
+Tests
+-----
+
+As a general rule, any behavioral change to rebar requires a test to go with it. If there's  
+already a test case, you may have to modify that one. If there isn't a test case or a test  
+suite, add a new test case or suite in `inttest/`. [retest](https://github.com/dizzyd/retest) based tests are preferred, but  
+we also have EUnit tests in `test/`.
+
+Say you've added a new test case in `inttest/erlc`. To only execute the modified suite,  
+you would do the following:
+```sh
+# First we build rebar and its deps to also get `deps/retest/retest`
+$ make debug deps
+# Now we can test the modified erlc suite
+$ deps/retest/retest -v inttest/erlc
+```
+
+To test EUnit tests, you would do:
+```sh
+$ make debug
+$ ./rebar -v eunit
+```
+
+You can also run `make test` to execute both EUnit and [retest](https://github.com/dizzyd/retest) tests as `make check` does.
+
+Credit
+------
+
+To give everyone proper credit in addition to the git history, please feel free to append  
+your name to `THANKS` in your first contribution.
+
 Committing your changes
 -----------------------
 
diff --git a/RELEASE-NOTES.md b/RELEASE-NOTES.md
index bdfb3d7..093e771 100644
--- a/RELEASE-NOTES.md
+++ b/RELEASE-NOTES.md
@@ -1,33 +1,64 @@
+# Rebar 2.3.1
+
+## PR's Merged
+
+* rebar/242: [Extra commits for #129](https://github.com/rebar/rebar/pull/242)
+* rebar/244: [Document skip_apps=, apps=, and require_*_vsn](https://github.com/rebar/rebar/pull/244)
+* rebar/251: [Make sure that eunit/qc_compile_opts works](https://github.com/rebar/rebar/pull/251)
+* rebar/255: [rebar.app: remove superfluous quoting](https://github.com/rebar/rebar/pull/255)
+* rebar/274: [Use lowercase for Windows drive name to resolve issue #250](https://github.com/rebar/rebar/pull/274)
+
+# Rebar 2.3.0
+
+## PR's Merged
+
+* rebar/98: [Repetition of environment variable definitions in child processes (ports)](https://github.com/rebar/rebar/pull/98)
+* rebar/115: [Incorrect REMSH args when sname is used.](https://github.com/rebar/rebar/pull/115)
+* rebar/129: [Speed up the compilation process v5](https://github.com/rebar/rebar/pull/129)
+* rebar/139: [Allow specification of module dependencies for appups](https://github.com/rebar/rebar/pull/139)
+* rebar/175: [CWD plugins regression](https://github.com/rebar/rebar/pull/175)
+* rebar/188: [Xref extra path](https://github.com/rebar/rebar/pull/188)
+* rebar/208: [Fix typo in rebar_erlydtl_compiler](https://github.com/rebar/rebar/pull/208)
+* rebar/219: [Added R16B01 and R16B02 to travis config.](https://github.com/rebar/rebar/pull/219)
+* rebar/221: [Adapt erlydtl compiler plugin to latest version of erlydtl](https://github.com/rebar/rebar/pull/221)
+* rebar/223: [Add random_suite_order option to eunit command](https://github.com/rebar/rebar/pull/223)
+* rebar/224: [allow suites or tests as options for eunit and ct](https://github.com/rebar/rebar/pull/224)
+* rebar/230: [eunit: fix dialyzer warnings introduced in 03da5e0b](https://github.com/rebar/rebar/pull/230)
+* rebar/232: [Document support for abbreviated commands](https://github.com/rebar/rebar/pull/232)
+* rebar/233: [docs: fix #228](https://github.com/rebar/rebar/pull/233)
+* rebar/234: [Fix #220 (Reported-by: Joseph Norton)](https://github.com/rebar/rebar/pull/234)
+* rebar/237: [Add partial support for Erlang/OTP 17](https://github.com/rebar/rebar/pull/237)
+* rebar/252: [file_utils: properly report errors (fix #95)](https://github.com/rebar/rebar/pull/252)
+* rebar/254: [Fix 'rebar generate' regression (#253)](https://github.com/rebar/rebar/pull/254)
+* rebar/265: [Fix 'rebar help clean' function_clause error](https://github.com/rebar/rebar/pull/265)
+* rebar/268: [Fix #267 (code path regression)](https://github.com/rebar/rebar/pull/268)
+* rebar/269: [Update THANKS](https://github.com/rebar/rebar/pull/269)
+
 # Rebar 2.2.0
 
 ## PR's Merged
 
-* rebar/137: [Filter documented behaviour callbacks](https://github.com/basho/rebar/pull/137)
-* rebar/142: [Fix  rebar_file_utils module on Windows with MSYS](https://github.com/basho/rebar/pull/142)
-* rebar/152: [Allow users to configure skip_deps for specific commands](https://github.com/basho/rebar/pull/152)
-* rebar/154: [Stop applications nicely before killing extra processes](https://github.com/basho/rebar/pull/154)
-* rebar/155: [Fix rebar_utils:expand_env_variable/3](https://github.com/basho/rebar/pull/155)
-* rebar/157: [add native Windows compiler support](https://github.com/basho/rebar/pull/157)
-* rebar/172: [Allow reltool target_dir to be constructed on the fly](https://github.com/basho/rebar/pull/172)
-* rebar/173: [rebar should expand VCS version in the top directory, if possible](https://github.com/basho/rebar/pull/173)
-* rebar/174: [Fixed handle_call response in simplesrv.erl](https://github.com/basho/rebar/pull/174)
-* rebar/177: [Cache vsn information during the run to avoid extra unnecessary shell calls](https://github.com/basho/rebar/pull/177)
-* rebar/179: [Add ebin to the path before compiling erlydtl templates](https://github.com/basho/rebar/pull/179)
-* rebar/183: [Fix compiling DTL templates with latest erlydtl](https://github.com/basho/rebar/pull/183)
-* rebar/184: [Fix for destruction of config app vars on reset](https://github.com/basho/rebar/pull/184)
-* rebar/185: [simple enhance and simple bugfix](https://github.com/basho/rebar/pull/185)
-* rebar/187: [fix for cp_r_win32 where copying a directory to a non-existant directory would crash](https://github.com/basho/rebar/pull/187)
-* rebar/189: [Fix typos in generated cmd script in bootstrap](https://github.com/basho/rebar/pull/189)
-* rebar/190: [Windows xcopy dir to non-existant dir (re-request)](https://github.com/basho/rebar/pull/190)
-* rebar/191: [Fix typos in rebar_templater](https://github.com/basho/rebar/pull/191)
-* rebar/196: [Escape '|' in the Windows runner usage string](https://github.com/basho/rebar/pull/196)
-* rebar/198: [New feature to rebar_xref to allow execution of custom queries.](https://github.com/basho/rebar/pull/198)
-* rebar/199: [Added new feature to rebar xref to allow execution of custom queries.](https://github.com/basho/rebar/pull/199)
-* rebar/200: [Enable runner to pass more than one argument to start](https://github.com/basho/rebar/pull/200)
-* rebar/201: [include simplemodule.app.src in simplemod template](https://github.com/basho/rebar/pull/201)
-* rebar/205: [Ports in languages other than C](https://github.com/basho/rebar/issues/205)
-* rebar/210: [use file:script if a .config.script file present](https://github.com/basho/rebar/pull/210)
-* rebar/212: [    Modified simplenode.runner to start from alternative directory](https://github.com/basho/rebar/pull/212)
-* rebar/214: [Foreground running doesn't allow console attaching](https://github.com/basho/rebar/issues/214)
-* rebar/215: [Add support for http proxy_friendly_github_urls](https://github.com/basho/rebar/pull/215)
-* rebar/388: [Less than useful rebar error message when error in .hrl files](https://github.com/basho/rebar/issues/388)
+* rebar/152: [Fix erl_opts use](https://github.com/rebar/rebar/pull/152)
+* rebar/154: [Fix update-deps with certain forms of the {tag, ...} type](https://github.com/rebar/rebar/pull/154)
+* rebar/155: [Fixes for #137 and #142](https://github.com/rebar/rebar/pull/155)
+* rebar/157: [Don't over-aggressively clean the code path in the presence of lib_dir directives](https://github.com/rebar/rebar/pull/157)
+* rebar/172: [Add missing dep examples and fix existing ones](https://github.com/rebar/rebar/pull/172)
+* rebar/173: [Fix false reporting of (plain) vsn strings](https://github.com/rebar/rebar/pull/173)
+* rebar/174: [rebar_core: fix Dialyzer warning introduced in aa46d85 (#157)](https://github.com/rebar/rebar/pull/174)
+* rebar/177: [Delete unused inttest/retest binary](https://github.com/rebar/rebar/pull/177)
+* rebar/179: [Make list of commands (for unabbreviation) easier to maintain](https://github.com/rebar/rebar/pull/179)
+* rebar/183: [generate-upgrade can now take target_dir argument](https://github.com/rebar/rebar/pull/183)
+* rebar/184: [Fix log levels](https://github.com/rebar/rebar/pull/184)
+* rebar/185: [Switch retest dep to upstream (dizzyd/retest.git)](https://github.com/rebar/rebar/pull/185)
+* rebar/189: [inttest/rgen1: increase retest timeout (30s -> 60s)](https://github.com/rebar/rebar/pull/189)
+* rebar/190: [inttest/rgen_1: double the timeout a second time](https://github.com/rebar/rebar/pull/190)
+* rebar/191: [Fix #187 (rename getopt and mustache)](https://github.com/rebar/rebar/pull/191)
+* rebar/196: [Print a more appropriate message on 'rebar info'](https://github.com/rebar/rebar/pull/196)
+* rebar/198: [Clean up rebar.config.script](https://github.com/rebar/rebar/pull/198)
+* rebar/199: [rebar_dia_compiler: fix Dialyzer warnings](https://github.com/rebar/rebar/pull/199)
+* rebar/200: [bootstrap: avoid trying to run 'debug' command](https://github.com/rebar/rebar/pull/200)
+* rebar/201: [Added a library template.](https://github.com/rebar/rebar/pull/201)
+* rebar/210: [Fix #205 (erlydtl:compile/3 returns warnings)](https://github.com/rebar/rebar/pull/210)
+* rebar/212: [Fix basho/rebar#388](https://github.com/rebar/rebar/pull/212)
+* rebar/214: [Document compile_only=true](https://github.com/rebar/rebar/pull/214)
+* rebar/215: [Remove experimental flags](https://github.com/rebar/rebar/pull/215)
diff --git a/THANKS b/THANKS
index 0b503d3..ef359ac 100644
--- a/THANKS
+++ b/THANKS
@@ -121,3 +121,8 @@
 Oliver Ferrigni
 Dave Thomas
 Evgeniy Khramtsov
+YeJun Su
+Yuki Ito
+alisdair sullivan
+Alexander Verbitsky
+Andras Horvath
diff --git a/bootstrap b/bootstrap
index 019aaea..f6aa6cc 100755
--- a/bootstrap
+++ b/bootstrap
@@ -28,10 +28,20 @@
     %% Extract the system info of the version of OTP we use to compile rebar
     OtpInfo = string:strip(erlang:system_info(otp_release), both, $\n),
 
+    %% Types dict:dict() and digraph:digraph() have been introduced in Erlang 17.
+    %% At the same time, their counterparts dict() and digraph() are to be deprecated
+    %% in Erlang 18. namespaced_types option is used to select proper type name
+    %% depending of the OTP version used.
+    NamespacedTypes = case is_otp(OtpInfo, "^[0-9]+") of
+                          true -> {d, namespaced_types};
+                          false -> undefined
+                      end,
+
     %% Compile all src/*.erl to ebin
     case make:files(filelib:wildcard("src/*.erl"),
                     [{outdir, "ebin"}, {i, "include"},
                      DebugFlag,
+                     NamespacedTypes,
                      {d, 'BUILD_TIME', Built},
                      {d, 'VCS_INFO', VcsInfo},
                      {d, 'OTP_INFO', OtpInfo}]) of
@@ -79,6 +89,12 @@
               "Place this script anywhere in your path\n"
               "and you can use rebar to build OTP-compliant apps.\n").
 
+is_otp(OtpInfo, Regex) ->
+     case re:run(OtpInfo, Regex, [{capture, none}]) of
+          match -> true;
+          nomatch -> false
+     end.
+
 rm(Path) ->
     NativePath = filename:nativename(Path),
     Cmd = case os:type() of
diff --git a/dialyzer_reference b/dialyzer_reference
index 7fbe609..c32104f 100644
--- a/dialyzer_reference
+++ b/dialyzer_reference
@@ -1,3 +1,3 @@
 
-rebar_eunit.erl:434: Call to missing or unexported function eunit_test:function_wrapper/2
-rebar_utils.erl:164: Call to missing or unexported function escript:foldl/3
+rebar_eunit.erl:471: Call to missing or unexported function eunit_test:function_wrapper/2
+rebar_utils.erl:184: Call to missing or unexported function escript:foldl/3
diff --git a/ebin/rebar.app b/ebin/rebar.app
index 6b4702a..cc9f751 100644
--- a/ebin/rebar.app
+++ b/ebin/rebar.app
@@ -3,7 +3,7 @@
 
 {application, rebar,
  [{description, "Rebar: Erlang Build Tool"},
-  {vsn, "2.2.0"},
+  {vsn, "2.3.1"},
   {modules, [ rebar,
               rebar_abnfc_compiler,
               rebar_app_utils,
@@ -14,6 +14,7 @@
               rebar_cleaner,
               rebar_config,
               rebar_core,
+              rebar_cover_utils,
               rebar_ct,
               rebar_deps,
               rebar_edoc,
@@ -93,7 +94,7 @@
                    ]},
          {recursive_cmds, [
                            'check-deps',
-                           'compile',
+                           compile,
                            'delete-deps',
                            'get-deps',
                            'list-deps',
diff --git a/inttest/code_path_no_recurse/code_path_no_recurse_rt.erl b/inttest/code_path_no_recurse/code_path_no_recurse_rt.erl
new file mode 100644
index 0000000..d884bcc
--- /dev/null
+++ b/inttest/code_path_no_recurse/code_path_no_recurse_rt.erl
@@ -0,0 +1,19 @@
+-module(code_path_no_recurse_rt).
+-export([files/0,
+         run/1]).
+
+files() ->
+    [
+     {copy, "../../rebar", "rebar"},
+     {copy, "rebar.config", "rebar.config"},
+     {copy, "src", "src"},
+     {copy, "test", "test"},
+     {copy, "deps", "deps"}
+    ].
+
+run(_Dir) ->
+    retest:log(info, "Compile project~n"),
+    {ok, _} = retest:sh("./rebar -v compile"),
+    retest:log(info, "Run eunit with referenced deps on the code path~n"),
+    {ok, _} = retest:sh("./rebar -v eunit"),
+    ok.
diff --git a/inttest/code_path_no_recurse/deps/bazdep/src/bazdep.app.src b/inttest/code_path_no_recurse/deps/bazdep/src/bazdep.app.src
new file mode 100644
index 0000000..7f7b3f9
--- /dev/null
+++ b/inttest/code_path_no_recurse/deps/bazdep/src/bazdep.app.src
@@ -0,0 +1,12 @@
+{application, bazdep,
+ [
+  {description, ""},
+  {vsn, "1"},
+  {registered, []},
+  {applications, [
+                  kernel,
+                  stdlib
+                 ]},
+  {mod, {bazdep, []}},
+  {env, []}
+ ]}.
diff --git a/inttest/code_path_no_recurse/deps/bazdep/src/bazdep.erl b/inttest/code_path_no_recurse/deps/bazdep/src/bazdep.erl
new file mode 100644
index 0000000..aef4cf3
--- /dev/null
+++ b/inttest/code_path_no_recurse/deps/bazdep/src/bazdep.erl
@@ -0,0 +1,6 @@
+-module(bazdep).
+
+-export([bazdep/0]).
+
+bazdep() ->
+    bazdep.
diff --git a/inttest/code_path_no_recurse/deps/bazdep/test/bazdep_tests.erl b/inttest/code_path_no_recurse/deps/bazdep/test/bazdep_tests.erl
new file mode 100644
index 0000000..b5190f6
--- /dev/null
+++ b/inttest/code_path_no_recurse/deps/bazdep/test/bazdep_tests.erl
@@ -0,0 +1,5 @@
+-module(bazdep_tests).
+-include_lib("eunit/include/eunit.hrl").
+
+bazdep_test() ->
+    ?assert(bazdep:bazdep() =:= bazdep).
diff --git a/inttest/code_path_no_recurse/deps/foodep/rebar.config b/inttest/code_path_no_recurse/deps/foodep/rebar.config
new file mode 100644
index 0000000..cdaf168
--- /dev/null
+++ b/inttest/code_path_no_recurse/deps/foodep/rebar.config
@@ -0,0 +1 @@
+{deps, [{bazdep, "1"}]}.
diff --git a/inttest/code_path_no_recurse/deps/foodep/src/foodep.app.src b/inttest/code_path_no_recurse/deps/foodep/src/foodep.app.src
new file mode 100644
index 0000000..c0642fb
--- /dev/null
+++ b/inttest/code_path_no_recurse/deps/foodep/src/foodep.app.src
@@ -0,0 +1,12 @@
+{application, foodep,
+ [
+  {description, ""},
+  {vsn, "1"},
+  {registered, []},
+  {applications, [
+                  kernel,
+                  stdlib
+                 ]},
+  {mod, {foodep, []}},
+  {env, []}
+ ]}.
diff --git a/inttest/code_path_no_recurse/deps/foodep/src/foodep.erl b/inttest/code_path_no_recurse/deps/foodep/src/foodep.erl
new file mode 100644
index 0000000..3d43d0e
--- /dev/null
+++ b/inttest/code_path_no_recurse/deps/foodep/src/foodep.erl
@@ -0,0 +1,6 @@
+-module(foodep).
+
+-export([foodep/0]).
+
+foodep() ->
+    bazdep:bazdep() =:= bazdep.
diff --git a/inttest/code_path_no_recurse/deps/foodep/test/foodep_tests.erl b/inttest/code_path_no_recurse/deps/foodep/test/foodep_tests.erl
new file mode 100644
index 0000000..66d7b8b
--- /dev/null
+++ b/inttest/code_path_no_recurse/deps/foodep/test/foodep_tests.erl
@@ -0,0 +1,5 @@
+-module(foodep_tests).
+-include_lib("eunit/include/eunit.hrl").
+
+foodep_test() ->
+    ?assert(foodep:foodep()).
diff --git a/inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.app.src b/inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.app.src
new file mode 100644
index 0000000..d0bc233
--- /dev/null
+++ b/inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.app.src
@@ -0,0 +1,12 @@
+{application, unuseddep,
+ [
+  {description, ""},
+  {vsn, "1"},
+  {registered, []},
+  {applications, [
+                  kernel,
+                  stdlib
+                 ]},
+  {mod, {unuseddep, []}},
+  {env, []}
+ ]}.
diff --git a/inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.erl b/inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.erl
new file mode 100644
index 0000000..a990345
--- /dev/null
+++ b/inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.erl
@@ -0,0 +1,6 @@
+-module(unuseddep).
+
+-export([unuseddep/0]).
+
+unuseddep() ->
+    unuseddep.
diff --git a/inttest/code_path_no_recurse/rebar.config b/inttest/code_path_no_recurse/rebar.config
new file mode 100644
index 0000000..4b358de
--- /dev/null
+++ b/inttest/code_path_no_recurse/rebar.config
@@ -0,0 +1 @@
+{deps, [{foodep, "1"}]}.
diff --git a/inttest/code_path_no_recurse/src/codepath.app.src b/inttest/code_path_no_recurse/src/codepath.app.src
new file mode 100644
index 0000000..3aa200f
--- /dev/null
+++ b/inttest/code_path_no_recurse/src/codepath.app.src
@@ -0,0 +1,12 @@
+{application, codepath,
+ [
+  {description, ""},
+  {vsn, "1"},
+  {registered, []},
+  {applications, [
+                  kernel,
+                  stdlib
+                 ]},
+  {mod, {codepath, []}},
+  {env, []}
+ ]}.
diff --git a/inttest/code_path_no_recurse/src/codepath.erl b/inttest/code_path_no_recurse/src/codepath.erl
new file mode 100644
index 0000000..df4e6b0
--- /dev/null
+++ b/inttest/code_path_no_recurse/src/codepath.erl
@@ -0,0 +1,6 @@
+-module(codepath).
+
+-export([codepath/0]).
+
+codepath() ->
+    foodep:foodep().
diff --git a/inttest/code_path_no_recurse/test/codepath_tests.erl b/inttest/code_path_no_recurse/test/codepath_tests.erl
new file mode 100644
index 0000000..01a1d2a
--- /dev/null
+++ b/inttest/code_path_no_recurse/test/codepath_tests.erl
@@ -0,0 +1,12 @@
+-module(codepath_tests).
+-include_lib("eunit/include/eunit.hrl").
+
+codepath_test() ->
+    ?assertEqual({module, codepath}, code:ensure_loaded(codepath)),
+    ?assertEqual({module, foodep}, code:ensure_loaded(foodep)),
+    ?assertEqual({module, bazdep}, code:ensure_loaded(bazdep)),
+    ?assert(codepath:codepath()).
+
+unuseddep_test() ->
+    ?assertEqual(non_existing, code:which(unuseddep)),
+    ?assertEqual({error, nofile}, code:ensure_loaded(unuseddep)).
diff --git a/inttest/erlc/asn1/SIMPLE-ASN.asn1 b/inttest/erlc/asn1/SIMPLE-ASN.asn1
new file mode 100644
index 0000000..62f0860
--- /dev/null
+++ b/inttest/erlc/asn1/SIMPLE-ASN.asn1
@@ -0,0 +1,7 @@
+SIMPLE-ASN DEFINITIONS ::= BEGIN
+
+    SimpleMessage ::= SEQUENCE {
+        id INTEGER
+    }
+
+END
diff --git a/inttest/erlc/erlc_rt.erl b/inttest/erlc/erlc_rt.erl
new file mode 100644
index 0000000..50cdb83
--- /dev/null
+++ b/inttest/erlc/erlc_rt.erl
@@ -0,0 +1,137 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+-module(erlc_rt).
+-export([files/0,
+         run/1]).
+
+-include_lib("eunit/include/eunit.hrl").
+
+-define(MODULES,
+        [first_xrl,
+         first_yrl,
+         first_erl,
+         foo,
+         foo_app,
+         foo_sup,
+         foo_test_worker,
+         foo_worker,
+         'SIMPLE-ASN']).
+
+-define(BEAM_FILES,
+        ["first_xrl.beam",
+         "first_yrl.beam",
+         "first_erl.beam",
+         "foo.beam",
+         "foo_app.beam",
+         "foo_sup.beam",
+         "foo_test_worker.beam",
+         "foo_worker.beam",
+         "SIMPLE-ASN.beam"]).
+
+files() ->
+    [
+     {copy, "../../rebar", "rebar"},
+     {copy, "rebar.config", "rebar.config"},
+     {copy, "rebar-no_debug_info.config", "rebar-no_debug_info.config"},
+     {copy, "include", "include"},
+     {copy, "extra-include", "extra-include"},
+     {copy, "src", "src"},
+     {copy, "extra-src", "extra-src"},
+     {copy, "mibs", "mibs"},
+     {copy, "asn1", "asn1"},
+     {create, "ebin/foo.app", app(foo, ?MODULES)},
+     %% deps
+     {create, "deps/foobar/ebin/foobar.app", app(foobar, [foobar])},
+     {copy, "foobar.erl", "deps/foobar/src/foobar.erl"}
+    ].
+
+run(_Dir) ->
+    ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])),
+    ok = check_beams(true),
+    ok = check_debug_info(true),
+    MibResult = filename:join(["priv", "mibs", "SIMPLE-MIB.bin"]),
+    ?assertMatch(true, filelib:is_regular(MibResult)),
+    ?assertMatch({ok, _}, retest_sh:run("./rebar clean", [])),
+    ok = check_beams(false),
+    ?assertMatch(false, filelib:is_regular(MibResult)),
+    ?assertMatch(
+       {ok, _},
+       retest_sh:run("./rebar -C rebar-no_debug_info.config compile", [])),
+    ok = check_beams(true),
+    ok = check_debug_info(false),
+    ?assertMatch(true, filelib:is_regular(MibResult)),
+    %% Regression test for https://github.com/rebar/rebar/issues/249
+    %%
+    %% Root cause: We didn't have per-project .rebar/erlcinfo but just one in
+    %% <base_dir>/.rebar/erlcinfo.
+    %%
+    %% Solution: Ensure every project has its own .rebar/erlcinfo
+    %%
+    %% For the bug to happen, the following conditions must be met:
+    %%
+    %% 1. <base_dir>/rebar.config has erl_first_files
+    %% 2. one of the 'first' files depends on another file (in this
+    %%    case via -include_lib())
+    %% 3. a sub project's rebar.config, if any, has no erl_first_files entry
+    %%
+    %% Now because erl_first_files is retrieved via rebar_config:get_list/3,
+    %% base_dir/rebar.config's erl_first_files is inherited, and because we had
+    %% a shared <base_dir>/.rebar/erlcinfo instead of one per project, the
+    %% cached entry was reused. Next, while compiling the sub project
+    %% rebar_erlc_compiler:needs_compile/3 gets a last modification time of
+    %% zero for the 'first' file which does not exist inside the sub project.
+    %% This, and the fact that it has at least one dependency, makes
+    %% needs_compile/3 return 'true'. The root cause is that we didn't have per
+    %% project .rebar/erlcinfo. For <base_dir>/.rebar/erlcinfo to be populated,
+    %% base_dir has to be compiled at least once. Therefore, after the first
+    %% compile any compile processing the sub project will fail because
+    %% needs_compile/3 will always return true for the non-existent 'first'
+    %% file.
+    ?assertMatch({ok, _}, retest_sh:run("./rebar clean", [])),
+    ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])),
+    ok = check_beams(true),
+    ?assertMatch({ok, _}, retest_sh:run("./rebar compile", [])),
+    ok = check_beams(true),
+    ok.
+
+check_beams(Exist) ->
+    check_files(Exist, fun filelib:is_regular/1).
+
+check_debug_info(HasDebugInfo) ->
+    check_files(HasDebugInfo, fun has_debug_info/1).
+
+check_files(Expected, Check) ->
+    lists:foreach(
+      fun(F) ->
+              File = filename:join("ebin", F),
+              ?assertEqual(Expected, Check(File))
+      end,
+      ?BEAM_FILES).
+
+%% NOTE: Copied from dialyzer_utils:get_abstract_code_from_beam/1 and
+%% modified for local use. We could have called the function directly,
+%% but dialyzer_utils is not an official API to rely on.
+has_debug_info(File) ->
+    case beam_lib:chunks(File, [abstract_code]) of
+        {ok, {_Mod, List}} ->
+            case lists:keyfind(abstract_code, 1, List) of
+                {abstract_code, {raw_abstract_v1, _Abstr}} ->
+                    true;
+                _ ->
+                    false
+            end;
+        _ ->
+            false
+    end.
+
+%%
+%% 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/erlc/extra-include/foo_extra.hrl b/inttest/erlc/extra-include/foo_extra.hrl
new file mode 100644
index 0000000..19e9f94
--- /dev/null
+++ b/inttest/erlc/extra-include/foo_extra.hrl
@@ -0,0 +1,3 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+-define(FOO_EXTRA, foo_extra).
diff --git a/inttest/erlc/extra-src/foo_sup.erl b/inttest/erlc/extra-src/foo_sup.erl
new file mode 100644
index 0000000..c68194e
--- /dev/null
+++ b/inttest/erlc/extra-src/foo_sup.erl
@@ -0,0 +1,15 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+-module(foo_sup).
+
+-behavior(supervisor).
+
+-export([start_link/0,
+         init/1]).
+
+start_link() ->
+    supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+init(_Args) ->
+    FooChild = {foo,{foo, start_link, []}, permanent, 5000, worker, [foo]},
+    {ok,{{one_for_all,1,1}, [FooChild]}}.
diff --git a/inttest/erlc/foobar.erl b/inttest/erlc/foobar.erl
new file mode 100644
index 0000000..b6d55a8
--- /dev/null
+++ b/inttest/erlc/foobar.erl
@@ -0,0 +1,8 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+-module(foobar).
+
+-export([test/0]).
+
+test() ->
+    true.
diff --git a/inttest/erlc/include/foo_core.hrl b/inttest/erlc/include/foo_core.hrl
new file mode 100644
index 0000000..803f2f0
--- /dev/null
+++ b/inttest/erlc/include/foo_core.hrl
@@ -0,0 +1,3 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+-define(FOO_CORE, foo_core).
diff --git a/inttest/erlc/mibs/SIMPLE-MIB.mib b/inttest/erlc/mibs/SIMPLE-MIB.mib
new file mode 100644
index 0000000..ca8735a
--- /dev/null
+++ b/inttest/erlc/mibs/SIMPLE-MIB.mib
@@ -0,0 +1,26 @@
+-- SIMPLE-MIB.
+-- This is just a simple MIB used for testing!
+--
+
+
+SIMPLE-MIB DEFINITIONS ::= BEGIN
+
+IMPORTS
+    MODULE-IDENTITY, enterprises
+        FROM SNMPv2-SMI;
+
+ericsson MODULE-IDENTITY
+    LAST-UPDATED
+        "201403060000Z"
+    ORGANIZATION
+        "rebar"
+    CONTACT-INFO
+        "rebar <rebar@example.com>
+	 or
+	 whoever is currently responsible for the SIMPLE
+	 enterprise MIB tree branch (enterprises.999)."
+    DESCRIPTION
+        "This very small module is made available
+	for mib-compilation testing."
+    ::= { enterprises 999 }
+END
diff --git a/inttest/erlc/rebar-no_debug_info.config b/inttest/erlc/rebar-no_debug_info.config
new file mode 100644
index 0000000..07b6fed
--- /dev/null
+++ b/inttest/erlc/rebar-no_debug_info.config
@@ -0,0 +1,11 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+{erl_first_files, ["src/first_xrl.erl", "src/first_yrl.erl"]}.
+
+{erl_opts,
+ [
+  no_debug_info,
+  {i, "extra-include"},
+  {src_dirs, ["src", "extra-src"]},
+  {platform_define, "R13|R14", 'NO_CALLBACK_ATTRIBUTE'}
+ ]}.
diff --git a/inttest/erlc/rebar.config b/inttest/erlc/rebar.config
new file mode 100644
index 0000000..71d6660
--- /dev/null
+++ b/inttest/erlc/rebar.config
@@ -0,0 +1,13 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+{erl_first_files,
+ ["src/first_xrl.erl", "src/first_yrl.erl", "src/first_erl.erl"]}.
+
+{deps, [foobar]}.
+
+{erl_opts,
+ [
+  {i, "extra-include"},
+  {src_dirs, ["src", "extra-src"]},
+  {platform_define, "R13|R14", 'NO_CALLBACK_ATTRIBUTE'}
+ ]}.
diff --git a/inttest/erlc/src/behaviour/foo_worker.erl b/inttest/erlc/src/behaviour/foo_worker.erl
new file mode 100644
index 0000000..307c69a
--- /dev/null
+++ b/inttest/erlc/src/behaviour/foo_worker.erl
@@ -0,0 +1,14 @@
+-module(foo_worker).
+
+-ifdef(NO_CALLBACK_ATTRIBUTE).
+
+-export([behaviour_info/1]).
+
+behaviour_info(callbacks) -> [{status, 0}];
+behaviour_info(_) -> undefined.
+
+-else.
+
+-callback status() -> 'idle' | 'busy'.
+
+-endif.
diff --git a/inttest/erlc/src/first_erl.erl b/inttest/erlc/src/first_erl.erl
new file mode 100644
index 0000000..4e9ff20
--- /dev/null
+++ b/inttest/erlc/src/first_erl.erl
@@ -0,0 +1,10 @@
+%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 ft=erlang et
+-module(first_erl).
+
+-include_lib("eunit/include/eunit.hrl").
+
+-export([test/0]).
+
+test() ->
+    ?debugHere.
diff --git a/inttest/erlc/src/first_xrl.xrl b/inttest/erlc/src/first_xrl.xrl
new file mode 100644
index 0000000..0de4c70
--- /dev/null
+++ b/inttest/erlc/src/first_xrl.xrl
@@ -0,0 +1,13 @@
+Definitions.
+
+D = [0-9]
+
+Rules.
+
+{D}+ :
+  {token,{integer,TokenLine,list_to_integer(TokenChars)}}.
+
+{D}+\.{D}+((E|e)(\+|\-)?{D}+)? :
+  {token,{float,TokenLine,list_to_float(TokenChars)}}.
+
+Erlang code.
diff --git a/inttest/erlc/src/first_yrl.yrl b/inttest/erlc/src/first_yrl.yrl
new file mode 100644
index 0000000..8ccdb0e
--- /dev/null
+++ b/inttest/erlc/src/first_yrl.yrl
@@ -0,0 +1,9 @@
+Nonterminals list elements element.
+Terminals atom '(' ')'.
+Rootsymbol list.
+list -> '(' ')'.
+list -> '(' elements ')'.
+elements -> element.
+elements -> element elements.
+element -> atom.
+element -> list.
diff --git a/inttest/erlc/src/foo.erl b/inttest/erlc/src/foo.erl
new file mode 100644
index 0000000..33e6cfc
--- /dev/null
+++ b/inttest/erlc/src/foo.erl
@@ -0,0 +1,35 @@
+-module(foo).
+
+-export([start_link/0,
+         start_link/1,
+         init/1,
+         terminate/2,
+         handle_info/2,
+         handle_call/3,
+         handle_cast/2,
+         code_change/3]).
+
+-behavior(gen_server).
+
+-include("foo_core.hrl").
+-include("foo_extra.hrl").
+-include_lib("kernel/include/file.hrl").
+
+-record(state, {node :: node()}).
+
+start_link() -> start_link(undefined).
+
+start_link(Args) ->
+    gen_server:start_link({local, ?MODULE}, ?MODULE, Args, []).
+
+init(_Args) -> {ok, #state{node=node()}}.
+
+terminate(_Reason, _Data) -> ok.
+
+handle_info(_Info, State) -> {noreply, State}.
+
+handle_cast(_Msg, State) -> {noreply, State}.
+
+handle_call(_Msg, _From, State) -> {reply, ok, State}.
+
+code_change(_OldVsn, State, _Extra) -> {ok, State}.
diff --git a/inttest/erlc/src/foo_app.erl b/inttest/erlc/src/foo_app.erl
new file mode 100644
index 0000000..a3c7a96
--- /dev/null
+++ b/inttest/erlc/src/foo_app.erl
@@ -0,0 +1,10 @@
+-module(foo_app).
+
+-behaviour(application).
+
+-export([start/2,
+         stop/1]).
+
+start(_Type, _Args) -> foo_sup:start_link().
+
+stop(_State) -> ok.
diff --git a/inttest/erlc/src/foo_test_worker.erl b/inttest/erlc/src/foo_test_worker.erl
new file mode 100644
index 0000000..96ae932
--- /dev/null
+++ b/inttest/erlc/src/foo_test_worker.erl
@@ -0,0 +1,34 @@
+-module(foo_test_worker).
+
+-behaviour(gen_server).
+-behaviour(foo_worker).
+
+-export([start_link/0,
+         start_link/1,
+         init/1,
+         handle_call/3,
+         handle_cast/2,
+         handle_info/2,
+         terminate/2,
+         code_change/3,
+         status/0]).
+
+-include_lib("kernel/include/inet.hrl").
+
+start_link() -> start_link(undefined).
+
+start_link(Args) -> gen_server:start_link(?MODULE, Args, []).
+
+init([]) -> {ok, undefined}.
+
+handle_call(_Event, _From, State) -> {reply, ok, State}.
+
+handle_cast(_Event, State) -> {noreply, State}.
+
+handle_info(_Info, State) -> {noreply, State}.
+
+terminate(_Reason, _State) -> ok.
+
+code_change(_OldVsn, State, _Extra) -> {ok, State}.
+
+status() -> busy.
diff --git a/inttest/eunit/eunit_rt.erl b/inttest/eunit/eunit_rt.erl
new file mode 100644
index 0000000..47f3331
--- /dev/null
+++ b/inttest/eunit/eunit_rt.erl
@@ -0,0 +1,48 @@
+-module(eunit_rt).
+-export([files/0, run/1]).
+
+-include_lib("eunit/include/eunit.hrl").
+
+files() ->
+    [{create, "ebin/foo.app", app(foo)},
+     {copy, "../../rebar", "rebar"},
+     {copy, "src", "src"},
+     {copy, "eunit_src", "eunit_src"},
+     {copy,
+      "rebar-eunit_compile_opts.config",
+      "rebar-eunit_compile_opts.config"}].
+
+run(_Dir) ->
+    ifdef_test(),
+    eunit_compile_opts_test(),
+    ok.
+
+ifdef_test() ->
+    {ok, Output} = retest:sh("./rebar -v eunit"),
+    ?assert(check_output(Output, "foo_test")),
+    ?assertMatch({ok, _}, retest:sh("./rebar clean")).
+
+eunit_compile_opts_test() ->
+    {ok, Output} =
+        retest:sh("./rebar -v -C rebar-eunit_compile_opts.config eunit"),
+    ?assert(check_output(Output, "bar_test")),
+    ?assertMatch(
+       {ok, _},
+       retest:sh("./rebar -C rebar-eunit_compile_opts.config clean")).
+
+check_output(Output, Target) ->
+    lists:any(fun(Line) ->
+                      string:str(Line, Target) > 0
+              end, Output).
+
+%%
+%% Generate the contents of a simple .app file
+%%
+app(Name) ->
+    App = {application, Name,
+           [{description, atom_to_list(Name)},
+            {vsn, "1"},
+            {modules, []},
+            {registered, []},
+            {applications, [kernel, stdlib]}]},
+    io_lib:format("~p.\n", [App]).
diff --git a/inttest/eunit/eunit_src/bar.erl b/inttest/eunit/eunit_src/bar.erl
new file mode 100644
index 0000000..6a80dac
--- /dev/null
+++ b/inttest/eunit/eunit_src/bar.erl
@@ -0,0 +1,6 @@
+-module(bar).
+
+-include_lib("eunit/include/eunit.hrl").
+
+bar_test() ->
+    ?assert(true).
diff --git a/inttest/eunit/rebar-eunit_compile_opts.config b/inttest/eunit/rebar-eunit_compile_opts.config
new file mode 100644
index 0000000..13b2d94
--- /dev/null
+++ b/inttest/eunit/rebar-eunit_compile_opts.config
@@ -0,0 +1 @@
+{eunit_compile_opts, [{src_dirs, ["eunit_src"]}]}.
diff --git a/inttest/eunit/src/foo.erl b/inttest/eunit/src/foo.erl
new file mode 100644
index 0000000..a4c91ba
--- /dev/null
+++ b/inttest/eunit/src/foo.erl
@@ -0,0 +1,10 @@
+-module(foo).
+
+-ifdef(TEST).
+
+-include_lib("eunit/include/eunit.hrl").
+
+foo_test() ->
+    ?assert(true).
+
+-endif.
diff --git a/priv/templates/simpleevent.erl b/priv/templates/simpleevent.erl
new file mode 100644
index 0000000..1d529a3
--- /dev/null
+++ b/priv/templates/simpleevent.erl
@@ -0,0 +1,60 @@
+-module({{eventid}}).
+-behaviour(gen_event).
+
+%% ------------------------------------------------------------------
+%% API Function Exports
+%% ------------------------------------------------------------------
+
+-export([start_link/0,
+         add_handler/2]).
+
+%% ------------------------------------------------------------------
+%% gen_event Function Exports
+%% ------------------------------------------------------------------
+
+-export([init/1,
+         handle_event/2,
+         handle_call/2,
+         handle_info/2,
+         terminate/2,
+         code_change/3]).
+
+-record(state, {}).
+
+%% ------------------------------------------------------------------
+%% API Function Definitions
+%% ------------------------------------------------------------------
+
+start_link() ->
+    gen_event:start_link({local, ?MODULE}).
+
+add_handler(Handler, Args) ->
+    gen_event:add_handler(?MODULE, Handler, Args).
+
+%% ------------------------------------------------------------------
+%% gen_event Function Definitions
+%% ------------------------------------------------------------------
+
+init([]) ->
+    {ok, #state{}}.
+
+handle_event(_Event, State) ->
+    {ok, State}.
+
+handle_call(_Request, State) ->
+    Reply = ok,
+    {ok, Reply, State}.
+
+handle_info(_Info, State) ->
+    {ok, State}.
+
+terminate(_Reason, _State) ->
+    ok.
+
+code_change(_OldVsn, State, _Extra) ->
+    {ok, State}.
+
+%% ------------------------------------------------------------------
+%% Internal Function Definitions
+%% ------------------------------------------------------------------
+
diff --git a/priv/templates/simpleevent.template b/priv/templates/simpleevent.template
new file mode 100644
index 0000000..68f3894
--- /dev/null
+++ b/priv/templates/simpleevent.template
@@ -0,0 +1,2 @@
+{variables, [{eventid, "myevent"}]}.
+{template, "simpleevent.erl", "src/{{eventid}}.erl"}.
diff --git a/priv/templates/simplenode.install_upgrade.escript b/priv/templates/simplenode.install_upgrade.escript
old mode 100644
new mode 100755
index 56cea19..0d4cbe3
--- a/priv/templates/simplenode.install_upgrade.escript
+++ b/priv/templates/simplenode.install_upgrade.escript
@@ -6,6 +6,14 @@
 -define(TIMEOUT, 60000).
 -define(INFO(Fmt,Args), io:format(Fmt,Args)).
 
+%% TODO: This script currently does NOT support slim releases.
+%% Necessary steps to upgrade a slim release are as follows:
+%% 1. unpack relup archive manually
+%% 2. copy releases directory and necessary libraries
+%% 3. using release_hander:set_unpacked/2 .
+%% For more details, see https://github.com/rebar/rebar/pull/52
+%% and https://github.com/rebar/rebar/issues/202
+
 main([NodeName, Cookie, ReleasePackage]) ->
     TargetNode = start_distribution(NodeName, Cookie),
     {ok, Vsn} = rpc:call(TargetNode, release_handler, unpack_release,
diff --git a/priv/templates/simplenode.reltool.config b/priv/templates/simplenode.reltool.config
index bac7270..eae8ab3 100644
--- a/priv/templates/simplenode.reltool.config
+++ b/priv/templates/simplenode.reltool.config
@@ -32,7 +32,7 @@
 {overlay, [
            {mkdir, "log/sasl"},
            {copy, "files/erl", "\{\{erts_vsn\}\}/bin/erl"},
-           {copy, "files/nodetool", "\{\{erts_vsn\}\}/bin/nodetool"},
+           {copy, "files/nodetool", "releases/\{\{rel_vsn\}\}/nodetool"},
            {copy, "{{nodeid}}/bin/start_clean.boot",
                   "\{\{erts_vsn\}\}/bin/start_clean.boot"},
            {copy, "files/{{nodeid}}", "bin/{{nodeid}}"},
diff --git a/priv/templates/simplenode.runner b/priv/templates/simplenode.runner
index 032eb28..3e9e10f 100755
--- a/priv/templates/simplenode.runner
+++ b/priv/templates/simplenode.runner
@@ -100,7 +100,7 @@
     *)
         REMSH_NAME_PART="$REMSH_NAME"
         if [ "$REMSH_TYPE" = "-sname" ]; then
-            REMSH_HOSTNAME_PART= "$HOSTNAME"
+            REMSH_HOSTNAME_PART="$HOSTNAME"
         else
             # -name type, check if `hostname` is fqdn
             if [ "$MAYBE_FQDN_HOSTNAME" = "$HOSTNAME" ]; then
@@ -130,11 +130,43 @@
 # 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
+RUNNER_SCRIPT_DATA=
+if [ -e "$RUNNER_BASE_DIR/releases/$APP_VSN/runner_script.data" ]; then
+    RUNNER_SCRIPT_DATA=`cat $RUNNER_BASE_DIR/releases/$APP_VSN/runner_script.data`
+fi
 
-# Setup command to control the node
-NODETOOL="$ERTS_PATH/escript $ERTS_PATH/nodetool $NAME_ARG $COOKIE_ARG"
+if [ -z "$RUNNER_SCRIPT_DATA" ]; then
+    ROOTDIR=$RUNNER_BASE_DIR
+    ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin
+    if [ -e "$RUNNER_BASE_DIR/releases/$APP_VSN/nodetool" ]; then
+        NODETOOL="$ERTS_PATH/escript $RUNNER_BASE_DIR/releases/$APP_VSN/nodetool $NAME_ARG $COOKIE_ARG"
+    else
+        NODETOOL="$ERTS_PATH/escript $ERTS_PATH/nodetool $NAME_ARG $COOKIE_ARG"
+    fi
+    SLIM_ARGS=
+elif [ "$RUNNER_SCRIPT_DATA" = "slim" ]; then
+    # Setup system paths
+    SYSTEM_ERL_PATH=`which erl`
+    if [ ! -x "$SYSTEM_ERL_PATH" ]; then
+        echo "Failed to find erl. Is Erlang/OTP available in PATH?"
+        exit 1
+    fi
+    SYSTEM_HOME_BIN=${SYSTEM_ERL_PATH%/*}
+    ROOTDIR=$SYSTEM_HOME_BIN/../lib/erlang
+    ERTS_PATH=$ROOTDIR/erts-$ERTS_VSN/bin
+    unset SYSTEM_ERL_PATH
+    unset SYSTEM_HOME_BIN
+
+    LOCAL_ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin
+    NODETOOL="$ERTS_PATH/escript $RUNNER_BASE_DIR/releases/$APP_VSN/nodetool $NAME_ARG $COOKIE_ARG"
+    unset LOCAL_ERL_PATH
+
+    # Setup additional arguments for slim release
+    SLIM_ARGS="-boot_var RELTOOL_EXT_LIB $RUNNER_BASE_DIR/lib -sasl releases_dir \"$RUNNER_BASE_DIR/releases\""
+else
+    echo "Unknown runner_script.data"
+    exit 1
+fi
 
 # Setup remote shell command to control node
 REMSH="$ERTS_PATH/erl -hidden $REMSH_NAME_ARG $REMSH_REMSH_ARG $COOKIE_ARG"
@@ -310,11 +342,10 @@
                 ;;
         esac
         # Setup beam-required vars
-        ROOTDIR=$RUNNER_BASE_DIR
         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"
+        CMD="$BINDIR/erlexec $SLIM_ARGS -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -mode embedded -config $CONFIG_PATH -args_file $VMARGS_PATH"
         export EMU
         export ROOTDIR
         export BINDIR
@@ -339,11 +370,10 @@
         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"
+        CMD="$BINDIR/erlexec $SLIM_ARGS $FOREGROUNDOPTIONS -boot $RUNNER_BASE_DIR/releases/$APP_VSN/$BOOTFILE -config $CONFIG_PATH -args_file $VMARGS_PATH"
         export EMU
         export ROOTDIR
         export BINDIR
diff --git a/rebar.config b/rebar.config
index 9028737..bf4ef4f 100644
--- a/rebar.config
+++ b/rebar.config
@@ -4,7 +4,11 @@
 %% escript_incl_extra is for internal rebar-private use only.
 %% Do not use outside rebar. Config interface is not stable.
 {escript_incl_extra, [{"priv/templates/*", "."}]}.
-{erl_opts, [warnings_as_errors]}.
+%% Types dict:dict() and digraph:digraph() have been introduced in Erlang 17.
+%% At the same time, their counterparts dict() and digraph() are to be deprecated
+%% in Erlang 18. namespaced_types option is used to select proper type name
+%% depending of the OTP version used.
+{erl_opts, [{platform_define, "^[0-9]+", namespaced_types}, warnings_as_errors]}.
 {xref_checks, []}.
 {xref_queries,
  [{"(XC - UC) || (XU - X - B
diff --git a/rebar.config.sample b/rebar.config.sample
index 515ed00..d154ac1 100644
--- a/rebar.config.sample
+++ b/rebar.config.sample
@@ -21,7 +21,7 @@
 %% Erlang files to compile before the rest. Rebar automatically compiles
 %% parse_transforms and custom behaviours before anything other than the files
 %% in this list.
-{erl_first_files, ["mymib1", "mymib2"]}.
+{erl_first_files, ["src/mymib1.erl", "src/mymib2.erl"]}.
 
 %% Erlang compiler options
 {erl_opts, [no_debug_info,
@@ -155,7 +155,7 @@
 %% name as an atom, eg. mochiweb, a name and a version (from the .app file), or
 %% an application name, a version and the SCM details on how to fetch it (SCM
 %% type, location and revision).
-%% Rebar currently supports git, hg, bzr, svn, rsync, and fossil.
+%% Rebar currently supports git, hg, bzr, svn, rsync, fossil, and p4.
 {deps, [app_name,
         {rebar, "1.0.*"},
         {rebar, ".*",
@@ -188,7 +188,8 @@
         {app_name, ".*", {svn, "svn://svn.example.org/url"}},
         {app_name, ".*", {bzr, "https://www.example.org/url", "Rev"}},
         {app_name, ".*", {fossil, "https://www.example.org/url"}},
-        {app_name, ".*", {fossil, "https://www.example.org/url", "Vsn"}}]}.
+        {app_name, ".*", {fossil, "https://www.example.org/url", "Vsn"}},
+        {app_name, ".*", {p4, "//depot/subdir/app_dir"}}]}.
 
 %% == Subdirectories ==
 
diff --git a/src/rebar.erl b/src/rebar.erl
index 618cce8..a43da5f 100644
--- a/src/rebar.erl
+++ b/src/rebar.erl
@@ -207,9 +207,18 @@
        "  ~p~n"
        "  ~p~n"
        "  ~p~n"
-       "  ~p~n",
+       "  ~p~n"
+       "  ~p~n"
+       "  ~p~n"
+       "  ~p~n"
+       "Core command line options:~n"
+       "  apps=app1,app2 (specify apps to process)~n"
+       "  skip_apps=app1,app2 (specify apps to skip)~n",
        [
         {recursive_cmds, []},
+        {require_erts_vsn, ".*"},
+        {require_otp_vsn, ".*"},
+        {require_min_otp_vsn, ".*"},
         {lib_dirs, []},
         {sub_dirs, ["dir1", "dir2"]},
         {plugins, [plugin1, plugin2]},
@@ -400,6 +409,9 @@
 
 xref                                     Run cross reference analysis
 
+shell                                    Start a shell similar to
+                                         'erl -pa ebin -pa deps/*/ebin'
+
 help                                     Show the program options
 version                                  Show version information
 ">>,
diff --git a/include/rebar.hrl b/src/rebar.hrl
similarity index 100%
rename from include/rebar.hrl
rename to src/rebar.hrl
diff --git a/src/rebar_config.erl b/src/rebar_config.erl
index 10c6483..1c90d22 100644
--- a/src/rebar_config.erl
+++ b/src/rebar_config.erl
@@ -39,13 +39,21 @@
 
 -include("rebar.hrl").
 
+-ifdef(namespaced_types).
+% dict:dict() exists starting from Erlang 17.
+-type rebar_dict() :: dict:dict().
+-else.
+% dict() has been obsoleted in Erlang 17 and deprecated in 18.
+-type rebar_dict() :: dict().
+-endif.
+
 -record(config, { dir :: file:filename(),
                   opts = [] :: list(),
-                  globals = new_globals() :: dict(),
-                  envs = new_env() :: dict(),
+                  globals = new_globals() :: rebar_dict(),
+                  envs = new_env() :: rebar_dict(),
                   %% cross-directory/-command config
-                  skip_dirs = new_skip_dirs() :: dict(),
-                  xconf = new_xconf() :: dict() }).
+                  skip_dirs = new_skip_dirs() :: rebar_dict(),
+                  xconf = new_xconf() :: rebar_dict() }).
 
 -export_type([config/0]).
 
diff --git a/src/rebar_core.erl b/src/rebar_core.erl
index 81b9a6d..212365b 100644
--- a/src/rebar_core.erl
+++ b/src/rebar_core.erl
@@ -88,7 +88,7 @@
             %% path from inside a subdirectory.
             true = rebar_utils:expand_code_path(),
             {ParentConfig2, _DirSet} = process_dir(rebar_utils:get_cwd(),
-                                                   ParentConfig1, Command,
+                                                   Command, ParentConfig1,
                                                    sets:new()),
             case get_operations(ParentConfig2) of
                 Operations ->
@@ -117,18 +117,13 @@
         end,
     process_commands(Rest, ParentConfig4).
 
-process_dir(Dir, ParentConfig, Command, DirSet) ->
+process_dir(Dir, Command, ParentConfig, DirSet) ->
     case filelib:is_dir(Dir) of
         false ->
             ?WARN("Skipping non-existent sub-dir: ~p\n", [Dir]),
             {ParentConfig, DirSet};
         true ->
-            maybe_process_dir(Dir, ParentConfig, Command, DirSet)
-    end.
-
-maybe_process_dir(Dir, ParentConfig, Command, DirSet) ->
-    case should_cd_into_dir(Dir, ParentConfig, Command) of
-        true ->
+            WouldCd = would_cd_into_dir(Dir, Command, ParentConfig),
             ok = file:set_cwd(Dir),
             Config = maybe_load_local_config(Dir, ParentConfig),
 
@@ -143,63 +138,89 @@
             %% set of modules to process this dir.
             {ok, AvailModuleSets} = application:get_env(rebar, modules),
             ModuleSet = choose_module_set(AvailModuleSets, Dir),
-            skip_or_process_dir(ModuleSet, Config, CurrentCodePath,
-                                Dir, Command, DirSet);
-        false ->
-            {ParentConfig, DirSet}
+            skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath,
+                                ModuleSet, WouldCd)
     end.
 
-should_cd_into_dir(Dir, Config, Command) ->
+would_cd_into_dir(Dir, Command, Config) ->
+    case would_cd_into_dir1(Dir, Command, Config) of
+        true ->
+            would_cd;
+        false ->
+            would_not_cd
+    end.
+
+would_cd_into_dir1(Dir, Command, Config) ->
     rebar_utils:processing_base_dir(Config, Dir) orelse
         rebar_config:is_recursive(Config) orelse
-        is_recursive_command(Config, Command).
+        is_recursive_command(Command, Config) orelse
+        is_generate_in_rel_dir(Command, Dir).
 
-is_recursive_command(Config, Command) ->
+%% Check whether the command is part of the built-in (or extended via
+%% rebar.config) list of default-recursive commands.
+is_recursive_command(Command, Config) ->
     {ok, AppCmds} = application:get_env(rebar, recursive_cmds),
     ConfCmds = rebar_config:get_local(Config, recursive_cmds, []),
     RecursiveCmds = AppCmds ++ ConfCmds,
     lists:member(Command, RecursiveCmds).
 
-skip_or_process_dir({[], undefined}=ModuleSet, Config, CurrentCodePath,
-                    Dir, Command, DirSet) ->
-    process_dir1(Dir, Command, DirSet, Config, CurrentCodePath, ModuleSet);
-skip_or_process_dir({_, ModuleSetFile}=ModuleSet, Config, CurrentCodePath,
-                    Dir, Command, DirSet) ->
-    case lists:suffix(".app.src", ModuleSetFile)
-        orelse lists:suffix(".app", ModuleSetFile) of
+%% If the directory we're about to process contains
+%% reltool.config[.script] and the command to be applied is
+%% 'generate', then it's safe to process. We do this to retain the
+%% behavior of specifying {sub_dirs, ["rel"]} and have "rebar generate"
+%% pick up rel/reltool.config[.script]. Without this workaround you'd
+%% have to run "rebar -r generate" (which you don't want to do if you
+%% have deps or other sub_dirs) or "cd rel && rebar generate".
+is_generate_in_rel_dir(generate, Dir) ->
+    case rebar_rel_utils:is_rel_dir(Dir) of
+        {true, _} ->
+            true;
+        false ->
+            false
+    end;
+is_generate_in_rel_dir(_, _) ->
+    false.
+
+skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath,
+                    {[], undefined}=ModuleSet, WouldCd) ->
+    process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, ModuleSet,
+                 WouldCd);
+skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath,
+                    {_, File}=ModuleSet, WouldCd) ->
+    case lists:suffix(".app.src", File)
+        orelse lists:suffix(".app", File) of
         true ->
             %% .app or .app.src file, check if is_skipped_app
-            skip_or_process_dir1(ModuleSetFile, ModuleSet,
-                                 Config, CurrentCodePath, Dir,
-                                 Command, DirSet);
+            skip_or_process_dir1(Dir, Command, Config, DirSet, CurrentCodePath,
+                                 ModuleSet, WouldCd, File);
         false ->
             %% not an app dir, no need to consider apps=/skip_apps=
-            process_dir1(Dir, Command, DirSet, Config,
-                         CurrentCodePath, ModuleSet)
+            process_dir1(Dir, Command, Config, DirSet, CurrentCodePath,
+                         ModuleSet, WouldCd)
     end.
 
-skip_or_process_dir1(AppFile, ModuleSet, Config, CurrentCodePath,
-                     Dir, Command, DirSet) ->
+skip_or_process_dir1(Dir, Command, Config, DirSet, CurrentCodePath, ModuleSet,
+                     WouldCd, AppFile) ->
     case rebar_app_utils:is_skipped_app(Config, AppFile) of
         {Config1, {true, _SkippedApp}} when Command == 'update-deps' ->
             %% update-deps does its own app skipping. Unfortunately there's no
             %% way to signal this to rebar_core, so we have to explicitly do it
             %% here... Otherwise if you use app=, it'll skip the toplevel
             %% directory and nothing will be updated.
-            process_dir1(Dir, Command, DirSet, Config1,
-                         CurrentCodePath, ModuleSet);
+            process_dir1(Dir, Command, Config1, DirSet, CurrentCodePath,
+                         ModuleSet, WouldCd);
         {Config1, {true, SkippedApp}} ->
             ?DEBUG("Skipping app: ~p~n", [SkippedApp]),
-            Config2 = increment_operations(Config1),
-            {Config2, DirSet};
+            {increment_operations(Config1), DirSet};
         {Config1, false} ->
-            process_dir1(Dir, Command, DirSet, Config1,
-                         CurrentCodePath, ModuleSet)
+            process_dir1(Dir, Command, Config1, DirSet, CurrentCodePath,
+                         ModuleSet, WouldCd)
     end.
 
-process_dir1(Dir, Command, DirSet, Config, CurrentCodePath,
-             {DirModules, ModuleSetFile}) ->
+process_dir1(Dir, Command, Config, DirSet, CurrentCodePath,
+             {DirModules, File}, WouldCd) ->
     Config0 = rebar_config:set_xconf(Config, current_command, Command),
+
     %% 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
@@ -210,8 +231,7 @@
 
     %% Invoke 'preprocess' on the modules -- this yields a list of other
     %% directories that should be processed _before_ the current one.
-    {Config1, Predirs} = acc_modules(Modules, preprocess, Config0,
-                                     ModuleSetFile),
+    {Config1, Predirs} = acc_modules(Modules, preprocess, Config0, File),
 
     %% Remember associated pre-dirs (used for plugin lookup)
     PredirsAssoc = remember_cwd_predirs(Dir, Predirs),
@@ -219,55 +239,33 @@
     %% Get the list of plug-in modules from rebar.config. These
     %% modules may participate in preprocess and postprocess.
     {ok, PluginModules} = plugin_modules(Config1, PredirsAssoc),
+    AllModules = Modules ++ PluginModules,
 
-    {Config2, PluginPredirs} = acc_modules(PluginModules, preprocess,
-                                           Config1, ModuleSetFile),
+    {Config2, PluginPredirs} = acc_modules(PluginModules, preprocess, Config1,
+                                           File),
 
     AllPredirs = Predirs ++ PluginPredirs,
 
     ?DEBUG("Predirs: ~p\n", [AllPredirs]),
-    {Config3, DirSet2} = process_each(AllPredirs, Command, Config2,
-                                      ModuleSetFile, DirSet),
+    {Config3, DirSet2} = process_each(AllPredirs, Command, Config2, DirSet,
+                                      File),
 
     %% 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
-    Config7 = case rebar_config:is_skip_dir(Config3, 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]),
-                      Config3;
-
-                  false ->
-                      %% Check for and get command specific environments
-                      {Config4, Env} = setup_envs(Config3, Modules),
-
-                      %% Execute any before_command plugins on this directory
-                      Config5 = execute_pre(Command, PluginModules,
-                                            Config4, ModuleSetFile, Env),
-
-                      %% Execute the current command on this directory
-                      Config6 = execute(Command, Modules ++ PluginModules,
-                                        Config5, ModuleSetFile, Env),
-
-                      %% Execute any after_command plugins on this directory
-                      execute_post(Command, PluginModules,
-                                   Config6, ModuleSetFile, Env)
-              end,
+    %% Maybe apply command to Dir
+    Config4 = maybe_execute(Dir, Command, Config3, Modules, PluginModules,
+                            AllModules, File, WouldCd),
 
     %% 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.
-    {Config8, Postdirs} = acc_modules(Modules ++ PluginModules, postprocess,
-                                      Config7, ModuleSetFile),
+    {Config5, Postdirs} = acc_modules(AllModules, postprocess, Config4, File),
     ?DEBUG("Postdirs: ~p\n", [Postdirs]),
-    Res = process_each(Postdirs, Command, Config8,
-                       ModuleSetFile, DirSet3),
+    Res = process_each(Postdirs, Command, Config5, DirSet3, File),
 
     %% Make sure the CWD is reset properly; processing the dirs may have
     %% caused it to change
@@ -280,6 +278,33 @@
     %% Return the updated {config, dirset} as result
     Res.
 
+maybe_execute(Dir, Command, Config, Modules, PluginModules, AllModules, File,
+              would_cd) ->
+    %% Check that this directory is not on the skip list
+    case rebar_config:is_skip_dir(Config, 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]),
+            Config;
+
+        false ->
+            %% Check for and get command specific environments
+            {Config1, Env} = setup_envs(Config, Modules),
+
+            %% Execute any before_command plugins on this directory
+            Config2 = execute_pre(Command, PluginModules, Config1, File, Env),
+
+            %% Execute the current command on this directory
+            Config3 = execute(Command, AllModules, Config2, File, Env),
+
+            %% Execute any after_command plugins on this directory
+            execute_post(Command, PluginModules, Config3, File, Env)
+    end;
+maybe_execute(_Dir, _Command, Config, _Modules, _PluginModules, _AllModules,
+              _File, would_not_cd) ->
+    Config.
+
 remember_cwd_predirs(Cwd, Predirs) ->
     Store = fun(Dir, Dict) ->
                     case dict:find(Dir, Dict) of
@@ -310,21 +335,21 @@
 %% Given a list of directories and a set of previously processed directories,
 %% process each one we haven't seen yet
 %%
-process_each([], _Command, Config, _ModuleSetFile, DirSet) ->
+process_each([], _Command, Config, DirSet, _File) ->
     %% reset cached (setup_env) envs
     Config1 = rebar_config:reset_envs(Config),
     {Config1, DirSet};
-process_each([Dir | Rest], Command, Config, ModuleSetFile, DirSet) ->
+process_each([Dir | Rest], Command, Config, DirSet, File) ->
     case sets:is_element(Dir, DirSet) of
         true ->
             ?DEBUG("Skipping ~s; already processed!\n", [Dir]),
-            process_each(Rest, Command, Config, ModuleSetFile, DirSet);
+            process_each(Rest, Command, Config, DirSet, File);
         false ->
-            {Config1, DirSet2} = process_dir(Dir, Config, Command, DirSet),
+            {Config1, DirSet2} = process_dir(Dir, Command, Config, DirSet),
             Config2 = rebar_config:clean_config(Config, Config1),
             %% reset cached (setup_env) envs
             Config3 = rebar_config:reset_envs(Config2),
-            process_each(Rest, Command, Config3, ModuleSetFile, DirSet2)
+            process_each(Rest, Command, Config3, DirSet2, File)
     end.
 
 %%
@@ -358,20 +383,21 @@
 
 execute_plugin_hook(Hook, Command, Modules, Config, ModuleFile, Env) ->
     HookFunction = list_to_atom(Hook ++ atom_to_list(Command)),
-    execute(HookFunction, Modules, Config, ModuleFile, Env).
+    execute(HookFunction, hook, Modules, Config, ModuleFile, Env).
 
 %%
 %% Execute a command across all applicable modules
 %%
 execute(Command, Modules, Config, ModuleFile, Env) ->
+    execute(Command, not_a_hook, Modules, Config, ModuleFile, Env).
+
+execute(Command, Type, Modules, Config, ModuleFile, Env) ->
     case select_modules(Modules, Command, []) of
         [] ->
-            Cmd = atom_to_list(Command),
-            case lists:prefix("pre_", Cmd)
-                orelse lists:prefix("post_", Cmd) of
-                true ->
+            case Type of
+                hook ->
                     ok;
-                false ->
+                not_a_hook ->
                     ?WARN("'~p' command does not apply to directory ~s\n",
                           [Command, rebar_utils:get_cwd()])
             end,
@@ -470,8 +496,9 @@
             {Module, Error}
     end.
 
-apply_hooks(Mode, Config, Command, Env) ->
+apply_hooks(Mode, Config, Command, Env0) ->
     Hooks = rebar_config:get_local(Config, Mode, []),
+    Env = rebar_utils:patch_env(Config, Env0),
     lists:foreach(fun apply_hook/1,
                   [{Env, Hook} || Hook <- Hooks,
                                   element(1, Hook) =:= Command orelse
diff --git a/src/rebar_cover_utils.erl b/src/rebar_cover_utils.erl
new file mode 100644
index 0000000..3195fe2
--- /dev/null
+++ b/src/rebar_cover_utils.erl
@@ -0,0 +1,261 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+%% -------------------------------------------------------------------
+%%
+%% rebar: Erlang Build Tools
+%%
+%% Copyright (c) 2009, 2010 Dave Smith (dizzyd@dizzyd.com)
+%% Copyright (c) 2013 Andras Horvath (andras.horvath@erlang-solutions.com)
+%%
+%% Permission is hereby granted, free of charge, to any person obtaining a copy
+%% of this software and associated documentation files (the "Software"), to deal
+%% in the Software without restriction, including without limitation the rights
+%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+%% copies of the Software, and to permit persons to whom the Software is
+%% furnished to do so, subject to the following conditions:
+%%
+%% The above copyright notice and this permission notice shall be included in
+%% all copies or substantial portions of the Software.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+%% THE SOFTWARE.
+%% -------------------------------------------------------------------
+-module(rebar_cover_utils).
+
+%% for internal use only
+-export([init/3,
+         perform_cover/4,
+         close/1,
+         exit/0]).
+
+-include("rebar.hrl").
+
+%% ====================================================================
+%% Internal functions
+%% ====================================================================
+
+perform_cover(Config, BeamFiles, SrcModules, TargetDir) ->
+    perform_cover(rebar_config:get(Config, cover_enabled, false),
+                  Config, BeamFiles, SrcModules, TargetDir).
+
+perform_cover(false, _Config, _BeamFiles, _SrcModules, _TargetDir) ->
+    ok;
+perform_cover(true, Config, BeamFiles, SrcModules, TargetDir) ->
+    analyze(Config, BeamFiles, SrcModules, TargetDir).
+
+close(not_enabled) ->
+    ok;
+close(F) ->
+    ok = file:close(F).
+
+exit() ->
+   cover:stop().
+
+init(false, _BeamFiles, _TargetDir) ->
+    {ok, not_enabled};
+init(true, BeamFiles, TargetDir) ->
+    %% Attempt to start the cover server, then set its group leader to
+    %% TargetDir/cover.log, so all cover log messages will go there instead of
+    %% to stdout. If the cover server is already started, we'll kill that
+    %% server and start a new one in order not to inherit a polluted
+    %% cover_server state.
+    {ok, CoverPid} = case whereis(cover_server) of
+                         undefined ->
+                             cover:start();
+                         _         ->
+                             cover:stop(),
+                             cover:start()
+                     end,
+
+    {ok, F} = OkOpen = file:open(
+                         filename:join([TargetDir, "cover.log"]),
+                         [write]),
+
+    group_leader(F, CoverPid),
+
+    ?INFO("Cover compiling ~s\n", [rebar_utils:get_cwd()]),
+
+    Compiled = [{Beam, cover:compile_beam(Beam)} || Beam <- BeamFiles],
+    case [Module || {_, {ok, Module}} <- Compiled] of
+        [] ->
+            %% No modules compiled successfully...fail
+            ?ERROR("Cover failed to compile any modules; aborting.~n", []),
+            ?FAIL;
+        _ ->
+            %% At least one module compiled successfully
+
+            %% It's not an error for cover compilation to fail partially,
+            %% but we do want to warn about them
+            PrintWarning =
+                fun(Beam, Desc) ->
+                        ?CONSOLE("Cover compilation warning for ~p: ~p",
+                                 [Beam, Desc])
+                end,
+            _ = [PrintWarning(Beam, Desc) || {Beam, {error, Desc}} <- Compiled],
+            OkOpen
+    end;
+init(Config, BeamFiles, TargetDir) ->
+    init(rebar_config:get(Config, cover_enabled, false), BeamFiles, TargetDir).
+
+analyze(_Config, [], _SrcModules, _TargetDir) ->
+    ok;
+analyze(Config, FilteredModules, SrcModules, TargetDir) ->
+    %% Generate coverage info for all the cover-compiled modules
+    Coverage = lists:flatten([analyze_mod(M)
+                              || M <- FilteredModules,
+                                 cover:is_compiled(M) =/= false]),
+
+    %% Write index of coverage info
+    write_index(lists:sort(Coverage), SrcModules, TargetDir),
+
+    %% Write coverage details for each file
+    lists:foreach(
+      fun({M, _, _}) ->
+              {ok, _} = cover:analyze_to_file(M,
+                                              cover_file(M, TargetDir),
+                                              [html])
+      end, Coverage),
+
+    Index = filename:join([rebar_utils:get_cwd(), TargetDir, "index.html"]),
+    ?CONSOLE("Cover analysis: ~s\n", [Index]),
+
+    %% Export coverage data, if configured
+    case rebar_config:get(Config, cover_export_enabled, false) of
+        true ->
+            export_coverdata(TargetDir);
+        false ->
+            ok
+    end,
+
+    %% Print coverage report, if configured
+    case rebar_config:get(Config, cover_print_enabled, false) of
+        true ->
+            print_coverage(lists:sort(Coverage));
+        false ->
+            ok
+    end.
+
+analyze_mod(Module) ->
+    case cover:analyze(Module, coverage, module) of
+        {ok, {Module, {Covered, NotCovered}}} ->
+            %% Modules that include the eunit header get an implicit
+            %% test/0 fun, which cover considers a runnable line, but
+            %% eunit:test(TestRepresentation) never calls.  Decrement
+            %% NotCovered in this case.
+            [align_notcovered_count(Module, Covered, NotCovered,
+                                    is_eunitized(Module))];
+        {error, Reason} ->
+            ?ERROR("Cover analyze failed for ~p: ~p ~p\n",
+                   [Module, Reason, code:which(Module)]),
+            []
+    end.
+
+is_eunitized(Mod) ->
+    has_eunit_test_fun(Mod) andalso
+        has_header(Mod, "include/eunit.hrl").
+
+has_eunit_test_fun(Mod) ->
+    [F || {exports, Funs} <- Mod:module_info(),
+          {F, 0} <- Funs, F =:= test] =/= [].
+
+has_header(Mod, Header) ->
+    Mod1 = case code:which(Mod) of
+               cover_compiled ->
+                   {file, File} = cover:is_compiled(Mod),
+                   File;
+               non_existing -> Mod;
+               preloaded -> Mod;
+               L -> L
+           end,
+    {ok, {_, [{abstract_code, {_, AC}}]}} =
+        beam_lib:chunks(Mod1, [abstract_code]),
+    [F || {attribute, 1, file, {F, 1}} <- AC,
+          string:str(F, Header) =/= 0] =/= [].
+
+align_notcovered_count(Module, Covered, NotCovered, false) ->
+    {Module, Covered, NotCovered};
+align_notcovered_count(Module, Covered, NotCovered, true) ->
+    {Module, Covered, NotCovered - 1}.
+
+write_index(Coverage, SrcModules, TargetDir) ->
+    {ok, F} = file:open(filename:join([TargetDir, "index.html"]), [write]),
+    ok = file:write(F, "<!DOCTYPE HTML><html>\n"
+                    "<head><meta charset=\"utf-8\">"
+                    "<title>Coverage Summary</title></head>\n"
+                    "<body>\n"),
+    IsSrcCoverage = fun({Mod,_C,_N}) -> lists:member(Mod, SrcModules) end,
+    {SrcCoverage, TestCoverage} = lists:partition(IsSrcCoverage, Coverage),
+    write_index_section(F, "Source", SrcCoverage),
+    write_index_section(F, "Test", TestCoverage),
+    ok = file:write(F, "</body></html>"),
+    ok = file:close(F).
+
+write_index_section(_F, _SectionName, []) ->
+    ok;
+write_index_section(F, SectionName, Coverage) ->
+    %% Calculate total coverage
+    {Covered, NotCovered} = lists:foldl(fun({_Mod, C, N}, {CAcc, NAcc}) ->
+                                                {CAcc + C, NAcc + N}
+                                        end, {0, 0}, Coverage),
+    TotalCoverage = percentage(Covered, NotCovered),
+
+    %% Write the report
+    ok = file:write(F, ?FMT("<h1>~s Summary</h1>\n", [SectionName])),
+    ok = file:write(F, ?FMT("<h3>Total: ~s</h3>\n", [TotalCoverage])),
+    ok = file:write(F, "<table><tr><th>Module</th><th>Coverage %</th></tr>\n"),
+
+    FmtLink =
+        fun(Module, Cov, NotCov) ->
+                ?FMT("<tr><td><a href='~s.COVER.html'>~s</a></td><td>~s</td>\n",
+                     [Module, Module, percentage(Cov, NotCov)])
+        end,
+    lists:foreach(fun({Module, Cov, NotCov}) ->
+                          ok = file:write(F, FmtLink(Module, Cov, NotCov))
+                  end, Coverage),
+    ok = file:write(F, "</table>\n").
+
+print_coverage(Coverage) ->
+    {Covered, NotCovered} = lists:foldl(fun({_Mod, C, N}, {CAcc, NAcc}) ->
+                                                {CAcc + C, NAcc + N}
+                                        end, {0, 0}, Coverage),
+    TotalCoverage = percentage(Covered, NotCovered),
+
+    %% Determine the longest module name for right-padding
+    Width = lists:foldl(fun({Mod, _, _}, Acc) ->
+                                case length(atom_to_list(Mod)) of
+                                    N when N > Acc ->
+                                        N;
+                                    _ ->
+                                        Acc
+                                end
+                        end, 0, Coverage) * -1,
+
+    %% Print the output the console
+    ?CONSOLE("~nCode Coverage:~n", []),
+    lists:foreach(fun({Mod, C, N}) ->
+                          ?CONSOLE("~*s : ~3s~n",
+                                   [Width, Mod, percentage(C, N)])
+                  end, Coverage),
+    ?CONSOLE("~n~*s : ~s~n", [Width, "Total", TotalCoverage]).
+
+cover_file(Module, TargetDir) ->
+    filename:join([TargetDir, atom_to_list(Module) ++ ".COVER.html"]).
+
+export_coverdata(TargetDir) ->
+    ExportFile = filename:join(TargetDir, "cover.coverdata"),
+    case cover:export(ExportFile) of
+        ok ->
+            ?CONSOLE("Coverdata export: ~s~n", [ExportFile]);
+        {error, Reason} ->
+            ?ERROR("Coverdata export failed: ~p~n", [Reason])
+    end.
+
+percentage(0, 0) ->
+    "not executed";
+percentage(Cov, NotCov) ->
+    integer_to_list(trunc((Cov / (Cov + NotCov)) * 100)) ++ "%".
diff --git a/src/rebar_ct.erl b/src/rebar_ct.erl
index f3ed29f..c075e8c 100644
--- a/src/rebar_ct.erl
+++ b/src/rebar_ct.erl
@@ -210,7 +210,7 @@
     CodeDirs = [io_lib:format("\"~s\"", [Dir]) ||
                    Dir <- [EbinDir|NonLibCodeDirs]],
     CodePathString = string:join(CodeDirs, " "),
-    Cmd = case get_ct_specs(Cwd) of
+    Cmd = case get_ct_specs(Config, Cwd) of
               undefined ->
                   ?FMT("~s"
                        " -pa ~s"
@@ -260,8 +260,8 @@
 get_extra_params(Config) ->
     rebar_config:get_local(Config, ct_extra_params, "").
 
-get_ct_specs(Cwd) ->
-    case collect_glob(Cwd, ".*\.test\.spec\$") of
+get_ct_specs(Config, Cwd) ->
+    case collect_glob(Config, Cwd, ".*\.test\.spec\$") of
         [] -> undefined;
         [Spec] ->
             " -spec " ++ Spec;
@@ -275,31 +275,38 @@
         false ->
             "";
         true ->
-            case collect_glob(Cwd, ".*cover\.spec\$") of
+            case collect_glob(Config, Cwd, ".*cover\.spec\$") of
                 [] ->
                     ?DEBUG("No cover spec found: ~s~n", [Cwd]),
                     "";
                 [Spec] ->
-                    ?DEBUG("Found cover file ~w~n", [Spec]),
+                    ?DEBUG("Found cover file ~s~n", [Spec]),
                     " -cover " ++ Spec;
                 Specs ->
                     ?ABORT("Multiple cover specs found: ~p~n", [Specs])
             end
     end.
 
-collect_glob(Cwd, Glob) ->
-    filelib:fold_files(Cwd, Glob, true, fun collect_files/2, []).
+collect_glob(Config, Cwd, Glob) ->
+    {true, Deps} = rebar_deps:get_deps_dir(Config),
+    CwdParts = filename:split(Cwd),
+    filelib:fold_files(Cwd, Glob, true, fun(F, Acc) ->
+        %% Ignore any specs under the deps/ directory. Do this pulling
+        %% the dirname off the F and then splitting it into a list.
+        Parts = filename:split(filename:dirname(F)),
+        Parts2 = remove_common_prefix(Parts, CwdParts),
+        case lists:member(Deps, Parts2) of
+            true ->
+                Acc;                % There is a directory named "deps" in path
+            false ->
+                [F | Acc]           % No "deps" directory in path
+        end
+    end, []).
 
-collect_files(F, Acc) ->
-    %% Ignore any specs under the deps/ directory. Do this pulling
-    %% the dirname off the the F and then splitting it into a list.
-    Parts = filename:split(filename:dirname(F)),
-    case lists:member("deps", Parts) of
-        true ->
-            Acc;                % There is a directory named "deps" in path
-        false ->
-            [F | Acc]           % No "deps" directory in path
-    end.
+remove_common_prefix([H1|T1], [H1|T2]) ->
+    remove_common_prefix(T1, T2);
+remove_common_prefix(L1, _) ->
+    L1.
 
 get_ct_config_file(TestDir) ->
     Config = filename:join(TestDir, "test.config"),
diff --git a/src/rebar_deps.erl b/src/rebar_deps.erl
index 43bde04..bd94921 100644
--- a/src/rebar_deps.erl
+++ b/src/rebar_deps.erl
@@ -40,6 +40,7 @@
 
 %% for internal use only
 -export([info/2]).
+-export([get_deps_dir/1]).
 
 -record(dep, { dir,
                app,
@@ -277,7 +278,8 @@
           {app_name, ".*", {svn, "svn://svn.example.org/url"}},
           {app_name, ".*", {bzr, "https://www.example.org/url", "Rev"}},
           {app_name, ".*", {fossil, "https://www.example.org/url"}},
-          {app_name, ".*", {fossil, "https://www.example.org/url", "Vsn"}}]}
+          {app_name, ".*", {fossil, "https://www.example.org/url", "Vsn"}},
+          {app_name, ".*", {p4, "//depot/subdir/app_dir"}}]}
        ]).
 
 %% Added because of trans deps,
@@ -507,6 +509,40 @@
             use_source(Config, Dep#dep { dir = TargetDir }, Count-1)
     end.
 
+-record(p4_settings, {
+          client=undefined,
+          transport="tcp4:perforce:1666",
+          username,
+          password
+         }).
+init_p4_settings(Basename) ->
+    #p4_settings{client =
+                     case inet:gethostname() of
+                         {ok,HostName} ->
+                             HostName ++ "-"
+                                 ++ os:getenv("USER") ++ "-"
+                                 ++ Basename
+                                 ++ "-Rebar-automated-download"
+                     end}.
+
+download_source(AppDir, {p4, Url}) ->
+    download_source(AppDir, {p4, Url, "#head"});
+download_source(AppDir, {p4, Url, Rev}) ->
+    download_source(AppDir, {p4, Url, Rev, init_p4_settings(filename:basename(AppDir))});
+download_source(AppDir, {p4, Url, _Rev, Settings}) ->
+    ok = filelib:ensure_dir(AppDir),
+    rebar_utils:sh_send("p4 client -i",
+                   ?FMT("Client: ~s~n"
+                        ++"Description: generated by Rebar~n"
+                        ++"Root: ~s~n"
+                        ++"View:~n"
+                        ++"  ~s/...  //~s/...~n",
+                        [Settings#p4_settings.client,
+                         AppDir,
+                         Url,
+                         Settings#p4_settings.client]),
+                       []),
+    rebar_utils:sh(?FMT("p4 -c ~s sync -f", [Settings#p4_settings.client]), []);
 download_source(AppDir, {hg, Url, Rev}) ->
     ok = filelib:ensure_dir(AppDir),
     rebar_utils:sh(?FMT("hg clone -U ~s ~s", [Url, filename:basename(AppDir)]),
@@ -573,6 +609,8 @@
             Dep
     end.
 
+update_source1(AppDir, Args) when element(1, Args) =:= p4 ->
+    download_source(AppDir, Args);
 update_source1(AppDir, {git, Url}) ->
     update_source1(AppDir, {git, Url, {branch, "HEAD"}});
 update_source1(AppDir, {git, Url, ""}) ->
@@ -696,7 +734,7 @@
 
 source_engine_avail(Name, Source)
   when Name == hg; Name == git; Name == svn; Name == bzr; Name == rsync;
-       Name == fossil ->
+       Name == fossil; Name == p4 ->
     case vcs_client_vsn(Name) >= required_vcs_client_vsn(Name) of
         true ->
             true;
@@ -717,6 +755,7 @@
             false
     end.
 
+required_vcs_client_vsn(p4)     -> {2013, 1};
 required_vcs_client_vsn(hg)     -> {1, 1};
 required_vcs_client_vsn(git)    -> {1, 5};
 required_vcs_client_vsn(bzr)    -> {2, 0};
@@ -724,6 +763,9 @@
 required_vcs_client_vsn(rsync)  -> {2, 0};
 required_vcs_client_vsn(fossil) -> {1, 0}.
 
+vcs_client_vsn(p4) ->
+    vcs_client_vsn(rebar_utils:find_executable("p4"), " -V",
+                   "Rev\\. .*/(\\d+)\\.(\\d)/");
 vcs_client_vsn(hg) ->
     vcs_client_vsn(rebar_utils:find_executable("hg"), " --version",
                    "version (\\d+).(\\d+)");
@@ -743,6 +785,8 @@
     vcs_client_vsn(rebar_utils:find_executable("fossil"), " version",
                    "version (\\d+).(\\d+)").
 
+has_vcs_dir(p4, _) ->
+    true;
 has_vcs_dir(git, Dir) ->
     filelib:is_dir(filename:join(Dir, ".git"));
 has_vcs_dir(hg, Dir) ->
@@ -760,6 +804,8 @@
 print_source(#dep{app=App, source=Source}) ->
     ?CONSOLE("~s~n", [format_source(App, Source)]).
 
+format_source(App, {p4, Url}) ->
+    format_source(App, {p4, Url, "#head"});
 format_source(App, {git, Url}) ->
     ?FMT("~p BRANCH ~s ~s", [App, "HEAD", Url]);
 format_source(App, {git, Url, ""}) ->
diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl
index 75d47fb..376cde5 100644
--- a/src/rebar_erlc_compiler.erl
+++ b/src/rebar_erlc_compiler.erl
@@ -47,6 +47,14 @@
           info = {[], []} :: erlc_info()
         }).
 
+-ifdef(namespaced_types).
+% digraph:digraph() exists starting from Erlang 17.
+-type rebar_digraph() :: digraph:digraph().
+-else.
+% digraph() has been obsoleted in Erlang 17 and deprecated in 18.
+-type rebar_digraph() :: digraph().
+-endif.
+
 %% ===================================================================
 %% Public API
 %% ===================================================================
@@ -134,12 +142,13 @@
     %% Obtain all the test modules for inclusion in the compile stage.
     TestErls = rebar_utils:find_files("test", ".*\\.erl\$"),
 
+    ErlOpts = rebar_utils:erl_opts(Config),
+    {Config1, ErlOpts1} = test_compile_config_and_opts(Config, ErlOpts, Cmd),
+
     %% Copy source files to eunit dir for cover in case they are not directly
     %% in src but in a subdirectory of src. Cover only looks in cwd and ../src
     %% for source files. Also copy files from src_dirs.
-    ErlOpts = rebar_utils:erl_opts(Config),
-
-    SrcDirs = rebar_utils:src_dirs(proplists:append_values(src_dirs, ErlOpts)),
+    SrcDirs = rebar_utils:src_dirs(proplists:append_values(src_dirs, ErlOpts1)),
     SrcErls = lists:foldl(
                 fun(Dir, Acc) ->
                         Files = rebar_utils:find_files(Dir, ".*\\.erl\$"),
@@ -172,8 +181,7 @@
     %% Compile erlang code to OutDir, using a tweaked config
     %% with appropriate defines for eunit, and include all the test modules
     %% as well.
-    ok = doterl_compile(test_compile_config(Config, ErlOpts, Cmd),
-                        OutDir, TestErls, ErlOpts),
+    ok = doterl_compile(Config1, OutDir, TestErls, ErlOpts1),
 
     {ok, SrcErls}.
 
@@ -208,7 +216,7 @@
                      "(linux|solaris|freebsd|darwin)", 'HAVE_SENDFILE'},
                     {platform_define, "(linux|freebsd)", 'BACKLOG', 128},
                     {platform_define, "R13", 'old_inets'}]},
-        {erl_first_files, ["mymib1", "mymib2"]},
+        {erl_first_files, ["src/mymib1.erl", "src/mymib2.erl"]},
         {mib_opts, []},
         {mib_first_files, []},
         {xrl_opts, []},
@@ -217,21 +225,29 @@
         {yrl_first_files, []}
        ]).
 
-test_compile_config(Config, ErlOpts, Cmd) ->
+test_compile_config_and_opts(Config, ErlOpts, Cmd) ->
     {Config1, TriqOpts} = triq_opts(Config),
     {Config2, PropErOpts} = proper_opts(Config1),
     {Config3, EqcOpts} = eqc_opts(Config2),
 
+    %% NOTE: For consistency, all *_first_files lists should be
+    %% retrieved via rebar_config:get_local. Right now
+    %% erl_first_files, eunit_first_files, and qc_first_files use
+    %% rebar_config:get_list and are inherited, but xrl_first_files
+    %% and yrl_first_files use rebar_config:get_local. Inheritance of
+    %% *_first_files is questionable as the file would need to exist
+    %% in all project directories for it to work.
     OptsAtom = list_to_atom(Cmd ++ "_compile_opts"),
-    EunitOpts = rebar_config:get_list(Config3, OptsAtom, []),
+    TestOpts = rebar_config:get_list(Config3, OptsAtom, []),
     Opts0 = [{d, 'TEST'}] ++
-        ErlOpts ++ EunitOpts ++ TriqOpts ++ PropErOpts ++ EqcOpts,
+        ErlOpts ++ TestOpts ++ TriqOpts ++ PropErOpts ++ EqcOpts,
     Opts = [O || O <- Opts0, O =/= no_debug_info],
     Config4 = rebar_config:set(Config3, erl_opts, Opts),
 
     FirstFilesAtom = list_to_atom(Cmd ++ "_first_files"),
     FirstErls = rebar_config:get_list(Config4, FirstFilesAtom, []),
-    rebar_config:set(Config4, erl_first_files, FirstErls).
+    Config5 = rebar_config:set(Config4, erl_first_files, FirstErls),
+    {Config5, Opts}.
 
 triq_opts(Config) ->
     {NewConfig, IsAvail} = is_lib_avail(Config, is_triq_avail, triq,
@@ -276,20 +292,28 @@
     doterl_compile(Config, OutDir, [], ErlOpts).
 
 doterl_compile(Config, OutDir, MoreSources, ErlOpts) ->
-    ErlFirstFiles = rebar_config:get_list(Config, erl_first_files, []),
+    ErlFirstFilesConf = rebar_config:get_list(Config, erl_first_files, []),
     ?DEBUG("erl_opts ~p~n", [ErlOpts]),
     %% Support the src_dirs option allowing multiple directories to
     %% contain erlang source. This might be used, for example, should
     %% eunit tests be separated from the core application source.
     SrcDirs = rebar_utils:src_dirs(proplists:append_values(src_dirs, ErlOpts)),
-    RestErls  = [Source || Source <- gather_src(SrcDirs, []) ++ MoreSources,
-                           not lists:member(Source, ErlFirstFiles)],
+    AllErlFiles = gather_src(SrcDirs, []) ++ MoreSources,
+    %% NOTE: If and when erl_first_files is not inherited anymore
+    %% (rebar_config:get_local instead of rebar_config:get_list), consider
+    %% logging a warning message for any file listed in erl_first_files which
+    %% wasn't found via gather_src.
+    {ErlFirstFiles, RestErls} =
+        lists:partition(
+          fun(Source) ->
+                  lists:member(Source, ErlFirstFilesConf)
+          end, AllErlFiles),
     %% Make sure that ebin/ exists and is on the path
     ok = filelib:ensure_dir(filename:join("ebin", "dummy.beam")),
     CurrPath = code:get_path(),
     true = code:add_path(filename:absname("ebin")),
     OutDir1 = proplists:get_value(outdir, ErlOpts, OutDir),
-    G = init_erlcinfo(Config, RestErls),
+    G = init_erlcinfo(Config, AllErlFiles),
     %% Split RestErls so that files which are depended on are treated
     %% like erl_first_files.
     {OtherFirstErls, OtherErls} =
@@ -386,8 +410,8 @@
     ?ABORT("~s file is invalid. Please delete before next run.~n",
            [erlcinfo_file(Config)]).
 
-erlcinfo_file(Config) ->
-    filename:join([rebar_utils:base_dir(Config), ".rebar", ?ERLCINFO_FILE]).
+erlcinfo_file(_Config) ->
+    filename:join([rebar_utils:get_cwd(), ".rebar", ?ERLCINFO_FILE]).
 
 init_erlcinfo(Config, Erls) ->
     G = restore_erlcinfo(Config),
@@ -408,17 +432,17 @@
 update_erlcinfo(G, Source, Dirs) ->
     case digraph:vertex(G, Source) of
         {_, LastUpdated} ->
-            LastModified = filelib:last_modified(Source),
-            if LastModified == 0 ->
+            case filelib:last_modified(Source) of
+                0 ->
                     %% The file doesn't exist anymore,
                     %% erase it from the graph.
                     %% All the edges will be erased automatically.
                     digraph:del_vertex(G, Source),
                     modified;
-               LastUpdated < LastModified ->
-                    modify_erlcinfo(G, Source, Dirs);
+                LastModified when LastUpdated < LastModified ->
+                    modify_erlcinfo(G, Source, Dirs),
                     modified;
-               true ->
+                _ ->
                     unmodified
             end;
         false ->
@@ -521,24 +545,24 @@
               end
       end, Files).
 
--spec get_parents(digraph(), file:filename()) -> [file:filename()].
+-spec get_parents(rebar_digraph(), file:filename()) -> [file:filename()].
 get_parents(G, Source) ->
     %% Return all files which the Source depends upon.
     digraph_utils:reachable_neighbours([Source], G).
 
--spec get_children(digraph(), file:filename()) -> [file:filename()].
+-spec get_children(rebar_digraph(), file:filename()) -> [file:filename()].
 get_children(G, Source) ->
     %% Return all files dependent on the Source.
     digraph_utils:reaching_neighbours([Source], G).
 
 -spec internal_erl_compile(rebar_config:config(), file:filename(),
                            file:filename(), list(),
-                           digraph()) -> 'ok' | 'skipped'.
+                           rebar_digraph()) -> 'ok' | 'skipped'.
 internal_erl_compile(Config, Source, OutDir, ErlOpts, G) ->
     %% Determine the target name and includes list by inspecting the source file
     Module = filename:basename(Source, ".erl"),
     Parents = get_parents(G, Source),
-    log_files(?FMT("~s depends on", [Source]), Parents),
+    log_files(?FMT("Dependencies of ~s", [Source]), Parents),
 
     %% Construct the target filename
     Target = filename:join([OutDir | string:tokens(Module, ".")]) ++ ".beam",
diff --git a/src/rebar_eunit.erl b/src/rebar_eunit.erl
index d969f96..a5b7b00 100644
--- a/src/rebar_eunit.erl
+++ b/src/rebar_eunit.erl
@@ -155,22 +155,22 @@
         [filename:rootname(N) ++ "_tests.beam" || N <- AllBeamFiles],
     ModuleBeamFiles = randomize_suites(Config, BeamFiles ++ OtherBeamFiles),
 
-    %% Get modules to be run in eunit
+    %% Get matching tests and modules
     AllModules = [rebar_utils:beam_to_mod(?EUNIT_DIR, N) || N <- AllBeamFiles],
-    {SuitesProvided, FilteredModules} = filter_suites(Config, AllModules),
-
-    %% Get matching tests
-    Tests = get_tests(Config, SuitesProvided, ModuleBeamFiles, FilteredModules),
+    {Tests, FilteredModules} =
+        get_tests_and_modules(Config, ModuleBeamFiles, AllModules),
 
     SrcModules = [rebar_utils:erl_to_mod(M) || M <- SrcErls],
 
-    {ok, CoverLog} = cover_init(Config, ModuleBeamFiles),
+    {ok, CoverLog} = rebar_cover_utils:init(Config, ModuleBeamFiles,
+                                            eunit_dir()),
 
     StatusBefore = status_before_eunit(),
     EunitResult = perform_eunit(Config, Tests),
 
-    perform_cover(Config, FilteredModules, SrcModules),
-    cover_close(CoverLog),
+    rebar_cover_utils:perform_cover(Config, FilteredModules, SrcModules,
+                                    eunit_dir()),
+    rebar_cover_utils:close(CoverLog),
 
     case proplists:get_value(reset_after_eunit, get_eunit_opts(Config),
                              true) of
@@ -182,7 +182,7 @@
 
     %% Stop cover to clean the cover_server state. This is important if we want
     %% eunit+cover to not slow down when analyzing many Erlang modules.
-    ok = cover:stop(),
+    ok = rebar_cover_utils:exit(),
 
     case EunitResult of
         ok ->
@@ -214,18 +214,23 @@
     CodePath.
 
 %%
-%% == filter suites ==
+%% == get matching tests ==
 %%
+get_tests_and_modules(Config, ModuleBeamFiles, AllModules) ->
+    SelectedSuites = get_selected_suites(Config, AllModules),
+    {Tests, QualifiedTests} = get_qualified_and_unqualified_tests(Config),
+    Modules = get_test_modules(SelectedSuites, Tests,
+                               QualifiedTests, ModuleBeamFiles),
+    FilteredModules = get_matching_modules(AllModules, Modules, QualifiedTests),
+    MatchedTests = get_matching_tests(Modules, Tests, QualifiedTests),
+    {MatchedTests, FilteredModules}.
 
-filter_suites(Config, Modules) ->
+%%
+%% == get suites specified via 'suites' option ==
+%%
+get_selected_suites(Config, Modules) ->
     RawSuites = get_suites(Config),
-    SuitesProvided = RawSuites =/= "",
     Suites = [list_to_atom(Suite) || Suite <- string:tokens(RawSuites, ",")],
-    {SuitesProvided, filter_suites1(Modules, Suites)}.
-
-filter_suites1(Modules, []) ->
-    Modules;
-filter_suites1(Modules, Suites) ->
     [M || M <- Suites, lists:member(M, Modules)].
 
 get_suites(Config) ->
@@ -236,6 +241,32 @@
             Suites
     end.
 
+get_qualified_and_unqualified_tests(Config) ->
+    RawFunctions = rebar_utils:get_experimental_global(Config, tests, ""),
+    FunctionNames = [FunctionName ||
+                        FunctionName <- string:tokens(RawFunctions, ",")],
+    get_qualified_and_unqualified_tests1(FunctionNames, [], []).
+
+get_qualified_and_unqualified_tests1([], Functions, QualifiedFunctions) ->
+    {Functions, QualifiedFunctions};
+get_qualified_and_unqualified_tests1([TestName|TestNames], Functions,
+                                     QualifiedFunctions) ->
+    case string:tokens(TestName, ":") of
+        [TestName] ->
+            Function = list_to_atom(TestName),
+            get_qualified_and_unqualified_tests1(
+              TestNames, [Function|Functions], QualifiedFunctions);
+        [ModuleName, FunctionName] ->
+            M = list_to_atom(ModuleName),
+            F = list_to_atom(FunctionName),
+            get_qualified_and_unqualified_tests1(TestNames, Functions,
+                                                 [{M, F}|QualifiedFunctions]);
+        _ ->
+            ?ABORT("Unsupported test function specification: ~s~n", [TestName])
+    end.
+
+%% Provide modules which are to be searched for tests.
+%% Several scenarios are possible:
 %%
 %% == randomize suites ==
 %%
@@ -265,60 +296,66 @@
 
 %%
 %% == get matching tests ==
+%% 1) Specific tests have been provided and/or
+%% no unqualified tests have been specified and
+%% there were some qualified tests, then we can search for
+%% functions in specified suites (or in empty set of suites).
 %%
-get_tests(Config, SuitesProvided, ModuleBeamFiles, FilteredModules) ->
-    Modules = case SuitesProvided of
-                  false ->
-                      %% No specific suites have been provided, use
-                      %% ModuleBeamFiles which filters out "*_tests" modules
-                      %% so eunit won't doubly run them and cover only
-                      %% calculates coverage on production code. However,
-                      %% keep "*_tests" modules that are not automatically
-                      %% included by eunit.
-                      %%
-                      %% From 'Primitives' in the EUnit User's Guide
-                      %% http://www.erlang.org/doc/apps/eunit/chapter.html
-                      %% "In addition, EUnit will also look for another
-                      %% module whose name is ModuleName plus the suffix
-                      %% _tests, and if it exists, all the tests from that
-                      %% module will also be added. (If ModuleName already
-                      %% contains the suffix _tests, this is not done.) E.g.,
-                      %% the specification {module, mymodule} will run all
-                      %% tests in the modules mymodule and mymodule_tests.
-                      %% Typically, the _tests module should only contain
-                      %% test cases that use the public interface of the main
-                      %% module (and no other code)."
-                      [rebar_utils:beam_to_mod(?EUNIT_DIR, N) ||
-                          N <- ModuleBeamFiles];
-                  true ->
-                      %% Specific suites have been provided, return the
-                      %% filtered modules
-                      FilteredModules
-              end,
-    get_matching_tests(Config, Modules).
-
-get_tests(Config) ->
-    case rebar_config:get_global(Config, tests, "") of
-        "" ->
-            rebar_config:get_global(Config, test, "");
-        Suites ->
-            Suites
+%% 2) Neither specific suites nor qualified test names have been
+%% provided use ModuleBeamFiles which filters out "*_tests"
+%% modules so EUnit won't doubly run them and cover only
+%% calculates coverage on production code. However,
+%% keep "*_tests" modules that are not automatically
+%% included by EUnit.
+%%
+%% From 'Primitives' in the EUnit User's Guide
+%% http://www.erlang.org/doc/apps/eunit/chapter.html
+%% "In addition, EUnit will also look for another
+%% module whose name is ModuleName plus the suffix
+%% _tests, and if it exists, all the tests from that
+%% module will also be added. (If ModuleName already
+%% contains the suffix _tests, this is not done.) E.g.,
+%% the specification {module, mymodule} will run all
+%% tests in the modules mymodule and mymodule_tests.
+%% Typically, the _tests module should only contain
+%% test cases that use the public interface of the main
+%% module (and no other code)."
+get_test_modules(SelectedSuites, Tests, QualifiedTests, ModuleBeamFiles) ->
+    SuitesProvided = SelectedSuites =/= [],
+    OnlyQualifiedTestsProvided = QualifiedTests =/= [] andalso Tests =:= [],
+    if
+        SuitesProvided orelse OnlyQualifiedTestsProvided ->
+            SelectedSuites;
+        true ->
+            [rebar_utils:beam_to_mod(?EUNIT_DIR, N) ||
+                N <- ModuleBeamFiles]
     end.
 
-get_matching_tests(Config, Modules) ->
-    RawFunctions = get_tests(Config),
-    Tests = [list_to_atom(F1) || F1 <- string:tokens(RawFunctions, ",")],
-    case Tests of
-        [] ->
-            Modules;
-        Functions ->
-            case get_matching_tests1(Modules, Functions, []) of
-                [] ->
-                    [];
-                RawTests ->
-                    make_test_primitives(RawTests)
-            end
-    end.
+get_matching_modules(AllModules, Modules, QualifiedTests) ->
+    ModuleFilterMapper =
+        fun({M, _}) ->
+                case lists:member(M, AllModules) of
+                    true -> {true, M};
+                    _-> false
+                end
+        end,
+    ModulesFromQualifiedTests = lists:zf(ModuleFilterMapper, QualifiedTests),
+    lists:usort(Modules ++ ModulesFromQualifiedTests).
+
+get_matching_tests(Modules, [], []) ->
+    Modules;
+get_matching_tests(Modules, [], QualifiedTests) ->
+    FilteredQualifiedTests = filter_qualified_tests(Modules, QualifiedTests),
+    lists:merge(Modules, make_test_primitives(FilteredQualifiedTests));
+get_matching_tests(Modules, Tests, QualifiedTests) ->
+    AllTests = lists:merge(QualifiedTests,
+                           get_matching_tests1(Modules, Tests, [])),
+    make_test_primitives(AllTests).
+
+filter_qualified_tests(Modules, QualifiedTests) ->
+    TestsFilter = fun({Module, _Function}) ->
+                          lists:all(fun(M) -> M =/= Module end, Modules) end,
+    lists:filter(TestsFilter, QualifiedTests).
 
 get_matching_tests1([], _Functions, TestFunctions) ->
     TestFunctions;
@@ -464,226 +501,6 @@
     BaseOpts ++ rebar_config:get_list(Config, eunit_opts, []).
 
 %%
-%% == code coverage ==
-%%
-
-perform_cover(Config, BeamFiles, SrcModules) ->
-    perform_cover(rebar_config:get(Config, cover_enabled, false),
-                  Config, BeamFiles, SrcModules).
-
-perform_cover(false, _Config, _BeamFiles, _SrcModules) ->
-    ok;
-perform_cover(true, Config, BeamFiles, SrcModules) ->
-    cover_analyze(Config, BeamFiles, SrcModules).
-
-cover_analyze(_Config, [], _SrcModules) ->
-    ok;
-cover_analyze(Config, FilteredModules, SrcModules) ->
-    %% Generate coverage info for all the cover-compiled modules
-    Coverage = lists:flatten([cover_analyze_mod(M)
-                              || M <- FilteredModules,
-                                 cover:is_compiled(M) =/= false]),
-
-    %% Write index of coverage info
-    cover_write_index(lists:sort(Coverage), SrcModules),
-
-    %% Write coverage details for each file
-    lists:foreach(fun({M, _, _}) ->
-                          {ok, _} = cover:analyze_to_file(M, cover_file(M),
-                                                          [html])
-                  end, Coverage),
-
-    Index = filename:join([rebar_utils:get_cwd(), ?EUNIT_DIR, "index.html"]),
-    ?CONSOLE("Cover analysis: ~s\n", [Index]),
-
-    %% Export coverage data, if configured
-    case rebar_config:get(Config, cover_export_enabled, false) of
-        true ->
-            cover_export_coverdata();
-        false ->
-            ok
-    end,
-
-    %% Print coverage report, if configured
-    case rebar_config:get(Config, cover_print_enabled, false) of
-        true ->
-            cover_print_coverage(lists:sort(Coverage));
-        false ->
-            ok
-    end.
-
-cover_close(not_enabled) ->
-    ok;
-cover_close(F) ->
-    ok = file:close(F).
-
-cover_init(false, _BeamFiles) ->
-    {ok, not_enabled};
-cover_init(true, BeamFiles) ->
-    %% Attempt to start the cover server, then set its group leader to
-    %% .eunit/cover.log, so all cover log messages will go there instead of
-    %% to stdout. If the cover server is already started, we'll kill that
-    %% server and start a new one in order not to inherit a polluted
-    %% cover_server state.
-    {ok, CoverPid} = case whereis(cover_server) of
-                         undefined ->
-                             cover:start();
-                         _         ->
-                             cover:stop(),
-                             cover:start()
-                     end,
-
-    {ok, F} = OkOpen = file:open(
-                         filename:join([?EUNIT_DIR, "cover.log"]),
-                         [write]),
-
-    group_leader(F, CoverPid),
-
-    ?INFO("Cover compiling ~s\n", [rebar_utils:get_cwd()]),
-
-    Compiled = [{Beam, cover:compile_beam(Beam)} || Beam <- BeamFiles],
-    case [Module || {_, {ok, Module}} <- Compiled] of
-        [] ->
-            %% No modules compiled successfully...fail
-            ?ERROR("Cover failed to compile any modules; aborting.~n", []),
-            ?FAIL;
-        _ ->
-            %% At least one module compiled successfully
-
-            %% It's not an error for cover compilation to fail partially,
-            %% but we do want to warn about them
-            PrintWarning =
-                fun(Beam, Desc) ->
-                        ?CONSOLE("Cover compilation warning for ~p: ~p",
-                                 [Beam, Desc])
-                end,
-            _ = [PrintWarning(Beam, Desc) || {Beam, {error, Desc}} <- Compiled],
-            OkOpen
-    end;
-cover_init(Config, BeamFiles) ->
-    cover_init(rebar_config:get(Config, cover_enabled, false), BeamFiles).
-
-cover_analyze_mod(Module) ->
-    case cover:analyze(Module, coverage, module) of
-        {ok, {Module, {Covered, NotCovered}}} ->
-            %% Modules that include the eunit header get an implicit
-            %% test/0 fun, which cover considers a runnable line, but
-            %% eunit:test(TestRepresentation) never calls.  Decrement
-            %% NotCovered in this case.
-            [align_notcovered_count(Module, Covered, NotCovered,
-                                    is_eunitized(Module))];
-        {error, Reason} ->
-            ?ERROR("Cover analyze failed for ~p: ~p ~p\n",
-                   [Module, Reason, code:which(Module)]),
-            []
-    end.
-
-is_eunitized(Mod) ->
-    has_eunit_test_fun(Mod) andalso
-        has_header(Mod, "include/eunit.hrl").
-
-has_eunit_test_fun(Mod) ->
-    [F || {exports, Funs} <- Mod:module_info(),
-          {F, 0} <- Funs, F =:= test] =/= [].
-
-has_header(Mod, Header) ->
-    Mod1 = case code:which(Mod) of
-               cover_compiled ->
-                   {file, File} = cover:is_compiled(Mod),
-                   File;
-               non_existing -> Mod;
-               preloaded -> Mod;
-               L -> L
-           end,
-    {ok, {_, [{abstract_code, {_, AC}}]}} = beam_lib:chunks(Mod1,
-                                                            [abstract_code]),
-    [F || {attribute, 1, file, {F, 1}} <- AC,
-          string:str(F, Header) =/= 0] =/= [].
-
-align_notcovered_count(Module, Covered, NotCovered, false) ->
-    {Module, Covered, NotCovered};
-align_notcovered_count(Module, Covered, NotCovered, true) ->
-    {Module, Covered, NotCovered - 1}.
-
-cover_write_index(Coverage, SrcModules) ->
-    {ok, F} = file:open(filename:join([?EUNIT_DIR, "index.html"]), [write]),
-    ok = file:write(F, "<!DOCTYPE HTML><html>\n"
-                        "<head><meta charset=\"utf-8\">"
-                        "<title>Coverage Summary</title></head>\n"
-                        "<body>\n"),
-    IsSrcCoverage = fun({Mod,_C,_N}) -> lists:member(Mod, SrcModules) end,
-    {SrcCoverage, TestCoverage} = lists:partition(IsSrcCoverage, Coverage),
-    cover_write_index_section(F, "Source", SrcCoverage),
-    cover_write_index_section(F, "Test", TestCoverage),
-    ok = file:write(F, "</body></html>"),
-    ok = file:close(F).
-
-cover_write_index_section(_F, _SectionName, []) ->
-    ok;
-cover_write_index_section(F, SectionName, Coverage) ->
-    %% Calculate total coverage
-    {Covered, NotCovered} = lists:foldl(fun({_Mod, C, N}, {CAcc, NAcc}) ->
-                                                {CAcc + C, NAcc + N}
-                                        end, {0, 0}, Coverage),
-    TotalCoverage = percentage(Covered, NotCovered),
-
-    %% Write the report
-    ok = file:write(F, ?FMT("<h1>~s Summary</h1>\n", [SectionName])),
-    ok = file:write(F, ?FMT("<h3>Total: ~s</h3>\n", [TotalCoverage])),
-    ok = file:write(F, "<table><tr><th>Module</th><th>Coverage %</th></tr>\n"),
-
-    FmtLink =
-        fun(Module, Cov, NotCov) ->
-                ?FMT("<tr><td><a href='~s.COVER.html'>~s</a></td><td>~s</td>\n",
-                     [Module, Module, percentage(Cov, NotCov)])
-        end,
-    lists:foreach(fun({Module, Cov, NotCov}) ->
-                          ok = file:write(F, FmtLink(Module, Cov, NotCov))
-                  end, Coverage),
-    ok = file:write(F, "</table>\n").
-
-cover_print_coverage(Coverage) ->
-    {Covered, NotCovered} = lists:foldl(fun({_Mod, C, N}, {CAcc, NAcc}) ->
-                                                {CAcc + C, NAcc + N}
-                                        end, {0, 0}, Coverage),
-    TotalCoverage = percentage(Covered, NotCovered),
-
-    %% Determine the longest module name for right-padding
-    Width = lists:foldl(fun({Mod, _, _}, Acc) ->
-                                case length(atom_to_list(Mod)) of
-                                    N when N > Acc ->
-                                        N;
-                                    _ ->
-                                        Acc
-                                end
-                        end, 0, Coverage) * -1,
-
-    %% Print the output the console
-    ?CONSOLE("~nCode Coverage:~n", []),
-    lists:foreach(fun({Mod, C, N}) ->
-                          ?CONSOLE("~*s : ~3s~n",
-                                   [Width, Mod, percentage(C, N)])
-                  end, Coverage),
-    ?CONSOLE("~n~*s : ~s~n", [Width, "Total", TotalCoverage]).
-
-cover_file(Module) ->
-    filename:join([?EUNIT_DIR, atom_to_list(Module) ++ ".COVER.html"]).
-
-cover_export_coverdata() ->
-    ExportFile = filename:join(eunit_dir(), "eunit.coverdata"),
-    case cover:export(ExportFile) of
-        ok ->
-            ?CONSOLE("Coverdata export: ~s~n", [ExportFile]);
-        {error, Reason} ->
-            ?ERROR("Coverdata export failed: ~p~n", [Reason])
-    end.
-
-percentage(0, 0) ->
-    "not executed";
-percentage(Cov, NotCov) ->
-    integer_to_list(trunc((Cov / (Cov + NotCov)) * 100)) ++ "%".
-
-%%
 %% == reset_after_eunit ==
 %%
 
diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl
index fcd9c5e..9ddbf27 100644
--- a/src/rebar_file_utils.erl
+++ b/src/rebar_file_utils.erl
@@ -46,7 +46,7 @@
         {unix, _} ->
             EscTarget = escape_spaces(Target),
             {ok, []} = rebar_utils:sh(?FMT("rm -rf ~s", [EscTarget]),
-                                      [{use_stdout, false}, return_on_error]),
+                                      [{use_stdout, false}, abort_on_error]),
             ok;
         {win32, _} ->
             Filelist = filelib:wildcard(Target),
@@ -67,7 +67,7 @@
             SourceStr = string:join(EscSources, " "),
             {ok, []} = rebar_utils:sh(?FMT("cp -R ~s \"~s\"",
                                            [SourceStr, Dest]),
-                                      [{use_stdout, false}, return_on_error]),
+                                      [{use_stdout, false}, abort_on_error]),
             ok;
         {win32, _} ->
             lists:foreach(fun(Src) -> ok = cp_r_win32(Src,Dest) end, Sources),
@@ -81,7 +81,7 @@
             EscSource = escape_spaces(Source),
             EscDest = escape_spaces(Dest),
             {ok, []} = rebar_utils:sh(?FMT("mv ~s ~s", [EscSource, EscDest]),
-                                      [{use_stdout, false}, return_on_error]),
+                                      [{use_stdout, false}, abort_on_error]),
             ok;
         {win32, _} ->
             {ok, R} = rebar_utils:sh(
diff --git a/src/rebar_qc.erl b/src/rebar_qc.erl
index 99d37a2..e08833b 100644
--- a/src/rebar_qc.erl
+++ b/src/rebar_qc.erl
@@ -4,7 +4,7 @@
 %%
 %% rebar: Erlang Build Tools
 %%
-%% Copyright (c) 2011-2012 Tuncer Ayaz
+%% Copyright (c) 2011-2014 Tuncer Ayaz
 %%
 %% Permission is hereby granted, free of charge, to any person obtaining a copy
 %% of this software and associated documentation files (the "Software"), to deal
@@ -68,12 +68,21 @@
        "  {qc_opts, [{qc_mod, module()}, Options]}~n"
        "  ~p~n"
        "  ~p~n"
+       "  ~p~n"
+       "  ~p~n"
+       "  ~p~n"
        "Valid command line options:~n"
        "  compile_only=true (Compile but do not test properties)",
        [
         {qc_compile_opts, []},
-        {qc_first_files, []}
-       ]).
+        {qc_first_files, []},
+        {cover_enabled, false},
+        {cover_print_enabled, false},
+        {cover_export_enabled, false}
+       ]);
+info(help, clean) ->
+    Description = ?FMT("Delete QuickCheck test dir (~s)", [?QC_DIR]),
+    ?CONSOLE("~s.~n", [Description]).
 
 -define(TRIQ_MOD, triq).
 -define(EQC_MOD, eqc).
@@ -148,21 +157,40 @@
     %% Compile erlang code to ?QC_DIR, using a tweaked config
     %% with appropriate defines, and include all the test modules
     %% as well.
-    {ok, _SrcErls} = rebar_erlc_compiler:test_compile(Config, "qc", ?QC_DIR),
+    {ok, SrcErls} = rebar_erlc_compiler:test_compile(Config, "qc", ?QC_DIR),
 
     case CompileOnly of
         "true" ->
             true = code:set_path(CodePath),
             ?CONSOLE("Compiled modules for qc~n", []);
         false ->
-            run1(QC, QCOpts, CodePath)
+            run1(QC, QCOpts, Config, CodePath, SrcErls)
     end.
 
-run1(QC, QCOpts, CodePath) ->
+run1(QC, QCOpts, Config, CodePath, SrcErls) ->
+
+    AllBeamFiles = rebar_utils:beams(?QC_DIR),
+    AllModules = [rebar_utils:beam_to_mod(?QC_DIR, N)
+                  || N <- AllBeamFiles],
+    PropMods = find_prop_mods(),
+    FilteredModules = AllModules -- PropMods,
+
+    SrcModules = [rebar_utils:erl_to_mod(M) || M <- SrcErls],
+
+    {ok, CoverLog} = rebar_cover_utils:init(Config, AllBeamFiles, qc_dir()),
+
     TestModule = fun(M) -> qc_module(QC, QCOpts, M) end,
-    case lists:flatmap(TestModule, find_prop_mods()) of
+    QCResult = lists:flatmap(TestModule, PropMods),
+
+    rebar_cover_utils:perform_cover(Config, FilteredModules, SrcModules,
+                                    qc_dir()),
+    rebar_cover_utils:close(CoverLog),
+    ok = rebar_cover_utils:exit(),
+
+    true = code:set_path(CodePath),
+
+    case QCResult of
         [] ->
-            true = code:set_path(CodePath),
             ok;
         Errors ->
             ?ABORT("One or more QC properties didn't hold true:~n~p~n",
diff --git a/src/rebar_rel_utils.erl b/src/rebar_rel_utils.erl
index 085dbd9..5d99948 100644
--- a/src/rebar_rel_utils.erl
+++ b/src/rebar_rel_utils.erl
@@ -37,6 +37,7 @@
          get_rel_file_path/2,
          load_config/2,
          get_sys_tuple/1,
+         get_excl_lib_tuple/1,
          get_target_dir/2,
          get_root_dir/2,
          get_target_parent_dir/2]).
@@ -144,6 +145,13 @@
     end.
 
 %%
+%% Look for the {excl_lib, ...} tuple in sys tuple of the reltool.config file.
+%% Without this present, return false.
+%%
+get_excl_lib_tuple(ReltoolConfig) ->
+    lists:keyfind(excl_lib, 1, element(2, get_sys_tuple(ReltoolConfig))).
+
+%%
 %% Look for {target_dir, TargetDir} in the reltool config file; if none is
 %% found, use the name of the release as the default target directory.
 %%
diff --git a/src/rebar_reltool.erl b/src/rebar_reltool.erl
index 9f9488e..fdaa7e0 100644
--- a/src/rebar_reltool.erl
+++ b/src/rebar_reltool.erl
@@ -147,15 +147,12 @@
                                                     OverlayVars1),
 
     %% Finally, overlay the files specified by the overlay section
-    case lists:keyfind(overlay, 1, ReltoolConfig) of
-        {overlay, Overlay} when is_list(Overlay) ->
+    case overlay_files(ReltoolConfig) of
+        [] ->
+            ok;
+        Overlay ->
             execute_overlay(Overlay, OverlayVars, rebar_utils:get_cwd(),
-                            TargetDir);
-        false ->
-            ?INFO("No {overlay, [...]} found in reltool.config.\n", []);
-        _ ->
-            ?ABORT("{overlay, [...]} entry in reltool.config "
-                   "must be a list.\n", [])
+                            TargetDir)
     end.
 
 %%
@@ -292,6 +289,26 @@
     end.
 
 
+overlay_files(ReltoolConfig) ->
+    Original = case lists:keyfind(overlay, 1, ReltoolConfig) of
+                   {overlay, Overlay} when is_list(Overlay) ->
+                       Overlay;
+                   false ->
+                       ?INFO("No {overlay, [...]} found in reltool.config.\n", []),
+                       [];
+                   _ ->
+                       ?ABORT("{overlay, [...]} entry in reltool.config "
+                              "must be a list.\n", [])
+               end,
+    SlimAddition = case rebar_rel_utils:get_excl_lib_tuple(ReltoolConfig) of
+                       {excl_lib, otp_root} ->
+                           [{create, "releases/{{rel_vsn}}/runner_script.data",
+                             "slim\n"}];
+                       false ->
+                           []
+                   end,
+    Original ++ SlimAddition.
+
 %% TODO: Merge functionality here with rebar_templater
 
 execute_overlay([], _Vars, _BaseDir, _TargetDir) ->
diff --git a/src/rebar_shell.erl b/src/rebar_shell.erl
index 2dbf4a0..348e540 100644
--- a/src/rebar_shell.erl
+++ b/src/rebar_shell.erl
@@ -30,27 +30,40 @@
 
 -include("rebar.hrl").
 
--export([shell/2]).
+-export([shell/2, info/2]).
+
+%% NOTE:
+%% this is an attempt to replicate `erl -pa ./ebin -pa deps/*/ebin`. it is
+%% mostly successful but does stop and then restart the user io system to get
+%% around issues with rebar being an escript and starting in `noshell` mode.
+%% it also lacks the ctrl-c interrupt handler that `erl` features. ctrl-c will
+%% immediately kill the script. ctrl-g, however, works fine
 
 shell(_Config, _AppFile) ->
-    ?CONSOLE("NOTICE: Using experimental 'shell' command~n", []),
-    %% backwards way to say we only want this executed
-    %% for the "top level" directory
-    case is_deps_dir(rebar_utils:get_cwd()) of
-        false ->
-            true = code:add_pathz(rebar_utils:ebin_dir()),
-            user_drv:start(),
-            %% this call never returns (until user quits shell)
-            shell:server(false, false);
-        true ->
-            ok
-    end,
-    ok.
+    true = code:add_pathz(rebar_utils:ebin_dir()),
+    %% terminate the current user
+    ok = supervisor:terminate_child(kernel_sup, user),
+    %% start a new shell (this also starts a new user under the correct group)
+    user_drv:start(),
+    %% enable error_logger's tty output
+    ok = error_logger:swap_handler(tty),
+    %% disable the simple error_logger (which may have been added multiple
+    %% times). removes at most the error_logger added by init and the
+    %% error_logger added by the tty handler
+    ok = remove_error_handler(3),
+    %% this call never returns (until user quits shell)
+    timer:sleep(infinity).
 
-is_deps_dir(Dir) ->
-    case lists:reverse(filename:split(Dir)) of
-        [_, "deps" | _] ->
-            true;
-        _V ->
-            false
-    end.
+info(help, shell) ->
+    ?CONSOLE(
+       "Start a shell with project and deps preloaded similar to~n"
+       "'erl -pa ebin -pa deps/*/ebin'.~n",
+       []).
+
+remove_error_handler(0) ->
+    ?WARN("Unable to remove simple error_logger handler~n", []);
+remove_error_handler(N) ->
+    case gen_event:delete_handler(error_logger, error_logger, []) of
+        {error, module_not_found} -> ok;
+        {error_logger, _} -> remove_error_handler(N-1)
+    end.
\ No newline at end of file
diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl
index fef4627..4abf404 100644
--- a/src/rebar_templater.erl
+++ b/src/rebar_templater.erl
@@ -397,6 +397,18 @@
     execute_template(Files, prepend_instructions(Instructions, Rest),
                      TemplateType, TemplateName, Context, Force,
                      ExistingFiles);
+execute_template(Files, [{'case', Variable, Values, Instructions} | Rest], TemplateType,
+                 TemplateName, Context, Force, ExistingFiles) ->
+    {ok, Value} = dict:find(Variable, Context),
+    Instructions2 = case lists:member(Value, Values) of
+                       true ->
+                           Instructions;
+                       _ ->
+                           []
+                   end,
+    execute_template(Files, prepend_instructions(Instructions2, Rest),
+                     TemplateType, TemplateName, Context, Force,
+                     ExistingFiles);
 execute_template(Files, [{template, Input, Output} | Rest], TemplateType,
                  TemplateName, Context, Force, ExistingFiles) ->
     InputName = filename:join(filename:dirname(TemplateName), Input),
diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl
index 2d227b6..fa35fed 100644
--- a/src/rebar_utils.erl
+++ b/src/rebar_utils.erl
@@ -31,6 +31,7 @@
          get_arch/0,
          wordsize/0,
          sh/2,
+         sh_send/3,
          find_files/2, find_files/3,
          now_str/0,
          ensure_dir/1,
@@ -53,7 +54,8 @@
          src_dirs/1,
          ebin_dir/0,
          base_dir/1,
-         processing_base_dir/1, processing_base_dir/2]).
+         processing_base_dir/1, processing_base_dir/2,
+         patch_env/2]).
 
 -include("rebar.hrl").
 
@@ -87,6 +89,24 @@
             integer_to_list(8 * erlang:system_info(wordsize))
     end.
 
+sh_send(Command0, String, Options0) ->
+    ?INFO("sh_send info:\n\tcwd: ~p\n\tcmd: ~s < ~s\n", [get_cwd(), Command0, String]),
+    ?DEBUG("\topts: ~p\n", [Options0]),
+
+    DefaultOptions = [use_stdout, abort_on_error],
+    Options = [expand_sh_flag(V)
+               || V <- proplists:compact(Options0 ++ DefaultOptions)],
+
+    Command = patch_on_windows(Command0, proplists:get_value(env, Options, [])),
+    PortSettings = proplists:get_all_values(port_settings, Options) ++
+        [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide],
+    Port = open_port({spawn, Command}, PortSettings),
+
+    %% allow us to send some data to the shell command's STDIN
+    %% Erlang doesn't let us get any reply after sending an EOF, though...
+    Port ! {self(), {command, String}},
+    port_close(Port).
+
 %%
 %% Options = [Option] -- defaults to [use_stdout, abort_on_error]
 %% Option = ErrorOption | OutputOption | {cd, string()} | {env, Env}
@@ -316,7 +336,25 @@
     processing_base_dir(Config, Cwd).
 
 processing_base_dir(Config, Dir) ->
-    Dir =:= base_dir(Config).
+    AbsDir = filename:absname(Dir),
+    AbsDir =:= base_dir(Config).
+
+%% @doc Returns the list of environment variables including 'REBAR' which points to the
+%% rebar executable used to execute the currently running command. The environment is
+%% not modified if rebar was invoked programmatically.
+-spec patch_env(rebar_config:config(), [{string(), string()}]) -> [{string(), string()}].
+patch_env(Config, []) ->
+    % if we reached an empty list the env did not contain the REBAR variable
+    case rebar_config:get_xconf(Config, escript, "") of
+        "" -> % rebar was invoked programmatically
+            [];
+        Path ->
+            [{"REBAR", Path}]
+    end;
+patch_env(_Config, [{"REBAR", _} | _]=All) ->
+    All;
+patch_env(Config, [E | Rest]) ->
+    [E | patch_env(Config, Rest)].
 
 %% ====================================================================
 %% Internal functions
@@ -398,8 +436,9 @@
 
 -spec log_and_abort(string(), {integer(), string()}) -> no_return().
 log_and_abort(Command, {Rc, Output}) ->
-    ?ABORT("~s failed with error: ~w and output:~n~s~n",
-           [Command, Rc, Output]).
+    ?ABORT("sh(~s)~n"
+           "failed with return code ~w and the following output:~n"
+           "~s~n", [Command, Rc, Output]).
 
 sh_loop(Port, Fun, Acc) ->
     receive
@@ -478,6 +517,7 @@
     end.
 
 vcs_vsn_cmd(git)    -> "git describe --always --tags";
+vcs_vsn_cmd(p4)     -> "echo #head";
 vcs_vsn_cmd(hg)     -> "hg identify -i";
 vcs_vsn_cmd(bzr)    -> "bzr revno";
 vcs_vsn_cmd(svn)    -> "svnversion";
diff --git a/test/rebar_eunit_tests.erl b/test/rebar_eunit_tests.erl
index 61a9bbf..bb64507 100644
--- a/test/rebar_eunit_tests.erl
+++ b/test/rebar_eunit_tests.erl
@@ -191,6 +191,46 @@
 
                {"Selected suite tests is run once",
                 ?_assert(string:str(RebarOut, "All 2 tests passed") =/= 0)}]
+      end},
+     {"Ensure EUnit runs a specific test by qualified function name",
+      setup,
+      fun() ->
+              setup_project_with_multiple_modules(),
+              rebar("-v eunit tests=myapp_mymod:myprivate_test")
+      end,
+      fun teardown/1,
+      fun(RebarOut) ->
+              [{"Selected test is run",
+                [?_assert(string:str(RebarOut,
+                                     "myapp_mymod:myprivate_test/0")
+                          =/= 0)]},
+
+               {"Only selected test is run",
+                [?_assert(string:str(RebarOut,
+                                     "Test passed.") =/= 0)]}]
+      end},
+     {"Ensure EUnit runs a specific test by qualified function "
+      ++ "name and tests from other module",
+      setup,
+      fun() ->
+              setup_project_with_multiple_modules(),
+              rebar("-v eunit suites=myapp_mymod3 "
+                    ++ "tests=myapp_mymod:myprivate_test")
+      end,
+      fun teardown/1,
+      fun(RebarOut) ->
+              [{"Selected test is run",
+                [?_assert(string:str(RebarOut,
+                                     "myapp_mymod:myprivate_test/0") =/= 0)]},
+
+               {"Tests from module are run",
+                [?_assert(string:str(RebarOut,
+                                     "myapp_mymod3:") =/= 0)]},
+
+               {"Only selected tests are run",
+                [?_assert(string:str(RebarOut,
+                                     "Failed: 1.  Skipped: 0.  Passed: 1")
+                          =/= 0)]}]
       end}].
 
 cover_test_() ->
diff --git a/test/rebar_file_utils_tests.erl b/test/rebar_file_utils_tests.erl
index 26a6f9f..a191765 100644
--- a/test/rebar_file_utils_tests.erl
+++ b/test/rebar_file_utils_tests.erl
@@ -191,7 +191,7 @@
                     filename:join([?TMP_DIR,"dest","file1"]),0)
      end,
      fun teardown/1,
-     [?_assertError({badmatch,_},
+     [?_assertThrow(rebar_abort,
                     rebar_file_utils:cp_r(
                       [filename:join([?TMP_DIR,"source","file1"])],
                       filename:join([?TMP_DIR,"dest"])))]}.
@@ -210,7 +210,7 @@
                     filename:join([?TMP_DIR,"dest","source","file1"]),0)
      end,
      fun teardown/1,
-     [?_assertError({badmatch,_},
+     [?_assertThrow(rebar_abort,
                     rebar_file_utils:cp_r(
                       [filename:join([?TMP_DIR,"source"])],
                       filename:join([?TMP_DIR,"dest"])))]}.