summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore17
-rw-r--r--.travis.yml1
-rw-r--r--CONTRIBUTING.md31
-rw-r--r--RELEASE-NOTES.md89
-rw-r--r--THANKS5
-rwxr-xr-xbootstrap16
-rw-r--r--dialyzer_reference4
-rw-r--r--ebin/rebar.app5
-rw-r--r--inttest/code_path_no_recurse/code_path_no_recurse_rt.erl19
-rw-r--r--inttest/code_path_no_recurse/deps/bazdep/src/bazdep.app.src12
-rw-r--r--inttest/code_path_no_recurse/deps/bazdep/src/bazdep.erl6
-rw-r--r--inttest/code_path_no_recurse/deps/bazdep/test/bazdep_tests.erl5
-rw-r--r--inttest/code_path_no_recurse/deps/foodep/rebar.config1
-rw-r--r--inttest/code_path_no_recurse/deps/foodep/src/foodep.app.src12
-rw-r--r--inttest/code_path_no_recurse/deps/foodep/src/foodep.erl6
-rw-r--r--inttest/code_path_no_recurse/deps/foodep/test/foodep_tests.erl5
-rw-r--r--inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.app.src12
-rw-r--r--inttest/code_path_no_recurse/deps/unuseddep/src/unuseddep.erl6
-rw-r--r--inttest/code_path_no_recurse/rebar.config1
-rw-r--r--inttest/code_path_no_recurse/src/codepath.app.src12
-rw-r--r--inttest/code_path_no_recurse/src/codepath.erl6
-rw-r--r--inttest/code_path_no_recurse/test/codepath_tests.erl12
-rw-r--r--inttest/erlc/asn1/SIMPLE-ASN.asn17
-rw-r--r--inttest/erlc/erlc_rt.erl137
-rw-r--r--inttest/erlc/extra-include/foo_extra.hrl3
-rw-r--r--inttest/erlc/extra-src/foo_sup.erl15
-rw-r--r--inttest/erlc/foobar.erl8
-rw-r--r--inttest/erlc/include/foo_core.hrl3
-rw-r--r--inttest/erlc/mibs/SIMPLE-MIB.mib26
-rw-r--r--inttest/erlc/rebar-no_debug_info.config11
-rw-r--r--inttest/erlc/rebar.config13
-rw-r--r--inttest/erlc/src/behaviour/foo_worker.erl14
-rw-r--r--inttest/erlc/src/first_erl.erl10
-rw-r--r--inttest/erlc/src/first_xrl.xrl13
-rw-r--r--inttest/erlc/src/first_yrl.yrl9
-rw-r--r--inttest/erlc/src/foo.erl35
-rw-r--r--inttest/erlc/src/foo_app.erl10
-rw-r--r--inttest/erlc/src/foo_test_worker.erl34
-rw-r--r--inttest/eunit/eunit_rt.erl48
-rw-r--r--inttest/eunit/eunit_src/bar.erl6
-rw-r--r--inttest/eunit/rebar-eunit_compile_opts.config1
-rw-r--r--inttest/eunit/src/foo.erl10
-rw-r--r--priv/templates/simpleevent.erl60
-rw-r--r--priv/templates/simpleevent.template2
-rwxr-xr-x[-rw-r--r--]priv/templates/simplenode.install_upgrade.escript8
-rw-r--r--priv/templates/simplenode.reltool.config2
-rwxr-xr-xpriv/templates/simplenode.runner48
-rw-r--r--rebar.config6
-rw-r--r--rebar.config.sample7
-rw-r--r--src/rebar.erl14
-rw-r--r--src/rebar.hrl (renamed from include/rebar.hrl)0
-rw-r--r--src/rebar_config.erl16
-rw-r--r--src/rebar_core.erl191
-rw-r--r--src/rebar_cover_utils.erl261
-rw-r--r--src/rebar_ct.erl43
-rw-r--r--src/rebar_deps.erl50
-rw-r--r--src/rebar_erlc_compiler.erl74
-rw-r--r--src/rebar_eunit.erl393
-rw-r--r--src/rebar_file_utils.erl6
-rw-r--r--src/rebar_qc.erl44
-rw-r--r--src/rebar_rel_utils.erl8
-rw-r--r--src/rebar_reltool.erl33
-rw-r--r--src/rebar_shell.erl55
-rw-r--r--src/rebar_templater.erl12
-rw-r--r--src/rebar_utils.erl48
-rw-r--r--test/rebar_eunit_tests.erl40
-rw-r--r--test/rebar_file_utils_tests.erl4
67 files changed, 1599 insertions, 522 deletions
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 @@ otp_release:
- 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 @@ Do not commit to master in your fork.
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 @@ Sylvain Benner
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 @@ main(Args) ->
%% 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 @@ main(Args) ->
"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
index 56cea19..0d4cbe3 100644..100755
--- 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 @@ case "$REMSH_NAME" in
*)
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 @@ cd $USE_DIR
# Make sure log directory exists
mkdir -p $USE_DIR/log
-# Add ERTS bin dir to our path
-ERTS_PATH=$RUNNER_BASE_DIR/erts-$ERTS_VSN/bin
+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 @@ case "$1" in
;;
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 @@ case "$1" in
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 @@ help() ->
" ~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 @@ qc Test QuickCheck properties
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
index b19fdd3..b19fdd3 100644
--- a/include/rebar.hrl
+++ b/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 @@ process_commands([Command | Rest], ParentConfig) ->
%% 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 @@ process_commands([Command | Rest], ParentConfig) ->
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 @@ maybe_process_dir(Dir, ParentConfig, Command, DirSet) ->
%% 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);
+ skip_or_process_dir(Dir, Command, Config, DirSet, CurrentCodePath,
+ ModuleSet, WouldCd)
+ end.
+
+would_cd_into_dir(Dir, Command, Config) ->
+ case would_cd_into_dir1(Dir, Command, Config) of
+ true ->
+ would_cd;
false ->
- {ParentConfig, DirSet}
+ would_not_cd
end.
-should_cd_into_dir(Dir, Config, Command) ->
+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 @@ process_dir1(Dir, Command, DirSet, Config, CurrentCodePath,
%% 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 @@ process_dir1(Dir, Command, DirSet, Config, CurrentCodePath,
%% 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 @@ process_dir1(Dir, Command, DirSet, Config, CurrentCodePath,
%% 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 @@ maybe_load_local_config(Dir, ParentConfig) ->
%% 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_post(Command, Modules, Config, ModuleFile, Env) ->
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 @@ run_modules([Module | Rest], Command, Config, File) ->
{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 @@ make_cmd(TestDir, RawLogDir, Config) ->
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 @@ build_name(Config) ->
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 @@ get_cover_config(Config, Cwd) ->
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_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.
+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, []).
+
+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 @@ info_help(Description) ->
{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, Count) ->
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 @@ update_source(Config, Dep) ->
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(Source) ->
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 @@ vcs_client_vsn(Path, VsnArg, VsnRegex) ->
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(svn) -> {1, 6};
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(fossil) ->
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 @@ has_vcs_dir(_, _) ->
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 @@ test_compile(Config, Cmd, OutDir) ->
%% 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 @@ test_compile(Config, Cmd, OutDir) ->
%% 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 @@ info_help(Description) ->
"(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 @@ info_help(Description) ->
{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) ->
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 @@ check_erlcinfo(Config, _) ->
?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 @@ init_erlcinfo(Config, Erls) ->
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 @@ expand_file_names(Files, Dirs) ->
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 @@ run_eunit(Config, CodePath, SrcErls) ->
[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 @@ run_eunit(Config, CodePath, SrcErls) ->
%% 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 @@ setup_code_path() ->
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 @@ get_suites(Config) ->
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 @@ randomize_suites1(Modules, Seed) ->
%%
%% == 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 @@ get_eunit_opts(Config) ->
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 @@ rm_rf(Target) ->
{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 @@ cp_r(Sources, Dest) ->
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 @@ mv(Source, Dest) ->
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 @@ info(help, qc) ->
" {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 @@ run(Config, QC, QCOpts) ->
%% 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 @@ get_sys_tuple(ReltoolConfig) ->
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 @@ process_overlay(Config, ReltoolConfig) ->
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 @@ dump_spec(Config, Spec) ->
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).
+
+info(help, shell) ->
+ ?CONSOLE(
+ "Start a shell with project and deps preloaded similar to~n"
+ "'erl -pa ebin -pa deps/*/ebin'.~n",
+ []).
-is_deps_dir(Dir) ->
- case lists:reverse(filename:split(Dir)) of
- [_, "deps" | _] ->
- true;
- _V ->
- false
- end.
+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, [{'if', Cond, True, False} | Rest], TemplateType,
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 @@ wordsize() ->
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) ->
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 @@ log_msg_and_abort(Message) ->
-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 @@ vcs_vsn_1(Vcs, Dir) ->
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 @@ eunit_with_suites_and_tests_test_() ->
{"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 @@ cp_r_overwrite_file_fail_test_() ->
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 @@ cp_r_overwrite_dir_fail_test_() ->
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"])))]}.