summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/ISSUE_TEMPLATE.md31
-rw-r--r--.travis.yml28
-rw-r--r--README.md2
-rwxr-xr-xbootstrap8
-rwxr-xr-xpr2relnotes.py188
-rwxr-xr-xpr2relnotes.sh25
-rw-r--r--priv/templates/escript_rebar.config2
-rw-r--r--priv/templates/gitignore1
-rw-r--r--rebar.config13
-rw-r--r--rebar.config.sample3
-rw-r--r--rebar.lock20
-rw-r--r--src/rebar.app.src4
-rw-r--r--src/rebar.hrl2
-rw-r--r--src/rebar3.erl14
-rw-r--r--src/rebar_app_utils.erl32
-rw-r--r--src/rebar_base_compiler.erl51
-rw-r--r--src/rebar_config.erl112
-rw-r--r--src/rebar_erlc_compiler.erl33
-rw-r--r--src/rebar_fetch.erl6
-rw-r--r--src/rebar_file_utils.erl39
-rw-r--r--src/rebar_git_resource.erl13
-rw-r--r--src/rebar_log.erl12
-rw-r--r--src/rebar_packages.erl2
-rw-r--r--src/rebar_pkg_resource.erl23
-rw-r--r--src/rebar_prv_common_test.erl25
-rw-r--r--src/rebar_prv_cover.erl5
-rw-r--r--src/rebar_prv_deps.erl2
-rw-r--r--src/rebar_prv_deps_tree.erl2
-rw-r--r--src/rebar_prv_dialyzer.erl45
-rw-r--r--src/rebar_prv_edoc.erl35
-rw-r--r--src/rebar_prv_escriptize.erl43
-rw-r--r--src/rebar_prv_help.erl5
-rw-r--r--src/rebar_prv_install_deps.erl6
-rw-r--r--src/rebar_prv_path.erl8
-rw-r--r--src/rebar_prv_shell.erl31
-rw-r--r--src/rebar_prv_update.erl12
-rw-r--r--src/rebar_prv_upgrade.erl24
-rw-r--r--src/rebar_state.erl3
-rw-r--r--src/rebar_templater.erl2
-rw-r--r--src/rebar_utils.erl20
-rw-r--r--test/mock_pkg_resource.erl11
-rw-r--r--test/rebar_compile_SUITE.erl95
-rw-r--r--test/rebar_ct_SUITE.erl50
-rw-r--r--test/rebar_deps_SUITE.erl75
-rw-r--r--test/rebar_dialyzer_SUITE.erl79
-rw-r--r--test/rebar_edoc_SUITE.erl52
-rw-r--r--test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.app.src16
-rw-r--r--test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.erl9
-rw-r--r--test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_app.erl26
-rw-r--r--test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_sup.erl35
-rw-r--r--test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.app.src16
-rw-r--r--test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.erl9
-rw-r--r--test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_app.erl26
-rw-r--r--test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_sup.erl35
-rw-r--r--test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.app.src17
-rw-r--r--test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.erl19
-rw-r--r--test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_app.erl26
-rw-r--r--test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_sup.erl35
-rw-r--r--test/rebar_escriptize_SUITE.erl26
-rw-r--r--test/rebar_file_utils_SUITE.erl28
-rw-r--r--test/rebar_install_deps_SUITE.erl2
-rw-r--r--test/rebar_lock_SUITE.erl78
-rw-r--r--test/rebar_pkg_SUITE.erl52
-rw-r--r--test/rebar_pkg_alias_SUITE.erl6
-rw-r--r--test/rebar_test_utils.erl47
-rw-r--r--test/rebar_unlock_SUITE.erl43
-rw-r--r--test/rebar_unlock_SUITE_data/pkg.rebar.lock24
-rw-r--r--test/rebar_upgrade_SUITE.erl61
68 files changed, 1516 insertions, 414 deletions
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
new file mode 100644
index 0000000..6d5a0c2
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE.md
@@ -0,0 +1,31 @@
+### Pre-Check ###
+
+- If you are filing for a bug, please do a quick search in current issues first
+- For bugs, mention if you are willing or interested in helping fix the issue
+- For questions or support, it helps to include context around your project or problem
+- Think of a descriptive title (more descriptive than 'feature X is broken' unless it is fully broken)
+
+### Environment ###
+
+- Add the result of `rebar3 report` to your message:
+
+```
+$ rebar3 report "my failing command"
+...
+```
+
+- Verify whether the version of rebar3 you're running is the latest release (see https://github.com/erlang/rebar3/releases)
+- If possible, include information about your project and its structure. Open source projects or examples are always easier to debug.
+ If you can provide an example code base to reproduce the issue on, we will generally be able to provide more help, and faster.
+
+### Current behaviour ###
+
+Describe the current behaviour. In case of a failure, crash, or exception, please include the result of running the command with debug information:
+
+```
+DEBUG=1 rebar3 <my failing command>
+```
+
+### Expected behaviour ###
+
+Describe what you expected to happen.
diff --git a/.travis.yml b/.travis.yml
index ab2d28d..dbb4f26 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,27 +1,25 @@
sudo: false
language: erlang
-install: "true"
+install: 'true'
otp_release:
- - 18.0
- - 17.5
- - R16B03-1
- - R15B03
+- 19.0
+- 18.0
+- 17.5
+- R16B03-1
+- R15B03
before_script: "./bootstrap"
script: "./rebar3 ct"
branches:
only:
- - master
+ - master
cache:
directories:
- - $HOME/.cache/rebar3/hex/default
-before_deploy: "rm -rf !(rebar3)"
+ - "$HOME/.cache/rebar3/hex/default"
deploy:
+ provider: releases
+ api_key:
+ secure: MjloYuaQF3cd3Oab57zqwPDLPqt5MDgBIrRLpXOQwNovr2tnkKd4aJK3QJ3pTxvZievjgl+qIYI1IZyjuRV37nkjAfMw14iig959wi0k8XTJoMdylVxE5X7hk4SiWhX/ycnJx3C28PPw1OitGTF76HAJDMgEelNdoNt+hvjvDEo=
+ file: rebar3
on:
+ repo: erlang/rebar3
tags: true
- condition: $TRAVIS_OTP_RELEASE = R16B03-1
- provider: s3
- access_key_id: AKIAJAPYAQEFYCYSNL7Q
- secret_access_key:
- secure: "BUv2KQABv0Q4e8DAVNBRTc/lXHWt27yCN46Fdgo1IrcSSIiP+hq2yXzQcXLbPwkEu6pxUZQtL3mvKbt6l7uw3wFrcRfFAi1PGTITAW8MTmxtwcZIBcHSk3XOzDbkK+fYYcaddszmt7hDzzEFPtmYXiNgnaMIVeynhQLgcCcIRRQ="
- bucket: "rebar3"
- skip_cleanup: true
diff --git a/README.md b/README.md
index 7560ab0..f44c7bd 100644
--- a/README.md
+++ b/README.md
@@ -53,7 +53,7 @@ best experience you can get.
## Getting Started
-A [getting started guide is maintained on the offcial documentation website](http://www.rebar3.org/docs/getting-started),
+A [getting started guide is maintained on the official documentation website](http://www.rebar3.org/docs/getting-started),
but installing rebar3 can be done by any of the ways described below
Nightly compiled version:
diff --git a/bootstrap b/bootstrap
index f52779e..e48aff4 100755
--- a/bootstrap
+++ b/bootstrap
@@ -24,7 +24,7 @@ main(_) ->
bootstrap_rebar3(),
%% Build rebar.app from rebar.app.src
- {ok, App} = rebar_app_info:new(rebar, "3.1.1", filename:absname("_build/default/lib/rebar/")),
+ {ok, App} = rebar_app_info:new(rebar, "3.3.0", filename:absname("_build/default/lib/rebar/")),
rebar_otp_app:compile(rebar_state:new(), App),
%% Because we are compiling files that are loaded already we want to silence
@@ -133,7 +133,11 @@ get_rebar_config() ->
end.
get_http_vars(Scheme) ->
- proplists:get_value(Scheme, get_rebar_config(), []).
+ OS = case os:getenv(atom_to_list(Scheme)) of
+ Str when is_list(Str) -> Str;
+ _ -> []
+ end,
+ proplists:get_value(Scheme, get_rebar_config(), OS).
set_httpc_options() ->
set_httpc_options(https_proxy, get_http_vars(https_proxy)),
diff --git a/pr2relnotes.py b/pr2relnotes.py
deleted file mode 100755
index 7a23fda..0000000
--- a/pr2relnotes.py
+++ /dev/null
@@ -1,188 +0,0 @@
-#!/usr/bin/env python
-
-## Install info
-## $ virtualenv env
-## $ source env/bin/activate
-## $ pip install PyGithub
-##
-## Examples:
-## Find the differences from last tag to current
-## $ pr2relnotes.py alpha-6 HEAD
-
-import argparse
-import re
-import os
-import subprocess
-from github import Github
-from github import GithubException
-
-
-def dprint(*args):
- if VERBOSE:
- print str(args)
-
-def get_args():
- """
- Get command line arguments
- """
- parser = argparse.ArgumentParser(description="Find the PR's between two versions")
- parser.add_argument("old", help = "old version to use")
- parser.add_argument("new", help = "new version to use")
- parser.add_argument("-v", "--verbose", help="Enable debug output",
- default=False,
- action="store_true")
- parser.add_argument("-f", "--file",
- help="Output file to store results (default: tagdiff.md)",
- default="tagdiff.md")
- return parser.parse_args()
-
-def search_prs(log):
- """
- Search lines of text for PR numbers
- """
- # Find all matches using regex iterator, using the PR # as the group match
- resultlist = [str(m.group(1)) for m in re.finditer(r"erge pull request #(\d+)", log)]
- return sorted(resultlist)
-
-def get_env(env):
- return os.environ[env]
-
-def get_formatted_issue(repo, issue, title, url):
- """
- Single place to adjust formatting output of PR data
- """
- # Newline support writelines() call which doesn't add newlines
- # on its own
- return("* {}/{}: [{}]({})\n".format(repo, issue, title.encode('utf-8'), url))
-
-def gh_get_issue_output(org, repo, issuenum):
- """
- Look up PR information using the GitHub api
- """
- # Attempt to look up the PR, and don't take down the whole
- # shebang if a API call fails
- # This will fail often on forks who don't have the
- # PRs numbers associated with the forked account
- # Return empty string on error
- try:
- repoObj = gh.get_repo(org + "/" + repo)
- issue = repoObj.get_issue(int(issuenum))
- title = issue.title
- html_url = issue.html_url
- except GithubException as e:
- print "Github error({0}): {1}".format(e.status, e.data)
- return ""
- except:
- print "Some github error"
- return ""
-
- return(get_formatted_issue(repo, issuenum, title, html_url))
-
-
-def get_org(repourl):
- """
- Simple function to parse the organization out of a GitHub URL
- """
- dprint("Current repourl to search: " + repourl)
- # GitHub URLs can be:
- # http[s]://www.github.com/org/repo
- # or git@github.com:/org/repo
- pattern = re.compile(r"github.com[/:]+(\w+)/")
- m = re.search(pattern, repourl)
- # Fail fast if this is wrong so we can add a pattern to the search
- if m:
- return m.group(1)
- else:
- raise Exception("Incorrect regex pattern finding repo org")
-
-def get_name(repourl):
- """
- Simple function to parse the repository name out of a GitHub URL
- """
- dprint("Current repourl to search: " + repourl)
- repo_pattern = re.compile(r"github.com[/:]\w+/(\w+)")
- m = re.search(repo_pattern, repourl)
- if m:
- return m.group(1)
- else:
- raise Exception("Incorrect rexex pattern finding repo url")
-
-def get_repo_url_from_remote():
- """
- Function that gets the repository URL from the `git remote` listing
- """
- git_remote_bytes = subprocess.check_output(["git", "remote", "-v"])
- # check_output returns the command results in raw byte format
- remote_string = git_remote_bytes.decode('utf-8')
-
- pattern = re.compile(r"github.com[/:]\w+/\w+")
- m = re.search(pattern, remote_string)
- if m:
- return m.group(0)
- else:
- raise Exception("Incorrect rexex pattern finding repo url")
-
-def process_log(gitlog, repo_url):
- """
- Handles the processing of the gitlog and returns a list
- of PRs already formatted for output
- """
- pr_list = search_prs(gitlog)
- repoorg = get_org(repo_url)
- reponame = get_name(repo_url)
- pr_buffer = []
- for issue in pr_list:
- pr_buffer.append(gh_get_issue_output(repoorg, reponame, issue))
-
- return pr_buffer
-
-def fetch_log(old_ver, new_ver):
- """
- Function that processes the git log between the old and new versions
- """
- dprint("Current working directory", os.getcwd())
- gitlogbytes = subprocess.check_output(["git", "log",
- str(old_ver + ".." + new_ver)])
- return gitlogbytes.decode('utf-8')
-
-
-def compare_versions(repo_url, old_ver, new_ver):
- # Formatted list of all PRs for all repos
- pr_out = []
- gitlog = fetch_log(old_ver, new_ver)
- pr_out.extend(process_log(gitlog, repo_url))
- return pr_out
-
-def main():
- args = get_args()
-
- # Setup the GitHub object for later use
- global gh
- gh = Github(get_env("GHAUTH"))
-
- if gh == "":
- raise Exception("Env var GHAUTH must be set to a valid GitHub API key")
-
- if args.verbose:
- global VERBOSE
- VERBOSE=True
-
- dprint("Inspecting difference in between: ", args.old, " and ", args.new)
-
- # Find the github URL of the repo we are operating on
- repo_url = get_repo_url_from_remote()
-
- # Compare old and new versions
- pr_list = compare_versions(repo_url, args.old, args.new)
-
- # Writeout PR listing
- print "Writing output to file %s" % args.file
- with open(args.file, 'w') as output:
- output.writelines(pr_list)
-
-
-if __name__ == "__main__":
- VERBOSE=False
- gh=None
- topdir=os.getcwd()
- main()
diff --git a/pr2relnotes.sh b/pr2relnotes.sh
new file mode 100755
index 0000000..28712bb
--- /dev/null
+++ b/pr2relnotes.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env sh
+
+if [ -z $1 ]
+then
+ echo "usage: $0 <tag> [pull-request-url]"
+ exit 0
+fi
+export url=${2:-"https://github.com/erlang/rebar3/pull/"}
+
+git log --merges --pretty=medium $1..HEAD | \
+awk -v url=$url '
+ # first line of a merge commit entry
+ /^commit / {mode="new"}
+
+ # merge commit default message
+ / +Merge pull request/ {
+ page_id=substr($4, 2, length($4)-1);
+ mode="started";
+ next;
+ }
+
+ # line of content including title
+ mode=="started" && / [^ ]+/ {
+ print "- [" substr($0, 5) "](" url page_id ")"; mode="done"
+ }'
diff --git a/priv/templates/escript_rebar.config b/priv/templates/escript_rebar.config
index 196f835..ef498a8 100644
--- a/priv/templates/escript_rebar.config
+++ b/priv/templates/escript_rebar.config
@@ -3,7 +3,7 @@
{escript_incl_apps,
[{{name}}]}.
-{escript_top_level_app, {{name}}}.
+{escript_main_app, {{name}}}.
{escript_name, {{name}}}.
{escript_emu_args, "%%! +sbtu +A0\n"}.
diff --git a/priv/templates/gitignore b/priv/templates/gitignore
index ced0c5e..121a4de 100644
--- a/priv/templates/gitignore
+++ b/priv/templates/gitignore
@@ -13,3 +13,4 @@ erl_crash.dump
.rebar
logs
_build
+.idea
diff --git a/rebar.config b/rebar.config
index ea5af27..eabf3ac 100644
--- a/rebar.config
+++ b/rebar.config
@@ -2,16 +2,20 @@
%% ex: ts=4 sw=4 ft=erlang et
{deps, [{erlware_commons, "0.21.0"},
- {ssl_verify_hostname, "1.0.5"},
+ {ssl_verify_fun, "1.1.1"},
{certifi, "0.4.0"},
{providers, "1.6.0"},
{getopt, "0.8.2"},
{bbmustache, "1.0.4"},
- {relx, "3.19.0"},
+ {relx, "3.21.0"},
{cf, "0.2.1"},
- {cth_readable, "1.2.2"},
+ {cth_readable, "1.2.3"},
{eunit_formatters, "0.3.1"}]}.
+{post_hooks, [{"(linux|darwin|solaris|freebsd|netbsd|openbsd)",
+ escriptize,
+ "cp $REBAR_BUILD_DIR/bin/rebar3 ./rebar3 && chmod u+x rebar3"}]}.
+
{escript_name, rebar3}.
{escript_emu_args, "%%! +sbtu +A0\n"}.
%% escript_incl_extra is for internal rebar-private use only.
@@ -20,6 +24,7 @@
{"rebar/priv/templates/*", "_build/default/lib/"}]}.
{erl_opts, [{platform_define, "^[0-9]+", namespaced_types},
+ {platform_define, "^(19|2)", rand_only},
no_debug_info,
warnings_as_errors]}.
@@ -36,7 +41,7 @@
{bootstrap, []},
{dialyze, [{overrides, [{add, erlware_commons, [{erl_opts, [debug_info]}]},
- {add, ssl_verify_hostname, [{erl_opts, [debug_info]}]},
+ {add, ssl_verify_fun, [{erl_opts, [debug_info]}]},
{add, certifi, [{erl_opts, [debug_info]}]},
{add, providers, [{erl_opts, [debug_info]}]},
{add, getopt, [{erl_opts, [debug_info]}]},
diff --git a/rebar.config.sample b/rebar.config.sample
index cc027f9..be3da71 100644
--- a/rebar.config.sample
+++ b/rebar.config.sample
@@ -131,7 +131,8 @@
%% Paths to miscellaneous Erlang files to compile for an app
%% without including them in its modules list
{extra_src_dirs, []}.
-
+%% Path where custom rebar3 templates could be found
+{template_dir, []}.
%% == EDoc ==
diff --git a/rebar.lock b/rebar.lock
index 40463ac..23fb552 100644
--- a/rebar.lock
+++ b/rebar.lock
@@ -1,10 +1,24 @@
+{"1.1.0",
[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0},
{<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0},
{<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0},
- {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.2">>},0},
+ {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.3">>},0},
{<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.21.0">>},0},
{<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0},
{<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0},
{<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0},
- {<<"relx">>,{pkg,<<"relx">>,<<"3.19.0">>},0},
- {<<"ssl_verify_hostname">>,{pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>},0}].
+ {<<"relx">>,{pkg,<<"relx">>,<<"3.21.0">>},0},
+ {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.1">>},0}]}.
+[
+{pkg_hash,[
+ {<<"bbmustache">>, <<"7BA94F971C5AFD7B6617918A4BB74705E36CAB36EB84B19B6A1B7EE06427AA38">>},
+ {<<"certifi">>, <<"A7966EFB868B179023618D29A407548F70C52466BF1849B9E8EBD0E34B7EA11F">>},
+ {<<"cf">>, <<"69D0B1349FD4D7D4DC55B7F407D29D7A840BF9A1EF5AF529F1EBE0CE153FC2AB">>},
+ {<<"cth_readable">>, <<"293120673DFF82F0768612C5282E35C40CACC1B6F94FE99077438FD3749D0E27">>},
+ {<<"erlware_commons">>, <<"A04433071AD7D112EDEFC75AC77719DD3E6753E697AC09428FC83D7564B80B15">>},
+ {<<"eunit_formatters">>, <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>},
+ {<<"getopt">>, <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>},
+ {<<"providers">>, <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>},
+ {<<"relx">>, <<"91E1EA9F09B4EDFDA8461901F4B5C5E0226E43EC161E147EEAB29F7761DF6EB5">>},
+ {<<"ssl_verify_fun">>, <<"28A4D65B7F59893BC2C7DE786DEC1E1555BD742D336043FE644AE956C3497FBE">>}]}
+].
diff --git a/src/rebar.app.src b/src/rebar.app.src
index 5934f38..5b735cf 100644
--- a/src/rebar.app.src
+++ b/src/rebar.app.src
@@ -3,7 +3,7 @@
{application, rebar,
[{description, "Rebar: Erlang Build Tool"},
- {vsn, "3.1.1"},
+ {vsn, "git"},
{modules, []},
{registered, []},
{applications, [kernel,
@@ -23,7 +23,7 @@
erlware_commons,
providers,
bbmustache,
- ssl_verify_hostname,
+ ssl_verify_fun,
certifi,
cth_readable,
relx,
diff --git a/src/rebar.hrl b/src/rebar.hrl
index 0b7f0b1..f96ed5e 100644
--- a/src/rebar.hrl
+++ b/src/rebar.hrl
@@ -10,6 +10,7 @@
-define(INFO(Str, Args), rebar_log:log(info, Str, Args)).
-define(WARN(Str, Args), rebar_log:log(warn, Str, Args)).
-define(ERROR(Str, Args), rebar_log:log(error, Str, Args)).
+-define(CRASHDUMP(Str, Args), rebar_log:crashdump(Str, Args)).
-define(FMT(Str, Args), lists:flatten(io_lib:format(Str, Args))).
@@ -22,6 +23,7 @@
-define(DEFAULT_TEST_DEPS_DIR, "test/lib").
-define(DEFAULT_RELEASE_DIR, "rel").
-define(DEFAULT_CONFIG_FILE, "rebar.config").
+-define(CONFIG_VERSION, "1.1.0").
-define(DEFAULT_CDN, "https://repo.hex.pm/").
-define(REMOTE_PACKAGE_DIR, "tarballs").
-define(REMOTE_REGISTRY_FILE, "registry.ets.gz").
diff --git a/src/rebar3.erl b/src/rebar3.erl
index ff0ab6a..d3ea15f 100644
--- a/src/rebar3.erl
+++ b/src/rebar3.erl
@@ -221,10 +221,10 @@ set_options(State, {Options, NonOptArgs}) ->
%%
log_level() ->
case os:getenv("QUIET") of
- false ->
+ Q when Q == false; Q == "" ->
DefaultLevel = rebar_log:default_level(),
case os:getenv("DEBUG") of
- false ->
+ D when D == false; D == "" ->
DefaultLevel;
_ ->
DefaultLevel + 3
@@ -272,20 +272,22 @@ handle_error({error, rebar_abort}) ->
handle_error({error, {Module, Reason}}) ->
case code:which(Module) of
non_existing ->
- ?ERROR("Uncaught error in rebar_core. Run with DEBUG=1 to see stacktrace", []),
+ ?CRASHDUMP("~p: ~p~n~p~n~n", [Module, Reason, erlang:get_stacktrace()]),
+ ?ERROR("Uncaught error in rebar_core. Run with DEBUG=1 to stacktrace or consult rebar3.crashdump", []),
?DEBUG("Uncaught error: ~p ~p", [Module, Reason]),
?INFO("When submitting a bug report, please include the output of `rebar3 report \"your command\"`", []);
_ ->
- ?ERROR(Module:format_error(Reason), [])
+ ?ERROR("~s", [Module:format_error(Reason)])
end,
erlang:halt(1);
handle_error({error, Error}) when is_list(Error) ->
- ?ERROR(Error, []),
+ ?ERROR("~s", [Error]),
erlang:halt(1);
handle_error(Error) ->
%% Nothing should percolate up from rebar_core;
%% Dump this error to console
- ?ERROR("Uncaught error in rebar_core. Run with DEBUG=1 to see stacktrace", []),
+ ?CRASHDUMP("Error: ~p~n~p~n~n", [Error, erlang:get_stacktrace()]),
+ ?ERROR("Uncaught error in rebar_core. Run with DEBUG=1 to see stacktrace or consult rebar3.crashdump", []),
?DEBUG("Uncaught error: ~p", [Error]),
case erlang:get_stacktrace() of
[] -> ok;
diff --git a/src/rebar_app_utils.erl b/src/rebar_app_utils.erl
index d3ef841..50c6314 100644
--- a/src/rebar_app_utils.erl
+++ b/src/rebar_app_utils.erl
@@ -119,26 +119,33 @@ parse_dep(Dep, Parent, DepsDir, State, Locks, Level) ->
parse_dep(Parent, {Name, Vsn, {pkg, PkgName}}, DepsDir, IsLock, State) ->
{PkgName1, PkgVsn} = {ec_cnv:to_binary(PkgName), ec_cnv:to_binary(Vsn)},
- dep_to_app(Parent, DepsDir, Name, PkgVsn, {pkg, PkgName1, PkgVsn}, IsLock, State);
+ dep_to_app(Parent, DepsDir, Name, PkgVsn, {pkg, PkgName1, PkgVsn, undefined}, IsLock, State);
parse_dep(Parent, {Name, {pkg, PkgName}}, DepsDir, IsLock, State) ->
%% Package dependency with different package name from app name
- dep_to_app(Parent, DepsDir, Name, undefined, {pkg, ec_cnv:to_binary(PkgName), undefined}, IsLock, State);
+ dep_to_app(Parent, DepsDir, Name, undefined, {pkg, ec_cnv:to_binary(PkgName), undefined, undefined}, IsLock, State);
parse_dep(Parent, {Name, Vsn}, DepsDir, IsLock, State) when is_list(Vsn); is_binary(Vsn) ->
%% Versioned Package dependency
{PkgName, PkgVsn} = {ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)},
- dep_to_app(Parent, DepsDir, PkgName, PkgVsn, {pkg, PkgName, PkgVsn}, IsLock, State);
+ dep_to_app(Parent, DepsDir, PkgName, PkgVsn, {pkg, PkgName, PkgVsn, undefined}, IsLock, State);
parse_dep(Parent, Name, DepsDir, IsLock, State) when is_atom(Name); is_binary(Name) ->
%% Unversioned package dependency
- dep_to_app(Parent, DepsDir, ec_cnv:to_binary(Name), undefined, {pkg, ec_cnv:to_binary(Name), undefined}, IsLock, State);
+ dep_to_app(Parent, DepsDir, ec_cnv:to_binary(Name), undefined, {pkg, ec_cnv:to_binary(Name), undefined, undefined}, IsLock, State);
parse_dep(Parent, {Name, Source}, DepsDir, IsLock, State) when is_tuple(Source) ->
dep_to_app(Parent, DepsDir, Name, [], Source, IsLock, State);
parse_dep(Parent, {Name, _Vsn, Source}, DepsDir, IsLock, State) when is_tuple(Source) ->
dep_to_app(Parent, DepsDir, Name, [], Source, IsLock, State);
-parse_dep(Parent, {Name, _Vsn, Source, Opts}, DepsDir, IsLock, State) when is_tuple(Source) ->
+parse_dep(Parent, {Name, _Vsn, Source, Opts}, DepsDir, IsLock, State) when is_tuple(Source),
+ is_list(Opts) ->
+ ?WARN("Dependency option list ~p in ~p is not supported and will be ignored", [Opts, Name]),
+ dep_to_app(Parent, DepsDir, Name, [], Source, IsLock, State);
+parse_dep(Parent, {Name, Source, Opts}, DepsDir, IsLock, State) when is_tuple(Source),
+ is_list(Opts) ->
?WARN("Dependency option list ~p in ~p is not supported and will be ignored", [Opts, Name]),
dep_to_app(Parent, DepsDir, Name, [], Source, IsLock, State);
parse_dep(Parent, {Name, {pkg, PkgName, Vsn}, Level}, DepsDir, IsLock, State) when is_integer(Level) ->
- dep_to_app(Parent, DepsDir, Name, Vsn, {pkg, PkgName, Vsn}, IsLock, State);
+ dep_to_app(Parent, DepsDir, Name, Vsn, {pkg, PkgName, Vsn, undefined}, IsLock, State);
+parse_dep(Parent, {Name, {pkg, PkgName, Vsn, Hash}, Level}, DepsDir, IsLock, State) when is_integer(Level) ->
+ dep_to_app(Parent, DepsDir, Name, Vsn, {pkg, PkgName, Vsn, Hash}, IsLock, State);
parse_dep(Parent, {Name, Source, Level}, DepsDir, IsLock, State) when is_tuple(Source)
, is_integer(Level) ->
dep_to_app(Parent, DepsDir, Name, [], Source, IsLock, State);
@@ -170,17 +177,24 @@ dep_to_app(Parent, DepsDir, Name, Vsn, Source, IsLock, State) ->
AppInfo5 = rebar_app_info:profiles(AppInfo4, [default]),
rebar_app_info:is_lock(AppInfo5, IsLock).
-update_source(AppInfo, {pkg, PkgName, PkgVsn}, State) ->
+update_source(AppInfo, {pkg, PkgName, PkgVsn, Hash}, State) ->
{PkgName1, PkgVsn1} = case PkgVsn of
undefined ->
get_package(PkgName, "0", State);
<<"~>", Vsn/binary>> ->
- [Vsn1] = binary:split(Vsn, [<<" ">>], [trim_all, global]),
+ [Vsn1] = [X || X <- binary:split(Vsn, [<<" ">>], [global]), X =/= <<>>],
get_package(PkgName, Vsn1, State);
_ ->
{PkgName, PkgVsn}
end,
- AppInfo1 = rebar_app_info:source(AppInfo, {pkg, PkgName1, PkgVsn1}),
+ %% store the expected hash for the dependency
+ Hash1 = case Hash of
+ undefined -> % unknown, define the hash since we know the dep
+ rebar_packages:registry_checksum({pkg, PkgName1, PkgVsn1, Hash}, State);
+ _ -> % keep as is
+ Hash
+ end,
+ AppInfo1 = rebar_app_info:source(AppInfo, {pkg, PkgName1, PkgVsn1, Hash1}),
Deps = rebar_packages:deps(PkgName1
,PkgVsn1
,State),
diff --git a/src/rebar_base_compiler.erl b/src/rebar_base_compiler.erl
index 31292af..5d54057 100644
--- a/src/rebar_base_compiler.erl
+++ b/src/rebar_base_compiler.erl
@@ -32,7 +32,11 @@
run/7,
run/8,
ok_tuple/2,
- error_tuple/4]).
+ error_tuple/4,
+ format_error_source/2]).
+
+-define(DEFAULT_COMPILER_SOURCE_FORMAT, relative).
+
%% ===================================================================
%% Public API
@@ -76,6 +80,28 @@ error_tuple(Source, Es, Ws, Opts) ->
{error, format_errors(Source, Es),
format_warnings(Source, Ws, Opts)}.
+format_error_source(Path, Opts) ->
+ Type = case rebar_opts:get(Opts, compiler_source_format,
+ ?DEFAULT_COMPILER_SOURCE_FORMAT) of
+ V when V == absolute; V == relative; V == build ->
+ V;
+ Other ->
+ ?WARN("Invalid argument ~p for compiler_source_format - "
+ "assuming ~s~n", [Other, ?DEFAULT_COMPILER_SOURCE_FORMAT]),
+ ?DEFAULT_COMPILER_SOURCE_FORMAT
+ end,
+ case Type of
+ absolute -> resolve_linked_source(Path);
+ build -> Path;
+ relative ->
+ Cwd = rebar_dir:get_cwd(),
+ rebar_dir:make_relative_path(resolve_linked_source(Path), Cwd)
+ end.
+
+resolve_linked_source(Src) ->
+ {Dir, Base} = rebar_file_utils:split_dirname(Src),
+ filename:join(rebar_file_utils:resolve_link(Dir), Base).
+
%% ===================================================================
%% Internal functions
%% ===================================================================
@@ -114,7 +140,8 @@ compile_each([Source | Rest], Config, CompileFn) ->
skipped ->
?DEBUG("~sSkipped ~s", [rebar_utils:indent(1), filename:basename(Source)]);
Error ->
- ?ERROR("Compiling ~s failed", [Source]),
+ NewSource = format_error_source(Source, Config),
+ ?ERROR("Compiling ~s failed", [NewSource]),
maybe_report(Error),
?DEBUG("Compilation failed: ~p", [Error]),
?FAIL
@@ -128,7 +155,13 @@ format_warnings(Source, Warnings) ->
format_warnings(Source, Warnings, []).
format_warnings(Source, Warnings, Opts) ->
- Prefix = case lists:member(warnings_as_errors, Opts) of
+ %% `Opts' can be passed in both as a list or a dictionary depending
+ %% on whether the first call to rebar_erlc_compiler was done with
+ %% the type `rebar_dict()' or `rebar_state:t()'.
+ LookupFn = if is_list(Opts) -> fun lists:member/2
+ ; true -> fun dict:is_key/2
+ end,
+ Prefix = case LookupFn(warnings_as_errors, Opts) of
true -> "";
false -> "Warning: "
end,
@@ -153,12 +186,12 @@ format_errors(_MainSource, Extra, Errors) ->
end
|| {Source, Descs} <- Errors].
-format_error(AbsSource, Extra, {{Line, Column}, Mod, Desc}) ->
+format_error(Source, Extra, {{Line, Column}, Mod, Desc}) ->
ErrorDesc = Mod:format_error(Desc),
- ?FMT("~s:~w:~w: ~s~s~n", [AbsSource, Line, Column, Extra, ErrorDesc]);
-format_error(AbsSource, Extra, {Line, Mod, Desc}) ->
+ ?FMT("~s:~w:~w: ~s~s~n", [Source, Line, Column, Extra, ErrorDesc]);
+format_error(Source, Extra, {Line, Mod, Desc}) ->
ErrorDesc = Mod:format_error(Desc),
- ?FMT("~s:~w: ~s~s~n", [AbsSource, Line, Extra, ErrorDesc]);
-format_error(AbsSource, Extra, {Mod, Desc}) ->
+ ?FMT("~s:~w: ~s~s~n", [Source, Line, Extra, ErrorDesc]);
+format_error(Source, Extra, {Mod, Desc}) ->
ErrorDesc = Mod:format_error(Desc),
- ?FMT("~s: ~s~s~n", [AbsSource, Extra, ErrorDesc]).
+ ?FMT("~s: ~s~s~n", [Source, Extra, ErrorDesc]).
diff --git a/src/rebar_config.erl b/src/rebar_config.erl
index 8d7bcf4..b50c030 100644
--- a/src/rebar_config.erl
+++ b/src/rebar_config.erl
@@ -56,35 +56,105 @@ consult_lock_file(File) ->
[] ->
[];
[Locks] when is_list(Locks) -> % beta lock file
- Locks;
+ read_attrs(beta, Locks, []);
[{Vsn, Locks}|Attrs] when is_list(Locks) -> % versioned lock file
- %% Make sure the warning above is to be shown whenever a version
- %% newer than the current one is being used, as we can't parse
- %% all the contents of the lock file properly.
+ %% Because this is the first version of rebar3 to introduce a lock
+ %% file, all versionned lock files with a different versions have
+ %% to be newer.
+ case Vsn of
+ ?CONFIG_VERSION ->
+ ok;
+ _ ->
+ %% Make sure the warning below is to be shown whenever a version
+ %% newer than the current one is being used, as we can't parse
+ %% all the contents of the lock file properly.
+ warn_vsn_once()
+ end,
+ read_attrs(Vsn, Locks, Attrs)
+ end.
+
+warn_vsn_once() ->
+ Warn = application:get_env(rebar, warn_config_vsn) =/= {ok, false},
+ application:set_env(rebar, warn_config_vsn, false),
+ case Warn of
+ false -> ok;
+ true ->
?WARN("Rebar3 detected a lock file from a newer version. "
"It will be loaded in compatibility mode, but important "
"information may be missing or lost. It is recommended to "
- "upgrade Rebar3.", []),
- read_attrs(Vsn, Locks, Attrs)
+ "upgrade Rebar3.", [])
end.
+
write_lock_file(LockFile, Locks) ->
- NewLocks = write_attrs(Locks),
+ {NewLocks, Attrs} = write_attrs(Locks),
%% Write locks in the beta format, at least until it's been long
%% enough we can start modifying the lock format.
- file:write_file(LockFile, io_lib:format("~p.~n", [NewLocks])).
+ case Attrs of
+ [] -> % write the old beta copy to avoid changes
+ file:write_file(LockFile, io_lib:format("~p.~n", [NewLocks]));
+ _ ->
+ file:write_file(LockFile,
+ io_lib:format("{~p,~n~p}.~n[~n~s~n].~n",
+ [?CONFIG_VERSION, NewLocks,
+ format_attrs(Attrs)]))
+ end.
+
+%% Attributes have a special formatting to ensure there's only one per
+%% line in terms of pkg_hash, so we disturb source diffing as little
+%% as possible.
+format_attrs([]) -> [];
+format_attrs([{pkg_hash, Vals}|T]) ->
+ [io_lib:format("{pkg_hash,[~n",[]), format_hashes(Vals), "]}",
+ maybe_comma(T) | format_attrs(T)];
+format_attrs([H|T]) ->
+ [io_lib:format("~p~s", [H, maybe_comma(T)]) | format_attrs(T)].
+
+format_hashes([]) -> [];
+format_hashes([{Pkg,Hash}|T]) ->
+ [" {", io_lib:format("~p",[Pkg]), ", ", io_lib:format("~p", [Hash]), "}",
+ maybe_comma(T) | format_hashes(T)].
+
+maybe_comma([]) -> "";
+maybe_comma([_|_]) -> io_lib:format(",~n", []).
-read_attrs(_Vsn, Locks, _Attrs) ->
+read_attrs(_Vsn, Locks, Attrs) ->
%% Beta copy does not know how to expand attributes, but
%% is ready to support it.
- Locks.
+ expand_locks(Locks, extract_pkg_hashes(Attrs)).
+
+extract_pkg_hashes(Attrs) ->
+ Props = case Attrs of
+ [First|_] -> First;
+ [] -> []
+ end,
+ proplists:get_value(pkg_hash, Props, []).
+
+expand_locks([], _Hashes) ->
+ [];
+expand_locks([{Name, {pkg,PkgName,Vsn}, Lvl} | Locks], Hashes) ->
+ Hash = proplists:get_value(Name, Hashes),
+ [{Name, {pkg,PkgName,Vsn,Hash}, Lvl} | expand_locks(Locks, Hashes)];
+expand_locks([Lock|Locks], Hashes) ->
+ [Lock | expand_locks(Locks, Hashes)].
write_attrs(Locks) ->
%% No attribute known that needs to be taken out of the structure,
%% just return terms as is.
- Locks.
-
+ {NewLocks, Hashes} = split_locks(Locks, [], []),
+ case Hashes of
+ [] -> {NewLocks, []};
+ _ -> {NewLocks, [{pkg_hash, lists:sort(Hashes)}]}
+ end.
+split_locks([], Locks, Hashes) ->
+ {lists:reverse(Locks), Hashes};
+split_locks([{Name, {pkg,PkgName,Vsn,undefined}, Lvl} | Locks], LAcc, HAcc) ->
+ split_locks(Locks, [{Name,{pkg,PkgName,Vsn},Lvl}|LAcc], HAcc);
+split_locks([{Name, {pkg,PkgName,Vsn,Hash}, Lvl} | Locks], LAcc, HAcc) ->
+ split_locks(Locks, [{Name,{pkg,PkgName,Vsn},Lvl}|LAcc], [{Name, Hash}|HAcc]);
+split_locks([Lock|Locks], LAcc, HAcc) ->
+ split_locks(Locks, [Lock|LAcc], HAcc).
consult_file(File) ->
Terms = consult_file_(File),
@@ -180,6 +250,24 @@ check_newly_added({Name, _, Source}, LockedDeps) ->
check_newly_added(Dep, LockedDeps) ->
check_newly_added_(Dep, LockedDeps).
+%% get [raw] deps out of the way
+check_newly_added_({Name, Source, Opts}, LockedDeps) when is_tuple(Source),
+ is_list(Opts) ->
+ case check_newly_added_(Name, LockedDeps) of
+ {true, Name1} ->
+ {true, {Name1, Source}};
+ false ->
+ false
+ end;
+check_newly_added_({Name,_Vsn,Source,Opts}, LockedDeps) when is_tuple(Source),
+ is_list(Opts) ->
+ case check_newly_added_(Name, LockedDeps) of
+ {true, Name1} ->
+ {true, {Name1, Source}};
+ false ->
+ false
+ end;
+%% and on to regular deps
check_newly_added_({Name, Vsn, Source}, LockedDeps) ->
case check_newly_added_(Name, LockedDeps) of
{true, Name1} ->
diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl
index 3480cf6..167f2bb 100644
--- a/src/rebar_erlc_compiler.erl
+++ b/src/rebar_erlc_compiler.erl
@@ -54,7 +54,6 @@
-define(DEFAULT_OUTDIR, "ebin").
-define(RE_PREFIX, "^[^._]").
-
%% ===================================================================
%% Public API
%% ===================================================================
@@ -201,7 +200,7 @@ compile_dirs(RebarOpts, BaseDir, SrcDirs, OutDir, Opts) ->
ok = filelib:ensure_dir(filename:join(OutDir, "dummy.beam")),
true = code:add_patha(filename:absname(OutDir)),
- G = init_erlcinfo(proplists:get_all_values(i, ErlOpts), AllErlFiles, BaseDir, OutDir),
+ G = init_erlcinfo(include_abs_dirs(ErlOpts, BaseDir), AllErlFiles, BaseDir, OutDir),
NeededErlFiles = needed_files(G, ErlOpts, BaseDir, OutDir, AllErlFiles),
{ErlFirstFiles, ErlOptsFirst} = erl_first_files(RebarOpts, ErlOpts, BaseDir, NeededErlFiles),
@@ -308,6 +307,7 @@ needed_files(G, ErlOpts, Dir, OutDir, SourceFiles) ->
,{i, Dir}] ++ ErlOpts,
digraph:vertex(G, Source) > {Source, filelib:last_modified(Target)}
orelse opts_changed(AllOpts, TargetBase)
+ orelse erl_compiler_opts_set()
end, SourceFiles).
maybe_rm_beam_and_edge(G, OutDir, Source) ->
@@ -340,6 +340,12 @@ compile_info(Target) ->
{error, Reason}
end.
+erl_compiler_opts_set() ->
+ case os:getenv("ERL_COMPILER_OPTIONS") of
+ false -> false;
+ _ -> true
+ end.
+
erlcinfo_file(Dir) ->
filename:join(rebar_dir:local_cache_dir(Dir), ?ERLCINFO_FILE).
@@ -381,7 +387,7 @@ maybe_rm_beams_and_edges(G, Dir, Files) ->
source_and_include_dirs(InclDirs, Erls) ->
SourceDirs = lists:map(fun filename:dirname/1, Erls),
- lists:usort(["include" | InclDirs ++ SourceDirs]).
+ lists:usort(InclDirs ++ SourceDirs).
update_erlcinfo(G, Dirs, Source) ->
case digraph:vertex(G, Source) of
@@ -497,10 +503,9 @@ expand_file_names(Files, Dirs) ->
end
end, Files).
-
-spec internal_erl_compile(rebar_dict(), file:filename(), file:filename(),
file:filename(), list()) -> ok | {ok, any()} | {error, any(), any()}.
-internal_erl_compile(_Opts, Dir, Module, OutDir, ErlOpts) ->
+internal_erl_compile(Opts, Dir, Module, OutDir, ErlOpts) ->
Target = target_base(OutDir, Module) ++ ".beam",
ok = filelib:ensure_dir(Target),
AllOpts = [{outdir, filename:dirname(Target)}] ++ ErlOpts ++
@@ -509,11 +514,21 @@ internal_erl_compile(_Opts, Dir, Module, OutDir, ErlOpts) ->
{ok, _Mod} ->
ok;
{ok, _Mod, Ws} ->
- rebar_base_compiler:ok_tuple(Module, Ws);
+ FormattedWs = format_error_sources(Ws, Opts),
+ rebar_base_compiler:ok_tuple(Module, FormattedWs);
{error, Es, Ws} ->
- rebar_base_compiler:error_tuple(Module, Es, Ws, AllOpts)
+ error_tuple(Module, Es, Ws, AllOpts, Opts)
end.
+error_tuple(Module, Es, Ws, AllOpts, Opts) ->
+ FormattedEs = format_error_sources(Es, Opts),
+ FormattedWs = format_error_sources(Ws, Opts),
+ rebar_base_compiler:error_tuple(Module, FormattedEs, FormattedWs, AllOpts).
+
+format_error_sources(Es, Opts) ->
+ [{rebar_base_compiler:format_error_source(Src, Opts), Desc}
+ || {Src, Desc} <- Es].
+
target_base(OutDir, Source) ->
filename:join(OutDir, filename:basename(Source, ".erl")).
@@ -722,6 +737,10 @@ outdir(RebarOpts) ->
ErlOpts = rebar_opts:erl_opts(RebarOpts),
proplists:get_value(outdir, ErlOpts, ?DEFAULT_OUTDIR).
+include_abs_dirs(ErlOpts, BaseDir) ->
+ InclDirs = ["include"|proplists:get_all_values(i, ErlOpts)],
+ lists:map(fun(Incl) -> filename:join([BaseDir, Incl]) end, InclDirs).
+
parse_opts(Opts) -> parse_opts(Opts, #compile_opts{}).
parse_opts([], CompileOpts) -> CompileOpts;
diff --git a/src/rebar_fetch.erl b/src/rebar_fetch.erl
index b80c125..47bfe1d 100644
--- a/src/rebar_fetch.erl
+++ b/src/rebar_fetch.erl
@@ -67,6 +67,12 @@ needs_update(AppDir, Source, State) ->
format_error({bad_download, CachePath}) ->
io_lib:format("Download of package does not match md5sum from server: ~s", [CachePath]);
+format_error({unexpected_hash, CachePath, Expected, Found}) ->
+ io_lib:format("The checksum for package at ~s (~s) does not match the "
+ "checksum previously locked (~s). Either unlock or "
+ "upgrade the package, or make sure you fetched it from "
+ "the same index from which it was initially fetched.",
+ [CachePath, Found, Expected]);
format_error({failed_extract, CachePath}) ->
io_lib:format("Failed to extract package: ~s", [CachePath]);
format_error({bad_etag, Source}) ->
diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl
index 0f84520..104c047 100644
--- a/src/rebar_file_utils.erl
+++ b/src/rebar_file_utils.erl
@@ -27,6 +27,7 @@
-module(rebar_file_utils).
-export([try_consult/1,
+ consult_config/2,
format_error/1,
symlink_or_copy/2,
rm_rf/1,
@@ -39,7 +40,9 @@
reset_dir/1,
touch/1,
path_from_ancestor/2,
- canonical_path/1]).
+ canonical_path/1,
+ resolve_link/1,
+ split_dirname/1]).
-include("rebar.hrl").
@@ -61,6 +64,20 @@ try_consult(File) ->
throw(?PRV_ERROR({bad_term_file, File, Reason}))
end.
+-spec consult_config(rebar_state:t(), string()) -> [[tuple()]].
+consult_config(State, Filename) ->
+ Fullpath = filename:join(rebar_dir:root_dir(State), Filename),
+ ?DEBUG("Loading configuration from ~p", [Fullpath]),
+ Config = case try_consult(Fullpath) of
+ [T] -> T;
+ [] -> []
+ end,
+ SubConfigs = [consult_config(State, Entry ++ ".config") ||
+ Entry <- Config, is_list(Entry)
+ ],
+
+ [Config | lists:merge(SubConfigs)].
+
format_error({bad_term_file, AppFile, Reason}) ->
io_lib:format("Error reading file ~s: ~s", [AppFile, file:format_error(Reason)]).
@@ -160,11 +177,11 @@ mv(Source, Dest) ->
{win32, _} ->
Cmd = case filelib:is_dir(Source) of
true ->
- ?FMT("robocopy /move /s \"~s\" \"~s\" 1> nul",
+ ?FMT("robocopy /move /e \"~s\" \"~s\" 1> nul",
[rebar_utils:escape_double_quotes(filename:nativename(Source)),
rebar_utils:escape_double_quotes(filename:nativename(Dest))]);
false ->
- ?FMT("robocopy /move /s \"~s\" \"~s\" \"~s\" 1> nul",
+ ?FMT("robocopy /move /e \"~s\" \"~s\" \"~s\" 1> nul",
[rebar_utils:escape_double_quotes(filename:nativename(filename:dirname(Source))),
rebar_utils:escape_double_quotes(filename:nativename(Dest)),
rebar_utils:escape_double_quotes(filename:basename(Source))])
@@ -273,6 +290,22 @@ canonical_path([_|Acc], [".."|Rest]) -> canonical_path(Acc, Rest);
canonical_path([], [".."|Rest]) -> canonical_path([], Rest);
canonical_path(Acc, [Component|Rest]) -> canonical_path([Component|Acc], Rest).
+%% returns canonical target of path if path is a link, otherwise returns path
+-spec resolve_link(string()) -> string().
+
+resolve_link(Path) ->
+ case file:read_link(Path) of
+ {ok, Target} ->
+ canonical_path(filename:absname(Target, filename:dirname(Path)));
+ {error, _} -> Path
+ end.
+
+%% splits a path into dirname and basename
+-spec split_dirname(string()) -> {string(), string()}.
+
+split_dirname(Path) ->
+ {filename:dirname(Path), filename:basename(Path)}.
+
%% ===================================================================
%% Internal functions
%% ===================================================================
diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl
index 5a6a5ef..acb9ec0 100644
--- a/src/rebar_git_resource.erl
+++ b/src/rebar_git_resource.erl
@@ -17,10 +17,17 @@
lock(AppDir, {git, Url, _}) ->
lock(AppDir, {git, Url});
lock(AppDir, {git, Url}) ->
- AbortMsg = io_lib:format("Locking of git dependency failed in ~s", [AppDir]),
+ AbortMsg = lists:flatten(io_lib:format("Locking of git dependency failed in ~s", [AppDir])),
+ Dir = rebar_utils:escape_double_quotes(AppDir),
{ok, VsnString} =
- rebar_utils:sh("git --git-dir=\"" ++ rebar_utils:escape_double_quotes(AppDir) ++ "/.git\" rev-parse --verify HEAD",
- [{use_stdout, false}, {debug_abort_on_error, AbortMsg}]),
+ case os:type() of
+ {win32, _} ->
+ rebar_utils:sh("git --git-dir=\"" ++ Dir ++ "/.git\" --work-tree=\"" ++ Dir ++ "\" rev-parse --verify HEAD",
+ [{use_stdout, false}, {debug_abort_on_error, AbortMsg}]);
+ _ ->
+ rebar_utils:sh("git --git-dir=\"" ++ Dir ++ "/.git\" rev-parse --verify HEAD",
+ [{use_stdout, false}, {debug_abort_on_error, AbortMsg}])
+ end,
Ref = string:strip(VsnString, both, $\n),
{git, Url, {ref, Ref}}.
diff --git a/src/rebar_log.erl b/src/rebar_log.erl
index 23ae81e..b1a70c2 100644
--- a/src/rebar_log.erl
+++ b/src/rebar_log.erl
@@ -27,6 +27,7 @@
-module(rebar_log).
-export([init/2,
+ crashdump/2,
set_level/1,
get_level/0,
error_level/0,
@@ -73,6 +74,7 @@ init(Caller, Verbosity) ->
?DEBUG_LEVEL -> debug
end,
Intensity = intensity(),
+ application:set_env(rebar, log_caller, Caller),
Log = ec_cmd_log:new(Level, Caller, Intensity),
set_level(valid_level(Verbosity)),
application:set_env(rebar, log, Log).
@@ -95,6 +97,16 @@ log(Level, Str, Args) ->
{ok, LogState} = application:get_env(rebar, log),
ec_cmd_log:Level(LogState, Str++"~n", Args).
+crashdump(Str, Args) ->
+ crashdump("rebar3.crashdump", Str, Args).
+crashdump(File, Str, Args) ->
+ case application:get_env(rebar, log_caller) of
+ {ok, api} ->
+ ok;
+ _ ->
+ file:write_file(File, io_lib:fwrite(Str, Args))
+ end.
+
error_level() -> ?ERROR_LEVEL.
default_level() -> ?INFO_LEVEL.
diff --git a/src/rebar_packages.erl b/src/rebar_packages.erl
index d4b8a14..8b4611b 100644
--- a/src/rebar_packages.erl
+++ b/src/rebar_packages.erl
@@ -122,7 +122,7 @@ package_dir(State) ->
Error
end.
-registry_checksum({pkg, Name, Vsn}, State) ->
+registry_checksum({pkg, Name, Vsn, _Hash}, State) ->
try
?MODULE:verify_table(State),
ets:lookup_element(?PACKAGE_TABLE, {Name, Vsn}, 3)
diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl
index ec7e09d..5817817 100644
--- a/src/rebar_pkg_resource.erl
+++ b/src/rebar_pkg_resource.erl
@@ -19,7 +19,7 @@
lock(_AppDir, Source) ->
Source.
-needs_update(Dir, {pkg, _Name, Vsn}) ->
+needs_update(Dir, {pkg, _Name, Vsn, _Hash}) ->
[AppInfo] = rebar_app_discover:find_apps([Dir], all),
case rebar_app_info:original_vsn(AppInfo) =:= ec_cnv:to_list(Vsn) of
true ->
@@ -28,7 +28,7 @@ needs_update(Dir, {pkg, _Name, Vsn}) ->
true
end.
-download(TmpDir, Pkg={pkg, Name, Vsn}, State) ->
+download(TmpDir, Pkg={pkg, Name, Vsn, _Hash}, State) ->
CDN = rebar_state:get(State, rebar_packages_cdn, ?DEFAULT_CDN),
{ok, PackageDir} = rebar_packages:package_dir(State),
Package = binary_to_list(<<Name/binary, "-", Vsn/binary, ".tar">>),
@@ -40,7 +40,7 @@ download(TmpDir, Pkg={pkg, Name, Vsn}, State) ->
{fetch_fail, Name, Vsn}
end.
-cached_download(TmpDir, CachePath, Pkg={pkg, Name, Vsn}, Url, ETag, State) ->
+cached_download(TmpDir, CachePath, Pkg={pkg, Name, Vsn, _Hash}, Url, ETag, State) ->
case request(Url, ETag) of
{ok, cached} ->
?INFO("Version cached at ~s is up to date, reusing it", [CachePath]),
@@ -58,17 +58,20 @@ cached_download(TmpDir, CachePath, Pkg={pkg, Name, Vsn}, Url, ETag, State) ->
serve_from_cache(TmpDir, CachePath, Pkg, State) ->
{Files, Contents, Version, Meta} = extract(TmpDir, CachePath),
case checksums(Pkg, Files, Contents, Version, Meta, State) of
- {Chk, Chk, Chk} ->
+ {Chk, Chk, Chk, Chk} ->
ok = erl_tar:extract({binary, Contents}, [{cwd, TmpDir}, compressed]),
{ok, true};
- {_Bin, Chk, Chk} ->
+ {_Hash, Chk, Chk, Chk} ->
+ ?DEBUG("Expected hash ~p does not match checksums ~p", [_Hash, Chk]),
+ {unexpected_hash, CachePath, _Hash, Chk};
+ {Chk, _Bin, Chk, Chk} ->
?DEBUG("Checksums: registry: ~p, pkg: ~p", [Chk, _Bin]),
{failed_extract, CachePath};
- {Chk, _Reg, Chk} ->
+ {Chk, Chk, _Reg, Chk} ->
?DEBUG("Checksums: registry: ~p, pkg: ~p", [_Reg, Chk]),
{bad_registry_checksum, CachePath};
- {_Bin, _Reg, _Tar} ->
- ?DEBUG("Checksums: registry: ~p, pkg: ~p, meta: ~p", [_Reg, _Bin, _Tar]),
+ {_Hash, _Bin, _Reg, _Tar} ->
+ ?DEBUG("Checksums: expected: ~p, registry: ~p, pkg: ~p, meta: ~p", [_Hash, _Reg, _Bin, _Tar]),
{bad_checksum, CachePath}
end.
@@ -92,13 +95,13 @@ extract(TmpDir, CachePath) ->
{"metadata.config", Meta} = lists:keyfind("metadata.config", 1, Files),
{Files, Contents, Version, Meta}.
-checksums(Pkg, Files, Contents, Version, Meta, State) ->
+checksums(Pkg={pkg, _Name, _Vsn, Hash}, Files, Contents, Version, Meta, State) ->
Blob = <<Version/binary, Meta/binary, Contents/binary>>,
<<X:256/big-unsigned>> = crypto:hash(sha256, Blob),
BinChecksum = list_to_binary(string:to_upper(lists:flatten(io_lib:format("~64.16.0b", [X])))),
RegistryChecksum = rebar_packages:registry_checksum(Pkg, State),
{"CHECKSUM", TarChecksum} = lists:keyfind("CHECKSUM", 1, Files),
- {BinChecksum, RegistryChecksum, TarChecksum}.
+ {Hash, BinChecksum, RegistryChecksum, TarChecksum}.
make_vsn(_) ->
{error, "Replacing version of type pkg not supported."}.
diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl
index 5712fbf..1e0632e 100644
--- a/src/rebar_prv_common_test.erl
+++ b/src/rebar_prv_common_test.erl
@@ -116,6 +116,7 @@ prepare_tests(State) ->
%% rebar.config test options
CfgOpts = cfgopts(State),
ProjectApps = rebar_state:project_apps(State),
+
%% prioritize tests to run first trying any command line specified
%% tests falling back to tests specified in the config file finally
%% running a default set if no other tests are present
@@ -215,6 +216,14 @@ add_hooks(Opts, State) ->
select_tests(_, _, {error, _} = Error, _) -> Error;
select_tests(_, _, _, {error, _} = Error) -> Error;
select_tests(State, ProjectApps, CmdOpts, CfgOpts) ->
+ %% set application env if sys_config argument is provided
+ SysConfigs = sys_config_list(CmdOpts, CfgOpts),
+ Configs = lists:flatmap(fun(Filename) ->
+ rebar_file_utils:consult_config(State, Filename)
+ end, SysConfigs),
+ [application:load(Application) || Config <- Configs, {Application, _} <- Config],
+ rebar_utils:reread_config(Configs),
+
Merged = lists:ukeymerge(1,
lists:ukeysort(1, CmdOpts),
lists:ukeysort(1, CfgOpts)),
@@ -229,6 +238,17 @@ select_tests(State, ProjectApps, CmdOpts, CfgOpts) ->
end,
discover_tests(State, ProjectApps, Opts).
+sys_config_list(CmdOpts, CfgOpts) ->
+ CmdSysConfigs = split_string(proplists:get_value(sys_config, CmdOpts, "")),
+ case proplists:get_value(sys_config, CfgOpts, []) of
+ [H | _]=Configs when is_list(H) ->
+ Configs ++ CmdSysConfigs;
+ [] ->
+ CmdSysConfigs;
+ Configs ->
+ [Configs | CmdSysConfigs]
+ end.
+
discover_tests(State, ProjectApps, Opts) ->
case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of
%% no dirs or suites defined, try using `$APP/test` and `$ROOT/test`
@@ -647,7 +667,8 @@ ct_opts(_State) ->
{verbose, $v, "verbose", boolean, help(verbose)},
{name, undefined, "name", atom, help(name)},
{sname, undefined, "sname", atom, help(sname)},
- {setcookie, undefined, "setcookie", atom, help(setcookie)}
+ {setcookie, undefined, "setcookie", atom, help(setcookie)},
+ {sys_config, undefined, "sys_config", string, help(sys_config)} %% comma-seperated list
].
help(dir) ->
@@ -662,6 +683,8 @@ help(label) ->
"Test label";
help(config) ->
"List of config files";
+help(sys_config) ->
+ "List of application config files";
help(allow_user_terms) ->
"Allow user defined config values in config files";
help(logdir) ->
diff --git a/src/rebar_prv_cover.erl b/src/rebar_prv_cover.erl
index b04b1c1..9772e96 100644
--- a/src/rebar_prv_cover.erl
+++ b/src/rebar_prv_cover.erl
@@ -298,6 +298,7 @@ cover_compile(State, apps) ->
AppDirs = app_dirs(Apps),
cover_compile(State, lists:filter(fun(D) -> ec_file:is_dir(D) end, AppDirs));
cover_compile(State, Dirs) ->
+ rebar_utils:update_code(rebar_state:code_paths(State, all_deps), [soft_purge]),
%% start the cover server if necessary
{ok, CoverPid} = start_cover(),
%% redirect cover output
@@ -327,7 +328,9 @@ cover_compile(State, Dirs) ->
{error, Reason} ->
?WARN("Directory ~p error ~p", [Dir, Reason])
end
- end, Dirs).
+ end, Dirs),
+ rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)),
+ ok.
app_dirs(Apps) ->
lists:foldl(fun app_ebin_dirs/2, [], Apps).
diff --git a/src/rebar_prv_deps.erl b/src/rebar_prv_deps.erl
index 9ff2bfa..c865276 100644
--- a/src/rebar_prv_deps.erl
+++ b/src/rebar_prv_deps.erl
@@ -97,7 +97,7 @@ display_dep(_State, {Name, _Vsn, Source}) when is_tuple(Source) ->
display_dep(_State, {Name, _Vsn, Source, _Opts}) when is_tuple(Source) ->
?CONSOLE("~s* (~s source)", [ec_cnv:to_binary(Name), type(Source)]);
%% Locked
-display_dep(State, {Name, Source={pkg, _, Vsn}, Level}) when is_integer(Level) ->
+display_dep(State, {Name, Source={pkg, _, Vsn, _}, Level}) when is_integer(Level) ->
DepsDir = rebar_dir:deps_dir(State),
AppDir = filename:join([DepsDir, ec_cnv:to_binary(Name)]),
NeedsUpdate = case rebar_fetch:needs_update(AppDir, Source, State) of
diff --git a/src/rebar_prv_deps_tree.erl b/src/rebar_prv_deps_tree.erl
index cefa60a..c0c8bab 100644
--- a/src/rebar_prv_deps_tree.erl
+++ b/src/rebar_prv_deps_tree.erl
@@ -70,7 +70,7 @@ print_children(Prefix, [{Name, Vsn, Source} | Rest], Dict, Verbose) ->
[Prefix, " "];
_ ->
io:format("~ts~ts", [Prefix, <<226,148,156,226,148,128,32>>]), %Binary for ├─ utf8%
- [Prefix, "│ "]
+ [Prefix, <<226,148,130,32,32>>] %Binary for │ utf8%
end,
io:format("~ts~ts~ts (~ts)~n", [Name, <<226,148,128>>, Vsn, type(Source, Verbose)]), %Binary for ─ utf8%
case dict:find(Name, Dict) of
diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl
index 622ee60..fc13de1 100644
--- a/src/rebar_prv_dialyzer.erl
+++ b/src/rebar_prv_dialyzer.erl
@@ -249,17 +249,32 @@ ebin_files(EbinDir) ->
File <- filelib:wildcard(Wildcard, EbinDir)].
read_plt(_State, Plt) ->
- case dialyzer:plt_info(Plt) of
- {ok, Info} ->
- Files = proplists:get_value(files, Info, []),
+ Vsn = dialyzer_version(),
+ case plt_files(Plt) of
+ {ok, Files} when Vsn < {2, 9, 0} ->
+ % Before dialyzer-2.9 (OTP 18.3) removing a beam file from the PLT
+ % that no longer exists would crash. Therefore force a rebuild of
+ % PLT if any files no longer exist.
read_plt_files(Plt, Files);
+ {ok, _} = Result when Vsn >= {2, 9, 0} ->
+ Result;
{error, no_such_file} ->
error;
+ {error, not_valid} ->
+ error;
{error, read_error} ->
Error = io_lib:format("Could not read the PLT file ~p", [Plt]),
throw({dialyzer_error, Error})
end.
+plt_files(Plt) ->
+ case dialyzer:plt_info(Plt) of
+ {ok, Info} ->
+ {ok, proplists:get_value(files, Info, [])};
+ {error, _} = Error ->
+ Error
+ end.
+
%% If any file no longer exists dialyzer will fail when updating the PLT.
read_plt_files(Plt, Files) ->
case [File || File <- Files, not filelib:is_file(File)] of
@@ -353,8 +368,19 @@ update_base_plt(State, BasePlt, Output, BaseFiles) ->
build_plt(State, BasePlt, Output, BaseFiles)
end.
+build_plt(State, Plt, _, []) ->
+ ?INFO("Building with no files in ~p...", [Plt]),
+ Opts = [{get_warnings, false},
+ {output_plt, Plt},
+ {apps, [erts]}],
+ % Create a PLT with erts files and then remove erts files to be left with an
+ % empty PLT. Dialyzer will crash when trying to build a PLT with an empty
+ % file list.
+ _ = dialyzer:run([{analysis_type, plt_build} | Opts]),
+ _ = dialyzer:run([{analysis_type, plt_remove}, {init_plt, Plt} | Opts]),
+ {0, State};
build_plt(State, Plt, Output, Files) ->
- ?INFO("Adding ~b files to ~p...", [length(Files), Plt]),
+ ?INFO("Building with ~b files in ~p...", [length(Files), Plt]),
GetWarnings = get_config(State, get_warnings, false),
Opts = [{analysis_type, plt_build},
{get_warnings, GetWarnings},
@@ -369,12 +395,15 @@ succ_typings(State, Plt, Output) ->
{0, State};
_ ->
Apps = rebar_state:project_apps(State),
- succ_typings(State, Plt, Output, Apps)
+ ?INFO("Doing success typing analysis...", []),
+ Files = apps_to_files(Apps),
+ succ_typings(State, Plt, Output, Files)
end.
-succ_typings(State, Plt, Output, Apps) ->
- ?INFO("Doing success typing analysis...", []),
- Files = apps_to_files(Apps),
+succ_typings(State, Plt, _, []) ->
+ ?INFO("Analyzing no files with ~p...", [Plt]),
+ {0, State};
+succ_typings(State, Plt, Output, Files) ->
?INFO("Analyzing ~b files with ~p...", [length(Files), Plt]),
Opts = [{analysis_type, succ_typings},
{get_warnings, true},
diff --git a/src/rebar_prv_edoc.erl b/src/rebar_prv_edoc.erl
index 6cefe14..465fc34 100644
--- a/src/rebar_prv_edoc.erl
+++ b/src/rebar_prv_edoc.erl
@@ -33,17 +33,25 @@ do(State) ->
code:add_pathsa(rebar_state:code_paths(State, all_deps)),
ProjectApps = rebar_state:project_apps(State),
Providers = rebar_state:providers(State),
- EDocOpts = rebar_state:get(State, edoc_opts, []),
+ EdocOpts = rebar_state:get(State, edoc_opts, []),
+ ShouldAccPaths = not has_configured_paths(EdocOpts),
Cwd = rebar_state:dir(State),
rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, State),
- lists:foreach(fun(AppInfo) ->
- rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, AppInfo, State),
- AppName = ec_cnv:to_list(rebar_app_info:name(AppInfo)),
- ?INFO("Running edoc for ~s", [AppName]),
- AppDir = rebar_app_info:dir(AppInfo),
- ok = edoc:application(list_to_atom(AppName), AppDir, EDocOpts),
- rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, AppInfo, State)
- end, ProjectApps),
+ lists:foldl(fun(AppInfo, EdocOptsAcc) ->
+ rebar_hooks:run_all_hooks(Cwd, pre, ?PROVIDER, Providers, AppInfo, State),
+ AppName = ec_cnv:to_list(rebar_app_info:name(AppInfo)),
+ ?INFO("Running edoc for ~s", [AppName]),
+ AppDir = rebar_app_info:dir(AppInfo),
+ ok = edoc:application(list_to_atom(AppName), AppDir, EdocOptsAcc),
+ rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, AppInfo, State),
+ case ShouldAccPaths of
+ true ->
+ %% edoc wants / on all OSes
+ add_to_paths(EdocOptsAcc, AppDir++"/doc");
+ false ->
+ EdocOptsAcc
+ end
+ end, EdocOpts, ProjectApps),
rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State),
rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)),
{ok, State}.
@@ -55,3 +63,12 @@ format_error(Reason) ->
%% ===================================================================
%% Internal functions
%% ===================================================================
+has_configured_paths(EdocOpts) ->
+ proplists:get_value(dir, EdocOpts) =/= undefined.
+
+add_to_paths([], Path) ->
+ [{doc_path, [Path]}];
+add_to_paths([{doc_path, Paths}|T], Path) ->
+ [{doc_path, [Path | Paths]} | T];
+add_to_paths([H|T], Path) ->
+ [H | add_to_paths(Path, T)].
diff --git a/src/rebar_prv_escriptize.erl b/src/rebar_prv_escriptize.erl
index 32f5508..7ee20c2 100644
--- a/src/rebar_prv_escriptize.erl
+++ b/src/rebar_prv_escriptize.erl
@@ -61,8 +61,11 @@ desc() ->
"the project's and its dependencies' BEAM files.".
do(State) ->
+ Providers = rebar_state:providers(State),
+ Cwd = rebar_state:dir(State),
+ rebar_hooks:run_project_and_app_hooks(Cwd, pre, ?PROVIDER, Providers, State),
?INFO("Building escript...", []),
- case rebar_state:get(State, escript_main_app, undefined) of
+ Res = case rebar_state:get(State, escript_main_app, undefined) of
undefined ->
case rebar_state:project_apps(State) of
[App] ->
@@ -72,9 +75,15 @@ do(State) ->
end;
Name ->
AllApps = rebar_state:all_deps(State)++rebar_state:project_apps(State),
- {ok, AppInfo} = rebar_app_utils:find(ec_cnv:to_binary(Name), AllApps),
- escriptize(State, AppInfo)
- end.
+ case rebar_app_utils:find(ec_cnv:to_binary(Name), AllApps) of
+ {ok, AppInfo} ->
+ escriptize(State, AppInfo);
+ _ ->
+ ?PRV_ERROR({bad_name, Name})
+ end
+ end,
+ rebar_hooks:run_project_and_app_hooks(Cwd, post, ?PROVIDER, Providers, State),
+ Res.
escriptize(State0, App) ->
AppName = rebar_app_info:name(App),
@@ -90,9 +99,9 @@ escriptize(State0, App) ->
%% 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([ec_cnv:to_atom(AppName) | rebar_state:get(State, escript_incl_apps, [])
- ++ all_deps(State)]),
+ TopInclApps = lists:usort([ec_cnv:to_atom(AppName) | rebar_state:get(State, escript_incl_apps, [])]),
AllApps = rebar_state:all_deps(State)++rebar_state:project_apps(State),
+ InclApps = find_deps(TopInclApps, AllApps),
InclBeams = get_apps_beams(InclApps, AllApps),
%% Look for a list of extra files to include in the output file.
@@ -219,9 +228,25 @@ usort(List) ->
get_nonempty(Files) ->
[{FName,FBin} || {FName,FBin} <- Files, FBin =/= <<>>].
-all_deps(State) ->
- [list_to_existing_atom(binary_to_list(rebar_app_info:name(App)))
- || App <- rebar_state:all_deps(State)].
+find_deps(AppNames, AllApps) ->
+ BinAppNames = [ec_cnv:to_binary(Name) || Name <- AppNames],
+ [ec_cnv:to_atom(Name) ||
+ Name <- find_deps_of_deps(BinAppNames, AllApps, BinAppNames)].
+
+%% Should look at the app files to find direct dependencies
+find_deps_of_deps([], _, Acc) -> Acc;
+find_deps_of_deps([Name|Names], Apps, Acc) ->
+ ?DEBUG("processing ~p", [Name]),
+ {ok, App} = rebar_app_utils:find(Name, Apps),
+ DepNames = proplists:get_value(applications, rebar_app_info:app_details(App), []),
+ BinDepNames = [ec_cnv:to_binary(Dep) || Dep <- DepNames,
+ %% ignore system libs; shouldn't include them.
+ DepDir <- [code:lib_dir(Dep)],
+ DepDir =:= {error, bad_name} orelse % those are all local
+ not lists:prefix(code:root_dir(), DepDir)]
+ -- ([Name|Names]++Acc), % avoid already seen deps
+ ?DEBUG("new deps of ~p found to be ~p", [Name, BinDepNames]),
+ find_deps_of_deps(BinDepNames ++ Names, Apps, BinDepNames ++ Acc).
def(Rm, State, Key, Default) ->
Value0 = rebar_state:get(State, Key, Default),
diff --git a/src/rebar_prv_help.erl b/src/rebar_prv_help.erl
index c028264..75cc609 100644
--- a/src/rebar_prv_help.erl
+++ b/src/rebar_prv_help.erl
@@ -41,7 +41,10 @@ do(State) ->
[Name] -> % default namespace
task_help(default, list_to_atom(Name), State);
[Namespace, Name] ->
- task_help(list_to_atom(Namespace), list_to_atom(Name), State)
+ task_help(list_to_atom(Namespace), list_to_atom(Name), State);
+ _ ->
+ {error, "Too many arguments given. " ++
+ "Usage: rebar3 help [<namespace>] <task>"}
end.
-spec format_error(any()) -> iolist().
diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl
index 5e6aa4c..a8a7ea0 100644
--- a/src/rebar_prv_install_deps.erl
+++ b/src/rebar_prv_install_deps.erl
@@ -352,10 +352,14 @@ make_relative_to_root(State, Path) when is_list(Path) ->
rebar_dir:make_relative_path(Path, Root).
fetch_app(AppInfo, AppDir, State) ->
- ?INFO("Fetching ~s (~p)", [rebar_app_info:name(AppInfo), rebar_app_info:source(AppInfo)]),
+ ?INFO("Fetching ~s (~p)", [rebar_app_info:name(AppInfo),
+ format_source(rebar_app_info:source(AppInfo))]),
Source = rebar_app_info:source(AppInfo),
true = rebar_fetch:download_source(AppDir, Source, State).
+format_source({pkg, Name, Vsn, _Hash}) -> {pkg, Name, Vsn};
+format_source(Source) -> Source.
+
%% 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.
diff --git a/src/rebar_prv_path.erl b/src/rebar_prv_path.erl
index 4e88496..4259eec 100644
--- a/src/rebar_prv_path.erl
+++ b/src/rebar_prv_path.erl
@@ -77,15 +77,15 @@ paths([{rel, true}|Rest], Apps, State, Acc) ->
base_dir(State) -> io_lib:format("~s", [rebar_dir:base_dir(State)]).
bin_dir(State) -> io_lib:format("~s/bin", [rebar_dir:base_dir(State)]).
-lib_dir(State) -> io_lib:format("~s/lib", [rebar_dir:base_dir(State)]).
+lib_dir(State) -> io_lib:format("~s", [rebar_dir:deps_dir(State)]).
rel_dir(State) -> io_lib:format("~s/rel", [rebar_dir:base_dir(State)]).
ebin_dirs(Apps, State) ->
- lists:map(fun(App) -> io_lib:format("~s/lib/~s/ebin", [rebar_dir:base_dir(State), App]) end, Apps).
+ lists:map(fun(App) -> io_lib:format("~s/~s/ebin", [rebar_dir:deps_dir(State), App]) end, Apps).
priv_dirs(Apps, State) ->
- lists:map(fun(App) -> io_lib:format("~s/lib/~s/priv", [rebar_dir:base_dir(State), App]) end, Apps).
+ lists:map(fun(App) -> io_lib:format("~s/~s/priv", [rebar_dir:deps_dir(State), App]) end, Apps).
src_dirs(Apps, State) ->
- lists:map(fun(App) -> io_lib:format("~s/lib/~s/src", [rebar_dir:base_dir(State), App]) end, Apps).
+ lists:map(fun(App) -> io_lib:format("~s/~s/src", [rebar_dir:deps_dir(State), App]) end, Apps).
print_paths_if_exist(Paths, State) ->
{RawOpts, _} = rebar_state:command_parsed_args(State),
diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl
index a5457ad..b7febf8 100644
--- a/src/rebar_prv_shell.erl
+++ b/src/rebar_prv_shell.erl
@@ -111,7 +111,7 @@ shell(State) ->
%% Hack to fool the init process into thinking we have stopped and the normal
%% node start process can go on. Without it, init:get_status() always return
%% '{starting, started}' instead of '{started, started}'
- init ! {'EXIT', self(), normal},
+ init ! {'EXIT', self(), normal},
gen_server:enter_loop(rebar_agent, [], GenState, {local, rebar_agent}, hibernate).
info() ->
@@ -332,15 +332,7 @@ reread_config(State) ->
no_config ->
ok;
ConfigList ->
- try
- [application:set_env(Application, Key, Val)
- || Config <- ConfigList,
- {Application, Items} <- Config,
- {Key, Val} <- Items]
- catch _:_ ->
- ?ERROR("The configuration file submitted could not be read "
- "and will be ignored.", [])
- end,
+ _ = rebar_utils:reread_config(ConfigList),
ok
end.
@@ -406,7 +398,7 @@ find_config(State) ->
no_value ->
no_config;
Filename when is_list(Filename) ->
- consult_config(State, Filename)
+ rebar_file_utils:consult_config(State, Filename)
end.
-spec first_value([Fun], State) -> no_value | Value when
@@ -414,7 +406,7 @@ find_config(State) ->
State :: rebar_state:t(),
Fun :: fun ((State) -> no_value | Value).
first_value([], _) -> no_value;
-first_value([Fun | Rest], State) ->
+first_value([Fun | Rest], State) ->
case Fun(State) of
no_value ->
first_value(Rest, State);
@@ -445,18 +437,3 @@ find_config_rebar(State) ->
find_config_relx(State) ->
debug_get_value(sys_config, rebar_state:get(State, relx, []), no_value,
"Found config from relx.").
-
--spec consult_config(rebar_state:t(), string()) -> [[tuple()]].
-consult_config(State, Filename) ->
- Fullpath = filename:join(rebar_dir:root_dir(State), Filename),
- ?DEBUG("Loading configuration from ~p", [Fullpath]),
- Config = case rebar_file_utils:try_consult(Fullpath) of
- [T] -> T;
- [] -> []
- end,
- SubConfigs = [consult_config(State, Entry ++ ".config") ||
- Entry <- Config, is_list(Entry)
- ],
-
- [Config | lists:merge(SubConfigs)].
-
diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl
index 5e1e253..54b135e 100644
--- a/src/rebar_prv_update.erl
+++ b/src/rebar_prv_update.erl
@@ -114,14 +114,14 @@ hex_to_index(State) ->
ets:foldl(fun({Pkg, [[]]}, _) when is_binary(Pkg) ->
true;
- ({Pkg, [Vsns=[Vsn | _Rest]]}, _) when is_binary(Pkg) ->
+ ({Pkg, [Vsns=[_Vsn | _Rest]]}, _) when is_binary(Pkg) ->
%% Verify the package is of the right build tool by checking if the first
%% version exists in the table from the foldl above
- case ets:member(?PACKAGE_TABLE, {Pkg, Vsn}) of
- true ->
- ets:insert(?PACKAGE_TABLE, {Pkg, Vsns});
- false ->
- true
+ case [V || V <- Vsns, ets:member(?PACKAGE_TABLE, {Pkg, V})] of
+ [] ->
+ true;
+ Vsns1 ->
+ ets:insert(?PACKAGE_TABLE, {Pkg, Vsns1})
end;
(_, _) ->
true
diff --git a/src/rebar_prv_upgrade.erl b/src/rebar_prv_upgrade.erl
index c5c43e4..18c307b 100644
--- a/src/rebar_prv_upgrade.erl
+++ b/src/rebar_prv_upgrade.erl
@@ -45,7 +45,29 @@ init(State) ->
do(State) ->
{Args, _} = rebar_state:command_parsed_args(State),
Locks = rebar_state:get(State, {locks, default}, []),
- Deps = rebar_state:get(State, deps, []),
+ %% We have 3 sources of dependencies to upgrade from:
+ %% 1. the top-level rebar.config (in `deps', dep name is an atom)
+ %% 2. the app-level rebar.config in umbrella apps (in `{deps, default}',
+ %% where the dep name is an atom)
+ %% 3. the formatted sources for all after app-parsing (in `{deps, default}',
+ %% where the reprocessed app name is a binary)
+ %%
+ %% The gotcha with these is that the selection of dependencies with a
+ %% binary name (those that are stable and usable internally) is done with
+ %% in the profile deps only, but while accounting for locks.
+ %% Because our job here is to unlock those that have changed, we must
+ %% instead use the atom-based names, both in `deps' and `{deps, default}',
+ %% as those are the dependencies that may have changed but have been
+ %% disregarded by locks.
+ %%
+ %% As such, the working set of dependencies is the addition of
+ %% `deps' and `{deps, default}' entries with an atom name, as those
+ %% disregard locks and parsed values post-selection altogether.
+ %% Packages without versions can of course be a single atom.
+ TopDeps = rebar_state:get(State, deps, []),
+ ProfileDeps = rebar_state:get(State, {deps, default}, []),
+ Deps = [Dep || Dep <- TopDeps ++ ProfileDeps, % TopDeps > ProfileDeps
+ is_atom(Dep) orelse is_atom(element(1, Dep))],
Names = parse_names(ec_cnv:to_binary(proplists:get_value(package, Args, <<"">>)), Locks),
DepsDict = deps_dict(rebar_state:all_deps(State)),
case prepare_locks(Names, Deps, Locks, [], DepsDict) of
diff --git a/src/rebar_state.erl b/src/rebar_state.erl
index a613a00..bdd4aeb 100644
--- a/src/rebar_state.erl
+++ b/src/rebar_state.erl
@@ -417,7 +417,8 @@ create_logic_providers(ProviderModules, State0) ->
catch
C:T ->
?DEBUG("~p: ~p ~p", [C, T, erlang:get_stacktrace()]),
- throw({error, "Failed creating providers. Run with DEBUG=1 for stacktrace."})
+ ?CRASHDUMP("~p: ~p~n~p~n~n~p", [C, T, erlang:get_stacktrace(), State0]),
+ throw({error, "Failed creating providers. Run with DEBUG=1 for stacktrace or consult rebar3.crashdump."})
end.
to_list(#state_t{} = State) ->
diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl
index 2f33bfc..299b957 100644
--- a/src/rebar_templater.erl
+++ b/src/rebar_templater.erl
@@ -326,7 +326,7 @@ find_other_templates(State) ->
undefined ->
[];
TemplateDir ->
- rebar_utils:find_files(TemplateDir, ?TEMPLATE_RE)
+ rebar_utils:find_files(TemplateDir, ?TEMPLATE_RE, true) % recursive
end.
%% Fetch template indexes that sit on disk in plugins
diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl
index 56a3940..aa9e268 100644
--- a/src/rebar_utils.erl
+++ b/src/rebar_utils.erl
@@ -69,7 +69,8 @@
check_blacklisted_otp_versions/1,
info_useless/2,
list_dir/1,
- user_agent/0]).
+ user_agent/0,
+ reread_config/1]).
%% for internal use only
-export([otp_release/0]).
@@ -412,6 +413,17 @@ user_agent() ->
{ok, Vsn} = application:get_key(rebar, vsn),
?FMT("Rebar/~s (OTP/~s)", [Vsn, otp_release()]).
+reread_config(ConfigList) ->
+ try
+ [application:set_env(Application, Key, Val)
+ || Config <- ConfigList,
+ {Application, Items} <- Config,
+ {Key, Val} <- Items]
+ catch _:_ ->
+ ?ERROR("The configuration file submitted could not be read "
+ "and will be ignored.", [])
+ end.
+
%% ====================================================================
%% Internal functions
%% ====================================================================
@@ -789,9 +801,13 @@ maybe_ends_in_comma(H) ->
end.
get_http_vars(Scheme) ->
+ OS = case os:getenv(atom_to_list(Scheme)) of
+ Str when is_list(Str) -> Str;
+ _ -> []
+ end,
GlobalConfigFile = rebar_dir:global_config(),
Config = rebar_config:consult_file(GlobalConfigFile),
- proplists:get_value(Scheme, Config, []).
+ proplists:get_value(Scheme, Config, OS).
set_httpc_options() ->
set_httpc_options(https_proxy, get_http_vars(https_proxy)),
diff --git a/test/mock_pkg_resource.erl b/test/mock_pkg_resource.erl
index a94fe2f..f837713 100644
--- a/test/mock_pkg_resource.erl
+++ b/test/mock_pkg_resource.erl
@@ -22,8 +22,9 @@ mock() -> mock([]).
| {not_in_index, [{App, Vsn}]}
| {pkgdeps, [{{App,Vsn}, [Dep]}]},
App :: string(),
- Dep :: {App, string(), {pkg, App, Vsn}},
- Vsn :: string().
+ Dep :: {App, string(), {pkg, App, Vsn, Hash}},
+ Vsn :: string(),
+ Hash :: string() | undefined.
mock(Opts) ->
meck:new(?MOD, [no_link]),
mock_lock(Opts),
@@ -51,7 +52,7 @@ mock_update(Opts) ->
ToUpdate = proplists:get_value(upgrade, Opts, []),
meck:expect(
?MOD, needs_update,
- fun(_Dir, {pkg, App, _Vsn}) ->
+ fun(_Dir, {pkg, App, _Vsn, _Hash}) ->
lists:member(binary_to_list(App), ToUpdate)
end).
@@ -66,7 +67,7 @@ mock_vsn(_Opts) ->
%% @doc For each app to download, create a dummy app on disk instead.
%% The configuration for this one (passed in from `mock/1') includes:
%%
-%% - Specify a version with `{pkg, _, Vsn}'
+%% - Specify a version with `{pkg, _, Vsn, _}'
%% - Dependencies for each application must be passed of the form:
%% `{pkgdeps, [{"app1", [{app2, ".*", {pkg, ...}}]}]}' -- basically
%% the `pkgdeps' option takes a key/value list of terms to output directly
@@ -76,7 +77,7 @@ mock_download(Opts) ->
Config = proplists:get_value(config, Opts, []),
meck:expect(
?MOD, download,
- fun (Dir, {pkg, AppBin, Vsn}, _) ->
+ fun (Dir, {pkg, AppBin, Vsn, _}, _) ->
App = binary_to_list(AppBin),
filelib:ensure_dir(Dir),
AppDeps = proplists:get_value({App,Vsn}, Deps, []),
diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl
index 76a3de5..d9b75e4 100644
--- a/test/rebar_compile_SUITE.erl
+++ b/test/rebar_compile_SUITE.erl
@@ -23,6 +23,7 @@
paths_extra_dirs_in_project_root/1,
clean_extra_dirs_in_project_root/1,
recompile_when_hrl_changes/1,
+ recompile_when_included_hrl_changes/1,
recompile_when_opts_change/1,
dont_recompile_when_opts_dont_change/1,
dont_recompile_yrl_or_xrl/1,
@@ -40,7 +41,8 @@
profile_override_deps/1,
deps_build_in_prod/1,
include_file_relative_to_working_directory/1,
- include_file_in_src/1]).
+ include_file_in_src/1,
+ always_recompile_when_erl_compiler_options_set/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -55,14 +57,19 @@ all() ->
{group, basic_srcdirs}, {group, release_srcdirs}, {group, unbalanced_srcdirs},
{group, basic_extras}, {group, release_extras}, {group, unbalanced_extras},
{group, root_extras},
- recompile_when_hrl_changes, recompile_when_opts_change,
+ recompile_when_hrl_changes, recompile_when_included_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, highest_version_of_pkg_dep,
parse_transform_test, erl_first_files_test, mib_test,
umbrella_mib_first_test, only_default_transitive_deps,
clean_all, override_deps, profile_override_deps, deps_build_in_prod,
- include_file_relative_to_working_directory, include_file_in_src].
+ include_file_relative_to_working_directory, include_file_in_src] ++
+ case erlang:function_exported(os, unsetenv, 1) of
+ true -> [always_recompile_when_erl_compiler_options_set];
+ false -> []
+ end.
groups() ->
[{basic_app, [], [build_basic_app, paths_basic_app, clean_basic_app]},
@@ -642,7 +649,6 @@ recompile_when_hrl_changes(Config) ->
Vsn = rebar_test_utils:create_random_vsn(),
rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),
-
ExtraSrc = <<"-module(test_header_include).\n"
"-export([main/0]).\n"
"-include(\"test_header_include.hrl\").\n"
@@ -660,10 +666,48 @@ recompile_when_hrl_changes(Config) ->
ModTime = [filelib:last_modified(filename:join([EbinDir, F]))
|| F <- Files, filename:extension(F) == ".beam"],
+ timer:sleep(1000),
+
+ NewExtraHeader = <<"-define(SOME_DEFINE, false).\n">>,
+ ok = file:write_file(HeaderFile, NewExtraHeader),
+
+ rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}),
+
+ {ok, NewFiles} = rebar_utils:list_dir(EbinDir),
+ NewModTime = [filelib:last_modified(filename:join([EbinDir, F]))
+ || F <- NewFiles, filename:extension(F) == ".beam"],
+
+ ?assert(ModTime =/= NewModTime).
+
+recompile_when_included_hrl_changes(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]),
+
+ ExtraSrc = <<"-module(test_header_include).\n"
+ "-export([main/0]).\n"
+ "-include(\"test_header_include.hrl\").\n"
+ "main() -> ?SOME_DEFINE.\n">>,
+
+ ExtraHeader = <<"-define(SOME_DEFINE, true).\n">>,
+ ok = filelib:ensure_dir(filename:join([AppDir, "include", "dummy"])),
+ HeaderFile = filename:join([AppDir, "include", "test_header_include.hrl"]),
+ ok = file:write_file(filename:join([AppDir, "src", "test_header_include.erl"]), ExtraSrc),
+ ok = file:write_file(HeaderFile, ExtraHeader),
+
+ rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}),
+
+ EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]),
+ {ok, Files} = rebar_utils:list_dir(EbinDir),
+ ModTime = [filelib:last_modified(filename:join([EbinDir, F]))
+ || F <- Files, filename:extension(F) == ".beam"],
timer:sleep(1000),
- rebar_file_utils:touch(HeaderFile),
+ NewExtraHeader = <<"-define(SOME_DEFINE, false).\n">>,
+ ok = file:write_file(HeaderFile, NewExtraHeader),
rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}),
@@ -1265,3 +1309,44 @@ include_file_in_src(Config) ->
rebar_test_utils:run_and_check(Config, RebarConfig,
["compile"],
{ok, [{app, Name}]}).
+
+always_recompile_when_erl_compiler_options_set(Config) ->
+ %% save existing env to restore after test
+ ExistingEnv = os:getenv("ERL_COMPILER_OPTIONS"),
+
+ AppDir = ?config(apps, Config),
+
+ Name = rebar_test_utils:create_random_name("erl_compiler_options_"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),
+
+ true = os:unsetenv("ERL_COMPILER_OPTIONS"),
+
+ rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}),
+
+ EbinDir = filename:join([AppDir, "_build", "default", "lib", Name, "ebin"]),
+
+ {ok, Files} = rebar_utils:list_dir(EbinDir),
+ ModTime = [filelib:last_modified(filename:join([EbinDir, F]))
+ || F <- Files, filename:extension(F) == ".beam"],
+
+ timer:sleep(1000),
+
+ true = os:putenv("ERL_COMPILER_OPTIONS", "[{d, some_macro}]"),
+
+ rebar_test_utils:run_and_check(Config, [], ["compile"], {ok, [{app, Name}]}),
+
+ {ok, NewFiles} = rebar_utils:list_dir(EbinDir),
+ NewModTime = [filelib:last_modified(filename:join([EbinDir, F]))
+ || F <- NewFiles, filename:extension(F) == ".beam"],
+
+ ?assert(ModTime =/= NewModTime),
+
+ %% restore existing env
+ case ExistingEnv of
+ false -> ok;
+ _ -> os:putenv("ERL_COMPILER_OPTIONS", ExistingEnv)
+ end.
+
+
+
diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl
index 1da7571..c10875b 100644
--- a/test/rebar_ct_SUITE.erl
+++ b/test/rebar_ct_SUITE.erl
@@ -41,6 +41,7 @@
cmd_scale_timetraps/1,
cmd_create_priv_dir/1,
cmd_include_dir/1,
+ cmd_sys_config/1,
cfg_opts/1,
cfg_arbitrary_opts/1,
cfg_test_spec/1,
@@ -51,6 +52,7 @@
misspecified_ct_compile_opts/1,
misspecified_ct_first_files/1]).
+-include_lib("eunit/include/eunit.hrl").
-include_lib("common_test/include/ct.hrl").
all() -> [{group, basic_app},
@@ -102,7 +104,8 @@ groups() -> [{basic_app, [], [basic_app_default_dirs,
cmd_multiply_timetraps,
cmd_scale_timetraps,
cmd_create_priv_dir,
- cmd_include_dir]},
+ cmd_include_dir,
+ cmd_sys_config]},
{cover, [], [cover_compiled]}].
init_per_group(basic_app, Config) ->
@@ -726,7 +729,7 @@ suite_at_app_root(Config) ->
data_dir_correct(Config) ->
DataDir = ?config(data_dir, Config),
Parts = filename:split(DataDir),
- ["rebar_ct_SUITE_data","test","rebar","lib","test","_build"|_] = lists:reverse(Parts).
+ ["rebar_ct_SUITE_data","test","rebar","lib",_,"_build"|_] = lists:reverse(Parts).
cmd_label(Config) ->
State = ?config(result, Config),
@@ -1020,7 +1023,41 @@ cmd_include_dir(Config) ->
CompileOpts = proplists:get_value(options, Info),
true = lists:member({i, "foo/bar/baz"}, CompileOpts),
true = lists:member({i, "qux"}, CompileOpts).
-
+
+cmd_sys_config(Config) ->
+ State = ?config(result, Config),
+ AppDir = ?config(apps, Config),
+ Name = ?config(name, Config),
+ AppName = list_to_atom(Name),
+
+ {ok, _} = rebar_prv_common_test:prepare_tests(State),
+ ?assertEqual(undefined, application:get_env(AppName, key)),
+
+ CfgFile = filename:join([AppDir, "config", "cfg_sys.config"]),
+ ok = filelib:ensure_dir(CfgFile),
+ ok = file:write_file(CfgFile, cfg_sys_config_file(AppName)),
+ RebarConfig = [{ct_opts, [{sys_config, CfgFile}]}],
+ {ok, State1} = rebar_test_utils:run_and_check(Config, RebarConfig, ["as", "test", "lock"], return),
+
+ {ok, _} = rebar_prv_common_test:prepare_tests(State1),
+ ?assertEqual({ok, cfg_value}, application:get_env(AppName, key)),
+
+ Providers = rebar_state:providers(State1),
+ Namespace = rebar_state:namespace(State1),
+ CommandProvider = providers:get_provider(ct, Providers, Namespace),
+ GetOptSpec = providers:opts(CommandProvider),
+
+ CmdFile = filename:join([AppDir, "config", "cmd_sys.config"]),
+ ok = filelib:ensure_dir(CmdFile),
+ ok = file:write_file(CmdFile, cmd_sys_config_file(AppName)),
+ {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--sys_config="++CmdFile]),
+ State2 = rebar_state:command_parsed_args(State1, GetOptResult),
+
+ {ok, _} = rebar_prv_common_test:prepare_tests(State2),
+
+ ?assertEqual({ok ,cmd_value}, application:get_env(AppName, key)).
+
+
cfg_opts(Config) ->
C = rebar_test_utils:init_rebar_state(Config, "ct_cfg_opts_"),
@@ -1181,10 +1218,15 @@ misspecified_ct_first_files(Config) ->
{badconfig, {"Value `~p' of option `~p' must be a list", {some_file, ct_first_files}}} = Error.
-
%% helper for generating test data
test_suite(Name) ->
io_lib:format("-module(~ts_SUITE).\n"
"-compile(export_all).\n"
"all() -> [some_test].\n"
"some_test(_) -> ok.\n", [Name]).
+
+cmd_sys_config_file(AppName) ->
+ io_lib:format("[{~s, [{key, cmd_value}]}].", [AppName]).
+
+cfg_sys_config_file(AppName) ->
+ io_lib:format("[{~s, [{key, cfg_value}]}].", [AppName]).
diff --git a/test/rebar_deps_SUITE.erl b/test/rebar_deps_SUITE.erl
index c95854a..24bf2a0 100644
--- a/test/rebar_deps_SUITE.erl
+++ b/test/rebar_deps_SUITE.erl
@@ -3,7 +3,11 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
-all() -> [sub_app_deps, newly_added_dep, newly_added_after_empty_lock, http_proxy_settings, https_proxy_settings, semver_matching_lt, semver_matching_lte, semver_matching_gt, valid_version, {group, git}, {group, pkg}].
+all() -> [sub_app_deps, newly_added_dep, newly_added_after_empty_lock,
+ http_proxy_settings, https_proxy_settings,
+ http_os_proxy_settings, https_os_proxy_settings,
+ semver_matching_lt, semver_matching_lte, semver_matching_gt,
+ valid_version, {group, git}, {group, pkg}].
groups() ->
[{all, [], [flat, pick_highest_left, pick_highest_right,
@@ -82,6 +86,47 @@ init_per_testcase(https_proxy_settings, Config) ->
rebar_test_utils:create_config(GlobalConfigDir,
[{https_proxy, "http://localhost:1234"}
]),
+ %% Add a bad value by default to show config overtakes default
+ os:putenv("https_proxy", "unparseable-garbage"),
+ rebar_test_utils:init_rebar_state(Config)
+ end;
+init_per_testcase(http_os_proxy_settings, Config) ->
+ %% Create private rebar.config
+ Priv = ?config(priv_dir, Config),
+ GlobalDir = filename:join(Priv, "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),
+
+ %% Insert proxy variables into os env, but not config
+ os:putenv("http_proxy", "http://localhost:1234"),
+ rebar_test_utils:create_config(GlobalConfigDir, []),
+ rebar_test_utils:init_rebar_state(Config);
+init_per_testcase(https_os_proxy_settings, Config) ->
+ SupportsHttpsProxy = case erlang:system_info(otp_release) of
+ "R16"++_ -> true;
+ "R"++_ -> false;
+ _ -> true % 17 and up don't have a "R" in the version
+ end,
+ if not SupportsHttpsProxy ->
+ {skip, https_proxy_unsupported_before_R16};
+ SupportsHttpsProxy ->
+ %% Create private rebar.config
+ Priv = ?config(priv_dir, Config),
+ GlobalDir = filename:join(Priv, "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),
+
+ %% Insert proxy variables into os env, not in config
+ os:putenv("https_proxy", "http://localhost:1234"),
+ rebar_test_utils:create_config(GlobalConfigDir, []),
rebar_test_utils:init_rebar_state(Config)
end;
init_per_testcase(Case, Config) ->
@@ -97,11 +142,20 @@ init_per_testcase(Case, Config) ->
| setup_project(Case, Config, rebar_test_utils:expand_deps(DepsType, Deps))].
end_per_testcase(https_proxy_settings, Config) ->
+ os:putenv("https_proxy", ""),
meck:unload(rebar_dir),
Config;
end_per_testcase(http_proxy_settings, Config) ->
meck:unload(rebar_dir),
Config;
+end_per_testcase(http_os_proxy_settings, Config) ->
+ os:putenv("http_proxy", ""),
+ meck:unload(rebar_dir),
+ Config;
+end_per_testcase(https_os_proxy_settings, Config) ->
+ os:putenv("https_proxy", ""),
+ meck:unload(rebar_dir),
+ Config;
end_per_testcase(_, Config) ->
meck:unload(),
Config.
@@ -311,6 +365,23 @@ https_proxy_settings(_Config) ->
?assertEqual({ok,{{"localhost", 1234}, []}},
httpc:get_option(https_proxy, rebar)).
+http_os_proxy_settings(_Config) ->
+ %% Load config
+ rebar_utils:set_httpc_options(),
+ rebar3:init_config(),
+
+ %% Assert variable is right
+ ?assertEqual({ok,{{"localhost", 1234}, []}},
+ httpc:get_option(proxy, rebar)).
+
+https_os_proxy_settings(_Config) ->
+ %% Load config
+ rebar_utils:set_httpc_options(),
+ rebar3:init_config(),
+
+ %% Assert variable is right
+ ?assertEqual({ok,{{"localhost", 1234}, []}},
+ httpc:get_option(https_proxy, rebar)).
semver_matching_lt(_Config) ->
Dep = <<"test">>,
@@ -405,5 +476,5 @@ in_warnings(git, Warns, NameRaw, VsnRaw) ->
in_warnings(pkg, Warns, NameRaw, VsnRaw) ->
Name = iolist_to_binary(NameRaw),
Vsn = iolist_to_binary(VsnRaw),
- 1 =< length([1 || {_, [AppName, {pkg, _, AppVsn}]} <- Warns,
+ 1 =< length([1 || {_, [AppName, {pkg, _, AppVsn, _}]} <- Warns,
AppName =:= Name, AppVsn =:= Vsn]).
diff --git a/test/rebar_dialyzer_SUITE.erl b/test/rebar_dialyzer_SUITE.erl
index 22a4894..e5d8c52 100644
--- a/test/rebar_dialyzer_SUITE.erl
+++ b/test/rebar_dialyzer_SUITE.erl
@@ -3,8 +3,14 @@
-export([suite/0,
init_per_suite/1,
end_per_suite/1,
+ init_per_group/2,
+ end_per_group/2,
init_per_testcase/2,
all/0,
+ groups/0,
+ empty_base_plt/1,
+ empty_app_plt/1,
+ empty_app_succ_typings/1,
update_base_plt/1,
update_app_plt/1,
build_release_plt/1,
@@ -23,6 +29,14 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
ok.
+init_per_group(empty, Config) ->
+ [{base_plt_apps, []} | Config];
+init_per_group(_Group, Config) ->
+ [{base_plt_apps, [erts]} | Config].
+
+end_per_group(_Group, _Config) ->
+ ok.
+
init_per_testcase(Testcase, Config) ->
PrivDir = ?config(priv_dir, Config),
Prefix = ec_cnv:to_list(Testcase),
@@ -31,7 +45,7 @@ init_per_testcase(Testcase, Config) ->
{plt_location, PrivDir},
{base_plt_prefix, BasePrefix},
{base_plt_location, PrivDir},
- {base_plt_apps, [erts]}],
+ {base_plt_apps, ?config(base_plt_apps, Config)}],
Suffix = "_" ++ rebar_utils:otp_release() ++ "_plt",
[{plt, filename:join(PrivDir, Prefix ++ Suffix)},
{base_plt, filename:join(PrivDir, BasePrefix ++ Suffix)},
@@ -39,7 +53,68 @@ init_per_testcase(Testcase, Config) ->
rebar_test_utils:init_rebar_state(Config)].
all() ->
- [update_base_plt, update_app_plt, build_release_plt, plt_apps_option].
+ [{group, empty}, {group, build_and_check}, {group, update}].
+
+groups() ->
+ [{empty, [empty_base_plt, empty_app_plt, empty_app_succ_typings]},
+ {build_and_check, [build_release_plt, plt_apps_option]},
+ {update, [update_base_plt, update_app_plt]}].
+
+empty_base_plt(Config) ->
+ AppDir = ?config(apps, Config),
+ RebarConfig = ?config(rebar_config, Config),
+ BasePlt = ?config(base_plt, Config),
+ Plt = ?config(plt, Config),
+
+ Name = rebar_test_utils:create_random_name("app1_"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ rebar_test_utils:create_app(AppDir, Name, Vsn, [erts]),
+
+ rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"],
+ {ok, [{app, Name}]}),
+
+ {ok, BasePltFiles} = plt_files(BasePlt),
+ ?assertEqual([], BasePltFiles),
+
+ ErtsFiles = erts_files(),
+ {ok, PltFiles} = plt_files(Plt),
+ ?assertEqual(ErtsFiles, PltFiles),
+
+ ok.
+
+empty_app_plt(Config) ->
+ AppDir = ?config(apps, Config),
+ RebarConfig = ?config(rebar_config, Config),
+ BasePlt = ?config(base_plt, Config),
+ Plt = ?config(plt, Config),
+
+ Name = rebar_test_utils:create_random_name("app1_"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ rebar_test_utils:create_app(AppDir, Name, Vsn, []),
+
+ rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"],
+ {ok, [{app, Name}]}),
+
+ {ok, BasePltFiles} = plt_files(BasePlt),
+ ?assertEqual([], BasePltFiles),
+
+ {ok, PltFiles} = plt_files(Plt),
+ ?assertEqual([], PltFiles),
+
+ ok.
+
+empty_app_succ_typings(Config) ->
+ AppDir = ?config(apps, Config),
+ RebarConfig = ?config(rebar_config, Config),
+
+ Name = rebar_test_utils:create_random_name("app1_"),
+ Vsn = rebar_test_utils:create_random_vsn(),
+ rebar_test_utils:create_empty_app(AppDir, Name, Vsn, []),
+
+ rebar_test_utils:run_and_check(Config, RebarConfig, ["dialyzer"],
+ {ok, [{app, Name}]}),
+
+ ok.
update_base_plt(Config) ->
AppDir = ?config(apps, Config),
diff --git a/test/rebar_edoc_SUITE.erl b/test/rebar_edoc_SUITE.erl
new file mode 100644
index 0000000..fded2b0
--- /dev/null
+++ b/test/rebar_edoc_SUITE.erl
@@ -0,0 +1,52 @@
+-module(rebar_edoc_SUITE).
+-include_lib("common_test/include/ct.hrl").
+-include_lib("eunit/include/eunit.hrl").
+-compile(export_all).
+
+all() -> [multiapp].
+
+init_per_testcase(multiapp, Config) ->
+ application:load(rebar),
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ Name = rebar_test_utils:create_random_name("multiapp"),
+ AppsDir = filename:join([PrivDir, rebar_test_utils:create_random_name(Name)]),
+ ec_file:copy(filename:join([DataDir, "foo"]), AppsDir, [recursive]),
+ Verbosity = rebar3:log_level(),
+ rebar_log:init(command_line, Verbosity),
+ State = rebar_state:new([{base_dir, filename:join([AppsDir, "_build"])}
+ ,{root_dir, AppsDir}]),
+ [{apps, AppsDir}, {state, State}, {name, Name} | Config].
+
+end_per_testcase(_, Config) ->
+ Config.
+
+multiapp(Config) ->
+ %% With an empty config (no `dir'), links are being processed
+ RebarConfig = [],
+ rebar_test_utils:run_and_check(Config, RebarConfig, ["edoc"], {ok, []}),
+ %% validate that all doc entries are generated and links work
+ AppsDir = ?config(apps, Config),
+ ct:pal("AppsDir: ~s", [AppsDir]),
+ ?assert(file_content_matches(
+ filename:join([AppsDir, "apps", "bar1", "doc", "bar1.html"]),
+ "barer1")),
+ ?assert(file_content_matches(
+ filename:join([AppsDir, "apps", "bar2", "doc", "bar2.html"]),
+ "barer2")),
+ %% Links are in place for types
+ ?assert(file_content_matches(
+ filename:join([AppsDir, "apps", "foo", "doc", "foo.html"]),
+ "barer1")),
+ ?assert(file_content_matches(
+ filename:join([AppsDir, "apps", "foo", "doc", "foo.html"]),
+ "apps/bar1/doc/bar1.html")).
+
+
+file_content_matches(Path, Regex) ->
+ case file:read_file(Path) of
+ {ok, Bin} ->
+ nomatch =/= re:run(Bin, Regex);
+ {error, Reason} ->
+ Reason
+ end.
diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.app.src b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.app.src
new file mode 100644
index 0000000..6e7ec24
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.app.src
@@ -0,0 +1,16 @@
+{application, bar1,
+ [{description, "An OTP application"},
+ {vsn, "0.1.0"},
+ {registered, []},
+ {mod, { bar1_app, []}},
+ {applications,
+ [kernel,
+ stdlib
+ ]},
+ {env,[]},
+ {modules, []},
+
+ {maintainers, []},
+ {licenses, []},
+ {links, []}
+ ]}.
diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.erl b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.erl
new file mode 100644
index 0000000..2700aef
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1.erl
@@ -0,0 +1,9 @@
+-module(bar1).
+-export([bar1/0]).
+-export_type([barer1/0]).
+
+-type barer1() :: string().
+
+% @doc Bar1 bars the bar.
+-spec bar1() -> barer1().
+bar1() -> "Barer1". \ No newline at end of file
diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_app.erl b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_app.erl
new file mode 100644
index 0000000..414ac30
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_app.erl
@@ -0,0 +1,26 @@
+%%%-------------------------------------------------------------------
+%% @doc bar1 public API
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(bar1_app).
+
+-behaviour(application).
+
+%% Application callbacks
+-export([start/2, stop/1]).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+start(_StartType, _StartArgs) ->
+ bar1_sup:start_link().
+
+%%--------------------------------------------------------------------
+stop(_State) ->
+ ok.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_sup.erl b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_sup.erl
new file mode 100644
index 0000000..f9d6670
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/foo/apps/bar1/src/bar1_sup.erl
@@ -0,0 +1,35 @@
+%%%-------------------------------------------------------------------
+%% @doc bar1 top level supervisor.
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(bar1_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+-define(SERVER, ?MODULE).
+
+%%====================================================================
+%% API functions
+%%====================================================================
+
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+%%====================================================================
+%% Supervisor callbacks
+%%====================================================================
+
+%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}
+init([]) ->
+ {ok, { {one_for_all, 0, 1}, []} }.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.app.src b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.app.src
new file mode 100644
index 0000000..58de8bc
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.app.src
@@ -0,0 +1,16 @@
+{application, bar2,
+ [{description, "An OTP application"},
+ {vsn, "0.1.0"},
+ {registered, []},
+ {mod, { bar2_app, []}},
+ {applications,
+ [kernel,
+ stdlib
+ ]},
+ {env,[]},
+ {modules, []},
+
+ {maintainers, []},
+ {licenses, []},
+ {links, []}
+ ]}.
diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.erl b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.erl
new file mode 100644
index 0000000..c639db0
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2.erl
@@ -0,0 +1,9 @@
+-module(bar2).
+-export([bar2/0]).
+-export_type([barer2/0]).
+
+-type barer2() :: string().
+
+% @doc Bar2 bars the bar2.
+-spec bar2() -> barer2().
+bar2() -> "Barer2". \ No newline at end of file
diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_app.erl b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_app.erl
new file mode 100644
index 0000000..d0058a0
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_app.erl
@@ -0,0 +1,26 @@
+%%%-------------------------------------------------------------------
+%% @doc bar2 public API
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(bar2_app).
+
+-behaviour(application).
+
+%% Application callbacks
+-export([start/2, stop/1]).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+start(_StartType, _StartArgs) ->
+ bar2_sup:start_link().
+
+%%--------------------------------------------------------------------
+stop(_State) ->
+ ok.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_sup.erl b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_sup.erl
new file mode 100644
index 0000000..0bdaf4a
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/foo/apps/bar2/src/bar2_sup.erl
@@ -0,0 +1,35 @@
+%%%-------------------------------------------------------------------
+%% @doc bar2 top level supervisor.
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(bar2_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+-define(SERVER, ?MODULE).
+
+%%====================================================================
+%% API functions
+%%====================================================================
+
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+%%====================================================================
+%% Supervisor callbacks
+%%====================================================================
+
+%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}
+init([]) ->
+ {ok, { {one_for_all, 0, 1}, []} }.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.app.src b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.app.src
new file mode 100644
index 0000000..9987fd5
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.app.src
@@ -0,0 +1,17 @@
+{application, foo,
+ [{description, "An OTP application"},
+ {vsn, "0.1.0"},
+ {registered, []},
+ {mod, { foo_app, []}},
+ {applications,
+ [kernel,
+ stdlib,
+ bar1, bar2
+ ]},
+ {env,[]},
+ {modules, []},
+
+ {maintainers, []},
+ {licenses, []},
+ {links, []}
+ ]}.
diff --git a/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.erl b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.erl
new file mode 100644
index 0000000..52e3d0a
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo.erl
@@ -0,0 +1,19 @@
+-module(foo).
+
+-export([foo/0, bar1/0, bar2/0]).
+
+-export_type([fooer/0]).
+
+-type fooer() :: string().
+
+% @doc Foo function returns fooer.
+-spec foo() -> fooer().
+foo() -> "fooer".
+
+% @doc Bar1 function returns barer1.
+-spec bar1() -> bar1:barer1().
+bar1() -> bar1:bar1().
+
+% @doc Bar2 functions returns barer2.
+-spec bar2() -> bar2:barer2().
+bar2() -> bar2:bar2(). \ No newline at end of file
diff --git a/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_app.erl b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_app.erl
new file mode 100644
index 0000000..d0158d7
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_app.erl
@@ -0,0 +1,26 @@
+%%%-------------------------------------------------------------------
+%% @doc foo public API
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(foo_app).
+
+-behaviour(application).
+
+%% Application callbacks
+-export([start/2, stop/1]).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+start(_StartType, _StartArgs) ->
+ foo_sup:start_link().
+
+%%--------------------------------------------------------------------
+stop(_State) ->
+ ok.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_sup.erl b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_sup.erl
new file mode 100644
index 0000000..67e88b4
--- /dev/null
+++ b/test/rebar_edoc_SUITE_data/foo/apps/foo/src/foo_sup.erl
@@ -0,0 +1,35 @@
+%%%-------------------------------------------------------------------
+%% @doc foo top level supervisor.
+%% @end
+%%%-------------------------------------------------------------------
+
+-module(foo_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+-define(SERVER, ?MODULE).
+
+%%====================================================================
+%% API functions
+%%====================================================================
+
+start_link() ->
+ supervisor:start_link({local, ?SERVER}, ?MODULE, []).
+
+%%====================================================================
+%% Supervisor callbacks
+%%====================================================================
+
+%% Child :: {Id,StartFunc,Restart,Shutdown,Type,Modules}
+init([]) ->
+ {ok, { {one_for_all, 0, 1}, []} }.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/test/rebar_escriptize_SUITE.erl b/test/rebar_escriptize_SUITE.erl
index 1817d6b..139d5cd 100644
--- a/test/rebar_escriptize_SUITE.erl
+++ b/test/rebar_escriptize_SUITE.erl
@@ -5,6 +5,8 @@
end_per_suite/1,
init_per_testcase/2,
all/0,
+ escriptize_with_name/1,
+ escriptize_with_bad_name/1,
build_and_clean_app/1]).
-include_lib("common_test/include/ct.hrl").
@@ -24,7 +26,11 @@ init_per_testcase(_, Config) ->
rebar_test_utils:init_rebar_state(Config).
all() ->
- [build_and_clean_app].
+ [
+ build_and_clean_app,
+ escriptize_with_name,
+ escriptize_with_bad_name
+ ].
%% Test escriptize builds and runs the app's escript
build_and_clean_app(Config) ->
@@ -35,3 +41,21 @@ build_and_clean_app(Config) ->
rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]),
rebar_test_utils:run_and_check(Config, [], ["escriptize"],
{ok, [{app, Name, valid}]}).
+
+escriptize_with_name(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]),
+ rebar_test_utils:run_and_check(Config, [{escript_main_app, Name}], ["escriptize"],
+ {ok, [{app, Name, valid}]}).
+
+escriptize_with_bad_name(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]),
+ rebar_test_utils:run_and_check(Config, [{escript_main_app, boogers}], ["escriptize"],
+ {error,{rebar_prv_escriptize, {bad_name, boogers}}}).
diff --git a/test/rebar_file_utils_SUITE.erl b/test/rebar_file_utils_SUITE.erl
index c1f85b3..a44a06d 100644
--- a/test/rebar_file_utils_SUITE.erl
+++ b/test/rebar_file_utils_SUITE.erl
@@ -12,7 +12,9 @@
reset_empty_dir/1,
reset_dir/1,
path_from_ancestor/1,
- canonical_path/1]).
+ canonical_path/1,
+ resolve_link/1,
+ split_dirname/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -22,7 +24,10 @@
all() ->
[{group, tmpdir},
{group, reset_dir},
- path_from_ancestor, canonical_path].
+ path_from_ancestor,
+ canonical_path,
+ resolve_link,
+ split_dirname].
groups() ->
[{tmpdir, [], [raw_tmpdir, empty_tmpdir, simple_tmpdir, multi_tmpdir]},
@@ -111,3 +116,22 @@ canonical_path(_Config) ->
?assertEqual(Root ++ "foo", rebar_file_utils:canonical_path("/foo/./.")),
?assertEqual(filename:nativename(Root ++ "foo/bar"),
rebar_file_utils:canonical_path("/foo/./bar")).
+
+resolve_link(_Config) ->
+ TmpDir = rebar_file_utils:system_tmpdir(
+ ["rebar_file_utils_SUITE", "resolve_link"]),
+ Link = filename:join(TmpDir, "link"),
+ Target = filename:join(TmpDir, "link-target"),
+ ec_file:remove(TmpDir, [recursive]),
+ ok = filelib:ensure_dir(Target),
+ ok = file:write_file(Target, <<>>),
+ ok = file:make_symlink(Target, Link),
+ ?assertEqual(Target, rebar_file_utils:resolve_link(Link)).
+
+split_dirname(_Config) ->
+ ?assertEqual({".", ""}, rebar_file_utils:split_dirname("")),
+ ?assertEqual({"/", ""}, rebar_file_utils:split_dirname("/")),
+ ?assertEqual({"/", "foo"}, rebar_file_utils:split_dirname("/foo")),
+ ?assertEqual({".", "foo"}, rebar_file_utils:split_dirname("foo")),
+ ?assertEqual({"/foo", "bar"}, rebar_file_utils:split_dirname("/foo/bar")),
+ ?assertEqual({"foo", "bar"}, rebar_file_utils:split_dirname("foo/bar")).
diff --git a/test/rebar_install_deps_SUITE.erl b/test/rebar_install_deps_SUITE.erl
index b8b70b3..9ff28c7 100644
--- a/test/rebar_install_deps_SUITE.erl
+++ b/test/rebar_install_deps_SUITE.erl
@@ -475,5 +475,5 @@ in_warnings(git, Warns, NameRaw, VsnRaw) ->
in_warnings(pkg, Warns, NameRaw, VsnRaw) ->
Name = iolist_to_binary(NameRaw),
Vsn = iolist_to_binary(VsnRaw),
- 1 =< length([1 || {_, [AppName, {pkg, _, AppVsn}]} <- Warns,
+ 1 =< length([1 || {_, [AppName, {pkg, _, AppVsn, _}]} <- Warns,
AppName =:= Name, AppVsn =:= Vsn]).
diff --git a/test/rebar_lock_SUITE.erl b/test/rebar_lock_SUITE.erl
index 00875f7..f1ab3b5 100644
--- a/test/rebar_lock_SUITE.erl
+++ b/test/rebar_lock_SUITE.erl
@@ -7,7 +7,8 @@
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
-all() -> [current_version, future_versions_no_attrs, future_versions_attrs].
+all() -> [current_version,
+ beta_version, future_versions_no_attrs, future_versions_attrs].
current_version(Config) ->
%% Current version just dumps the locks as is on disk.
@@ -15,9 +16,60 @@ current_version(Config) ->
Locks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2},
{<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0},
{<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1},
+ {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>},3},
+ {<<"pkg2">>,{pkg,<<"name1">>,<<"1.1.6">>},2},
+ {<<"pkg3">>,{pkg,<<"name2">>,<<"3.0.6">>},1}
+ ],
+ ExpandedNull = [
+ {<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2},
+ {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0},
+ {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1},
+ {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>,undefined},3},
+ {<<"pkg2">>,{pkg,<<"name1">>,<<"1.1.6">>,undefined},2},
+ {<<"pkg3">>,{pkg,<<"name2">>,<<"3.0.6">>,undefined},1}
+ ],
+ %% Simulate a beta lockfile
+ file:write_file(LockFile, io_lib:format("~p.~n", [Locks])),
+ %% No properties fetched from a beta lockfile, expand locks
+ %% to undefined
+ ?assertEqual(ExpandedNull,
+ rebar_config:consult_lock_file(LockFile)),
+ %% Adding hash data
+ Hashes = [{<<"pkg1">>, <<"tarballhash">>},
+ {<<"pkg3">>, <<"otherhash">>}],
+ ExpandedLocks = [
+ {<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2},
+ {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0},
+ {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1},
+ {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>,<<"tarballhash">>},3},
+ {<<"pkg2">>,{pkg,<<"name1">>,<<"1.1.6">>,undefined},2},
+ {<<"pkg3">>,{pkg,<<"name2">>,<<"3.0.6">>,<<"otherhash">>},1}
+ ],
+ file:write_file(LockFile,
+ io_lib:format("~p.~n~p.~n",
+ [{"1.1.0", Locks},
+ [{pkg_hash, Hashes}]])),
+ ?assertEqual(ExpandedLocks, rebar_config:consult_lock_file(LockFile)),
+ %% Then check that we can reverse that
+ ok = rebar_config:write_lock_file(LockFile, ExpandedLocks),
+ ?assertEqual({ok, [{"1.1.0", Locks}, [{pkg_hash, Hashes}]]},
+ file:consult(LockFile)).
+
+beta_version(Config) ->
+ %% Current version just dumps the locks as is on disk.
+ LockFile = filename:join(?config(priv_dir, Config), "current_version"),
+ Locks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2},
+ {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0},
+ {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1},
{<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>},3}],
+ ExpandedLocks = [
+ {<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2},
+ {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0},
+ {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1},
+ {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>,undefined},3}
+ ],
file:write_file(LockFile, io_lib:format("~p.~n", [Locks])),
- ?assertEqual(Locks, rebar_config:consult_lock_file(LockFile)).
+ ?assertEqual(ExpandedLocks, rebar_config:consult_lock_file(LockFile)).
future_versions_no_attrs(Config) ->
%% Future versions will keep the same core attribute in there, but
@@ -27,10 +79,14 @@ future_versions_no_attrs(Config) ->
Locks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2},
{<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0},
{<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1},
- {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>},3}],
+ {<<"pkg1">>, {pkg,<<"name">>,<<"0.1.6">>},3}],
+ ExpandedLocks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2},
+ {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0},
+ {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1},
+ {<<"pkg1">>, {pkg,<<"name">>,<<"0.1.6">>,undefined},3}],
LockData = {"3.5.2", Locks},
file:write_file(LockFile, io_lib:format("~p.~n", [LockData])),
- ?assertEqual(Locks, rebar_config:consult_lock_file(LockFile)).
+ ?assertEqual(ExpandedLocks, rebar_config:consult_lock_file(LockFile)).
future_versions_attrs(Config) ->
%% Future versions will keep the same core attribute in there, but
@@ -41,6 +97,16 @@ future_versions_attrs(Config) ->
{<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0},
{<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1},
{<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>},3}],
+ ExpandedLocks = [{<<"app1">>, {git,"some_url", {ref,"some_ref"}}, 2},
+ {<<"app2">>, {git,"some_url", {ref,"some_ref"}}, 0},
+ {<<"app3">>, {hg,"some_url", {ref,"some_ref"}}, 1},
+ {<<"pkg1">>,{pkg,<<"name">>,<<"0.1.6">>, <<"tarballhash">>},3}],
+ Hashes = [{<<"pkg1">>, <<"tarballhash">>}],
LockData = {"3.5.2", Locks},
- file:write_file(LockFile, io_lib:format("~p.~na.~n{b,c}.~n[d,e,f].~n", [LockData])),
- ?assertEqual(Locks, rebar_config:consult_lock_file(LockFile)).
+ file:write_file(LockFile,
+ io_lib:format("~p.~n~p.~ngarbage.~n",
+ [LockData,
+ [{a, x},
+ {pkg_hash, Hashes},
+ {b, y}]])),
+ ?assertEqual(ExpandedLocks, rebar_config:consult_lock_file(LockFile)).
diff --git a/test/rebar_pkg_SUITE.erl b/test/rebar_pkg_SUITE.erl
index 6a75f32..30cc0a8 100644
--- a/test/rebar_pkg_SUITE.erl
+++ b/test/rebar_pkg_SUITE.erl
@@ -11,6 +11,7 @@
-define(good_checksum, <<"1C6CE379D191FBAB41B7905075E0BF87CBBE23C77CECE775C5A0B786B2244C35">>).
all() -> [good_uncached, good_cached, badindexchk, badpkg,
+ badhash_nocache, badhash_cache,
bad_to_good, good_disconnect, bad_disconnect, pkgs_provider,
find_highest_matching].
@@ -58,6 +59,19 @@ init_per_testcase(badpkg=Name, Config0) ->
{pkg, {<<"badpkg">>, <<"1.0.0">>}}
| Config0],
mock_config(Name, Config);
+init_per_testcase(badhash_nocache=Name, Config0) ->
+ Config = [{good_cache, false},
+ {pkg, {<<"goodpkg">>, <<"1.0.0">>}}
+ | Config0],
+ mock_config(Name, Config);
+init_per_testcase(badhash_cache=Name, Config0) ->
+ Pkg = {<<"goodpkg">>, <<"1.0.0">>},
+ Config1 = [{good_cache, true},
+ {pkg, Pkg}
+ | Config0],
+ Config = mock_config(Name, Config1),
+ copy_to_cache(Pkg, Config),
+ Config;
init_per_testcase(bad_to_good=Name, Config0) ->
Config1 = [{good_cache, false},
{pkg, {<<"goodpkg">>, <<"1.0.0">>}}
@@ -103,7 +117,7 @@ good_uncached(Config) ->
{Pkg,Vsn} = ?config(pkg, Config),
State = ?config(state, Config),
?assertEqual({ok, true},
- rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn}, State)),
+ rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum}, State)),
Cache = ?config(cache_dir, Config),
?assert(filelib:is_regular(filename:join(Cache, <<Pkg/binary, "-", Vsn/binary, ".tar">>))).
@@ -116,7 +130,7 @@ good_cached(Config) ->
?assert(filelib:is_regular(CachedFile)),
{ok, Content} = file:read_file(CachedFile),
?assertEqual({ok, true},
- rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn}, State)),
+ rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum}, State)),
{ok, Content} = file:read_file(CachedFile).
badindexchk(Config) ->
@@ -124,7 +138,7 @@ badindexchk(Config) ->
{Pkg,Vsn} = ?config(pkg, Config),
State = ?config(state, Config),
?assertMatch({bad_registry_checksum, _Path},
- rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn}, State)),
+ rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum}, State)),
%% The cached file is there for forensic purposes
Cache = ?config(cache_dir, Config),
?assert(filelib:is_regular(filename:join(Cache, <<Pkg/binary, "-", Vsn/binary, ".tar">>))).
@@ -134,11 +148,35 @@ badpkg(Config) ->
{Pkg,Vsn} = ?config(pkg, Config),
State = ?config(state, Config),
?assertMatch({bad_download, _Path},
- rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn}, State)),
+ rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum}, State)),
%% The cached file is there for forensic purposes
Cache = ?config(cache_dir, Config),
?assert(filelib:is_regular(filename:join(Cache, <<Pkg/binary, "-", Vsn/binary, ".tar">>))).
+badhash_nocache(Config) ->
+ Tmp = ?config(tmp_dir, Config),
+ {Pkg,Vsn} = ?config(pkg, Config),
+ State = ?config(state, Config),
+ ?assertMatch({unexpected_hash, _Path, ?bad_checksum, ?good_checksum},
+ rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?bad_checksum}, State)),
+ %% The cached file is there for forensic purposes
+ Cache = ?config(cache_dir, Config),
+ ?assert(filelib:is_regular(filename:join(Cache, <<Pkg/binary, "-", Vsn/binary, ".tar">>))).
+
+badhash_cache(Config) ->
+ Tmp = ?config(tmp_dir, Config),
+ {Pkg,Vsn} = ?config(pkg, Config),
+ Cache = ?config(cache_dir, Config),
+ State = ?config(state, Config),
+ CachedFile = filename:join(Cache, <<Pkg/binary, "-", Vsn/binary, ".tar">>),
+ ?assert(filelib:is_regular(CachedFile)),
+ {ok, Content} = file:read_file(CachedFile),
+ ?assertMatch({unexpected_hash, _Path, ?bad_checksum, ?good_checksum},
+ rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?bad_checksum}, State)),
+ %% The cached file is there still, unchanged.
+ ?assert(filelib:is_regular(CachedFile)),
+ ?assertEqual({ok, Content}, file:read_file(CachedFile)).
+
bad_to_good(Config) ->
Tmp = ?config(tmp_dir, Config),
{Pkg,Vsn} = ?config(pkg, Config),
@@ -148,7 +186,7 @@ bad_to_good(Config) ->
?assert(filelib:is_regular(CachedFile)),
{ok, Contents} = file:read_file(CachedFile),
?assertEqual({ok, true},
- rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn}, State)),
+ rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum}, State)),
%% Cache has refreshed
?assert({ok, Contents} =/= file:read_file(CachedFile)).
@@ -161,7 +199,7 @@ good_disconnect(Config) ->
?assert(filelib:is_regular(CachedFile)),
{ok, Content} = file:read_file(CachedFile),
?assertEqual({ok, true},
- rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn}, State)),
+ rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum}, State)),
{ok, Content} = file:read_file(CachedFile).
bad_disconnect(Config) ->
@@ -169,7 +207,7 @@ bad_disconnect(Config) ->
{Pkg,Vsn} = ?config(pkg, Config),
State = ?config(state, Config),
?assertEqual({fetch_fail, Pkg, Vsn},
- rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn}, State)).
+ rebar_pkg_resource:download(Tmp, {pkg, Pkg, Vsn, ?good_checksum}, State)).
pkgs_provider(Config) ->
Config1 = rebar_test_utils:init_rebar_state(Config),
diff --git a/test/rebar_pkg_alias_SUITE.erl b/test/rebar_pkg_alias_SUITE.erl
index fef2310..8915357 100644
--- a/test/rebar_pkg_alias_SUITE.erl
+++ b/test/rebar_pkg_alias_SUITE.erl
@@ -55,7 +55,7 @@ diff_alias(Config) ->
Config, RebarConfig, ["lock"],
{ok, [{lock, "fakelib"},{dep, "fakelib"}]}
),
- {ok, [LockData]} = file:consult(Lockfile),
+ {ok, [{_Vsn, LockData}|_]} = file:consult(Lockfile),
?assert(lists:any(fun({<<"fakelib">>,{pkg,<<"goodpkg">>,_},_}) -> true
; (_) -> false end, LockData)),
%% An second run yields the same
@@ -63,13 +63,13 @@ diff_alias(Config) ->
Config, RebarConfig, ["lock"],
{ok, [{lock, "fakelib"},{dep, "fakelib"}]}
),
- {ok, [LockData]} = file:consult(Lockfile),
+ {ok, [{_Vsn, LockData}|_]} = file:consult(Lockfile),
%% So does an upgrade
rebar_test_utils:run_and_check(
Config, RebarConfig, ["upgrade"],
{ok, [{lock, "fakelib"},{dep, "fakelib"}]}
),
- {ok, [LockData]} = file:consult(Lockfile).
+ {ok, [{_Vsn, LockData}|_]} = file:consult(Lockfile).
diff_alias_vsn(Config) -> diff_alias(Config).
diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl
index 23b0178..8c177c9 100644
--- a/test/rebar_test_utils.erl
+++ b/test/rebar_test_utils.erl
@@ -7,6 +7,13 @@
create_config/2, create_config/3, package_app/3]).
-export([create_random_name/1, create_random_vsn/0, write_src_file/2]).
+%% Pick the right random module
+-ifdef(rand_only).
+-define(random, rand).
+-else.
+-define(random, random).
+-endif.
+
%%%%%%%%%%%%%%
%%% Public %%%
%%%%%%%%%%%%%%
@@ -126,20 +133,24 @@ create_config(_AppDir, ConfFilename, Contents) ->
%% @doc Util to create a random variation of a given name.
create_random_name(Name) ->
random_seed(),
- Name ++ erlang:integer_to_list(random:uniform(1000000)).
+ Name ++ erlang:integer_to_list(?random:uniform(1000000)).
%% @doc Util to create a random variation of a given version.
create_random_vsn() ->
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))]).
+ lists:flatten([erlang:integer_to_list(?random:uniform(100)),
+ ".", erlang:integer_to_list(?random:uniform(100)),
+ ".", erlang:integer_to_list(?random:uniform(100))]).
+-ifdef(rand_only).
+random_seed() ->
+ %% the rand module self-seeds
+ ok.
+-else.
random_seed() ->
<<A:32, B:32, C:32>> = crypto:rand_bytes(12),
random:seed({A,B,C}).
-
-
+-endif.
expand_deps(_, []) -> [];
expand_deps(git, [{Name, Deps} | Rest]) ->
@@ -149,21 +160,21 @@ expand_deps(git, [{Name, Vsn, Deps} | Rest]) ->
Dep = {Name, Vsn, {git, "https://example.org/user/"++Name++".git", {tag, Vsn}}},
[{Dep, expand_deps(git, Deps)} | expand_deps(git, Rest)];
expand_deps(pkg, [{Name, Deps} | Rest]) ->
- Dep = {pkg, Name, "0.0.0"},
+ Dep = {pkg, Name, "0.0.0", undefined},
[{Dep, expand_deps(pkg, Deps)} | expand_deps(pkg, Rest)];
expand_deps(pkg, [{Name, Vsn, Deps} | Rest]) ->
- Dep = {pkg, Name, Vsn},
+ Dep = {pkg, Name, Vsn, undefined},
[{Dep, expand_deps(pkg, Deps)} | expand_deps(pkg, Rest)];
expand_deps(mixed, [{Name, Deps} | Rest]) ->
Dep = if hd(Name) >= $a, hd(Name) =< $z ->
- {pkg, string:to_upper(Name), "0.0.0"}
+ {pkg, string:to_upper(Name), "0.0.0", undefined}
; hd(Name) >= $A, hd(Name) =< $Z ->
{Name, ".*", {git, "https://example.org/user/"++Name++".git", "master"}}
end,
[{Dep, expand_deps(mixed, Deps)} | expand_deps(mixed, Rest)];
expand_deps(mixed, [{Name, Vsn, Deps} | Rest]) ->
Dep = if hd(Name) >= $a, hd(Name) =< $z ->
- {pkg, string:to_upper(Name), Vsn}
+ {pkg, string:to_upper(Name), Vsn, undefined}
; hd(Name) >= $A, hd(Name) =< $Z ->
{Name, Vsn, {git, "https://example.org/user/"++Name++".git", {tag, Vsn}}}
end,
@@ -177,7 +188,7 @@ expand_deps(mixed, [{Name, Vsn, Deps} | Rest]) ->
flat_deps(Deps) -> flat_deps(Deps, [], []).
flat_deps([], Src, Pkg) -> {Src, Pkg};
-flat_deps([{{pkg, Name, Vsn}, PkgDeps} | Rest], Src, Pkg) ->
+flat_deps([{{pkg, Name, Vsn, undefined}, PkgDeps} | Rest], Src, Pkg) ->
Current = {{iolist_to_binary(Name), iolist_to_binary(Vsn)},
top_level_deps(PkgDeps)},
{[], FlatPkgDeps} = flat_deps(PkgDeps),
@@ -195,7 +206,7 @@ vsn_from_ref({git, _, {_, Vsn}}) -> Vsn;
vsn_from_ref({git, _, Vsn}) -> Vsn.
top_level_deps([]) -> [];
-top_level_deps([{{pkg, Name, Vsn}, _} | Deps]) ->
+top_level_deps([{{pkg, Name, Vsn, undefined}, _} | Deps]) ->
[{list_to_atom(Name), Vsn} | top_level_deps(Deps)];
top_level_deps([{{Name, Vsn, Ref}, _} | Deps]) ->
[{list_to_atom(Name), Vsn, Ref} | top_level_deps(Deps)].
@@ -306,9 +317,10 @@ check_results(AppDir, Expected, ProfileRun) ->
case lists:keyfind(iolist_to_binary(Name), 1, Locks) of
false ->
error({lock_not_found, Name});
- {_LockName, {pkg, _, LockVsn}, _} ->
+ {_LockName, {pkg, _, LockVsn, Hash}, _} ->
?assertEqual(iolist_to_binary(Vsn),
- iolist_to_binary(LockVsn));
+ iolist_to_binary(LockVsn)),
+ ?assertNotEqual(undefined, Hash);
{_LockName, {_, _, {ref, LockVsn}}, _} ->
?assertEqual(iolist_to_binary(Vsn),
iolist_to_binary(LockVsn))
@@ -318,9 +330,10 @@ check_results(AppDir, Expected, ProfileRun) ->
case lists:keyfind(iolist_to_binary(Name), 1, Locks) of
false ->
error({lock_not_found, Name});
- {_LockName, {pkg, _, LockVsn}, _} ->
+ {_LockName, {pkg, _, LockVsn, Hash}, _} ->
?assertEqual(iolist_to_binary(Vsn),
- iolist_to_binary(LockVsn));
+ iolist_to_binary(LockVsn)),
+ ?assertNotEqual(undefined, Hash);
{_LockName, {_, _, {ref, LockVsn}}, _} ->
error({source_lock, {Name, LockVsn}})
end
@@ -329,7 +342,7 @@ check_results(AppDir, Expected, ProfileRun) ->
case lists:keyfind(iolist_to_binary(Name), 1, Locks) of
false ->
error({lock_not_found, Name});
- {_LockName, {pkg, _, LockVsn}, _} ->
+ {_LockName, {pkg, _, LockVsn, _}, _} ->
error({pkg_lock, {Name, LockVsn}});
{_LockName, {_, _, {ref, LockVsn}}, _} ->
?assertEqual(iolist_to_binary(Vsn),
diff --git a/test/rebar_unlock_SUITE.erl b/test/rebar_unlock_SUITE.erl
index 31dca72..8dbdb3a 100644
--- a/test/rebar_unlock_SUITE.erl
+++ b/test/rebar_unlock_SUITE.erl
@@ -3,8 +3,14 @@
-include_lib("eunit/include/eunit.hrl").
-compile(export_all).
-all() -> [unlock, unlock_all].
+all() -> [pkgunlock, unlock, unlock_all].
+init_per_testcase(pkgunlock, Config0) ->
+ Config = rebar_test_utils:init_rebar_state(Config0, "pkgunlock"),
+ Lockfile = filename:join(?config(apps, Config), "rebar.lock"),
+ ec_file:copy(filename:join(?config(data_dir, Config), "pkg.rebar.lock"),
+ Lockfile),
+ [{lockfile, Lockfile} | Config];
init_per_testcase(Case, Config0) ->
Config = rebar_test_utils:init_rebar_state(Config0, atom_to_list(Case)),
Lockfile = filename:join(?config(apps, Config), "rebar.lock"),
@@ -15,6 +21,23 @@ init_per_testcase(Case, Config0) ->
end_per_testcase(_, Config) ->
Config.
+pkgunlock(Config) ->
+ Locks = read_locks(Config),
+ Hashes = read_hashes(Config),
+ rebar_test_utils:run_and_check(Config, [], ["unlock", "fakeapp"], {ok, []}),
+ Locks = read_locks(Config),
+ Hashes = read_hashes(Config),
+ rebar_test_utils:run_and_check(Config, [], ["unlock", "bbmustache"], {ok, []}),
+ ?assertEqual(Locks -- ["bbmustache"], read_locks(Config)),
+ ?assertEqual(Hashes -- ["bbmustache"], read_hashes(Config)),
+ rebar_test_utils:run_and_check(Config, [], ["unlock", "cf,certifi"], {ok, []}),
+ ?assertEqual(Locks -- ["bbmustache","cf","certifi"], read_locks(Config)),
+ ?assertEqual(Hashes -- ["bbmustache","cf","certifi"], read_hashes(Config)),
+ rebar_test_utils:run_and_check(Config, [], ["unlock", string:join(Locks,",")], {ok, []}),
+ ?assertEqual({error, enoent}, read_locks(Config)),
+ ?assertEqual({error, enoent}, read_hashes(Config)),
+ ok.
+
unlock(Config) ->
Locks = read_locks(Config),
rebar_test_utils:run_and_check(Config, [], ["unlock", "fakeapp"], {ok, []}),
@@ -35,6 +58,20 @@ unlock_all(Config) ->
read_locks(Config) ->
case file:consult(?config(lockfile, Config)) of
- {ok, [Locks]} -> [binary_to_list(element(1,Lock)) || Lock <- Locks];
- Other -> Other
+ {ok, _} ->
+ Locks = rebar_config:consult_lock_file(?config(lockfile, Config)),
+ [binary_to_list(element(1,Lock)) || Lock <- Locks];
+ Other ->
+ Other
+ end.
+
+read_hashes(Config) ->
+ case file:consult(?config(lockfile, Config)) of
+ {ok, [{_Vsn, _Locks},Props|_]} ->
+ Hashes = proplists:get_value(pkg_hash, Props, []),
+ [binary_to_list(element(1,Hash)) || Hash <- Hashes];
+ {ok, [{_Vsn, _Locks}]} ->
+ [];
+ Other ->
+ Other
end.
diff --git a/test/rebar_unlock_SUITE_data/pkg.rebar.lock b/test/rebar_unlock_SUITE_data/pkg.rebar.lock
new file mode 100644
index 0000000..231e266
--- /dev/null
+++ b/test/rebar_unlock_SUITE_data/pkg.rebar.lock
@@ -0,0 +1,24 @@
+{"1.1.0",
+[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0},
+ {<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},0},
+ {<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0},
+ {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.2.3">>},0},
+ {<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.21.0">>},0},
+ {<<"eunit_formatters">>,{pkg,<<"eunit_formatters">>,<<"0.3.1">>},0},
+ {<<"getopt">>,{pkg,<<"getopt">>,<<"0.8.2">>},0},
+ {<<"providers">>,{pkg,<<"providers">>,<<"1.6.0">>},0},
+ {<<"relx">>,{pkg,<<"relx">>,<<"3.20.0">>},0},
+ {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.1">>},0}]}.
+[
+{pkg_hash,[
+ {<<"bbmustache">>, <<"7BA94F971C5AFD7B6617918A4BB74705E36CAB36EB84B19B6A1B7EE06427AA38">>},
+ {<<"certifi">>, <<"A7966EFB868B179023618D29A407548F70C52466BF1849B9E8EBD0E34B7EA11F">>},
+ {<<"cf">>, <<"69D0B1349FD4D7D4DC55B7F407D29D7A840BF9A1EF5AF529F1EBE0CE153FC2AB">>},
+ {<<"cth_readable">>, <<"293120673DFF82F0768612C5282E35C40CACC1B6F94FE99077438FD3749D0E27">>},
+ {<<"erlware_commons">>, <<"A04433071AD7D112EDEFC75AC77719DD3E6753E697AC09428FC83D7564B80B15">>},
+ {<<"eunit_formatters">>, <<"7A6FC351EB5B873E2356B8852EB751E20C13A72FBCA03393CF682B8483509573">>},
+ {<<"getopt">>, <<"B17556DB683000BA50370B16C0619DF1337E7AF7ECBF7D64FBF8D1D6BCE3109B">>},
+ {<<"providers">>, <<"DB0E2F9043AE60C0155205FCD238D68516331D0E5146155E33D1E79DC452964A">>},
+ {<<"relx">>, <<"B515B8317D25B3A1508699294C3D1FA6DC0527851DFFC87446661BCE21A36710">>},
+ {<<"ssl_verify_fun">>, <<"28A4D65B7F59893BC2C7DE786DEC1E1555BD742D336043FE644AE956C3497FBE">>}]}
+].
diff --git a/test/rebar_upgrade_SUITE.erl b/test/rebar_upgrade_SUITE.erl
index cfe1d8a..66e1fdf 100644
--- a/test/rebar_upgrade_SUITE.erl
+++ b/test/rebar_upgrade_SUITE.erl
@@ -11,7 +11,7 @@ groups() ->
triplet_a, triplet_b, triplet_c,
tree_a, tree_b, tree_c, tree_c2, tree_cj, tree_ac, tree_all,
delete_d, promote, stable_lock, fwd_lock,
- compile_upgrade_parity]},
+ compile_upgrade_parity, umbrella_config]},
{git, [], [{group, all}]},
{pkg, [], [{group, all}]}].
@@ -66,6 +66,18 @@ end_per_testcase(_, Config) ->
meck:unload(),
Config.
+setup_project(Case=umbrella_config, Config0, Deps, UpDeps) ->
+ DepsType = ?config(deps_type, Config0),
+ NameRoot = atom_to_list(Case)++"_"++atom_to_list(DepsType),
+ Config = rebar_test_utils:init_rebar_state(Config0, NameRoot++"_"),
+ AppDir = filename:join([?config(apps, Config), "apps", NameRoot]),
+ rebar_test_utils:create_app(AppDir, "Root", "0.0.0", [kernel, stdlib]),
+ TopDeps = rebar_test_utils:top_level_deps(Deps),
+ TopConf = rebar_test_utils:create_config(AppDir, [{deps, []}]),
+ RebarConf = rebar_test_utils:create_config(AppDir, [{deps, TopDeps}]),
+ [{rebarconfig, TopConf},
+ {rebarumbrella, RebarConf},
+ {next_top_deps, rebar_test_utils:top_level_deps(UpDeps)} | Config];
setup_project(Case, Config0, Deps, UpDeps) ->
DepsType = ?config(deps_type, Config0),
Config = rebar_test_utils:init_rebar_state(
@@ -437,7 +449,12 @@ upgrades(compile_upgrade_parity) ->
[],
{"", [{"A","1"}, "D", "J", "E", {"I","1"},
{"B","1"}, "F", "G",
- {"C","1"}, "H"]}}.
+ {"C","1"}, "H"]}};
+upgrades(umbrella_config) ->
+ {[{"A", "1", []}],
+ [{"A", "2", []}],
+ ["A"],
+ {"A", [{"A","2"}]}}.
%% TODO: add a test that verifies that unlocking files and then
%% running the upgrade code is enough to properly upgrade things.
@@ -570,9 +587,36 @@ compile_upgrade_parity(Config) ->
?assertEqual(CompileLockData1, CompileLockData2),
?assertEqual(CompileLockData1, UpgradeLockData).
+umbrella_config(Config) ->
+ apply(?config(mock, Config), []),
+ {ok, TopConfig} = file:consult(?config(rebarconfig, Config)),
+ %% Install dependencies before re-mocking for an upgrade
+ rebar_test_utils:run_and_check(Config, TopConfig, ["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,
+
+ meck:new(rebar_prv_upgrade, [passthrough]),
+ meck:expect(rebar_prv_upgrade, do, fun(S) ->
+ apply(?config(mock_update, Config), []),
+ meck:passthrough([S])
+ end),
+ _NewRebarConf = rebar_test_utils:create_config(filename:dirname(?config(rebarumbrella, Config)),
+ [{deps, ?config(next_top_deps, Config)}]),
+ %% re-run from the top-level with the old config still in place;
+ %% detection must happen when going for umbrella apps!
+ rebar_test_utils:run_and_check(
+ Config, TopConfig, ["upgrade", App], Expectation
+ ),
+ meck:unload(rebar_prv_upgrade).
+
run(Config) ->
apply(?config(mock, Config), []),
- {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)),
+ ConfigPath = ?config(rebarconfig, Config),
+ {ok, RebarConfig} = file:consult(ConfigPath),
%% Install dependencies before re-mocking for an upgrade
rebar_test_utils:run_and_check(Config, RebarConfig, ["lock"], {ok, []}),
{App, Unlocks} = ?config(expected, Config),
@@ -587,7 +631,7 @@ run(Config) ->
apply(?config(mock_update, Config), []),
meck:passthrough([S])
end),
- NewRebarConf = rebar_test_utils:create_config(?config(apps, Config),
+ NewRebarConf = rebar_test_utils:create_config(filename:dirname(ConfigPath),
[{deps, ?config(next_top_deps, Config)}]),
{ok, NewRebarConfig} = file:consult(NewRebarConf),
rebar_test_utils:run_and_check(
@@ -610,7 +654,14 @@ novsn_pkg(Config) ->
rewrite_locks({ok, Expectations}, Config) ->
AppDir = ?config(apps, Config),
LockFile = filename:join([AppDir, "rebar.lock"]),
- {ok, [Locks]} = file:consult(LockFile),
+ Locks = case ?config(deps_type, Config) of
+ git ->
+ {ok, [LockData]} = file:consult(LockFile),
+ LockData;
+ pkg ->
+ {ok, [{_Vsn, LockData}|_]} = file:consult(LockFile),
+ LockData
+ end,
ExpLocks = [{list_to_binary(Name), Vsn}
|| {lock, Name, Vsn} <- Expectations],
NewLocks = lists:foldl(