diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/rebar.app.src | 1 | ||||
-rw-r--r-- | src/rebar3.erl | 3 | ||||
-rw-r--r-- | src/rebar_base_compiler.erl | 2 | ||||
-rw-r--r-- | src/rebar_erlc_compiler.erl | 2 | ||||
-rw-r--r-- | src/rebar_git_resource.erl | 72 | ||||
-rw-r--r-- | src/rebar_opts.erl | 93 | ||||
-rw-r--r-- | src/rebar_pkg_resource.erl | 4 | ||||
-rw-r--r-- | src/rebar_prv_common_test.erl | 221 | ||||
-rw-r--r-- | src/rebar_prv_eunit.erl | 11 | ||||
-rw-r--r-- | src/rebar_prv_get_deps.erl | 37 | ||||
-rw-r--r-- | src/rebar_prv_install_deps.erl | 4 | ||||
-rw-r--r-- | src/rebar_prv_shell.erl | 6 | ||||
-rw-r--r-- | src/rebar_templater.erl | 2 | ||||
-rw-r--r-- | src/rebar_utils.erl | 24 |
14 files changed, 344 insertions, 138 deletions
diff --git a/src/rebar.app.src b/src/rebar.app.src index 5b735cf..74efe97 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -52,6 +52,7 @@ rebar_prv_edoc, rebar_prv_escriptize, rebar_prv_eunit, + rebar_prv_get_deps, rebar_prv_help, rebar_prv_install_deps, rebar_prv_local_install, diff --git a/src/rebar3.erl b/src/rebar3.erl index fa26ab2..56bf3e8 100644 --- a/src/rebar3.erl +++ b/src/rebar3.erl @@ -170,6 +170,8 @@ run_aux(State, RawArgs) -> %% to find config files, and so on, and return an internal rebar3 state term. -spec init_config() -> rebar_state:t(). init_config() -> + rebar_utils:set_httpc_options(), + %% Initialize logging system Verbosity = log_level(), ok = rebar_log:init(command_line, Verbosity), @@ -366,7 +368,6 @@ ensure_running(App, Caller) -> -spec state_from_global_config([term()], file:filename()) -> rebar_state:t(). state_from_global_config(Config, GlobalConfigFile) -> - rebar_utils:set_httpc_options(), GlobalConfigTerms = rebar_config:consult_file(GlobalConfigFile), GlobalConfig = rebar_state:new(GlobalConfigTerms), diff --git a/src/rebar_base_compiler.erl b/src/rebar_base_compiler.erl index 02d567c..dcb1975 100644 --- a/src/rebar_base_compiler.erl +++ b/src/rebar_base_compiler.erl @@ -98,7 +98,7 @@ run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt, run(Config, FirstFiles, SourceDir, SourceExt, TargetDir, TargetExt, Compile3Fn, Opts) -> %% Convert simple extension to proper regex - SourceExtRe = "^[^._].*\\" ++ SourceExt ++ [$$], + SourceExtRe = "^(?!\\._).*\\" ++ SourceExt ++ [$$], Recursive = proplists:get_value(recursive, Opts, true), %% Find all possible source files diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl index 325bb4f..95573fd 100644 --- a/src/rebar_erlc_compiler.erl +++ b/src/rebar_erlc_compiler.erl @@ -48,7 +48,7 @@ -type compile_opt() :: {recursive, boolean()}. -define(DEFAULT_OUTDIR, "ebin"). --define(RE_PREFIX, "^[^._]"). +-define(RE_PREFIX, "^(?!\\._)"). %% =================================================================== %% Public API diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl index acb9ec0..201b8b6 100644 --- a/src/rebar_git_resource.erl +++ b/src/rebar_git_resource.erl @@ -107,28 +107,50 @@ download(Dir, {git, Url, ""}, State) -> download(Dir, {git, Url, {branch, "master"}}, State); download(Dir, {git, Url, {branch, Branch}}, _State) -> ok = filelib:ensure_dir(Dir), - rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch", - [rebar_utils:escape_chars(Url), - rebar_utils:escape_chars(filename:basename(Dir)), - rebar_utils:escape_chars(Branch)]), - [{cd, filename:dirname(Dir)}]); + git_clone(branch, git_vsn(), Url, Dir, Branch); download(Dir, {git, Url, {tag, Tag}}, _State) -> ok = filelib:ensure_dir(Dir), - rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch", - [rebar_utils:escape_chars(Url), - rebar_utils:escape_chars(filename:basename(Dir)), - rebar_utils:escape_chars(Tag)]), - [{cd, filename:dirname(Dir)}]); + git_clone(tag, git_vsn(), Url, Dir, Tag); download(Dir, {git, Url, {ref, Ref}}, _State) -> ok = filelib:ensure_dir(Dir), + git_clone(ref, git_vsn(), Url, Dir, Ref); +download(Dir, {git, Url, Rev}, _State) -> + ?WARN("WARNING: It is recommended to use {branch, Name}, {tag, Tag} or {ref, Ref}, otherwise updating the dep may not work as expected.", []), + ok = filelib:ensure_dir(Dir), + git_clone(rev, git_vsn(), Url, Dir, Rev). + +%% Use different git clone commands depending on git --version +git_clone(branch,Vsn,Url,Dir,Branch) when Vsn >= {1,7,10}; Vsn =:= undefined -> + rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch", + [rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir)), + rebar_utils:escape_chars(Branch)]), + [{cd, filename:dirname(Dir)}]); +git_clone(branch,_Vsn,Url,Dir,Branch) -> + rebar_utils:sh(?FMT("git clone ~s ~s -b ~s", + [rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir)), + rebar_utils:escape_chars(Branch)]), + [{cd, filename:dirname(Dir)}]); +git_clone(tag,Vsn,Url,Dir,Tag) when Vsn >= {1,7,10}; Vsn =:= undefined -> + rebar_utils:sh(?FMT("git clone ~s ~s -b ~s --single-branch", + [rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir)), + rebar_utils:escape_chars(Tag)]), + [{cd, filename:dirname(Dir)}]); +git_clone(tag,_Vsn,Url,Dir,Tag) -> + rebar_utils:sh(?FMT("git clone ~s ~s -b ~s", + [rebar_utils:escape_chars(Url), + rebar_utils:escape_chars(filename:basename(Dir)), + rebar_utils:escape_chars(Tag)]), + [{cd, filename:dirname(Dir)}]); +git_clone(ref,_Vsn,Url,Dir,Ref) -> rebar_utils:sh(?FMT("git clone -n ~s ~s", [rebar_utils:escape_chars(Url), rebar_utils:escape_chars(filename:basename(Dir))]), [{cd, filename:dirname(Dir)}]), rebar_utils:sh(?FMT("git checkout -q ~s", [Ref]), [{cd, Dir}]); -download(Dir, {git, Url, Rev}, _State) -> - ?WARN("WARNING: It is recommended to use {branch, Name}, {tag, Tag} or {ref, Ref}, otherwise updating the dep may not work as expected.", []), - ok = filelib:ensure_dir(Dir), +git_clone(rev,_Vsn,Url,Dir,Rev) -> rebar_utils:sh(?FMT("git clone -n ~s ~s", [rebar_utils:escape_chars(Url), rebar_utils:escape_chars(filename:basename(Dir))]), @@ -136,6 +158,30 @@ download(Dir, {git, Url, Rev}, _State) -> rebar_utils:sh(?FMT("git checkout -q ~s", [rebar_utils:escape_chars(Rev)]), [{cd, Dir}]). +git_vsn() -> + case application:get_env(rebar, git_vsn) of + {ok, Vsn} -> Vsn; + undefined -> + Vsn = git_vsn_fetch(), + application:set_env(rebar, git_vsn, Vsn), + Vsn + end. + +git_vsn_fetch() -> + case rebar_utils:sh("git --version",[]) of + {ok, VsnStr} -> + case re:run(VsnStr, "git version\\h+(\\d)\\.(\\d)\\.(\\d).*",[{capture,[1,2,3],list}]) of + {match,[Maj,Min,Patch]} -> + {list_to_integer(Maj), + list_to_integer(Min), + list_to_integer(Patch)}; + nomatch -> + undefined + end; + {error, _} -> + undefined + end. + make_vsn(Dir) -> case collect_default_refcount(Dir) of Vsn={plain, _} -> diff --git a/src/rebar_opts.erl b/src/rebar_opts.erl index b02a504..444b760 100644 --- a/src/rebar_opts.erl +++ b/src/rebar_opts.erl @@ -101,43 +101,70 @@ merge_opts(Profile, NewOpts, OldOpts) -> end. merge_opts(NewOpts, OldOpts) -> - dict:merge(fun(deps, _NewValue, OldValue) -> - OldValue; - ({deps, _}, NewValue, _OldValue) -> - NewValue; - (plugins, NewValue, _OldValue) -> - NewValue; - ({plugins, _}, NewValue, _OldValue) -> - NewValue; - (profiles, NewValue, OldValue) -> - dict:to_list(merge_opts(dict:from_list(NewValue), dict:from_list(OldValue))); - (mib_first_files, Value, Value) -> - Value; - (mib_first_files, NewValue, OldValue) -> - OldValue ++ NewValue; - (relx, NewValue, OldValue) -> - rebar_utils:tup_umerge(OldValue, NewValue); - (_Key, NewValue, OldValue) when is_list(NewValue) -> - case io_lib:printable_list(NewValue) of - true when NewValue =:= [] -> - case io_lib:printable_list(OldValue) of - true -> - NewValue; - false -> - OldValue - end; - true -> - NewValue; - false -> - rebar_utils:tup_umerge(NewValue, OldValue) - end; - (_Key, NewValue, _OldValue) -> - NewValue - end, NewOpts, OldOpts). + dict:merge(fun merge_opt/3, NewOpts, OldOpts). %% Internal functions %% +%% Function for dict:merge/3 (in merge_opts/2) to merge options by priority. +%% +merge_opt(deps, _NewValue, OldValue) -> + OldValue; +merge_opt({deps, _}, NewValue, _OldValue) -> + NewValue; +merge_opt(plugins, NewValue, _OldValue) -> + NewValue; +merge_opt({plugins, _}, NewValue, _OldValue) -> + NewValue; +merge_opt(profiles, NewValue, OldValue) -> + dict:to_list(merge_opts(dict:from_list(NewValue), dict:from_list(OldValue))); +merge_opt(mib_first_files, Value, Value) -> + Value; +merge_opt(mib_first_files, NewValue, OldValue) -> + OldValue ++ NewValue; +merge_opt(relx, NewValue, OldValue) -> + rebar_utils:tup_umerge(OldValue, NewValue); +merge_opt(Key, NewValue, OldValue) + when Key == erl_opts; Key == eunit_compile_opts; Key == ct_compile_opts -> + merge_erl_opts(lists:reverse(OldValue), NewValue); +merge_opt(_Key, NewValue, OldValue) when is_list(NewValue) -> + case io_lib:printable_list(NewValue) of + true when NewValue =:= [] -> + case io_lib:printable_list(OldValue) of + true -> + NewValue; + false -> + OldValue + end; + true -> + NewValue; + false -> + rebar_utils:tup_umerge(NewValue, OldValue) + end; +merge_opt(_Key, NewValue, _OldValue) -> + NewValue. + +%% +%% Merge Erlang compiler options such that the result +%% a) Doesn't contain duplicates. +%% b) Resulting options are ordered by increasing precedence as expected by +%% the compiler. +%% The first parameter is the lower precedence options, in reverse order, to +%% be merged with the higher-precedence options in the second parameter. +%% +merge_erl_opts([Opt | Opts], []) -> + merge_erl_opts(Opts, [Opt]); +merge_erl_opts([Opt | Opts], Merged) -> + case lists:member(Opt, Merged) of + true -> + merge_erl_opts(Opts, Merged); + _ -> + merge_erl_opts(Opts, [Opt | Merged]) + end; +merge_erl_opts([], Merged) -> + Merged. + +%% %% Filter a list of erl_opts platform_define options such that only %% those which match the provided architecture regex are returned. %% diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl index 8a84ce3..88419bd 100644 --- a/src/rebar_pkg_resource.erl +++ b/src/rebar_pkg_resource.erl @@ -107,8 +107,10 @@ make_vsn(_) -> {error, "Replacing version of type pkg not supported."}. request(Url, ETag) -> + HttpOptions = [{ssl, ssl_opts(Url)}, {relaxed, true} | rebar_utils:get_proxy_auth()], + case httpc:request(get, {Url, [{"if-none-match", ETag} || ETag =/= false]++[{"User-Agent", rebar_utils:user_agent()}]}, - [{ssl, ssl_opts(Url)}, {relaxed, true}], + HttpOptions, [{body_format, binary}], rebar) of {ok, {{_Version, 200, _Reason}, Headers, Body}} -> diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl index be31e8c..2ac8fc7 100644 --- a/src/rebar_prv_common_test.erl +++ b/src/rebar_prv_common_test.erl @@ -100,7 +100,9 @@ format_error({badconfig, Msg}) -> io_lib:format(Msg, []); format_error({multiple_errors, Errors}) -> io_lib:format(lists:concat(["Error running tests:"] ++ - lists:map(fun(Error) -> "~n " ++ Error end, Errors)), []). + lists:map(fun(Error) -> "~n " ++ Error end, Errors)), []); +format_error({error_reading_testspec, Reason}) -> + io_lib:format("Error reading testspec: ~p", [Reason]). %% =================================================================== %% Internal functions @@ -227,20 +229,42 @@ select_tests(State, ProjectApps, CmdOpts, CfgOpts) -> rebar_utils:reread_config(Configs), code:set_path(OldPath), - Merged = lists:ukeymerge(1, - lists:ukeysort(1, CmdOpts), - lists:ukeysort(1, CfgOpts)), - %% make sure `dir` and/or `suite` from command line go in as - %% a pair overriding both `dir` and `suite` from config if - %% they exist - Opts = case {proplists:get_value(suite, CmdOpts), proplists:get_value(dir, CmdOpts)} of - {undefined, undefined} -> Merged; - {_Suite, undefined} -> lists:keydelete(dir, 1, Merged); - {undefined, _Dir} -> lists:keydelete(suite, 1, Merged); - {_Suite, _Dir} -> Merged - end, + Opts = merge_opts(CmdOpts,CfgOpts), discover_tests(State, ProjectApps, Opts). +%% Merge the option lists from command line and rebar.config: +%% +%% - Options set on the command line will replace the same options if +%% set in rebar.config. +%% +%% - Special care is taken with options that select which tests to +%% run - ANY such option on the command line will replace ALL such +%% options in the config. +%% +%% Note that if 'spec' is given, common_test will ignore all 'dir', +%% 'suite', 'group' and 'case', so there is no need to explicitly +%% remove any options from the command line. +%% +%% All faulty combinations of options are also handled by +%% common_test and are not taken into account here. +merge_opts(CmdOpts0, CfgOpts0) -> + TestSelectOpts = [spec,dir,suite,group,testcase], + CmdOpts = lists:ukeysort(1, CmdOpts0), + CfgOpts1 = lists:ukeysort(1, CfgOpts0), + CfgOpts = case is_any_defined(TestSelectOpts,CmdOpts) of + false -> + CfgOpts1; + true -> + [Opt || Opt={K,_} <- CfgOpts1, + not lists:member(K,TestSelectOpts)] + end, + lists:ukeymerge(1, CmdOpts, CfgOpts). + +is_any_defined([Key|Keys],Opts) -> + proplists:is_defined(Key,Opts) orelse is_any_defined(Keys,Opts); +is_any_defined([],_Opts) -> + false. + sys_config_list(CmdOpts, CfgOpts) -> CmdSysConfigs = split_string(proplists:get_value(sys_config, CmdOpts, "")), case proplists:get_value(sys_config, CfgOpts, []) of @@ -253,11 +277,10 @@ sys_config_list(CmdOpts, CfgOpts) -> 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` - %% as suites - {undefined, undefined} -> {ok, [default_tests(State, ProjectApps)|Opts]}; - {_, _} -> {ok, Opts} + case is_any_defined([spec,dir,suite],Opts) of + %% no tests defined, try using `$APP/test` and `$ROOT/test` as dirs + false -> {ok, [default_tests(State, ProjectApps)|Opts]}; + true -> {ok, Opts} end. default_tests(State, ProjectApps) -> @@ -397,14 +420,29 @@ readable(State) -> end. test_dirs(State, Apps, Opts) -> - case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of - {Suites, undefined} -> set_compile_dirs(State, Apps, {suite, Suites}); - {undefined, Dirs} -> set_compile_dirs(State, Apps, {dir, Dirs}); - {Suites, Dir} when is_integer(hd(Dir)) -> - set_compile_dirs(State, Apps, join(Suites, Dir)); - {Suites, [Dir]} when is_integer(hd(Dir)) -> - set_compile_dirs(State, Apps, join(Suites, Dir)); - {_Suites, _Dirs} -> {error, "Only a single directory may be specified when specifying suites"} + case proplists:get_value(spec, Opts) of + undefined -> + case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of + {Suites, undefined} -> set_compile_dirs(State, Apps, {suite, Suites}); + {undefined, Dirs} -> set_compile_dirs(State, Apps, {dir, Dirs}); + {Suites, Dir} when is_integer(hd(Dir)) -> + set_compile_dirs(State, Apps, join(Suites, Dir)); + {Suites, [Dir]} when is_integer(hd(Dir)) -> + set_compile_dirs(State, Apps, join(Suites, Dir)); + {_Suites, _Dirs} -> {error, "Only a single directory may be specified when specifying suites"} + end; + Specs0 -> + case get_dirs_from_specs(Specs0) of + {ok,{Specs,SuiteDirs}} -> + {State1,Apps1} = set_compile_dirs1(State, Apps, + {dir, SuiteDirs}), + {State2,Apps2} = set_compile_dirs1(State1, Apps1, + {spec, Specs}), + [maybe_copy_spec(State2,Apps2,S) || S <- Specs], + {ok, rebar_state:project_apps(State2, Apps2)}; + Error -> + Error + end end. join(Suite, Dir) when is_integer(hd(Suite)) -> @@ -412,27 +450,28 @@ join(Suite, Dir) when is_integer(hd(Suite)) -> join(Suites, Dir) -> {suite, lists:map(fun(S) -> filename:join([Dir, S]) end, Suites)}. -set_compile_dirs(State, Apps, {dir, Dir}) when is_integer(hd(Dir)) -> +set_compile_dirs(State, Apps, What) -> + {NewState,NewApps} = set_compile_dirs1(State, Apps, What), + {ok, rebar_state:project_apps(NewState, NewApps)}. + +set_compile_dirs1(State, Apps, {dir, Dir}) when is_integer(hd(Dir)) -> %% single directory %% insert `Dir` into an app if relative, or the base state if not %% app relative but relative to the root or not at all if outside %% project scope - {NewState, NewApps} = maybe_inject_test_dir(State, [], Apps, Dir), - {ok, rebar_state:project_apps(NewState, NewApps)}; -set_compile_dirs(State, Apps, {dir, Dirs}) -> + maybe_inject_test_dir(State, [], Apps, Dir); +set_compile_dirs1(State, Apps, {dir, Dirs}) -> %% multiple directories F = fun(Dir, {S, A}) -> maybe_inject_test_dir(S, [], A, Dir) end, - {NewState, NewApps} = lists:foldl(F, {State, Apps}, Dirs), - {ok, rebar_state:project_apps(NewState, NewApps)}; -set_compile_dirs(State, Apps, {suite, Suites}) -> - %% suites with dir component - Dirs = find_suite_dirs(Suites), + lists:foldl(F, {State, Apps}, Dirs); +set_compile_dirs1(State, Apps, {Type, Files}) when Type==spec; Type==suite -> + %% specs or suites with dir component + Dirs = find_file_dirs(Files), F = fun(Dir, {S, A}) -> maybe_inject_test_dir(S, [], A, Dir) end, - {NewState, NewApps} = lists:foldl(F, {State, Apps}, Dirs), - {ok, rebar_state:project_apps(NewState, NewApps)}. + lists:foldl(F, {State, Apps}, Dirs). -find_suite_dirs(Suites) -> - AllDirs = lists:map(fun(S) -> filename:dirname(filename:absname(S)) end, Suites), +find_file_dirs(Files) -> + AllDirs = lists:map(fun(F) -> filename:dirname(filename:absname(F)) end, Files), %% eliminate duplicates lists:usort(AllDirs). @@ -480,52 +519,82 @@ copy_bare_suites(From, To) -> ok = rebar_file_utils:cp_r(SrcFiles, To), rebar_file_utils:cp_r(DataDirs, To). +maybe_copy_spec(State, [App|Apps], Spec) -> + case rebar_file_utils:path_from_ancestor(filename:dirname(Spec), rebar_app_info:dir(App)) of + {ok, []} -> + ok = rebar_file_utils:cp_r([Spec],rebar_app_info:out_dir(App)); + {ok,_} -> + ok; + {error,badparent} -> + maybe_copy_spec(State, Apps, Spec) + end; +maybe_copy_spec(State, [], Spec) -> + case rebar_file_utils:path_from_ancestor(filename:dirname(Spec), rebar_state:dir(State)) of + {ok, []} -> + ExtrasDir = filename:join([rebar_dir:base_dir(State), "extras"]), + ok = rebar_file_utils:cp_r([Spec],ExtrasDir); + _R -> + ok + end. + inject_test_dir(Opts, Dir) -> %% append specified test targets to app defined `extra_src_dirs` ExtraSrcDirs = rebar_opts:get(Opts, extra_src_dirs, []), rebar_opts:set(Opts, extra_src_dirs, ExtraSrcDirs ++ [Dir]). +get_dirs_from_specs(Specs) -> + case get_tests_from_specs(Specs) of + {ok,Tests} -> + {SpecLists,NodeRunSkipLists} = lists:unzip(Tests), + SpecList = lists:append(SpecLists), + NodeRunSkipList = lists:append(NodeRunSkipLists), + RunList = lists:append([R || {_,R,_} <- NodeRunSkipList]), + DirList = [element(1,R) || R <- RunList], + {ok,{SpecList,DirList}}; + {error,Reason} -> + {error,{?MODULE,{error_reading_testspec,Reason}}} + end. + +get_tests_from_specs(Specs) -> + _ = ct_testspec:module_info(), % make sure ct_testspec is loaded + case erlang:function_exported(ct_testspec,get_tests,1) of + true -> + ct_testspec:get_tests(Specs); + false -> + case ct_testspec:collect_tests_from_file(Specs,true) of + Tests when is_list(Tests) -> + {ok,[{S,ct_testspec:prepare_tests(R)} || {S,R} <- Tests]}; + R when is_tuple(R), element(1,R)==testspec -> + %% R15 + {ok,[{Specs,ct_testspec:prepare_tests(R)}]}; + Error -> + Error + end + end. + translate_paths(State, Opts) -> - case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of - {_Suites, undefined} -> translate_suites(State, Opts, []); - {undefined, _Dirs} -> translate_dirs(State, Opts, []); - %% both dirs and suites are defined, only translate dir paths - _ -> translate_dirs(State, Opts, []) + case proplists:get_value(spec, Opts) of + undefined -> + case {proplists:get_value(suite, Opts), proplists:get_value(dir, Opts)} of + {_Suites, undefined} -> translate_paths(State, suite, Opts, []); + {undefined, _Dirs} -> translate_paths(State, dir, Opts, []); + %% both dirs and suites are defined, only translate dir paths + _ -> translate_paths(State, dir, Opts, []) + end; + _Specs -> + translate_paths(State, spec, Opts, []) end. -translate_dirs(_State, [], Acc) -> lists:reverse(Acc); -translate_dirs(State, [{dir, Dir}|Rest], Acc) when is_integer(hd(Dir)) -> - %% single dir - Apps = rebar_state:project_apps(State), - translate_dirs(State, Rest, [{dir, translate(State, Apps, Dir)}|Acc]); -translate_dirs(State, [{dir, Dirs}|Rest], Acc) -> - %% multiple dirs +translate_paths(_State, _Type, [], Acc) -> lists:reverse(Acc); +translate_paths(State, Type, [{Type, Val}|Rest], Acc) when is_integer(hd(Val)) -> + %% single file or dir + translate_paths(State, Type, [{Type, [Val]}|Rest], Acc); +translate_paths(State, Type, [{Type, Files}|Rest], Acc) -> Apps = rebar_state:project_apps(State), - NewDirs = {dir, lists:map(fun(Dir) -> translate(State, Apps, Dir) end, Dirs)}, - translate_dirs(State, Rest, [NewDirs|Acc]); -translate_dirs(State, [Test|Rest], Acc) -> - translate_dirs(State, Rest, [Test|Acc]). - -translate_suites(_State, [], Acc) -> lists:reverse(Acc); -translate_suites(State, [{suite, Suite}|Rest], Acc) when is_integer(hd(Suite)) -> - %% single suite - Apps = rebar_state:project_apps(State), - translate_suites(State, Rest, [{suite, translate_suite(State, Apps, Suite)}|Acc]); -translate_suites(State, [{suite, Suites}|Rest], Acc) -> - %% multiple suites - Apps = rebar_state:project_apps(State), - NewSuites = {suite, lists:map(fun(Suite) -> translate_suite(State, Apps, Suite) end, Suites)}, - translate_suites(State, Rest, [NewSuites|Acc]); -translate_suites(State, [Test|Rest], Acc) -> - translate_suites(State, Rest, [Test|Acc]). - -translate_suite(State, Apps, Suite) -> - Dirname = filename:dirname(Suite), - Basename = filename:basename(Suite), - case Dirname of - "." -> Suite; - _ -> filename:join([translate(State, Apps, Dirname), Basename]) - end. + New = {Type, lists:map(fun(File) -> translate(State, Apps, File) end, Files)}, + translate_paths(State, Type, Rest, [New|Acc]); +translate_paths(State, Type, [Test|Rest], Acc) -> + translate_paths(State, Type, Rest, [Test|Acc]). translate(State, [App|Rest], Path) -> case rebar_file_utils:path_from_ancestor(Path, rebar_app_info:dir(App)) of diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl index 0908ec9..7d44137 100644 --- a/src/rebar_prv_eunit.erl +++ b/src/rebar_prv_eunit.erl @@ -18,7 +18,7 @@ %% we need to modify app_info state before compile -define(DEPS, [lock]). --define(DEFAULT_TEST_REGEX, "^[^._].*\\.erl\$"). +-define(DEFAULT_TEST_REGEX, "^(?!\\._).*\\.erl\$"). %% =================================================================== %% Public API @@ -195,17 +195,18 @@ gather_src([Dir|Rest], Regex, Srcs) -> gather_src(Rest, Regex, Srcs ++ rebar_utils:find_files(Dir, Regex, true)). dedupe_tests({AppMods, TestMods}) -> + UniqueTestMods = lists:usort(TestMods) -- AppMods, %% for each modules in TestMods create a test if there is not a module %% in AppMods that will trigger it - F = fun(Mod) -> - M = filename:basename(Mod, ".erl"), - MatchesTest = fun(Dir) -> filename:basename(Dir, ".erl") ++ "_tests" == M end, + F = fun(TestMod) -> + M = filename:rootname(filename:basename(TestMod)), + MatchesTest = fun(AppMod) -> filename:rootname(filename:basename(AppMod)) ++ "_tests" == M end, case lists:any(MatchesTest, AppMods) of false -> {true, {module, list_to_atom(M)}}; true -> false end end, - lists:usort(rebar_utils:filtermap(F, TestMods)). + rebar_utils:filtermap(F, UniqueTestMods). inject_eunit_state(State, {ok, Tests}) -> Apps = rebar_state:project_apps(State), diff --git a/src/rebar_prv_get_deps.erl b/src/rebar_prv_get_deps.erl new file mode 100644 index 0000000..020e50b --- /dev/null +++ b/src/rebar_prv_get_deps.erl @@ -0,0 +1,37 @@ +%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*- +%% ex: ts=4 sw=4 et + +-module(rebar_prv_get_deps). + +-behaviour(provider). + +-export([init/1, + do/1, + format_error/1]). + +-define(PROVIDER, 'get-deps'). +-define(DEPS, [lock]). + +%% =================================================================== +%% Public API +%% =================================================================== + +-spec init(rebar_state:t()) -> {ok, rebar_state:t()}. +init(State) -> + Provider = providers:create([{name, ?PROVIDER}, + {module, ?MODULE}, + {deps, ?DEPS}, + {bare, true}, + {example, "rebar3 get-deps"}, + {short_desc, "Fetch dependencies."}, + {desc, "Fetch project dependencies."}, + {opts, []}, + {profiles, []}]), + {ok, rebar_state:add_provider(State, Provider)}. + +-spec do(rebar_state:t()) -> {ok, rebar_state:t()}. +do(State) -> {ok, State}. + +-spec format_error(any()) -> iolist(). +format_error(Reason) -> + io_lib:format("~p", [Reason]).
\ No newline at end of file diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl index 9c5e8ac..c9fe0ad 100644 --- a/src/rebar_prv_install_deps.erl +++ b/src/rebar_prv_install_deps.erl @@ -120,9 +120,9 @@ format_error({missing_package, Package}) -> format_error({cycles, Cycles}) -> Prints = [["applications: ", [io_lib:format("~s ", [Dep]) || Dep <- Cycle], - "depend on each other~n"] + "depend on each other\n"] || Cycle <- Cycles], - ["Dependency cycle(s) detected:~n", Prints]; + ["Dependency cycle(s) detected:\n", Prints]; format_error(Reason) -> io_lib:format("~p", [Reason]). diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index 31b2e17..72efcf1 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -339,7 +339,7 @@ reread_config(State) -> boot_apps(Apps) -> ?WARN("The rebar3 shell is a development tool; to deploy " "applications in production, consider using releases " - "(http://www.rebar3.org/v3.0/docs/releases)", []), + "(http://www.rebar3.org/docs/releases)", []), Normalized = normalize_boot_apps(Apps), Res = [application:ensure_all_started(App) || App <- Normalized], _ = [?INFO("Booted ~p", [App]) @@ -352,11 +352,15 @@ boot_apps(Apps) -> normalize_load_apps([]) -> []; normalize_load_apps([{App, _} | T]) -> [App | normalize_load_apps(T)]; normalize_load_apps([{App, _Vsn, load} | T]) -> [App | normalize_load_apps(T)]; +normalize_load_apps([{App, _Vsn, Operator} | T]) when is_atom(Operator) -> + [App | normalize_load_apps(T)]; normalize_load_apps([App | T]) when is_atom(App) -> [App | normalize_load_apps(T)]. normalize_boot_apps([]) -> []; normalize_boot_apps([{_App, load} | T]) -> normalize_boot_apps(T); normalize_boot_apps([{_App, _Vsn, load} | T]) -> normalize_boot_apps(T); +normalize_boot_apps([{App, _Vsn, Operator} | T]) when is_atom(Operator) -> + [App | normalize_boot_apps(T)]; normalize_boot_apps([{App, _Vsn} | T]) -> [App | normalize_boot_apps(T)]; normalize_boot_apps([App | T]) when is_atom(App) -> [App | normalize_boot_apps(T)]. diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl index 299b957..e64ce71 100644 --- a/src/rebar_templater.erl +++ b/src/rebar_templater.erl @@ -33,7 +33,7 @@ -include("rebar.hrl"). --define(TEMPLATE_RE, "^[^._].*\\.template\$"). +-define(TEMPLATE_RE, "^(?!\\._).*\\.template\$"). %% =================================================================== %% Public API diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl index d92f119..c357e94 100644 --- a/src/rebar_utils.erl +++ b/src/rebar_utils.erl @@ -70,7 +70,9 @@ info_useless/2, list_dir/1, user_agent/0, - reread_config/1]). + reread_config/1, + get_proxy_auth/0]). + %% for internal use only -export([otp_release/0]). @@ -838,8 +840,9 @@ set_httpc_options(_, []) -> ok; set_httpc_options(Scheme, Proxy) -> - {ok, {_, _, Host, Port, _, _}} = http_uri:parse(Proxy), - httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar). + {ok, {_, UserInfo, Host, Port, _, _}} = http_uri:parse(Proxy), + httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar), + set_proxy_auth(UserInfo). url_append_path(Url, ExtraPath) -> case http_uri:parse(Url) of @@ -878,3 +881,18 @@ list_dir(Dir) -> true -> file:list_dir_all(Dir); false -> file:list_dir(Dir) end. + +set_proxy_auth([]) -> + ok; +set_proxy_auth(UserInfo) -> + Idx = string:chr(UserInfo, $:), + Username = string:sub_string(UserInfo, 1, Idx-1), + Password = string:sub_string(UserInfo, Idx+1), + %% password may contain url encoded characters, need to decode them first + application:set_env(rebar, proxy_auth, [{proxy_auth, {Username, http_uri:decode(Password)}}]). + +get_proxy_auth() -> + case application:get_env(rebar, proxy_auth) of + undefined -> []; + {ok, ProxyAuth} -> ProxyAuth + end. |