summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rwxr-xr-xbootstrap8
-rwxr-xr-xpr2relnotes.py188
-rwxr-xr-xpr2relnotes.sh25
-rw-r--r--src/rebar.hrl1
-rw-r--r--src/rebar3.erl14
-rw-r--r--src/rebar_app_utils.erl7
-rw-r--r--src/rebar_base_compiler.erl43
-rw-r--r--src/rebar_config.erl18
-rw-r--r--src/rebar_erlc_compiler.erl33
-rw-r--r--src/rebar_file_utils.erl35
-rw-r--r--src/rebar_git_resource.erl2
-rw-r--r--src/rebar_log.erl12
-rw-r--r--src/rebar_prv_common_test.erl25
-rw-r--r--src/rebar_prv_cover.erl5
-rw-r--r--src/rebar_prv_deps_tree.erl2
-rw-r--r--src/rebar_prv_shell.erl31
-rw-r--r--src/rebar_prv_upgrade.erl24
-rw-r--r--src/rebar_state.erl3
-rw-r--r--src/rebar_utils.erl20
-rw-r--r--test/rebar_compile_SUITE.erl95
-rw-r--r--test/rebar_ct_SUITE.erl48
-rw-r--r--test/rebar_deps_SUITE.erl73
-rw-r--r--test/rebar_file_utils_SUITE.erl28
-rw-r--r--test/rebar_upgrade_SUITE.erl52
25 files changed, 530 insertions, 264 deletions
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..90775c8 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.2.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/src/rebar.hrl b/src/rebar.hrl
index 4e1ec00..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))).
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 f3b2962..d256cac 100644
--- a/src/rebar_app_utils.erl
+++ b/src/rebar_app_utils.erl
@@ -134,7 +134,12 @@ 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) ->
diff --git a/src/rebar_base_compiler.erl b/src/rebar_base_compiler.erl
index 31292af..6b8c7ca 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
@@ -153,12 +180,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 9e13d46..b50c030 100644
--- a/src/rebar_config.erl
+++ b/src/rebar_config.erl
@@ -250,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_file_utils.erl b/src/rebar_file_utils.erl
index 0f84520..667be62 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)]).
@@ -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 ff43f76..f0f5f03 100644
--- a/src/rebar_git_resource.erl
+++ b/src/rebar_git_resource.erl
@@ -17,7 +17,7 @@
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} =
case os:type() of
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_prv_common_test.erl b/src/rebar_prv_common_test.erl
index 5712fbf..fbd0e89 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 <- SysConfigs, {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 e555a9b..464967b 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
@@ -315,7 +316,9 @@ cover_compile(State, Dirs) ->
%% print any warnings about modules that failed to cover compile
lists:foreach(fun print_cover_warnings/1, lists:flatten(Results))
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_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_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_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_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/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 e409c29..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) ->
@@ -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 4ef9f79..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">>,
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_upgrade_SUITE.erl b/test/rebar_upgrade_SUITE.erl
index 22fb14d..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(