summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--CONTRIBUTING.md110
-rw-r--r--README.md37
-rw-r--r--THANKS3
-rwxr-xr-xbootstrap52
-rw-r--r--priv/shell-completion/bash/rebar327
-rw-r--r--priv/shell-completion/zsh/_rebar342
-rw-r--r--priv/templates/gitignore1
-rw-r--r--priv/templates/otp_app.app.src6
-rw-r--r--priv/templates/otp_lib.app.src6
-rw-r--r--priv/templates/plugin.erl31
-rw-r--r--priv/templates/plugin.template1
-rw-r--r--priv/templates/provider.erl32
-rw-r--r--rebar.config70
-rw-r--r--rebar.lock25
-rw-r--r--src/r3.erl7
-rw-r--r--src/rebar.app.src6
-rw-r--r--src/rebar3.erl66
-rw-r--r--src/rebar_agent.erl118
-rw-r--r--src/rebar_app_discover.erl149
-rw-r--r--src/rebar_app_info.erl80
-rw-r--r--src/rebar_config.erl43
-rw-r--r--src/rebar_core.erl18
-rw-r--r--src/rebar_digraph.erl8
-rw-r--r--src/rebar_dir.erl58
-rw-r--r--src/rebar_erlc_compiler.erl3
-rw-r--r--src/rebar_fetch.erl2
-rw-r--r--src/rebar_file_utils.erl108
-rw-r--r--src/rebar_git_resource.erl2
-rw-r--r--src/rebar_hooks.erl81
-rw-r--r--src/rebar_otp_app.erl17
-rw-r--r--src/rebar_pkg_resource.erl3
-rw-r--r--src/rebar_plugins.erl107
-rw-r--r--src/rebar_prv_app_discovery.erl13
-rw-r--r--src/rebar_prv_as.erl9
-rw-r--r--src/rebar_prv_clean.erl6
-rw-r--r--src/rebar_prv_common_test.erl70
-rw-r--r--src/rebar_prv_compile.erl57
-rw-r--r--src/rebar_prv_cover.erl2
-rw-r--r--src/rebar_prv_dialyzer.erl243
-rw-r--r--src/rebar_prv_do.erl18
-rw-r--r--src/rebar_prv_edoc.erl2
-rw-r--r--src/rebar_prv_escriptize.erl11
-rw-r--r--src/rebar_prv_eunit.erl69
-rw-r--r--src/rebar_prv_help.erl10
-rw-r--r--src/rebar_prv_install_deps.erl180
-rw-r--r--src/rebar_prv_lock.erl2
-rw-r--r--src/rebar_prv_new.erl2
-rw-r--r--src/rebar_prv_packages.erl25
-rw-r--r--src/rebar_prv_plugins.erl67
-rw-r--r--src/rebar_prv_plugins_upgrade.erl110
-rw-r--r--src/rebar_prv_release.erl28
-rw-r--r--src/rebar_prv_relup.erl40
-rw-r--r--src/rebar_prv_report.erl2
-rw-r--r--src/rebar_prv_shell.erl189
-rw-r--r--src/rebar_prv_tar.erl25
-rw-r--r--src/rebar_prv_unlock.erl2
-rw-r--r--src/rebar_prv_update.erl2
-rw-r--r--src/rebar_prv_upgrade.erl23
-rw-r--r--src/rebar_prv_version.erl2
-rw-r--r--src/rebar_prv_xref.erl2
-rw-r--r--src/rebar_relx.erl69
-rw-r--r--src/rebar_state.erl101
-rw-r--r--src/rebar_templater.erl2
-rw-r--r--src/rebar_utils.erl49
-rw-r--r--test/mock_pkg_resource.erl11
-rw-r--r--test/rebar_compile_SUITE.erl85
-rw-r--r--test/rebar_deps_SUITE.erl2
-rw-r--r--test/rebar_dialyzer_SUITE.erl19
-rw-r--r--test/rebar_dir_SUITE.erl99
-rw-r--r--test/rebar_disable_app_SUITE.erl49
-rw-r--r--test/rebar_hooks_SUITE.erl97
-rw-r--r--test/rebar_install_deps_SUITE.erl34
-rw-r--r--test/rebar_pkg_SUITE.erl8
-rw-r--r--test/rebar_plugins_SUITE.erl201
-rw-r--r--test/rebar_release_SUITE.erl21
-rw-r--r--test/rebar_src_dirs_SUITE.erl (renamed from test/rebar_extra_src_dirs_SUITE.erl)95
-rw-r--r--test/rebar_test_utils.erl58
-rw-r--r--test/rebar_upgrade_SUITE.erl118
-rw-r--r--test/rebar_utils_SUITE.erl17
80 files changed, 2713 insertions, 953 deletions
diff --git a/.gitignore b/.gitignore
index bd0314f..8028d9c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+.rebar3
rebar3
_build
.depsolver_plt
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index e0de0eb..0ce1e93 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,7 +1,7 @@
Contributing to rebar
---------------------
-Before implementing a new feature, please submit a ticket to discuss your plans.
+Before implementing a new feature, please submit a ticket to discuss your plans.
The feature might have been rejected already, or the implementation might already be decided.
See [Community and Resources](README.md#community-and-resources).
@@ -9,95 +9,99 @@ See [Community and Resources](README.md#community-and-resources).
Code style
----------
-The following rules must be followed:
+The following rules apply:
* Do not introduce trailing whitespace
- * Do not mix spaces and tabs
- * Do not introduce lines longer than 80 characters
-
-The following rules should be followed:
+ * We use spaces for indenting only
+ * Try not to introduce lines longer than 80 characters
* Write small functions whenever possible
- * Avoid having too many clauses containing clauses containing clauses.
+ * Avoid having too many clauses containing clauses containing clauses.
Basically, avoid deeply nested functions.
-[erlang-mode (emacs)](http://www.erlang.org/doc/man/erlang.el.html)
-indentation is preferred. This will keep the code base consistent.
-vi users are encouraged to give [Vim emulation](http://emacswiki.org/emacs/Evil) ([more
-info](https://gitorious.org/evil/pages/Home)) a try.
+Follow the indentation style of existing files. The [erlang-mode for
+(emacs)](http://www.erlang.org/doc/man/erlang.el.html) indentation is going to
+always work. Other users may want to use 4-width spaces and make sure things
+align mostly the way they would with Emacs code, or with the rest of the
+project.
+
+Where possible, include type specifications for your code so type analysis
+will be as accurate as possible.
+
+Please add comments around tricky fixes or workarounds so that we can
+easily know why they're there at a glance.
Pull requests and branching
---------------------------
-Use one topic branch per pull request. If you do that, you can add extra commits or fix up
-buggy commits via `git rebase -i`, and update the branch. The updated branch will be
-visible in the same pull request. Therefore, you should not open a new pull request when
-you have to fix your changes.
+All fixes to rebar end up requiring a +1 from one or more of the project's
+maintainers. When opening a pull request, explain what the patch is doing
+and if it makes sense, why the proposed implementation was chosen.
+
+Try to use well-defined commits (one feature per commit) so that reading
+them and testing them is easier for reviewers and while bissecting the code
+base for issues.
+
+During the review process, you may be asked to correct or edit a few things
+before a final rebase to merge things.
-Do not commit to master in your fork.
+Please work in feature branches, and do not commit to `master` in your fork.
Provide a clean branch without merge commits.
Tests
-----
-As a general rule, any behavioral change to rebar requires a test to go with it. If there's
-already a test case, you may have to modify that one. If there isn't a test case or a test
-suite, add a new test case or suite in `inttest/`. [retest](https://github.com/dizzyd/retest) based tests are preferred, but
-we also have EUnit tests in `test/`.
+As a general rule, any behavioral change to rebar requires a test to go with
+it. If there's already a test case, you may have to modify that one. If there
+isn't a test case or a test suite, add a new test case or suite in `test/`.
-Say you've added a new test case in `inttest/erlc`. To only execute the modified suite,
-you would do the following:
-```sh
-# First we build rebar and its deps to also get `deps/retest/retest`
-$ make debug deps
-# Now we can test the modified erlc suite
-$ deps/retest/retest -v inttest/erlc
-```
+To run the tests:
-To test EUnit tests, you would do:
```sh
-$ make debug
-$ ./rebar -v eunit
+$ ./bootstrap
+$ ./rebar3 ct
```
-You can also run `make test` to execute both EUnit and [retest](https://github.com/dizzyd/retest) tests as `make check` does.
+The rebar3 test suite is written using Common Test. As many of the tests as
+possible are written by using the programmatic rebar3 API rather than
+by running the escriptized project directly. The tests should be restricted
+to their `priv_dir` and leave the system clean after a run.
+
+ If such tests prove hard to write, you can ask for help doing that in your
+pull request.
+
+For tests having a lot to do with I/O and terminal interaction, consider
+adding them to https://github.com/tsloughter/rebar3_tests
+
Credit
------
-To give everyone proper credit in addition to the git history, please feel free to append
+To give everyone proper credit in addition to the git history, please feel free to append
your name to `THANKS` in your first contribution.
Committing your changes
-----------------------
-Please ensure that all commits pass all tests, and do not have extra Dialyzer warnings.
-To do that run `make check`. If you didn't build via `make debug` at first, the beam files in
-`ebin/` might be missing debug_info required for [xref](http://www.erlang.org/doc/man/xref.html)
-and [Dialyzer](http://www.erlang.org/doc/man/dialyzer.html), causing a test
-failure.
-If that happens, running `make clean` before running `make check` could solve the problem.
-If you change any of the files with known but safe to ignore Dialyzer warnings, you may
-have to adapt the line number(s) in [dialyzer_reference](dialyzer_reference). If you do that,
-do not remove the
-leading blank line.
+Please ensure that all commits pass all tests, and do not have extra Dialyzer warnings.
+To do that run `./rebar3 ct` and `./rebar3 as dialyze dialyzer`.
#### Structuring your commits
-Fixing a bug is one commit.
-Adding a feature is one commit.
-Adding two features is two commits.
-Two unrelated changes is two commits.
+- Fixing a bug is one commit.
+- Adding a feature is one commit.
+- Adding two features is two commits.
+- Two unrelated changes is two commits (and likely two Pull requests)
-If you fix a (buggy) commit, squash (`git rebase -i`) the changes as a fixup commit into
+If you fix a (buggy) commit, squash (`git rebase -i`) the changes as a fixup commit into
the original commit.
#### Writing Commit Messages
-It's important to write a proper commit title and description. The commit title must be
-at most 50 characters; it is the first line of the commit text. The second line of the
-commit text must be left blank. The third line and beyond is the commit message. You
-should write a commit message. If you do, wrap all lines at 72 characters. You should
-explain what the commit does, what references you used, and any other information
+It's important to write a proper commit title and description. The commit title must be
+at most 50 characters; it is the first line of the commit text. The second line of the
+commit text must be left blank. The third line and beyond is the commit message. You
+should write a commit message. If you do, wrap all lines at 72 characters. You should
+explain what the commit does, what references you used, and any other information
that helps understanding your changes.
Basically, structure your commit message like this:
diff --git a/README.md b/README.md
index 8cca7de..e2bb9ac 100644
--- a/README.md
+++ b/README.md
@@ -4,28 +4,20 @@ rebar
rebar [3.0](#30) is an Erlang build tool that makes it easy to compile and test Erlang
applications, port drivers and releases.
-[![Build Status](https://travis-ci.org/rebar/rebar3.svg?branch=master)](https://travis-ci.org/rebar/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)
rebar is a self-contained Erlang script, so it's easy to distribute or even
embed directly in a project. Where possible, rebar uses standard Erlang/OTP
conventions for project structures, thus minimizing the amount of build
configuration work. rebar also provides dependency management, enabling
application writers to easily re-use common libraries from a variety of
-locations (git, hg, etc).
+locations ([hex.pm](http://hex.pm), git, hg, and so on).
-3.0 Alpha-5
+3.0 Beta-1
====
[DOCUMENTATION](http://www.rebar3.org/v3.0/docs)
-This is a preliminary branch, considered to be alpha, and still
-very unstable. Use at your own risk, but please do report bugs
-and issues encountered and we'll try to resolve problems as
-soon as possible.
-
-Compatibility with rebar 2.0 has been broken, and features removed to
-limit scope.
-
### Commands
| Command | Description |
@@ -35,32 +27,47 @@ limit scope.
| clean | Remove project apps beam files |
| cover | Generate coverage info from data compiled by `eunit --cover` or `ct --cover` |
| ct | Run Common Test suites |
+| deps | Lists dependencies currently in use |
| do | Higher-order provider to run multiple tasks in sequence |
| dialyzer | Run the Dialyzer analyzer on the project |
| edoc | Generate documentation using edoc |
-| eunit | Run eunit tests |
| escriptize | Generate escript of project |
+| eunit | Run eunit tests |
| help | Print help for rebar or task |
| new | Create new rebar project from templates |
| pkgs | List available packages |
+| plugins | List or upgrade plugins |
| release | Build release of project |
+| relup | Creates relup from 2 releases |
| report | Report on environment and versions for bug reports |
| shell | Run shell with project apps in path |
| tar | Package release into tarball |
+| unlock | Unlock dependencies |
| update | Update package index |
| upgrade | Fetch latest version of dep |
| version | Print current version of Erlang/OTP and rebar |
| xref | Run cross reference analysis on the project |
+A more complete list can be found on the [docs page](http://www.rebar3.org/v3.0/docs/commands)
+
### Changes
+#### Since Rebar 2.x
+
* Fetches and builds deps if missing when running any command that relies on them
* Automatically recognizes `apps` and `lib` directory structure
-* Relx for releases
+* Relx for releases and relups
+* deterministic builds and conflict resolution
+* New plugin handling mechanism
+* New hook mechanism
+* Support for packages
+* A ton more
### Gone
* Reltool integeration
+* Quickcheck integration (moved to [a plugin](http://www.rebar3.org/v3.0/docs/using-available-plugins#quickcheck))
+* C code compiling (moved to [a plugin](http://www.rebar3.org/v3.0/docs/using-available-plugins#port-compiler) or hooks)
### Providers
@@ -110,7 +117,7 @@ format_error(Reason) ->
Building
--------
-Recommended installation of [Erlang/OTP](http://www.erlang.org) is binary packages from [Erlang Solutions](https://www.erlang-solutions.com/downloads/download-erlang-otp). For source it is recommended you use [erln8](http://metadave.github.io/erln8/) or [kerl](https://github.com/yrashk/kerl).
+Recommended installation of [Erlang/OTP](http://www.erlang.org) is source built using [erln8](http://metadave.github.io/erln8/) or [kerl](https://github.com/yrashk/kerl). For binary packages use those provided by [Erlang Solutions](https://www.erlang-solutions.com/downloads/download-erlang-otp), but be sure to choose the "Standard" download option or you'll have issues building projects.
### Dependencies
@@ -141,7 +148,6 @@ $ ./rebar3 escriptize
$ _build/default/bin/rebar3
```
-
Contributing to rebar
=====================
@@ -171,3 +177,4 @@ General rebar community resources and links:
- #rebar on [irc.freenode.net](http://freenode.net/)
- [wiki](https://github.com/rebar/rebar/wiki)
- [issues](https://github.com/rebar/rebar/issues)
+- [Documentation](http://www.rebar3.org/v3.0/docs)
diff --git a/THANKS b/THANKS
index 983d883..a6562ed 100644
--- a/THANKS
+++ b/THANKS
@@ -132,4 +132,5 @@ Tristan Sloughter
Kelly McLaughlin
Martin Karlsson
Pierre Fenoll
-David Kubecka \ No newline at end of file
+David Kubecka
+Stefan Grundmann
diff --git a/bootstrap b/bootstrap
index 1a3999b..41d9725 100755
--- a/bootstrap
+++ b/bootstrap
@@ -4,6 +4,12 @@
main(_Args) ->
+ application:start(crypto),
+ application:start(asn1),
+ application:start(public_key),
+ application:start(ssl),
+ inets:start(),
+
%% Fetch and build deps required to build rebar3
BaseDeps = [{providers, []}
,{getopt, []}
@@ -24,6 +30,7 @@ main(_Args) ->
setup_env(),
os:putenv("REBAR_PROFILE", "bootstrap"),
+ rebar3:run(["update"]),
{ok, State} = rebar3:run(["compile"]),
reset_env(),
os:putenv("REBAR_PROFILE", ""),
@@ -56,28 +63,33 @@ fetch_and_compile({Name, ErlFirstFiles}, Deps) ->
ok = fetch(Repo, Name),
compile(Name, ErlFirstFiles).
-fetch({git, Url, Source}, App) ->
+fetch({pkg, Name, Vsn}, App) ->
Dir = filename:join([filename:absname("_build/default/lib/"), App]),
- case filelib:is_dir(Dir) of
- true ->
- true = code:add_path(filename:join(Dir, "ebin")),
- ok;
- false ->
- fetch_source(Dir, Url, Source),
- ok
+ CDN = "https://s3.amazonaws.com/s3.hex.pm/tarballs",
+ Package = binary_to_list(<<Name/binary, "-", Vsn/binary, ".tar">>),
+ Url = string:join([CDN, Package], "/"),
+ case request(Url) of
+ {ok, Binary} ->
+ {ok, Contents} = extract(Binary),
+ ok = erl_tar:extract({binary, Contents}, [{cwd, Dir}, compressed]);
+ _ ->
+ io:format("Error: Unable to fetch package ~p ~p~n", [Name, Vsn])
end.
-fetch_source(Dir, Url, {ref, Ref}) ->
- ok = filelib:ensure_dir(Dir),
- os:cmd(io_lib:format("git clone ~s ~s", [Url, Dir])),
- {ok, Cwd} = file:get_cwd(),
- file:set_cwd(Dir),
- os:cmd(io_lib:format("git checkout -q ~s", [Ref])),
- file:set_cwd(Cwd);
-fetch_source(Dir, Url, {_, Branch}) ->
- ok = filelib:ensure_dir(Dir),
- os:cmd(io_lib:format("git clone ~s ~s -b ~s --single-branch",
- [Url, Dir, Branch])).
+extract(Binary) ->
+ {ok, Files} = erl_tar:extract({binary, Binary}, [memory]),
+ {"contents.tar.gz", Contents} = lists:keyfind("contents.tar.gz", 1, Files),
+ {ok, Contents}.
+
+request(Url) ->
+ case httpc:request(get, {Url, []},
+ [{relaxed, true}],
+ [{body_format, binary}]) of
+ {ok, {{_Version, 200, _Reason}, _Headers, Body}} ->
+ {ok, Body};
+ Error ->
+ Error
+ end.
compile(App, FirstFiles) ->
Dir = filename:join(filename:absname("_build/default/lib/"), App),
@@ -86,6 +98,7 @@ compile(App, FirstFiles) ->
FirstFilesPaths = [filename:join([Dir, "src", Module]) || Module <- FirstFiles],
Sources = FirstFilesPaths ++ filelib:wildcard(filename:join([Dir, "src", "*.erl"])),
[compile_file(X, [{i, filename:join(Dir, "include")}
+ ,debug_info
,{outdir, filename:join(Dir, "ebin")}
,return | additional_defines()]) || X <- Sources].
@@ -219,6 +232,7 @@ setup_env() ->
application:load(rebar),
{ok, Providers} = application:get_env(rebar, providers),
Providers1 = Providers -- [rebar_prv_release,
+ rebar_prv_relup,
rebar_prv_tar],
application:set_env(rebar, providers, Providers1).
diff --git a/priv/shell-completion/bash/rebar3 b/priv/shell-completion/bash/rebar3
index cb6f69d..4e28d3d 100644
--- a/priv/shell-completion/bash/rebar3
+++ b/priv/shell-completion/bash/rebar3
@@ -23,10 +23,13 @@ _rebar3()
help \
new \
pkgs \
+ plugins \
release \
+ relup \
report \
shell \
tar \
+ unlock \
update \
upgrade \
version \
@@ -93,6 +96,8 @@ _rebar3()
lopts="--force"
elif [[ ${prev} == pkgs ]] ; then
:
+ elif [[ ${prev} == plugins ]] ; then
+ :
elif [[ ${prev} == release ]] ; then
sopts="-n -v -g -u -o -h -l -p -V -d -i -a -c -r"
lopts="--relname \
@@ -115,6 +120,28 @@ _rebar3()
--system_libs \
--version \
--root"
+ elif [[ ${prev} == relup ]] ; then
+ sopts="-n -v -g -u -o -h -l -p -V -d -i -a -c -r"
+ lopts="--relname \
+ --relvsn \
+ --goal \
+ --upfrom \
+ --output-dir \
+ --help \
+ --lib-dir \
+ --path \
+ --default-libs \
+ --verbose \
+ --dev-mode \
+ --include-erts \
+ --override \
+ --config \
+ --overlay_vars \
+ --vm_args \
+ --sys_config \
+ --system_libs \
+ --version \
+ --root"
elif [[ ${prev} == report ]] ; then
:
elif [[ ${prev} == shell ]] ; then
diff --git a/priv/shell-completion/zsh/_rebar3 b/priv/shell-completion/zsh/_rebar3
index 6950688..b03b7c9 100644
--- a/priv/shell-completion/zsh/_rebar3
+++ b/priv/shell-completion/zsh/_rebar3
@@ -72,6 +72,9 @@ _rebar3 () {
'(-v --verbose)'{-v,--verbose}'[Print coverage analysis]' \
&& ret=0
;;
+ (deps)
+ _message 'no more arguments' && ret=0
+ ;;
(dialyzer)
_arguments \
'(-u --update-plt)'{-u, --update-plt}'[Enable updating the PLT.]' \
@@ -100,7 +103,7 @@ _rebar3 () {
;;
(new)
_arguments \
- '1:type:(app lib release plugin)' \
+ '1:type:(app cmake escript lib plugin release)' \
'2:name:' \
'(-f --force)'{-f,--force}'[ overwrite existing files]' \
&& ret=0
@@ -108,6 +111,11 @@ _rebar3 () {
(pkgs)
_message 'List available packages.' && ret=0
;;
+ (plugins)
+ _arguments \
+ '1:type:(list upgrade)' \
+ && ret=0
+ ;;
(release)
_arguments \
'(-n --relname)'{-n,--relname}'[Specify the name for the release that will be generated.]:relname' \
@@ -131,6 +139,29 @@ _rebar3 () {
'(-r --root)'{-r,--root}'[The project root directory]:system libs:_files -/' \
&& ret=0
;;
+ (relup)
+ _arguments \
+ '(-n --relname)'{-n,--relname}'[Specify the name for the release that will be generated.]:relname' \
+ '(-v --relvsn)'{-n,--relname}'[Specify the version for the release.]:relvsn' \
+ '(-g --goal)'{-g,--goal}'[Specify a target constraint on the system. These are usually the OTP.]:goal' \
+ '(-u --upfrom)'{-u,--upfrom}'[Only valid with relup target, specify the release to upgrade from.]:upfrom' \
+ '(-o --output-dir)'{-o,--output-dir}'[The output directory for the release. This is ./ by default.]:out directory:_files -/' \
+ '(-l --lib-dir)'{-l,--output-dir}'[Additional dir that should be searched for OTP Apps]:lib directory:_files -/' \
+ '(-p --path)'{-p,--path}'[Additional dir to add to the code path]:path directory:_files -/' \
+ '(--default-libs)--default-libs[Whether to use the default system added lib dirs]:default libs:(true false)' \
+ '(-V --verbose)'{-V,--verbose}'[Verbosity level, maybe between 0 and 3 ,default: 2]:verbosity level:(0 1 2 3)' \
+ '(-d --dev-mode)'{-d,--dev-mode}'[Symlink the applications and configuration into the release instead of copying]' \
+ '(-i --include-erts)'{-i,--dev-mode}'[If true include a copy of erts used to build with, if a path include erts at that path. If false, do not include erts]' \
+ '(-a --override)'{-a,--override}'[Provide an app name and a directory to override in the form <appname>:<app directory>]:override' \
+ '(-c --config)'{-c,--config}'[The path to a config file]:config file:_files ' \
+ '(--overlay_vars)--overlay_vars[Path to a file of overlay variables]:overlay variables file:_files' \
+ '(--vm_args)--vm_args[Path to a file to use for vm.args]:vm args file:_files' \
+ '(--sys_config)--sys_config[Path to a file to use for sys.config]:sys config file:_files' \
+ '(--system_libs)--system_libs[Path to dir of Erlang system libs]:system libs:_files -/' \
+ '(--version)--version[Print relx version]' \
+ '(-r --root)'{-r,--root}'[The project root directory]:system libs:_files -/' \
+ && ret=0
+ ;;
(report)
_arguments '1: :_rebar3_tasks' && ret=0
;;
@@ -160,6 +191,11 @@ _rebar3 () {
'(-r --root)'{-r,--root}'[The project root directory]:system libs:_files -/' \
&& ret=0
;;
+ (unlock)
+ _arguments \
+ '*: :_rebar3_list_deps' \
+ && ret=0
+ ;;
(update)
_message 'rebar update' && ret=0
;;
@@ -185,6 +221,7 @@ _rebar3_tasks() {
'compile:Compile apps .app.src and .erl files.'
'cover:Perform coverage analysis.'
'ct:Run Common Tests.'
+ 'deps:List dependencies.'
'dialyzer:Run the Dialyzer analyzer on the project.'
'do:Higher order provider for running multiple tasks in a sequence.'
'edoc:Generate documentation using edoc.'
@@ -193,10 +230,13 @@ _rebar3_tasks() {
'help:Display a list of tasks or help for a given task or subtask.'
'new:Create new project from templates.'
'pkgs:List available packages.'
+ 'plugins:List or upgrade plugins.'
'release:Build release of project.'
+ 'relup:Create relup from 2 releases.'
'report:Provide a crash report to be sent to the rebar3 issues page.'
'shell:Run shell with project apps and deps in path.'
'tar:Tar archive of release built of project.'
+ 'unlock:Unlock dependencies.'
'update:Update package index.'
'upgrade:Upgrade dependencies.'
'version:Print version for rebar and current Erlang.'
diff --git a/priv/templates/gitignore b/priv/templates/gitignore
index 40a1d4f..a939dce 100644
--- a/priv/templates/gitignore
+++ b/priv/templates/gitignore
@@ -16,3 +16,4 @@ _deps
_plugins
_tdeps
logs
+_build \ No newline at end of file
diff --git a/priv/templates/otp_app.app.src b/priv/templates/otp_app.app.src
index 9ad7478..09e4a48 100644
--- a/priv/templates/otp_app.app.src
+++ b/priv/templates/otp_app.app.src
@@ -8,5 +8,9 @@
stdlib
]},
{env,[]},
- {modules, []}
+ {modules, []},
+
+ {contributors, []},
+ {licenses, []},
+ {links, []}
]}.
diff --git a/priv/templates/otp_lib.app.src b/priv/templates/otp_lib.app.src
index 3f4b56b..f07293e 100644
--- a/priv/templates/otp_lib.app.src
+++ b/priv/templates/otp_lib.app.src
@@ -7,5 +7,9 @@
stdlib
]},
{env,[]},
- {modules, []}
+ {modules, []},
+
+ {contributors, []},
+ {licenses, []},
+ {links, []}
]}.
diff --git a/priv/templates/plugin.erl b/priv/templates/plugin.erl
index 018dd0e..c6e5e40 100644
--- a/priv/templates/plugin.erl
+++ b/priv/templates/plugin.erl
@@ -1,33 +1,8 @@
-module('{{name}}').
--behaviour(provider).
--export([init/1, do/1, format_error/1]).
+-export([init/1]).
--define(PROVIDER, '{{name}}').
--define(DEPS, [app_discovery]).
-
-%% ===================================================================
-%% Public API
-%% ===================================================================
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
- Provider = providers:create([
- {name, ?PROVIDER}, % The 'user friendly' name of the task
- {module, ?MODULE}, % The module implementation of the task
- {bare, true}, % The task can be run by the user, always true
- {deps, ?DEPS}, % The list of dependencies
- {example, "rebar3 {{name}}"}, % How to use the plugin
- {opts, []}, % list of options understood by the plugin
- {short_desc, "{{desc}}"},
- {desc, "{{desc}}"}
- ]),
- {ok, rebar_state:add_provider(State, Provider)}.
-
-
--spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
-do(State) ->
- {ok, State}.
-
--spec format_error(any()) -> iolist().
-format_error(Reason) ->
- io_lib:format("~p", [Reason]).
+ {ok, State1} = '{{name}}_prv':init(State),
+ {ok, State1}.
diff --git a/priv/templates/plugin.template b/priv/templates/plugin.template
index 811be0b..c0e36de 100644
--- a/priv/templates/plugin.template
+++ b/priv/templates/plugin.template
@@ -4,6 +4,7 @@
{desc, "A rebar plugin", "Short description of the plugin's purpose"}
]}.
{template, "plugin.erl", "{{name}}/src/{{name}}.erl"}.
+{template, "provider.erl", "{{name}}/src/prv_{{name}}_prv.erl"}.
{template, "otp_lib.app.src", "{{name}}/src/{{name}}.app.src"}.
{template, "rebar.config", "{{name}}/rebar.config"}.
{template, "gitignore", "{{name}}/.gitignore"}.
diff --git a/priv/templates/provider.erl b/priv/templates/provider.erl
new file mode 100644
index 0000000..669df83
--- /dev/null
+++ b/priv/templates/provider.erl
@@ -0,0 +1,32 @@
+-module('{{name}}_prv').
+
+-export([init/1, do/1, format_error/1]).
+
+-define(PROVIDER, '{{name}}').
+-define(DEPS, [app_discovery]).
+
+%% ===================================================================
+%% Public API
+%% ===================================================================
+-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
+init(State) ->
+ Provider = providers:create([
+ {name, ?PROVIDER}, % The 'user friendly' name of the task
+ {module, ?MODULE}, % The module implementation of the task
+ {bare, true}, % The task can be run by the user, always true
+ {deps, ?DEPS}, % The list of dependencies
+ {example, "rebar3 {{name}}"}, % How to use the plugin
+ {opts, []}, % list of options understood by the plugin
+ {short_desc, "{{desc}}"},
+ {desc, "{{desc}}"}
+ ]),
+ {ok, rebar_state:add_provider(State, Provider)}.
+
+
+-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
+do(State) ->
+ {ok, State}.
+
+-spec format_error(any()) -> iolist().
+format_error(Reason) ->
+ io_lib:format("~p", [Reason]).
diff --git a/rebar.config b/rebar.config
index 504a462..0b117de 100644
--- a/rebar.config
+++ b/rebar.config
@@ -1,26 +1,12 @@
%% -*- mode: erlang;erlang-indent-level: 4;indent-tabs-mode: nil -*-
%% ex: ts=4 sw=4 ft=erlang et
-{deps, [
- {erlware_commons, "",
- {git, "https://github.com/erlware/erlware_commons.git",
- {branch, "master"}}},
- {providers, "",
- {git, "https://github.com/tsloughter/providers.git",
- {tag, "v1.3.1"}}},
- {relx, "",
- {git, "https://github.com/erlware/relx.git",
- {tag, "v2.0.0"}}},
- {mustache, ".*",
- {git, "https://github.com/soranoba/mustache.git",
- {tag, "v0.3.0"}}},
- {getopt, "",
- {git, "https://github.com/jcomellas/getopt.git",
- {branch, "master"}}}]}.
+{deps, [{erlware_commons, "0.12.0"},
+ {providers, "1.4.1"},
+ {getopt, "0.8.2"},
+ {bbmustache, "1.0.1"},
+ {relx, "3.0.0"}]}.
-{escript_incl_apps,
- [getopt, erlware_commons, relx, providers, rebar]}.
-{escript_top_level_app, rebar}.
{escript_name, rebar3}.
{escript_emu_args, "%%! +sbtu +A0\n"}.
%% escript_incl_extra is for internal rebar-private use only.
@@ -29,23 +15,22 @@
{"priv/templates/*", "."},
{"rebar/include/*", "."}]}.
-{erl_opts,
- [{platform_define, "^[0-9]+", namespaced_types},
- no_debug_info,
- warnings_as_errors]}.
+{erl_opts, [{platform_define, "^[0-9]+", namespaced_types},
+ no_debug_info,
+ warnings_as_errors]}.
{dialyzer_plt_apps, [common_test, dialyzer, eunit, snmp]}.
%% Profiles
-{profiles, [{test,
- [{deps, [
- {meck, "", {git, "https://github.com/eproxus/meck.git", {tag, "0.8.2"}}}
- ]},
- {erl_opts, [debug_info]}
- ]},
+{profiles, [{test, [
+ {deps, [{meck, "0.8.2"}]},
+ {erl_opts, [debug_info]}
+ ]
+ },
- %% We don't want erlydtl to attempt to run on the first compile pass to bootstrap
- {bootstrap, []}
+ {bootstrap, []},
+
+ {dialyze, [{erl_opts, [debug_info]}]}
]}.
%% Overrides
@@ -54,16 +39,23 @@
{platform_define, "^R1[4|5]", deprecated_crypto},
no_debug_info,
warnings_as_errors]},
- {deps, []}, {plugins, []}
+ {deps, []}, {plugins, []},
+ {profiles, [{dialyze, [{erl_opts, [debug_info]}]}]}
]},
- {override, mustache, [
- {erl_opts, [{platform_define, "^[0-9]+", namespaced_types},
- no_debug_info]},
- {deps, []}, {plugins, []}]},
- {override, getopt, [{erl_opts, [no_debug_info]}]},
- {override, providers, [{erl_opts, [no_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]}]}
+ warnings_as_errors]},
+ {profiles, [{dialyze, [{erl_opts, [debug_info]}]}]}
+ ]}
]}.
diff --git a/rebar.lock b/rebar.lock
index 6bff315..3452cea 100644
--- a/rebar.lock
+++ b/rebar.lock
@@ -1,20 +1,5 @@
-[{<<"relx">>,
- {git,"https://github.com/erlware/relx.git",
- {ref,"2e59b1c95575b3c104cc191e954c82baadc43c6c"}},
- 0},
- {<<"providers">>,
- {git,"https://github.com/tsloughter/providers.git",
- {ref,"7563ba7e916d5a35972b25b3aa1945ffe0a8e7a5"}},
- 0},
- {<<"mustache">>,
- {git,"https://github.com/soranoba/mustache.git",
- {ref,"e5401042c66039eef20ee81abc1537ced1f22bc7"}},
- 0},
- {<<"getopt">>,
- {git,"https://github.com/jcomellas/getopt.git",
- {ref,"626698975e63866156159661d100785d65eab6f9"}},
- 0},
- {<<"erlware_commons">>,
- {git,"https://github.com/erlware/erlware_commons.git",
- {ref,"ef0d252b11c863f9c228af2fe93a4e42fba2f7f3"}},
- 0}].
+[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.1">>},0},
+ {<<"providers">>,{pkg,<<"providers">>,<<"1.4.1">>},0},
+ {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.12.0">>},0},
+ {<<"relx">>,{pkg,<<"relx">>,<<"3.0.0">>},0},
+ {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0}].
diff --git a/src/r3.erl b/src/r3.erl
new file mode 100644
index 0000000..5e8b26d
--- /dev/null
+++ b/src/r3.erl
@@ -0,0 +1,7 @@
+%%% external alias for rebar_agent
+-module(r3).
+-export([do/1, do/2]).
+
+do(Command) -> rebar_agent:do(Command).
+
+do(Namespace, Command) -> rebar_agent:do(Namespace, Command).
diff --git a/src/rebar.app.src b/src/rebar.app.src
index f753784..e5d56bb 100644
--- a/src/rebar.app.src
+++ b/src/rebar.app.src
@@ -3,7 +3,7 @@
{application, rebar,
[{description, "Rebar: Erlang Build Tool"},
- {vsn, "3.0.0-alpha-5"},
+ {vsn, "3.0.0-beta-1"},
{modules, []},
{registered, []},
{applications, [kernel,
@@ -17,6 +17,7 @@
common_test,
erlware_commons,
providers,
+ bbmustache,
relx,
inets]},
{env, [
@@ -44,7 +45,10 @@
rebar_prv_lock,
rebar_prv_new,
rebar_prv_packages,
+ rebar_prv_plugins,
+ rebar_prv_plugins_upgrade,
rebar_prv_release,
+ rebar_prv_relup,
rebar_prv_report,
rebar_prv_shell,
rebar_prv_tar,
diff --git a/src/rebar3.erl b/src/rebar3.erl
index 1a02407..c501709 100644
--- a/src/rebar3.erl
+++ b/src/rebar3.erl
@@ -56,7 +56,7 @@ main(Args) ->
%% Erlang-API entry point
run(BaseState, Commands) ->
- _ = application:load(rebar),
+ start_and_load_apps(),
BaseState1 = rebar_state:set(BaseState, task, Commands),
BaseState2 = rebar_state:set(BaseState1, caller, api),
run_aux(BaseState2, Commands).
@@ -66,7 +66,7 @@ run(BaseState, Commands) ->
%% ====================================================================
run(RawArgs) ->
- _ = application:load(rebar),
+ start_and_load_apps(),
BaseState = init_config(),
BaseState1 = rebar_state:set(BaseState, caller, command_line),
@@ -83,16 +83,6 @@ run(RawArgs) ->
run_aux(BaseState2, RawArgs).
run_aux(State, RawArgs) ->
- %% Make sure crypto is running
- case crypto:start() of
- ok -> ok;
- {error,{already_started,crypto}} -> ok
- end,
- application:start(asn1),
- application:start(public_key),
- application:start(ssl),
- inets:start(),
-
State2 = case os:getenv("REBAR_PROFILE") of
false ->
State;
@@ -105,13 +95,12 @@ run_aux(State, RawArgs) ->
%% Process each command, resetting any state between each one
BaseDir = rebar_state:get(State, base_dir, ?DEFAULT_BASE_DIR),
State3 = rebar_state:set(State2, base_dir,
- filename:join(filename:absname(rebar_state:dir(State2)), BaseDir)),
+ filename:join(filename:absname(rebar_state:dir(State2)), BaseDir)),
{ok, Providers} = application:get_env(rebar, providers),
- State4 = rebar_plugins:install(State3),
-
%% Providers can modify profiles stored in opts, so set default after initializing providers
- State5 = rebar_state:create_logic_providers(Providers, State4),
+ State4 = rebar_state:create_logic_providers(Providers, State3),
+ State5 = rebar_plugins:project_apps_install(State4),
State6 = rebar_state:default(State5, rebar_state:opts(State5)),
{Task, Args} = parse_args(RawArgs),
@@ -131,8 +120,7 @@ init_config() ->
ConfigFile ->
rebar_config:consult_file(ConfigFile)
end,
-
- Config1 = rebar_config:merge_locks(Config, rebar_config:consult_file(?LOCK_FILE)),
+ Config1 = rebar_config:merge_locks(Config, rebar_config:consult_lock_file(?LOCK_FILE)),
%% If $HOME/.config/rebar3/config exists load and use as global config
GlobalConfigFile = rebar_dir:global_config(),
@@ -140,8 +128,19 @@ init_config() ->
true ->
?DEBUG("Load global config file ~p",
[GlobalConfigFile]),
- GlobalConfig = rebar_state:new(rebar_config:consult_file(GlobalConfigFile)),
- rebar_state:new(GlobalConfig, Config1);
+ GlobalConfigTerms = rebar_config:consult_file(GlobalConfigFile),
+ GlobalConfig = rebar_state:new(GlobalConfigTerms),
+
+ %% We don't want to worry about global plugin install state effecting later
+ %% usage. So we throw away the global profile state used for plugin install.
+ GlobalConfigThrowAway = rebar_state:current_profiles(GlobalConfig, [global]),
+ GlobalState = rebar_plugins:handle_plugins(global,
+ rebar_state:get(GlobalConfigThrowAway, plugins, []),
+ GlobalConfigThrowAway),
+ GlobalPlugins = rebar_state:providers(GlobalState),
+ GlobalConfig2 = rebar_state:set(GlobalConfig, plugins, []),
+ GlobalConfig3 = rebar_state:set(GlobalConfig2, {plugins, global}, rebar_state:get(GlobalConfigThrowAway, plugins, [])),
+ rebar_state:providers(rebar_state:new(GlobalConfig3, Config1), GlobalPlugins);
false ->
rebar_state:new(Config1)
end,
@@ -255,5 +254,32 @@ handle_error(Error) ->
%% Dump this error to console
?ERROR("Uncaught error in rebar_core. Run with DEBUG=1 to see stacktrace", []),
?DEBUG("Uncaught error: ~p", [Error]),
+ case erlang:get_stacktrace() of
+ [] -> ok;
+ Trace ->
+ ?DEBUG("Stack trace to the error location: ~p", [Trace])
+ end,
?INFO("When submitting a bug report, please include the output of `rebar3 report \"your command\"`", []),
erlang:halt(1).
+
+start_and_load_apps() ->
+ _ = application:load(rebar),
+ %% Make sure crypto is running
+ case crypto:start() of
+ ok -> ok;
+ {error,{already_started,crypto}} -> ok
+ end,
+ application:start(asn1),
+ application:start(public_key),
+ application:start(ssl),
+ inets:start(),
+ inets:start(httpc, [{profile, hex}]),
+ http_opts().
+
+http_opts() ->
+ Opts = [{max_sessions, 4},
+ {max_keep_alive_length, 4},
+ {keep_alive_timeout, 120000},
+ {max_pipeline_length, 4},
+ {pipeline_timeout, 60000}],
+ httpc:set_options(Opts, hex).
diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl
new file mode 100644
index 0000000..24ac626
--- /dev/null
+++ b/src/rebar_agent.erl
@@ -0,0 +1,118 @@
+-module(rebar_agent).
+-export([start_link/1, do/1, do/2]).
+-export([init/1,
+ handle_call/3, handle_cast/2, handle_info/2,
+ code_change/3, terminate/2]).
+
+-include("rebar.hrl").
+
+-record(state, {state,
+ cwd,
+ show_warning=true}).
+
+start_link(State) ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE, State, []).
+
+do(Command) when is_atom(Command) ->
+ gen_server:call(?MODULE, {cmd, Command}, infinity).
+
+do(Namespace, Command) when is_atom(Namespace), is_atom(Command) ->
+ gen_server:call(?MODULE, {cmd, Namespace, Command}, infinity).
+
+init(State) ->
+ Cwd = rebar_dir:get_cwd(),
+ {ok, #state{state=State, cwd=Cwd}}.
+
+handle_call({cmd, Command}, _From, State=#state{state=RState, cwd=Cwd}) ->
+ MidState = maybe_show_warning(State),
+ {Res, NewRState} = run(default, Command, RState, Cwd),
+ {reply, Res, MidState#state{state=NewRState}};
+handle_call({cmd, Namespace, Command}, _From, State = #state{state=RState, cwd=Cwd}) ->
+ MidState = maybe_show_warning(State),
+ {Res, NewRState} = run(Namespace, Command, RState, Cwd),
+ {reply, Res, MidState#state{state=NewRState}};
+handle_call(_Call, _From, State) ->
+ {noreply, State}.
+
+handle_cast(_Cast, State) ->
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+run(Namespace, Command, RState, Cwd) ->
+ try
+ case rebar_dir:get_cwd() of
+ Cwd ->
+ Args = [atom_to_list(Namespace), atom_to_list(Command)],
+ CmdState0 = refresh_state(RState, Cwd),
+ CmdState1 = rebar_state:set(CmdState0, task, atom_to_list(Command)),
+ CmdState = rebar_state:set(CmdState1, caller, api),
+ case rebar3:run(CmdState, Args) of
+ {ok, TmpState} ->
+ refresh_paths(TmpState),
+ {ok, CmdState};
+ {error, Err} when is_list(Err) ->
+ refresh_paths(CmdState),
+ {{error, lists:flatten(Err)}, CmdState};
+ {error, Err} ->
+ refresh_paths(CmdState),
+ {{error, Err}, CmdState}
+ end;
+ _ ->
+ {{error, cwd_changed}, RState}
+ end
+ catch
+ Type:Reason ->
+ ?DEBUG("Agent Stacktrace: ~p", [erlang:get_stacktrace()]),
+ {{error, {Type, Reason}}, RState}
+ end.
+
+maybe_show_warning(S=#state{show_warning=true}) ->
+ ?WARN("This feature is experimental and may be modified or removed at any time.", []),
+ S#state{show_warning=false};
+maybe_show_warning(State) ->
+ State.
+
+refresh_paths(RState) ->
+ ToRefresh = (rebar_state:code_paths(RState, all_deps)
+ ++ [filename:join([rebar_app_info:out_dir(App), "test"])
+ || App <- rebar_state:project_apps(RState)]
+ %% make sure to never reload self; halt()s the VM
+ ) -- [filename:dirname(code:which(?MODULE))],
+ %% 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'
+ %% allows to load and compile files that are not to be kept
+ %% in the app file.
+ lists:foreach(fun(Path) ->
+ Name = filename:basename(Path, "/ebin"),
+ Files = filelib:wildcard(filename:join([Path, "*.beam"])),
+ Modules = [list_to_atom(filename:basename(F, ".beam"))
+ || F <- Files],
+ App = list_to_atom(Name),
+ application:load(App),
+ case application:get_key(App, modules) of
+ undefined ->
+ code:add_patha(Path),
+ ok;
+ {ok, _} ->
+ ?DEBUG("reloading ~p from ~s", [Modules, Path]),
+ code:replace_path(Name, Path),
+ [begin code:purge(M), code:delete(M), code:load_file(M) end
+ || M <- Modules]
+ end
+ end, ToRefresh).
+
+refresh_state(RState, _Dir) ->
+ lists:foldl(
+ fun(F, State) -> F(State) end,
+ rebar3:init_config(),
+ [fun(S) -> rebar_state:apply_profiles(S, rebar_state:current_profiles(RState)) end]
+ ).
diff --git a/src/rebar_app_discover.erl b/src/rebar_app_discover.erl
index 73401bc..9c4a5ff 100644
--- a/src/rebar_app_discover.erl
+++ b/src/rebar_app_discover.erl
@@ -7,6 +7,7 @@
find_apps/2,
find_app/2]).
+-include("rebar.hrl").
-include_lib("providers/include/providers.hrl").
do(State, LibDirs) ->
@@ -19,13 +20,19 @@ do(State, LibDirs) ->
%% Sort apps so we get the same merged deps config everytime
SortedApps = rebar_utils:sort_deps(Apps),
lists:foldl(fun(AppInfo, StateAcc) ->
- {AppInfo1, StateAcc1} = merge_deps(AppInfo, StateAcc),
Name = rebar_app_info:name(AppInfo),
- OutDir = filename:join(DepsDir, Name),
- AppInfo2 = rebar_app_info:out_dir(AppInfo1, OutDir),
- ProjectDeps1 = lists:delete(Name, ProjectDeps),
- rebar_state:project_apps(StateAcc1
- ,rebar_app_info:deps(AppInfo2, ProjectDeps1))
+ case enable(State, AppInfo) of
+ true ->
+ {AppInfo1, StateAcc1} = merge_deps(AppInfo, StateAcc),
+ OutDir = filename:join(DepsDir, Name),
+ AppInfo2 = rebar_app_info:out_dir(AppInfo1, OutDir),
+ ProjectDeps1 = lists:delete(Name, ProjectDeps),
+ rebar_state:project_apps(StateAcc1
+ ,rebar_app_info:deps(AppInfo2, ProjectDeps1));
+ false ->
+ ?INFO("Ignoring ~s", [Name]),
+ StateAcc
+ end
end, State, SortedApps).
format_error({module_list, File}) ->
@@ -44,7 +51,8 @@ merge_deps(AppInfo, State) ->
rebar_state:apply_profiles(
rebar_state:new(reset_hooks(rebar_state:opts(State, Default)), C,
rebar_app_info:dir(AppInfo)), CurrentProfiles), Name),
- AppInfo1 = rebar_app_info:state(AppInfo, AppState),
+ AppState1 = rebar_state:set(AppState, artifacts, []),
+ AppInfo1 = rebar_app_info:state(AppInfo, AppState1),
State1 = lists:foldl(fun(Profile, StateAcc) ->
AppProfDeps = rebar_state:get(AppState, {deps, Profile}, []),
@@ -65,10 +73,10 @@ project_app_config(AppInfo, State) ->
%% Here we check if the app is at the root of the project.
%% If it is, then drop the hooks from the config so they aren't run twice
maybe_reset_hooks(C, Dir, State) ->
- case filename:dirname(rebar_dir:root_dir(State)) of
+ case ec_file:real_dir_path(rebar_dir:root_dir(State)) of
Dir ->
C1 = proplists:delete(provider_hooks, C),
- proplists:delete(hooks, C1);
+ proplists:delete(post_hooks, proplists:delete(pre_hooks, C1));
_ ->
C
end.
@@ -123,9 +131,42 @@ find_apps(LibDirs, Validate) ->
find_app(AppDir, Validate) ->
AppFile = filelib:wildcard(filename:join([AppDir, "ebin", "*.app"])),
AppSrcFile = filelib:wildcard(filename:join([AppDir, "src", "*.app.src"])),
- case AppFile of
- [File] ->
- AppInfo = create_app_info(AppDir, File),
+ AppInfo = try_handle_app_file(AppFile, AppDir, AppSrcFile, Validate),
+ AppInfo.
+
+app_dir(AppFile) ->
+ filename:join(rebar_utils:droplast(filename:split(filename:dirname(AppFile)))).
+
+-spec create_app_info(file:name(), file:name()) -> rebar_app_info:t() | {error, term()}.
+create_app_info(AppDir, AppFile) ->
+ [{application, AppName, AppDetails}] = rebar_config:consult_app_file(AppFile),
+ AppVsn = proplists:get_value(vsn, AppDetails),
+ Applications = proplists:get_value(applications, AppDetails, []),
+ IncludedApplications = proplists:get_value(included_applications, AppDetails, []),
+ {ok, AppInfo} = rebar_app_info:new(AppName, AppVsn, AppDir, []),
+ AppInfo1 = rebar_app_info:applications(
+ rebar_app_info:app_details(AppInfo, AppDetails),
+ IncludedApplications++Applications),
+ Valid = case rebar_app_utils:validate_application_info(AppInfo1) of
+ true ->
+ true;
+ _ ->
+ false
+ end,
+ rebar_app_info:dir(rebar_app_info:valid(AppInfo1, Valid), AppDir).
+
+dedup([]) -> [];
+dedup([A]) -> [A];
+dedup([H,H|T]) -> dedup([H|T]);
+dedup([H|T]) -> [H|dedup(T)].
+
+%% Read in and parse the .app file if it is availabe. Do the same for
+%% the .app.src file if it exists.
+try_handle_app_file([], AppDir, AppSrcFile, Validate) ->
+ try_handle_app_src_file([], AppDir, AppSrcFile, Validate);
+try_handle_app_file([File], AppDir, AppSrcFile, Validate) ->
+ try create_app_info(AppDir, File) of
+ AppInfo ->
AppInfo1 = rebar_app_info:app_file(AppInfo, File),
AppInfo2 = case AppSrcFile of
[F] ->
@@ -134,7 +175,7 @@ find_app(AppDir, Validate) ->
AppInfo1;
Other when is_list(Other) ->
throw({error, {multiple_app_files, Other}})
- end,
+ end,
case Validate of
valid ->
case rebar_app_utils:validate_application_info(AppInfo2) of
@@ -152,57 +193,35 @@ find_app(AppDir, Validate) ->
end;
all ->
{true, AppInfo2}
- end;
- [] ->
- case AppSrcFile of
- [File] ->
- case Validate of
- V when V =:= invalid ; V =:= all ->
- AppInfo = create_app_info(AppDir, File),
- case AppInfo of
- {error, Reason} ->
- throw({error, {invalid_app_file, File, Reason}});
- _ ->
- {true, rebar_app_info:app_file_src(AppInfo, File)}
- end;
- valid ->
- false
- end;
- [] ->
- false;
- Other when is_list(Other) ->
- throw({error, {multiple_app_files, Other}})
- end;
- Other when is_list(Other) ->
- throw({error, {multiple_app_files, Other}})
- end.
-
-app_dir(AppFile) ->
- filename:join(rebar_utils:droplast(filename:split(filename:dirname(AppFile)))).
-
--spec create_app_info(file:name(), file:name()) -> rebar_app_info:t() | {error, term()}.
-create_app_info(AppDir, AppFile) ->
- case file:consult(AppFile) of
- {ok, [{application, AppName, AppDetails}]} ->
- AppVsn = proplists:get_value(vsn, AppDetails),
- Applications = proplists:get_value(applications, AppDetails, []),
- IncludedApplications = proplists:get_value(included_applications, AppDetails, []),
- {ok, AppInfo} = rebar_app_info:new(AppName, AppVsn, AppDir, []),
- AppInfo1 = rebar_app_info:applications(
- rebar_app_info:app_details(AppInfo, AppDetails),
- IncludedApplications++Applications),
- Valid = case rebar_app_utils:validate_application_info(AppInfo1) of
- true ->
- true;
- _ ->
- false
- end,
- rebar_app_info:dir(rebar_app_info:valid(AppInfo1, Valid), AppDir);
+ end
+ catch
+ throw:{error, {Module, Reason}} ->
+ ?DEBUG("Falling back to app.src file because .app failed: ~s", [Module:format_error(Reason)]),
+ try_handle_app_src_file(File, AppDir, AppSrcFile, Validate)
+ end;
+try_handle_app_file(Other, _AppDir, _AppSrcFile, _Validate) ->
+ throw({error, {multiple_app_files, Other}}).
+
+%% Read in the .app.src file if we aren't looking for a valid (already built) app
+try_handle_app_src_file(_, _AppDir, [], _Validate) ->
+ false;
+try_handle_app_src_file(_, _AppDir, _AppSrcFile, valid) ->
+ false;
+try_handle_app_src_file(_, AppDir, [File], Validate) when Validate =:= invalid
+ ; Validate =:= all ->
+ AppInfo = create_app_info(AppDir, File),
+ case AppInfo of
{error, Reason} ->
- {error, Reason}
- end.
+ throw({error, {invalid_app_file, File, Reason}});
+ _ ->
+ {true, rebar_app_info:app_file_src(AppInfo, File)}
+ end;
+try_handle_app_src_file(_, _AppDir, Other, _Validate) ->
+ throw({error, {multiple_app_files, Other}}).
-dedup([]) -> [];
-dedup([A]) -> [A];
-dedup([H,H|T]) -> dedup([H|T]);
-dedup([H|T]) -> [H|dedup(T)].
+enable(State, AppInfo) ->
+ not lists:member(to_atom(rebar_app_info:name(AppInfo)),
+ rebar_state:get(State, excluded_apps, [])).
+
+to_atom(Bin) ->
+ list_to_atom(binary_to_list(Bin)).
diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl
index 91640f2..6e35b8f 100644
--- a/src/rebar_app_info.erl
+++ b/src/rebar_app_info.erl
@@ -34,29 +34,35 @@
source/2,
state/1,
state/2,
+ state_or_new/2,
+ is_lock/1,
+ is_lock/2,
is_checkout/1,
is_checkout/2,
valid/1,
valid/2]).
+-include("rebar.hrl").
+
-export_type([t/0]).
--record(app_info_t, {name :: binary(),
- app_file_src :: file:filename_all() | undefined,
- app_file :: file:filename_all() | undefined,
- config :: rebar_state:t() | undefined,
- original_vsn :: binary() | string() | undefined,
- app_details=[] :: list(),
- applications=[] :: list(),
- deps=[] :: list(),
+-record(app_info_t, {name :: binary(),
+ app_file_src :: file:filename_all() | undefined,
+ app_file :: file:filename_all() | undefined,
+ config :: rebar_state:t() | undefined,
+ original_vsn :: binary() | string() | undefined,
+ app_details=[] :: list(),
+ applications=[] :: list(),
+ deps=[] :: list(),
profiles=[default] :: [atom()],
- dep_level=0 :: integer(),
- dir :: file:name(),
- out_dir :: file:name(),
- source :: string() | tuple() | undefined,
- state :: rebar_state:t() | undefined,
- is_checkout=false :: boolean(),
- valid :: boolean()}).
+ dep_level=0 :: integer(),
+ dir :: file:name(),
+ out_dir :: file:name(),
+ source :: string() | tuple() | undefined,
+ state :: rebar_state:t() | undefined,
+ is_lock=false :: boolean(),
+ is_checkout=false :: boolean(),
+ valid :: boolean()}).
%%============================================================================
%% types
@@ -158,17 +164,18 @@ app_file(AppInfo=#app_info_t{}, AppFile) ->
-spec app_details(t()) -> list().
app_details(AppInfo=#app_info_t{app_details=[]}) ->
- AppFile = case app_file(AppInfo) of
- undefined ->
- app_file_src(AppInfo);
- File ->
- File
- end,
- case file:consult(AppFile) of
- {ok, [{application, _, AppDetails}]} ->
- AppDetails;
- _ ->
- []
+ case app_file(AppInfo) of
+ undefined ->
+ rebar_file_utils:try_consult(app_file_src(AppInfo));
+ AppFile ->
+ try
+ rebar_file_utils:try_consult(AppFile)
+ catch
+ throw:{error, {Module, Reason}} ->
+ ?DEBUG("Warning, falling back to .app.src because of: ~s",
+ [Module:format_error(Reason)]),
+ rebar_file_utils:try_consult(app_file_src(AppInfo))
+ end
end;
app_details(#app_info_t{app_details=AppDetails}) ->
AppDetails.
@@ -254,6 +261,22 @@ state(AppInfo=#app_info_t{}, State) ->
state(#app_info_t{state=State}) ->
State.
+-spec state_or_new(rebar_state:t(), t()) -> rebar_state:t().
+state_or_new(State, AppInfo=#app_info_t{state=undefined}) ->
+ AppDir = dir(AppInfo),
+ C = rebar_config:consult(AppDir),
+ rebar_state:new(State, C, AppDir);
+state_or_new(_State, #app_info_t{state=State}) ->
+ State.
+
+-spec is_lock(t(), boolean()) -> t().
+is_lock(AppInfo=#app_info_t{}, IsLock) ->
+ AppInfo#app_info_t{is_lock=IsLock}.
+
+-spec is_lock(t()) -> boolean().
+is_lock(#app_info_t{is_lock=IsLock}) ->
+ IsLock.
+
-spec is_checkout(t(), boolean()) -> t().
is_checkout(AppInfo=#app_info_t{}, IsCheckout) ->
AppInfo#app_info_t{is_checkout=IsCheckout}.
@@ -263,8 +286,9 @@ is_checkout(#app_info_t{is_checkout=IsCheckout}) ->
IsCheckout.
-spec valid(t()) -> boolean().
-valid(AppInfo=#app_info_t{valid=undefined}) ->
- case rebar_app_utils:validate_application_info(AppInfo) of
+valid(AppInfo=#app_info_t{valid=undefined, state=State}) ->
+ case rebar_app_utils:validate_application_info(AppInfo)
+ andalso rebar_state:has_all_artifacts(State) =:= true of
true ->
true;
_ ->
diff --git a/src/rebar_config.erl b/src/rebar_config.erl
index c858fef..3e06c38 100644
--- a/src/rebar_config.erl
+++ b/src/rebar_config.erl
@@ -27,7 +27,10 @@
-module(rebar_config).
-export([consult/1
+ ,consult_app_file/1
,consult_file/1
+ ,consult_lock_file/1
+ ,verify_config_format/1
,format_error/1
,merge_locks/2]).
@@ -43,10 +46,21 @@
consult(Dir) ->
consult_file(filename:join(Dir, ?DEFAULT_CONFIG_FILE)).
--spec consult_file(file:name()) -> [any()].
-consult_file(File) when is_binary(File) ->
- consult_file(binary_to_list(File));
+consult_app_file(File) ->
+ consult_file_(File).
+
+consult_lock_file(File) ->
+ consult_file_(File).
+
consult_file(File) ->
+ Terms = consult_file_(File),
+ true = verify_config_format(Terms),
+ Terms.
+
+-spec consult_file_(file:name()) -> [any()].
+consult_file_(File) when is_binary(File) ->
+ consult_file_(binary_to_list(File));
+consult_file_(File) ->
case filename:extension(File) of
".script" ->
consult_and_eval(remove_script_ext(File), File);
@@ -57,10 +71,17 @@ consult_file(File) ->
{ok, Terms} = consult_and_eval(File, Script),
Terms;
false ->
- try_consult(File)
+ rebar_file_utils:try_consult(File)
end
end.
+verify_config_format([]) ->
+ true;
+verify_config_format([{_Key, _Value} | T]) ->
+ verify_config_format(T);
+verify_config_format([Term | _]) ->
+ throw(?PRV_ERROR({bad_config_format, Term})).
+
%% no lockfile
merge_locks(Config, []) ->
Config;
@@ -78,6 +99,8 @@ merge_locks(Config, [Locks]) ->
NewDeps = find_newly_added(ConfigDeps, Locks),
[{{locks, default}, Locks}, {{deps, default}, NewDeps++Deps} | Config].
+format_error({bad_config_format, Term}) ->
+ io_lib:format("Unable to parse config. Term is not in {Key, Value} format:~n~p", [Term]);
format_error({bad_dep_name, Dep}) ->
io_lib:format("Dependency name must be an atom, instead found: ~p", [Dep]).
@@ -87,22 +110,12 @@ format_error({bad_dep_name, Dep}) ->
consult_and_eval(File, Script) ->
?DEBUG("Evaluating config script ~p", [Script]),
- StateData = try_consult(File),
+ StateData = rebar_file_utils:try_consult(File),
file:script(Script, bs([{'CONFIG', StateData}, {'SCRIPT', Script}])).
remove_script_ext(F) ->
filename:rootname(F, ".script").
-try_consult(File) ->
- case file:consult(File) of
- {ok, Terms} ->
- Terms;
- {error, enoent} ->
- [];
- {error, Reason} ->
- ?ABORT("Failed to read config file ~s:~n ~p", [File, Reason])
- end.
-
bs(Vars) ->
lists:foldl(fun({K,V}, Bs) ->
erl_eval:add_binding(K, V, Bs)
diff --git a/src/rebar_core.erl b/src/rebar_core.erl
index 7fe7332..b1647f0 100644
--- a/src/rebar_core.erl
+++ b/src/rebar_core.erl
@@ -26,9 +26,10 @@
%% -------------------------------------------------------------------
-module(rebar_core).
--export([init_command/2, process_namespace/2, process_command/2, do/2]).
+-export([init_command/2, process_namespace/2, process_command/2, do/2, format_error/1]).
-include("rebar.hrl").
+-include_lib("providers/include/providers.hrl").
init_command(State, do) ->
process_command(rebar_state:namespace(State, default), do);
@@ -119,9 +120,22 @@ do([ProviderName | Rest], State) ->
,rebar_state:providers(State)
,rebar_state:namespace(State))
end,
- case providers:do(Provider, State) of
+
+ try providers:do(Provider, State) of
{ok, State1} ->
do(Rest, State1);
{error, Error} ->
{error, Error}
+ catch
+ error:undef ->
+ %% This should really only happen if a plugin provider doesn't export do/1
+ ?DEBUG("Undefined call to provider's do function:~n~p", [erlang:get_stacktrace()]),
+ ?PRV_ERROR({bad_provider_namespace, ProviderName});
+ error:{badrecord,provider} ->
+ {error, ProviderName}
end.
+
+format_error({bad_provider_namespace, {Namespace, Name}}) ->
+ io_lib:format("Undefined command ~s in namespace ~s", [Name, Namespace]);
+format_error({bad_provider_namespace, Name}) ->
+ io_lib:format("Undefined command ~s", [Name]).
diff --git a/src/rebar_digraph.erl b/src/rebar_digraph.erl
index 9e10d49..d52a811 100644
--- a/src/rebar_digraph.erl
+++ b/src/rebar_digraph.erl
@@ -124,6 +124,10 @@ find_app_by_name(Name, Apps) ->
rebar_app_info:name(App) =:= Name
end, Apps).
+%% The union of all entries in the applications list for an app and
+%% the deps listed in its rebar.config is all deps that may be needed
+%% for building the app.
all_apps_deps(App) ->
- Applications = [atom_to_binary(X, utf8) || X <- rebar_app_info:applications(App)],
- lists:usort(rebar_app_info:deps(App) ++ Applications).
+ Applications = lists:usort([atom_to_binary(X, utf8) || X <- rebar_app_info:applications(App)]),
+ Deps = lists:usort(lists:map(fun({Name, _}) -> Name; (Name) -> Name end, rebar_app_info:deps(App))),
+ lists:umerge(Deps, Applications).
diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl
index a94c72d..7af94ea 100644
--- a/src/rebar_dir.erl
+++ b/src/rebar_dir.erl
@@ -20,7 +20,10 @@
template_dir/1,
processing_base_dir/1,
processing_base_dir/2,
- make_relative_path/2]).
+ make_relative_path/2,
+ src_dirs/1, src_dirs/2,
+ extra_src_dirs/1, extra_src_dirs/2,
+ all_src_dirs/1, all_src_dirs/3]).
-include("rebar.hrl").
@@ -30,15 +33,16 @@ base_dir(State) ->
-spec profile_dir(rebar_state:t(), [atom()]) -> file:filename_all().
profile_dir(State, Profiles) ->
- ProfilesStrings = case [ec_cnv:to_list(P) || P <- Profiles] of
- ["default"] -> ["default"];
+ {BaseDir, ProfilesStrings} = case [ec_cnv:to_list(P) || P <- Profiles] of
+ ["global" | _] -> {?MODULE:global_cache_dir(State), [""]};
+ ["bootstrap", "default"] -> {rebar_state:get(State, base_dir, ?DEFAULT_BASE_DIR), ["default"]};
+ ["default"] -> {rebar_state:get(State, base_dir, ?DEFAULT_BASE_DIR), ["default"]};
%% drop `default` from the profile dir if it's implicit and reverse order
%% of profiles to match order passed to `as`
- ["default"|Rest] -> Rest
+ ["default"|Rest] -> {rebar_state:get(State, base_dir, ?DEFAULT_BASE_DIR), Rest}
end,
ProfilesDir = string:join(ProfilesStrings, "+"),
- filename:join(rebar_state:get(State, base_dir, ?DEFAULT_BASE_DIR), ProfilesDir).
-
+ filename:join(BaseDir, ProfilesDir).
-spec deps_dir(rebar_state:t()) -> file:filename_all().
deps_dir(State) ->
@@ -81,11 +85,11 @@ global_config_dir(State) ->
rebar_state:get(State, global_rebar_dir, filename:join([Home, ".config", "rebar3"])).
global_config(State) ->
- filename:join(global_config_dir(State), "config").
+ filename:join(global_config_dir(State), "rebar.config").
global_config() ->
Home = home_dir(),
- filename:join([Home, ".config", "rebar3", "config"]).
+ filename:join([Home, ".config", "rebar3", "rebar.config"]).
global_cache_dir(State) ->
Home = home_dir(),
@@ -96,7 +100,11 @@ local_cache_dir(Dir) ->
get_cwd() ->
{ok, Dir} = file:get_cwd(),
- Dir.
+ %% On windows cwd may return capital letter for drive,
+ %% for example C:/foobar. But as said in http://www.erlang.org/doc/man/filename.html#join-1
+ %% filename:join/1,2 anyway will convert drive-letter to lowercase, so we have to "internalize"
+ %% cwd as soon as it possible.
+ filename:join([Dir]).
template_globals(State) ->
filename:join([global_config_dir(State), "templates", "globals"]).
@@ -120,3 +128,35 @@ do_make_relative_path([H|T1], [H|T2]) ->
do_make_relative_path(Source, Target) ->
Base = lists:duplicate(max(length(Target) - 1, 0), ".."),
filename:join(Base ++ Source).
+
+-spec src_dirs(rebar_state:t()) -> list(file:filename_all()).
+src_dirs(State) -> src_dirs(State, []).
+
+-spec src_dirs(rebar_state:t(), list(file:filename_all())) -> list(file:filename_all()).
+src_dirs(State, Default) ->
+ ErlOpts = rebar_utils:erl_opts(State),
+ Vs = proplists:get_all_values(src_dirs, ErlOpts),
+ case lists:append([rebar_state:get(State, src_dirs, []) | Vs]) of
+ [] -> Default;
+ Dirs -> Dirs
+ end.
+
+-spec extra_src_dirs(rebar_state:t()) -> list(file:filename_all()).
+extra_src_dirs(State) -> extra_src_dirs(State, []).
+
+-spec extra_src_dirs(rebar_state:t(), list(file:filename_all())) -> list(file:filename_all()).
+extra_src_dirs(State, Default) ->
+ ErlOpts = rebar_utils:erl_opts(State),
+ Vs = proplists:get_all_values(extra_src_dirs, ErlOpts),
+ case lists:append([rebar_state:get(State, extra_src_dirs, []) | Vs]) of
+ [] -> Default;
+ Dirs -> Dirs
+ end.
+
+-spec all_src_dirs(rebar_state:t()) -> list(file:filename_all()).
+all_src_dirs(State) -> all_src_dirs(State, [], []).
+
+-spec all_src_dirs(rebar_state:t(), list(file:filename_all()), list(file:filename_all())) ->
+ list(file:filename_all()).
+all_src_dirs(State, SrcDefault, ExtraDefault) ->
+ src_dirs(State, SrcDefault) ++ extra_src_dirs(State, ExtraDefault).
diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl
index c7a3474..b9072a3 100644
--- a/src/rebar_erlc_compiler.erl
+++ b/src/rebar_erlc_compiler.erl
@@ -143,8 +143,7 @@ doterl_compile(Config, Dir, OutDir, MoreSources, ErlOpts) ->
%% Support the src_dirs option allowing multiple directories to
%% contain erlang source. This might be used, for example, should
%% eunit tests be separated from the core application source.
- SrcDirs = [filename:join(Dir, X) || X <- proplists:get_value(src_dirs, ErlOpts, ["src"]) ++
- proplists:get_value(extra_src_dirs, ErlOpts, [])],
+ SrcDirs = [filename:join(Dir, X) || X <- rebar_dir:all_src_dirs(Config, ["src"], [])],
AllErlFiles = gather_src(SrcDirs, []) ++ MoreSources,
%% Make sure that ebin/ exists and is on the path
diff --git a/src/rebar_fetch.erl b/src/rebar_fetch.erl
index 0aca308..64c5380 100644
--- a/src/rebar_fetch.erl
+++ b/src/rebar_fetch.erl
@@ -72,7 +72,7 @@ format_error({failed_extract, CachePath}) ->
format_error({bad_etag, Source}) ->
io_lib:format("MD5 Checksum comparison failed for: ~s", [Source]);
format_error({fetch_fail, Source}) ->
- io_lib:format("Failed to fetch and copy dep: ~s", [Source]);
+ io_lib:format("Failed to fetch and copy dep: ~p", [Source]);
format_error({bad_checksum, File}) ->
io_lib:format("Checksum mismatch against tarball in ~s", [File]);
format_error({bad_registry_checksum, File}) ->
diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl
index ef2c70f..3fc5698 100644
--- a/src/rebar_file_utils.erl
+++ b/src/rebar_file_utils.erl
@@ -26,7 +26,9 @@
%% -------------------------------------------------------------------
-module(rebar_file_utils).
--export([symlink_or_copy/2,
+-export([try_consult/1,
+ format_error/1,
+ symlink_or_copy/2,
rm_rf/1,
cp_r/2,
mv/2,
@@ -34,14 +36,32 @@
write_file_if_contents_differ/2,
system_tmpdir/0,
system_tmpdir/1,
- reset_dir/1]).
+ reset_dir/1,
+ touch/1]).
-include("rebar.hrl").
+-include_lib("providers/include/providers.hrl").
+-include_lib("kernel/include/file.hrl").
+
+
%% ===================================================================
%% Public API
%% ===================================================================
+try_consult(File) ->
+ case file:consult(File) of
+ {ok, Terms} ->
+ Terms;
+ {error, enoent} ->
+ [];
+ {error, Reason} ->
+ throw(?PRV_ERROR({bad_term_file, File, Reason}))
+ end.
+
+format_error({bad_term_file, AppFile, Reason}) ->
+ io_lib:format("Error reading file ~s: ~s", [AppFile, file:format_error(Reason)]).
+
symlink_or_copy(Source, Target) ->
Link = case os:type() of
{win32, _} ->
@@ -55,9 +75,40 @@ symlink_or_copy(Source, Target) ->
{error, eexist} ->
ok;
{error, _} ->
- cp_r([Source], Target)
+ case os:type() of
+ {win32, _} ->
+ S = unicode:characters_to_list(Source),
+ T = unicode:characters_to_list(Target),
+ case filelib:is_dir(S) of
+ true ->
+ win32_symlink(S, T);
+ false ->
+ cp_r([S], T)
+ end;
+ _ ->
+ case filelib:is_dir(Target) of
+ true ->
+ ok;
+ false ->
+ cp_r([Source], Target)
+ end
+ end
end.
+win32_symlink(Source, Target) ->
+ Res = rebar_utils:sh(
+ ?FMT("cmd /c mklink /j \"~s\" \"~s\"",
+ [filename:nativename(Target), filename:nativename(Source)]),
+ [{use_stdout, false}, return_on_error]),
+ case win32_ok(Res) of
+ true -> ok;
+ false ->
+ {error, lists:flatten(
+ io_lib:format("Failed to symlink ~s to ~s~n",
+ [Source, Target]))}
+ end.
+
+
%% @doc Remove files and directories.
%% Target is a single filename, directoryname or wildcard expression.
-spec rm_rf(string()) -> 'ok'.
@@ -104,21 +155,24 @@ mv(Source, Dest) ->
[{use_stdout, false}, abort_on_error]),
ok;
{win32, _} ->
- {ok, R} = rebar_utils:sh(
- ?FMT("move /y \"~s\" \"~s\" 1> nul",
+ Res = rebar_utils:sh(
+ ?FMT("robocopy /move /s \"~s\" \"~s\" 1> nul",
[filename:nativename(Source),
filename:nativename(Dest)]),
[{use_stdout, false}, return_on_error]),
- case R of
- [] ->
- ok;
- _ ->
+ case win32_ok(Res) of
+ true -> ok;
+ false ->
{error, lists:flatten(
io_lib:format("Failed to move ~s to ~s~n",
[Source, Dest]))}
end
end.
+win32_ok({ok, _}) -> true;
+win32_ok({error, {Rc, _}}) when Rc<9; Rc=:=16 -> true;
+win32_ok(_) -> false.
+
delete_each([]) ->
ok;
delete_each([File | Rest]) ->
@@ -170,6 +224,17 @@ reset_dir(Path) ->
%% recreate the directory
filelib:ensure_dir(filename:join([Path, "dummy.beam"])).
+
+%% Linux touch but using erlang functions to work in bot *nix os and
+%% windows
+-spec touch(Path) -> ok | {error, Reason} when
+ Path :: file:name(),
+ Reason :: file:posix().
+touch(Path) ->
+ {ok, A} = file:read_file_info(Path),
+ ok = file:write_file_info(Path, A#file_info{mtime = calendar:local_time(),
+ atime = calendar:local_time()}).
+
%% ===================================================================
%% Internal functions
%% ===================================================================
@@ -182,28 +247,27 @@ delete_each_dir_win32([Dir | Rest]) ->
delete_each_dir_win32(Rest).
xcopy_win32(Source,Dest)->
- {ok, R} = rebar_utils:sh(
- ?FMT("xcopy \"~s\" \"~s\" /q /y /e 2> nul",
+ %% "xcopy \"~s\" \"~s\" /q /y /e 2> nul", Chanegd to robocopy to
+ %% handle long names. May have issues with older windows.
+ Res = rebar_utils:sh(
+ ?FMT("robocopy \"~s\" \"~s\" /e /is /purge 2> nul",
[filename:nativename(Source), filename:nativename(Dest)]),
[{use_stdout, false}, return_on_error]),
- case length(R) > 0 of
- %% when xcopy fails, stdout is empty and and error message is printed
- %% to stderr (which is redirected to nul)
- true -> ok;
- false ->
- {error, lists:flatten(
- io_lib:format("Failed to xcopy from ~s to ~s~n",
- [Source, Dest]))}
+ case win32_ok(Res) of
+ true -> ok;
+ false ->
+ {error, lists:flatten(
+ io_lib:format("Failed to copy ~s to ~s~n",
+ [Source, Dest]))}
end.
cp_r_win32({true, SourceDir}, {true, DestDir}) ->
%% from directory to directory
- SourceBase = filename:basename(SourceDir),
- ok = case file:make_dir(filename:join(DestDir, SourceBase)) of
+ ok = case file:make_dir(DestDir) of
{error, eexist} -> ok;
Other -> Other
end,
- ok = xcopy_win32(SourceDir, filename:join(DestDir, SourceBase));
+ ok = xcopy_win32(SourceDir, DestDir);
cp_r_win32({false, Source} = S,{true, DestDir}) ->
%% from file to directory
cp_r_win32(S, {false, filename:join(DestDir, filename:basename(Source))});
diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl
index 2d83579..dfec86a 100644
--- a/src/rebar_git_resource.erl
+++ b/src/rebar_git_resource.erl
@@ -109,7 +109,7 @@ download(Dir, {git, Url, Rev}, _State) ->
rebar_utils:sh(?FMT("git checkout -q ~s", [Rev]), [{cd, Dir}]).
make_vsn(Dir) ->
- {ok, Cwd} = file:get_cwd(),
+ Cwd = rebar_dir:get_cwd(),
try
ok = file:set_cwd(Dir),
{Vsn, RawRef, RawCount} = collect_default_refcount(),
diff --git a/src/rebar_hooks.erl b/src/rebar_hooks.erl
index e144a8e..56c4e91 100644
--- a/src/rebar_hooks.erl
+++ b/src/rebar_hooks.erl
@@ -1,6 +1,10 @@
-module(rebar_hooks).
--export([run_all_hooks/5]).
+-export([run_all_hooks/5
+ ,format_error/1]).
+
+-include("rebar.hrl").
+-include_lib("providers/include/providers.hrl").
-spec run_all_hooks(file:filename_all(), pre | post,
atom() | {atom(), atom()} | string(),
@@ -10,12 +14,50 @@ run_all_hooks(Dir, Type, Command, Providers, State) ->
run_hooks(Dir, Type, Command, State).
run_provider_hooks(Dir, Type, Command, Providers, State) ->
- State1 = rebar_state:providers(rebar_state:dir(State, Dir), Providers),
+ PluginDepsPaths = rebar_state:code_paths(State, all_plugin_deps),
+ code:add_pathsa(PluginDepsPaths),
+ Providers1 = rebar_state:providers(State),
+ State1 = rebar_state:providers(rebar_state:dir(State, Dir), Providers++Providers1),
AllHooks = rebar_state:get(State1, provider_hooks, []),
TypeHooks = proplists:get_value(Type, AllHooks, []),
HookProviders = proplists:get_all_values(Command, TypeHooks),
- rebar_core:do(HookProviders, State1).
+ case rebar_core:do(HookProviders, State1) of
+ {error, ProviderName} ->
+ ?DEBUG(format_error({bad_provider, Type, Command, ProviderName}), []),
+ throw(?PRV_ERROR({bad_provider, Type, Command, ProviderName}));
+ {ok, _} ->
+ rebar_utils:remove_from_code_path(PluginDepsPaths)
+ end.
+
+format_error({bad_provider, Type, Command, {Name, Namespace}}) ->
+ io_lib:format("Unable to run ~s hooks for '~p', command '~p' in namespace '~p' not found.", [Type, Command, Namespace, Name]);
+format_error({bad_provider, Type, Command, Name}) ->
+ io_lib:format("Unable to run ~s hooks for '~p', command '~p' not found.", [Type, Command, Name]).
+
+%% @doc The following environment variables are exported when running
+%% a hook (absolute paths):
+%%
+%% REBAR_DEPS_DIR = rebar_dir:deps_dir/1
+%% REBAR_BUILD_DIR = rebar_dir:base_dir/1
+%% REBAR_ROOT_DIR = rebar_dir:root_dir/1
+%% REBAR_CHECKOUTS_DIR = rebar_dir:checkouts_dir/1
+%% REBAR_PLUGINS_DIR = rebar_dir:plugins_dir/1
+%% REBAR_GLOBAL_CONFIG_DIR = rebar_dir:global_config_dir/1
+%% REBAR_GLOBAL_CACHE_DIR = rebar_dir:global_cache_dir/1
+%% REBAR_TEMPLATE_DIR = rebar_dir:template_dir/1
+%% REBAR_APP_DIRS = rebar_dir:lib_dirs/1
+%% REBAR_SRC_DIRS = rebar_dir:src_dirs/1
+%%
+%% autoconf compatible variables
+%% (see: http://www.gnu.org/software/autoconf/manual/autoconf.html#Erlang-Libraries):
+%% ERLANG_ERTS_VER = erlang:system_info(version)
+%% ERLANG_ROOT_DIR = code:root_dir/0
+%% ERLANG_LIB_DIR_erl_interface = code:lib_dir(erl_interface)
+%% ERLANG_LIB_VER_erl_interface = version part of path returned by code:lib_dir(erl_interface)
+%% ERL = ERLANG_ROOT_DIR/bin/erl
+%% ERLC = ERLANG_ROOT_DIR/bin/erl
+%%
run_hooks(Dir, Type, Command, State) ->
Hooks = case Type of
pre ->
@@ -25,7 +67,8 @@ run_hooks(Dir, Type, Command, State) ->
_ ->
[]
end,
- Env = [{"REBAR_DEPS_DIR", filename:absname(rebar_dir:deps_dir(State))}],
+
+ Env = create_env(State),
lists:foreach(fun({_, C, _}=Hook) when C =:= Command ->
apply_hook(Dir, Env, Hook);
({C, _}=Hook) when C =:= Command ->
@@ -44,3 +87,33 @@ apply_hook(Dir, Env, {Arch, Command, Hook}) ->
apply_hook(Dir, Env, {Command, Hook}) ->
Msg = lists:flatten(io_lib:format("Hook for ~p failed!~n", [Command])),
rebar_utils:sh(Hook, [use_stdout, {cd, Dir}, {env, Env}, {abort_on_error, Msg}]).
+
+create_env(State) ->
+ BaseDir = rebar_state:dir(State),
+ [
+ {"REBAR_DEPS_DIR", filename:absname(rebar_dir:deps_dir(State))},
+ {"REBAR_BUILD_DIR", filename:absname(rebar_dir:base_dir(State))},
+ {"REBAR_ROOT_DIR", filename:absname(rebar_dir:root_dir(State))},
+ {"REBAR_CHECKOUTS_DIR", filename:absname(rebar_dir:checkouts_dir(State))},
+ {"REBAR_PLUGINS_DIR", filename:absname(rebar_dir:plugins_dir(State))},
+ {"REBAR_GLOBAL_CONFIG_DIR", filename:absname(rebar_dir:global_config_dir(State))},
+ {"REBAR_GLOBAL_CACHE_DIR", filename:absname(rebar_dir:global_cache_dir(State))},
+ {"REBAR_TEMPLATE_DIR", filename:absname(rebar_dir:template_dir(State))},
+ {"REBAR_APP_DIRS", join_dirs(BaseDir, rebar_dir:lib_dirs(State))},
+ {"REBAR_SRC_DIRS", join_dirs(BaseDir, rebar_dir:all_src_dirs(State))},
+ {"ERLANG_ERTS_VER", erlang:system_info(version)},
+ {"ERLANG_ROOT_DIR", code:root_dir()},
+ {"ERLANG_LIB_DIR_erl_interface", code:lib_dir(erl_interface)},
+ {"ERLANG_LIB_VER_erl_interface", re_version(code:lib_dir(erl_interface))},
+ {"ERL", filename:join([code:root_dir(), "bin", "erl"])},
+ {"ERLC", filename:join([code:root_dir(), "bin", "erlc"])}
+ ].
+
+join_dirs(BaseDir, Dirs) ->
+ string:join([ filename:join(BaseDir, Dir) || Dir <- Dirs ], ":").
+
+re_version(Path) ->
+ case re:run(Path, "^.*-(?<VER>[^/-]*)$", [{capture, [1], list}]) of
+ nomatch -> "";
+ {match, [Ver]} -> Ver
+ end.
diff --git a/src/rebar_otp_app.erl b/src/rebar_otp_app.erl
index e5ad1d2..9f61e71 100644
--- a/src/rebar_otp_app.erl
+++ b/src/rebar_otp_app.erl
@@ -51,10 +51,10 @@ compile(State, App) ->
%% Load the app file and validate it.
validate_app(State, App1).
-format_error(invalid_app_file) ->
- "Failed to read app file";
+format_error({missing_app_file, Filename}) ->
+ io_lib:format("App file is missing: ~s", [Filename]);
format_error({file_read, File, Reason}) ->
- io_lib:format("Failed to read ~s for processing: ~p", [File, Reason]);
+ io_lib:format("Failed to read app file ~s for processing: ~p", [File, file:format_error(Reason)]);
format_error({invalid_name, File, AppName}) ->
io_lib:format("Invalid ~s: name of application (~p) must match filename.", [File, AppName]).
@@ -160,9 +160,8 @@ ebin_modules(State, App, Dir) ->
[rebar_utils:beam_to_mod(N) || N <- Filtered].
extra_dirs(State) ->
- ErlOpts = rebar_utils:erl_opts(State),
- Extras = proplists:get_value(extra_src_dirs, ErlOpts, []),
- SrcDirs = proplists:get_value(src_dirs, ErlOpts, ["src"]),
+ Extras = rebar_dir:extra_src_dirs(State),
+ SrcDirs = rebar_dir:src_dirs(State, ["src"]),
%% remove any dirs that are defined in `src_dirs` from `extra_src_dirs`
Extras -- SrcDirs.
@@ -198,13 +197,13 @@ ensure_registered(AppData) ->
consult_app_file(Filename) ->
case filelib:is_file(Filename) of
false ->
- throw(?PRV_ERROR(invalid_app_file));
+ {error, enoent};
true ->
case lists:suffix(".app.src", Filename) of
false ->
file:consult(Filename);
true ->
- {ok, rebar_config:consult_file(Filename)}
+ {ok, rebar_config:consult_app_file(Filename)}
end
end.
@@ -215,7 +214,7 @@ app_vsn(AppFile, State) ->
Resources = rebar_state:resources(State),
rebar_utils:vcs_vsn(get_value(vsn, AppData, AppFile), AppDir, Resources);
{error, Reason} ->
- ?ABORT("Failed to consult app file ~s: ~p\n", [AppFile, Reason])
+ throw(?PRV_ERROR({file_read, AppFile, Reason}))
end.
get_value(Key, AppInfo, AppFile) ->
diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl
index 59ce0dc..5b37788 100644
--- a/src/rebar_pkg_resource.erl
+++ b/src/rebar_pkg_resource.erl
@@ -95,7 +95,8 @@ make_vsn(_) ->
request(Url, ETag) ->
case httpc:request(get, {Url, [{"if-none-match", ETag} || ETag =/= false]},
[{relaxed, true}],
- [{body_format, binary}]) of
+ [{body_format, binary}],
+ hex) of
{ok, {{_Version, 200, _Reason}, Headers, Body}} ->
?DEBUG("Successfully downloaded ~s", [Url]),
{"etag", ETag1} = lists:keyfind("etag", 1, Headers),
diff --git a/src/rebar_plugins.erl b/src/rebar_plugins.erl
index c16223e..7e12324 100644
--- a/src/rebar_plugins.erl
+++ b/src/rebar_plugins.erl
@@ -3,7 +3,10 @@
-module(rebar_plugins).
--export([install/1, handle_plugins/2]).
+-export([project_apps_install/1
+ ,install/1
+ ,handle_plugins/3
+ ,handle_plugins/4]).
-include("rebar.hrl").
@@ -11,52 +14,90 @@
%% Public API
%% ===================================================================
+-spec project_apps_install(rebar_state:t()) -> rebar_state:t().
+project_apps_install(State) ->
+ Profiles = rebar_state:current_profiles(State),
+ ProjectApps = rebar_state:project_apps(State),
+
+ lists:foldl(fun(Profile, StateAcc) ->
+ Plugins = rebar_state:get(State, {plugins, Profile}, []),
+ StateAcc1 = handle_plugins(Profile, Plugins, StateAcc),
+
+ lists:foldl(fun(App, StateAcc2) ->
+ AppDir = rebar_app_info:dir(App),
+ C = rebar_config:consult(AppDir),
+ S = rebar_state:new(rebar_state:new(), C, AppDir),
+ Plugins2 = rebar_state:get(S, {plugins, Profile}, []),
+ handle_plugins(Profile, Plugins2, StateAcc2)
+ end, StateAcc1, ProjectApps)
+ end, State, Profiles).
+
-spec install(rebar_state:t()) -> rebar_state:t().
install(State) ->
- Plugins = rebar_state:get(State, plugins, []),
+ Profiles = rebar_state:current_profiles(State),
+ lists:foldl(fun(Profile, StateAcc) ->
+ Plugins = rebar_state:get(State, {plugins, Profile}, []),
+ handle_plugins(Profile, Plugins, StateAcc)
+ end, State, Profiles).
- ProjectApps = rebar_state:project_apps(State),
+handle_plugins(Profile, Plugins, State) ->
+ handle_plugins(Profile, Plugins, State, false).
- OtherPlugins = lists:flatmap(fun(App) ->
- AppDir = rebar_app_info:dir(App),
- C = rebar_config:consult(AppDir),
- S = rebar_state:new(rebar_state:new(), C, AppDir),
- rebar_state:get(S, plugins, [])
- end, ProjectApps),
+handle_plugins(Profile, Plugins, State, Upgrade) ->
+ %% Set deps dir to plugins dir so apps are installed there
+ Locks = rebar_state:lock(State),
+ DepsDir = rebar_state:get(State, deps_dir, ?DEFAULT_DEPS_DIR),
+ State1 = rebar_state:set(State, deps_dir, ?DEFAULT_PLUGINS_DIR),
- handle_plugins(Plugins++OtherPlugins, State).
+ %% Install each plugin individually so if one fails to install it doesn't effect the others
+ {_PluginProviders, State2} =
+ lists:foldl(fun(Plugin, {PluginAcc, StateAcc}) ->
+ {NewPlugins, NewState} = handle_plugin(Profile, Plugin, StateAcc, Upgrade),
+ NewState1 = rebar_state:create_logic_providers(NewPlugins, NewState),
+ {PluginAcc++NewPlugins, NewState1}
+ end, {[], State1}, Plugins),
--spec handle_plugins([rebar_prv_install_deps:dep()], rebar_state:t()) -> rebar_state:t().
-handle_plugins(Plugins, State) ->
- PluginProviders = lists:flatmap(fun(Plugin) ->
- handle_plugin(Plugin, State)
- end, Plugins),
- rebar_state:create_logic_providers(PluginProviders, State).
+ %% reset deps dir
+ State3 = rebar_state:set(State2, deps_dir, DepsDir),
+ rebar_state:lock(State3, Locks).
-handle_plugin(Plugin, State) ->
+handle_plugin(Profile, Plugin, State, Upgrade) ->
try
- %% Set deps dir to plugins dir so apps are installed there
- State1 = rebar_state:set(State, deps_dir, ?DEFAULT_PLUGINS_DIR),
- {ok, _, State2} = rebar_prv_install_deps:handle_deps(default, State1, [Plugin]),
-
- Apps = rebar_state:all_deps(State2),
- ToBuild = lists:dropwhile(fun rebar_app_info:valid/1, Apps),
- [build_plugin(AppInfo) || AppInfo <- ToBuild],
- [true = code:add_patha(filename:join(rebar_app_info:dir(AppInfo), "ebin")) || AppInfo <- Apps],
- plugin_providers(Plugin)
+ {ok, Apps, State2} = rebar_prv_install_deps:handle_deps(Profile, State, [Plugin], Upgrade),
+ {no_cycle, Sorted} = rebar_prv_install_deps:find_cycles(Apps),
+ ToBuild = rebar_prv_install_deps:cull_compile(Sorted, []),
+
+ %% Add already built plugin deps to the code path
+ CodePaths = [rebar_app_info:ebin_dir(A) || A <- Apps -- ToBuild],
+ code:add_pathsa(CodePaths),
+
+ %% Build plugin and its deps
+ [build_plugin(AppInfo, Apps, State2) || AppInfo <- ToBuild],
+
+ %% Add newly built deps and plugin to code path
+ State3 = rebar_state:update_all_plugin_deps(State2, Apps),
+ NewCodePaths = [rebar_app_info:ebin_dir(A) || A <- ToBuild],
+ code:add_pathsa(CodePaths),
+
+ %% Store plugin code paths so we can remove them when compiling project apps
+ State4 = rebar_state:update_code_paths(State3, all_plugin_deps, CodePaths++NewCodePaths),
+
+ {plugin_providers(Plugin), State4}
catch
C:T ->
- ?DEBUG("~p ~p", [C, T]),
+ ?DEBUG("~p ~p ~p", [C, T, erlang:get_stacktrace()]),
?WARN("Plugin ~p not available. It will not be used.", [Plugin]),
- []
+ {[], State}
end.
-build_plugin(AppInfo) ->
- AppDir = rebar_app_info:dir(AppInfo),
- C = rebar_config:consult(AppDir),
- S = rebar_state:new(rebar_state:new(), C, AppDir),
- rebar_prv_compile:compile(S, AppInfo).
+build_plugin(AppInfo, Apps, State) ->
+ Providers = rebar_state:providers(State),
+ Providers1 = rebar_state:providers(rebar_app_info:state(AppInfo)),
+ S = rebar_state:all_deps(rebar_app_info:state_or_new(State, AppInfo), Apps),
+ rebar_prv_compile:compile(S, Providers++Providers1, AppInfo).
+plugin_providers({Plugin, _, _, _}) when is_atom(Plugin) ->
+ validate_plugin(Plugin);
plugin_providers({Plugin, _, _}) when is_atom(Plugin) ->
validate_plugin(Plugin);
plugin_providers({Plugin, _}) when is_atom(Plugin) ->
diff --git a/src/rebar_prv_app_discovery.erl b/src/rebar_prv_app_discovery.erl
index 97862c1..ea55e11 100644
--- a/src/rebar_prv_app_discovery.erl
+++ b/src/rebar_prv_app_discovery.erl
@@ -23,7 +23,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, true},
+ {bare, false},
{deps, ?DEPS},
{example, ""},
{short_desc, ""},
@@ -38,7 +38,7 @@ do(State) ->
State1 = rebar_app_discover:do(State, LibDirs),
{ok, State1}
catch
- throw:{error, Error}->
+ throw:{error, Error} ->
?PRV_ERROR(Error)
end.
@@ -46,12 +46,17 @@ do(State) ->
format_error({multiple_app_files, Files}) ->
io_lib:format("Multiple app files found in one app dir: ~s", [string:join(Files, " and ")]);
format_error({invalid_app_file, File, Reason}) ->
- case Reason of
+ case Reason of
{Line, erl_parse, Description} ->
- io_lib:format("Invalid app file ~s at line ~b: ~p",
+ io_lib:format("Invalid app file ~s at line ~b: ~p",
[File, Line, lists:flatten(Description)]);
_ ->
io_lib:format("Invalid app file ~s: ~p", [File, Reason])
end;
+%% Provide a slightly more informative error message for consult of app file failure
+format_error({rebar_file_utils, {bad_term_file, AppFile, Reason}}) ->
+ io_lib:format("Error in app file ~s: ~s", [rebar_dir:make_relative_path(AppFile,
+ rebar_dir:get_cwd()),
+ file:format_error(Reason)]);
format_error(Reason) ->
io_lib:format("~p", [Reason]).
diff --git a/src/rebar_prv_as.erl b/src/rebar_prv_as.erl
index 64ad951..ead7b01 100644
--- a/src/rebar_prv_as.erl
+++ b/src/rebar_prv_as.erl
@@ -22,7 +22,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 as <profile1>,<profile2>,... <task1>, <task2>, ..."},
{short_desc, "Higher order provider for running multiple tasks in a sequence as a certain profiles."},
@@ -38,13 +38,14 @@ do(State) ->
{error, "At least one profile must be specified when using `as`"};
_ ->
State1 = rebar_state:apply_profiles(State, [list_to_atom(X) || X <- Profiles]),
+ State2 = rebar_plugins:project_apps_install(State1),
{FirstTask, FirstTaskArgs} = hd(Tasks),
FirstTaskAtom = list_to_atom(FirstTask),
- case rebar_core:process_namespace(State1, FirstTaskAtom) of
- {ok, State2, NewTask} ->
+ case rebar_core:process_namespace(State2, FirstTaskAtom) of
+ {ok, State3, NewTask} ->
rebar_prv_do:do_tasks(
[{atom_to_list(NewTask),FirstTaskArgs}|tl(Tasks)],
- State2
+ State3
);
{error, Reason} ->
{error, Reason}
diff --git a/src/rebar_prv_clean.erl b/src/rebar_prv_clean.erl
index 8fafe23..e3cb84e 100644
--- a/src/rebar_prv_clean.erl
+++ b/src/rebar_prv_clean.erl
@@ -22,7 +22,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 clean"},
{short_desc, "Remove compiled beam files from apps."},
@@ -67,11 +67,9 @@ format_error(Reason) ->
clean_apps(State, Providers, Apps) ->
lists:foreach(fun(AppInfo) ->
AppDir = rebar_app_info:dir(AppInfo),
- C = rebar_config:consult(AppDir),
- S = rebar_state:new(State, C, AppDir),
+ S = rebar_app_info:state_or_new(State, AppInfo),
?INFO("Cleaning out ~s...", [rebar_app_info:name(AppInfo)]),
- %% Legacy hook support
rebar_hooks:run_all_hooks(AppDir, pre, ?PROVIDER, Providers, S),
rebar_erlc_compiler:clean(State, rebar_app_info:out_dir(AppInfo)),
rebar_hooks:run_all_hooks(AppDir, post, ?PROVIDER, Providers, S)
diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl
index eb51d8d..2b024cf 100644
--- a/src/rebar_prv_common_test.erl
+++ b/src/rebar_prv_common_test.erl
@@ -25,7 +25,7 @@ init(State) ->
Provider = providers:create([{name, ?PROVIDER},
{module, ?MODULE},
{deps, ?DEPS},
- {bare, false},
+ {bare, true},
{example, "rebar3 ct"},
{short_desc, "Run Common Tests."},
{desc, "Run Common Tests."},
@@ -38,7 +38,7 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
?INFO("Running Common Test suites...", []),
- code:add_pathsa(rebar_state:code_paths(State, all_deps)),
+ rebar_utils:update_code(rebar_state:code_paths(State, all_deps)),
%% Run ct provider prehooks
Providers = rebar_state:providers(State),
@@ -49,7 +49,7 @@ do(State) ->
{ok, State1} = Result ->
%% Run ct provider posthooks
rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State1),
- rebar_utils:cleanup_code_path(rebar_state:code_paths(State1, default)),
+ rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)),
Result;
?PRV_ERROR(_) = Error ->
rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)),
@@ -105,14 +105,18 @@ run_test_verbose(Opts) -> handle_results(ct:run_test(Opts)).
run_test_quiet(Opts) ->
Pid = self(),
+ Ref = erlang:make_ref(),
LogDir = proplists:get_value(logdir, Opts),
- erlang:spawn_monitor(fun() ->
+ {_, Monitor} = erlang:spawn_monitor(fun() ->
{ok, F} = file:open(filename:join([LogDir, "ct.latest.log"]),
[write]),
true = group_leader(F, self()),
- Pid ! ct:run_test(Opts)
+ Pid ! {Ref, ct:run_test(Opts)}
end),
- receive Result -> handle_quiet_results(Opts, Result) end.
+ receive
+ {Ref, Result} -> handle_quiet_results(Opts, Result);
+ {'DOWN', Monitor, _, _, Reason} -> handle_results(?PRV_ERROR(Reason))
+ end.
handle_results(Results) when is_list(Results) ->
Result = lists:foldl(fun sum_results/2, {0, 0, {0,0}}, Results),
@@ -132,8 +136,6 @@ sum_results({Passed, Failed, {UserSkipped, AutoSkipped}},
handle_quiet_results(_, {error, _} = Result) ->
handle_results(Result);
-handle_quiet_results(_, {'DOWN', _, _, _, Reason}) ->
- handle_results(?PRV_ERROR(Reason));
handle_quiet_results(CTOpts, Results) when is_list(Results) ->
_ = [format_result(Result) || Result <- Results],
case handle_results(Results) of
@@ -247,7 +249,7 @@ copy_and_compile_test_suites(State, Opts) ->
Dirs = find_suite_dirs(AllSuites),
lists:foreach(fun(S) ->
NewPath = copy(State, S),
- compile_dir(State, S, NewPath)
+ compile_dir(State, NewPath)
end, Dirs),
NewSuites = lists:map(fun(S) -> retarget_path(State, S) end, AllSuites),
[{suite, NewSuites}|lists:keydelete(suite, 1, Opts)]
@@ -259,12 +261,12 @@ copy_and_compile_test_dirs(State, Opts) ->
%% dir is a single directory
Dir when is_list(Dir), is_integer(hd(Dir)) ->
NewPath = copy(State, Dir),
- [{dir, compile_dir(State, Dir, NewPath)}|lists:keydelete(dir, 1, Opts)];
+ [{dir, compile_dir(State, NewPath)}|lists:keydelete(dir, 1, Opts)];
%% dir is a list of directories
Dirs when is_list(Dirs) ->
NewDirs = lists:map(fun(Dir) ->
NewPath = copy(State, Dir),
- compile_dir(State, Dir, NewPath)
+ compile_dir(State, NewPath)
end, Dirs),
[{dir, NewDirs}|lists:keydelete(dir, 1, Opts)]
end.
@@ -301,11 +303,11 @@ copy(State, Dir) ->
Target
end.
-compile_dir(State, Dir, OutDir) ->
- NewState = replace_src_dirs(State, [Dir]),
- ok = rebar_erlc_compiler:compile(NewState, rebar_state:dir(State), OutDir),
+compile_dir(State, Dir) ->
+ NewState = replace_src_dirs(State, [filename:absname(Dir)]),
+ ok = rebar_erlc_compiler:compile(NewState, rebar_dir:base_dir(State), Dir),
ok = maybe_cover_compile(State, Dir),
- OutDir.
+ Dir.
retarget_path(State, Path) ->
ProjectApps = rebar_state:project_apps(State),
@@ -345,31 +347,39 @@ reduce_path([_|Acc], [".."|Rest]) -> reduce_path(Acc, Rest);
reduce_path([], [".."|Rest]) -> reduce_path([], Rest);
reduce_path(Acc, [Component|Rest]) -> reduce_path([Component|Acc], Rest).
-remove_links(Path) ->
- case ec_file:is_dir(Path) of
- false -> ok;
- true -> remove_links1(Path)
- end.
-remove_links1(Path) ->
+remove_links(Path) ->
+ IsDir = ec_file:is_dir(Path),
case ec_file:is_symlink(Path) of
- true ->
- file:delete(Path);
- false ->
- lists:foreach(fun(ChildPath) ->
- remove_links(ChildPath)
- end, sub_dirs(Path))
+ true when IsDir ->
+ delete_dir_link(Path);
+ false when IsDir ->
+ lists:foreach(fun(ChildPath) ->
+ remove_links(ChildPath)
+ end, dir_entries(Path));
+ _ -> file:delete(Path)
end.
-sub_dirs(Path) ->
+delete_dir_link(Path) ->
+ case os:type() of
+ {unix, _} -> file:delete(Path);
+ {win32, _} -> file:del_dir(Path)
+ end.
+
+dir_entries(Path) ->
{ok, SubDirs} = file:list_dir(Path),
[filename:join(Path, SubDir) || SubDir <- SubDirs].
replace_src_dirs(State, Dirs) ->
%% replace any `src_dirs` with the test dirs
ErlOpts = rebar_state:get(State, erl_opts, []),
- StrippedOpts = lists:keydelete(src_dirs, 1, ErlOpts),
- rebar_state:set(State, erl_opts, [{src_dirs, Dirs}|StrippedOpts]).
+ StrippedErlOpts = filter_src_dirs(ErlOpts),
+ State1 = rebar_state:set(State, erl_opts, StrippedErlOpts),
+ State2 = rebar_state:set(State1, src_dirs, []),
+ rebar_state:set(State2, extra_src_dirs, Dirs).
+
+filter_src_dirs(ErlOpts) ->
+ lists:filter(fun({src_dirs, _}) -> false; ({extra_src_dirs, _}) -> false; (_) -> true end, ErlOpts).
test_dirs(State, Opts) ->
BareTest = filename:join([rebar_state:dir(State), "test"]),
diff --git a/src/rebar_prv_compile.erl b/src/rebar_prv_compile.erl
index f70ca28..6eb8a4f 100644
--- a/src/rebar_prv_compile.erl
+++ b/src/rebar_prv_compile.erl
@@ -6,8 +6,9 @@
do/1,
format_error/1]).
--export([compile/2]).
+-export([compile/3]).
+-include_lib("providers/include/providers.hrl").
-include("rebar.hrl").
-define(PROVIDER, compile).
@@ -21,7 +22,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 compile"},
{short_desc, "Compile apps .app.src and .erl files."},
@@ -32,17 +33,21 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
DepsPaths = rebar_state:code_paths(State, all_deps),
+ PluginDepsPaths = rebar_state:code_paths(State, all_plugin_deps),
+ rebar_utils:remove_from_code_path(PluginDepsPaths),
code:add_pathsa(DepsPaths),
ProjectApps = rebar_state:project_apps(State),
Providers = rebar_state:providers(State),
Deps = rebar_state:deps_to_build(State),
- Cwd = rebar_dir:get_cwd(),
+ Cwd = rebar_state:dir(State),
- %% Need to allow global config vars used on deps
- %% Right now no way to differeniate and just give deps a new state
+ %% Need to allow global config vars used on deps.
+ %% Right now no way to differeniate and just give deps a new state.
+ %% But need an account of "all deps" for some hooks to use.
EmptyState = rebar_state:new(),
- build_apps(EmptyState, Providers, Deps),
+ build_apps(rebar_state:all_deps(EmptyState,
+ rebar_state:all_deps(State)), Providers, Deps),
{ok, ProjectApps1} = rebar_digraph:compile_order(ProjectApps),
@@ -56,12 +61,15 @@ do(State) ->
State3 = rebar_state:code_paths(State2, all_deps, DepsPaths ++ ProjAppsPaths),
rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State2),
+ has_all_artifacts(State3),
rebar_utils:cleanup_code_path(rebar_state:code_paths(State3, default)),
{ok, State3}.
-spec format_error(any()) -> iolist().
+format_error({missing_artifact, File}) ->
+ io_lib:format("Missing artifact ~s", [File]);
format_error(Reason) ->
io_lib:format("~p", [Reason]).
@@ -71,29 +79,22 @@ build_apps(State, Providers, Apps) ->
build_app(State, Providers, AppInfo) ->
AppDir = rebar_app_info:dir(AppInfo),
OutDir = rebar_app_info:out_dir(AppInfo),
-
copy_app_dirs(State, AppDir, OutDir),
- S = case rebar_app_info:state(AppInfo) of
- undefined ->
- C = rebar_config:consult(AppDir),
- rebar_state:new(State, C, AppDir);
- AppState ->
- AppState
- end,
-
- %% Legacy hook support
- rebar_hooks:run_all_hooks(AppDir, pre, ?PROVIDER, Providers, S),
- AppInfo1 = compile(S, AppInfo),
- rebar_hooks:run_all_hooks(AppDir, post, ?PROVIDER, Providers, S),
+ S = rebar_app_info:state_or_new(State, AppInfo),
+ S1 = rebar_state:all_deps(S, rebar_state:all_deps(State)),
+ compile(S1, Providers, AppInfo).
- AppInfo1.
-
-compile(State, AppInfo) ->
+compile(State, Providers, AppInfo) ->
?INFO("Compiling ~s", [rebar_app_info:name(AppInfo)]),
+ AppDir = rebar_app_info:dir(AppInfo),
+ rebar_hooks:run_all_hooks(AppDir, pre, ?PROVIDER, Providers, State),
+
rebar_erlc_compiler:compile(State, ec_cnv:to_list(rebar_app_info:out_dir(AppInfo))),
case rebar_otp_app:compile(State, AppInfo) of
{ok, AppInfo1} ->
+ rebar_hooks:run_all_hooks(AppDir, post, ?PROVIDER, Providers, State),
+ has_all_artifacts(State),
AppInfo1;
Error ->
throw(Error)
@@ -103,6 +104,14 @@ compile(State, AppInfo) ->
%% Internal functions
%% ===================================================================
+has_all_artifacts(State) ->
+ case rebar_state:has_all_artifacts(State) of
+ {false, File} ->
+ throw(?PRV_ERROR({missing_artifact, File}));
+ true ->
+ true
+ end.
+
copy_app_dirs(State, OldAppDir, AppDir) ->
case ec_cnv:to_binary(filename:absname(OldAppDir)) =/=
ec_cnv:to_binary(filename:absname(AppDir)) of
@@ -119,9 +128,7 @@ copy_app_dirs(State, OldAppDir, AppDir) ->
end,
filelib:ensure_dir(filename:join(AppDir, "dummy")),
%% link to src_dirs to be adjacent to ebin is needed for R15 use of cover/xref
- ErlOpts = rebar_utils:erl_opts(State),
- SrcDirs = proplists:get_value(src_dirs, ErlOpts, ["src"]) ++
- proplists:get_value(extra_src_dirs, ErlOpts, []),
+ SrcDirs = rebar_dir:all_src_dirs(State, ["src"], ["test"]),
[symlink_or_copy(OldAppDir, AppDir, Dir) || Dir <- ["priv", "include"] ++ SrcDirs];
false ->
ok
diff --git a/src/rebar_prv_cover.erl b/src/rebar_prv_cover.erl
index 900c569..8c26521 100644
--- a/src/rebar_prv_cover.erl
+++ b/src/rebar_prv_cover.erl
@@ -25,7 +25,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 cover"},
{short_desc, "Perform coverage analysis."},
diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl
index 96e2277..1cf7b71 100644
--- a/src/rebar_prv_dialyzer.erl
+++ b/src/rebar_prv_dialyzer.erl
@@ -14,6 +14,7 @@
-define(PROVIDER, dialyzer).
-define(DEPS, [compile]).
+-define(PLT_PREFIX, "rebar3").
%% ===================================================================
%% Public API
@@ -25,7 +26,7 @@ init(State) ->
{succ_typings, $s, "succ-typings", boolean, "Enable success typing analysis. Default: true"}],
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 dialyzer"},
{short_desc, short_desc()},
@@ -39,33 +40,42 @@ desc() ->
"This command will build, and keep up-to-date, a suitable PLT and will use "
"it to carry out success typing analysis on the current project.\n"
"\n"
- "The following (optional) configurations can be added to a rebar.config:\n"
- "`dialyzer_warnings` - a list of dialyzer warnings\n"
- "`dialyzer_plt` - the PLT file to use\n"
- "`dialyzer_plt_apps` - a list of applications to include in the PLT file*\n"
- "`dialyzer_plt_warnings` - display warnings when updating a PLT file "
- "(boolean)\n"
- "`dialyzer_base_plt` - the base PLT file to use**\n"
- "`dialyzer_base_plt_dir` - the base PLT directory**\n"
- "`dialyzer_base_plt_apps` - a list of applications to include in the base "
- "PLT file**\n"
+ "The following (optional) configurations can be added to a `proplist` of "
+ "options `dialyzer` in rebar.config:\n"
+ "`warnings` - a list of dialyzer warnings\n"
+ "`get_warnings` - display warnings when altering a PLT file (boolean)\n"
+ "`plt_extra_apps` - a list of applications to include in the PLT file*\n"
+ "`plt_location` - the location of the PLT file, `local` to store in the "
+ "profile's base directory (default) or a custom directory.\n"
+ "`plt_prefix` - the prefix to the PLT file, defaults to \"rebar3\"**\n"
+ "`base_plt_apps` - a list of applications to include in the base "
+ "PLT file***\n"
+ "`base_plt_location` - the location of base PLT file, `global` to store in "
+ "$HOME/.cache/rebar3 (default) or a custom directory***\n"
+ "`base_plt_prefix` - the prefix to the base PLT file, defaults to "
+ "\"rebar3\"** ***\n"
+ "\n"
+ "For example, to warn on unmatched returns: \n"
+ "{dialyzer, [{warnings, [unmatched_returns]}]}.\n"
"\n"
"*The applications in `dialyzer_base_plt_apps` and any `applications` and "
"`included_applications` listed in their .app files will be added to the "
"list.\n"
- "**The base PLT is a PLT containing the core OTP applications often "
- "required for a project's PLT. One base PLT is created per OTP version and "
- "stored in `dialyzer_base_plt_dir` (defaults to $HOME/.rebar3/). A base "
- "PLT is used to create a project's initial PLT.".
+ "**PLT files are named \"<prefix>_<otp_release>_plt\".\n"
+ "***The base PLT is a PLT containing the core applications often required "
+ "for a project's PLT. One base PLT is created per OTP version and "
+ "stored in `base_plt_location`. A base PLT is used to build project PLTs."
+ "\n".
short_desc() ->
"Run the Dialyzer analyzer on the project.".
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
+ maybe_fix_env(),
?INFO("Dialyzer starting, this may take a while...", []),
code:add_pathsa(rebar_state:code_paths(State, all_deps)),
- Plt = get_plt_location(State),
+ Plt = get_plt(State),
try
do(State, Plt)
@@ -74,17 +84,28 @@ do(State) ->
?PRV_ERROR({error_processing_apps, Error});
throw:{dialyzer_warnings, Warnings} ->
?PRV_ERROR({dialyzer_warnings, Warnings});
+ throw:{unknown_application, _} = Error ->
+ ?PRV_ERROR(Error);
throw:{output_file_error, _, _} = Error ->
?PRV_ERROR(Error)
after
rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default))
end.
+%% This is used to workaround dialyzer quirk discussed here
+%% https://github.com/rebar/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() ->
+ os:putenv("DIALYZER_PLT", filename:join(rebar_dir:home_dir(), ".dialyzer_plt")).
+
-spec format_error(any()) -> iolist().
format_error({error_processing_apps, Error}) ->
io_lib:format("Error in dialyzing apps: ~s", [Error]);
format_error({dialyzer_warnings, Warnings}) ->
io_lib:format("Warnings occured running dialyzer: ~b", [Warnings]);
+format_error({unknown_application, App}) ->
+ io_lib:format("Could not find application: ~s", [App]);
format_error({output_file_error, File, Error}) ->
Error1 = file:format_error(Error),
io_lib:format("Failed to write to ~s: ~s", [File, Error1]);
@@ -93,13 +114,19 @@ format_error(Reason) ->
%% Internal functions
-get_plt_location(State) ->
- BaseDir = rebar_dir:base_dir(State),
- DefaultPlt = filename:join(BaseDir, default_plt()),
- rebar_state:get(State, dialyzer_plt, DefaultPlt).
+get_plt(State) ->
+ Prefix = get_config(State, plt_prefix, ?PLT_PREFIX),
+ Name = plt_name(Prefix),
+ case get_config(State, plt_location, local) of
+ local ->
+ BaseDir = rebar_dir:base_dir(State),
+ filename:join(BaseDir, Name);
+ Dir ->
+ filename:join(Dir, Name)
+ end.
-default_plt() ->
- rebar_utils:otp_release() ++ ".plt".
+plt_name(Prefix) ->
+ Prefix ++ "_" ++ rebar_utils:otp_release() ++ "_plt".
do(State, Plt) ->
Output = get_output_file(State),
@@ -138,21 +165,17 @@ update_proj_plt(State, Plt, Output) ->
do_update_proj_plt(State, Plt, Output) ->
?INFO("Updating plt...", []),
- {Files, Warnings} = proj_plt_files(State),
- Warnings2 = format_warnings(Output, Warnings),
- {Warnings3, State2} = case read_plt(State, Plt) of
- {ok, OldFiles} ->
- check_plt(State, Plt, Output, OldFiles,
- Files);
- {error, no_such_file} ->
- build_proj_plt(State, Plt, Output, Files)
- end,
- {Warnings2 + Warnings3, State2}.
+ Files = proj_plt_files(State),
+ case read_plt(State, Plt) of
+ {ok, OldFiles} ->
+ check_plt(State, Plt, Output, OldFiles, Files);
+ {error, no_such_file} ->
+ build_proj_plt(State, Plt, Output, Files)
+ end.
proj_plt_files(State) ->
- BasePltApps = rebar_state:get(State, dialyzer_base_plt_apps,
- default_plt_apps()),
- PltApps = rebar_state:get(State, dialyzer_plt_apps, []),
+ BasePltApps = get_config(State, base_plt_apps, default_plt_apps()),
+ PltApps = get_config(State, plt_extra_apps, []),
Apps = rebar_state:project_apps(State),
DepApps = lists:flatmap(fun rebar_app_info:applications/1, Apps),
get_plt_files(BasePltApps ++ PltApps ++ DepApps, Apps).
@@ -165,23 +188,18 @@ default_plt_apps() ->
get_plt_files(DepApps, Apps) ->
?INFO("Resolving files...", []),
- get_plt_files(DepApps, Apps, [], [], []).
+ get_plt_files(DepApps, Apps, [], []).
-get_plt_files([], _, _, Files, Warnings) ->
- {Files, Warnings};
-get_plt_files([AppName | DepApps], Apps, PltApps, Files, Warnings) ->
+get_plt_files([], _, _, Files) ->
+ Files;
+get_plt_files([AppName | DepApps], Apps, PltApps, Files) ->
case lists:member(AppName, PltApps) orelse app_member(AppName, Apps) of
true ->
- get_plt_files(DepApps, Apps, PltApps, Files, Warnings);
+ get_plt_files(DepApps, Apps, PltApps, Files);
false ->
- {DepApps2, Files2, Warnings2} = app_name_to_info(AppName),
- ?DEBUG("~s dependencies: ~p", [AppName, DepApps2]),
+ Files2 = app_files(AppName),
?DEBUG("~s files: ~p", [AppName, Files2]),
- DepApps3 = DepApps2 ++ DepApps,
- PltApps2 = [AppName | PltApps],
- Files3 = Files2 ++ Files,
- Warnings3 = Warnings2 ++ Warnings,
- get_plt_files(DepApps3, Apps, PltApps2, Files3, Warnings3)
+ get_plt_files(DepApps, Apps, [AppName | PltApps], Files2 ++ Files)
end.
app_member(AppName, Apps) ->
@@ -192,71 +210,28 @@ app_member(AppName, Apps) ->
false
end.
-app_name_to_info(AppName) ->
- case app_name_to_ebin(AppName) of
- {error, _} ->
- {[], [], [{unknown_application, {"", 0}, [AppName]}]};
- EbinDir ->
- ebin_to_info(EbinDir, AppName)
+app_files(AppName) ->
+ case app_ebin(AppName) of
+ {ok, EbinDir} ->
+ ebin_files(EbinDir);
+ {error, bad_name} ->
+ throw({unknown_application, AppName})
end.
-app_name_to_ebin(AppName) ->
+app_ebin(AppName) ->
case code:lib_dir(AppName, ebin) of
- {error, bad_name} ->
- search_ebin(AppName);
+ {error, bad_name} = Error ->
+ Error;
EbinDir ->
- check_ebin(EbinDir, AppName)
+ check_ebin(EbinDir)
end.
-check_ebin(EbinDir, AppName) ->
+check_ebin(EbinDir) ->
case filelib:is_dir(EbinDir) of
true ->
- EbinDir;
- false ->
- search_ebin(AppName)
- end.
-
-search_ebin(AppName) ->
- case code:where_is_file(atom_to_list(AppName) ++ ".app") of
- non_existing ->
- {error, bad_name};
- AppFile ->
- filename:dirname(AppFile)
- end.
-
-ebin_to_info(EbinDir, AppName) ->
- AppFile = filename:join(EbinDir, atom_to_list(AppName) ++ ".app"),
- ?DEBUG("Consulting app file ~p", [AppFile]),
- case file:consult(AppFile) of
- {ok, [{application, AppName, AppDetails}]} ->
- DepApps = proplists:get_value(applications, AppDetails, []),
- IncApps = proplists:get_value(included_applications, AppDetails,
- []),
- Modules = proplists:get_value(modules, AppDetails, []),
- {Files, Warnings} = modules_to_files(Modules, EbinDir),
- {IncApps ++ DepApps, Files, Warnings};
- {error, enoent} when AppName =:= erts ->
- {[], ebin_files(EbinDir), []};
- _ ->
- Error = io_lib:format("Could not parse ~p", [AppFile]),
- throw({dialyzer_error, Error})
- end.
-
-modules_to_files(Modules, EbinDir) ->
- Ext = code:objfile_extension(),
- Result = [module_to_file(Module, EbinDir, Ext) || Module <- Modules],
- Files = [File || {_, File} <- Result, File =/= unknown],
- Warnings = [{unknown_module, {"", 0}, [Module]} ||
- {Module, unknown} <- Result],
- {Files, Warnings}.
-
-module_to_file(Module, EbinDir, Ext) ->
- File = filename:join(EbinDir, atom_to_list(Module) ++ Ext),
- case filelib:is_file(File) of
- true ->
- {Module, File};
+ {ok, EbinDir};
false ->
- {Module, unknown}
+ {error, bad_name}
end.
ebin_files(EbinDir) ->
@@ -306,43 +281,46 @@ add_plt(State, Plt, Output, Files) ->
run_plt(State, Plt, Output, plt_add, Files).
run_plt(State, Plt, Output, Analysis, Files) ->
- GetWarnings = rebar_state:get(State, dialyzer_plt_warnings, false),
+ GetWarnings = get_config(State, get_warnings, false),
Opts = [{analysis_type, Analysis},
{get_warnings, GetWarnings},
{init_plt, Plt},
+ {output_plt, Plt},
{from, byte_code},
{files, Files}],
run_dialyzer(State, Opts, Output).
build_proj_plt(State, Plt, Output, Files) ->
- BasePlt = get_base_plt_location(State),
+ BasePlt = get_base_plt(State),
?INFO("Updating base plt...", []),
- {BaseFiles, BaseWarnings} = base_plt_files(State),
- BaseWarnings2 = format_warnings(Output, BaseWarnings),
- {BaseWarnings3, State1} = update_base_plt(State, BasePlt, Output,
- BaseFiles),
+ BaseFiles = base_plt_files(State),
+ {BaseWarnings, State1} = update_base_plt(State, BasePlt, Output, BaseFiles),
?INFO("Copying ~p to ~p...", [BasePlt, Plt]),
_ = filelib:ensure_dir(Plt),
case file:copy(BasePlt, Plt) of
{ok, _} ->
{CheckWarnings, State2} = check_plt(State1, Plt, Output, BaseFiles,
Files),
- {BaseWarnings2 + BaseWarnings3 + CheckWarnings, State2};
+ {BaseWarnings + CheckWarnings, State2};
{error, Reason} ->
Error = io_lib:format("Could not copy PLT from ~p to ~p: ~p",
[BasePlt, Plt, file:format_error(Reason)]),
throw({dialyzer_error, Error})
end.
-get_base_plt_location(State) ->
- GlobalCacheDir = rebar_dir:global_cache_dir(State),
- BaseDir = rebar_state:get(State, dialyzer_base_plt_dir, GlobalCacheDir),
- BasePlt = rebar_state:get(State, dialyzer_base_plt, default_plt()),
- filename:join(BaseDir, BasePlt).
+get_base_plt(State) ->
+ Prefix = get_config(State, base_plt_prefix, ?PLT_PREFIX),
+ Name = plt_name(Prefix),
+ case get_config(State, base_plt_location, global) of
+ global ->
+ GlobalCacheDir = rebar_dir:global_cache_dir(State),
+ filename:join(GlobalCacheDir, Name);
+ Dir ->
+ filename:join(Dir, Name)
+ end.
base_plt_files(State) ->
- BasePltApps = rebar_state:get(State, dialyzer_base_plt_apps,
- default_plt_apps()),
+ BasePltApps = get_config(State, base_plt_apps, default_plt_apps()),
Apps = rebar_state:project_apps(State),
get_plt_files(BasePltApps, Apps).
@@ -357,7 +335,7 @@ update_base_plt(State, BasePlt, Output, BaseFiles) ->
build_plt(State, Plt, Output, Files) ->
?INFO("Adding ~b files to ~p...", [length(Files), Plt]),
- GetWarnings = rebar_state:get(State, dialyzer_plt_warnings, false),
+ GetWarnings = get_config(State, get_warnings, false),
Opts = [{analysis_type, plt_build},
{get_warnings, GetWarnings},
{output_plt, Plt},
@@ -376,36 +354,29 @@ succ_typings(State, Plt, Output) ->
succ_typings(State, Plt, Output, Apps) ->
?INFO("Doing success typing analysis...", []),
- {Files, Warnings} = apps_to_files(Apps),
- Warnings2 = format_warnings(Output, Warnings),
+ Files = apps_to_files(Apps),
?INFO("Analyzing ~b files with ~p...", [length(Files), Plt]),
Opts = [{analysis_type, succ_typings},
{get_warnings, true},
{from, byte_code},
{files, Files},
{init_plt, Plt}],
- {Warnings3, State2} = run_dialyzer(State, Opts, Output),
- {Warnings2 + Warnings3, State2}.
+ run_dialyzer(State, Opts, Output).
apps_to_files(Apps) ->
?INFO("Resolving files...", []),
- Result = [{Files, Warnings} ||
- App <- Apps,
- {Files, Warnings} <- [app_to_files(App)]],
- Files = [File || {Files, _} <- Result, File <- Files],
- Warnings = [Warning || {_, Warnings} <- Result, Warning <- Warnings],
- {Files, Warnings}.
+ [File || App <- Apps,
+ File <- app_to_files(App)].
app_to_files(App) ->
AppName = ec_cnv:to_atom(rebar_app_info:name(App)),
- {_, Files, Warnings} = app_name_to_info(AppName),
- {Files, Warnings}.
+ app_files(AppName).
run_dialyzer(State, Opts, Output) ->
%% dialyzer may return callgraph warnings when get_warnings is false
case proplists:get_bool(get_warnings, Opts) of
true ->
- WarningsList = rebar_state:get(State, dialyzer_warnings, []),
+ WarningsList = get_config(State, warnings, []),
Opts2 = [{warnings, WarningsList},
{check_plt, false} |
Opts],
@@ -417,7 +388,7 @@ run_dialyzer(State, Opts, Output) ->
{check_plt, false} |
Opts],
?DEBUG("Running dialyzer with options: ~p~n", [Opts2]),
- _ = dialyzer:run(Opts2),
+ dialyzer:run(Opts2),
{0, State}
end.
@@ -430,10 +401,6 @@ format_warnings(Output, Warnings) ->
format_warnings(Warnings) ->
[format_warning(Warning) || Warning <- Warnings].
-format_warning({unknown_application, _, [AppName]}) ->
- io_lib:format("Unknown application: ~s", [AppName]);
-format_warning({unknown_module, _, [Module]}) ->
- io_lib:format("Unknown module: ~s", [Module]);
format_warning(Warning) ->
case strip(dialyzer:format_warning(Warning, fullpath)) of
":0: " ++ Unknown ->
@@ -471,3 +438,7 @@ no_warnings() ->
no_contracts,
no_behaviours,
no_undefined_callbacks].
+
+get_config(State, Key, Default) ->
+ Config = rebar_state:get(State, dialyzer, []),
+ proplists:get_value(Key, Config, Default).
diff --git a/src/rebar_prv_do.erl b/src/rebar_prv_do.erl
index aee3a27..f850135 100644
--- a/src/rebar_prv_do.erl
+++ b/src/rebar_prv_do.erl
@@ -23,7 +23,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 do <task1>, <task2>, ..."},
{short_desc, "Higher order provider for running multiple tasks in a sequence."},
@@ -33,8 +33,16 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
- Tasks = rebar_utils:args_to_tasks(rebar_state:command_args(State)),
- do_tasks(Tasks, State).
+ case rebar_utils:args_to_tasks(rebar_state:command_args(State)) of
+ [] ->
+ AllProviders = rebar_state:providers(State),
+ Namespace = rebar_state:namespace(State),
+ Providers = providers:get_providers_by_namespace(Namespace, AllProviders),
+ providers:help(Providers),
+ {ok, State};
+ Tasks ->
+ do_tasks(Tasks, State)
+ end.
do_tasks([], State) ->
{ok, State};
@@ -47,6 +55,8 @@ do_tasks([{TaskStr, Args}|Tail], State) ->
default ->
%% The first task we hit might be a namespace!
case maybe_namespace(State2, Task, Args) of
+ {ok, FinalState} when Tail =:= [] ->
+ {ok, FinalState};
{ok, _} ->
do_tasks(Tail, State);
{error, Reason} ->
@@ -56,6 +66,8 @@ do_tasks([{TaskStr, Args}|Tail], State) ->
%% We're already in a non-default namespace, check the
%% task directly.
case rebar_core:process_command(State2, Task) of
+ {ok, FinalState} when Tail =:= [] ->
+ {ok, FinalState};
{ok, _} ->
do_tasks(Tail, State);
{error, Reason} ->
diff --git a/src/rebar_prv_edoc.erl b/src/rebar_prv_edoc.erl
index efcfeb5..14df269 100644
--- a/src/rebar_prv_edoc.erl
+++ b/src/rebar_prv_edoc.erl
@@ -19,7 +19,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 edoc"},
{short_desc, "Generate documentation using edoc."},
diff --git a/src/rebar_prv_escriptize.erl b/src/rebar_prv_escriptize.erl
index 03332a0..3cdc9bf 100644
--- a/src/rebar_prv_escriptize.erl
+++ b/src/rebar_prv_escriptize.erl
@@ -47,7 +47,7 @@ init(State) ->
Provider = providers:create([
{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 escriptize"},
{opts, []},
@@ -72,7 +72,7 @@ do(State) ->
end;
Name ->
AllApps = rebar_state:all_deps(State)++rebar_state:project_apps(State),
- AppInfo = rebar_app_utils:find(Name, AllApps),
+ {ok, AppInfo} = rebar_app_utils:find(ec_cnv:to_binary(Name), AllApps),
escriptize(State, AppInfo)
end.
@@ -83,14 +83,15 @@ escriptize(State0, App) ->
%% Get the output filename for the escript -- this may include dirs
Filename = filename:join([rebar_dir:base_dir(State0), "bin",
rebar_state:get(State0, escript_name, AppName)]),
+ ?DEBUG("Creating escript file ~s", [Filename]),
ok = filelib:ensure_dir(Filename),
State = rebar_state:escript_path(State0, Filename),
%% Look for a list of other applications (dependencies) to include
%% in the output file. We then use the .app files for each of these
%% to pull in all the .beam files.
- InclApps = lists:usort(rebar_state:get(State, escript_incl_apps, [])
- ++ all_deps(State)),
+ InclApps = lists:usort([ec_cnv:to_atom(AppName) | rebar_state:get(State, escript_incl_apps, [])
+ ++ all_deps(State)]),
AllApps = rebar_state:all_deps(State)++rebar_state:project_apps(State),
InclBeams = get_apps_beams(InclApps, AllApps),
@@ -134,7 +135,7 @@ format_error({bad_name, App}) ->
io_lib:format("Failed to get ebin/ directory for "
"escript_incl_app: ~p", [App]);
format_error(no_main_app) ->
- io_lib:format("Multiple project apps and {rebar_escript_plugin, [{main_app, atom()}]}."
+ io_lib:format("Multiple project apps and {escript_main_app, atom()}."
" not set in rebar.config", []).
%% ===================================================================
diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl
index 8eaa926..28c0ed6 100644
--- a/src/rebar_prv_eunit.erl
+++ b/src/rebar_prv_eunit.erl
@@ -24,7 +24,7 @@ init(State) ->
Provider = providers:create([{name, ?PROVIDER},
{module, ?MODULE},
{deps, ?DEPS},
- {bare, false},
+ {bare, true},
{example, "rebar3 eunit"},
{short_desc, "Run EUnit Tests."},
{desc, "Run EUnit Tests."},
@@ -37,7 +37,8 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
?INFO("Performing EUnit tests...", []),
- code:add_pathsa(rebar_state:code_paths(State, all_deps)),
+ rebar_utils:update_code(rebar_state:code_paths(State, all_deps)),
+
%% Run eunit provider prehooks
Providers = rebar_state:providers(State),
Cwd = rebar_dir:get_cwd(),
@@ -49,7 +50,7 @@ do(State) ->
{ok, State1} ->
%% Run eunit provider posthooks
rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State1),
- rebar_utils:cleanup_code_path(rebar_state:code_paths(State1, default)),
+ rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)),
{ok, State1};
Error ->
rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)),
@@ -84,8 +85,7 @@ format_error({error_running_tests, Reason}) ->
test_state(State) ->
ErlOpts = rebar_state:get(State, eunit_compile_opts, []),
TestOpts = safe_define_test_macro(ErlOpts),
- TestDir = [{extra_src_dirs, ["test"]}],
- first_files(State) ++ [{erl_opts, TestOpts ++ TestDir}].
+ first_files(State) ++ [{erl_opts, TestOpts}].
safe_define_test_macro(Opts) ->
%% defining a compile macro twice results in an exception so
@@ -106,38 +106,49 @@ first_files(State) ->
prepare_tests(State) ->
{RawOpts, _} = rebar_state:command_parsed_args(State),
- ok = maybe_cover_compile(State, RawOpts),
- ProjectApps = project_apps(State),
- resolve_apps(ProjectApps, RawOpts).
-
-maybe_cover_compile(State, Opts) ->
- State1 = case proplists:get_value(cover, Opts, false) of
- true -> rebar_state:set(State, cover_enabled, true);
- false -> State
- end,
- rebar_prv_cover:maybe_cover_compile(State1).
+ resolve_apps(State, RawOpts).
-resolve_apps(ProjectApps, RawOpts) ->
+resolve_apps(State, RawOpts) ->
case proplists:get_value(app, RawOpts) of
- undefined -> resolve_suites(ProjectApps, RawOpts);
+ undefined -> resolve_suites(State, RawOpts);
%% convert app name strings to `rebar_app_info` objects
Apps -> AppNames = string:tokens(Apps, [$,]),
+ ProjectApps = project_apps(State),
case filter_apps_by_name(AppNames, ProjectApps) of
- {ok, TestApps} -> resolve_suites(TestApps, RawOpts);
+ {ok, TestApps} -> resolve_suites(State, TestApps, RawOpts);
Error -> Error
end
end.
-resolve_suites(Apps, RawOpts) ->
+resolve_suites(State, RawOpts) -> resolve_suites(State, project_apps(State), RawOpts).
+
+resolve_suites(State, Apps, RawOpts) ->
case proplists:get_value(suite, RawOpts) of
- undefined -> test_set(Apps, all);
+ undefined -> compile_tests(State, Apps, all, RawOpts);
Suites -> SuiteNames = string:tokens(Suites, [$,]),
case filter_suites_by_apps(SuiteNames, Apps) of
- {ok, S} -> test_set(Apps, S);
+ {ok, S} -> compile_tests(State, Apps, S, RawOpts);
Error -> Error
end
end.
+compile_tests(State, TestApps, Suites, RawOpts) ->
+ F = fun(AppInfo) ->
+ S = rebar_app_info:state_or_new(State, AppInfo),
+ ok = rebar_erlc_compiler:compile(replace_src_dirs(S),
+ ec_cnv:to_list(rebar_app_info:out_dir(AppInfo)))
+ end,
+ lists:foreach(F, TestApps),
+ ok = maybe_cover_compile(State, RawOpts),
+ {ok, test_set(TestApps, Suites)}.
+
+maybe_cover_compile(State, Opts) ->
+ State1 = case proplists:get_value(cover, Opts, false) of
+ true -> rebar_state:set(State, cover_enabled, true);
+ false -> State
+ end,
+ rebar_prv_cover:maybe_cover_compile(State1).
+
project_apps(State) ->
filter_checkouts(rebar_state:project_apps(State)).
@@ -204,8 +215,20 @@ app_modules([App|Rest], Acc) ->
app_modules(Rest, NewAcc)
end.
-test_set(Apps, all) -> {ok, set_apps(Apps, [])};
-test_set(_Apps, Suites) -> {ok, set_suites(Suites, [])}.
+replace_src_dirs(State) ->
+ %% replace any `src_dirs` with the test dirs
+ ErlOpts = rebar_state:get(State, erl_opts, []),
+ StrippedOpts = filter_src_dirs(ErlOpts),
+ case rebar_dir:extra_src_dirs(State) of
+ [] -> rebar_state:set(State, erl_opts, [{src_dirs, ["test"]}|StrippedOpts]);
+ _ -> rebar_state:set(State, erl_opts, StrippedOpts)
+ end.
+
+filter_src_dirs(ErlOpts) ->
+ lists:filter(fun({src_dirs, _}) -> false; (_) -> true end, ErlOpts).
+
+test_set(Apps, all) -> set_apps(Apps, []);
+test_set(_Apps, Suites) -> set_suites(Suites, []).
set_apps([], Acc) -> lists:reverse(Acc);
set_apps([App|Rest], Acc) ->
diff --git a/src/rebar_prv_help.erl b/src/rebar_prv_help.erl
index be5717f..c028264 100644
--- a/src/rebar_prv_help.erl
+++ b/src/rebar_prv_help.erl
@@ -22,7 +22,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 help <task>"},
{short_desc, "Display a list of tasks or help for a given task or subtask."},
@@ -65,7 +65,13 @@ task_help(Namespace, Name, State) ->
Providers = rebar_state:providers(State),
case providers:get_provider(Name, Providers, Namespace) of
not_found ->
- {error, io_lib:format("Unknown task ~p", [Name])};
+ case providers:get_providers_by_namespace(Name, Providers) of
+ [] ->
+ {error, io_lib:format("Unknown task ~p", [Name])};
+ NSProviders ->
+ providers:help(NSProviders),
+ {ok, State}
+ end;
Provider ->
providers:help(Provider),
{ok, State}
diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl
index ba49532..768d41a 100644
--- a/src/rebar_prv_install_deps.erl
+++ b/src/rebar_prv_install_deps.erl
@@ -37,7 +37,10 @@
-export([handle_deps/3,
handle_deps/4,
- handle_deps/5]).
+ handle_deps/5,
+
+ find_cycles/1,
+ cull_compile/2]).
-export_type([dep/0]).
@@ -58,7 +61,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, true},
+ {bare, false},
{deps, ?DEPS},
{example, undefined},
{short_desc, ""},
@@ -76,6 +79,10 @@ do(State) ->
{Apps, State1} =
lists:foldl(fun deps_per_profile/2, {[], State}, lists:reverse(Profiles)),
+ State2 = rebar_state:update_all_deps(State1, Apps),
+ CodePaths = [rebar_app_info:ebin_dir(A) || A <- Apps],
+ State3 = rebar_state:update_code_paths(State2, all_deps, CodePaths),
+
Source = ProjectApps ++ Apps,
case find_cycles(Source) of
{cycles, Cycles} ->
@@ -84,7 +91,7 @@ do(State) ->
{error, Error};
{no_cycle, Sorted} ->
ToCompile = cull_compile(Sorted, ProjectApps),
- {ok, rebar_state:deps_to_build(State1, ToCompile)}
+ {ok, rebar_state:deps_to_build(State3, ToCompile)}
end
catch
%% maybe_fetch will maybe_throw an exception to break out of some loops
@@ -158,11 +165,7 @@ handle_deps(Profile, State0, Deps, Upgrade, Locks) ->
,lists:ukeysort(2, SrcApps)
,lists:ukeysort(2, Solved)),
- State5 = rebar_state:update_all_deps(State4, AllDeps),
- CodePaths = [rebar_app_info:ebin_dir(A) || A <- AllDeps],
- State6 = rebar_state:update_code_paths(State5, all_deps, CodePaths),
-
- {ok, AllDeps, State6}.
+ {ok, AllDeps, State4}.
%% ===================================================================
%% Internal functions
@@ -202,31 +205,40 @@ update_pkg_deps(Profile, Packages, PkgDeps, Graph, Upgrade, Seen, State, Locks)
update_pkg_deps(Profile, S, Packages, Upgrade, Seen, State, Locks)
end.
+pkg_locked({Name, _, _}, Locks) ->
+ pkg_locked(Name, Locks);
pkg_locked({Name, _}, Locks) ->
+ pkg_locked(Name, Locks);
+pkg_locked(Name, Locks) ->
false =/= lists:keyfind(Name, 1, Locks).
-update_pkg_deps(Profile, Pkgs, Packages, Upgrade, Seen, State, _Locks) ->
+update_pkg_deps(Profile, Pkgs, Packages, Upgrade, Seen, State, Locks) ->
%% Create app_info record for each pkg dep
DepsDir = profile_dep_dir(State, Profile),
{Solved, _, State1}
= lists:foldl(fun(Pkg, {Acc, SeenAcc, StateAcc}) ->
- handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Acc, SeenAcc, StateAcc)
+ handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Acc, SeenAcc, Locks, StateAcc)
end, {[], Seen, State}, Pkgs),
{Solved, State1}.
-handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Fetched, Seen, State) ->
- AppInfo = package_to_app(DepsDir, Packages, Pkg, State),
+handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Fetched, Seen, Locks, State) ->
+ IsLock = pkg_locked(Pkg, Locks),
+ AppInfo = package_to_app(DepsDir, Packages, Pkg, IsLock, State),
+ Deps = rebar_app_info:deps(AppInfo),
Level = rebar_app_info:dep_level(AppInfo),
{NewSeen, NewState} = maybe_lock(Profile, AppInfo, Seen, State, Level),
{_, AppInfo1} = maybe_fetch(AppInfo, Profile, Upgrade, Seen, NewState),
- {[AppInfo1 | Fetched], NewSeen, NewState}.
+ {AppInfo2, _, _, _, _} =
+ handle_dep(NewState, Profile, DepsDir, AppInfo1, Locks, Level),
+ AppInfo3 = rebar_app_info:deps(AppInfo2, Deps),
+ {[AppInfo3 | Fetched], NewSeen, NewState}.
maybe_lock(Profile, AppInfo, Seen, State, Level) ->
+ Name = rebar_app_info:name(AppInfo),
case rebar_app_info:is_checkout(AppInfo) of
false ->
case Profile of
default ->
- Name = rebar_app_info:name(AppInfo),
case sets:is_element(Name, Seen) of
false ->
Locks = rebar_state:lock(State),
@@ -241,13 +253,13 @@ maybe_lock(Profile, AppInfo, Seen, State, Level) ->
{Seen, State}
end;
_ ->
- {Seen, State}
+ {sets:add_element(Name, Seen), State}
end;
true ->
- {Seen, State}
+ {sets:add_element(Name, Seen), State}
end.
-package_to_app(DepsDir, Packages, {Name, Vsn, Level}, State) ->
+package_to_app(DepsDir, Packages, {Name, Vsn, Level}, IsLock, State) ->
case dict:find({Name, Vsn}, Packages) of
error ->
case rebar_packages:check_registry(Name, Vsn, State) of
@@ -256,14 +268,13 @@ package_to_app(DepsDir, Packages, {Name, Vsn, Level}, State) ->
false ->
throw(?PRV_ERROR({missing_package, Name, Vsn}))
end;
- {ok, P} ->
- PkgDeps = [{PkgName, PkgVsn}
- || {PkgName,PkgVsn} <- proplists:get_value(<<"deps">>, P, [])],
- {ok, AppInfo} = rebar_app_info:new(Name, Vsn),
- AppInfo1 = rebar_app_info:deps(AppInfo, PkgDeps),
- AppInfo2 = rebar_app_info:dir(AppInfo1, filename:join([DepsDir, Name])),
- AppInfo3 = rebar_app_info:dep_level(AppInfo2, Level),
- rebar_app_info:source(AppInfo3, {pkg, Name, Vsn})
+ {ok, PkgDeps} ->
+ Source = {pkg, Name, Vsn},
+ AppInfo = new_dep(DepsDir, Name, Vsn, Source, IsLock, State),
+ AppInfo1 = rebar_app_info:dep_level(rebar_app_info:deps(AppInfo, PkgDeps), Level),
+ BaseDir = rebar_state:get(State, base_dir, []),
+ AppState1 = rebar_state:set(rebar_app_info:state(AppInfo1), base_dir, BaseDir),
+ rebar_app_info:state(AppInfo1, AppState1)
end.
-spec update_src_deps(atom(), non_neg_integer(), list(), list(), list(), rebar_state:t(), boolean(), sets:set(binary()), list()) -> {rebar_state:t(), list(), list(), sets:set(binary())}.
@@ -345,7 +356,6 @@ handle_upgrade(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Locks)
{true, AppInfo1} ->
handle_dep(AppInfo1, Profile, SrcDeps, PkgDeps, SrcApps,
Level, State, Locks);
-
{false, AppInfo1} ->
{[AppInfo1|SrcDeps], PkgDeps, SrcApps, State, Locks}
end;
@@ -356,7 +366,7 @@ handle_upgrade(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Locks)
handle_dep(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Locks) ->
DepsDir = profile_dep_dir(State, Profile),
{AppInfo1, NewSrcDeps, NewPkgDeps, NewLocks, State1} =
- handle_dep(State, DepsDir, AppInfo, Locks, Level),
+ handle_dep(State, Profile, DepsDir, AppInfo, Locks, Level),
AppInfo2 = rebar_app_info:dep_level(AppInfo1, Level),
{NewSrcDeps ++ SrcDeps
,NewPkgDeps++PkgDeps
@@ -364,9 +374,9 @@ handle_dep(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Locks) ->
,State1
,NewLocks}.
--spec handle_dep(rebar_state:t(), file:filename_all(), rebar_app_info:t(), list(), integer()) ->
+-spec handle_dep(rebar_state:t(), atom(), file:filename_all(), rebar_app_info:t(), list(), integer()) ->
{rebar_app_info:t(), [rebar_app_info:t()], [pkg_dep()], [integer()]}.
-handle_dep(State, DepsDir, AppInfo, Locks, Level) ->
+handle_dep(State, Profile, DepsDir, AppInfo, Locks, Level) ->
Profiles = rebar_state:current_profiles(State),
Name = rebar_app_info:name(AppInfo),
@@ -374,22 +384,26 @@ handle_dep(State, DepsDir, AppInfo, Locks, Level) ->
S = rebar_app_info:state(AppInfo),
S1 = rebar_state:new(S, C, rebar_app_info:dir(AppInfo)),
- S2 = rebar_state:apply_profiles(S1, Profiles),
- S3 = rebar_state:apply_overrides(S2, Name),
- AppInfo1 = rebar_app_info:state(AppInfo, S3),
+ S2 = rebar_state:apply_overrides(S1, Name),
+
+ S3 = rebar_state:apply_profiles(S2, Profiles),
+ Plugins = rebar_state:get(S3, plugins, []),
+ S4 = rebar_state:set(S3, {plugins, Profile}, Plugins),
+ AppInfo1 = rebar_app_info:state(AppInfo, S4),
%% Dep may have plugins to install. Find and install here.
- State1 = rebar_plugins:handle_plugins(rebar_state:get(S3, plugins, []), State),
+ S5 = rebar_plugins:install(S4),
+ AppInfo2 = rebar_app_info:state(AppInfo1, S5),
- Deps = rebar_state:get(S3, deps, []),
%% Upgrade lock level to be the level the dep will have in this dep tree
+ Deps = rebar_state:get(S5, deps, []),
NewLocks = [{DepName, Source, LockLevel+Level} ||
- {DepName, Source, LockLevel} <- rebar_state:get(S3, {locks, default}, [])],
- AppInfo2 = rebar_app_info:deps(AppInfo1, rebar_state:deps_names(Deps)),
- {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, S3, Locks, Level),
- {AppInfo2, SrcDeps, PkgDeps, Locks++NewLocks, State1}.
+ {DepName, Source, LockLevel} <- rebar_state:get(S5, {locks, default}, [])],
+ AppInfo3 = rebar_app_info:deps(AppInfo2, rebar_state:deps_names(Deps)),
+ {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, S5, Locks, Level+1),
+ {AppInfo3, SrcDeps, PkgDeps, Locks++NewLocks, State}.
--spec maybe_fetch(rebar_app_info:t(), atom(), boolean() | {true, binary(), integer()},
+-spec maybe_fetch(rebar_app_info:t(), atom(), boolean(),
sets:set(binary()), rebar_state:t()) -> {boolean(), rebar_app_info:t()}.
maybe_fetch(AppInfo, Profile, Upgrade, Seen, State) ->
AppDir = ec_cnv:to_list(rebar_app_info:dir(AppInfo)),
@@ -405,7 +419,7 @@ maybe_fetch(AppInfo, Profile, Upgrade, Seen, State) ->
case fetch_app(AppInfo, AppDir, State) of
true ->
maybe_symlink_default(State, Profile, AppDir, AppInfo),
- {true, update_app_info(AppInfo)};
+ {true, update_app_info(AppDir, AppInfo)};
Other ->
{Other, AppInfo}
end;
@@ -432,7 +446,7 @@ maybe_fetch(AppInfo, Profile, Upgrade, Seen, State) ->
already_in_default(AppInfo, State) ->
Name = ec_cnv:to_list(rebar_app_info:name(AppInfo)),
- DefaultAppDir = filename:join([rebar_state:get(State, base_dir), "default", "lib", Name]),
+ DefaultAppDir = filename:join([rebar_state:get(State, base_dir, []), "default", "lib", Name]),
rebar_app_discover:find_app(DefaultAppDir, all).
needs_symlinking(State, Profile) ->
@@ -477,69 +491,69 @@ parse_deps(DepsDir, Deps, State, Locks, Level) ->
end,
case lists:keyfind(ec_cnv:to_binary(Name), 1, Locks) of
false ->
- parse_dep(Dep, Acc, DepsDir, State);
+ parse_dep(Dep, Acc, DepsDir, false, State);
LockedDep ->
LockedLevel = element(3, LockedDep),
case LockedLevel > Level of
true ->
- parse_dep(Dep, Acc, DepsDir, State);
+ parse_dep(Dep, Acc, DepsDir, false, State);
false ->
- parse_dep(LockedDep, Acc, DepsDir, State)
+ parse_dep(LockedDep, Acc, DepsDir, true, State)
end
end
end, {[], []}, Deps).
-parse_dep({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_list(Vsn) ->
+parse_dep({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_list(Vsn) ->
CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)),
case rebar_app_info:discover(CheckoutsDir) of
{ok, _App} ->
- Dep = new_dep(DepsDir, Name, [], [], State),
+ Dep = new_dep(DepsDir, Name, [], [], IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
not_found ->
{SrcDepsAcc, [parse_goal(ec_cnv:to_binary(Name)
,ec_cnv:to_binary(Vsn)) | PkgDepsAcc]}
end;
-parse_dep(Name, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_atom(Name) ->
+parse_dep(Name, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_atom(Name) ->
{PkgName, PkgVsn} = get_package(ec_cnv:to_binary(Name), State),
CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)),
case rebar_app_info:discover(CheckoutsDir) of
{ok, _App} ->
- Dep = new_dep(DepsDir, Name, [], [], State),
+ Dep = new_dep(DepsDir, Name, [], [], IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
not_found ->
{SrcDepsAcc, [{PkgName, PkgVsn} | PkgDepsAcc]}
end;
-parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) ->
- Dep = new_dep(DepsDir, Name, [], Source, State),
+parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) ->
+ Dep = new_dep(DepsDir, Name, [], Source, IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
-parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) ->
- Dep = new_dep(DepsDir, Name, [], Source, State),
+parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) ->
+ Dep = new_dep(DepsDir, Name, [], Source, IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
-parse_dep({Name, _Vsn, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) ->
- Dep = new_dep(DepsDir, Name, [], Source, State),
+parse_dep({Name, _Vsn, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) ->
+ Dep = new_dep(DepsDir, Name, [], Source, IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
-parse_dep({Name, _Vsn, Source, Opts}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) ->
+parse_dep({Name, _Vsn, Source, Opts}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) ->
?WARN("Dependency option list ~p in ~p is not supported and will be ignored", [Opts, Name]),
- Dep = new_dep(DepsDir, Name, [], Source, State),
+ Dep = new_dep(DepsDir, Name, [], Source, IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
-parse_dep({_Name, {pkg, Name, Vsn}, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_integer(Level) ->
+parse_dep({_Name, {pkg, Name, Vsn}, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_integer(Level) ->
CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)),
case rebar_app_info:discover(CheckoutsDir) of
{ok, _App} ->
- Dep = new_dep(DepsDir, Name, [], [], State),
+ Dep = new_dep(DepsDir, Name, [], [], IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
not_found ->
{SrcDepsAcc, [{Name, Vsn} | PkgDepsAcc]}
end;
-parse_dep({Name, Source, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source)
+parse_dep({Name, Source, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source)
, is_integer(Level) ->
- Dep = new_dep(DepsDir, Name, [], Source, State),
+ Dep = new_dep(DepsDir, Name, [], Source, IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
-parse_dep(Dep, _, _, _) ->
+parse_dep(Dep, _, _, _, _) ->
throw(?PRV_ERROR({parse_dep, Dep})).
-new_dep(DepsDir, Name, Vsn, Source, State) ->
+new_dep(DepsDir, Name, Vsn, Source, IsLock, State) ->
CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)),
{ok, Dep} = case rebar_app_info:discover(CheckoutsDir) of
{ok, App} ->
@@ -560,7 +574,7 @@ new_dep(DepsDir, Name, Vsn, Source, State) ->
ParentOverrides = rebar_state:overrides(State),
Dep1 = rebar_app_info:state(Dep,
rebar_state:overrides(S, ParentOverrides++Overrides)),
- rebar_app_info:source(Dep1, Source).
+ rebar_app_info:is_lock(rebar_app_info:source(Dep1, Source), IsLock).
fetch_app(AppInfo, AppDir, State) ->
?INFO("Fetching ~s (~p)", [rebar_app_info:name(AppInfo), rebar_app_info:source(AppInfo)]),
@@ -572,8 +586,12 @@ fetch_app(AppInfo, AppDir, State) ->
throw(Error)
end.
-update_app_info(AppInfo) ->
- AppDetails = rebar_app_info:app_details(AppInfo),
+%% This is called after the dep has been downloaded and unpacked, if it hadn't been already.
+%% So this is the first time for newly downloaded apps that its .app/.app.src data can
+%% be read in an parsed.
+update_app_info(AppDir, AppInfo) ->
+ {ok, Found} = rebar_app_info:discover(AppDir),
+ AppDetails = rebar_app_info:app_details(Found),
Applications = proplists:get_value(applications, AppDetails, []),
IncludedApplications = proplists:get_value(included_applications, AppDetails, []),
AppInfo1 = rebar_app_info:applications(
@@ -581,22 +599,29 @@ update_app_info(AppInfo) ->
IncludedApplications++Applications),
rebar_app_info:valid(AppInfo1, false).
-maybe_upgrade(AppInfo, AppDir, false, State) ->
- Source = rebar_app_info:source(AppInfo),
- rebar_fetch:needs_update(AppDir, Source, State);
-maybe_upgrade(AppInfo, AppDir, true, State) ->
+maybe_upgrade(AppInfo, AppDir, Upgrade, State) ->
Source = rebar_app_info:source(AppInfo),
- case rebar_fetch:needs_update(AppDir, Source, State) of
+ case Upgrade orelse rebar_app_info:is_lock(AppInfo) of
true ->
- ?INFO("Upgrading ~s", [rebar_app_info:name(AppInfo)]),
- case rebar_fetch:download_source(AppDir, Source, State) of
+ case rebar_fetch:needs_update(AppDir, Source, State) of
true ->
- true;
- Error ->
- throw(Error)
+ ?INFO("Upgrading ~s", [rebar_app_info:name(AppInfo)]),
+ case rebar_fetch:download_source(AppDir, Source, State) of
+ true ->
+ true;
+ Error ->
+ throw(Error)
+ end;
+ false ->
+ case Upgrade of
+ true ->
+ ?INFO("No upgrade needed for ~s", [rebar_app_info:name(AppInfo)]),
+ false;
+ false ->
+ false
+ end
end;
false ->
- ?INFO("No upgrade needed for ~s", [rebar_app_info:name(AppInfo)]),
false
end.
@@ -632,7 +657,8 @@ warn_skip_pkg({Name, Source}, State) ->
not_needs_compile(App) ->
not(rebar_app_info:is_checkout(App))
- andalso rebar_app_info:valid(App).
+ andalso rebar_app_info:valid(App)
+ andalso rebar_state:has_all_artifacts(rebar_app_info:state(App)) =:= true.
get_package(Dep, State) ->
case rebar_state:registry(State) of
diff --git a/src/rebar_prv_lock.erl b/src/rebar_prv_lock.erl
index 6fdd743..058846a 100644
--- a/src/rebar_prv_lock.erl
+++ b/src/rebar_prv_lock.erl
@@ -19,7 +19,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, true},
+ {bare, false},
{deps, ?DEPS},
{example, ""},
{short_desc, "Locks dependencies."},
diff --git a/src/rebar_prv_new.erl b/src/rebar_prv_new.erl
index 6bc9f53..0528f44 100644
--- a/src/rebar_prv_new.erl
+++ b/src/rebar_prv_new.erl
@@ -20,7 +20,7 @@ init(State) ->
Provider = providers:create([
{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 new <template>"},
{short_desc, "Create new project from templates."},
diff --git a/src/rebar_prv_packages.erl b/src/rebar_prv_packages.erl
index c7c0d50..8ba66de 100644
--- a/src/rebar_prv_packages.erl
+++ b/src/rebar_prv_packages.erl
@@ -17,7 +17,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 pkgs"},
{short_desc, "List available packages."},
@@ -27,9 +27,9 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
- case rebar_packages:registry(State) of
- {ok, Registry} ->
- print_packages(Registry),
+ case rebar_packages:get_packages(State) of
+ {Dict, _} ->
+ print_packages(Dict),
{ok, State};
error ->
?PRV_ERROR(load_registry_fail)
@@ -39,13 +39,16 @@ do(State) ->
format_error(load_registry_fail) ->
"Failed to load package regsitry. Try running 'rebar3 update' to fix".
-print_packages(Table) ->
- MS = ets:fun2ms(fun({Key, [Value]}) when is_binary(Key) -> {Key, Value} end),
- Pkgs = ets:select(Table, MS),
- lists:foreach(fun({Name, Vsns}) ->
- VsnStr = join(Vsns, <<", ">>),
- io:format("~s:~n Versions: ~s~n~n", [Name, VsnStr])
- end, Pkgs).
+print_packages(Dict) ->
+ Pkgs = lists:keysort(1, dict:fetch_keys(Dict)),
+ SortedPkgs = lists:foldl(fun({Pkg, Vsn}, Acc) ->
+ orddict:append_list(Pkg, [Vsn], Acc)
+ end, orddict:new(), Pkgs),
+
+ orddict:map(fun(Name, Vsns) ->
+ VsnStr = join(Vsns, <<", ">>),
+ io:format("~s:~n Versions: ~s~n~n", [Name, VsnStr])
+ end, SortedPkgs).
-spec join([binary()], binary()) -> binary().
join([Bin], _Sep) ->
diff --git a/src/rebar_prv_plugins.erl b/src/rebar_prv_plugins.erl
new file mode 100644
index 0000000..328958e
--- /dev/null
+++ b/src/rebar_prv_plugins.erl
@@ -0,0 +1,67 @@
+-module(rebar_prv_plugins).
+
+-behaviour(provider).
+
+-export([init/1,
+ do/1,
+ format_error/1]).
+
+-include("rebar.hrl").
+-include_lib("providers/include/providers.hrl").
+
+-define(PROVIDER, list).
+-define(NAMESPACE, plugins).
+-define(DEPS, []).
+
+-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
+init(State) ->
+ State1 = rebar_state:add_provider(
+ State,
+ providers:create([
+ {name, ?PROVIDER},
+ {module, ?MODULE},
+ {namespace, ?NAMESPACE},
+ {bare, true},
+ {deps, ?DEPS},
+ {example, "rebar3 plugins list"},
+ {short_desc, "List local and global plugins for this project"},
+ {desc, "List local and global plugins for this project"},
+ {opts, []}])),
+ {ok, State1}.
+
+-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
+do(State) ->
+ GlobalConfigFile = rebar_dir:global_config(),
+ GlobalConfig = rebar_state:new(rebar_config:consult_file(GlobalConfigFile)),
+ GlobalPlugins = rebar_state:get(GlobalConfig, plugins, []),
+ GlobalPluginsDir = filename:join(rebar_dir:global_cache_dir(State), "plugins"),
+ display_plugins("Global plugins", GlobalPluginsDir, GlobalPlugins),
+
+ Plugins = rebar_state:get(State, plugins, []),
+ PluginsDir =rebar_dir:plugins_dir(State),
+ display_plugins("Local plugins", PluginsDir, Plugins),
+ {ok, State}.
+
+-spec format_error(any()) -> iolist().
+format_error(Reason) ->
+ io_lib:format("~p", [Reason]).
+
+display_plugins(_Header, _Dir, []) ->
+ ok;
+display_plugins(Header, Dir, Plugins) ->
+ ?CONSOLE("--- ~s ---", [Header]),
+ display_plugins(Dir, Plugins),
+ ?CONSOLE("", []).
+
+display_plugins(Dir, Plugins) ->
+ lists:foreach(fun(Plugin) ->
+ Name = if is_atom(Plugin) -> Plugin;
+ is_tuple(Plugin) -> element(1, Plugin)
+ end,
+ case rebar_app_info:discover(filename:join(Dir, Name)) of
+ {ok, _App} ->
+ ?CONSOLE("~s", [Name]);
+ not_found ->
+ ?DEBUG("Unable to find plugin ~s", [Name])
+ end
+ end, Plugins).
diff --git a/src/rebar_prv_plugins_upgrade.erl b/src/rebar_prv_plugins_upgrade.erl
new file mode 100644
index 0000000..5ccd054
--- /dev/null
+++ b/src/rebar_prv_plugins_upgrade.erl
@@ -0,0 +1,110 @@
+-module(rebar_prv_plugins_upgrade).
+
+-behaviour(provider).
+
+-export([init/1,
+ do/1,
+ format_error/1]).
+
+-include("rebar.hrl").
+-include_lib("providers/include/providers.hrl").
+
+-define(PROVIDER, upgrade).
+-define(NAMESPACE, plugins).
+-define(DEPS, []).
+
+-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
+init(State) ->
+ State1 = rebar_state:add_provider(
+ State,
+ providers:create([
+ {name, ?PROVIDER},
+ {module, ?MODULE},
+ {namespace, ?NAMESPACE},
+ {bare, true},
+ {deps, ?DEPS},
+ {example, "rebar3 plugins upgrade <plugin>"},
+ {short_desc, "Uprade plugins"},
+ {desc, "List or upgrade plugins"},
+ {opts, [{plugin, undefined, undefined, string,
+ "Plugin to upgrade"}]}])),
+ {ok, State1}.
+
+-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
+do(State) ->
+ {Args, _} = rebar_state:command_parsed_args(State),
+ case proplists:get_value(plugin, Args, none) of
+ none ->
+ ?PRV_ERROR(no_plugin_arg);
+ Plugin ->
+ upgrade(Plugin, State)
+ end.
+
+-spec format_error(any()) -> iolist().
+format_error(no_plugin_arg) ->
+ io_lib:format("Must give an installed plugin to upgrade as an argument", []);
+format_error({not_found, Plugin}) ->
+ io_lib:format("Plugin to upgrade not found: ~s", [Plugin]);
+format_error(Reason) ->
+ io_lib:format("~p", [Reason]).
+
+upgrade(Plugin, State) ->
+ Profiles = rebar_state:current_profiles(State),
+ case find_plugin(Plugin, Profiles, State) of
+ not_found ->
+ Dep = find_plugin(Plugin, [global], State);
+ Dep ->
+ Dep
+ end,
+
+ case Dep of
+ not_found ->
+ ?PRV_ERROR({not_found, Plugin});
+ {ok, P, Profile} ->
+ State1 = rebar_state:set(State, deps_dir, ?DEFAULT_PLUGINS_DIR),
+ {ok, Apps, _State2} = rebar_prv_install_deps:handle_deps(Profile
+ ,State1
+ ,[P]
+ ,true),
+
+ {no_cycle, Sorted} = rebar_prv_install_deps:find_cycles(Apps),
+ ToBuild = rebar_prv_install_deps:cull_compile(Sorted, []),
+
+ %% Add already built plugin deps to the code path
+ CodePaths = [rebar_app_info:ebin_dir(A) || A <- Apps -- ToBuild],
+ code:add_pathsa(CodePaths),
+
+ %% Build plugin and its deps
+ [build_plugin(AppInfo, Apps, State) || AppInfo <- ToBuild],
+ {ok, State}
+ end.
+
+find_plugin(Plugin, Profiles, State) ->
+ ec_lists:search(fun(Profile) ->
+ Plugins = rebar_state:get(State, {plugins, Profile}, []),
+ case find(list_to_atom(Plugin), Plugins) of
+ false ->
+ not_found;
+ P ->
+ {ok, P}
+ end
+ end, Profiles).
+
+find(_Plugin, []) ->
+ false;
+find(Plugin, [Plugin | _Plugins]) ->
+ Plugin;
+find(Plugin, [Plugin1 | Plugins]) when is_tuple(Plugin1) ->
+ case element(1, Plugin1) =:= Plugin of
+ true ->
+ Plugin1;
+ false ->
+ find(Plugin, Plugins)
+ end.
+
+build_plugin(AppInfo, Apps, State) ->
+ Providers = rebar_state:providers(State),
+ AppDir = rebar_app_info:dir(AppInfo),
+ C = rebar_config:consult(AppDir),
+ S = rebar_state:new(rebar_state:all_deps(rebar_state:new(), Apps), C, AppDir),
+ rebar_prv_compile:compile(S, Providers, AppInfo).
diff --git a/src/rebar_prv_release.erl b/src/rebar_prv_release.erl
index 595280c..2cf9b23 100644
--- a/src/rebar_prv_release.erl
+++ b/src/rebar_prv_release.erl
@@ -22,7 +22,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 release"},
{short_desc, "Build release of project."},
@@ -32,31 +32,7 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
- Caller = rebar_state:get(State, caller, api),
- Options = rebar_state:command_args(State),
- DepsDir = rebar_dir:deps_dir(State),
- ProjectAppDirs = lists:delete(".", ?DEFAULT_PROJECT_APP_DIRS),
- LibDirs = rebar_utils:filtermap(fun ec_file:exists/1,
- [?DEFAULT_CHECKOUTS_DIR, DepsDir | ProjectAppDirs]),
- OutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR),
- AllOptions = string:join(["release" | Options], " "),
- try
- case rebar_state:get(State, relx, []) of
- [] ->
- relx:main([{lib_dirs, LibDirs}
- ,{output_dir, OutputDir}
- ,{caller, Caller}], AllOptions);
- Config ->
- relx:main([{lib_dirs, LibDirs}
- ,{config, lists:reverse(Config)}
- ,{output_dir, OutputDir}
- ,{caller, Caller}], AllOptions)
- end,
- {ok, State}
- catch
- throw:T ->
- {error, {rlx_prv_release, T}}
- end.
+ rebar_relx:do(rlx_prv_release, "release", ?PROVIDER, State).
-spec format_error(any()) -> iolist().
format_error(Reason) ->
diff --git a/src/rebar_prv_relup.erl b/src/rebar_prv_relup.erl
new file mode 100644
index 0000000..a4cd8ae
--- /dev/null
+++ b/src/rebar_prv_relup.erl
@@ -0,0 +1,40 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+
+-module(rebar_prv_relup).
+
+-behaviour(provider).
+
+-export([init/1,
+ do/1,
+ format_error/1]).
+
+-include("rebar.hrl").
+
+-define(PROVIDER, relup).
+-define(DEPS, [release]).
+
+%% ===================================================================
+%% Public API
+%% ===================================================================
+
+-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
+init(State) ->
+ Provider = providers:create([{name, ?PROVIDER},
+ {module, ?MODULE},
+ {bare, true},
+ {deps, ?DEPS},
+ {example, "rebar3 relup"},
+ {short_desc, "Create relup of releases."},
+ {desc, "Create relup of releases."},
+ {opts, relx:opt_spec_list()}]),
+ State1 = rebar_state:add_provider(State, Provider),
+ {ok, State1}.
+
+-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
+do(State) ->
+ rebar_relx:do(rlx_prv_release, "relup", ?PROVIDER, State).
+
+-spec format_error(any()) -> iolist().
+format_error(Reason) ->
+ io_lib:format("~p", [Reason]).
diff --git a/src/rebar_prv_report.erl b/src/rebar_prv_report.erl
index e052998..587fad7 100644
--- a/src/rebar_prv_report.erl
+++ b/src/rebar_prv_report.erl
@@ -23,7 +23,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 report \"<task>\""},
{short_desc, "Provide a crash report to be sent to the rebar3 issues page."},
diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl
index ec2f692..84ad723 100644
--- a/src/rebar_prv_shell.erl
+++ b/src/rebar_prv_shell.erl
@@ -45,14 +45,25 @@
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
- State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
- {module, ?MODULE},
- {bare, false},
- {deps, ?DEPS},
- {example, "rebar3 shell"},
- {short_desc, "Run shell with project apps and deps in path."},
- {desc, info()},
- {opts, [{config, undefined, "config", string, "Path to the config file to use. Defaults to the sys_config defined for relx, if present."}]}])),
+ State1 = rebar_state:add_provider(
+ State,
+ providers:create([
+ {name, ?PROVIDER},
+ {module, ?MODULE},
+ {bare, true},
+ {deps, ?DEPS},
+ {example, "rebar3 shell"},
+ {short_desc, "Run shell with project apps and deps in path."},
+ {desc, info()},
+ {opts, [{config, undefined, "config", string,
+ "Path to the config file to use. Defaults to the "
+ "sys_config defined for relx, if present."},
+ {name, undefined, "name", atom,
+ "Gives a long name to the node."},
+ {sname, undefined, "sname", atom,
+ "Gives a short name to the node."}]}
+ ])
+ ),
{ok, State1}.
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
@@ -72,6 +83,21 @@ format_error(Reason) ->
%% immediately kill the script. ctrl-g, however, works fine
shell(State) ->
+ setup_name(State),
+ setup_paths(State),
+ setup_shell(),
+ %% apps must be started after the change in shell because otherwise
+ %% their application masters never gets the new group leader (held in
+ %% their internal state)
+ maybe_boot_apps(State),
+ rebar_agent:start_link(State),
+ %% this call never returns (until user quits shell)
+ timer:sleep(infinity).
+
+info() ->
+ "Start a shell with project and deps preloaded similar to~n'erl -pa ebin -pa deps/*/ebin'.~n".
+
+setup_shell() ->
%% scan all processes for any with references to the old user and save them to
%% update later
NeedsUpdate = [Pid || Pid <- erlang:processes(),
@@ -84,25 +110,100 @@ shell(State) ->
%% wait until user_drv and user have been registered (max 3 seconds)
ok = wait_until_user_started(3000),
%% set any process that had a reference to the old user's group leader to the
- %% new user process
- _ = [erlang:group_leader(whereis(user), Pid) || Pid <- NeedsUpdate],
- %% enable error_logger's tty output
- ok = error_logger:swap_handler(tty),
- %% disable the simple error_logger (which may have been added multiple
- %% times). removes at most the error_logger added by init and the
- %% error_logger added by the tty handler
- ok = remove_error_handler(3),
+ %% new user process. Catch the race condition when the Pid exited after the
+ %% liveness check.
+ _ = [catch erlang:group_leader(whereis(user), Pid) || Pid <- NeedsUpdate,
+ is_process_alive(Pid)],
+ try
+ %% enable error_logger's tty output
+ error_logger:swap_handler(tty),
+ %% disable the simple error_logger (which may have been added multiple
+ %% times). removes at most the error_logger added by init and the
+ %% error_logger added by the tty handler
+ remove_error_handler(3)
+ catch
+ E:R -> % may fail with custom loggers
+ ?DEBUG("Logger changes failed for ~p:~p (~p)", [E,R,erlang:get_stacktrace()]),
+ hope_for_best
+ end.
+
+setup_paths(State) ->
%% Add deps to path
code:add_pathsa(rebar_state:code_paths(State, all_deps)),
%% add project app test paths
- ok = add_test_paths(State),
- %% try to read in sys.config file
- ok = reread_config(State),
- %% this call never returns (until user quits shell)
- timer:sleep(infinity).
+ ok = add_test_paths(State).
-info() ->
- "Start a shell with project and deps preloaded similar to~n'erl -pa ebin -pa deps/*/ebin'.~n".
+maybe_boot_apps(State) ->
+ case find_apps_to_boot(State) of
+ undefined ->
+ %% try to read in sys.config file
+ ok = reread_config(State);
+ Apps ->
+ %% load apps, then check config, then boot them.
+ load_apps(Apps),
+ ok = reread_config(State),
+ boot_apps(Apps)
+ end.
+
+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} ->
+ net_kernel:start([Name, longnames]);
+ {undefined, SName} ->
+ net_kernel:start([SName, shortnames]);
+ {_, _} ->
+ ?ABORT("Cannot have both short and long node names defined", [])
+ end.
+
+find_apps_to_boot(State) ->
+ %% Try the shell_apps option
+ case rebar_state:get(State, shell_apps, undefined) of
+ undefined ->
+ %% Get to the relx tuple instead
+ case lists:keyfind(release, 1, rebar_state:get(State, relx, [])) of
+ {_, _, Apps} -> Apps;
+ false -> undefined
+ end;
+ Apps ->
+ Apps
+ end.
+
+load_apps(Apps) ->
+ [case application:load(App) of
+ ok ->
+ {ok, Ks} = application:get_all_key(App),
+ load_apps(proplists:get_value(applications, Ks));
+ _ ->
+ error % will be caught when starting the app
+ end || App <- Apps,
+ not lists:keymember(App, 1, application:loaded_applications())],
+ ok.
+
+reread_config(State) ->
+ case find_config(State) of
+ no_config ->
+ ok;
+ ConfigList ->
+ _ = [application:set_env(Application, Key, Val)
+ || {Application, Items} <- ConfigList,
+ {Key, Val} <- Items],
+ ok
+ end.
+
+boot_apps(Apps) ->
+ ?WARN("The rebar3 shell is a development tool; to deploy "
+ "applications in production, consider using releases "
+ "(http://www.rebar3.org/v3.0/docs/releases)", []),
+ Res = [application:ensure_all_started(App) || App <- Apps],
+ _ = [?INFO("Booted ~p", [App])
+ || {ok, Booted} <- Res,
+ App <- Booted],
+ _ = [?ERROR("Failed to boot ~p for reason ~p", [App, Reason])
+ || {error, {App, Reason}} <- Res],
+ ok.
remove_error_handler(0) ->
?WARN("Unable to remove simple error_logger handler", []);
@@ -124,32 +225,16 @@ wait_until_user_started(Timeout) ->
end.
add_test_paths(State) ->
- lists:foreach(fun(App) ->
- AppDir = rebar_app_info:out_dir(App),
- %% ignore errors resulting from non-existent directories
- _ = code:add_path(filename:join([AppDir, "test"]))
- end, rebar_state:project_apps(State)),
+ _ = [begin
+ AppDir = rebar_app_info:out_dir(App),
+ %% ignore errors resulting from non-existent directories
+ _ = code:add_path(filename:join([AppDir, "test"]))
+ end || App <- rebar_state:project_apps(State)],
_ = code:add_path(filename:join([rebar_dir:base_dir(State), "test"])),
ok.
-reread_config(State) ->
- case find_config(State) of
- no_config ->
- ok;
- {ok, ConfigList} ->
- lists:foreach(fun ({Application, Items}) ->
- lists:foreach(fun ({Key, Val}) ->
- application:set_env(Application, Key, Val)
- end,
- Items)
- end,
- ConfigList);
- {error, Error} ->
- ?ABORT("Error while attempting to read configuration file: ~p", [Error])
- end.
-
% First try the --config flag, then try the relx sys_config
--spec find_config(rebar_state:t()) -> {ok, [tuple()]}|no_config|{error, tuple()}.
+-spec find_config(rebar_state:t()) -> [tuple()] | no_config.
find_config(State) ->
case find_config_option(State) of
no_config ->
@@ -158,7 +243,7 @@ find_config(State) ->
Result
end.
--spec find_config_option(rebar_state:t()) -> {ok, [tuple()]}|no_config|{error, tuple()}.
+-spec find_config_option(rebar_state:t()) -> [tuple()] | no_config.
find_config_option(State) ->
{Opts, _} = rebar_state:command_parsed_args(State),
case proplists:get_value(config, Opts) of
@@ -168,7 +253,7 @@ find_config_option(State) ->
consult_config(State, Filename)
end.
--spec find_config_relx(rebar_state:t()) -> {ok, [tuple()]}|no_config|{error, tuple()}.
+-spec find_config_relx(rebar_state:t()) -> [tuple()] | no_config.
find_config_relx(State) ->
case proplists:get_value(sys_config, rebar_state:get(State, relx, [])) of
undefined ->
@@ -181,11 +266,7 @@ find_config_relx(State) ->
consult_config(State, Filename) ->
Fullpath = filename:join(rebar_dir:root_dir(State), Filename),
?DEBUG("Loading configuration from ~p", [Fullpath]),
- case file:consult(Fullpath) of
- {ok, [Config]} ->
- {ok, Config};
- {ok, []} ->
- {ok, []};
- {error, Error} ->
- {error, {Error, Fullpath}}
+ case rebar_file_utils:try_consult(Fullpath) of
+ [T] -> T;
+ [] -> []
end.
diff --git a/src/rebar_prv_tar.erl b/src/rebar_prv_tar.erl
index f7557bd..b3a12c0 100644
--- a/src/rebar_prv_tar.erl
+++ b/src/rebar_prv_tar.erl
@@ -12,7 +12,7 @@
-include("rebar.hrl").
-define(PROVIDER, tar).
--define(DEPS, [compile]).
+-define(DEPS, [release]).
%% ===================================================================
%% Public API
@@ -22,7 +22,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 tar"},
{short_desc, "Tar archive of release built of project."},
@@ -32,26 +32,7 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
- Caller = rebar_state:get(State, caller, api),
- Options = rebar_state:command_args(State),
- DepsDir = rebar_dir:deps_dir(State),
- ProjectAppDirs = lists:delete(".", ?DEFAULT_PROJECT_APP_DIRS),
- LibDirs = rebar_utils:filtermap(fun ec_file:exists/1,
- [?DEFAULT_CHECKOUTS_DIR, DepsDir | ProjectAppDirs]),
- OutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR),
- AllOptions = string:join(["release", "tar" | Options], " "),
- case rebar_state:get(State, relx, []) of
- [] ->
- relx:main([{lib_dirs, LibDirs}
- ,{output_dir, OutputDir}
- ,{caller, Caller}], AllOptions);
- Config ->
- relx:main([{lib_dirs, LibDirs}
- ,{config, Config}
- ,{output_dir, OutputDir}
- ,{caller, Caller}], AllOptions)
- end,
- {ok, State}.
+ rebar_relx:do(rlx_prv_release, "tar", ?PROVIDER, State).
-spec format_error(any()) -> iolist().
format_error(Reason) ->
diff --git a/src/rebar_prv_unlock.erl b/src/rebar_prv_unlock.erl
index 9790fcf..b049c92 100644
--- a/src/rebar_prv_unlock.erl
+++ b/src/rebar_prv_unlock.erl
@@ -22,7 +22,7 @@ init(State) ->
State,
providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, ""},
{short_desc, "Unlock dependencies."},
diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl
index dfb719a..6838bab 100644
--- a/src/rebar_prv_update.erl
+++ b/src/rebar_prv_update.erl
@@ -23,7 +23,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 update"},
{short_desc, "Update package index."},
diff --git a/src/rebar_prv_upgrade.erl b/src/rebar_prv_upgrade.erl
index 05845e4..49d5674 100644
--- a/src/rebar_prv_upgrade.erl
+++ b/src/rebar_prv_upgrade.erl
@@ -28,7 +28,7 @@ init(State) ->
rebar_state:add_provider(State,
providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 upgrade [cowboy[,ranch]]"},
{short_desc, "Upgrade dependencies."},
@@ -94,10 +94,15 @@ prepare_locks([Name|Names], Deps, Locks, Unlocks) ->
AtomName = binary_to_atom(Name, utf8),
case lists:keyfind(Name, 1, Locks) of
{_, _, 0} = Lock ->
- case lists:keyfind(AtomName, 1, Deps) of
- false ->
+ case {lists:keyfind(AtomName, 1, Deps), lists:member(AtomName, Deps)} of
+ {false, false} ->
?PRV_ERROR({unknown_dependency, Name});
- Dep ->
+ {Dep, false} ->
+ {Source, NewLocks, NewUnlocks} = prepare_lock(Dep, Lock, Locks),
+ prepare_locks(Names, Deps, NewLocks,
+ [{Name, Source, 0} | NewUnlocks ++ Unlocks]);
+ {false, true} -> % package as a single atom
+ Dep = AtomName,
{Source, NewLocks, NewUnlocks} = prepare_lock(Dep, Lock, Locks),
prepare_locks(Names, Deps, NewLocks,
[{Name, Source, 0} | NewUnlocks ++ Unlocks])
@@ -116,9 +121,13 @@ prepare_locks([Name|Names], Deps, Locks, Unlocks) ->
end.
prepare_lock(Dep, Lock, Locks) ->
- Source = Source = case Dep of
- {_, Src} -> Src;
- {_, _, Src} -> Src
+ Source = case Dep of
+ {_, SrcOrVsn} -> SrcOrVsn;
+ {_, _, Src} -> Src;
+ _ when is_atom(Dep) ->
+ %% version-free package. Must unlock whatever matches in locks
+ {_, Vsn, _} = lists:keyfind(ec_cnv:to_binary(Dep), 1, Locks),
+ Vsn
end,
{NewLocks, NewUnlocks} = unlock_higher_than(0, Locks -- [Lock]),
{Source, NewLocks, NewUnlocks}.
diff --git a/src/rebar_prv_version.erl b/src/rebar_prv_version.erl
index 5edcc85..bcc5f6c 100644
--- a/src/rebar_prv_version.erl
+++ b/src/rebar_prv_version.erl
@@ -22,7 +22,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 version"},
{short_desc, "Print version for rebar and current Erlang."},
diff --git a/src/rebar_prv_xref.erl b/src/rebar_prv_xref.erl
index ed53da6..623e946 100644
--- a/src/rebar_prv_xref.erl
+++ b/src/rebar_prv_xref.erl
@@ -27,7 +27,7 @@ init(State) ->
Provider = providers:create([{name, ?PROVIDER},
{module, ?MODULE},
{deps, ?DEPS},
- {bare, false},
+ {bare, true},
{example, "rebar3 xref"},
{short_desc, short_desc()},
{desc, desc()}]),
diff --git a/src/rebar_relx.erl b/src/rebar_relx.erl
new file mode 100644
index 0000000..a3adedd
--- /dev/null
+++ b/src/rebar_relx.erl
@@ -0,0 +1,69 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+
+-module(rebar_relx).
+
+-export([do/4,
+ format_error/1]).
+
+-include("rebar.hrl").
+
+%% ===================================================================
+%% Public API
+%% ===================================================================
+
+-spec do(atom(), string(), atom(), rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
+do(Module, Command, Provider, State) ->
+ Options = rebar_state:command_args(State),
+ DepsDir = rebar_dir:deps_dir(State),
+ ProjectAppDirs = lists:delete(".", ?DEFAULT_PROJECT_APP_DIRS),
+ LibDirs = rebar_utils:filtermap(fun ec_file:exists/1,
+ [?DEFAULT_CHECKOUTS_DIR, DepsDir | ProjectAppDirs]),
+ OutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR),
+ 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),
+ try
+ case rebar_state:get(State, relx, []) of
+ [] ->
+ relx:main([{lib_dirs, LibDirs}
+ ,{output_dir, OutputDir}
+ ,{caller, api}], AllOptions);
+ Config ->
+ Config1 = update_config(Config),
+ relx:main([{lib_dirs, LibDirs}
+ ,{config, Config1}
+ ,{output_dir, OutputDir}
+ ,{caller, api}], AllOptions)
+ end,
+ rebar_hooks:run_all_hooks(Cwd, post, Provider, Providers, State),
+ {ok, State}
+ catch
+ throw:T ->
+ {error, {Module, T}}
+ end.
+
+-spec format_error(any()) -> iolist().
+format_error(Reason) ->
+ io_lib:format("~p", [Reason]).
+
+%% To handle profiles rebar3 expects the provider to use the first entry
+%% in a configuration key-value list as the value of a key if dups exist.
+%% This does not work with relx. Some config options must not lose their
+%% order (release which has an extends option is one). So here we pull out
+%% options that are special so we can reverse the rest so what we expect
+%% from a rebar3 profile is what we get on the relx side.
+-define(SPECIAL_KEYS, [release, vm_args, sys_config, overlay_vars, lib_dirs]).
+
+update_config(Config) ->
+ {Special, Other} =
+ lists:foldl(fun(Tuple, {SpecialAcc, OtherAcc}) when is_tuple(Tuple) ->
+ case lists:member(element(1, Tuple), ?SPECIAL_KEYS) of
+ true ->
+ {[Tuple | SpecialAcc], OtherAcc};
+ false ->
+ {SpecialAcc, [Tuple | OtherAcc]}
+ end
+ end, {[], []}, Config),
+ lists:reverse(Special) ++ Other.
diff --git a/src/rebar_state.erl b/src/rebar_state.erl
index 7a6e60d..d0b28de 100644
--- a/src/rebar_state.erl
+++ b/src/rebar_state.erl
@@ -1,8 +1,13 @@
-module(rebar_state).
-export([new/0, new/1, new/2, new/3,
+
get/2, get/3, set/3,
+ format_error/1,
+
+ has_all_artifacts/1,
+
code_paths/2, code_paths/3, update_code_paths/3,
opts/1, opts/2,
@@ -12,7 +17,7 @@
lock/1, lock/2,
- current_profiles/1,
+ current_profiles/1, current_profiles/2,
command_args/1, command_args/2,
command_parsed_args/1, command_parsed_args/2,
@@ -24,6 +29,7 @@
project_apps/1, project_apps/2,
deps_to_build/1, deps_to_build/2,
+ all_plugin_deps/1, all_plugin_deps/2, update_all_plugin_deps/2,
all_deps/1, all_deps/2, update_all_deps/2,
namespace/1, namespace/2,
@@ -39,6 +45,7 @@
providers/1, providers/2, add_provider/2]).
-include("rebar.hrl").
+-include_lib("providers/include/providers.hrl").
-record(state_t, {dir :: file:name(),
opts = dict:new() :: rebar_dict(),
@@ -48,13 +55,14 @@
lock = [],
current_profiles = [default] :: [atom()],
- namespace = undefined :: atom(),
+ namespace = default :: atom(),
command_args = [],
- command_parsed_args = [],
+ command_parsed_args = {[], []},
project_apps = [] :: [rebar_app_info:t()],
deps_to_build = [] :: [rebar_app_info:t()],
+ all_plugin_deps = [] :: [rebar_app_info:t()],
all_deps = [] :: [rebar_app_info:t()],
packages = undefined :: {rebar_dict(), rebar_digraph()} | undefined,
@@ -76,7 +84,10 @@ new() ->
new(Config) when is_list(Config) ->
BaseState = base_state(),
Deps = proplists:get_value(deps, Config, []),
- Opts = dict:from_list([{{deps, default}, Deps} | Config]),
+ Plugins = proplists:get_value(plugins, Config, []),
+ Terms = [{{deps, default}, Deps}, {{plugins, default}, Plugins} | Config],
+ true = rebar_config:verify_config_format(Terms),
+ Opts = dict:from_list(Terms),
BaseState#state_t { dir = rebar_dir:get_cwd(),
default = Opts,
opts = Opts }.
@@ -86,7 +97,11 @@ new(Profile, Config) when is_atom(Profile)
, is_list(Config) ->
BaseState = base_state(),
Deps = proplists:get_value(deps, Config, []),
- Opts = dict:from_list([{{deps, default}, Deps} | Config]),
+
+ Plugins = proplists:get_value(plugins, Config, []),
+ Terms = [{{deps, default}, Deps}, {{plugins, default}, Plugins} | Config],
+ true = rebar_config:verify_config_format(Terms),
+ Opts = dict:from_list(Terms),
BaseState#state_t { dir = rebar_dir:get_cwd(),
current_profiles = [Profile],
default = Opts,
@@ -99,15 +114,21 @@ new(ParentState=#state_t{}, Config) ->
-spec new(t(), list(), file:name()) -> t().
new(ParentState, Config, Dir) ->
Opts = ParentState#state_t.opts,
- LocalOpts = case rebar_config:consult_file(filename:join(Dir, ?LOCK_FILE)) of
+ LocalOpts = case rebar_config:consult_lock_file(filename:join(Dir, ?LOCK_FILE)) of
[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],
- dict:from_list([{{locks, default}, D}, {{deps, default}, Deps} | Config]);
+ Plugins = proplists:get_value(plugins, Config, []),
+ Terms = [{{locks, default}, D}, {{deps, default}, Deps}, {{plugins, default}, Plugins} | Config],
+ true = rebar_config:verify_config_format(Terms),
+ dict:from_list(Terms);
_ ->
D = proplists:get_value(deps, Config, []),
- dict:from_list([{{deps, default}, D} | Config])
+ Plugins = proplists:get_value(plugins, Config, []),
+ Terms = [{{deps, default}, D}, {{plugins, default}, Plugins} | Config],
+ true = rebar_config:verify_config_format(Terms),
+ dict:from_list(Terms)
end,
NewOpts = merge_opts(LocalOpts, Opts),
@@ -147,6 +168,26 @@ default(#state_t{default=Opts}) ->
default(State, Opts) ->
State#state_t{default=Opts}.
+format_error({profile_not_list, Profile, Other}) ->
+ io_lib:format("Profile config must be a list but for profile '~p' config given as:~n~p", [Profile, Other]).
+
+-spec has_all_artifacts(rebar_app_info:t()) -> true | providers:error().
+has_all_artifacts(State) ->
+ Artifacts = rebar_state:get(State, artifacts, []),
+ Dir = rebar_dir:base_dir(State),
+ all(Dir, Artifacts).
+
+all(_, []) ->
+ true;
+all(Dir, [File|Artifacts]) ->
+ case filelib:is_regular(filename:join(Dir, File)) of
+ false ->
+ ?DEBUG("Missing artifact ~s", [filename:join(Dir, File)]),
+ {false, File};
+ true ->
+ all(Dir, Artifacts)
+ end.
+
code_paths(#state_t{code_paths=CodePaths}, Key) ->
case dict:find(Key, CodePaths) of
{ok, CodePath} ->
@@ -175,6 +216,9 @@ opts(State, Opts) ->
current_profiles(#state_t{current_profiles=Profiles}) ->
Profiles.
+current_profiles(State, Profiles) ->
+ State#state_t{current_profiles=Profiles}.
+
lock(#state_t{lock=Lock}) ->
Lock.
@@ -221,14 +265,17 @@ apply_overrides(State=#state_t{overrides=Overrides}, AppName) ->
StateAcc
end, State1, Overrides),
- lists:foldl(fun({add, N, O}, StateAcc) when N =:= Name ->
+ State3 = lists:foldl(fun({add, N, O}, StateAcc) when N =:= Name ->
lists:foldl(fun({Key, Value}, StateAcc1) ->
OldValue = rebar_state:get(StateAcc1, Key, []),
rebar_state:set(StateAcc1, Key, Value++OldValue)
end, StateAcc, O);
(_, StateAcc) ->
StateAcc
- end, State2, Overrides).
+ end, State2, Overrides),
+
+ Opts = opts(State3),
+ State3#state_t{default=Opts}.
add_to_profile(State, Profile, KVs) when is_atom(Profile), is_list(KVs) ->
Profiles = rebar_state:get(State, profiles, []),
@@ -244,12 +291,18 @@ apply_profiles(State, [default]) ->
apply_profiles(State=#state_t{default = Defaults, current_profiles=CurrentProfiles}, Profiles) ->
AppliedProfiles = deduplicate(CurrentProfiles ++ Profiles),
ConfigProfiles = rebar_state:get(State, profiles, []),
+
NewOpts =
lists:foldl(fun(default, OptsAcc) ->
OptsAcc;
(Profile, OptsAcc) ->
- ProfileOpts = dict:from_list(proplists:get_value(Profile, ConfigProfiles, [])),
- merge_opts(Profile, ProfileOpts, OptsAcc)
+ case proplists:get_value(Profile, ConfigProfiles, []) of
+ OptsList when is_list(OptsList) ->
+ ProfileOpts = dict:from_list(OptsList),
+ merge_opts(Profile, ProfileOpts, OptsAcc);
+ Other ->
+ throw(?PRV_ERROR({profile_not_list, Profile, Other}))
+ end
end, Defaults, AppliedProfiles),
State#state_t{current_profiles = AppliedProfiles, opts=NewOpts}.
@@ -267,11 +320,18 @@ do_deduplicate([Head | Rest], Acc) ->
merge_opts(Profile, NewOpts, OldOpts) ->
Opts = merge_opts(NewOpts, OldOpts),
- case dict:find(deps, NewOpts) of
+ Opts2 = case dict:find(plugins, NewOpts) of
{ok, Value} ->
- dict:store({deps, Profile}, Value, Opts);
+ dict:store({plugins, Profile}, Value, Opts);
error ->
Opts
+ end,
+
+ case dict:find(deps, NewOpts) of
+ {ok, Value2} ->
+ dict:store({deps, Profile}, Value2, Opts2);
+ error ->
+ Opts2
end.
merge_opts(NewOpts, OldOpts) ->
@@ -279,6 +339,10 @@ merge_opts(NewOpts, OldOpts) ->
NewValue;
({deps, _}, NewValue, _OldValue) ->
NewValue;
+ (plugins, NewValue, _OldValue) ->
+ NewValue;
+ ({plugins, _}, NewValue, _OldValue) ->
+ NewValue;
(profiles, NewValue, OldValue) ->
dict:to_list(merge_opts(dict:from_list(NewValue), dict:from_list(OldValue)));
(_Key, NewValue, OldValue) when is_list(NewValue) ->
@@ -344,6 +408,15 @@ all_deps(#state_t{all_deps=Apps}) ->
all_deps(State=#state_t{}, NewApps) ->
State#state_t{all_deps=NewApps}.
+all_plugin_deps(#state_t{all_plugin_deps=Apps}) ->
+ Apps.
+
+all_plugin_deps(State=#state_t{}, NewApps) ->
+ State#state_t{all_plugin_deps=NewApps}.
+
+update_all_plugin_deps(State=#state_t{all_plugin_deps=Apps}, NewApps) ->
+ State#state_t{all_plugin_deps=Apps++NewApps}.
+
update_all_deps(State=#state_t{all_deps=Apps}, NewApps) ->
State#state_t{all_deps=Apps++NewApps}.
diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl
index 353fa36..3aa6e90 100644
--- a/src/rebar_templater.erl
+++ b/src/rebar_templater.erl
@@ -380,4 +380,4 @@ write_file(Output, Data, Force) ->
%% Render a binary to a string, using mustache and the specified context
%%
render(Bin, Context) ->
- mustache:render(ec_cnv:to_binary(Bin), Context, [{key_type, atom}]).
+ bbmustache:render(ec_cnv:to_binary(Bin), Context, [{key_type, atom}]).
diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl
index 160d547..0cbc7c2 100644
--- a/src/rebar_utils.erl
+++ b/src/rebar_utils.erl
@@ -47,6 +47,8 @@
deprecated/4,
erl_opts/1,
indent/1,
+ update_code/1,
+ remove_from_code_path/1,
cleanup_code_path/1,
args_to_tasks/1,
expand_env_variable/3,
@@ -152,7 +154,7 @@ sh(Command0, Options0) ->
Command = lists:flatten(patch_on_windows(Command0, proplists:get_value(env, Options, []))),
PortSettings = proplists:get_all_values(port_settings, Options) ++
- [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide],
+ [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide, eof],
?DEBUG("Port Cmd: ~s\nPort Opts: ~p\n", [Command, PortSettings]),
Port = open_port({spawn, Command}, PortSettings),
@@ -433,10 +435,14 @@ sh_loop(Port, Fun, Acc) ->
sh_loop(Port, Fun, Fun(Line ++ "\n", Acc));
{Port, {data, {noeol, Line}}} ->
sh_loop(Port, Fun, Fun(Line, Acc));
- {Port, {exit_status, 0}} ->
- {ok, lists:flatten(lists:reverse(Acc))};
- {Port, {exit_status, Rc}} ->
- {error, {Rc, lists:flatten(lists:reverse(Acc))}}
+ {Port, eof} ->
+ Data = lists:flatten(lists:reverse(Acc)),
+ receive
+ {Port, {exit_status, 0}} ->
+ {ok, Data};
+ {Port, {exit_status, Rc}} ->
+ {error, {Rc, Data}}
+ end
end.
beam_to_mod(Dir, Filename) ->
@@ -563,6 +569,39 @@ filter_defines([Opt | Rest], Acc) ->
indent(Amount) when erlang:is_integer(Amount) ->
[?ONE_LEVEL_INDENT || _ <- lists:seq(1, Amount)].
+%% Replace code paths with new paths for existing apps and
+%% purge code of the old modules from those apps.
+update_code(Paths) ->
+ lists:foreach(fun(Path) ->
+ Name = filename:basename(Path, "/ebin"),
+ App = list_to_atom(Name),
+ application:load(App),
+ case application:get_key(App, modules) of
+ undefined ->
+ code:add_patha(Path),
+ ok;
+ {ok, Modules} ->
+ code:replace_path(Name, Path),
+ [begin code:purge(M), code:delete(M) end || M <- Modules]
+ end
+ end, Paths).
+
+remove_from_code_path(Paths) ->
+ lists:foreach(fun(Path) ->
+ Name = filename:basename(Path, "/ebin"),
+ App = list_to_atom(Name),
+ application:load(App),
+ case application:get_key(App, modules) of
+ undefined ->
+ application:unload(App),
+ ok;
+ {ok, Modules} ->
+ application:unload(App),
+ [begin code:purge(M), code:delete(M) end || M <- Modules]
+ end,
+ code:del_path(Path)
+ end, Paths).
+
cleanup_code_path(OrigPath) ->
CurrentPath = code:get_path(),
AddedPaths = CurrentPath -- OrigPath,
diff --git a/test/mock_pkg_resource.erl b/test/mock_pkg_resource.erl
index e94ea93..eda863b 100644
--- a/test/mock_pkg_resource.erl
+++ b/test/mock_pkg_resource.erl
@@ -15,7 +15,7 @@ mock() -> mock([]).
%% Specific config options are explained in each of the private functions.
-spec mock(Opts) -> ok when
Opts :: [Option],
- Option :: {update, [App]}
+ Option :: {upgrade, [App]}
| {cache_dir, string()}
| {default_vsn, Vsn}
| {override_vsn, [{App, Vsn}]}
@@ -73,6 +73,7 @@ mock_vsn(_Opts) ->
%% into a `rebar.config' file to describe dependencies.
mock_download(Opts) ->
Deps = proplists:get_value(pkgdeps, Opts, []),
+ Config = proplists:get_value(config, Opts, []),
meck:expect(
?MOD, download,
fun (Dir, {pkg, AppBin, Vsn}, _) ->
@@ -83,7 +84,7 @@ mock_download(Opts) ->
Dir, App, binary_to_list(Vsn),
[kernel, stdlib] ++ [element(1,D) || D <- AppDeps]
),
- rebar_test_utils:create_config(Dir, [{deps, AppDeps}]),
+ rebar_test_utils:create_config(Dir, [{deps, AppDeps}]++Config),
TarApp = App++"-"++binary_to_list(Vsn)++".tar",
Tarball = filename:join([Dir, TarApp]),
Contents = filename:join([Dir, "contents.tar.gz"]),
@@ -120,7 +121,6 @@ mock_pkg_index(Opts) ->
meck:expect(rebar_packages, get_packages,
fun(_State) -> {Dict, Digraph} end).
-
%%%%%%%%%%%%%%%
%%% Helpers %%%
%%%%%%%%%%%%%%%
@@ -151,15 +151,14 @@ find_parts([{AppName, Deps}|Rest], Skip, Acc) ->
true -> find_parts(Rest, Skip, Acc);
false ->
AccNew = dict:store(AppName,
- [{<<"deps">>,Deps}, {<<"link">>,<<"undef">>}],
+ Deps,
Acc),
find_parts(Rest, Skip, AccNew)
end.
to_graph_parts(Dict) ->
LastUpdated = os:timestamp(),
- dict:fold(fun(K,V,{Ks,Vs}) ->
- {_,Deps} = lists:keyfind(<<"deps">>, 1, V),
+ dict:fold(fun(K,Deps,{Ks,Vs}) ->
{[{K,LastUpdated}|Ks],
[{K,{list_to_binary(atom_to_list(DK)), list_to_binary(DV)}}
|| {DK,DV} <- Deps] ++ Vs}
diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl
index bdab075..8dca46c 100644
--- a/test/rebar_compile_SUITE.erl
+++ b/test/rebar_compile_SUITE.erl
@@ -18,9 +18,9 @@
deps_in_path/1,
delete_beam_if_source_deleted/1,
checkout_priority/1,
- compile_plugins/1,
highest_version_of_pkg_dep/1,
- parse_transform_test/1]).
+ parse_transform_test/1,
+ erl_first_files_test/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -47,8 +47,8 @@ all() ->
build_all_srcdirs, recompile_when_hrl_changes,
recompile_when_opts_change, dont_recompile_when_opts_dont_change,
dont_recompile_yrl_or_xrl, delete_beam_if_source_deleted,
- deps_in_path, checkout_priority, compile_plugins, highest_version_of_pkg_dep,
- parse_transform_test].
+ deps_in_path, checkout_priority, highest_version_of_pkg_dep,
+ parse_transform_test, erl_first_files_test].
build_basic_app(Config) ->
AppDir = ?config(apps, Config),
@@ -163,9 +163,10 @@ recompile_when_hrl_changes(Config) ->
ModTime = [filelib:last_modified(filename:join([EbinDir, F]))
|| F <- Files, filename:extension(F) == ".beam"],
+
timer:sleep(1000),
- os:cmd("touch " ++ HeaderFile),
+ rebar_file_utils:touch(HeaderFile),
rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}),
@@ -274,9 +275,9 @@ delete_beam_if_source_deleted(Config) ->
rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}),
EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]),
- SrcDir = filename:join([AppDir, "_build", "default", "lib", Name, "src"]),
+ _SrcDir = filename:join([AppDir, "_build", "default", "lib", Name, "src"]),
?assert(filelib:is_regular(filename:join(EbinDir, "not_a_real_src_" ++ Name ++ ".beam"))),
- file:delete(filename:join(SrcDir, "not_a_real_src_" ++ Name ++ ".erl")),
+ file:delete(filename:join([AppDir, "src", "not_a_real_src_" ++ Name ++ ".erl"])),
rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}),
@@ -307,6 +308,7 @@ deps_in_path(Config) ->
?assertEqual([], [Path || Path <- code:get_path(),
{match, _} <- [re:run(Path, DepName)]]),
%% Hope not to find pkg name in there
+
?assertEqual([], [Path || Path <- code:get_path(),
{match, _} <- [re:run(Path, PkgName)]]),
%% Build things
@@ -397,35 +399,6 @@ checkout_priority(Config) ->
?assertEqual(Vsn2, proplists:get_value(vsn, DepProps)),
?assertEqual(Vsn2, proplists:get_value(vsn, PkgProps)).
-%% Tests that compiling a project installs and compiles the plugins of deps
-compile_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 = rebar_test_utils:create_random_name("plugin1_"),
- mock_git_resource:mock([{config, [{plugins, [
- {list_to_atom(PluginName), Vsn}
- ]}]}]),
- mock_pkg_resource:mock([
- {pkgdeps, [{{iolist_to_binary(PluginName), iolist_to_binary(Vsn)}, []}]}
- ]),
-
- RConfFile =
- rebar_test_utils:create_config(AppDir,
- [{deps, [
- {list_to_atom(DepName), {git, "http://site.com/user/"++DepName++".git", {tag, Vsn}}}
- ]}]),
- {ok, RConf} = file:consult(RConfFile),
-
- %% Build with deps.
- rebar_test_utils:run_and_check(
- Config, RConf, ["compile"],
- {ok, [{app, Name}, {plugin, PluginName}, {dep, DepName}]}
- ).
highest_version_of_pkg_dep(Config) ->
AppDir = ?config(apps, Config),
@@ -472,3 +445,43 @@ parse_transform_test(Config) ->
EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]),
true = filelib:is_file(filename:join([EbinDir, "pascal.beam"])).
+
+erl_first_files_test(Config) ->
+ AppDir = ?config(apps, Config),
+ RebarConfig = [{erl_opts, [{parse_transform, mark_time}]},
+ {erl_first_files, ["src/mark_time.erl",
+ "src/b.erl",
+ "src/d.erl",
+ "src/a.erl"]}],
+
+ 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]),
+ rebar_test_utils:write_src_file(AppDir, "a.erl"),
+ rebar_test_utils:write_src_file(AppDir, "b.erl"),
+ rebar_test_utils:write_src_file(AppDir, "d.erl"),
+ rebar_test_utils:write_src_file(AppDir, "e.erl"),
+
+ ExtraSrc = <<"-module(mark_time). "
+ "-export([parse_transform/2]). "
+ "parse_transform([Form={attribute,_,module,Mod}|Forms], Options) -> "
+ " [Form, {attribute,1,number, os:timestamp()} | Forms];"
+ "parse_transform([Form|Forms], Options) -> "
+ " [Form | parse_transform(Forms, Options)].">>,
+
+ ok = file:write_file(filename:join([AppDir, "src", "mark_time.erl"]), ExtraSrc),
+
+ rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, [{app, Name}]}),
+
+ EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]),
+ true = filelib:is_file(filename:join([EbinDir, "mark_time.beam"])),
+
+ code:load_abs(filename:join([EbinDir, "a"])),
+ code:load_abs(filename:join([EbinDir, "b"])),
+ code:load_abs(filename:join([EbinDir, "d"])),
+ code:load_abs(filename:join([EbinDir, "e"])),
+ A = proplists:get_value(number, a:module_info(attributes)),
+ B = proplists:get_value(number, b:module_info(attributes)),
+ D = proplists:get_value(number, d:module_info(attributes)),
+ E = proplists:get_value(number, e:module_info(attributes)),
+ ?assertEqual([B,D,A,E], lists:sort([A,B,D,E])).
diff --git a/test/rebar_deps_SUITE.erl b/test/rebar_deps_SUITE.erl
index 6b902a5..afd487e 100644
--- a/test/rebar_deps_SUITE.erl
+++ b/test/rebar_deps_SUITE.erl
@@ -215,7 +215,7 @@ newly_added_dep(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_file(LockFile)),
+ rebar_config:consult_lock_file(LockFile)),
%% a should now be installed and c should not change
rebar_test_utils:run_and_check(
diff --git a/test/rebar_dialyzer_SUITE.erl b/test/rebar_dialyzer_SUITE.erl
index 724d8f0..1160d2d 100644
--- a/test/rebar_dialyzer_SUITE.erl
+++ b/test/rebar_dialyzer_SUITE.erl
@@ -25,15 +25,16 @@ end_per_suite(_Config) ->
init_per_testcase(Testcase, Config) ->
PrivDir = ?config(priv_dir, Config),
Prefix = ec_cnv:to_list(Testcase),
- Plt = filename:join(PrivDir, Prefix ++ ".project.plt"),
- BasePlt = Prefix ++ "base.plt",
- RebarConfig = [{dialyzer_plt, Plt},
- {dialyzer_base_plt, BasePlt},
- {dialyzer_base_plt_dir, PrivDir},
- {dialyzer_base_plt_apps, [erts]}],
- [{plt, Plt},
- {base_plt, filename:join(PrivDir, BasePlt)},
- {rebar_config, RebarConfig} |
+ BasePrefix = Prefix ++ "_base",
+ Opts = [{plt_prefix, Prefix},
+ {plt_location, PrivDir},
+ {base_plt_prefix, BasePrefix},
+ {base_plt_location, PrivDir},
+ {base_plt_apps, [erts]}],
+ Suffix = "_" ++ rebar_utils:otp_release() ++ "_plt",
+ [{plt, filename:join(PrivDir, Prefix ++ Suffix)},
+ {base_plt, filename:join(PrivDir, BasePrefix ++ Suffix)},
+ {rebar_config, [{dialyzer, Opts}]} |
rebar_test_utils:init_rebar_state(Config)].
all() ->
diff --git a/test/rebar_dir_SUITE.erl b/test/rebar_dir_SUITE.erl
new file mode 100644
index 0000000..a3c5052
--- /dev/null
+++ b/test/rebar_dir_SUITE.erl
@@ -0,0 +1,99 @@
+-module(rebar_dir_SUITE).
+
+-export([all/0, init_per_testcase/2, end_per_testcase/2]).
+
+-export([default_src_dirs/1, default_extra_src_dirs/1, default_all_src_dirs/1]).
+-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]).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+-include_lib("kernel/include/file.hrl").
+
+
+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].
+
+init_per_testcase(_, Config) ->
+ C = rebar_test_utils:init_rebar_state(Config),
+ AppDir = ?config(apps, C),
+
+ 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]),
+ C.
+
+end_per_testcase(_, _Config) -> ok.
+
+default_src_dirs(Config) ->
+ {ok, State} = rebar_test_utils:run_and_check(Config, [], ["compile"], return),
+
+ [] = rebar_dir:src_dirs(State),
+ ["src"] = rebar_dir:src_dirs(State, ["src"]).
+
+default_extra_src_dirs(Config) ->
+ {ok, State} = rebar_test_utils:run_and_check(Config, [], ["compile"], return),
+
+ [] = rebar_dir:extra_src_dirs(State),
+ ["src"] = rebar_dir:extra_src_dirs(State, ["src"]).
+
+default_all_src_dirs(Config) ->
+ {ok, State} = rebar_test_utils:run_and_check(Config, [], ["compile"], return),
+
+ [] = rebar_dir:all_src_dirs(State),
+ ["src", "test"] = rebar_dir:all_src_dirs(State, ["src"], ["test"]).
+
+src_dirs(Config) ->
+ RebarConfig = [{erl_opts, [{src_dirs, ["foo", "bar", "baz"]}]}],
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return),
+
+ ["foo", "bar", "baz"] = rebar_dir:src_dirs(State).
+
+extra_src_dirs(Config) ->
+ RebarConfig = [{erl_opts, [{extra_src_dirs, ["foo", "bar", "baz"]}]}],
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return),
+
+ ["foo", "bar", "baz"] = rebar_dir:extra_src_dirs(State).
+
+all_src_dirs(Config) ->
+ RebarConfig = [{erl_opts, [{src_dirs, ["foo", "bar"]}, {extra_src_dirs, ["baz", "qux"]}]}],
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return),
+
+ ["foo", "bar", "baz", "qux"] = rebar_dir:all_src_dirs(State).
+
+profile_src_dirs(Config) ->
+ RebarConfig = [
+ {erl_opts, [{src_dirs, ["foo", "bar"]}]},
+ {profiles, [
+ {more, [{erl_opts, [{src_dirs, ["baz", "qux"]}]}]}
+ ]}
+ ],
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["as", "more", "compile"], return),
+
+ R = lists:sort(["foo", "bar", "baz", "qux"]),
+ R = lists:sort(rebar_dir:src_dirs(State)).
+
+profile_extra_src_dirs(Config) ->
+ RebarConfig = [
+ {erl_opts, [{extra_src_dirs, ["foo", "bar"]}]},
+ {profiles, [
+ {more, [{erl_opts, [{extra_src_dirs, ["baz", "qux"]}]}]}
+ ]}
+ ],
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["as", "more", "compile"], return),
+
+ R = lists:sort(["foo", "bar", "baz", "qux"]),
+ R = lists:sort(rebar_dir:extra_src_dirs(State)).
+
+profile_all_src_dirs(Config) ->
+ RebarConfig = [
+ {erl_opts, [{src_dirs, ["foo"]}, {extra_src_dirs, ["bar"]}]},
+ {profiles, [
+ {more, [{erl_opts, [{src_dirs, ["baz"]}, {extra_src_dirs, ["qux"]}]}]}
+ ]}
+ ],
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["as", "more", "compile"], return),
+
+ R = lists:sort(["foo", "bar", "baz", "qux"]),
+ R = lists:sort(rebar_dir:all_src_dirs(State)).
diff --git a/test/rebar_disable_app_SUITE.erl b/test/rebar_disable_app_SUITE.erl
new file mode 100644
index 0000000..dd71ffb
--- /dev/null
+++ b/test/rebar_disable_app_SUITE.erl
@@ -0,0 +1,49 @@
+-module(rebar_disable_app_SUITE).
+-compile(export_all).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+-define(MOD(Name),
+ io_lib:format("-module(~s).~n-export([x/0]).~nx() -> ok.~n", [Name])).
+
+all() -> [disable_app].
+
+init_per_testcase(_, Config) ->
+ rebar_test_utils:init_rebar_state(Config).
+
+end_per_testcase(_, _Config) ->
+ ok.
+
+disable_app(Config) ->
+ AppDir = ?config(apps, Config),
+
+ Name1 = create_random_app(AppDir, "app1_"),
+ Name2 = create_random_app(AppDir, "app2_"),
+
+ RebarConfig = [{excluded_apps, [list_to_atom(Name1)]}],
+ %RebarConfig = [],
+
+ rebar_test_utils:run_and_check(
+ Config, RebarConfig, ["compile"],
+ {ok, [{app, Name2}]}),
+
+ App1 = filename:join([AppDir, "_build", "default", "lib", Name1, "ebin", Name1 ++ ".app"]),
+ ?assertEqual(filelib:is_file(App1), false),
+
+ App2 = filename:join([AppDir, "_build", "default", "lib", Name2, "ebin", Name2 ++ ".app"]),
+ ?assertEqual(filelib:is_file(App2), true).
+
+%%
+%% Utils
+%%
+create_random_app(AppDir, Prefix) ->
+ Name = rebar_test_utils:create_random_name(Prefix),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ rebar_test_utils:create_empty_app(filename:join([AppDir, "apps", Name]), Name, Vsn, [kernel, stdlib]),
+
+ ModName = rebar_test_utils:create_random_name("mod1_"),
+ Mod = filename:join([AppDir, "apps", Name, "src", ModName ++ ".erl"]),
+ ok = filelib:ensure_dir(Mod),
+ Src = ?MOD(ModName),
+ ok = ec_file:write(Mod, Src),
+ Name.
diff --git a/test/rebar_hooks_SUITE.erl b/test/rebar_hooks_SUITE.erl
index 642798f..85ca0e5 100644
--- a/test/rebar_hooks_SUITE.erl
+++ b/test/rebar_hooks_SUITE.erl
@@ -4,8 +4,13 @@
init_per_suite/1,
end_per_suite/1,
init_per_testcase/2,
+ end_per_testcase/2,
all/0,
- build_and_clean_app/1]).
+ build_and_clean_app/1,
+ escriptize_artifacts/1,
+ run_hooks_once/1,
+ run_hooks_for_plugins/1,
+ deps_hook_namespace/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -18,13 +23,17 @@ init_per_suite(Config) ->
Config.
end_per_suite(_Config) ->
- ok.
+ meck:unload().
init_per_testcase(_, Config) ->
rebar_test_utils:init_rebar_state(Config).
+end_per_testcase(_, _Config) ->
+ catch meck:unload().
+
all() ->
- [build_and_clean_app].
+ [build_and_clean_app, run_hooks_once, escriptize_artifacts,
+ run_hooks_for_plugins, deps_hook_namespace].
%% Test post provider hook cleans compiled project app, leaving it invalid
build_and_clean_app(Config) ->
@@ -36,3 +45,85 @@ build_and_clean_app(Config) ->
rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name, valid}]}),
rebar_test_utils:run_and_check(Config, [{provider_hooks, [{post, [{compile, clean}]}]}],
["compile"], {ok, [{app, Name, invalid}]}).
+
+escriptize_artifacts(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]),
+
+ Artifact = "bin/"++Name,
+ RConfFile =
+ rebar_test_utils:create_config(AppDir,
+ [
+ {escript_name, list_to_atom(Name)}
+ ,{artifacts, [Artifact]}
+ ]),
+ {ok, RConf} = file:consult(RConfFile),
+
+ try rebar_test_utils:run_and_check(Config, RConf, ["compile"], return)
+ catch
+ {error,
+ {rebar_prv_compile,
+ {missing_artifact, Artifact}}} ->
+ ok
+ end,
+ rebar_test_utils:run_and_check(Config, RConf++[{provider_hooks, [{post, [{compile, escriptize}]}]}],
+ ["compile"], {ok, [{app, Name, valid}
+ ,{file, filename:join([AppDir, "_build/default/", Artifact])}]}).
+
+run_hooks_once(Config) ->
+ AppDir = ?config(apps, Config),
+
+ Name = rebar_test_utils:create_random_name("app1_"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ RebarConfig = [{pre_hooks, [{compile, "mkdir blah"}]}],
+ rebar_test_utils:create_config(AppDir, RebarConfig),
+ rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),
+ rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], {ok, [{app, Name, valid}]}).
+
+deps_hook_namespace(Config) ->
+ mock_git_resource:mock([{deps, [{some_dep, "0.0.1"}]}]),
+ Deps = rebar_test_utils:expand_deps(git, [{"some_dep", "0.0.1", []}]),
+ TopDeps = rebar_test_utils:top_level_deps(Deps),
+
+ RebarConfig = [
+ {deps, TopDeps},
+ {overrides, [
+ {override, some_dep, [
+ {provider_hooks, [
+ {pre, [
+ {compile, clean}
+ ]}
+ ]}
+ ]}
+ ]}
+ ],
+ rebar_test_utils:run_and_check(
+ Config, RebarConfig, ["compile"],
+ {ok, [{dep, "some_dep"}]}
+ ).
+
+run_hooks_for_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]),
+
+ PluginName = rebar_test_utils:create_random_name("plugin1_"),
+ mock_git_resource:mock([{config, [{pre_hooks, [{compile, "echo whatsup > randomfile"}]}]}]),
+
+ RConfFile = rebar_test_utils:create_config(AppDir,
+ [{plugins, [
+ {list_to_atom(PluginName),
+ {git, "http://site.com/user/"++PluginName++".git",
+ {tag, Vsn}}}
+ ]}]),
+ {ok, RConf} = file:consult(RConfFile),
+
+ rebar_test_utils:run_and_check(Config, RConf, ["compile"], {ok, [{app, Name, valid},
+ {plugin, PluginName},
+ {file, filename:join([AppDir, "_build", "default", "plugins", PluginName, "randomfile"])}]}).
diff --git a/test/rebar_install_deps_SUITE.erl b/test/rebar_install_deps_SUITE.erl
index d1a1118..dca6308 100644
--- a/test/rebar_install_deps_SUITE.erl
+++ b/test/rebar_install_deps_SUITE.erl
@@ -48,10 +48,10 @@ end_per_testcase(_, Config) ->
Config.
format_expected_deps(Deps) ->
- [case Dep of
- {N,V} -> {dep, N, V};
- N -> {dep, N}
- end || Dep <- Deps].
+ lists:append([case Dep of
+ {N,V} -> [{dep, N, V}, {lock, N, V}];
+ N -> [{dep, N}, {lock, N}]
+ end || Dep <- Deps]).
%% format:
%% {Spec,
@@ -200,7 +200,7 @@ circular_skip(Config) -> run(Config).
fail_conflict(Config) ->
{ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
rebar_test_utils:run_and_check(
- Config, RebarConfig, ["install_deps"], ?config(expect, Config)
+ Config, RebarConfig, ["lock"], ?config(expect, Config)
),
check_warnings(error_calls(), ?config(warnings, Config), ?config(deps_type, Config)).
@@ -209,7 +209,7 @@ default_profile(Config) ->
AppDir = ?config(apps, Config),
{ok, Apps} = Expect = ?config(expect, Config),
rebar_test_utils:run_and_check(
- Config, RebarConfig, ["as", "profile", "install_deps"], Expect
+ Config, RebarConfig, ["as", "profile", "lock"], Expect
),
check_warnings(error_calls(), ?config(warnings, Config), ?config(deps_type, Config)),
BuildDir = filename:join([AppDir, "_build"]),
@@ -221,18 +221,30 @@ default_profile(Config) ->
|| {dep, App} <- Apps],
%% A second run to another profile also links default to the right spot
rebar_test_utils:run_and_check(
- Config, RebarConfig, ["as", "other", "install_deps"], Expect
+ Config, RebarConfig, ["as", "other", "lock"], Expect
),
[?assertMatch({ok, #file_info{type=directory}}, % somehow symlinks return dirs
file:read_file_info(filename:join([BuildDir, "other", "lib", App])))
|| {dep, App} <- Apps].
nondefault_profile(Config) ->
+ %% The dependencies here are saved directly to the
{ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
AppDir = ?config(apps, Config),
- {ok, Apps} = Expect = ?config(expect, Config),
+ {ok, AppLocks} = ?config(expect, Config),
+ try
+ rebar_test_utils:run_and_check(
+ Config, RebarConfig, ["as", "nondef", "lock"], {ok, AppLocks}
+ ),
+ error(generated_locks)
+ catch
+ error:generated_locks -> error(generated_locks);
+ _:_ -> ok
+ end,
+ Apps = [App || App = {dep, _} <- AppLocks],
+ Expect = {ok, Apps},
rebar_test_utils:run_and_check(
- Config, RebarConfig, ["as", "nondef", "install_deps"], Expect
+ Config, RebarConfig, ["as", "nondef", "lock"], Expect
),
check_warnings(error_calls(), ?config(warnings, Config), ?config(deps_type, Config)),
BuildDir = filename:join([AppDir, "_build"]),
@@ -244,7 +256,7 @@ nondefault_profile(Config) ->
|| {dep, App} <- Apps],
%% A second run to another profile doesn't link dependencies
rebar_test_utils:run_and_check(
- Config, RebarConfig, ["as", "other", "install_deps"], Expect
+ Config, RebarConfig, ["as", "other", "lock"], Expect
),
[?assertMatch({error, enoent},
file:read_file_info(filename:join([BuildDir, "default", "lib", App])))
@@ -254,7 +266,7 @@ nondefault_profile(Config) ->
run(Config) ->
{ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
rebar_test_utils:run_and_check(
- Config, RebarConfig, ["install_deps"], ?config(expect, Config)
+ Config, RebarConfig, ["lock"], ?config(expect, Config)
),
check_warnings(warning_calls(), ?config(warnings, Config), ?config(deps_type, Config)).
diff --git a/test/rebar_pkg_SUITE.erl b/test/rebar_pkg_SUITE.erl
index 95eb6f6..85bd6f0 100644
--- a/test/rebar_pkg_SUITE.erl
+++ b/test/rebar_pkg_SUITE.erl
@@ -61,7 +61,7 @@ init_per_testcase(good_disconnect=Name, Config0) ->
copy_to_cache(Pkg, Config),
meck:unload(httpc),
meck:new(httpc, [passthrough, unsticky]),
- meck:expect(httpc, request, fun(_, _, _, _) -> {error, econnrefused} end),
+ meck:expect(httpc, request, fun(_, _, _, _, _) -> {error, econnrefused} end),
Config;
init_per_testcase(bad_disconnect=Name, Config0) ->
Pkg = {<<"goodpkg">>, <<"1.0.0">>},
@@ -71,7 +71,7 @@ init_per_testcase(bad_disconnect=Name, Config0) ->
Config = mock_config(Name, Config1),
meck:unload(httpc),
meck:new(httpc, [passthrough, unsticky]),
- meck:expect(httpc, request, fun(_, _, _, _) -> {error, econnrefused} end),
+ meck:expect(httpc, request, fun(_, _, _, _, _) -> {error, econnrefused} end),
Config.
end_per_testcase(_, Config) ->
@@ -186,9 +186,9 @@ mock_config(Name, Config) ->
{ok, PkgContents} = file:read_file(filename:join(?config(data_dir, Config), PkgFile)),
meck:new(httpc, [passthrough, unsticky]),
meck:expect(httpc, request,
- fun(get, {_Url, _Opts}, _, _) when GoodCache ->
+ fun(get, {_Url, _Opts}, _, _, _) when GoodCache ->
{ok, {{Vsn, 304, <<"Not Modified">>}, [{"etag", ?good_etag}], <<>>}};
- (get, {_Url, _Opts}, _, _) ->
+ (get, {_Url, _Opts}, _, _, _) ->
{ok, {{Vsn, 200, <<"OK">>}, [{"etag", ?good_etag}], PkgContents}}
end),
[{cache_root, CacheRoot},
diff --git a/test/rebar_plugins_SUITE.erl b/test/rebar_plugins_SUITE.erl
new file mode 100644
index 0000000..adfeafe
--- /dev/null
+++ b/test/rebar_plugins_SUITE.erl
@@ -0,0 +1,201 @@
+-module(rebar_plugins_SUITE).
+
+-export([suite/0,
+ init_per_suite/1,
+ end_per_suite/1,
+ init_per_testcase/2,
+ end_per_testcase/2,
+ all/0,
+ compile_plugins/1,
+ compile_global_plugins/1,
+ complex_plugins/1,
+ upgrade/1]).
+
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+-include_lib("kernel/include/file.hrl").
+
+suite() ->
+ [].
+
+init_per_suite(Config) ->
+ Config.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_testcase(_, Config) ->
+ rebar_test_utils:init_rebar_state(Config).
+
+end_per_testcase(_, _Config) ->
+ catch meck:unload().
+
+all() ->
+ [compile_plugins, compile_global_plugins, complex_plugins, upgrade].
+
+%% Tests that compiling a project installs and compiles the plugins of deps
+compile_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 = rebar_test_utils:create_random_name("plugin1_"),
+
+ Plugins = rebar_test_utils:expand_deps(git, [{PluginName, Vsn, []}]),
+ mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Plugins)}]),
+
+ 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)
+ ]}]),
+ {ok, RConf} = file:consult(RConfFile),
+
+ %% Build with deps.
+ rebar_test_utils:run_and_check(
+ Config, RConf, ["compile"],
+ {ok, [{app, Name}, {plugin, PluginName}, {dep, DepName}]}
+ ).
+
+%% Tests that compiling a project installs and compiles the global plugins
+compile_global_plugins(Config) ->
+ AppDir = ?config(apps, Config),
+ GlobalDir = filename:join(AppDir, "global"),
+ GlobalConfigDir = filename:join([GlobalDir, ".config", "rebar3"]),
+ GlobalConfig = filename:join([GlobalDir, ".config", "rebar3", "rebar.config"]),
+
+ meck:new(rebar_dir, [passthrough]),
+ meck:expect(rebar_dir, global_config, fun() -> GlobalConfig end),
+ meck:expect(rebar_dir, global_cache_dir, fun(_) -> GlobalDir end),
+
+ Name = rebar_test_utils:create_random_name("app1_"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ Vsn2 = 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 = rebar_test_utils:create_random_name("plugin1_"),
+
+ mock_git_resource:mock([{deps, [{list_to_atom(PluginName), Vsn},
+ {list_to_atom(PluginName), Vsn2},
+ {{iolist_to_binary(DepName), iolist_to_binary(Vsn)}, []}]}]),
+
+
+ rebar_test_utils:create_config(GlobalConfigDir,
+ [{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), {git, "http://site.com/user/"++DepName++".git", {tag, Vsn}}}
+ ]},
+ {plugins, [
+ {list_to_atom(PluginName), {git, "http://site.com/user/"++PluginName++".git", {tag, Vsn2}}}
+ ]}]),
+ {ok, RConf} = file:consult(RConfFile),
+
+ %% Runs global plugin install
+ rebar3:init_config(),
+
+ %% Build with deps.
+ rebar_test_utils:run_and_check(
+ Config, RConf, ["compile"],
+ {ok, [{app, Name},
+ {global_plugin, PluginName, Vsn},
+ {plugin, PluginName, Vsn2},
+ {dep, DepName}]}
+ ),
+
+ meck:unload(rebar_dir).
+
+%% Tests installing of plugin with transitive deps
+complex_plugins(Config) ->
+ AppDir = ?config(apps, Config),
+
+ meck:new(rebar_dir, [passthrough]),
+
+ Name = rebar_test_utils:create_random_name("app1_"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ Vsn2 = rebar_test_utils:create_random_vsn(),
+ rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),
+
+ DepName = rebar_test_utils:create_random_name("dep1_"),
+ DepName2 = rebar_test_utils:create_random_name("dep2_"),
+ DepName3 = rebar_test_utils:create_random_name("dep3_"),
+ PluginName = rebar_test_utils:create_random_name("plugin1_"),
+
+ Deps = rebar_test_utils:expand_deps(git, [{PluginName, Vsn2, [{DepName2, Vsn,
+ [{DepName3, Vsn, []}]}]}
+ ,{DepName, Vsn, []}]),
+ mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Deps)}]),
+
+ RConfFile =
+ rebar_test_utils:create_config(AppDir,
+ [{deps, [
+ {list_to_atom(DepName), {git, "http://site.com/user/"++DepName++".git", {tag, Vsn}}}
+ ]},
+ {plugins, [
+ {list_to_atom(PluginName), {git, "http://site.com/user/"++PluginName++".git", {tag, Vsn2}}}
+ ]}]),
+ {ok, RConf} = file:consult(RConfFile),
+
+ %% Build with deps.
+ rebar_test_utils:run_and_check(
+ Config, RConf, ["compile"],
+ {ok, [{app, Name},
+ {plugin, PluginName, Vsn2},
+ {plugin, DepName2},
+ {plugin, DepName3},
+ {dep, DepName}]}
+ ),
+
+ meck:unload(rebar_dir).
+
+upgrade(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, [{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">>}]}
+ ).
diff --git a/test/rebar_release_SUITE.erl b/test/rebar_release_SUITE.erl
index 3809106..1ef0771 100644
--- a/test/rebar_release_SUITE.erl
+++ b/test/rebar_release_SUITE.erl
@@ -6,7 +6,8 @@
all() -> [release,
dev_mode_release,
profile_dev_mode_override_release,
- tar].
+ tar,
+ extend_release].
init_per_testcase(Case, Config0) ->
Config = rebar_test_utils:init_rebar_state(Config0),
@@ -90,3 +91,21 @@ tar(Config) ->
["tar"],
{ok, [{release, list_to_atom(Name), Vsn, false}, {tar, Name, Vsn}]}
).
+
+%% Test that the order of release config args is not lost. If it is extend would fail.
+extend_release(Config) ->
+ AppDir = ?config(apps, Config),
+ Name = ?config(name, Config),
+ Vsn = "1.0.0",
+ {ok, RebarConfig} =
+ file:consult(rebar_test_utils:create_config(AppDir,
+ [{relx, [{release, {list_to_atom(Name), Vsn},
+ [list_to_atom(Name)]},
+ {release, {extended, Vsn, {extend, list_to_atom(Name)}},
+ []},
+ {lib_dirs, [AppDir]}]}])),
+ rebar_test_utils:run_and_check(
+ Config, RebarConfig,
+ ["release", "-n", "extended"],
+ {ok, [{release, extended, Vsn, false}]}
+ ).
diff --git a/test/rebar_extra_src_dirs_SUITE.erl b/test/rebar_src_dirs_SUITE.erl
index a00bb2d..1804fbf 100644
--- a/test/rebar_extra_src_dirs_SUITE.erl
+++ b/test/rebar_src_dirs_SUITE.erl
@@ -1,4 +1,4 @@
--module(rebar_extra_src_dirs_SUITE).
+-module(rebar_src_dirs_SUITE).
-export([suite/0,
init_per_suite/1,
@@ -6,9 +6,15 @@
init_per_testcase/2,
end_per_testcase/2,
all/0,
+ src_dirs_at_root/1,
+ extra_src_dirs_at_root/1,
+ src_dirs_in_erl_opts/1,
+ extra_src_dirs_in_erl_opts/1,
+ src_dirs_at_root_and_in_erl_opts/1,
+ extra_src_dirs_at_root_and_in_erl_opts/1,
build_basic_app/1,
build_multi_apps/1,
- src_dir_takes_precedence/1]).
+ src_dir_takes_precedence_over_extra/1]).
-include_lib("common_test/include/ct.hrl").
@@ -27,7 +33,88 @@ init_per_testcase(_, Config) ->
end_per_testcase(_, _Config) -> ok.
all() ->
- [build_basic_app, build_multi_apps, src_dir_takes_precedence].
+ [src_dirs_at_root, extra_src_dirs_at_root,
+ src_dirs_in_erl_opts, extra_src_dirs_in_erl_opts,
+ src_dirs_at_root_and_in_erl_opts, extra_src_dirs_at_root_and_in_erl_opts,
+ build_basic_app, build_multi_apps, src_dir_takes_precedence_over_extra].
+
+src_dirs_at_root(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]),
+
+ RebarConfig = [{src_dirs, ["foo", "bar", "baz"]}],
+
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return),
+
+ ["foo", "bar", "baz"] = rebar_dir:src_dirs(State, []).
+
+extra_src_dirs_at_root(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]),
+
+ RebarConfig = [{extra_src_dirs, ["foo", "bar", "baz"]}],
+
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return),
+
+ ["foo", "bar", "baz"] = rebar_dir:extra_src_dirs(State, []).
+
+src_dirs_in_erl_opts(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]),
+
+ RebarConfig = [{erl_opts, [{src_dirs, ["foo", "bar", "baz"]}]}],
+
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return),
+
+ ["foo", "bar", "baz"] = rebar_dir:src_dirs(State, []).
+
+extra_src_dirs_in_erl_opts(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]),
+
+ RebarConfig = [{erl_opts, [{extra_src_dirs, ["foo", "bar", "baz"]}]}],
+
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return),
+
+ ["foo", "bar", "baz"] = rebar_dir:extra_src_dirs(State, []).
+
+src_dirs_at_root_and_in_erl_opts(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]),
+
+ RebarConfig = [{erl_opts, [{src_dirs, ["foo", "bar"]}]}, {src_dirs, ["baz", "qux"]}],
+
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return),
+
+ ["baz", "qux", "foo", "bar"] = rebar_dir:src_dirs(State, []).
+
+extra_src_dirs_at_root_and_in_erl_opts(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]),
+
+ RebarConfig = [{erl_opts, [{extra_src_dirs, ["foo", "bar"]}]}, {extra_src_dirs, ["baz", "qux"]}],
+
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return),
+
+ ["baz", "qux", "foo", "bar"] = rebar_dir:extra_src_dirs(State, []).
build_basic_app(Config) ->
AppDir = ?config(apps, Config),
@@ -118,7 +205,7 @@ build_multi_apps(Config) ->
Mods2 = proplists:get_value(modules, KVs2),
false = lists:member(extra2, Mods2).
-src_dir_takes_precedence(Config) ->
+src_dir_takes_precedence_over_extra(Config) ->
AppDir = ?config(apps, Config),
Name = rebar_test_utils:create_random_name("app1_"),
diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl
index 2cdc58b..4943d4b 100644
--- a/test/rebar_test_utils.erl
+++ b/test/rebar_test_utils.erl
@@ -4,7 +4,7 @@
-export([init_rebar_state/1, init_rebar_state/2, run_and_check/4]).
-export([expand_deps/2, flat_deps/1, flat_pkgdeps/1, top_level_deps/1]).
-export([create_app/4, create_eunit_app/4, create_empty_app/4, create_config/2]).
--export([create_random_name/1, create_random_vsn/0]).
+-export([create_random_name/1, create_random_vsn/0, write_src_file/2]).
%%%%%%%%%%%%%%
%%% Public %%%
@@ -70,7 +70,8 @@ run_and_check(Config, RebarConfig, Command, Expect) ->
%% - src/<file>.app.src
%% And returns a `rebar_app_info' object.
create_app(AppDir, Name, Vsn, Deps) ->
- write_src_file(AppDir, Name),
+ write_src_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).
@@ -104,16 +105,22 @@ create_config(AppDir, Contents) ->
%% @doc Util to create a random variation of a given name.
create_random_name(Name) ->
- random:seed(os:timestamp()),
+ random_seed(),
Name ++ erlang:integer_to_list(random:uniform(1000000)).
%% @doc Util to create a random variation of a given version.
create_random_vsn() ->
- random:seed(os:timestamp()),
+ random_seed(),
lists:flatten([erlang:integer_to_list(random:uniform(100)),
".", erlang:integer_to_list(random:uniform(100)),
".", erlang:integer_to_list(random:uniform(100))]).
+random_seed() ->
+ <<A:32, B:32, C:32>> = crypto:rand_bytes(12),
+ random:seed({A,B,C}).
+
+
+
expand_deps(_, []) -> [];
expand_deps(git, [{Name, Deps} | Rest]) ->
Dep = {Name, ".*", {git, "https://example.org/user/"++Name++".git", "master"}},
@@ -160,9 +167,10 @@ top_level_deps([{{Name, Vsn, Ref}, _} | Deps]) ->
check_results(AppDir, Expected) ->
BuildDirs = filelib:wildcard(filename:join([AppDir, "_build", "*", "lib"])),
PluginDirs = filelib:wildcard(filename:join([AppDir, "_build", "*", "plugins"])),
+ GlobalPluginDirs = filelib:wildcard(filename:join([AppDir, "global", "plugins"])),
CheckoutsDir = filename:join([AppDir, "_checkouts"]),
LockFile = filename:join([AppDir, "rebar.lock"]),
- Locks = lists:flatten(rebar_config:consult_file(LockFile)),
+ Locks = lists:flatten(rebar_config:consult_lock_file(LockFile)),
InvalidApps = rebar_app_discover:find_apps(BuildDirs, invalid),
ValidApps = rebar_app_discover:find_apps(BuildDirs, valid),
@@ -176,10 +184,12 @@ check_results(AppDir, Expected) ->
CheckoutsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- Checkouts],
Plugins = rebar_app_discover:find_apps(PluginDirs, all),
PluginsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- Plugins],
+ GlobalPlugins = rebar_app_discover:find_apps(GlobalPluginDirs, all),
+ GlobalPluginsNames = [{ec_cnv:to_list(rebar_app_info:name(App)), App} || App <- GlobalPlugins],
lists:foreach(
fun({app, Name}) ->
- ct:pal("Name: ~p", [Name]),
+ ct:pal("App Name: ~p", [Name]),
case lists:keyfind(Name, 1, DepsNames) of
false ->
error({app_not_found, Name});
@@ -187,7 +197,7 @@ check_results(AppDir, Expected) ->
ok
end
; ({app, Name, invalid}) ->
- ct:pal("Name: ~p", [Name]),
+ ct:pal("Invalid Name: ~p", [Name]),
case lists:keyfind(Name, 1, InvalidDepsNames) of
false ->
error({app_not_found, Name});
@@ -195,7 +205,7 @@ check_results(AppDir, Expected) ->
ok
end
; ({app, Name, valid}) ->
- ct:pal("Name: ~p", [Name]),
+ ct:pal("Valid Name: ~p", [Name]),
case lists:keyfind(Name, 1, ValidDepsNames) of
false ->
error({app_not_found, Name});
@@ -203,13 +213,13 @@ check_results(AppDir, Expected) ->
ok
end
; ({checkout, Name}) ->
- ct:pal("Name: ~p", [Name]),
+ ct:pal("Checkout Name: ~p", [Name]),
?assertNotEqual(false, lists:keyfind(Name, 1, CheckoutsNames))
; ({dep, Name}) ->
- ct:pal("Name: ~p", [Name]),
+ ct:pal("Dep Name: ~p", [Name]),
?assertNotEqual(false, lists:keyfind(Name, 1, DepsNames))
; ({dep, Name, Vsn}) ->
- ct:pal("Name: ~p, Vsn: ~p", [Name, Vsn]),
+ ct:pal("Dep Name: ~p, Vsn: ~p", [Name, Vsn]),
case lists:keyfind(Name, 1, DepsNames) of
false ->
error({dep_not_found, Name});
@@ -218,22 +228,34 @@ check_results(AppDir, Expected) ->
iolist_to_binary(rebar_app_info:original_vsn(App)))
end
; ({plugin, Name}) ->
- ct:pal("Name: ~p", [Name]),
+ ct:pal("Plugin Name: ~p", [Name]),
?assertNotEqual(false, lists:keyfind(Name, 1, PluginsNames))
; ({plugin, Name, Vsn}) ->
- ct:pal("Name: ~p, Vsn: ~p", [Name, Vsn]),
+ ct:pal("Plugin Name: ~p, Vsn: ~p", [Name, Vsn]),
case lists:keyfind(Name, 1, PluginsNames) of
false ->
- error({dep_not_found, Name});
+ error({plugin_not_found, Name});
+ {Name, App} ->
+ ?assertEqual(iolist_to_binary(Vsn),
+ iolist_to_binary(rebar_app_info:original_vsn(App)))
+ end
+ ; ({global_plugin, Name}) ->
+ ct:pal("Global Plugin Name: ~p", [Name]),
+ ?assertNotEqual(false, lists:keyfind(Name, 1, GlobalPluginsNames))
+ ; ({global_plugin, Name, Vsn}) ->
+ ct:pal("Global Plugin Name: ~p, Vsn: ~p", [Name, Vsn]),
+ case lists:keyfind(Name, 1, GlobalPluginsNames) of
+ false ->
+ error({global_plugin_not_found, Name});
{Name, App} ->
?assertEqual(iolist_to_binary(Vsn),
iolist_to_binary(rebar_app_info:original_vsn(App)))
end
; ({lock, Name}) ->
- ct:pal("Name: ~p", [Name]),
+ ct:pal("Lock Name: ~p", [Name]),
?assertNotEqual(false, lists:keyfind(iolist_to_binary(Name), 1, Locks))
; ({lock, Name, Vsn}) ->
- ct:pal("Name: ~p, Vsn: ~p", [Name, Vsn]),
+ ct:pal("Lock Name: ~p, Vsn: ~p", [Name, Vsn]),
case lists:keyfind(iolist_to_binary(Name), 1, Locks) of
false ->
error({lock_not_found, Name});
@@ -282,9 +304,9 @@ check_results(AppDir, Expected) ->
end, Expected).
write_src_file(Dir, Name) ->
- Erl = filename:join([Dir, "src", "not_a_real_src_" ++ Name ++ ".erl"]),
+ Erl = filename:join([Dir, "src", Name]),
ok = filelib:ensure_dir(Erl),
- ok = ec_file:write(Erl, erl_src_file("not_a_real_src_" ++ Name ++ ".erl")).
+ ok = ec_file:write(Erl, erl_src_file(Name)).
write_eunitized_src_file(Dir, Name) ->
Erl = filename:join([Dir, "src", "not_a_real_src_" ++ Name ++ ".erl"]),
diff --git a/test/rebar_upgrade_SUITE.erl b/test/rebar_upgrade_SUITE.erl
index 1dc0af2..79cf29e 100644
--- a/test/rebar_upgrade_SUITE.erl
+++ b/test/rebar_upgrade_SUITE.erl
@@ -3,14 +3,14 @@
-include_lib("eunit/include/eunit.hrl").
-compile(export_all).
-all() -> [{group, git}, {group, pkg}].
+all() -> [{group, git}, {group, pkg}, novsn_pkg].
groups() ->
[{all, [], [top_a, top_b, top_c, top_d1, top_d2, top_e,
pair_a, pair_b, pair_ab, pair_c, pair_all,
triplet_a, triplet_b, triplet_c,
tree_a, tree_b, tree_c, tree_c2, tree_ac, tree_all,
- delete_d, promote]},
+ delete_d, promote, stable_lock, fwd_lock]},
{git, [], [{group, all}]},
{pkg, [], [{group, all}]}].
@@ -31,6 +31,26 @@ init_per_group(_, Config) ->
end_per_group(_, Config) ->
Config.
+init_per_testcase(novsn_pkg, Config0) ->
+ Config = rebar_test_utils:init_rebar_state(Config0, "novsn_pkg_"),
+ AppDir = ?config(apps, Config),
+ RebarConf = rebar_test_utils:create_config(AppDir, [{deps, [fakeapp]}]),
+
+ Deps = [{{<<"fakeapp">>, <<"1.0.0">>}, []}],
+ UpDeps = [{{<<"fakeapp">>, <<"1.1.0">>}, []}],
+ Upgrades = ["fakeapp"],
+
+ [{rebarconfig, RebarConf},
+ {mock, fun() ->
+ catch mock_pkg_resource:unmock(),
+ mock_pkg_resource:mock([{pkgdeps, Deps}, {upgrade, []}])
+ end},
+ {mock_update, fun() ->
+ catch mock_pkg_resource:unmock(),
+ mock_pkg_resource:mock([{pkgdeps, UpDeps}, {upgrade, Upgrades}])
+ end},
+ {expected, {ok, [{dep, "fakeapp", "1.1.0"}, {lock, "fakeapp", "1.1.0"}]}}
+ | Config];
init_per_testcase(Case, Config) ->
DepsType = ?config(deps_type, Config),
{Deps, UpDeps, ToUp, Expectations} = upgrades(Case),
@@ -361,7 +381,30 @@ upgrades(promote) ->
{"C", "3", []}
],
["A","B","C","D"],
- {"C", [{"A","1"},{"C","3"},{"B","1"},{"D","1"}]}}.
+ {"C", [{"A","1"},{"C","3"},{"B","1"},{"D","1"}]}};
+upgrades(stable_lock) ->
+ {[{"A", "1", [{"C", "1", []}]},
+ {"B", "1", [{"D", "1", []}]}
+ ], % lock after this
+ [{"A", "2", [{"C", "2", []}]},
+ {"B", "2", [{"D", "2", []}]}
+ ],
+ [],
+ %% Run a regular lock and no app should be upgraded
+ {"any", [{"A","1"},{"C","1"},{"B","1"},{"D","1"}]}};
+upgrades(fwd_lock) ->
+ {[{"A", "1", [{"C", "1", []}]},
+ {"B", "1", [{"D", "1", []}]}
+ ],
+ [{"A", "2", [{"C", "2", []}]},
+ {"B", "2", [{"D", "2", []}]}
+ ],
+ ["A","B","C","D"],
+ %% For this one, we should build, rewrite the lock
+ %% file to include the result post-upgrade, and then
+ %% run a regular lock to see that the lock file is respected
+ %% in deps.
+ {"any", [{"A","2"},{"C","2"},{"B","2"},{"D","2"}]}}.
%% TODO: add a test that verifies that unlocking files and then
%% running the upgrade code is enough to properly upgrade things.
@@ -435,6 +478,46 @@ delete_d(Config) ->
?assertNotEqual([],
[1 || {"App ~ts is no longer needed and can be deleted.",
[<<"D">>]} <- Infos]).
+
+stable_lock(Config) ->
+ apply(?config(mock, Config), []),
+ {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
+ %% Install dependencies before re-mocking for an upgrade
+ rebar_test_utils:run_and_check(Config, RebarConfig, ["lock"], {ok, []}),
+ {App, Unlocks} = ?config(expected, Config),
+ ct:pal("Upgrades: ~p -> ~p", [App, Unlocks]),
+ Expectation = case Unlocks of
+ {error, Term} -> {error, Term};
+ _ -> {ok, Unlocks}
+ end,
+ apply(?config(mock_update, Config), []),
+ NewRebarConf = rebar_test_utils:create_config(?config(apps, Config),
+ [{deps, ?config(next_top_deps, Config)}]),
+ {ok, NewRebarConfig} = file:consult(NewRebarConf),
+ rebar_test_utils:run_and_check(
+ Config, NewRebarConfig, ["lock", App], Expectation
+ ).
+
+fwd_lock(Config) ->
+ apply(?config(mock, Config), []),
+ {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
+ %% Install dependencies before re-mocking for an upgrade
+ rebar_test_utils:run_and_check(Config, RebarConfig, ["lock"], {ok, []}),
+ {App, Unlocks} = ?config(expected, Config),
+ ct:pal("Upgrades: ~p -> ~p", [App, Unlocks]),
+ Expectation = case Unlocks of
+ {error, Term} -> {error, Term};
+ _ -> {ok, Unlocks}
+ end,
+ rewrite_locks(Expectation, Config),
+ apply(?config(mock_update, Config), []),
+ NewRebarConf = rebar_test_utils:create_config(?config(apps, Config),
+ [{deps, ?config(next_top_deps, Config)}]),
+ {ok, NewRebarConfig} = file:consult(NewRebarConf),
+ rebar_test_utils:run_and_check(
+ Config, NewRebarConfig, ["lock", App], Expectation
+ ).
+
run(Config) ->
apply(?config(mock, Config), []),
{ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
@@ -453,3 +536,32 @@ run(Config) ->
rebar_test_utils:run_and_check(
Config, NewRebarConfig, ["upgrade", App], Expectation
).
+
+novsn_pkg(Config) ->
+ apply(?config(mock, Config), []),
+ {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
+ %% Install dependencies before re-mocking for an upgrade
+ rebar_test_utils:run_and_check(Config, RebarConfig, ["lock"], {ok, []}),
+ Expectation = ?config(expected, Config),
+ apply(?config(mock_update, Config), []),
+ rebar_test_utils:run_and_check(
+ Config, RebarConfig, ["upgrade"], Expectation
+ ),
+ ok.
+
+rewrite_locks({ok, Expectations}, Config) ->
+ AppDir = ?config(apps, Config),
+ LockFile = filename:join([AppDir, "rebar.lock"]),
+ {ok, [Locks]} = file:consult(LockFile),
+ ExpLocks = [{list_to_binary(Name), Vsn}
+ || {lock, Name, Vsn} <- Expectations],
+ NewLocks = lists:foldl(
+ fun({App, {pkg, Name, _}, Lvl}, Acc) ->
+ Vsn = list_to_binary(proplists:get_value(App,ExpLocks)),
+ [{App, {pkg, Name, Vsn}, Lvl} | Acc]
+ ; ({App, {git, URL, {ref, _}}, Lvl}, Acc) ->
+ Vsn = proplists:get_value(App,ExpLocks),
+ [{App, {git, URL, {ref, Vsn}}, Lvl} | Acc]
+ end, [], Locks),
+ ct:pal("rewriting locks from ~p to~n~p", [Locks, NewLocks]),
+ file:write_file(LockFile, io_lib:format("~p.~n", [NewLocks])).
diff --git a/test/rebar_utils_SUITE.erl b/test/rebar_utils_SUITE.erl
index e9b32e2..f04ab63 100644
--- a/test/rebar_utils_SUITE.erl
+++ b/test/rebar_utils_SUITE.erl
@@ -21,7 +21,8 @@
task_with_flag_with_trailing_comma/1,
task_with_flag_with_commas/1,
task_with_multiple_flags/1,
- special_task_do/1]).
+ special_task_do/1,
+ sh_does_not_miss_messages/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -29,7 +30,8 @@
all() ->
- [{group, args_to_tasks}].
+ [{group, args_to_tasks},
+ sh_does_not_miss_messages].
groups() ->
[{args_to_tasks, [], [empty_arglist,
@@ -118,3 +120,14 @@ special_task_do(_Config) ->
"do",
"bar,",
"baz"]).
+sh_does_not_miss_messages(_Config) ->
+ Source = "~nmain(_) ->~n io:format(\"donotmissme\").~n",
+ file:write_file("do_not_miss_messages", io_lib:format(Source,[])),
+ {ok, "donotmissme"} = rebar_utils:sh("escript do_not_miss_messages", []),
+ AnyMessageRemained =
+ receive
+ What -> What
+ after 100 ->
+ false
+ end,
+ AnyMessageRemained = false.