summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.travis.yml2
-rw-r--r--CONTRIBUTING.md4
-rw-r--r--README.md11
-rw-r--r--THANKS3
-rwxr-xr-xbootstrap4
-rw-r--r--priv/shell-completion/bash/rebar33
-rw-r--r--priv/shell-completion/fish/rebar3.fish9
-rw-r--r--priv/shell-completion/zsh/_rebar39
-rw-r--r--priv/templates/app.erl3
-rw-r--r--rebar.config61
-rw-r--r--rebar.config.sample2
-rw-r--r--rebar.lock8
-rw-r--r--src/rebar.app.src1
-rw-r--r--src/rebar.hrl2
-rw-r--r--src/rebar3.erl25
-rw-r--r--src/rebar_agent.erl17
-rw-r--r--src/rebar_app_info.erl8
-rw-r--r--src/rebar_config.erl38
-rw-r--r--src/rebar_dir.erl1
-rw-r--r--src/rebar_dist_utils.erl89
-rw-r--r--src/rebar_git_resource.erl2
-rw-r--r--src/rebar_hooks.erl7
-rw-r--r--src/rebar_log.erl46
-rw-r--r--src/rebar_packages.erl79
-rw-r--r--src/rebar_plugins.erl13
-rw-r--r--src/rebar_prv_common_test.erl106
-rw-r--r--src/rebar_prv_dialyzer.erl2
-rw-r--r--src/rebar_prv_edoc.erl8
-rw-r--r--src/rebar_prv_eunit.erl61
-rw-r--r--src/rebar_prv_install_deps.erl8
-rw-r--r--src/rebar_prv_lock.erl3
-rw-r--r--src/rebar_prv_packages.erl32
-rw-r--r--src/rebar_prv_path.erl8
-rw-r--r--src/rebar_prv_plugins_upgrade.erl15
-rw-r--r--src/rebar_prv_report.erl2
-rw-r--r--src/rebar_prv_shell.erl60
-rw-r--r--src/rebar_prv_state.erl44
-rw-r--r--src/rebar_prv_unlock.erl9
-rw-r--r--src/rebar_prv_update.erl122
-rw-r--r--src/rebar_prv_upgrade.erl2
-rw-r--r--src/rebar_relx.erl13
-rw-r--r--src/rebar_state.erl47
-rw-r--r--test/mock_git_resource.erl11
-rw-r--r--test/rebar_ct_SUITE.erl90
-rw-r--r--test/rebar_deps_SUITE.erl112
-rw-r--r--test/rebar_dir_SUITE.erl36
-rw-r--r--test/rebar_dist_utils_SUITE.erl74
-rw-r--r--test/rebar_eunit_SUITE.erl43
-rw-r--r--test/rebar_hooks_SUITE.erl22
-rw-r--r--test/rebar_lock_SUITE.erl46
-rw-r--r--test/rebar_pkg_SUITE.erl9
-rw-r--r--test/rebar_plugins_SUITE.erl93
-rw-r--r--test/rebar_test_utils.erl29
53 files changed, 1279 insertions, 275 deletions
diff --git a/.travis.yml b/.travis.yml
index 630beb2..ab2d28d 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -17,7 +17,7 @@ cache:
before_deploy: "rm -rf !(rebar3)"
deploy:
on:
- branch: master
+ tags: true
condition: $TRAVIS_OTP_RELEASE = R16B03-1
provider: s3
access_key_id: AKIAJAPYAQEFYCYSNL7Q
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index f175cc2..8077813 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -23,7 +23,7 @@ All files without specific headers can safely be assumed to be under Apache
## Submitting a Bug
-Bugs can be submitted to the [Github issue page](https://github.com/rebar/rebar3/issues).
+Bugs can be submitted to the [Github issue page](https://github.com/erlang/rebar3/issues).
Rebar3 is not perfect software and will be buggy. When submitting a bug, be
careful to know the following:
@@ -58,7 +58,7 @@ Before requesting or implementing a new feature, please do the following:
- Take a look at our [list of plugins](http://www.rebar3.org/docs/using-available-plugins)
to know if the feature isn't already supported by the community.
-- Verify in existing [tickets](https://github.com/rebar/rebar3/issues) whether
+- Verify in existing [tickets](https://github.com/erlang/rebar3/issues) whether
the feature might already is in the works, has been moved to a plugin, or
has already been rejected.
diff --git a/README.md b/README.md
index 0fa486a..7560ab0 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Rebar3
-[![Build Status](https://travis-ci.org/rebar/rebar3.svg?branch=master)](https://travis-ci.org/rebar/rebar3) [![Windows build status](https://ci.appveyor.com/api/projects/status/yx4oitd9pvd2kab3?svg=true)](https://ci.appveyor.com/project/TristanSloughter/rebar3)
+[![Build Status](https://travis-ci.org/erlang/rebar3.svg?branch=master)](https://travis-ci.org/erlang/rebar3) [![Windows build status](https://ci.appveyor.com/api/projects/status/yx4oitd9pvd2kab3?svg=true)](https://ci.appveyor.com/project/TristanSloughter/rebar3)
1. [What is Rebar3?](#what-is-rebar3)
2. [Why Rebar3?](#why-rebar3)
@@ -64,12 +64,12 @@ $ wget https://s3.amazonaws.com/rebar3/rebar3 && chmod +x rebar3
From Source (assuming you have a full Erlang install):
```bash
-$ git clone https://github.com/rebar/rebar3.git
+$ git clone https://github.com/erlang/rebar3.git
$ cd rebar3
$ ./bootstrap
```
-Stable versions can be obtained from the [releases page](https://github.com/rebar/rebar3/releases).
+Stable versions can be obtained from the [releases page](https://github.com/erlang/rebar3/releases).
The rebar3 escript can also extract itself with a run script under the user's home directory:
@@ -159,13 +159,14 @@ quick feedback, you can try the #rebar channel on
asking about things with well known answers.
For bug reports, roadmaps, and issues, visit the [github issues
-page](https://github.com/rebar/rebar3/issues).
+page](https://github.com/erlang/rebar3/issues).
General rebar community resources and links:
- [Rebar Mailing List](http://lists.basho.com/pipermail/rebar_lists.basho.com/)
- #rebar on [irc.freenode.net](http://freenode.net/)
-- [issues](https://github.com/rebar/rebar3/issues)
+- [issues](https://github.com/erlang/rebar3/issues)
- [Documentation](http://www.rebar3.org/v3.0/docs)
To contribute to rebar3, please refer to [CONTRIBUTING](CONTRIBUTING.md).
+
diff --git a/THANKS b/THANKS
index cf59ef1..c5f7522 100644
--- a/THANKS
+++ b/THANKS
@@ -135,4 +135,5 @@ Pierre Fenoll
David Kubecka
Stefan Grundmann
Carlos Eduardo de Paula
-Derek Brown \ No newline at end of file
+Derek Brown
+Heinz N. Gies
diff --git a/bootstrap b/bootstrap
index b20d0d3..e2a393b 100755
--- a/bootstrap
+++ b/bootstrap
@@ -24,7 +24,7 @@ main(_) ->
bootstrap_rebar3(),
%% Build rebar.app from rebar.app.src
- {ok, App} = rebar_app_info:new(rebar, "3.0.0", filename:absname("_build/default/lib/rebar/")),
+ {ok, App} = rebar_app_info:new(rebar, "3.1.0", filename:absname("_build/default/lib/rebar/")),
rebar_otp_app:compile(rebar_state:new(), App),
%% Because we are compiling files that are loaded already we want to silence
@@ -92,7 +92,7 @@ fetch({pkg, Name, Vsn}, App) ->
Dir = filename:join([filename:absname("_build/default/lib/"), App]),
case filelib:is_dir(Dir) of
false ->
- CDN = "https://s3.amazonaws.com/s3.hex.pm/tarballs",
+ CDN = "https://repo.hex.pm/tarballs",
Package = binary_to_list(<<Name/binary, "-", Vsn/binary, ".tar">>),
Url = string:join([CDN, Package], "/"),
case request(Url) of
diff --git a/priv/shell-completion/bash/rebar3 b/priv/shell-completion/bash/rebar3
index 3cd3cd7..be9af44 100644
--- a/priv/shell-completion/bash/rebar3
+++ b/priv/shell-completion/bash/rebar3
@@ -77,6 +77,7 @@ _rebar3()
--multiply_timetraps \
--scale_timetraps \
--create_priv_dir \
+ --include \
--verbose \
--auto_compile \
"
@@ -92,7 +93,7 @@ _rebar3()
elif [[ ${prev} == escriptize ]] ; then
:
elif [[ ${prev} == eunit ]] ; then
- sopts="-c -e -v"
+ sopts="-c -e -v -d -f -m -s"
lopts="--app --application --cover --dir --error_on_warning --file --module --suite --verbose"
elif [[ ${prev} == help ]] ; then
:
diff --git a/priv/shell-completion/fish/rebar3.fish b/priv/shell-completion/fish/rebar3.fish
index 7b63e20..fd28c97 100644
--- a/priv/shell-completion/fish/rebar3.fish
+++ b/priv/shell-completion/fish/rebar3.fish
@@ -110,6 +110,7 @@ complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l abort_if_missing_
complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l multiply_timetraps -d "Multiply timetraps."
complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l scale_timetraps -d "Scale timetraps."
complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l create_priv_dir -d "Create priv dir (auto_per_run | auto_per_tc | manual_per_tc)."
+complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l include -d "Directories containing additional include files."
complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -s v -l verbose -d "Enable verbose output. Default: false."
complete -f -c 'rebar3' -n '__fish_rebar3_using_command ct' -l auto_compile -d "Let common test compile test suites instead of rebar3."
@@ -130,11 +131,11 @@ complete -f -c 'rebar3' -n '__fish_rebar3_needs_command' -a eunit -d "Run EUnit
complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -l app -d "Comma separated list of application test suites to run. Equivalent to `[{application, App}]`"
complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -l application -d "Comma separated list of application test suites to run. Equivalent to `[{application, App}]`"
complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -s c -l cover -d "Generate cover data"
-complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -l dir -d "Comma separated list of dirs to load tests from. Equivalent to `[{dir, Dir}]`"
+complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -s d -l dir -d "Comma separated list of dirs to load tests from. Equivalent to `[{dir, Dir}]`"
complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunut' -s e -l error_on_warning -d "Error on invalid test specifications instead of warning"
-complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -l file -d "Comma separated list of files to load tests from. Equivalent to `[{file, File}]`"
-complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -l module -d "Comma separated list of modules to load tests from. Equivalent to `[{module, Module}]`"
-complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -l suite -d "Comma separated list of modules to load tests from. Equivalent to `[{module, Module}]`"
+complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -s f -l file -d "Comma separated list of files to load tests from. Equivalent to `[{file, File}]`"
+complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -s m -l module -d "Comma separated list of modules to load tests from. Equivalent to `[{module, Module}]`"
+complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -s s -l suite -d "Comma separated list of modules to load tests from. Equivalent to `[{module, Module}]`"
complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -s v -l verbose -d "Verbose output"
complete -f -c 'rebar3' -n '__fish_rebar3_using_command eunit' -l suite -d "Lists of test suites to run"
diff --git a/priv/shell-completion/zsh/_rebar3 b/priv/shell-completion/zsh/_rebar3
index f0fb351..0abc340 100644
--- a/priv/shell-completion/zsh/_rebar3
+++ b/priv/shell-completion/zsh/_rebar3
@@ -62,6 +62,7 @@ _rebar3 () {
'(--multiply_timetraps)--multiply_timetraps' \
'(--scale_timetraps)--scale_timetraps' \
'(--create_priv_dir)--create_priv_dir' \
+ '(--include)--include[Directories containing additional include files]:includes' \
'(-v --verbose)'{-v,--verbose}'[Print coverage analysis]' \
'(--auto_compile)--auto_compile' \
&& ret=0
@@ -89,11 +90,11 @@ _rebar3 () {
'(--app)--app[Comma separated list of application test suites to run]:suites' \
'(--application)--application[Comma separated list of application test suites to run]:applications' \
'(-c --cover)'{-c,--cover}'[Generate cover data]' \
- '(--dir)--dir[Comma separated list of dirs to load tests from]:dirs' \
+ '(-d --dir)'{-d,--dir}'[Comma separated list of dirs to load tests from]:dirs' \
'(-e --error_on_warning)'{-e,--error_on_warning}'[Error on invalid test specifications instead of warning]' \
- '(--file)--file[Comma separated list of files to load tests from]:files' \
- '(--module)--module[Comma separated list of modules to load tests from]:modules' \
- '(--suite)--suite[Comma separated list of modules to load tests from]:modules' \
+ '(-f --file)'{-f,--file}'[Comma separated list of files to load tests from]:files' \
+ '(-m --module)'{-m,--module}'[Comma separated list of modules to load tests from]:modules' \
+ '(-s --suite)'{-s,--suite}'[Comma separated list of modules to load tests from]:modules' \
'(-v --verbose)'{-v,--verbose}'[Verbose output]' \
&& ret=0
;;
diff --git a/priv/templates/app.erl b/priv/templates/app.erl
index 83eb9a3..62d2ddf 100644
--- a/priv/templates/app.erl
+++ b/priv/templates/app.erl
@@ -8,8 +8,7 @@
-behaviour(application).
%% Application callbacks
--export([start/2
- ,stop/1]).
+-export([start/2, stop/1]).
%%====================================================================
%% API
diff --git a/rebar.config b/rebar.config
index 52c2f80..5d3b310 100644
--- a/rebar.config
+++ b/rebar.config
@@ -1,15 +1,15 @@
%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 ft=erlang et
-{deps, [{erlware_commons, "0.18.0"},
+{deps, [{erlware_commons, "0.20.0"},
{ssl_verify_hostname, "1.0.5"},
- {certifi, "0.3.0"},
+ {certifi, "0.4.0"},
{providers, "1.6.0"},
{getopt, "0.8.2"},
{bbmustache, "1.0.4"},
- {relx, "3.15.0"},
+ {relx, "3.19.0"},
{cf, "0.2.1"},
- {cth_readable, "1.2.0"},
+ {cth_readable, "1.2.2"},
{eunit_formatters, "0.3.1"}]}.
{escript_name, rebar3}.
@@ -35,32 +35,33 @@
{bootstrap, []},
- {dialyze, [{erl_opts, [debug_info]}]}
+ {dialyze, [{overrides, [{add, erlware_commons, [{erl_opts, [debug_info]}]},
+ {add, ssl_verify_hostname, [{erl_opts, [debug_info]}]},
+ {add, certifi, [{erl_opts, [debug_info]}]},
+ {add, providers, [{erl_opts, [debug_info]}]},
+ {add, getopt, [{erl_opts, [debug_info]}]},
+ {add, bbmustache, [{erl_opts, [debug_info]}]},
+ {add, relx, [{erl_opts, [debug_info]}]},
+ {add, cf, [{erl_opts, [debug_info]}]},
+ {add, cth_readable, [{erl_opts, [debug_info]}]},
+ {add, eunit_formatters, [{erl_opts, [debug_info]}]}]},
+ {erl_opts, [debug_info]}]}
]}.
%% Overrides
-{overrides, [{override, erlware_commons, [
- {erl_opts, [{platform_define, "^[0-9]+", namespaced_types},
- {platform_define, "^R1[4|5]", deprecated_crypto},
- no_debug_info,
- warnings_as_errors]},
- {deps, []}, {plugins, []},
- {profiles, [{dialyze, [{erl_opts, [debug_info]}]}]}
- ]},
- {override, bbmustache, [
- {erl_opts, [no_debug_info,
- {platform_define, "^[0-9]+", namespaced_types}]},
- {deps, []}, {plugins, []},
- {profiles, [{dialyze, [{erl_opts, [debug_info]}]}]}
- ]},
- {override, getopt, [{erl_opts, [no_debug_info]},
- {profiles, [{dialyze, [{erl_opts, [debug_info]}]}]}]},
- {override, providers, [{erl_opts, [no_debug_info]},
- {profiles, [{dialyze, [{erl_opts, [debug_info]}]}]}]},
- {override, relx, [{erl_opts, [{platform_define, "^[0-9]+", namespaced_types},
- {platform_define, "^R1[4|5]", deprecated_crypto},
- no_debug_info,
- warnings_as_errors]},
- {profiles, [{dialyze, [{erl_opts, [debug_info]}]}]}
- ]}
- ]}.
+{overrides, [{override, erlware_commons, [{erl_opts, [{platform_define, "^[0-9]+", namespaced_types},
+ {platform_define, "^R1[4|5]", deprecated_crypto},
+ no_debug_info,
+ warnings_as_errors]},
+ {deps, []}, {plugins, []}]},
+ {add, ssl_verify_hostname, [{erl_opts, [no_debug_info]}]},
+ {add, certifi, [{erl_opts, [no_debug_info]}]},
+ {add, cf, [{erl_opts, [no_debug_info]}]},
+ {add, cth_readable, [{erl_opts, [no_debug_info]}]},
+ {add, eunit_formatters, [{erl_opts, [no_debug_info]}]},
+ {override, bbmustache, [{erl_opts, [no_debug_info,
+ {platform_define, "^[0-9]+", namespaced_types}]},
+ {deps, []}, {plugins, []}]},
+ {add, getopt, [{erl_opts, [no_debug_info]}]},
+ {add, providers, [{erl_opts, [no_debug_info]}]},
+ {add, relx, [{erl_opts, [no_debug_info]}]}]}.
diff --git a/rebar.config.sample b/rebar.config.sample
index de70998..f57f8dc 100644
--- a/rebar.config.sample
+++ b/rebar.config.sample
@@ -211,7 +211,7 @@
%% apps to auto-boot with `rebar3 shell'; defaults to apps
%% specified in a `relx' tuple, if any.
-{shell_apps, [app1, app2]}
+{shell, [{apps, [app1, app2]}]}.
%% == xref ==
diff --git a/rebar.lock b/rebar.lock
index fb4788b..9c6fe4a 100644
--- a/rebar.lock
+++ b/rebar.lock
@@ -1,10 +1,10 @@
[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0},
- {<<"certifi">>,{pkg,<<"certifi">>,<<"0.3.0">>},0},
+ {<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0},
{<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0},
- {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.0">>},0},
- {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.18.0">>},0},
+ {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.2">>},0},
+ {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.20.0">>},0},
{<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0},
{<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0},
{<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0},
- {<<"relx">>,{pkg,<<"relx">>,<<"3.15.0">>},0},
+ {<<"relx">>,{pkg,<<"relx">>,<<"3.19.0">>},0},
{<<"ssl_verify_hostname">>,{pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>},0}].
diff --git a/src/rebar.app.src b/src/rebar.app.src
index 58fee02..bd0f871 100644
--- a/src/rebar.app.src
+++ b/src/rebar.app.src
@@ -66,6 +66,7 @@
rebar_prv_relup,
rebar_prv_report,
rebar_prv_shell,
+ rebar_prv_state,
rebar_prv_tar,
rebar_prv_unlock,
rebar_prv_update,
diff --git a/src/rebar.hrl b/src/rebar.hrl
index f4e7f5e..0b7f0b1 100644
--- a/src/rebar.hrl
+++ b/src/rebar.hrl
@@ -22,7 +22,7 @@
-define(DEFAULT_TEST_DEPS_DIR, "test/lib").
-define(DEFAULT_RELEASE_DIR, "rel").
-define(DEFAULT_CONFIG_FILE, "rebar.config").
--define(DEFAULT_CDN, "https://s3.amazonaws.com/s3.hex.pm/").
+-define(DEFAULT_CDN, "https://repo.hex.pm/").
-define(REMOTE_PACKAGE_DIR, "tarballs").
-define(REMOTE_REGISTRY_FILE, "registry.ets.gz").
-define(LOCK_FILE, "rebar.lock").
diff --git a/src/rebar3.erl b/src/rebar3.erl
index 879378e..ff0ab6a 100644
--- a/src/rebar3.erl
+++ b/src/rebar3.erl
@@ -126,14 +126,23 @@ run_aux(State, RawArgs) ->
{ok, Providers} = application:get_env(rebar, providers),
%% Providers can modify profiles stored in opts, so set default after initializing providers
State5 = rebar_state:create_logic_providers(Providers, State4),
- State6 = rebar_plugins:top_level_install(State5),
- State7 = rebar_state:default(State6, rebar_state:opts(State6)),
+ %% Initializing project_plugins which can override default providers
+ State6 = rebar_plugins:project_plugins_install(State5),
+ State7 = rebar_plugins:top_level_install(State6),
+ State8 = case os:getenv("REBAR_CACHE_DIR") of
+ false ->
+ State7;
+ ConfigFile ->
+ rebar_state:set(State7, global_rebar_dir, ConfigFile)
+ end,
+
+ State9 = rebar_state:default(State8, rebar_state:opts(State8)),
{Task, Args} = parse_args(RawArgs),
- State8 = rebar_state:code_paths(State7, default, code:get_path()),
+ State10 = rebar_state:code_paths(State9, default, code:get_path()),
- rebar_core:init_command(rebar_state:command_args(State8, Args), Task).
+ rebar_core:init_command(rebar_state:command_args(State10, Args), Task).
init_config() ->
%% Initialize logging system
@@ -250,10 +259,10 @@ set_global_flag(State, Options, Flag) ->
%%
global_option_spec_list() ->
[
- %% {Name, ShortOpt, LongOpt, ArgSpec, HelpMsg}
- {help, $h, "help", undefined, "Print this help."},
- {version, $v, "version", undefined, "Show version information."},
- {task, undefined, undefined, string, "Task to run."}
+ %% {Name, ShortOpt, LongOpt, ArgSpec, HelpMsg}
+ {help, $h, "help", undefined, "Print this help."},
+ {version, $v, "version", undefined, "Show version information."},
+ {task, undefined, undefined, string, "Task to run."}
].
handle_error(rebar_abort) ->
diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl
index 2b69812..95818d8 100644
--- a/src/rebar_agent.erl
+++ b/src/rebar_agent.erl
@@ -86,6 +86,8 @@ refresh_paths(RState) ->
|| App <- rebar_state:project_apps(RState)]
%% make sure to never reload self; halt()s the VM
) -- [filename:dirname(code:which(?MODULE))],
+ %% Modules from apps we can't reload without breaking functionality
+ Blacklist = [ec_cmd_log, providers, cf, cth_readable],
%% Similar to rebar_utils:update_code/1, but also forces a reload
%% of used modules. Also forces to reload all of ebin/ instead
%% of just the modules in the .app file, because 'extra_src_dirs'
@@ -102,11 +104,16 @@ refresh_paths(RState) ->
undefined ->
code:add_patha(Path),
ok;
- {ok, _} ->
- ?DEBUG("reloading ~p from ~s", [Modules, Path]),
- code:replace_path(App, Path),
- [begin code:purge(M), code:delete(M), code:load_file(M) end
- || M <- Modules]
+ {ok, Mods} ->
+ case {length(Mods), length(Mods -- Blacklist)} of
+ {X,X} ->
+ ?DEBUG("reloading ~p from ~s", [Modules, Path]),
+ code:replace_path(App, Path),
+ [begin code:purge(M), code:delete(M), code:load_file(M) end
+ || M <- Modules];
+ {_,_} ->
+ ?DEBUG("skipping app ~p, stable copy required", [App])
+ end
end
end, ToRefresh).
diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl
index 9fee4e0..cf3b82e 100644
--- a/src/rebar_app_info.erl
+++ b/src/rebar_app_info.erl
@@ -165,13 +165,13 @@ update_opts(AppInfo, Opts, Config) ->
deps_from_config(Dir, Config) ->
case rebar_config:consult_lock_file(filename:join(Dir, ?LOCK_FILE)) of
- [D] ->
+ [] ->
+ [{{deps, default}, proplists:get_value(deps, Config, [])}];
+ D ->
%% We want the top level deps only from the lock file.
%% This ensures deterministic overrides for configs.
Deps = [X || X <- D, element(3, X) =:= 0],
- [{{locks, default}, D}, {{deps, default}, Deps}];
- _ ->
- [{{deps, default}, proplists:get_value(deps, Config, [])}]
+ [{{locks, default}, D}, {{deps, default}, Deps}]
end.
%% @doc discover a complete version of the app info with all fields set.
diff --git a/src/rebar_config.erl b/src/rebar_config.erl
index 61301cb..8d7bcf4 100644
--- a/src/rebar_config.erl
+++ b/src/rebar_config.erl
@@ -30,6 +30,7 @@
,consult_app_file/1
,consult_file/1
,consult_lock_file/1
+ ,write_lock_file/2
,verify_config_format/1
,format_error/1
@@ -50,7 +51,40 @@ consult_app_file(File) ->
consult_file_(File).
consult_lock_file(File) ->
- consult_file_(File).
+ Terms = consult_file_(File),
+ case Terms of
+ [] ->
+ [];
+ [Locks] when is_list(Locks) -> % beta lock file
+ Locks;
+ [{Vsn, Locks}|Attrs] when is_list(Locks) -> % versioned lock file
+ %% Make sure the warning above is to be shown whenever a version
+ %% newer than the current one is being used, as we can't parse
+ %% all the contents of the lock file properly.
+ ?WARN("Rebar3 detected a lock file from a newer version. "
+ "It will be loaded in compatibility mode, but important "
+ "information may be missing or lost. It is recommended to "
+ "upgrade Rebar3.", []),
+ read_attrs(Vsn, Locks, Attrs)
+ end.
+
+write_lock_file(LockFile, Locks) ->
+ NewLocks = write_attrs(Locks),
+ %% Write locks in the beta format, at least until it's been long
+ %% enough we can start modifying the lock format.
+ file:write_file(LockFile, io_lib:format("~p.~n", [NewLocks])).
+
+read_attrs(_Vsn, Locks, _Attrs) ->
+ %% Beta copy does not know how to expand attributes, but
+ %% is ready to support it.
+ Locks.
+
+write_attrs(Locks) ->
+ %% No attribute known that needs to be taken out of the structure,
+ %% just return terms as is.
+ Locks.
+
+
consult_file(File) ->
Terms = consult_file_(File),
@@ -87,7 +121,7 @@ verify_config_format([Term | _]) ->
merge_locks(Config, []) ->
Config;
%% lockfile with entries
-merge_locks(Config, [Locks]) ->
+merge_locks(Config, Locks) ->
ConfigDeps = proplists:get_value(deps, Config, []),
%% We want the top level deps only from the lock file.
%% This ensures deterministic overrides for configs.
diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl
index 3729704..1ec58d4 100644
--- a/src/rebar_dir.erl
+++ b/src/rebar_dir.erl
@@ -92,6 +92,7 @@ global_config() ->
Home = home_dir(),
filename:join([Home, ".config", "rebar3", "rebar.config"]).
+-spec global_cache_dir(rebar_dict()) -> file:filename_all().
global_cache_dir(Opts) ->
Home = home_dir(),
rebar_opts:get(Opts, global_rebar_dir, filename:join([Home, ".cache", "rebar3"])).
diff --git a/src/rebar_dist_utils.erl b/src/rebar_dist_utils.erl
new file mode 100644
index 0000000..f462826
--- /dev/null
+++ b/src/rebar_dist_utils.erl
@@ -0,0 +1,89 @@
+%%% Common functions to boot/stop distributed setups for
+%%% the rebar3 script.
+-module(rebar_dist_utils).
+-export([either/3, short/2, long/2, find_options/1]).
+-include("rebar.hrl").
+
+%%%%%%%%%%%%%%%%%%
+%%% PUBLIC API %%%
+%%%%%%%%%%%%%%%%%%
+-spec either(Name::atom(), SName::atom(), Opts::[{setcookie,term()}]) -> atom().
+either(undefined, undefined, _) ->
+ 'nonode@nohost';
+either(Name, undefined, Opts) ->
+ long(Name, Opts),
+ node();
+either(undefined, SName, Opts) ->
+ short(SName, Opts),
+ node();
+either(_, _, _) ->
+ ?ABORT("Cannot have both short and long node names defined", []).
+
+short(Name, Opts) ->
+ start(Name, shortnames, Opts).
+
+long(Name, Opts) ->
+ start(Name, longnames, Opts).
+
+-spec find_options(rebar_state:state()) -> {Long, Short, Opts} when
+ Long :: atom(),
+ Short :: atom(),
+ Opts :: [{setcookie,term()}].
+find_options(State) ->
+ {Long, Short} = find_name_options(State),
+ case find_cookie_option(State) of
+ nocookie ->
+ {Long, Short, []};
+ Cookie ->
+ {Long, Short, [{setcookie, Cookie}]}
+ end.
+
+%%%%%%%%%%%%%%%
+%%% PRIVATE %%%
+%%%%%%%%%%%%%%%
+start(Name, Type, Opts) ->
+ check_epmd(net_kernel:start([Name, Type])),
+ setup_cookie(Opts).
+
+check_epmd({error,{{shutdown, {_,net_kernel,{'EXIT',nodistribution}}},_}}) ->
+ ?ERROR("Erlang Distribution failed, falling back to nonode@nohost. "
+ "Verify that epmd is running and try again.",[]);
+check_epmd(_) ->
+ ok.
+
+setup_cookie(Opts) ->
+ case {node(), proplists:get_value(setcookie, Opts, nocookie)} of
+ {'nonode@nohost', _} -> nocookie;
+ {_, nocookie} -> nocookie;
+ {Node, Name} -> erlang:set_cookie(Node, Name)
+ end.
+
+find_name_options(State) ->
+ {Opts, _} = rebar_state:command_parsed_args(State),
+ %% First try the CLI
+ case {proplists:get_value(name, Opts), proplists:get_value(sname, Opts)} of
+ {undefined, undefined} ->
+ %% Else try the config file
+ DistOpts = rebar_state:get(State, dist_node, []),
+ %% Pick the first one seen to support profile merges
+ find_first_name(DistOpts);
+ Res ->
+ Res
+ end.
+
+find_first_name([]) -> {undefined, undefined};
+find_first_name([{sname,Val}|_]) -> {undefined, Val};
+find_first_name([{name,Val}|_]) -> {Val, undefined};
+find_first_name([_|Opts]) -> find_first_name(Opts).
+
+find_cookie_option(State) ->
+ {Opts, _} = rebar_state:command_parsed_args(State),
+ %% First try the CLI
+ case proplists:get_value(setcookie, Opts) of
+ undefined ->
+ %% Else try the config file
+ DistOpts = rebar_state:get(State, dist_node, []),
+ proplists:get_value(setcookie, DistOpts, nocookie);
+ Res ->
+ Res
+ end.
diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl
index 876d047..5a6a5ef 100644
--- a/src/rebar_git_resource.erl
+++ b/src/rebar_git_resource.erl
@@ -201,7 +201,7 @@ parse_tags(Dir) ->
{error, _} ->
{undefined, "0.0.0"};
{ok, Line} ->
- case re:run(Line, "(\\(|\\s)(HEAD,\\s)tag:\\s(v?([^,\\)]+))", [{capture, [3, 4], list}]) of
+ case re:run(Line, "(\\(|\\s)(HEAD[^,]*,\\s)tag:\\s(v?([^,\\)]+))", [{capture, [3, 4], list}]) of
{match,[Tag, Vsn]} ->
{Tag, Vsn};
nomatch ->
diff --git a/src/rebar_hooks.erl b/src/rebar_hooks.erl
index 4e6d486..3af17ca 100644
--- a/src/rebar_hooks.erl
+++ b/src/rebar_hooks.erl
@@ -2,6 +2,7 @@
-export([run_all_hooks/5
,run_all_hooks/6
+ ,run_project_and_app_hooks/5
,format_error/1]).
-include("rebar.hrl").
@@ -20,6 +21,11 @@ run_all_hooks(Dir, Type, Command, Providers, State) ->
run_provider_hooks(Dir, Type, Command, Providers, rebar_state:opts(State), State),
run_hooks(Dir, Type, Command, rebar_state:opts(State), State).
+run_project_and_app_hooks(Dir, Type, Command, Providers, State) ->
+ ProjectApps = rebar_state:project_apps(State),
+ [rebar_hooks:run_all_hooks(Dir, Type, Command, Providers, AppInfo, State) || AppInfo <- ProjectApps],
+ run_all_hooks(Dir, Type, Command, Providers, State).
+
run_provider_hooks(Dir, Type, Command, Providers, Opts, State) ->
case rebar_opts:get(Opts, provider_hooks, []) of
[] ->
@@ -81,6 +87,7 @@ run_hooks(Dir, post, Command, Opts, State) ->
run_hooks(Dir, Type, Command, Opts, State) ->
case rebar_opts:get(Opts, Type, []) of
[] ->
+ ?DEBUG("run_hooks(~p, ~p, ~p) -> no hooks defined\n", [Dir, Type, Command]),
ok;
Hooks ->
Env = create_env(State, Opts),
diff --git a/src/rebar_log.erl b/src/rebar_log.erl
index 06cfa9c..23ae81e 100644
--- a/src/rebar_log.erl
+++ b/src/rebar_log.erl
@@ -28,20 +28,43 @@
-export([init/2,
set_level/1,
+ get_level/0,
error_level/0,
default_level/0,
+ intensity/0,
log/3,
- is_verbose/1]).
+ is_verbose/1,
+ valid_level/1]).
-define(ERROR_LEVEL, 0).
-define(WARN_LEVEL, 1).
-define(INFO_LEVEL, 2).
-define(DEBUG_LEVEL, 3).
+-define(DFLT_INTENSITY, high).
%% ===================================================================
%% Public API
%% ===================================================================
+%% @doc Returns the color intensity, we first check the application envorinment
+%% if that is not set we check the environment variable REBAR_COLOR.
+intensity() ->
+ case application:get_env(rebar, color_intensity) of
+ undefined ->
+ R = case os:getenv("REBAR_COLOR") of
+ "high" ->
+ high;
+ "low" ->
+ low;
+ _ ->
+ ?DFLT_INTENSITY
+ end,
+ application:set_env(rebar, color_intensity, R),
+ R;
+ {ok, Mode} ->
+ Mode
+ end.
+
init(Caller, Verbosity) ->
Level = case valid_level(Verbosity) of
?ERROR_LEVEL -> error;
@@ -49,12 +72,25 @@ init(Caller, Verbosity) ->
?INFO_LEVEL -> info;
?DEBUG_LEVEL -> debug
end,
- Log = ec_cmd_log:new(Level, Caller),
+ Intensity = intensity(),
+ Log = ec_cmd_log:new(Level, Caller, Intensity),
+ set_level(valid_level(Verbosity)),
application:set_env(rebar, log, Log).
set_level(Level) ->
ok = application:set_env(rebar, log_level, Level).
+get_level() ->
+ case application:get_env(rebar, log_level) of
+ undefined ->
+ default_level();
+ {ok, Level} ->
+ Level
+ end.
+
+log(Level = error, Str, Args) ->
+ {ok, LogState} = application:get_env(rebar, log),
+ ec_cmd_log:Level(LogState, lists:flatten(cf:format("~!^~s~n", [Str])), Args);
log(Level, Str, Args) ->
{ok, LogState} = application:get_env(rebar, log),
ec_cmd_log:Level(LogState, Str++"~n", Args).
@@ -65,9 +101,9 @@ default_level() -> ?INFO_LEVEL.
is_verbose(State) ->
rebar_state:get(State, is_verbose, false).
+valid_level(Level) ->
+ erlang:max(?ERROR_LEVEL, erlang:min(Level, ?DEBUG_LEVEL)).
+
%% ===================================================================
%% Internal functions
%% ===================================================================
-
-valid_level(Level) ->
- erlang:max(?ERROR_LEVEL, erlang:min(Level, ?DEBUG_LEVEL)).
diff --git a/src/rebar_packages.erl b/src/rebar_packages.erl
index c56009e..d4b8a14 100644
--- a/src/rebar_packages.erl
+++ b/src/rebar_packages.erl
@@ -7,7 +7,9 @@
,registry_dir/1
,package_dir/1
,registry_checksum/2
+ ,find_highest_matching/6
,find_highest_matching/4
+ ,find_all/3
,verify_table/1
,format_error/1]).
@@ -65,22 +67,28 @@ deps(Name, Vsn, State) ->
deps_(Name, Vsn, State)
catch
_:_ ->
- handle_missing_package(Name, Vsn, State)
+ handle_missing_package({Name, Vsn}, State, fun(State1) -> deps_(Name, Vsn, State1) end)
end.
deps_(Name, Vsn, State) ->
?MODULE:verify_table(State),
ets:lookup_element(?PACKAGE_TABLE, {ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)}, 2).
-handle_missing_package(Name, Vsn, State) ->
- ?INFO("Package ~s-~s not found. Fetching registry updates and trying again...", [Name, Vsn]),
+handle_missing_package(Dep, State, Fun) ->
+ case Dep of
+ {Name, Vsn} ->
+ ?INFO("Package ~s-~s not found. Fetching registry updates and trying again...", [Name, Vsn]);
+ _ ->
+ ?INFO("Package ~p not found. Fetching registry updates and trying again...", [Dep])
+ end,
+
{ok, State1} = rebar_prv_update:do(State),
try
- deps_(Name, Vsn, State1)
+ Fun(State1)
catch
_:_ ->
%% Even after an update the package is still missing, time to error out
- throw(?PRV_ERROR({missing_package, ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)}))
+ throw(?PRV_ERROR({missing_package, Dep}))
end.
registry_dir(State) ->
@@ -139,16 +147,43 @@ registry_checksum({pkg, Name, Vsn}, State) ->
%% `~> 2.0` | `>= 2.0.0 and < 3.0.0`
%% `~> 2.1` | `>= 2.1.0 and < 3.0.0`
find_highest_matching(Dep, Constraint, Table, State) ->
+ find_highest_matching(undefined, undefined, Dep, Constraint, Table, State).
+
+find_highest_matching(Pkg, PkgVsn, Dep, Constraint, Table, State) ->
+ try find_highest_matching_(Pkg, PkgVsn, Dep, Constraint, Table, State) of
+ none ->
+ handle_missing_package(Dep, State,
+ fun(State1) ->
+ find_highest_matching_(Pkg, PkgVsn, Dep, Constraint, Table, State1)
+ end);
+ Result ->
+ Result
+ catch
+ _:_ ->
+ handle_missing_package(Dep, State,
+ fun(State1) ->
+ find_highest_matching_(Pkg, PkgVsn, Dep, Constraint, Table, State1)
+ end)
+ end.
+
+find_highest_matching_(Pkg, PkgVsn, Dep, Constraint, Table, State) ->
+ try find_all(Dep, Table, State) of
+ {ok, [Vsn]} ->
+ handle_single_vsn(Pkg, PkgVsn, Dep, Vsn, Constraint);
+ {ok, [HeadVsn | VsnTail]} ->
+ {ok, handle_vsns(Constraint, HeadVsn, VsnTail)}
+ catch
+ error:badarg ->
+ none
+ end.
+
+find_all(Dep, Table, State) ->
?MODULE:verify_table(State),
try ets:lookup_element(Table, Dep, 2) of
- [[HeadVsn | VsnTail]] ->
- {ok, handle_vsns(Constraint, HeadVsn, VsnTail)};
- [[Vsn]] ->
- handle_single_vsn(Dep, Vsn, Constraint);
- [Vsn] ->
- handle_single_vsn(Dep, Vsn, Constraint);
- [HeadVsn | VsnTail] ->
- {ok, handle_vsns(Constraint, HeadVsn, VsnTail)}
+ [Vsns] when is_list(Vsns)->
+ {ok, Vsns};
+ Vsns ->
+ {ok, Vsns}
catch
error:badarg ->
none
@@ -165,18 +200,26 @@ handle_vsns(Constraint, HeadVsn, VsnTail) ->
end
end, HeadVsn, VsnTail).
-handle_single_vsn(Dep, Vsn, Constraint) ->
+handle_single_vsn(Pkg, PkgVsn, Dep, Vsn, Constraint) ->
case ec_semver:pes(Vsn, Constraint) of
true ->
{ok, Vsn};
false ->
- ?WARN("Only existing version of ~s is ~s which does not match constraint ~~> ~s. "
- "Using anyway, but it is not guaranteed to work.", [Dep, Vsn, Constraint]),
+ case {Pkg, PkgVsn} of
+ {undefined, undefined} ->
+ ?WARN("Only existing version of ~s is ~s which does not match constraint ~~> ~s. "
+ "Using anyway, but it is not guaranteed to work.", [Dep, Vsn, Constraint]);
+ _ ->
+ ?WARN("[~s:~s] Only existing version of ~s is ~s which does not match constraint ~~> ~s. "
+ "Using anyway, but it is not guaranteed to work.", [Pkg, PkgVsn, Dep, Vsn, Constraint])
+ end,
{ok, Vsn}
end.
-format_error({missing_package, Package, Version}) ->
- io_lib:format("Package not found in registry: ~s-~s.", [Package, Version]).
+format_error({missing_package, {Name, Vsn}}) ->
+ io_lib:format("Package not found in registry: ~s-~s.", [ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)]);
+format_error({missing_package, Dep}) ->
+ io_lib:format("Package not found in registry: ~p.", [Dep]).
verify_table(State) ->
ets:info(?PACKAGE_TABLE, named_table) =:= true orelse load_and_verify_version(State).
diff --git a/src/rebar_plugins.erl b/src/rebar_plugins.erl
index 3c33498..68ba6da 100644
--- a/src/rebar_plugins.erl
+++ b/src/rebar_plugins.erl
@@ -3,7 +3,8 @@
-module(rebar_plugins).
--export([top_level_install/1
+-export([project_plugins_install/1
+ ,top_level_install/1
,project_apps_install/1
,install/2
,handle_plugins/3
@@ -15,6 +16,16 @@
%% Public API
%% ===================================================================
+-spec project_plugins_install(rebar_state:t()) -> rebar_state:t().
+project_plugins_install(State) ->
+ Profiles = rebar_state:current_profiles(State),
+ State1 = rebar_state:allow_provider_overrides(State, true),
+ State2 = lists:foldl(fun(Profile, StateAcc) ->
+ Plugins = rebar_state:get(State, {project_plugins, Profile}, []),
+ handle_plugins(Profile, Plugins, StateAcc)
+ end, State1, Profiles),
+ rebar_state:allow_provider_overrides(State2, false).
+
-spec top_level_install(rebar_state:t()) -> rebar_state:t().
top_level_install(State) ->
Profiles = rebar_state:current_profiles(State),
diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl
index 4be50d8..5712fbf 100644
--- a/src/rebar_prv_common_test.erl
+++ b/src/rebar_prv_common_test.erl
@@ -37,6 +37,7 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
+ setup_name(State),
Tests = prepare_tests(State),
case compile(State, Tests) of
%% successfully compiled apps
@@ -52,14 +53,16 @@ do(State, Tests) ->
%% Run ct provider prehooks
Providers = rebar_state:providers(State),
Cwd = rebar_dir:get_cwd(),
- rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State),
+
+ %% Run ct provider pre hooks for all project apps and top level project hooks
+ rebar_hooks:run_project_and_app_hooks(Cwd, pre, ?PROVIDER, Providers, State),
case Tests of
{ok, T} ->
case run_tests(State, T) of
ok ->
- %% Run ct provider posthooks
- rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State),
+ %% Run ct provider post hooks for all project apps and top level project hooks
+ rebar_hooks:run_project_and_app_hooks(Cwd, post, ?PROVIDER, Providers, State),
rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)),
{ok, State};
Error ->
@@ -103,6 +106,10 @@ format_error({multiple_errors, Errors}) ->
%% Internal functions
%% ===================================================================
+setup_name(State) ->
+ {Long, Short, Opts} = rebar_dist_utils:find_options(State),
+ rebar_dist_utils:either(Long, Short, Opts).
+
prepare_tests(State) ->
%% command line test options
CmdOpts = cmdopts(State),
@@ -131,6 +138,8 @@ transform_opts([{testcase, Cases}|Rest], Acc) ->
transform_opts(Rest, [{testcase, split_string(Cases)}|Acc]);
transform_opts([{config, Configs}|Rest], Acc) ->
transform_opts(Rest, [{config, split_string(Configs)}|Acc]);
+transform_opts([{include, Includes}|Rest], Acc) ->
+ transform_opts(Rest, [{include, split_string(Includes)}|Acc]);
transform_opts([{logopts, LogOpts}|Rest], Acc) ->
transform_opts(Rest, [{logopts, lists:map(fun(P) -> list_to_atom(P) end, split_string(LogOpts))}|Acc]);
transform_opts([{force_stop, "true"}|Rest], Acc) ->
@@ -164,10 +173,15 @@ cfgopts(State) ->
end.
ensure_opts([], Acc) -> lists:reverse(Acc);
-ensure_opts([{test_spec, _}|_Rest], _Acc) ->
- ?PRV_ERROR({badconfig, "Test specs not supported"});
-ensure_opts([{auto_compile, _}|_Rest], _Acc) ->
- ?PRV_ERROR({badconfig, "Auto compile not supported"});
+ensure_opts([{test_spec, _}|Rest], Acc) ->
+ ?WARN("Test specs not supported. See http://www.rebar3.org/docs/running-tests#common-test", []),
+ ensure_opts(Rest, Acc);
+ensure_opts([{cover, _}|Rest], Acc) ->
+ ?WARN("Cover specs not supported. See http://www.rebar3.org/docs/running-tests#common-test", []),
+ ensure_opts(Rest, Acc);
+ensure_opts([{auto_compile, _}|Rest], Acc) ->
+ ?WARN("Auto compile not supported", []),
+ ensure_opts(Rest, Acc);
ensure_opts([{suite, Suite}|Rest], Acc) when is_integer(hd(Suite)) ->
ensure_opts(Rest, [{suite, Suite}|Acc]);
ensure_opts([{suite, Suite}|Rest], Acc) when is_atom(Suite) ->
@@ -180,8 +194,10 @@ ensure_opts([{suite, Suites}|Rest], Acc) ->
ensure_opts(Rest, [NewSuites|Acc]);
ensure_opts([{K, V}|Rest], Acc) ->
ensure_opts(Rest, [{K, V}|Acc]);
-ensure_opts([V|_Rest], _Acc) ->
- ?PRV_ERROR({badconfig, {"Member `~p' of option `~p' must be a 2-tuple", {V, ct_opts}}}).
+%% pass through other options, in case of things like config terms
+%% in `ct_opts`
+ensure_opts([V|Rest], Acc) ->
+ ensure_opts(Rest, [V|Acc]).
add_hooks(Opts, State) ->
case {readable(State), lists:keyfind(ct_hooks, 1, Opts)} of
@@ -243,8 +259,8 @@ application_dirs([App|Rest], Acc) ->
end.
compile(State, {ok, _} = Tests) ->
- %% inject `ct_first_files` and `ct_compile_opts` into the applications
- %% to be compiled
+ %% inject `ct_first_files`, `ct_compile_opts` and `include` (from `ct_opts`
+ %% and command line options) into the applications to be compiled
case inject_ct_state(State, Tests) of
{ok, NewState} -> do_compile(NewState);
Error -> Error
@@ -264,22 +280,22 @@ do_compile(State) ->
inject_ct_state(State, {ok, Tests}) ->
Apps = rebar_state:project_apps(State),
- case inject_ct_state(State, Apps, []) of
+ case inject_ct_state(State, Tests, Apps, []) of
{ok, {NewState, ModdedApps}} ->
test_dirs(NewState, ModdedApps, Tests);
{error, _} = Error -> Error
end;
inject_ct_state(_State, Error) -> Error.
-inject_ct_state(State, [App|Rest], Acc) ->
- case inject(rebar_app_info:opts(App), State) of
+inject_ct_state(State, Tests, [App|Rest], Acc) ->
+ case inject(rebar_app_info:opts(App), State, Tests) of
{error, _} = Error -> Error;
NewOpts ->
NewApp = rebar_app_info:opts(App, NewOpts),
- inject_ct_state(State, Rest, [NewApp|Acc])
+ inject_ct_state(State, Tests, Rest, [NewApp|Acc])
end;
-inject_ct_state(State, [], Acc) ->
- case inject(rebar_state:opts(State), State) of
+inject_ct_state(State, Tests, [], Acc) ->
+ case inject(rebar_state:opts(State), State, Tests) of
{error, _} = Error -> Error;
NewOpts ->
{ok, {rebar_state:opts(State, NewOpts), lists:reverse(Acc)}}
@@ -292,26 +308,55 @@ opts(Opts, Key, Default) ->
?PRV_ERROR({badconfig, {"Value `~p' of option `~p' must be a list", {Wrong, Key}}})
end.
-inject(Opts, State) -> erl_opts(Opts, State).
+inject(Opts, State, Tests) -> erl_opts(Opts, State, Tests).
-erl_opts(Opts, State) ->
+erl_opts(Opts, State, Tests) ->
%% append `ct_compile_opts` to app defined `erl_opts`
ErlOpts = opts(Opts, erl_opts, []),
CTOpts = opts(Opts, ct_compile_opts, []),
case add_transforms(append(CTOpts, ErlOpts), State) of
- {error, Error} -> {error, Error};
- NewErlOpts -> first_files(rebar_opts:set(Opts, erl_opts, NewErlOpts))
+ {error, _} = Error -> Error;
+ NewErlOpts -> first_files(rebar_opts:set(Opts, erl_opts, NewErlOpts), Tests)
end.
-first_files(Opts) ->
+first_files(Opts, Tests) ->
%% append `ct_first_files` to app defined `erl_first_files`
FirstFiles = opts(Opts, erl_first_files, []),
CTFirstFiles = opts(Opts, ct_first_files, []),
case append(CTFirstFiles, FirstFiles) of
{error, _} = Error -> Error;
- NewFirstFiles -> rebar_opts:set(Opts, erl_first_files, NewFirstFiles)
+ NewFirstFiles -> include_files(rebar_opts:set(Opts, erl_first_files, NewFirstFiles), Tests)
+ end.
+
+include_files(Opts, Tests) ->
+ %% append include dirs from command line and `ct_opts` to app defined
+ %% `erl_opts`
+ ErlOpts = opts(Opts, erl_opts, []),
+ Includes = proplists:get_value(include, Tests, []),
+ Is = lists:map(fun(I) -> {i, I} end, Includes),
+ case append(Is, ErlOpts) of
+ {error, _} = Error -> Error;
+ NewIncludes -> ct_macro(rebar_opts:set(Opts, erl_opts, NewIncludes))
+ end.
+
+ct_macro(Opts) ->
+ ErlOpts = opts(Opts, erl_opts, []),
+ NewOpts = safe_define_ct_macro(ErlOpts),
+ rebar_opts:set(Opts, erl_opts, NewOpts).
+
+safe_define_ct_macro(Opts) ->
+ %% defining a compile macro twice results in an exception so
+ %% make sure 'COMMON_TEST' is only defined once
+ case test_defined(Opts) of
+ true -> Opts;
+ false -> [{d, 'COMMON_TEST'}|Opts]
end.
+test_defined([{d, 'COMMON_TEST'}|_]) -> true;
+test_defined([{d, 'COMMON_TEST', true}|_]) -> true;
+test_defined([_|Rest]) -> test_defined(Rest);
+test_defined([]) -> false.
+
append({error, _} = Error, _) -> Error;
append(_, {error, _} = Error) -> Error;
append(A, B) -> A ++ B.
@@ -597,8 +642,12 @@ ct_opts(_State) ->
{multiply_timetraps, undefined, "multiply_timetraps", integer, help(multiple_timetraps)}, %% Integer
{scale_timetraps, undefined, "scale_timetraps", boolean, help(scale_timetraps)},
{create_priv_dir, undefined, "create_priv_dir", string, help(create_priv_dir)},
+ {include, undefined, "include", string, help(include)},
{readable, undefined, "readable", boolean, help(readable)},
- {verbose, $v, "verbose", boolean, help(verbose)}
+ {verbose, $v, "verbose", boolean, help(verbose)},
+ {name, undefined, "name", atom, help(name)},
+ {sname, undefined, "sname", atom, help(sname)},
+ {setcookie, undefined, "setcookie", atom, help(setcookie)}
].
help(dir) ->
@@ -647,10 +696,17 @@ help(scale_timetraps) ->
"Scale timetraps";
help(create_priv_dir) ->
"Create priv dir (auto_per_run | auto_per_tc | manual_per_tc)";
+help(include) ->
+ "Directories containing additional include files";
help(readable) ->
"Shows test case names and only displays logs to shell on failures";
help(verbose) ->
"Verbose output";
+help(name) ->
+ "Gives a long name to the node";
+help(sname) ->
+ "Gives a short name to the node";
+help(setcookie) ->
+ "Sets the cookie if the node is distributed";
help(_) ->
"".
-
diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl
index 834eb98..622ee60 100644
--- a/src/rebar_prv_dialyzer.erl
+++ b/src/rebar_prv_dialyzer.erl
@@ -97,7 +97,7 @@ do(State) ->
end.
%% This is used to workaround dialyzer quirk discussed here
-%% https://github.com/rebar/rebar3/pull/489#issuecomment-107953541
+%% https://github.com/erlang/rebar3/pull/489#issuecomment-107953541
%% Dialyzer gets default plt location wrong way by peeking HOME environment
%% variable which usually is not defined on Windows.
maybe_fix_env() ->
diff --git a/src/rebar_prv_edoc.erl b/src/rebar_prv_edoc.erl
index e7048b6..6cefe14 100644
--- a/src/rebar_prv_edoc.erl
+++ b/src/rebar_prv_edoc.erl
@@ -32,13 +32,19 @@ init(State) ->
do(State) ->
code:add_pathsa(rebar_state:code_paths(State, all_deps)),
ProjectApps = rebar_state:project_apps(State),
+ Providers = rebar_state:providers(State),
EDocOpts = rebar_state:get(State, edoc_opts, []),
+ Cwd = rebar_state:dir(State),
+ rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State),
lists:foreach(fun(AppInfo) ->
+ rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, AppInfo, State),
AppName = ec_cnv:to_list(rebar_app_info:name(AppInfo)),
?INFO("Running edoc for ~s", [AppName]),
AppDir = rebar_app_info:dir(AppInfo),
- ok = edoc:application(list_to_atom(AppName), AppDir, EDocOpts)
+ ok = edoc:application(list_to_atom(AppName), AppDir, EDocOpts),
+ rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, AppInfo, State)
end, ProjectApps),
+ rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State),
rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)),
{ok, State}.
diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl
index a1a4408..942fd10 100644
--- a/src/rebar_prv_eunit.erl
+++ b/src/rebar_prv_eunit.erl
@@ -51,19 +51,20 @@ do(State) ->
do(State, Tests) ->
?INFO("Performing EUnit tests...", []),
+ setup_name(State),
rebar_utils:update_code(rebar_state:code_paths(State, all_deps), [soft_purge]),
%% Run eunit provider prehooks
Providers = rebar_state:providers(State),
Cwd = rebar_dir:get_cwd(),
- rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State),
+ rebar_hooks:run_project_and_app_hooks(Cwd, pre, ?PROVIDER, Providers, State),
case validate_tests(State, Tests) of
{ok, T} ->
case run_tests(State, T) of
{ok, State1} ->
%% Run eunit provider posthooks
- rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State1),
+ rebar_hooks:run_project_and_app_hooks(Cwd, post, ?PROVIDER, Providers, State1),
rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)),
{ok, State1};
Error ->
@@ -106,6 +107,10 @@ format_error({error, Error}) ->
%% Internal functions
%% ===================================================================
+setup_name(State) ->
+ {Long, Short, Opts} = rebar_dist_utils:find_options(State),
+ rebar_dist_utils:either(Long, Short, Opts).
+
prepare_tests(State) ->
%% parse and translate command line tests
CmdTests = resolve_tests(State),
@@ -190,7 +195,7 @@ dedupe_tests({AppMods, TestMods}) ->
%% in AppMods that will trigger it
F = fun(Mod) ->
M = filename:basename(Mod, ".erl"),
- MatchesTest = fun(Dir) -> filename:basename(Dir, ".erl") ++ "_tests" == M end,
+ MatchesTest = fun(Dir) -> filename:basename(Dir, ".erl") ++ "_tests" == M end,
case lists:any(MatchesTest, AppMods) of
false -> {true, {module, list_to_atom(M)}};
true -> false
@@ -244,9 +249,27 @@ first_files(Opts) ->
EUnitFirstFiles = opts(Opts, eunit_first_files, []),
case append(EUnitFirstFiles, FirstFiles) of
{error, _} = Error -> Error;
- NewFirstFiles -> rebar_opts:set(Opts, erl_first_files, NewFirstFiles)
+ NewFirstFiles -> eunit_macro(rebar_opts:set(Opts, erl_first_files, NewFirstFiles))
end.
+eunit_macro(Opts) ->
+ ErlOpts = opts(Opts, erl_opts, []),
+ NewOpts = safe_define_eunit_macro(ErlOpts),
+ rebar_opts:set(Opts, erl_opts, NewOpts).
+
+safe_define_eunit_macro(Opts) ->
+ %% defining a compile macro twice results in an exception so
+ %% make sure 'EUNIT' is only defined once
+ case test_defined(Opts) of
+ true -> Opts;
+ false -> [{d, 'EUNIT'}|Opts]
+ end.
+
+test_defined([{d, 'EUNIT'}|_]) -> true;
+test_defined([{d, 'EUNIT', true}|_]) -> true;
+test_defined([_|Rest]) -> test_defined(Rest);
+test_defined([]) -> false.
+
append({error, _} = Error, _) -> Error;
append(_, {error, _} = Error) -> Error;
append(A, B) -> A ++ B.
@@ -457,15 +480,21 @@ eunit_opts(_State) ->
[{app, undefined, "app", string, help(app)},
{application, undefined, "application", string, help(app)},
{cover, $c, "cover", boolean, help(cover)},
- {dir, undefined, "dir", string, help(dir)},
- {file, undefined, "file", string, help(file)},
- {module, undefined, "module", string, help(module)},
- {suite, undefined, "suite", string, help(module)},
- {verbose, $v, "verbose", boolean, help(verbose)}].
-
-help(app) -> "Comma separated list of application test suites to run. Equivalent to `[{application, App}]`.";
-help(cover) -> "Generate cover data. Defaults to false.";
-help(dir) -> "Comma separated list of dirs to load tests from. Equivalent to `[{dir, Dir}]`.";
-help(file) -> "Comma separated list of files to load tests from. Equivalent to `[{file, File}]`.";
-help(module) -> "Comma separated list of modules to load tests from. Equivalent to `[{module, Module}]`.";
-help(verbose) -> "Verbose output. Defaults to false.".
+ {dir, $d, "dir", string, help(dir)},
+ {file, $f, "file", string, help(file)},
+ {module, $m, "module", string, help(module)},
+ {suite, $s, "suite", string, help(module)},
+ {verbose, $v, "verbose", boolean, help(verbose)},
+ {name, undefined, "name", atom, help(name)},
+ {sname, undefined, "sname", atom, help(sname)},
+ {setcookie, undefined, "setcookie", atom, help(setcookie)}].
+
+help(app) -> "Comma separated list of application test suites to run. Equivalent to `[{application, App}]`.";
+help(cover) -> "Generate cover data. Defaults to false.";
+help(dir) -> "Comma separated list of dirs to load tests from. Equivalent to `[{dir, Dir}]`.";
+help(file) -> "Comma separated list of files to load tests from. Equivalent to `[{file, File}]`.";
+help(module) -> "Comma separated list of modules to load tests from. Equivalent to `[{module, Module}]`.";
+help(verbose) -> "Verbose output. Defaults to false.";
+help(name) -> "Gives a long name to the node";
+help(sname) -> "Gives a short name to the node";
+help(setcookie) -> "Sets the cookie if the node is distributed".
diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl
index a484c5f..5e6aa4c 100644
--- a/src/rebar_prv_install_deps.erl
+++ b/src/rebar_prv_install_deps.erl
@@ -35,7 +35,8 @@
-include("rebar.hrl").
-include_lib("providers/include/providers.hrl").
--export([handle_deps_as_profile/4,
+-export([do_/1,
+ handle_deps_as_profile/4,
profile_dep_dir/2,
find_cycles/1,
cull_compile/2]).
@@ -69,8 +70,11 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
+ ?INFO("Verifying dependencies...", []),
+ do_(State).
+
+do_(State) ->
try
- ?INFO("Verifying dependencies...", []),
Profiles = rebar_state:current_profiles(State),
ProjectApps = rebar_state:project_apps(State),
diff --git a/src/rebar_prv_lock.erl b/src/rebar_prv_lock.erl
index 8578979..cbe8dfe 100644
--- a/src/rebar_prv_lock.erl
+++ b/src/rebar_prv_lock.erl
@@ -35,8 +35,7 @@ do(State) ->
OldLocks = rebar_state:get(State, {locks, default}, []),
Locks = lists:keysort(1, build_locks(State)),
Dir = rebar_state:dir(State),
- file:write_file(filename:join(Dir, ?LOCK_FILE),
- io_lib:format("~p.~n", [Locks])),
+ rebar_config:write_lock_file(filename:join(Dir, ?LOCK_FILE), Locks),
State1 = rebar_state:set(State, {locks, default}, Locks),
OldLockNames = [element(1,L) || L <- OldLocks],
diff --git a/src/rebar_prv_packages.erl b/src/rebar_prv_packages.erl
index 998320c..7217ab8 100644
--- a/src/rebar_prv_packages.erl
+++ b/src/rebar_prv_packages.erl
@@ -28,22 +28,19 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
rebar_packages:packages(State),
- print_packages(),
+ case rebar_state:command_args(State) of
+ [Name] ->
+ print_packages(get_packages(iolist_to_binary(Name)));
+ _ ->
+ print_packages(sort_packages())
+ end,
{ok, State}.
-spec format_error(any()) -> iolist().
format_error(load_registry_fail) ->
"Failed to load package regsitry. Try running 'rebar3 update' to fix".
-print_packages() ->
- SortedPkgs = ets:foldl(fun({package_index_version, _}, Acc) ->
- Acc;
- ({Pkg, Vsns}, Acc) ->
- orddict:store(Pkg, Vsns, Acc);
- (_, Acc) ->
- Acc
- end, orddict:new(), ?PACKAGE_TABLE),
-
+print_packages(Pkgs) ->
orddict:map(fun(Name, Vsns) ->
SortedVsns = lists:sort(fun(A, B) ->
ec_semver:lte(ec_semver:parse(A)
@@ -51,7 +48,20 @@ print_packages() ->
end, Vsns),
VsnStr = join(SortedVsns, <<", ">>),
?CONSOLE("~s:~n Versions: ~s~n", [Name, VsnStr])
- end, SortedPkgs).
+ end, Pkgs).
+
+sort_packages() ->
+ ets:foldl(fun({package_index_version, _}, Acc) ->
+ Acc;
+ ({Pkg, Vsns}, Acc) ->
+ orddict:store(Pkg, Vsns, Acc);
+ (_, Acc) ->
+ Acc
+ end, orddict:new(), ?PACKAGE_TABLE).
+
+get_packages(Name) ->
+ ets:lookup(?PACKAGE_TABLE, Name).
+
-spec join([binary()], binary()) -> binary().
join([Bin], _Sep) ->
diff --git a/src/rebar_prv_path.erl b/src/rebar_prv_path.erl
index 37c9834..4e88496 100644
--- a/src/rebar_prv_path.erl
+++ b/src/rebar_prv_path.erl
@@ -95,10 +95,14 @@ print_paths_if_exist(Paths, State) ->
project_deps(State) ->
Profiles = rebar_state:current_profiles(State),
- List = lists:foldl(fun(Profile, Acc) -> rebar_state:get(State, {deps, Profile}, []) ++ Acc end, [], Profiles),
- Deps = [normalize(Name) || {Name, _} <- List],
+ DepList = lists:foldl(fun(Profile, Acc) -> rebar_state:get(State, {deps, Profile}, []) ++ Acc end, [], Profiles),
+ LockList = lists:foldl(fun(Profile, Acc) -> rebar_state:get(State, {locks, Profile}, []) ++ Acc end, [], Profiles),
+ Deps = [normalize(name(Dep)) || Dep <- DepList++LockList],
lists:usort(Deps).
+name(App) when is_tuple(App) -> element(1, App);
+name(Name) when is_binary(Name); is_list(Name); is_atom(Name) -> Name.
+
normalize(AppName) when is_list(AppName) -> AppName;
normalize(AppName) when is_atom(AppName) -> atom_to_list(AppName);
normalize(AppName) when is_binary(AppName) -> binary_to_list(AppName).
diff --git a/src/rebar_prv_plugins_upgrade.erl b/src/rebar_prv_plugins_upgrade.erl
index 87f20df..03521c7 100644
--- a/src/rebar_prv_plugins_upgrade.erl
+++ b/src/rebar_prv_plugins_upgrade.erl
@@ -78,13 +78,14 @@ upgrade(Plugin, State) ->
find_plugin(Plugin, Profiles, State) ->
ec_lists:search(fun(Profile) ->
- Plugins = rebar_state:get(State, {plugins, Profile}, []),
- case rebar_utils:tup_find(list_to_atom(Plugin), Plugins) of
- false ->
- not_found;
- P ->
- {ok, P}
- end
+ Plugins = rebar_state:get(State, {plugins, Profile}, []) ++
+ rebar_state:get(State, {project_plugins, Profile}, []),
+ case rebar_utils:tup_find(list_to_atom(Plugin), Plugins) of
+ false ->
+ not_found;
+ P ->
+ {ok, P}
+ end
end, Profiles).
build_plugin(AppInfo, Apps, State) ->
diff --git a/src/rebar_prv_report.erl b/src/rebar_prv_report.erl
index 587fad7..d6c8b60 100644
--- a/src/rebar_prv_report.erl
+++ b/src/rebar_prv_report.erl
@@ -13,7 +13,7 @@
-define(PROVIDER, report).
-define(DEPS, []).
--define(ISSUES_URL, "https://github.com/rebar/rebar3/issues").
+-define(ISSUES_URL, "https://github.com/erlang/rebar3/issues").
%% ===================================================================
%% Public API
diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl
index ea759fc..0ede495 100644
--- a/src/rebar_prv_shell.erl
+++ b/src/rebar_prv_shell.erl
@@ -64,6 +64,8 @@ init(State) ->
"Gives a long name to the node."},
{sname, undefined, "sname", atom,
"Gives a short name to the node."},
+ {setcookie, undefined, "setcookie", atom,
+ "Sets the cookie if the node is distributed."},
{script_file, undefined, "script", string,
"Path to an escript file to run before "
"starting the project apps. Defaults to "
@@ -131,11 +133,25 @@ kill_old_user() ->
%% fully die
[P] = [P || P <- element(2,process_info(whereis(user), links)), is_port(P)],
user ! {'EXIT', P, normal}, % pretend the port died, then the port can die!
+ exit(P, kill),
+ wait_for_port_death(1000, P),
OldUser.
+wait_for_port_death(N, _) when N < 0 ->
+ %% This risks displaying a warning!
+ whatever;
+wait_for_port_death(N, P) ->
+ case erlang:port_info(P) of
+ undefined ->
+ ok;
+ _ ->
+ timer:sleep(10),
+ wait_for_port_death(N-10, P)
+ end.
+
setup_new_shell() ->
- %% terminate the current user supervision structure
- ok = supervisor:terminate_child(kernel_sup, user),
+ %% terminate the current user supervision structure, if any
+ _ = supervisor:terminate_child(kernel_sup, user),
%% start a new shell (this also starts a new user under the correct group)
_ = user_drv:start(),
%% wait until user_drv and user have been registered (max 3 seconds)
@@ -176,7 +192,9 @@ rewrite_leaders(OldUser, NewUser) ->
%% 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
- remove_error_handler(3)
+ remove_error_handler(3),
+ %% reset the tty handler once more for remote shells
+ error_logger:swap_handler(tty)
catch
E:R -> % may fail with custom loggers
?DEBUG("Logger changes failed for ~p:~p (~p)", [E,R,erlang:get_stacktrace()]),
@@ -253,23 +271,8 @@ simulate_proc_lib() ->
put('$initial_call', {rebar_agent, init, 1}).
setup_name(State) ->
- {Opts, _} = rebar_state:command_parsed_args(State),
- case {proplists:get_value(name, Opts), proplists:get_value(sname, Opts)} of
- {undefined, undefined} ->
- ok;
- {Name, undefined} ->
- check_epmd(net_kernel:start([Name, longnames]));
- {undefined, SName} ->
- check_epmd(net_kernel:start([SName, shortnames]));
- {_, _} ->
- ?ABORT("Cannot have both short and long node names defined", [])
- end.
-
-check_epmd({error,{{shutdown, {_,net_kernel,{'EXIT',nodistribution}}},_}}) ->
- ?ERROR("Erlang Distribution failed, falling back to nonode@nohost. "
- "Verify that epmd is running and try again.",[]);
-check_epmd(_) ->
- ok.
+ {Long, Short, Opts} = rebar_dist_utils:find_options(State),
+ rebar_dist_utils:either(Long, Short, Opts).
find_apps_to_boot(State) ->
%% Try the shell_apps option
@@ -327,7 +330,8 @@ reread_config(State) ->
ConfigList ->
try
[application:set_env(Application, Key, Val)
- || {Application, Items} <- ConfigList,
+ || Config <- ConfigList,
+ {Application, Items} <- Config,
{Key, Val} <- Items]
catch _:_ ->
?ERROR("The configuration file submitted could not be read "
@@ -390,7 +394,7 @@ add_test_paths(State) ->
ok.
% First try the --config flag, then try the relx sys_config
--spec find_config(rebar_state:t()) -> [tuple()] | no_config.
+-spec find_config(rebar_state:t()) -> [[tuple()]] | no_config.
find_config(State) ->
case first_value([fun find_config_option/1,
fun find_config_rebar/1,
@@ -438,11 +442,17 @@ find_config_relx(State) ->
debug_get_value(sys_config, rebar_state:get(State, relx, []), no_value,
"Found config from relx.").
--spec consult_config(rebar_state:t(), string()) -> [tuple()].
+-spec consult_config(rebar_state:t(), string()) -> [[tuple()]].
consult_config(State, Filename) ->
Fullpath = filename:join(rebar_dir:root_dir(State), Filename),
?DEBUG("Loading configuration from ~p", [Fullpath]),
- case rebar_file_utils:try_consult(Fullpath) of
+ Config = case rebar_file_utils:try_consult(Fullpath) of
[T] -> T;
[] -> []
- end.
+ end,
+ SubConfigs = [consult_config(State, Entry ++ ".config") ||
+ Entry <- Config, is_list(Entry)
+ ],
+
+ [Config | lists:merge(SubConfigs)].
+
diff --git a/src/rebar_prv_state.erl b/src/rebar_prv_state.erl
new file mode 100644
index 0000000..4fbcb67
--- /dev/null
+++ b/src/rebar_prv_state.erl
@@ -0,0 +1,44 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+
+-module(rebar_prv_state).
+
+-behaviour(provider).
+
+-export([init/1,
+ do/1,
+ format_error/1]).
+
+-include("rebar.hrl").
+
+-define(PROVIDER, state).
+-define(DEPS, []).
+
+%% ===================================================================
+%% Public API
+%% ===================================================================
+
+-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
+init(State) ->
+ Provider = providers:create(
+ [{name, ?PROVIDER},
+ {module, ?MODULE},
+ {bare, false},
+ {deps, ?DEPS},
+ {example, "rebar3 state"},
+ {short_desc, "Print current configuration state"},
+ {desc, "Display rebar configuration for debugging purpose"},
+ {opts, []}]),
+ State1 = rebar_state:add_provider(State, Provider),
+ {ok, State1}.
+
+-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
+do(State) ->
+ L = rebar_state:to_list(State),
+ ?CONSOLE("State:", []),
+ [?CONSOLE(" ~w: ~p", [K, V]) || {K,V} <- L],
+ {ok, State}.
+
+-spec format_error(any()) -> iolist().
+format_error(Reason) ->
+ io_lib:format("~p", [Reason]).
diff --git a/src/rebar_prv_unlock.erl b/src/rebar_prv_unlock.erl
index b049c92..7ff0d89 100644
--- a/src/rebar_prv_unlock.erl
+++ b/src/rebar_prv_unlock.erl
@@ -46,15 +46,14 @@ do(State) ->
{ok, State};
{error, Reason} ->
?PRV_ERROR({file,Reason});
- {ok, [Locks]} ->
+ {ok, _} ->
+ Locks = rebar_config:consult_lock_file(LockFile),
case handle_unlocks(State, Locks, LockFile) of
ok ->
{ok, State};
{error, Reason} ->
?PRV_ERROR({file,Reason})
- end;
- {ok, _Other} ->
- ?PRV_ERROR(unknown_lock_format)
+ end
end.
-spec format_error(any()) -> iolist().
@@ -74,7 +73,7 @@ handle_unlocks(State, Locks, LockFile) ->
_ when Names =:= [] -> % implicitly all locks
file:delete(LockFile);
NewLocks ->
- file:write_file(LockFile, io_lib:format("~p.~n", [NewLocks]))
+ rebar_config:write_lock_file(LockFile, NewLocks)
end.
parse_names(Bin) ->
diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl
index 0e3b9a0..5e1e253 100644
--- a/src/rebar_prv_update.erl
+++ b/src/rebar_prv_update.erl
@@ -11,6 +11,10 @@
-export([hex_to_index/1]).
+-ifdef(TEST).
+-export([cmp_/6, cmpl_/6, valid_vsn/1]).
+-endif.
+
-include("rebar.hrl").
-include_lib("providers/include/providers.hrl").
@@ -99,7 +103,7 @@ hex_to_index(State) ->
ets:foldl(fun({{Pkg, PkgVsn}, [Deps, Checksum, BuildTools | _]}, _) when is_list(BuildTools) ->
case lists:any(fun is_supported/1, BuildTools) of
true ->
- DepsList = update_deps_list(Deps, Registry, State),
+ DepsList = update_deps_list(Pkg, PkgVsn, Deps, Registry, State),
ets:insert(?PACKAGE_TABLE, {{Pkg, PkgVsn}, DepsList, Checksum});
false ->
true
@@ -137,20 +141,114 @@ hex_to_index(State) ->
fail
end.
-update_deps_list(Deps, HexRegistry, State) ->
+update_deps_list(Pkg, PkgVsn, Deps, HexRegistry, State) ->
lists:foldl(fun([Dep, DepVsn, false, _AppName | _], DepsListAcc) ->
- case DepVsn of
- <<"~> ", Vsn/binary>> ->
- case rebar_packages:find_highest_matching(Dep, Vsn, HexRegistry, State) of
- {ok, HighestDepVsn} ->
- [{Dep, HighestDepVsn} | DepsListAcc];
- none ->
- ?WARN("Missing registry entry for package ~s. Try to fix with `rebar3 update`", [Dep]),
- DepsListAcc
- end;
- Vsn ->
+ Dep1 = {Pkg, PkgVsn, Dep},
+ case {valid_vsn(DepVsn), DepVsn} of
+ %% Those are all not perfectly implemented!
+ %% and doubled since spaces seem not to be
+ %% enforced
+ {false, Vsn} ->
+ ?WARN("[~s:~s], Bad dependency version for ~s: ~s.",
+ [Pkg, PkgVsn, Dep, Vsn]),
+ DepsListAcc;
+ {_, <<"~>", Vsn/binary>>} ->
+ highest_matching(Dep1, rm_ws(Vsn), HexRegistry,
+ State, DepsListAcc);
+ {_, <<">=", Vsn/binary>>} ->
+ cmp(Dep1, rm_ws(Vsn), HexRegistry, State,
+ DepsListAcc, fun ec_semver:gte/2);
+ {_, <<">", Vsn/binary>>} ->
+ cmp(Dep1, rm_ws(Vsn), HexRegistry, State,
+ DepsListAcc, fun ec_semver:gt/2);
+ {_, <<"<=", Vsn/binary>>} ->
+ cmpl(Dep1, rm_ws(Vsn), HexRegistry, State,
+ DepsListAcc, fun ec_semver:lte/2);
+ {_, <<"<", Vsn/binary>>} ->
+ cmpl(Dep1, rm_ws(Vsn), HexRegistry, State,
+ DepsListAcc, fun ec_semver:lt/2);
+ {_, <<"==", Vsn/binary>>} ->
+ [{Dep, Vsn} | DepsListAcc];
+ {_, Vsn} ->
[{Dep, Vsn} | DepsListAcc]
end;
([_Dep, _DepVsn, true, _AppName | _], DepsListAcc) ->
DepsListAcc
end, [], Deps).
+
+rm_ws(<<" ", R/binary>>) ->
+ rm_ws(R);
+rm_ws(R) ->
+ R.
+
+valid_vsn(Vsn) ->
+ %% Regepx from https://github.com/sindresorhus/semver-regex/blob/master/index.js
+ SemVerRegExp = "v?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\.(0|[1-9][0-9]*))?"
+ "(-[0-9a-z-]+(\\.[0-9a-z-]+)*)?(\\+[0-9a-z-]+(\\.[0-9a-z-]+)*)?",
+ SupportedVersions = "^(>=?|<=?|~>|==)?\\s*" ++ SemVerRegExp ++ "$",
+ re:run(Vsn, SupportedVersions) =/= nomatch.
+
+highest_matching({Pkg, PkgVsn, Dep}, Vsn, HexRegistry, State, DepsListAcc) ->
+ case rebar_packages:find_highest_matching(Pkg, PkgVsn, Dep, Vsn, HexRegistry, State) of
+ {ok, HighestDepVsn} ->
+ [{Dep, HighestDepVsn} | DepsListAcc];
+ none ->
+ ?WARN("[~s:~s] Missing registry entry for package ~s. Try to fix with `rebar3 update`",
+ [Pkg, PkgVsn, Dep]),
+ DepsListAcc
+ end.
+
+cmp({_Pkg, _PkgVsn, Dep} = Dep1, Vsn, HexRegistry, State, DepsListAcc, CmpFun) ->
+ {ok, Vsns} = rebar_packages:find_all(Dep, HexRegistry, State),
+ cmp_(undefined, Vsn, Vsns, DepsListAcc, Dep1, CmpFun).
+
+
+cmp_(undefined, _MinVsn, [], DepsListAcc, {Pkg, PkgVsn, Dep}, _CmpFun) ->
+ ?WARN("[~s:~s] Missing registry entry for package ~s. Try to fix with `rebar3 update`",
+ [Pkg, PkgVsn, Dep]),
+ DepsListAcc;
+cmp_(HighestDepVsn, _MinVsn, [], DepsListAcc, {_Pkg, _PkgVsn, Dep}, _CmpFun) ->
+ [{Dep, HighestDepVsn} | DepsListAcc];
+
+cmp_(BestMatch, MinVsn, [Vsn | R], DepsListAcc, Dep, CmpFun) ->
+ case CmpFun(Vsn, MinVsn) of
+ true ->
+ cmp_(Vsn, Vsn, R, DepsListAcc, Dep, CmpFun);
+ false ->
+ cmp_(BestMatch, MinVsn, R, DepsListAcc, Dep, CmpFun)
+ end.
+
+%% We need to treat this differently since we want a version that is LOWER but
+%% the higest possible one.
+cmpl({_Pkg, _PkgVsn, Dep} = Dep1, Vsn, HexRegistry, State, DepsListAcc, CmpFun) ->
+ {ok, Vsns} = rebar_packages:find_all(Dep, HexRegistry, State),
+ cmpl_(undefined, Vsn, Vsns, DepsListAcc, Dep1, CmpFun).
+
+cmpl_(undefined, _MaxVsn, [], DepsListAcc, {Pkg, PkgVsn, Dep}, _CmpFun) ->
+ ?WARN("[~s:~s] Missing registry entry for package ~s. Try to fix with `rebar3 update`",
+ [Pkg, PkgVsn, Dep]),
+ DepsListAcc;
+
+cmpl_(HighestDepVsn, _MaxVsn, [], DepsListAcc, {_Pkg, _PkgVsn, Dep}, _CmpFun) ->
+ [{Dep, HighestDepVsn} | DepsListAcc];
+
+cmpl_(undefined, MaxVsn, [Vsn | R], DepsListAcc, Dep, CmpFun) ->
+ case CmpFun(Vsn, MaxVsn) of
+ true ->
+ cmpl_(Vsn, MaxVsn, R, DepsListAcc, Dep, CmpFun);
+ false ->
+ cmpl_(undefined, MaxVsn, R, DepsListAcc, Dep, CmpFun)
+ end;
+
+cmpl_(BestMatch, MaxVsn, [Vsn | R], DepsListAcc, Dep, CmpFun) ->
+ case CmpFun(Vsn, MaxVsn) of
+ true ->
+ case ec_semver:gte(Vsn, BestMatch) of
+ true ->
+ cmpl_(Vsn, MaxVsn, R, DepsListAcc, Dep, CmpFun);
+ false ->
+ cmpl_(BestMatch, MaxVsn, R, DepsListAcc, Dep, CmpFun)
+ end;
+ false ->
+ cmpl_(BestMatch, MaxVsn, R, DepsListAcc, Dep, CmpFun)
+ end.
diff --git a/src/rebar_prv_upgrade.erl b/src/rebar_prv_upgrade.erl
index a2864ab..c5c43e4 100644
--- a/src/rebar_prv_upgrade.erl
+++ b/src/rebar_prv_upgrade.erl
@@ -61,7 +61,7 @@ do(State) ->
State4 = rebar_state:set(State3, upgrade, true),
UpdatedLocks = [L || L <- rebar_state:lock(State4),
lists:keymember(rebar_app_info:name(L), 1, Locks0)],
- Res = rebar_prv_install_deps:do(rebar_state:lock(State4, UpdatedLocks)),
+ Res = rebar_prv_install_deps:do_(rebar_state:lock(State4, UpdatedLocks)),
case Res of
{ok, State5} ->
rebar_utils:info_useless(
diff --git a/src/rebar_relx.erl b/src/rebar_relx.erl
index 5d29258..5c653a3 100644
--- a/src/rebar_relx.erl
+++ b/src/rebar_relx.erl
@@ -14,6 +14,9 @@
-spec do(atom(), string(), atom(), rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(Module, Command, Provider, State) ->
+ %% We set the color mode for relx as a application env
+ application:set_env(relx, color_intensity, rebar_log:intensity()),
+ LogLevel = rebar_log:get_level(),
Options = rebar_state:command_args(State),
DepsDir = rebar_dir:deps_dir(State),
ProjectAppDirs = lists:delete(".", ?DEFAULT_PROJECT_APP_DIRS),
@@ -23,19 +26,21 @@ do(Module, Command, Provider, State) ->
AllOptions = string:join([Command | Options], " "),
Cwd = rebar_state:dir(State),
Providers = rebar_state:providers(State),
- rebar_hooks:run_all_hooks(Cwd, pre, Provider, Providers, State),
+ rebar_hooks:run_project_and_app_hooks(Cwd, pre, Provider, Providers, State),
try
case rebar_state:get(State, relx, []) of
[] ->
relx:main([{lib_dirs, LibDirs}
- ,{caller, api} | output_dir(OutputDir, Options)], AllOptions);
+ ,{caller, api}
+ ,{log_level, LogLevel} | output_dir(OutputDir, Options)], AllOptions);
Config ->
Config1 = merge_overlays(Config),
relx:main([{lib_dirs, LibDirs}
,{config, Config1}
- ,{caller, api} | output_dir(OutputDir, Options)], AllOptions)
+ ,{caller, api}
+ ,{log_level, LogLevel} | output_dir(OutputDir, Options)], AllOptions)
end,
- rebar_hooks:run_all_hooks(Cwd, post, Provider, Providers, State),
+ rebar_hooks:run_project_and_app_hooks(Cwd, post, Provider, Providers, State),
{ok, State}
catch
throw:T ->
diff --git a/src/rebar_state.erl b/src/rebar_state.erl
index 0c07b2a..a613a00 100644
--- a/src/rebar_state.erl
+++ b/src/rebar_state.erl
@@ -36,9 +36,12 @@
deps_names/1,
+ to_list/1,
resources/1, resources/2, add_resource/2,
- providers/1, providers/2, add_provider/2]).
+ providers/1, providers/2, add_provider/2,
+ allow_provider_overrides/1, allow_provider_overrides/2
+ ]).
-include("rebar.hrl").
-include_lib("providers/include/providers.hrl").
@@ -63,7 +66,8 @@
all_deps = [] :: [rebar_app_info:t()],
resources = [],
- providers = []}).
+ providers = [],
+ allow_provider_overrides = false :: boolean()}).
-export_type([t/0]).
@@ -103,7 +107,8 @@ new(ParentState, Config, Dir) ->
new(ParentState, Config, Deps, Dir) ->
Opts = ParentState#state_t.opts,
Plugins = proplists:get_value(plugins, Config, []),
- Terms = Deps++[{{plugins, default}, Plugins} | Config],
+ ProjectPlugins = proplists:get_value(project_plugins, Config, []),
+ Terms = Deps++[{{project_plugins, default}, ProjectPlugins}, {{plugins, default}, Plugins} | Config],
true = rebar_config:verify_config_format(Terms),
LocalOpts = dict:from_list(Terms),
@@ -115,13 +120,13 @@ new(ParentState, Config, Deps, Dir) ->
deps_from_config(Dir, Config) ->
case rebar_config:consult_lock_file(filename:join(Dir, ?LOCK_FILE)) of
- [D] ->
+ [] ->
+ [{{deps, default}, proplists:get_value(deps, Config, [])}];
+ D ->
%% We want the top level deps only from the lock file.
%% This ensures deterministic overrides for configs.
Deps = [X || X <- D, element(3, X) =:= 0],
- [{{locks, default}, D}, {{deps, default}, Deps}];
- _ ->
- [{{deps, default}, proplists:get_value(deps, Config, [])}]
+ [{{locks, default}, D}, {{deps, default}, Deps}]
end.
base_state() ->
@@ -136,7 +141,8 @@ base_state() ->
base_opts(Config) ->
Deps = proplists:get_value(deps, Config, []),
Plugins = proplists:get_value(plugins, Config, []),
- Terms = [{{deps, default}, Deps}, {{plugins, default}, Plugins} | Config],
+ ProjectPlugins = proplists:get_value(project_plugins, Config, []),
+ Terms = [{{deps, default}, Deps}, {{plugins, default}, Plugins}, {{project_plugins, default}, ProjectPlugins} | Config],
true = rebar_config:verify_config_format(Terms),
dict:from_list(Terms).
@@ -368,8 +374,16 @@ providers(#state_t{providers=Providers}) ->
providers(State, NewProviders) ->
State#state_t{providers=NewProviders}.
+allow_provider_overrides(#state_t{allow_provider_overrides=Allow}) ->
+ Allow.
+
+allow_provider_overrides(State, Allow) ->
+ State#state_t{allow_provider_overrides=Allow}.
+
-spec add_provider(t(), providers:t()) -> t().
-add_provider(State=#state_t{providers=Providers}, Provider) ->
+add_provider(State=#state_t{providers=Providers, allow_provider_overrides=true}, Provider) ->
+ State#state_t{providers=[Provider | Providers]};
+add_provider(State=#state_t{providers=Providers, allow_provider_overrides=false}, Provider) ->
Name = providers:impl(Provider),
Namespace = providers:namespace(Provider),
Module = providers:module(Provider),
@@ -406,6 +420,21 @@ create_logic_providers(ProviderModules, State0) ->
throw({error, "Failed creating providers. Run with DEBUG=1 for stacktrace."})
end.
+to_list(#state_t{} = State) ->
+ Fields = record_info(fields, state_t),
+ Values = tl(tuple_to_list(State)),
+ DictSz = tuple_size(dict:new()),
+ lists:zip(Fields, [reformat(I, DictSz) || I <- Values]).
+
+reformat({K,V}, DSz) when is_list(V) ->
+ {K, [reformat(I, DSz) || I <- V]};
+reformat(V, DSz) when is_tuple(V), element(1,V) =:= dict, tuple_size(V) =:= DSz ->
+ [reformat(I, DSz) || I <- dict:to_list(V)];
+reformat({K,V}, DSz) when is_tuple(V), element(1,V) =:= dict, tuple_size(V) =:= DSz ->
+ {K, [reformat(I, DSz) || I <- dict:to_list(V)]};
+reformat(Other, _DSz) ->
+ Other.
+
%% ===================================================================
%% Internal functions
%% ===================================================================
diff --git a/test/mock_git_resource.erl b/test/mock_git_resource.erl
index 0f4aff6..e922af3 100644
--- a/test/mock_git_resource.erl
+++ b/test/mock_git_resource.erl
@@ -1,6 +1,6 @@
%%% Mock a git resource and create an app magically for each URL submitted.
-module(mock_git_resource).
--export([mock/0, mock/1, unmock/0]).
+-export([mock/0, mock/1, mock/2, unmock/0]).
-define(MOD, rebar_git_resource).
%%%%%%%%%%%%%%%%%
@@ -24,11 +24,14 @@ mock() -> mock([]).
| {pkg, App, term()},
Vsn :: string().
mock(Opts) ->
+ mock(Opts, create_app).
+
+mock(Opts, CreateType) ->
meck:new(?MOD, [no_link]),
mock_lock(Opts),
mock_update(Opts),
mock_vsn(Opts),
- mock_download(Opts),
+ mock_download(Opts, CreateType),
ok.
unmock() ->
@@ -98,7 +101,7 @@ mock_vsn(Opts) ->
%% `{deps, [{"app1", [{app2, ".*", {git, ...}}]}]}' -- basically
%% the `deps' option takes a key/value list of terms to output directly
%% into a `rebar.config' file to describe dependencies.
-mock_download(Opts) ->
+mock_download(Opts, CreateType) ->
Deps = proplists:get_value(deps, Opts, []),
Config = proplists:get_value(config, Opts, []),
Default = proplists:get_value(default_vsn, Opts, "0.0.0"),
@@ -110,7 +113,7 @@ mock_download(Opts) ->
{git, Url, {_, Vsn}} = normalize_git(Git, Overrides, Default),
App = app(Url),
AppDeps = proplists:get_value({App,Vsn}, Deps, []),
- rebar_test_utils:create_app(
+ rebar_test_utils:CreateType(
Dir, App, Vsn,
[kernel, stdlib] ++ [element(1,D) || D <- AppDeps]
),
diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl
index 94ab690..1da7571 100644
--- a/test/rebar_ct_SUITE.erl
+++ b/test/rebar_ct_SUITE.erl
@@ -6,8 +6,10 @@
end_per_group/2]).
-export([basic_app_default_dirs/1,
basic_app_default_beams/1,
+ basic_app_ct_macro/1,
multi_app_default_dirs/1,
multi_app_default_beams/1,
+ multi_app_ct_macro/1,
single_app_dir/1,
single_extra_dir/1,
single_unmanaged_dir/1,
@@ -38,9 +40,11 @@
cmd_multiply_timetraps/1,
cmd_scale_timetraps/1,
cmd_create_priv_dir/1,
+ cmd_include_dir/1,
cfg_opts/1,
cfg_arbitrary_opts/1,
cfg_test_spec/1,
+ cfg_cover_spec/1,
cfg_atom_suites/1,
cover_compiled/1,
misspecified_ct_opts/1,
@@ -56,16 +60,18 @@ all() -> [{group, basic_app},
{group, ct_opts},
{group, cover},
cfg_opts, cfg_arbitrary_opts,
- cfg_test_spec,
+ cfg_test_spec, cfg_cover_spec,
cfg_atom_suites,
misspecified_ct_opts,
misspecified_ct_compile_opts,
misspecified_ct_first_files].
groups() -> [{basic_app, [], [basic_app_default_dirs,
- basic_app_default_beams]},
+ basic_app_default_beams,
+ basic_app_ct_macro]},
{multi_app, [], [multi_app_default_dirs,
- multi_app_default_beams]},
+ multi_app_default_beams,
+ multi_app_ct_macro]},
{dirs_and_suites, [], [single_app_dir,
single_extra_dir,
single_unmanaged_dir,
@@ -95,7 +101,8 @@ groups() -> [{basic_app, [], [basic_app_default_dirs,
cmd_abort_if_missing_suites,
cmd_multiply_timetraps,
cmd_scale_timetraps,
- cmd_create_priv_dir]},
+ cmd_create_priv_dir,
+ cmd_include_dir]},
{cover, [], [cover_compiled]}].
init_per_group(basic_app, Config) ->
@@ -118,7 +125,7 @@ init_per_group(basic_app, Config) ->
{ok, T} = Tests,
Opts = rebar_prv_common_test:translate_paths(NewState, T),
- [{result, Opts}, {appnames, [Name]}|C];
+ [{result, Opts}, {appnames, [Name]}, {compile_state, NewState}|C];
init_per_group(multi_app, Config) ->
C = rebar_test_utils:init_rebar_state(Config, "ct_"),
@@ -153,7 +160,7 @@ init_per_group(multi_app, Config) ->
{ok, T} = Tests,
Opts = rebar_prv_common_test:translate_paths(NewState, T),
- [{result, Opts}, {appnames, [Name1, Name2]}|C];
+ [{result, Opts}, {appnames, [Name1, Name2]}, {compile_state, NewState}|C];
init_per_group(dirs_and_suites, Config) ->
C = rebar_test_utils:init_rebar_state(Config, "ct_"),
@@ -211,7 +218,7 @@ init_per_group(ct_opts, Config) ->
{ok, State} = rebar_test_utils:run_and_check(C, [], ["as", "test", "lock"], return),
- [{result, State}|C];
+ [{result, State}, {name, Name}|C];
init_per_group(cover, Config) ->
C = rebar_test_utils:init_rebar_state(Config, "ct_opts"),
@@ -252,6 +259,15 @@ basic_app_default_beams(Config) ->
true = filelib:is_file(File).
+basic_app_ct_macro(Config) ->
+ State = ?config(compile_state, Config),
+
+ [App] = rebar_state:project_apps(State),
+ Opts = rebar_app_info:opts(App),
+ ErlOpts = dict:fetch(erl_opts, Opts),
+ true = lists:member({d, 'COMMON_TEST'}, ErlOpts).
+
+
multi_app_default_dirs(Config) ->
AppDir = ?config(apps, Config),
[Name1, Name2] = ?config(appnames, Config),
@@ -293,6 +309,16 @@ multi_app_default_beams(Config) ->
true = filelib:is_file(File2),
true = filelib:is_file(File3).
+multi_app_ct_macro(Config) ->
+ State = ?config(compile_state, Config),
+
+ Apps = rebar_state:project_apps(State),
+ lists:foreach(fun(App) ->
+ Opts = rebar_app_info:opts(App),
+ ErlOpts = dict:fetch(erl_opts, Opts),
+ true = lists:member({d, 'COMMON_TEST'}, ErlOpts)
+ end, Apps).
+
single_app_dir(Config) ->
AppDir = ?config(apps, Config),
[Name1, _Name2] = ?config(appnames, Config),
@@ -700,7 +726,6 @@ suite_at_app_root(Config) ->
data_dir_correct(Config) ->
DataDir = ?config(data_dir, Config),
Parts = filename:split(DataDir),
- ct:pal(Parts),
["rebar_ct_SUITE_data","test","rebar","lib","test","_build"|_] = lists:reverse(Parts).
cmd_label(Config) ->
@@ -973,6 +998,29 @@ cmd_create_priv_dir(Config) ->
true = lists:member({create_priv_dir, manual_per_tc}, TestOpts).
+cmd_include_dir(Config) ->
+ State = ?config(result, Config),
+ AppDir = ?config(apps, Config),
+
+ Providers = rebar_state:providers(State),
+ Namespace = rebar_state:namespace(State),
+ CommandProvider = providers:get_provider(ct, Providers, Namespace),
+ GetOptSpec = providers:opts(CommandProvider),
+ {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--include=foo/bar/baz,qux"]),
+
+ NewState = rebar_state:command_parsed_args(State, GetOptResult),
+
+ Tests = rebar_prv_common_test:prepare_tests(NewState),
+ {ok, _} = rebar_prv_common_test:compile(NewState, Tests),
+
+ Name = ?config(name, Config),
+ Beam = filename:join([AppDir, "_build", "test", "lib", Name, "ebin", Name ++ ".beam"]),
+
+ {ok, {_, [{compile_info, Info}]}} = beam_lib:chunks(Beam, [compile_info]),
+ CompileOpts = proplists:get_value(options, Info),
+ true = lists:member({i, "foo/bar/baz"}, CompileOpts),
+ true = lists:member({i, "qux"}, CompileOpts).
+
cfg_opts(Config) ->
C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_opts_"),
@@ -1020,13 +1068,30 @@ cfg_test_spec(Config) ->
Vsn = rebar_test_utils:create_random_vsn(),
rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),
- RebarConfig = [{ct_opts, [{test_spec, "spec/foo.spec"}]}],
+ RebarConfig = [{ct_opts, [Opt = {test_spec, "spec/foo.spec"}]}],
{ok, State} = rebar_test_utils:run_and_check(C, RebarConfig, ["as", "test", "lock"], return),
- {error, {rebar_prv_common_test, Error}} = rebar_prv_common_test:prepare_tests(State),
+ {ok, TestOpts} = rebar_prv_common_test:prepare_tests(State),
+
+ false = lists:member(Opt, TestOpts).
+
+cfg_cover_spec(Config) ->
+ C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_cover_spec_opts_"),
+
+ AppDir = ?config(apps, C),
- {badconfig, "Test specs not supported"} = Error.
+ Name = rebar_test_utils:create_random_name("ct_cfg_cover_spec_opts_"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),
+
+ RebarConfig = [{ct_opts, [Opt = {cover, "spec/foo.spec"}]}],
+
+ {ok, State} = rebar_test_utils:run_and_check(C, RebarConfig, ["as", "test", "lock"], return),
+
+ {ok, TestOpts} = rebar_prv_common_test:prepare_tests(State),
+
+ false = lists:member(Opt, TestOpts).
cfg_atom_suites(Config) ->
C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_atom_suites_"),
@@ -1116,9 +1181,10 @@ misspecified_ct_first_files(Config) ->
{badconfig, {"Value `~p' of option `~p' must be a list", {some_file, ct_first_files}}} = Error.
+
%% helper for generating test data
test_suite(Name) ->
io_lib:format("-module(~ts_SUITE).\n"
"-compile(export_all).\n"
"all() -> [some_test].\n"
- "some_test(_) -> ok.\n", [Name]). \ No newline at end of file
+ "some_test(_) -> ok.\n", [Name]).
diff --git a/test/rebar_deps_SUITE.erl b/test/rebar_deps_SUITE.erl
index fcc46c3..c95854a 100644
--- a/test/rebar_deps_SUITE.erl
+++ b/test/rebar_deps_SUITE.erl
@@ -3,7 +3,7 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
-all() -> [sub_app_deps, newly_added_dep, newly_added_after_empty_lock, http_proxy_settings, https_proxy_settings, {group, git}, {group, pkg}].
+all() -> [sub_app_deps, newly_added_dep, newly_added_after_empty_lock, http_proxy_settings, https_proxy_settings, semver_matching_lt, semver_matching_lte, semver_matching_gt, valid_version, {group, git}, {group, pkg}].
groups() ->
[{all, [], [flat, pick_highest_left, pick_highest_right,
@@ -29,6 +29,14 @@ init_per_group(_, Config) ->
end_per_group(_, Config) ->
Config.
+init_per_testcase(valid_version, Config) ->
+ rebar_test_utils:init_rebar_state(Config);
+init_per_testcase(semver_matching_lt, Config) ->
+ rebar_test_utils:init_rebar_state(Config);
+init_per_testcase(semver_matching_lte, Config) ->
+ rebar_test_utils:init_rebar_state(Config);
+init_per_testcase(semver_matching_gt, Config) ->
+ rebar_test_utils:init_rebar_state(Config);
init_per_testcase(newly_added_after_empty_lock, Config) ->
rebar_test_utils:init_rebar_state(Config);
init_per_testcase(newly_added_dep, Config) ->
@@ -49,14 +57,14 @@ init_per_testcase(http_proxy_settings, Config) ->
%% Insert proxy variables into config
rebar_test_utils:create_config(GlobalConfigDir,
[{http_proxy, "http://localhost:1234"}
- ]),
+ ]),
rebar_test_utils:init_rebar_state(Config);
init_per_testcase(https_proxy_settings, Config) ->
SupportsHttpsProxy = case erlang:system_info(otp_release) of
- "R16"++_ -> true;
- "R"++_ -> false;
- _ -> true % 17 and up don't have a "R" in the version
- end,
+ "R16"++_ -> true;
+ "R"++_ -> false;
+ _ -> true % 17 and up don't have a "R" in the version
+ end,
if not SupportsHttpsProxy ->
{skip, https_proxy_unsupported_before_R16};
SupportsHttpsProxy ->
@@ -73,20 +81,20 @@ init_per_testcase(https_proxy_settings, Config) ->
%% Insert proxy variables into config
rebar_test_utils:create_config(GlobalConfigDir,
[{https_proxy, "http://localhost:1234"}
- ]),
+ ]),
rebar_test_utils:init_rebar_state(Config)
end;
init_per_testcase(Case, Config) ->
{Deps, Warnings, Expect} = deps(Case),
Expected = case Expect of
- {ok, List} -> {ok, format_expected_deps(List)};
- {error, Reason} -> {error, Reason}
- end,
+ {ok, List} -> {ok, format_expected_deps(List)};
+ {error, Reason} -> {error, Reason}
+ end,
DepsType = ?config(deps_type, Config),
mock_warnings(),
[{expect, Expected},
{warnings, Warnings}
- | setup_project(Case, Config, rebar_test_utils:expand_deps(DepsType, Deps))].
+ | setup_project(Case, Config, rebar_test_utils:expand_deps(DepsType, Deps))].
end_per_testcase(https_proxy_settings, Config) ->
meck:unload(rebar_dir),
@@ -100,8 +108,8 @@ end_per_testcase(_, Config) ->
format_expected_deps(Deps) ->
[case Dep of
- {N,V} -> {dep, N, V};
- N -> {dep, N}
+ {N,V} -> {dep, N, V};
+ N -> {dep, N}
end || Dep <- Deps].
%% format:
@@ -208,7 +216,7 @@ sub_app_deps(Config) ->
SubAppsDir = filename:join([AppDir, "apps", Name]),
SubDeps = rebar_test_utils:top_level_deps(rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}
- ,{"b", "2.0.0", []}])),
+ ,{"b", "2.0.0", []}])),
rebar_test_utils:create_app(SubAppsDir, Name, Vsn, [kernel, stdlib]),
rebar_test_utils:create_config(SubAppsDir, [{deps, SubDeps}]),
@@ -242,12 +250,12 @@ newly_added_dep(Config) ->
%% Add a and c to top level
TopDeps2 = rebar_test_utils:top_level_deps(rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []}
- ,{"c", "2.0.0", []}
- ,{"b", "1.0.0", []}])),
+ ,{"c", "2.0.0", []}
+ ,{"b", "1.0.0", []}])),
{ok, RebarConfig2} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, TopDeps2}])),
LockFile = filename:join(AppDir, "rebar.lock"),
RebarConfig3 = rebar_config:merge_locks(RebarConfig2,
- rebar_config:consult_lock_file(LockFile)),
+ rebar_config:consult_lock_file(LockFile)),
%% a should now be installed and c should not change
rebar_test_utils:run_and_check(
@@ -277,7 +285,7 @@ newly_added_after_empty_lock(Config) ->
{ok, RebarConfig2} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, TopDeps2}])),
LockFile = filename:join(AppDir, "rebar.lock"),
RebarConfig3 = rebar_config:merge_locks(RebarConfig2,
- rebar_config:consult_lock_file(LockFile)),
+ rebar_config:consult_lock_file(LockFile)),
%% a should now be installed and c should not change
rebar_test_utils:run_and_check(
@@ -304,6 +312,74 @@ https_proxy_settings(_Config) ->
httpc:get_option(https_proxy, rebar)).
+semver_matching_lt(_Config) ->
+ Dep = <<"test">>,
+ Dep1 = {Dep, <<"1.0.0">>, Dep},
+ MaxVsn = <<"0.2.0">>,
+ Vsns = [<<"0.1.7">>, <<"0.1.9">>, <<"0.1.8">>, <<"0.2.0">>, <<"0.2.1">>],
+ ?assertEqual([{Dep, <<"0.1.9">>}],
+ rebar_prv_update:cmpl_(undefined, MaxVsn, Vsns, [], Dep1,
+ fun ec_semver:lt/2)).
+
+semver_matching_lte(_Config) ->
+ Dep = <<"test">>,
+ Dep1 = {Dep, <<"1.0.0">>, Dep},
+ MaxVsn = <<"0.2.0">>,
+ Vsns = [<<"0.1.7">>, <<"0.1.9">>, <<"0.1.8">>, <<"0.2.0">>, <<"0.2.1">>],
+ ?assertEqual([{Dep, <<"0.2.0">>}],
+ rebar_prv_update:cmpl_(undefined, MaxVsn, Vsns, [], Dep1,
+ fun ec_semver:lte/2)).
+
+semver_matching_gt(_Config) ->
+ Dep = <<"test">>,
+ Dep1 = {Dep, <<"1.0.0">>, Dep},
+ MaxVsn = <<"0.2.0">>,
+ Vsns = [<<"0.1.7">>, <<"0.1.9">>, <<"0.1.8">>, <<"0.2.0">>, <<"0.2.1">>],
+ ?assertEqual([{Dep, <<"0.2.1">>}],
+ rebar_prv_update:cmp_(undefined, MaxVsn, Vsns, [], Dep1,
+ fun ec_semver:gt/2)).
+semver_matching_gte(_Config) ->
+ Dep = <<"test">>,
+ Dep1 = {Dep, <<"1.0.0">>, Dep},
+ MaxVsn = <<"0.2.0">>,
+ Vsns = [<<"0.1.7">>, <<"0.1.9">>, <<"0.1.8">>, <<"0.2.0">>],
+ ?assertEqual([{Dep, <<"0.2.0">>}],
+ rebar_prv_update:cmp_(undefined, MaxVsn, Vsns, [], Dep1,
+ fun ec_semver:gt/2)).
+
+valid_version(_Config) ->
+ ?assert(rebar_prv_update:valid_vsn(<<"0.1">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<" 0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<" 0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"<0.1">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"<0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"< 0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"< 0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<">0.1">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<">0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"> 0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"> 0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"<=0.1">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"<=0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"<= 0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"<= 0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<">=0.1">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<">=0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<">= 0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<">= 0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"==0.1">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"==0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"== 0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"== 0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"~>0.1">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"~>0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"~> 0.1.0">>)),
+ ?assert(rebar_prv_update:valid_vsn(<<"~> 0.1.0">>)),
+ ?assertNot(rebar_prv_update:valid_vsn(<<"> 0.1.0 and < 0.2.0">>)),
+ ok.
+
+
run(Config) ->
{ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
rebar_test_utils:run_and_check(
diff --git a/test/rebar_dir_SUITE.erl b/test/rebar_dir_SUITE.erl
index 1221db7..9734830 100644
--- a/test/rebar_dir_SUITE.erl
+++ b/test/rebar_dir_SUITE.erl
@@ -6,6 +6,7 @@
-export([src_dirs/1, extra_src_dirs/1, all_src_dirs/1]).
-export([profile_src_dirs/1, profile_extra_src_dirs/1, profile_all_src_dirs/1]).
-export([retarget_path/1, alt_base_dir_abs/1, alt_base_dir_rel/1]).
+-export([global_cache_dir/1, default_global_cache_dir/1, overwrite_default_global_cache_dir/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -15,8 +16,20 @@
all() -> [default_src_dirs, default_extra_src_dirs, default_all_src_dirs,
src_dirs, extra_src_dirs, all_src_dirs,
profile_src_dirs, profile_extra_src_dirs, profile_all_src_dirs,
- retarget_path, alt_base_dir_abs, alt_base_dir_rel].
-
+ retarget_path, alt_base_dir_abs, alt_base_dir_rel, global_cache_dir,
+ default_global_cache_dir, overwrite_default_global_cache_dir].
+
+init_per_testcase(default_global_cache_dir, Config) ->
+ [{apps, AppsDir}, {checkouts, CheckoutsDir}, {state, _State} | Config] = rebar_test_utils:init_rebar_state(Config),
+ NewState = rebar_state:new([{base_dir, filename:join([AppsDir, "_build"])}
+ ,{root_dir, AppsDir}]),
+ [{apps, AppsDir}, {checkouts, CheckoutsDir}, {state, NewState} | Config];
+init_per_testcase(overwrite_default_global_cache_dir, Config) ->
+ os:putenv("REBAR_CACHE_DIR", ?config(priv_dir, Config)),
+ [{apps, AppsDir}, {checkouts, CheckoutsDir}, {state, _State} | Config] = rebar_test_utils:init_rebar_state(Config),
+ NewState = rebar_state:new([{base_dir, filename:join([AppsDir, "_build"])}
+ ,{root_dir, AppsDir}]),
+ [{apps, AppsDir}, {checkouts, CheckoutsDir}, {state, NewState} | Config];
init_per_testcase(_, Config) ->
C = rebar_test_utils:init_rebar_state(Config),
AppDir = ?config(apps, C),
@@ -162,3 +175,22 @@ alt_base_dir_rel(Config) ->
?assert(filelib:is_dir(filename:join([BaseDir, "lib", Name2, "ebin"]))),
?assert(filelib:is_file(filename:join([BaseDir, "lib", Name2, "ebin", Name2++".app"]))),
?assert(filelib:is_file(filename:join([BaseDir, "lib", Name2, "ebin", Name2++".beam"]))).
+
+global_cache_dir(Config) ->
+ RebarConfig = [{erl_opts, []}],
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return),
+ DataDir = ?config(priv_dir, Config),
+ Expected = filename:join([DataDir, "cache"]),
+ ?assertEqual(Expected, rebar_dir:global_cache_dir(rebar_state:opts(State))).
+
+default_global_cache_dir(Config) ->
+ RebarConfig = [{erl_opts, []}],
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return),
+ Expected = filename:join([rebar_dir:home_dir(), ".cache", "rebar3"]),
+ ?assertEqual(Expected, rebar_dir:global_cache_dir(rebar_state:opts(State))).
+
+overwrite_default_global_cache_dir(Config) ->
+ RebarConfig = [{erl_opts, []}],
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return),
+ Expected = ?config(priv_dir, Config),
+ ?assertEqual(Expected, rebar_dir:global_cache_dir(rebar_state:opts(State))).
diff --git a/test/rebar_dist_utils_SUITE.erl b/test/rebar_dist_utils_SUITE.erl
new file mode 100644
index 0000000..e190b94
--- /dev/null
+++ b/test/rebar_dist_utils_SUITE.erl
@@ -0,0 +1,74 @@
+%%% This suite currently only tests for options parsing since we do
+%%% not know if epmd will be running to actually boot nodes.
+-module(rebar_dist_utils_SUITE).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+-compile(export_all).
+
+all() -> [from_config, from_cli, overlap, from_config_profile].
+
+init_per_testcase(_, Config0) ->
+ Config = rebar_test_utils:init_rebar_state(Config0),
+ AppDir = ?config(apps, Config),
+ Name = rebar_test_utils:create_random_name("app_"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ rebar_test_utils:create_app(filename:join([AppDir,"apps",Name]), Name, Vsn, [kernel, stdlib]),
+ Config.
+
+
+end_per_testcase(_, _) ->
+ ok.
+
+from_config(Config) ->
+ ShortConfig = [{dist_node, [{sname, 'a@localhost'}, {setcookie, abc}]}],
+ LongConfig = [{dist_node, [{name, 'a@localhost.x'}, {setcookie, abc}]}],
+ BothConfig = [{dist_node, [{sname, 'a@localhost'}, {name, 'a@localhost.x'}, {setcookie,abc}]}],
+ NoConfig = [],
+ CookieConfig = [{dist_node, [{setcookie, def}]}],
+ NoCookie = [{dist_node, [{sname, 'a@localhost'}]}],
+ {ok, State0} = rebar_test_utils:run_and_check(Config, ShortConfig, ["version"], return),
+ {undefined, 'a@localhost', [{setcookie, abc}]} = rebar_dist_utils:find_options(State0),
+ {ok, State1} = rebar_test_utils:run_and_check(Config, LongConfig, ["version"], return),
+ {'a@localhost.x', undefined, [{setcookie, abc}]} = rebar_dist_utils:find_options(State1),
+ %% only support the first name found, side-effect of wanting profile support
+ {ok, State2} = rebar_test_utils:run_and_check(Config, BothConfig, ["version"], return),
+ {undefined, 'a@localhost', [{setcookie, abc}]} = rebar_dist_utils:find_options(State2),
+ {ok, State3} = rebar_test_utils:run_and_check(Config, NoConfig, ["version"], return),
+ {undefined, undefined, []} = rebar_dist_utils:find_options(State3),
+ {ok, State4} = rebar_test_utils:run_and_check(Config, CookieConfig, ["version"], return),
+ {undefined, undefined, [{setcookie, def}]} = rebar_dist_utils:find_options(State4),
+ {ok, State5} = rebar_test_utils:run_and_check(Config, NoCookie, ["version"], return),
+ {undefined, 'a@localhost', []} = rebar_dist_utils:find_options(State5),
+ ok.
+
+from_cli(Config) ->
+ {ok, State0} = rebar_test_utils:run_and_check(Config, [], ["version"], return),
+ {undefined, undefined, []} = rebar_dist_utils:find_options(State0),
+ State1 = rebar_state:command_parsed_args(State0, {[{sname, 'a@localhost'}, {setcookie,abc}], []}),
+ {undefined, 'a@localhost', [{setcookie, abc}]} = rebar_dist_utils:find_options(State1),
+ State2 = rebar_state:command_parsed_args(State0, {[{name, 'a@localhost.x'}, {setcookie,abc}], []}),
+ {'a@localhost.x', undefined, [{setcookie, abc}]} = rebar_dist_utils:find_options(State2),
+ State3 = rebar_state:command_parsed_args(State0, {[{sname, 'a@localhost'}, {name, 'a@localhost.x'}, {setcookie,abc}], []}),
+ {'a@localhost.x', 'a@localhost', [{setcookie, abc}]} = rebar_dist_utils:find_options(State3),
+ State4 = rebar_state:command_parsed_args(State0, {[{setcookie,def}], []}),
+ {undefined, undefined, [{setcookie, def}]} = rebar_dist_utils:find_options(State4),
+ State5 = rebar_state:command_parsed_args(State0, {[{sname, 'a@localhost'}], []}),
+ {undefined, 'a@localhost', []} = rebar_dist_utils:find_options(State5),
+ ok.
+
+overlap(Config) ->
+ %% Make sure that CLI config takes over rebar config without clash for names, though
+ %% cookies can pass through
+ RebarConfig = [{dist_node, [{sname, 'a@localhost'}, {setcookie, abc}]}],
+ {ok, State0} = rebar_test_utils:run_and_check(Config, RebarConfig, ["version"], return),
+ State1 = rebar_state:command_parsed_args(State0, {[{name, 'b@localhost.x'}], []}),
+ {'b@localhost.x', undefined, [{setcookie, abc}]} = rebar_dist_utils:find_options(State1),
+ ok.
+
+from_config_profile(Config) ->
+ %% running as a profile does not create name clashes
+ RebarConfig = [{dist_node, [{sname, 'a@localhost'}, {setcookie, abc}]},
+ {profiles, [ {fake, [{dist_node, [{name, 'a@localhost.x'}]}]} ]}],
+ {ok, State0} = rebar_test_utils:run_and_check(Config, RebarConfig, ["as","fake","version"], return),
+ {'a@localhost.x', undefined, [{setcookie, abc}]} = rebar_dist_utils:find_options(State0),
+ ok.
diff --git a/test/rebar_eunit_SUITE.erl b/test/rebar_eunit_SUITE.erl
index cb2c911..41ab6ff 100644
--- a/test/rebar_eunit_SUITE.erl
+++ b/test/rebar_eunit_SUITE.erl
@@ -2,8 +2,12 @@
-export([all/0, groups/0]).
-export([init_per_suite/1, init_per_group/2, end_per_group/2]).
--export([basic_app_compiles/1, basic_app_files/1, basic_app_exports/1, basic_app_testset/1]).
--export([multi_app_compiles/1, multi_app_files/1, multi_app_exports/1, multi_app_testset/1]).
+-export([basic_app_compiles/1, basic_app_files/1]).
+-export([basic_app_exports/1, basic_app_testset/1]).
+-export([basic_app_eunit_macro/1]).
+-export([multi_app_compiles/1, multi_app_files/1]).
+-export([multi_app_exports/1, multi_app_testset/1]).
+-export([multi_app_eunit_macro/1]).
-export([eunit_tests/1, eunit_opts/1, eunit_first_files/1]).
-export([single_application_arg/1, multi_application_arg/1, missing_application_arg/1]).
-export([single_module_arg/1, multi_module_arg/1, missing_module_arg/1]).
@@ -27,9 +31,15 @@ all() ->
groups() ->
[{basic_app, [sequence], [basic_app_compiles, {group, basic_app_results}]},
- {basic_app_results, [], [basic_app_files, basic_app_exports, basic_app_testset]},
+ {basic_app_results, [], [basic_app_files,
+ basic_app_exports,
+ basic_app_testset,
+ basic_app_eunit_macro]},
{multi_app, [sequence], [multi_app_compiles, {group, multi_app_results}]},
- {multi_app_results, [], [multi_app_files, multi_app_exports, multi_app_testset]},
+ {multi_app_results, [], [multi_app_files,
+ multi_app_exports,
+ multi_app_testset,
+ multi_app_eunit_macro]},
{cmd_line_args, [], [eunit_tests, eunit_opts, eunit_first_files,
single_application_arg, multi_application_arg, missing_application_arg,
single_module_arg, multi_module_arg, missing_module_arg,
@@ -160,7 +170,16 @@ basic_app_testset(Config) ->
{module, basic_app_tests_helper}]},
Set = rebar_prv_eunit:prepare_tests(Result).
-
+basic_app_eunit_macro(_Config) ->
+ Macro = fun(Mod) ->
+ begin
+ Path = code:which(Mod),
+ {ok, {Mod, [{compile_info, CompileInfo}]}} = beam_lib:chunks(Path, [compile_info]),
+ Opts = proplists:get_value(options, CompileInfo, []),
+ true = lists:member({d, 'EUNIT'}, Opts)
+ end
+ end,
+ lists:foreach(Macro, [basic_app, basic_app_tests, basic_app_tests_helper]).
%% === tests for multiple applications in the `apps' directory of a project ===
@@ -220,7 +239,19 @@ multi_app_testset(Config) ->
{module, multi_app_tests_helper}]},
Set = rebar_prv_eunit:prepare_tests(Result).
-
+multi_app_eunit_macro(_Config) ->
+ Macro = fun(Mod) ->
+ begin
+ Path = code:which(Mod),
+ {ok, {Mod, [{compile_info, CompileInfo}]}} = beam_lib:chunks(Path, [compile_info]),
+ Opts = proplists:get_value(options, CompileInfo, []),
+ true = lists:member({d, 'EUNIT'}, Opts)
+ end
+ end,
+ lists:foreach(Macro, [multi_app_bar, multi_app_bar_tests,
+ multi_app_baz, multi_app_baz_tests,
+ multi_app_tests, multi_app_tests_helper,
+ multi_app_bar_tests_helper, multi_app_baz_tests_helper]).
%% === tests for command line arguments ===
diff --git a/test/rebar_hooks_SUITE.erl b/test/rebar_hooks_SUITE.erl
index 188fb34..b121dd5 100644
--- a/test/rebar_hooks_SUITE.erl
+++ b/test/rebar_hooks_SUITE.erl
@@ -10,6 +10,7 @@
escriptize_artifacts/1,
run_hooks_once/1,
run_hooks_for_plugins/1,
+ eunit_app_hooks/1,
deps_hook_namespace/1]).
-include_lib("common_test/include/ct.hrl").
@@ -33,7 +34,7 @@ end_per_testcase(_, _Config) ->
all() ->
[build_and_clean_app, run_hooks_once, escriptize_artifacts,
- run_hooks_for_plugins, deps_hook_namespace].
+ run_hooks_for_plugins, deps_hook_namespace, eunit_app_hooks].
%% Test post provider hook cleans compiled project app, leaving it invalid
build_and_clean_app(Config) ->
@@ -119,6 +120,25 @@ deps_hook_namespace(Config) ->
{ok, [{dep, "some_dep"}]}
).
+%% Checks that a hook that is defined on an app (not a top level hook of a project with subapps) is run
+eunit_app_hooks(Config) ->
+ AppDir = ?config(apps, Config),
+ Name = rebar_test_utils:create_random_name("app1_"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),
+
+ RConfFile =
+ rebar_test_utils:create_config(AppDir,
+ [
+ {escript_name, list_to_atom(Name)}
+ ,{provider_hooks, [{post, [{eunit, escriptize}]}]}
+ ]),
+ {ok, RConf} = file:consult(RConfFile),
+
+ rebar_test_utils:run_and_check(Config, RConf,
+ ["eunit"], {ok, [{app, Name, valid}
+ ,{file, filename:join([AppDir, "_build/test/bin", Name])}]}).
+
run_hooks_for_plugins(Config) ->
AppDir = ?config(apps, Config),
diff --git a/test/rebar_lock_SUITE.erl b/test/rebar_lock_SUITE.erl
new file mode 100644
index 0000000..00875f7
--- /dev/null
+++ b/test/rebar_lock_SUITE.erl
@@ -0,0 +1,46 @@
+%%% Most locking tests are implicit in other test suites handling
+%%% dependencies.
+%%% This suite is to test the compatibility layers between various
+%%% versions of lockfiles.
+-module(rebar_lock_SUITE).
+-compile(export_all).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+all() -> [current_version, future_versions_no_attrs, future_versions_attrs].
+
+current_version(Config) ->
+ %% Current version just dumps the locks as is on disk.
+ LockFile = filename:join(?config(priv_dir, Config), "current_version"),
+ Locks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2},
+ {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0},
+ {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1},
+ {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>},3}],
+ file:write_file(LockFile, io_lib:format("~p.~n", [Locks])),
+ ?assertEqual(Locks, rebar_config:consult_lock_file(LockFile)).
+
+future_versions_no_attrs(Config) ->
+ %% Future versions will keep the same core attribute in there, but
+ %% will do so under a new format bundled with a version and potentially
+ %% some trailing attributes
+ LockFile = filename:join(?config(priv_dir, Config), "future_versions"),
+ Locks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2},
+ {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0},
+ {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1},
+ {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>},3}],
+ LockData = {"3.5.2", Locks},
+ file:write_file(LockFile, io_lib:format("~p.~n", [LockData])),
+ ?assertEqual(Locks, rebar_config:consult_lock_file(LockFile)).
+
+future_versions_attrs(Config) ->
+ %% Future versions will keep the same core attribute in there, but
+ %% will do so under a new format bundled with a version and potentially
+ %% some trailing attributes
+ LockFile = filename:join(?config(priv_dir, Config), "future_versions"),
+ Locks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2},
+ {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0},
+ {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1},
+ {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>},3}],
+ LockData = {"3.5.2", Locks},
+ file:write_file(LockFile, io_lib:format("~p.~na.~n{b,c}.~n[d,e,f].~n", [LockData])),
+ ?assertEqual(Locks, rebar_config:consult_lock_file(LockFile)).
diff --git a/test/rebar_pkg_SUITE.erl b/test/rebar_pkg_SUITE.erl
index 9f19e0d..6a75f32 100644
--- a/test/rebar_pkg_SUITE.erl
+++ b/test/rebar_pkg_SUITE.erl
@@ -180,11 +180,14 @@ pkgs_provider(Config) ->
find_highest_matching(_Config) ->
State = rebar_state:new(),
- {ok, Vsn} = rebar_packages:find_highest_matching(<<"goodpkg">>, <<"1.0.0">>, package_index, State),
+ {ok, Vsn} = rebar_packages:find_highest_matching(
+ <<"test">>, <<"1.0.0">>, <<"goodpkg">>, <<"1.0.0">>, package_index, State),
?assertEqual(<<"1.0.1">>, Vsn),
- {ok, Vsn1} = rebar_packages:find_highest_matching(<<"goodpkg">>, <<"1.0">>, package_index, State),
+ {ok, Vsn1} = rebar_packages:find_highest_matching(
+ <<"test">>, <<"1.0.0">>, <<"goodpkg">>, <<"1.0">>, package_index, State),
?assertEqual(<<"1.1.1">>, Vsn1),
- {ok, Vsn2} = rebar_packages:find_highest_matching(<<"goodpkg">>, <<"2.0">>, package_index, State),
+ {ok, Vsn2} = rebar_packages:find_highest_matching(
+ <<"test">>, <<"1.0.0">>, <<"goodpkg">>, <<"2.0">>, package_index, State),
?assertEqual(<<"2.0.0">>, Vsn2).
diff --git a/test/rebar_plugins_SUITE.erl b/test/rebar_plugins_SUITE.erl
index c1a98de..a313683 100644
--- a/test/rebar_plugins_SUITE.erl
+++ b/test/rebar_plugins_SUITE.erl
@@ -11,8 +11,10 @@
complex_plugins/1,
list/1,
upgrade/1,
+ upgrade_project_plugin/1,
sub_app_plugins/1,
- sub_app_plugin_overrides/1]).
+ sub_app_plugin_overrides/1,
+ project_plugins/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -34,7 +36,8 @@ end_per_testcase(_, _Config) ->
catch meck:unload().
all() ->
- [compile_plugins, compile_global_plugins, complex_plugins, list, upgrade, sub_app_plugins, sub_app_plugin_overrides].
+ [compile_plugins, compile_global_plugins, complex_plugins, list, upgrade, upgrade_project_plugin,
+ sub_app_plugins, sub_app_plugin_overrides, project_plugins].
%% Tests that compiling a project installs and compiles the plugins of deps
compile_plugins(Config) ->
@@ -211,6 +214,45 @@ upgrade(Config) ->
{ok, [{app, Name}, {plugin, PkgName, <<"0.1.3">>}]}
).
+upgrade_project_plugin(Config) ->
+ AppDir = ?config(apps, Config),
+
+ Name = rebar_test_utils:create_random_name("app1_"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),
+
+ PkgName = rebar_test_utils:create_random_name("pkg1_"),
+ mock_git_resource:mock([]),
+ mock_pkg_resource:mock([
+ {pkgdeps, [{{iolist_to_binary(PkgName), <<"0.1.0">>}, []},
+ {{iolist_to_binary(PkgName), <<"0.0.1">>}, []},
+ {{iolist_to_binary(PkgName), <<"0.1.1">>}, []}]}
+ ]),
+
+ RConfFile = rebar_test_utils:create_config(AppDir, [{project_plugins, [list_to_atom(PkgName)]}]),
+ {ok, RConf} = file:consult(RConfFile),
+
+ %% Build with deps.
+ rebar_test_utils:run_and_check(
+ Config, RConf, ["compile"],
+ {ok, [{app, Name}, {plugin, PkgName, <<"0.1.1">>}]}
+ ),
+
+ catch mock_pkg_resource:unmock(),
+ mock_pkg_resource:mock([
+ {pkgdeps, [{{iolist_to_binary(PkgName), <<"0.1.0">>}, []},
+ {{iolist_to_binary(PkgName), <<"0.0.1">>}, []},
+ {{iolist_to_binary(PkgName), <<"0.1.3">>}, []},
+ {{iolist_to_binary(PkgName), <<"0.1.1">>}, []}]},
+ {upgrade, [PkgName]}
+ ]),
+
+ %% Build with deps.
+ rebar_test_utils:run_and_check(
+ Config, RConf, ["plugins", "upgrade", PkgName],
+ {ok, [{app, Name}, {plugin, PkgName, <<"0.1.3">>}]}
+ ).
+
sub_app_plugins(Config) ->
AppDir = ?config(apps, Config),
@@ -281,3 +323,50 @@ sub_app_plugin_overrides(Config) ->
Config, RConf, ["compile"],
{ok, [{app, Name}, {dep, Dep2Name, Vsn}, {plugin, DepName, Vsn2}, {plugin, PluginName}]}
).
+
+%% Check that project plugins are first in providers even if they override defaults but that
+%% normal plugins do not
+project_plugins(Config) ->
+ AppDir = ?config(apps, Config),
+
+ Name = rebar_test_utils:create_random_name("app1_"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),
+
+ DepName = rebar_test_utils:create_random_name("dep1_"),
+ PluginName = "compile",
+ PluginName2 = "release",
+
+ Plugins = rebar_test_utils:expand_deps(git, [{PluginName, Vsn, []}, {PluginName2, Vsn, []}]),
+ {SrcDeps, _} = rebar_test_utils:flat_deps(Plugins),
+ mock_git_resource:mock([{deps, SrcDeps}], create_plugin),
+
+ mock_pkg_resource:mock([{pkgdeps, [{{list_to_binary(DepName), list_to_binary(Vsn)}, []}]},
+ {config, [{plugins, [
+ {list_to_atom(PluginName),
+ {git, "http://site.com/user/"++PluginName++".git",
+ {tag, Vsn}}}]}]}]),
+
+ RConfFile =
+ rebar_test_utils:create_config(AppDir,
+ [{deps, [
+ list_to_atom(DepName)
+ ]},
+ {project_plugins, [
+ {list_to_atom(PluginName2),
+ {git, "http://site.com/user/"++PluginName2++".git",
+ {tag, Vsn}}}]}]),
+ {ok, RConf} = file:consult(RConfFile),
+
+ %% Build with deps.
+ {ok, State} = rebar_test_utils:run_and_check(
+ Config, RConf, ["compile"],
+ {ok, [{app, Name}, {plugin, PluginName}, {plugin, PluginName2}, {dep, DepName}]}
+ ),
+
+ %% Should have 2 release providers but only 1 compile provider
+ Release = [P || P <- rebar_state:providers(State), providers:impl(P) =:= release, providers:namespace(P) =:= default],
+ Compile = [P || P <- rebar_state:providers(State), providers:impl(P) =:= compile, providers:namespace(P) =:= default],
+
+ ?assertEqual(length(Release), 2),
+ ?assertEqual(length(Compile), 1).
diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl
index 5187bda..23b0178 100644
--- a/test/rebar_test_utils.erl
+++ b/test/rebar_test_utils.erl
@@ -3,7 +3,7 @@
-include_lib("eunit/include/eunit.hrl").
-export([init_rebar_state/1, init_rebar_state/2, run_and_check/4, check_results/3]).
-export([expand_deps/2, flat_deps/1, top_level_deps/1]).
--export([create_app/4, create_eunit_app/4, create_empty_app/4,
+-export([create_app/4, create_plugin/4, create_eunit_app/4, create_empty_app/4,
create_config/2, create_config/3, package_app/3]).
-export([create_random_name/1, create_random_vsn/0, write_src_file/2]).
@@ -82,6 +82,16 @@ create_app(AppDir, Name, Vsn, Deps) ->
write_app_src_file(AppDir, Name, Vsn, Deps),
rebar_app_info:new(Name, Vsn, AppDir, Deps).
+%% @doc Creates a dummy plugin including:
+%% - src/<file>.erl
+%% - src/<file>.app.src
+%% And returns a `rebar_app_info' object.
+create_plugin(AppDir, Name, Vsn, Deps) ->
+ write_plugin_file(AppDir, Name ++ ".erl"),
+ write_src_file(AppDir, "not_a_real_src_" ++ Name ++ ".erl"),
+ write_app_src_file(AppDir, Name, Vsn, Deps),
+ rebar_app_info:new(Name, Vsn, AppDir, Deps).
+
%% @doc Creates a dummy application including:
%% - src/<file>.erl
%% - src/<file>.app.src
@@ -365,6 +375,11 @@ check_results(AppDir, Expected, ProfileRun) ->
?assert(filelib:is_dir(Dirname))
end, Expected).
+write_plugin_file(Dir, Name) ->
+ Erl = filename:join([Dir, "src", Name]),
+ ok = filelib:ensure_dir(Erl),
+ ok = ec_file:write(Erl, plugin_src_file(Name)).
+
write_src_file(Dir, Name) ->
Erl = filename:join([Dir, "src", Name]),
ok = filelib:ensure_dir(Erl),
@@ -395,6 +410,18 @@ erl_src_file(Name) ->
"-export([main/0]).\n"
"main() -> ok.\n", [filename:basename(Name, ".erl")]).
+plugin_src_file(Name) ->
+ io_lib:format("-module('~s').\n"
+ "-export([init/1]).\n"
+ "init(State) -> \n"
+ "Provider = providers:create([\n"
+ "{name, '~s'},\n"
+ "{module, '~s'}\n"
+ "]),\n"
+ "{ok, rebar_state:add_provider(State, Provider)}.\n", [filename:basename(Name, ".erl"),
+ filename:basename(Name, ".erl"),
+ filename:basename(Name, ".erl")]).
+
erl_eunitized_src_file(Name) ->
io_lib:format("-module('~s').\n"
"-export([main/0]).\n"