summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--rebar.config4
-rw-r--r--rebar.lock4
-rw-r--r--src/rebar.hrl5
-rw-r--r--src/rebar3.erl25
-rw-r--r--src/rebar_app_info.erl5
-rw-r--r--src/rebar_app_utils.erl38
-rw-r--r--src/rebar_dir.erl31
-rw-r--r--src/rebar_packages.erl33
-rw-r--r--src/rebar_pkg_resource.erl10
-rw-r--r--src/rebar_prv_common_test.erl32
-rw-r--r--src/rebar_prv_compile.erl4
-rw-r--r--src/rebar_prv_cover.erl18
-rw-r--r--src/rebar_prv_new.erl23
-rw-r--r--src/rebar_prv_update.erl52
-rw-r--r--src/rebar_templater.erl52
-rw-r--r--src/rebar_utils.erl10
-rw-r--r--test/rebar_ct_SUITE.erl95
-rw-r--r--test/rebar_dir_SUITE.erl43
-rw-r--r--test/rebar_new_SUITE.erl31
-rw-r--r--test/rebar_new_SUITE_data/.rebar3/templates/bad_index/LICENSE.dtl29
-rw-r--r--test/rebar_new_SUITE_data/.rebar3/templates/bad_index/README.md.dtl9
-rw-r--r--test/rebar_new_SUITE_data/.rebar3/templates/bad_index/app.erl.dtl27
-rw-r--r--test/rebar_new_SUITE_data/.rebar3/templates/bad_index/bad_index.template13
-rw-r--r--test/rebar_new_SUITE_data/.rebar3/templates/bad_index/gitignore.dtl18
-rw-r--r--test/rebar_new_SUITE_data/.rebar3/templates/bad_index/otp_app.app.src.dtl12
-rw-r--r--test/rebar_new_SUITE_data/.rebar3/templates/bad_index/rebar.config.dtl2
-rw-r--r--test/rebar_new_SUITE_data/.rebar3/templates/bad_index/sup.erl.dtl35
-rw-r--r--test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/.gitignore19
-rw-r--r--test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/priv/module.erl.dtl2
-rw-r--r--test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/priv/tpl.template7
-rw-r--r--test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/rebar.config2
-rw-r--r--test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/src/tpl.app.src15
-rw-r--r--test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/src/tpl.erl8
-rw-r--r--test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/src/tpl_prv.erl32
-rw-r--r--test/rebar_new_SUITE_data/plugin_tpl/rebar.config3
-rw-r--r--test/rebar_new_SUITE_data/plugin_tpl/src/plugin_tpl.app.src15
-rw-r--r--test/rebar_new_SUITE_data/plugin_tpl/src/plugin_tpl.erl13
-rw-r--r--test/rebar_pkg_SUITE.erl33
-rw-r--r--test/rebar_pkg_alias_SUITE.erl4
40 files changed, 705 insertions, 109 deletions
diff --git a/.gitignore b/.gitignore
index c6be7ef..34b6ef0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+_checkouts
.rebar3
rebar3
_build
diff --git a/rebar.config b/rebar.config
index 276acff..61dcd97 100644
--- a/rebar.config
+++ b/rebar.config
@@ -7,9 +7,9 @@
{providers, "1.6.0"},
{getopt, "0.8.2"},
{bbmustache, "1.0.4"},
- {relx, "3.9.0"},
+ {relx, "3.11.0"},
{cf, "0.2.1"},
- {cth_readable, "1.1.0"},
+ {cth_readable, "1.1.1"},
{eunit_formatters, "0.3.1"}]}.
{escript_name, rebar3}.
diff --git a/rebar.lock b/rebar.lock
index 8edc3d7..b1dd0b6 100644
--- a/rebar.lock
+++ b/rebar.lock
@@ -1,10 +1,10 @@
[{<<"bbmustache">>,{pkg,<<"bbmustache">>,<<"1.0.4">>},0},
{<<"certifi">>,{pkg,<<"certifi">>,<<"0.3.0">>},0},
{<<"cf">>,{pkg,<<"cf">>,<<"0.2.1">>},0},
- {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.1.0">>},0},
+ {<<"cth_readable">>,{pkg,<<"cth_readable">>,<<"1.1.1">>},0},
{<<"erlware_commons">>,{pkg,<<"erlware_commons">>,<<"0.18.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.9.0">>},0},
+ {<<"relx">>,{pkg,<<"relx">>,<<"3.11.0">>},0},
{<<"ssl_verify_hostname">>,{pkg,<<"ssl_verify_hostname">>,<<"1.0.5">>},0}].
diff --git a/src/rebar.hrl b/src/rebar.hrl
index 8ad0faa..f4e7f5e 100644
--- a/src/rebar.hrl
+++ b/src/rebar.hrl
@@ -22,8 +22,9 @@
-define(DEFAULT_TEST_DEPS_DIR, "test/lib").
-define(DEFAULT_RELEASE_DIR, "rel").
-define(DEFAULT_CONFIG_FILE, "rebar.config").
--define(DEFAULT_CDN, "https://s3.amazonaws.com/s3.hex.pm/tarballs").
--define(DEFAULT_HEX_REGISTRY, "https://s3.amazonaws.com/s3.hex.pm/registry.ets.gz").
+-define(DEFAULT_CDN, "https://s3.amazonaws.com/s3.hex.pm/").
+-define(REMOTE_PACKAGE_DIR, "tarballs").
+-define(REMOTE_REGISTRY_FILE, "registry.ets.gz").
-define(LOCK_FILE, "rebar.lock").
-define(PACKAGE_INDEX_VERSION, 3).
diff --git a/src/rebar3.erl b/src/rebar3.erl
index 2b73844..9106bb8 100644
--- a/src/rebar3.erl
+++ b/src/rebar3.erl
@@ -105,25 +105,32 @@ run_aux(State, RawArgs) ->
rebar_state:apply_profiles(State, [list_to_atom(Profile)])
end,
+ State2 = case os:getenv("HEX_CDN") of
+ false ->
+ State1;
+ CDN ->
+ rebar_state:set(State1, rebar_packages_cdn, CDN)
+ end,
+
%% bootstrap test profile
- State2 = rebar_state:add_to_profile(State1, test, test_state(State1)),
+ State3 = rebar_state:add_to_profile(State2, test, test_state(State1)),
%% Process each command, resetting any state between each one
BaseDir = rebar_state:get(State, base_dir, ?DEFAULT_BASE_DIR),
- State3 = rebar_state:set(State2, base_dir,
- filename:join(filename:absname(rebar_state:dir(State2)), BaseDir)),
+ State4 = rebar_state:set(State3, base_dir,
+ filename:join(filename:absname(rebar_state:dir(State3)), BaseDir)),
{ok, Providers} = application:get_env(rebar, providers),
%% Providers can modify profiles stored in opts, so set default after initializing providers
- State4 = rebar_state:create_logic_providers(Providers, State3),
- State5 = rebar_plugins:project_apps_install(State4),
- State6 = rebar_state:default(State5, rebar_state:opts(State5)),
+ State5 = rebar_state:create_logic_providers(Providers, State4),
+ State6 = rebar_plugins:project_apps_install(State5),
+ State7 = rebar_state:default(State6, rebar_state:opts(State6)),
{Task, Args} = parse_args(RawArgs),
- State7 = rebar_state:code_paths(State6, default, code:get_path()),
+ State8 = rebar_state:code_paths(State7, default, code:get_path()),
- rebar_core:init_command(rebar_state:command_args(State7, Args), Task).
+ rebar_core:init_command(rebar_state:command_args(State8, Args), Task).
init_config() ->
%% Initialize logging system
@@ -339,4 +346,4 @@ safe_define_test_macro(Opts) ->
test_defined([{d, 'TEST'}|_]) -> true;
test_defined([{d, 'TEST', true}|_]) -> true;
test_defined([_|Rest]) -> test_defined(Rest);
-test_defined([]) -> false. \ No newline at end of file
+test_defined([]) -> false.
diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl
index 95b4624..9fee4e0 100644
--- a/src/rebar_app_info.erl
+++ b/src/rebar_app_info.erl
@@ -23,6 +23,7 @@
original_vsn/1,
original_vsn/2,
ebin_dir/1,
+ priv_dir/1,
applications/1,
applications/2,
profiles/1,
@@ -361,6 +362,10 @@ out_dir(AppInfo=#app_info_t{}, OutDir) ->
ebin_dir(#app_info_t{out_dir=OutDir}) ->
ec_cnv:to_list(filename:join(OutDir, "ebin")).
+-spec priv_dir(t()) -> file:name().
+priv_dir(#app_info_t{out_dir=OutDir}) ->
+ ec_cnv:to_list(filename:join(OutDir, "priv")).
+
-spec resource_type(t(), pkg | src) -> t().
resource_type(AppInfo=#app_info_t{}, Type) ->
AppInfo#app_info_t{resource_type=Type}.
diff --git a/src/rebar_app_utils.erl b/src/rebar_app_utils.erl
index 602fd42..ae1ea1c 100644
--- a/src/rebar_app_utils.erl
+++ b/src/rebar_app_utils.erl
@@ -118,14 +118,14 @@ parse_dep(Dep, Parent, DepsDir, State, Locks, Level) ->
end.
parse_dep(Parent, {Name, Vsn, {pkg, PkgName}}, DepsDir, IsLock, State) ->
- {PkgName1, PkgVsn} = parse_goal(ec_cnv:to_binary(PkgName), ec_cnv:to_binary(Vsn)),
+ {PkgName1, PkgVsn} = {ec_cnv:to_binary(PkgName), ec_cnv:to_binary(Vsn)},
dep_to_app(Parent, DepsDir, Name, PkgVsn, {pkg, PkgName1, PkgVsn}, 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);
parse_dep(Parent, {Name, Vsn}, DepsDir, IsLock, State) when is_list(Vsn); is_binary(Vsn) ->
%% Versioned Package dependency
- {PkgName, PkgVsn} = parse_goal(ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)),
+ {PkgName, PkgVsn} = {ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn)},
dep_to_app(Parent, DepsDir, PkgName, PkgVsn, {pkg, PkgName, PkgVsn}, IsLock, State);
parse_dep(Parent, Name, DepsDir, IsLock, State) when is_atom(Name); is_binary(Name) ->
%% Unversioned package dependency
@@ -168,21 +168,22 @@ dep_to_app(Parent, DepsDir, Name, Vsn, Source, IsLock, State) ->
AppInfo3 = rebar_app_info:apply_overrides(rebar_app_info:get(AppInfo2, overrides, []), AppInfo2),
rebar_app_info:is_lock(AppInfo3, IsLock).
-update_source(AppInfo, {pkg, PkgName, undefined}, State) ->
- {PkgName1, PkgVsn1} = get_package(PkgName, State),
+update_source(AppInfo, {pkg, PkgName, PkgVsn}, State) ->
+ {PkgName1, PkgVsn1} = case PkgVsn of
+ undefined ->
+ get_package(PkgName, "0", State);
+ <<"~>", Vsn/binary>> ->
+ [Vsn1] = binary:split(Vsn, [<<" ">>], [trim_all, global]),
+ get_package(PkgName, Vsn1, State);
+ _ ->
+ {PkgName, PkgVsn}
+ end,
AppInfo1 = rebar_app_info:source(AppInfo, {pkg, PkgName1, PkgVsn1}),
Deps = rebar_packages:deps(PkgName1
,PkgVsn1
,State),
AppInfo2 = rebar_app_info:resource_type(rebar_app_info:deps(AppInfo1, Deps), pkg),
rebar_app_info:original_vsn(AppInfo2, PkgVsn1);
-update_source(AppInfo, {pkg, PkgName, PkgVsn}, State) ->
- AppInfo1 = rebar_app_info:source(AppInfo, {pkg, PkgName, PkgVsn}),
- Deps = rebar_packages:deps(PkgName
- ,PkgVsn
- ,State),
- AppInfo2 = rebar_app_info:resource_type(rebar_app_info:deps(AppInfo1, Deps), pkg),
- rebar_app_info:original_vsn(AppInfo2, PkgVsn);
update_source(AppInfo, Source, _State) ->
rebar_app_info:source(AppInfo, Source).
@@ -198,19 +199,8 @@ format_error(Error) ->
%% Internal functions
%% ===================================================================
--spec parse_goal(binary(), binary()) -> {binary(), binary()} | {binary(), binary(), binary()}.
-parse_goal(Name, Constraint) ->
- case re:run(Constraint, "([^\\d]*)(\\d.*)", [{capture, [1,2], binary}]) of
- {match, [<<>>, Vsn]} ->
- {Name, Vsn};
- {match, [Op, Vsn]} ->
- {Name, Vsn, binary_to_atom(Op, utf8)};
- nomatch ->
- throw(?PRV_ERROR({bad_constraint, Name, Constraint}))
- end.
-
-get_package(Dep, State) ->
- case rebar_packages:find_highest_matching(Dep, "0", ?PACKAGE_TABLE, State) of
+get_package(Dep, Vsn, State) ->
+ case rebar_packages:find_highest_matching(Dep, Vsn, ?PACKAGE_TABLE, State) of
{ok, HighestDepVsn} ->
{Dep, HighestDepVsn};
none ->
diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl
index 09e3114..3729704 100644
--- a/src/rebar_dir.erl
+++ b/src/rebar_dir.erl
@@ -121,8 +121,37 @@ processing_base_dir(State, Dir) ->
AbsDir = filename:absname(Dir),
AbsDir =:= rebar_state:get(State, base_dir).
+make_absolute_path(Path) ->
+ case filename:pathtype(Path) of
+ absolute ->
+ Path;
+ relative ->
+ {ok, Dir} = file:get_cwd(),
+ filename:join([Dir, Path]);
+ volumerelative ->
+ Volume = hd(filename:split(Path)),
+ {ok, Dir} = file:get_cwd(Volume),
+ filename:join([Dir, Path])
+ end.
+
+make_normalized_path(Path) ->
+ AbsPath = make_absolute_path(Path),
+ Components = filename:split(AbsPath),
+ make_normalized_path(Components, []).
+
+make_normalized_path([], NormalizedPath) ->
+ filename:join(lists:reverse(NormalizedPath));
+make_normalized_path([H|T], NormalizedPath) ->
+ case H of
+ "." -> make_normalized_path(T, NormalizedPath);
+ ".." -> make_normalized_path(T, tl(NormalizedPath));
+ _ -> make_normalized_path(T, [H|NormalizedPath])
+ end.
+
make_relative_path(Source, Target) ->
- do_make_relative_path(filename:split(Source), filename:split(Target)).
+ AbsSource = make_normalized_path(Source),
+ AbsTarget = make_normalized_path(Target),
+ do_make_relative_path(filename:split(AbsSource), filename:split(AbsTarget)).
do_make_relative_path([H|T1], [H|T2]) ->
do_make_relative_path(T1, T2);
diff --git a/src/rebar_packages.erl b/src/rebar_packages.erl
index c0ed69d..c56009e 100644
--- a/src/rebar_packages.erl
+++ b/src/rebar_packages.erl
@@ -46,7 +46,7 @@ close_packages() ->
catch ets:delete(?PACKAGE_TABLE).
load_and_verify_version(State) ->
- RegistryDir = registry_dir(State),
+ {ok, RegistryDir} = registry_dir(State),
case ets:file2tab(filename:join(RegistryDir, ?INDEX_FILE)) of
{ok, _} ->
case ets:lookup_element(?PACKAGE_TABLE, package_index_version, 2) of
@@ -89,21 +89,30 @@ registry_dir(State) ->
?DEFAULT_CDN ->
RegistryDir = filename:join([CacheDir, "hex", "default"]),
ok = filelib:ensure_dir(filename:join(RegistryDir, "placeholder")),
- RegistryDir;
+ {ok, RegistryDir};
CDN ->
- {ok, {_, _, Host, _, Path, _}} = http_uri:parse(CDN),
- CDNHostPath = lists:reverse(string:tokens(Host, ".")),
- CDNPath = tl(filename:split(Path)),
- RegistryDir = filename:join([CacheDir, "hex"] ++ CDNHostPath ++ CDNPath),
- ok = filelib:ensure_dir(filename:join(RegistryDir, "placeholder")),
- RegistryDir
+ case rebar_utils:url_append_path(CDN, ?REMOTE_PACKAGE_DIR) of
+ {ok, Parsed} ->
+ {ok, {_, _, Host, _, Path, _}} = http_uri:parse(Parsed),
+ CDNHostPath = lists:reverse(string:tokens(Host, ".")),
+ CDNPath = tl(filename:split(Path)),
+ RegistryDir = filename:join([CacheDir, "hex"] ++ CDNHostPath ++ CDNPath),
+ ok = filelib:ensure_dir(filename:join(RegistryDir, "placeholder")),
+ {ok, RegistryDir};
+ _ ->
+ {uri_parse_error, CDN}
+ end
end.
package_dir(State) ->
- RegistryDir = registry_dir(State),
- PackageDir = filename:join([RegistryDir, "packages"]),
- ok = filelib:ensure_dir(filename:join(PackageDir, "placeholder")),
- PackageDir.
+ case registry_dir(State) of
+ {ok, RegistryDir} ->
+ PackageDir = filename:join([RegistryDir, "packages"]),
+ ok = filelib:ensure_dir(filename:join(PackageDir, "placeholder")),
+ {ok, PackageDir};
+ Error ->
+ Error
+ end.
registry_checksum({pkg, Name, Vsn}, State) ->
try
diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl
index 4f55ad1..33687e4 100644
--- a/src/rebar_pkg_resource.erl
+++ b/src/rebar_pkg_resource.erl
@@ -30,11 +30,15 @@ needs_update(Dir, {pkg, _Name, Vsn}) ->
download(TmpDir, Pkg={pkg, Name, Vsn}, State) ->
CDN = rebar_state:get(State, rebar_packages_cdn, ?DEFAULT_CDN),
- PackageDir = rebar_packages:package_dir(State),
+ {ok, PackageDir} = rebar_packages:package_dir(State),
Package = binary_to_list(<<Name/binary, "-", Vsn/binary, ".tar">>),
CachePath = filename:join(PackageDir, Package),
- Url = string:join([CDN, Package], "/"),
- cached_download(TmpDir, CachePath, Pkg, Url, etag(CachePath), State).
+ case rebar_utils:url_append_path(CDN, filename:join(?REMOTE_PACKAGE_DIR, Package)) of
+ {ok, Url} ->
+ cached_download(TmpDir, CachePath, Pkg, Url, etag(CachePath), State);
+ _ ->
+ {fetch_fail, Name, Vsn}
+ end.
cached_download(TmpDir, CachePath, Pkg={pkg, Name, Vsn}, Url, ETag, State) ->
case request(Url, ETag) of
diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl
index 05a1dc6..1136e08 100644
--- a/src/rebar_prv_common_test.erl
+++ b/src/rebar_prv_common_test.erl
@@ -340,7 +340,7 @@ test_dirs(State, Apps, Opts) ->
{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));
+ set_compile_dirs(State, Apps, join(Suites, Dir));
{_Suites, _Dirs} -> {error, "Only a single directory may be specified when specifying suites"}
end.
@@ -375,6 +375,17 @@ find_suite_dirs(Suites) ->
maybe_inject_test_dir(State, AppAcc, [App|Rest], Dir) ->
case rebar_file_utils:path_from_ancestor(Dir, rebar_app_info:dir(App)) of
+ {ok, []} ->
+ %% normal operation involves copying the entire directory a
+ %% suite exists in but if the suite is in the app root directory
+ %% the current compiler tries to compile all subdirs including priv
+ %% instead copy only files ending in `.erl' and directories
+ %% ending in `_SUITE_data' into the `_build/PROFILE/extras' dir
+ {ok, RelAppDir} = rebar_file_utils:path_from_ancestor(rebar_app_info:dir(App), rebar_state:dir(State)),
+ ExtrasDir = filename:join([rebar_dir:base_dir(State), "extras", RelAppDir]),
+ ok = copy_bare_suites(Dir, ExtrasDir),
+ Opts = inject_test_dir(rebar_state:opts(State), ExtrasDir),
+ {rebar_state:opts(State, Opts), AppAcc};
{ok, Path} ->
Opts = inject_test_dir(rebar_app_info:opts(App), Path),
{State, AppAcc ++ [rebar_app_info:opts(App, Opts)] ++ Rest};
@@ -384,8 +395,15 @@ maybe_inject_test_dir(State, AppAcc, [App|Rest], Dir) ->
maybe_inject_test_dir(State, AppAcc, [], Dir) ->
case rebar_file_utils:path_from_ancestor(Dir, rebar_state:dir(State)) of
{ok, []} ->
- ?WARN("Can't have suites in root of project dir, dropping from tests", []),
- {State, AppAcc};
+ %% normal operation involves copying the entire directory a
+ %% suite exists in but if the suite is in the root directory
+ %% that results in a loop as we copy `_build' into itself
+ %% instead copy only files ending in `.erl' and directories
+ %% ending in `_SUITE_data' in the `_build/PROFILE/extras' dir
+ ExtrasDir = filename:join([rebar_dir:base_dir(State), "extras"]),
+ ok = copy_bare_suites(Dir, ExtrasDir),
+ Opts = inject_test_dir(rebar_state:opts(State), ExtrasDir),
+ {rebar_state:opts(State, Opts), AppAcc};
{ok, Path} ->
Opts = inject_test_dir(rebar_state:opts(State), Path),
{rebar_state:opts(State, Opts), AppAcc};
@@ -393,6 +411,14 @@ maybe_inject_test_dir(State, AppAcc, [], Dir) ->
{State, AppAcc}
end.
+copy_bare_suites(From, To) ->
+ filelib:ensure_dir(filename:join([To, "dummy.txt"])),
+ SrcFiles = rebar_utils:find_files(From, ".*\\.[e|h]rl\$", false),
+ DataDirs = lists:filter(fun filelib:is_dir/1,
+ filelib:wildcard(filename:join([From, "*_SUITE_data"]))),
+ ok = rebar_file_utils:cp_r(SrcFiles, To),
+ rebar_file_utils:cp_r(DataDirs, To).
+
inject_test_dir(Opts, Dir) ->
%% append specified test targets to app defined `extra_src_dirs`
ExtraSrcDirs = rebar_opts:get(Opts, extra_src_dirs, []),
diff --git a/src/rebar_prv_compile.erl b/src/rebar_prv_compile.erl
index 2996aee..d57b82b 100644
--- a/src/rebar_prv_compile.erl
+++ b/src/rebar_prv_compile.erl
@@ -217,6 +217,10 @@ copy(OldAppDir, AppDir, Dir) ->
%% TODO: use ec_file:copy/2 to do this, it preserves timestamps and
%% may prevent recompilation of files in extra dirs
+copy(Source, Source) ->
+ %% allow users to specify a directory in _build as a directory
+ %% containing additional source/tests
+ ok;
copy(Source, Target) ->
%% important to do this so no files are copied onto themselves
%% which truncates them to zero length on some platforms
diff --git a/src/rebar_prv_cover.erl b/src/rebar_prv_cover.erl
index 15a067e..c915141 100644
--- a/src/rebar_prv_cover.erl
+++ b/src/rebar_prv_cover.erl
@@ -207,6 +207,8 @@ format_table(Stats, CoverFiles) ->
MaxLength = max(lists:foldl(fun max_length/2, 0, Stats), 20),
Header = header(MaxLength),
Seperator = seperator(MaxLength),
+ TotalLabel = format("total", MaxLength),
+ TotalCov = format(calculate_total(Stats), 8),
[io_lib:format("~ts~n~ts~n~ts~n", [Seperator, Header, Seperator]),
lists:map(fun({Mod, Coverage}) ->
Name = format(Mod, MaxLength),
@@ -214,6 +216,8 @@ format_table(Stats, CoverFiles) ->
io_lib:format(" | ~ts | ~ts |~n", [Name, Cov])
end, Stats),
io_lib:format("~ts~n", [Seperator]),
+ io_lib:format(" | ~ts | ~ts |~n", [TotalLabel, TotalCov]),
+ io_lib:format("~ts~n", [Seperator]),
io_lib:format(" coverage calculated from:~n", []),
lists:map(fun(File) ->
io_lib:format(" ~ts~n", [File])
@@ -234,6 +238,18 @@ seperator(Width) ->
format(String, Width) -> io_lib:format("~*.ts", [Width, String]).
+calculate_total(Stats) when length(Stats) =:= 0 ->
+ "0%";
+calculate_total(Stats) ->
+ TotalStats = length(Stats),
+ TotalCovInt = round(lists:foldl(
+ fun({_Mod, Coverage, _File}, Acc) ->
+ Acc + (list_to_integer(string:strip(Coverage, right, $%)) / TotalStats);
+ ({_Mod, Coverage}, Acc) ->
+ Acc + (list_to_integer(string:strip(Coverage, right, $%)) / TotalStats)
+ end, 0, Stats)),
+ integer_to_list(TotalCovInt) ++ "%".
+
write_index(State, Coverage) ->
CoverDir = cover_dir(State),
FileName = filename:join([CoverDir, "index.html"]),
@@ -265,6 +281,8 @@ write_index_section(F, [{Section, DataFile, Mods}|Rest]) ->
[strip_coverdir(Report), Mod, Cov])
end,
lists:foreach(fun(M) -> ok = file:write(F, FmtLink(M)) end, Mods),
+ ok = file:write(F, ?FMT("<tr><td><strong>Total</strong></td><td>~ts</td>\n",
+ [calculate_total(Mods)])),
ok = file:write(F, "</table>\n"),
write_index_section(F, Rest).
diff --git a/src/rebar_prv_new.erl b/src/rebar_prv_new.erl
index 28572a9..064315e 100644
--- a/src/rebar_prv_new.erl
+++ b/src/rebar_prv_new.erl
@@ -7,6 +7,7 @@
format_error/1]).
-include("rebar.hrl").
+-include_lib("providers/include/providers.hrl").
-define(PROVIDER, new).
-define(DEPS, []).
@@ -35,16 +36,16 @@ do(State) ->
case strip_flags(rebar_state:command_args(State)) of
["help"] ->
?CONSOLE("Call `rebar3 new help <template>` for a detailed description~n", []),
- show_short_templates(rebar_templater:list_templates(State)),
+ show_short_templates(list_templates(State)),
{ok, State};
["help", TemplateName] ->
- case lists:keyfind(TemplateName, 1, rebar_templater:list_templates(State)) of
+ case lists:keyfind(TemplateName, 1, list_templates(State)) of
false -> ?CONSOLE("template not found.", []);
Term -> show_template(Term)
end,
{ok, State};
[TemplateName | Opts] ->
- case lists:keyfind(TemplateName, 1, rebar_templater:list_templates(State)) of
+ case lists:keyfind(TemplateName, 1, list_templates(State)) of
false ->
?CONSOLE("template not found.", []);
_ ->
@@ -53,11 +54,13 @@ do(State) ->
end,
{ok, State};
[] ->
- show_short_templates(rebar_templater:list_templates(State)),
+ show_short_templates(list_templates(State)),
{ok, State}
end.
-spec format_error(any()) -> iolist().
+format_error({consult, File, Reason}) ->
+ io_lib:format("Error consulting file at ~s for reason ~p", [File, Reason]);
format_error(Reason) ->
io_lib:format("~p", [Reason]).
@@ -65,6 +68,15 @@ format_error(Reason) ->
%% Internal functions
%% ===================================================================
+list_templates(State) ->
+ lists:foldl(fun({error, {consult, File, Reason}}, Acc) ->
+ ?WARN("Error consulting template file ~s for reason ~p",
+ [File, Reason]),
+ Acc
+ ; (Tpl, Acc) ->
+ [Tpl|Acc]
+ end, [], lists:reverse(rebar_templater:list_templates(State))).
+
info() ->
io_lib:format(
"Create rebar3 project based on template and vars.~n"
@@ -120,10 +132,13 @@ show_template({Name, Type, Location, Description, Vars}) ->
format_vars(Vars)]).
format_type(escript) -> "built-in";
+format_type(plugin) -> "plugin";
format_type(file) -> "custom".
format_type(escript, _) ->
"built-in template";
+format_type(plugin, Loc) ->
+ io_lib:format("plugin template (~s)", [Loc]);
format_type(file, Loc) ->
io_lib:format("custom template (~s)", [Loc]).
diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl
index 33d757a..1cdf6af 100644
--- a/src/rebar_prv_update.erl
+++ b/src/rebar_prv_update.erl
@@ -36,26 +36,36 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
try
- RegistryDir = rebar_packages:registry_dir(State),
- filelib:ensure_dir(filename:join(RegistryDir, "dummy")),
- HexFile = filename:join(RegistryDir, "registry"),
- ?INFO("Updating package registry...", []),
- TmpDir = ec_file:insecure_mkdtemp(),
- TmpFile = filename:join(TmpDir, "packages.gz"),
+ case rebar_packages:registry_dir(State) of
+ {ok, RegistryDir} ->
+ filelib:ensure_dir(filename:join(RegistryDir, "dummy")),
+ HexFile = filename:join(RegistryDir, "registry"),
+ ?INFO("Updating package registry...", []),
+ TmpDir = ec_file:insecure_mkdtemp(),
+ TmpFile = filename:join(TmpDir, "packages.gz"),
- Url = rebar_state:get(State, rebar_packages_cdn, ?DEFAULT_HEX_REGISTRY),
- case httpc:request(get, {Url, []},
- [], [{stream, TmpFile}, {sync, true}],
- rebar) of
- {ok, saved_to_file} ->
- {ok, Data} = file:read_file(TmpFile),
- Unzipped = zlib:gunzip(Data),
- ok = file:write_file(HexFile, Unzipped),
- ?INFO("Writing registry to ~s", [HexFile]),
- hex_to_index(State),
- {ok, State};
- _ ->
- ?PRV_ERROR(package_index_download)
+ CDN = rebar_state:get(State, rebar_packages_cdn, ?DEFAULT_CDN),
+ case rebar_utils:url_append_path(CDN, ?REMOTE_REGISTRY_FILE) of
+ {ok, Url} ->
+ ?DEBUG("Fetching registry from ~p", [Url]),
+ case httpc:request(get, {Url, []},
+ [], [{stream, TmpFile}, {sync, true}],
+ rebar) of
+ {ok, saved_to_file} ->
+ {ok, Data} = file:read_file(TmpFile),
+ Unzipped = zlib:gunzip(Data),
+ ok = file:write_file(HexFile, Unzipped),
+ ?INFO("Writing registry to ~s", [HexFile]),
+ hex_to_index(State),
+ {ok, State};
+ _ ->
+ ?PRV_ERROR(package_index_download)
+ end;
+ _ ->
+ ?PRV_ERROR({package_parse_cdn, CDN})
+ end;
+ {uri_parse_error, CDN} ->
+ ?PRV_ERROR({package_parse_cdn, CDN})
end
catch
_E:C ->
@@ -64,6 +74,8 @@ do(State) ->
end.
-spec format_error(any()) -> iolist().
+format_error({package_parse_cdn, Uri}) ->
+ io_lib:format("Failed to parse CDN url: ~p", [Uri]);
format_error(package_index_download) ->
"Failed to download package index.";
format_error(package_index_write) ->
@@ -75,7 +87,7 @@ is_supported(<<"rebar3">>) -> true;
is_supported(_) -> false.
hex_to_index(State) ->
- RegistryDir = rebar_packages:registry_dir(State),
+ {ok, RegistryDir} = rebar_packages:registry_dir(State),
HexFile = filename:join(RegistryDir, "registry"),
try ets:file2tab(HexFile) of
{ok, Registry} ->
diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl
index c7fb2af..f687637 100644
--- a/src/rebar_templater.erl
+++ b/src/rebar_templater.erl
@@ -59,10 +59,14 @@ list_templates(State) ->
%% Expand a single template's value
list_template(Files, {Name, Type, File}, State) ->
- TemplateTerms = consult(load_file(Files, Type, File)),
- {Name, Type, File,
- get_template_description(TemplateTerms),
- get_template_vars(TemplateTerms, State)}.
+ case consult(load_file(Files, Type, File)) of
+ {error, Reason} ->
+ {error, {consult, File, Reason}};
+ TemplateTerms ->
+ {Name, Type, File,
+ get_template_description(TemplateTerms),
+ get_template_vars(TemplateTerms, State)}
+ end.
%% Load up the template description out from a list of attributes read in
%% a .template file.
@@ -235,6 +239,7 @@ replace_var([H|T], Acc, Vars) ->
%% Load a list of all the files in the escript and on disk
find_templates(State) ->
DiskTemplates = find_disk_templates(State),
+ PluginTemplates = find_plugin_templates(State),
{MainTemplates, Files} =
case rebar_state:escript_path(State) of
undefined ->
@@ -245,19 +250,23 @@ find_templates(State) ->
F = cache_escript_files(State),
{find_escript_templates(F), F}
end,
- AvailTemplates = find_available_templates(DiskTemplates,
- MainTemplates),
+ AvailTemplates = find_available_templates([MainTemplates,
+ PluginTemplates,
+ DiskTemplates]),
?DEBUG("Available templates: ~p\n", [AvailTemplates]),
{AvailTemplates, Files}.
-find_available_templates(TemplateList1, TemplateList2) ->
- AvailTemplates = prioritize_templates(
- tag_names(TemplateList1),
- tag_names(TemplateList2)),
-
+find_available_templates(TemplateListList) ->
+ AvailTemplates = prioritize_templates(TemplateListList),
?DEBUG("Available templates: ~p\n", [AvailTemplates]),
AvailTemplates.
+prioritize_templates([TemplateList]) ->
+ tag_names(TemplateList);
+prioritize_templates([TemplateList | TemplateListList]) ->
+ prioritize_templates(tag_names(TemplateList),
+ prioritize_templates(TemplateListList)).
+
%% Scan the current escript for available files
cache_escript_files(State) ->
{ok, Files} = rebar_utils:escript_foldl(
@@ -295,6 +304,14 @@ find_other_templates(State) ->
rebar_utils:find_files(TemplateDir, ?TEMPLATE_RE)
end.
+%% Fetch template indexes that sit on disk in plugins
+find_plugin_templates(State) ->
+ [{plugin, File}
+ || App <- rebar_state:all_plugin_deps(State),
+ Priv <- [rebar_app_info:priv_dir(App)],
+ Priv =/= undefined,
+ File <- rebar_utils:find_files(Priv, ?TEMPLATE_RE)].
+
%% Take an existing list of templates and tag them by name the way
%% the user would enter it from the CLI
tag_names(List) ->
@@ -312,6 +329,10 @@ prioritize_templates([{Name, Type, File} | Rest], Valid) ->
?DEBUG("Skipping template ~p, due to presence of a built-in "
"template with the same name", [Name]),
prioritize_templates(Rest, Valid);
+ {_, plugin, _} ->
+ ?DEBUG("Skipping template ~p, due to presence of a plugin "
+ "template with the same name", [Name]),
+ prioritize_templates(Rest, Valid);
{_, file, _} ->
?DEBUG("Skipping template ~p, due to presence of a custom "
"template at ~s", [Name, File]),
@@ -323,6 +344,9 @@ prioritize_templates([{Name, Type, File} | Rest], Valid) ->
load_file(Files, escript, Name) ->
{Name, Bin} = lists:keyfind(Name, 1, Files),
Bin;
+load_file(_Files, plugin, Name) ->
+ {ok, Bin} = file:read_file(Name),
+ Bin;
load_file(_Files, file, Name) ->
{ok, Bin} = file:read_file(Name),
Bin.
@@ -338,8 +362,10 @@ consult(Cont, Str, Acc) ->
{done, Result, Remaining} ->
case Result of
{ok, Tokens, _} ->
- {ok, Term} = erl_parse:parse_term(Tokens),
- consult([], Remaining, [Term | Acc]);
+ case erl_parse:parse_term(Tokens) of
+ {ok, Term} -> consult([], Remaining, [Term | Acc]);
+ {error, Reason} -> {error, Reason}
+ end;
{eof, _Other} ->
lists:reverse(Acc);
{error, Info, _} ->
diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl
index fb651c3..746c57a 100644
--- a/src/rebar_utils.erl
+++ b/src/rebar_utils.erl
@@ -61,6 +61,7 @@
tup_find/2,
line_count/1,
set_httpc_options/0,
+ url_append_path/2,
escape_chars/1,
escape_double_quotes/1,
escape_double_quotes_weak/1,
@@ -798,6 +799,15 @@ set_httpc_options(Scheme, Proxy) ->
{ok, {_, _, Host, Port, _, _}} = http_uri:parse(Proxy),
httpc:set_options([{Scheme, {{Host, Port}, []}}], rebar).
+url_append_path(Url, ExtraPath) ->
+ case http_uri:parse(Url) of
+ {ok, {Scheme, UserInfo, Host, Port, Path, Query}} ->
+ {ok, lists:append([atom_to_list(Scheme), "://", UserInfo, Host, ":", integer_to_list(Port),
+ filename:join(Path, ExtraPath), "?", Query])};
+ _ ->
+ error
+ end.
+
%% escape\ as\ a\ shell\?
escape_chars(Str) when is_atom(Str) ->
escape_chars(atom_to_list(Str));
diff --git a/test/rebar_ct_SUITE.erl b/test/rebar_ct_SUITE.erl
index 267a9ee..183a1d0 100644
--- a/test/rebar_ct_SUITE.erl
+++ b/test/rebar_ct_SUITE.erl
@@ -17,6 +17,8 @@
multi_suite/1,
all_suite/1,
single_dir_and_single_suite/1,
+ suite_at_root/1,
+ suite_at_app_root/1,
data_dir_correct/1,
cmd_label/1,
cmd_config/1,
@@ -72,7 +74,9 @@ groups() -> [{basic_app, [], [basic_app_default_dirs,
single_unmanaged_suite,
multi_suite,
all_suite,
- single_dir_and_single_suite]},
+ single_dir_and_single_suite,
+ suite_at_root,
+ suite_at_app_root]},
{data_dirs, [], [data_dir_correct]},
{ct_opts, [], [cmd_label,
cmd_config,
@@ -177,6 +181,22 @@ init_per_group(dirs_and_suites, Config) ->
ok = filelib:ensure_dir(Suite3),
ok = file:write_file(Suite3, test_suite("extras")),
+ Suite4 = filename:join([AppDir, "root_SUITE.erl"]),
+ ok = file:write_file(Suite4, test_suite("root")),
+
+ ok = file:write_file(filename:join([AppDir, "root_SUITE.hrl"]), <<>>),
+
+ ok = filelib:ensure_dir(filename:join([AppDir, "root_SUITE_data", "dummy.txt"])),
+ ok = file:write_file(filename:join([AppDir, "root_SUITE_data", "some_data.txt"]), <<>>),
+
+ Suite5 = filename:join([AppDir, "apps", Name2, "app_root_SUITE.erl"]),
+ ok = file:write_file(Suite5, test_suite("app_root")),
+
+ ok = file:write_file(filename:join([AppDir, "apps", Name2, "app_root_SUITE.hrl"]), <<>>),
+
+ ok = filelib:ensure_dir(filename:join([AppDir, "apps", Name2, "app_root_SUITE_data", "dummy.txt"])),
+ ok = file:write_file(filename:join([AppDir, "apps", Name2, "app_root_SUITE_data", "some_data.txt"]), <<>>),
+
{ok, State} = rebar_test_utils:run_and_check(C, [], ["as", "test", "lock"], return),
[{s, State}, {appnames, [Name1, Name2]}|C];
@@ -603,6 +623,79 @@ single_dir_and_single_suite(Config) ->
Suite = proplists:get_value(suite, Opts),
["extra_SUITE"] = Suite.
+suite_at_root(Config) ->
+ AppDir = ?config(apps, Config),
+ State = ?config(s, Config),
+
+ LibDirs = rebar_dir:lib_dirs(State),
+ State1 = rebar_app_discover:do(State, LibDirs),
+
+ Providers = rebar_state:providers(State1),
+ Namespace = rebar_state:namespace(State1),
+ CommandProvider = providers:get_provider(ct, Providers, Namespace),
+ GetOptSpec = providers:opts(CommandProvider),
+ {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--suite=" ++ filename:join([AppDir, "root_SUITE"])]),
+
+ State2 = rebar_state:command_parsed_args(State1, GetOptResult),
+
+ Tests = rebar_prv_common_test:prepare_tests(State2),
+ {ok, NewState} = rebar_prv_common_test:compile(State2, Tests),
+ {ok, T} = Tests,
+ Opts = rebar_prv_common_test:translate_paths(NewState, T),
+
+ Suite = proplists:get_value(suite, Opts),
+ Expected = filename:join([AppDir, "_build", "test", "extras", "root_SUITE"]),
+ [Expected] = Suite,
+
+ TestHrl = filename:join([AppDir, "_build", "test", "extras", "root_SUITE.hrl"]),
+ true = filelib:is_file(TestHrl),
+
+ TestBeam = filename:join([AppDir, "_build", "test", "extras", "root_SUITE.beam"]),
+ true = filelib:is_file(TestBeam),
+
+ DataDir = filename:join([AppDir, "_build", "test", "extras", "root_SUITE_data"]),
+ true = filelib:is_dir(DataDir),
+
+ DataFile = filename:join([AppDir, "_build", "test", "extras", "root_SUITE_data", "some_data.txt"]),
+ true = filelib:is_file(DataFile).
+
+suite_at_app_root(Config) ->
+ AppDir = ?config(apps, Config),
+ [_Name1, Name2] = ?config(appnames, Config),
+ State = ?config(s, Config),
+
+ LibDirs = rebar_dir:lib_dirs(State),
+ State1 = rebar_app_discover:do(State, LibDirs),
+
+ Providers = rebar_state:providers(State1),
+ Namespace = rebar_state:namespace(State1),
+ CommandProvider = providers:get_provider(ct, Providers, Namespace),
+ GetOptSpec = providers:opts(CommandProvider),
+ {ok, GetOptResult} = getopt:parse(GetOptSpec, ["--suite=" ++ filename:join([AppDir, "apps", Name2, "app_root_SUITE"])]),
+
+ State2 = rebar_state:command_parsed_args(State1, GetOptResult),
+
+ Tests = rebar_prv_common_test:prepare_tests(State2),
+ {ok, NewState} = rebar_prv_common_test:compile(State2, Tests),
+ {ok, T} = Tests,
+ Opts = rebar_prv_common_test:translate_paths(NewState, T),
+
+ Suite = proplists:get_value(suite, Opts),
+ Expected = filename:join([AppDir, "_build", "test", "extras", "apps", Name2, "app_root_SUITE"]),
+ [Expected] = Suite,
+
+ TestHrl = filename:join([AppDir, "_build", "test", "extras", "apps", Name2, "app_root_SUITE.hrl"]),
+ true = filelib:is_file(TestHrl),
+
+ TestBeam = filename:join([AppDir, "_build", "test", "extras", "apps", Name2, "app_root_SUITE.beam"]),
+ true = filelib:is_file(TestBeam),
+
+ DataDir = filename:join([AppDir, "_build", "test", "extras", "apps", Name2, "app_root_SUITE_data"]),
+ true = filelib:is_dir(DataDir),
+
+ DataFile = filename:join([AppDir, "_build", "test", "extras", "root_SUITE_data", "some_data.txt"]),
+ true = filelib:is_file(DataFile).
+
%% this test probably only fails when this suite is run via rebar3 with the --cover flag
data_dir_correct(Config) ->
DataDir = ?config(data_dir, Config),
diff --git a/test/rebar_dir_SUITE.erl b/test/rebar_dir_SUITE.erl
index 526f827..1221db7 100644
--- a/test/rebar_dir_SUITE.erl
+++ b/test/rebar_dir_SUITE.erl
@@ -5,7 +5,7 @@
-export([default_src_dirs/1, default_extra_src_dirs/1, default_all_src_dirs/1]).
-export([src_dirs/1, extra_src_dirs/1, all_src_dirs/1]).
-export([profile_src_dirs/1, profile_extra_src_dirs/1, profile_all_src_dirs/1]).
--export([retarget_path/1]).
+-export([retarget_path/1, alt_base_dir_abs/1, alt_base_dir_rel/1]).
-include_lib("common_test/include/ct.hrl").
-include_lib("eunit/include/eunit.hrl").
@@ -15,7 +15,7 @@
all() -> [default_src_dirs, default_extra_src_dirs, default_all_src_dirs,
src_dirs, extra_src_dirs, all_src_dirs,
profile_src_dirs, profile_extra_src_dirs, profile_all_src_dirs,
- retarget_path].
+ retarget_path, alt_base_dir_abs, alt_base_dir_rel].
init_per_testcase(_, Config) ->
C = rebar_test_utils:init_rebar_state(Config),
@@ -124,4 +124,41 @@ retarget_path(Config) ->
?assertEqual(filename:join([BaseDir, "some_other_dir"]),
rebar_dir:retarget_path(State, filename:join([rebar_dir:root_dir(State), "some_other_dir"]))),
?assertEqual("/somewhere/outside/the/project",
- rebar_dir:retarget_path(State, "/somewhere/outside/the/project")). \ No newline at end of file
+ rebar_dir:retarget_path(State, "/somewhere/outside/the/project")).
+
+alt_base_dir_abs(Config) ->
+ AltName = lists:flatten(io_lib:format("~p", [os:timestamp()])),
+ AltBaseDir = filename:join(?config(priv_dir, Config), AltName),
+ RebarConfig = [{base_dir, AltBaseDir}],
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return),
+
+ BaseDir = rebar_dir:base_dir(State),
+ ?assertEqual(filename:join(AltBaseDir, "default"), BaseDir),
+
+ Name1 = ?config(app_one, Config),
+ Name2 = ?config(app_two, Config),
+
+ ?assert(filelib:is_dir(filename:join([BaseDir, "lib", Name1, "ebin"]))),
+ ?assert(filelib:is_file(filename:join([BaseDir, "lib", Name1, "ebin", Name1++".app"]))),
+ ?assert(filelib:is_file(filename:join([BaseDir, "lib", Name1, "ebin", Name1++".beam"]))),
+ ?assert(filelib:is_dir(filename:join([BaseDir, "lib", Name2, "ebin"]))),
+ ?assert(filelib:is_file(filename:join([BaseDir, "lib", Name2, "ebin", Name2++".app"]))),
+ ?assert(filelib:is_file(filename:join([BaseDir, "lib", Name2, "ebin", Name2++".beam"]))).
+
+alt_base_dir_rel(Config) ->
+ AltName = lists:flatten(io_lib:format("~p", [os:timestamp()])),
+ AltBaseDir = filename:join("..", AltName),
+ RebarConfig = [{base_dir, AltBaseDir}],
+ {ok, State} = rebar_test_utils:run_and_check(Config, RebarConfig, ["compile"], return),
+
+ BaseDir = rebar_dir:base_dir(State),
+
+ Name1 = ?config(app_one, Config),
+ Name2 = ?config(app_two, Config),
+
+ ?assert(filelib:is_dir(filename:join([BaseDir, "lib", Name1, "ebin"]))),
+ ?assert(filelib:is_file(filename:join([BaseDir, "lib", Name1, "ebin", Name1++".app"]))),
+ ?assert(filelib:is_file(filename:join([BaseDir, "lib", Name1, "ebin", Name1++".beam"]))),
+ ?assert(filelib:is_dir(filename:join([BaseDir, "lib", Name2, "ebin"]))),
+ ?assert(filelib:is_file(filename:join([BaseDir, "lib", Name2, "ebin", Name2++".app"]))),
+ ?assert(filelib:is_file(filename:join([BaseDir, "lib", Name2, "ebin", Name2++".beam"]))).
diff --git a/test/rebar_new_SUITE.erl b/test/rebar_new_SUITE.erl
index b514a2b..1971be6 100644
--- a/test/rebar_new_SUITE.erl
+++ b/test/rebar_new_SUITE.erl
@@ -7,9 +7,25 @@
-include_lib("eunit/include/eunit.hrl").
all() -> [app_git_user, app_hg_user, app_with_fallbacks,
- app_with_flags1, app_with_flags2].
+ app_with_flags1, app_with_flags2, plugin_tpl].
+init_per_testcase(plugin_tpl, Config) ->
+ application:load(rebar),
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ Name = rebar_test_utils:create_random_name("plugin_tpl"),
+ AppsDir = filename:join([PrivDir, rebar_test_utils:create_random_name(Name)]),
+ ec_file:copy(filename:join([DataDir, "plugin_tpl"]), AppsDir, [recursive]),
+ Verbosity = rebar3:log_level(),
+ rebar_log:init(command_line, Verbosity),
+ GlobalDir = filename:join([DataDir, "cache"]),
+ State = rebar_state:new([{base_dir, filename:join([AppsDir, "_build"])}
+ ,{global_rebar_dir, GlobalDir}
+ ,{root_dir, AppsDir}]),
+ mock_home_dir(DataDir),
+ mock_empty_escript_templates(),
+ [{apps, AppsDir}, {state, State}, {name, Name} | Config];
init_per_testcase(Case, Config0) ->
Config = rebar_test_utils:init_rebar_state(Config0),
Name = rebar_test_utils:create_random_name(atom_to_list(Case)),
@@ -132,11 +148,24 @@ app_with_flags2(Config) ->
{filename:join(["src", Name++"_app.erl"]), [Name]}
]).
+plugin_tpl(Config) ->
+ Name = ?config(name, Config),
+ rebar_test_utils:run_and_check(
+ Config, [],
+ ["new", "-f", "tpl", Name],
+ {ok, []}
+ ),
+ Result = filename:join(["src", Name++".erl"]), % In CWD
+ {ok, Bin} = file:read_file(Result),
+ {match, _} = re:run(Bin, Name, [multiline,global]).
+
validate_files(_Config, Name, Checks) ->
[begin
Path = filename:join([Name, File]),
+ ct:pal("validating ~s for content", [Path]),
{ok, Bin} = file:read_file(Path),
[{match, _} = re:run(Bin, Pattern, [multiline,global])
|| Pattern <- Patterns]
end || {File, Patterns} <- Checks],
ok.
+
diff --git a/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/LICENSE.dtl b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/LICENSE.dtl
new file mode 100644
index 0000000..41588ab
--- /dev/null
+++ b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/LICENSE.dtl
@@ -0,0 +1,29 @@
+Copyright (c) {{copyright_year}}, {{author_name}} <{{author_email}}>.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+* The names of its contributors may not be used to endorse or promote
+ products derived from this software without specific prior written
+ permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/README.md.dtl b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/README.md.dtl
new file mode 100644
index 0000000..5507536
--- /dev/null
+++ b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/README.md.dtl
@@ -0,0 +1,9 @@
+{{name}}
+=====
+
+{{desc}}
+
+Build
+-----
+
+ $ rebar3 compile
diff --git a/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/app.erl.dtl b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/app.erl.dtl
new file mode 100644
index 0000000..83eb9a3
--- /dev/null
+++ b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/app.erl.dtl
@@ -0,0 +1,27 @@
+%%%-------------------------------------------------------------------
+%% @doc {{name}} public API
+%% @end
+%%%-------------------------------------------------------------------
+
+-module({{name}}_app).
+
+-behaviour(application).
+
+%% Application callbacks
+-export([start/2
+ ,stop/1]).
+
+%%====================================================================
+%% API
+%%====================================================================
+
+start(_StartType, _StartArgs) ->
+ {{name}}_sup:start_link().
+
+%%--------------------------------------------------------------------
+stop(_State) ->
+ ok.
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/bad_index.template b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/bad_index.template
new file mode 100644
index 0000000..50998cc
--- /dev/null
+++ b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/bad_index.template
@@ -0,0 +1,13 @@
+{description, "OTP Application"}.
+{variables, [
+ {name, "mylib", "Name of the OTP application"},
+ {desc, "An OTP application", "Short description of the app"}
+]}.
+bad_term,
+{template, "app.erl.dtl", "{{name}}/src/{{name}}_app.erl"}.
+{template, "sup.erl.dtl", "{{name}}/src/{{name}}_sup.erl"}.
+{template, "otp_app.app.src.dtl", "{{name}}/src/{{name}}.app.src"}.
+{template, "rebar.config.dtl", "{{name}}/rebar.config"}.
+{template, "gitignore.dtl", "{{name}}/.gitignore"}.
+{template, "LICENSE.dtl", "{{name}}/LICENSE"}.
+{template, "README.md.dtl", "{{name}}/README.md"}.
diff --git a/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/gitignore.dtl b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/gitignore.dtl
new file mode 100644
index 0000000..40a1d4f
--- /dev/null
+++ b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/gitignore.dtl
@@ -0,0 +1,18 @@
+.rebar3
+_*
+.eunit
+*.o
+*.beam
+*.plt
+*.swp
+*.swo
+.erlang.cookie
+ebin
+log
+erl_crash.dump
+.rebar
+_rel
+_deps
+_plugins
+_tdeps
+logs
diff --git a/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/otp_app.app.src.dtl b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/otp_app.app.src.dtl
new file mode 100644
index 0000000..5188f56
--- /dev/null
+++ b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/otp_app.app.src.dtl
@@ -0,0 +1,12 @@
+{application, {{name}},
+ [{description, "{{desc}}"}
+ ,{vsn, "0.1.0"}
+ ,{registered, []}
+ ,{mod, {'{{name}}_app', []}}
+ ,{applications,
+ [kernel
+ ,stdlib
+ ]}
+ ,{env,[]}
+ ,{modules, []}
+ ]}.
diff --git a/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/rebar.config.dtl b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/rebar.config.dtl
new file mode 100644
index 0000000..f618f3e
--- /dev/null
+++ b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/rebar.config.dtl
@@ -0,0 +1,2 @@
+{erl_opts, [debug_info]}.
+{deps, []}. \ No newline at end of file
diff --git a/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/sup.erl.dtl b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/sup.erl.dtl
new file mode 100644
index 0000000..a2e7209
--- /dev/null
+++ b/test/rebar_new_SUITE_data/.rebar3/templates/bad_index/sup.erl.dtl
@@ -0,0 +1,35 @@
+%%%-------------------------------------------------------------------
+%% @doc {{name}} top level supervisor.
+%% @end
+%%%-------------------------------------------------------------------
+
+-module({{name}}_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_new_SUITE_data/plugin_tpl/_checkouts/tpl/.gitignore b/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/.gitignore
new file mode 100644
index 0000000..a939dce
--- /dev/null
+++ b/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/.gitignore
@@ -0,0 +1,19 @@
+.rebar3
+_*
+.eunit
+*.o
+*.beam
+*.plt
+*.swp
+*.swo
+.erlang.cookie
+ebin
+log
+erl_crash.dump
+.rebar
+_rel
+_deps
+_plugins
+_tdeps
+logs
+_build \ No newline at end of file
diff --git a/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/priv/module.erl.dtl b/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/priv/module.erl.dtl
new file mode 100644
index 0000000..9129961
--- /dev/null
+++ b/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/priv/module.erl.dtl
@@ -0,0 +1,2 @@
+-module({{name}}).
+
diff --git a/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/priv/tpl.template b/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/priv/tpl.template
new file mode 100644
index 0000000..7fa4caf
--- /dev/null
+++ b/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/priv/tpl.template
@@ -0,0 +1,7 @@
+{description, "A basic template"}.
+{variables, [
+ {name, "mod", "Name of the module"}
+]}.
+
+{dir, "test"}.
+{template, "module.erl.dtl", "src/{{name}}.erl"}.
diff --git a/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/rebar.config b/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/rebar.config
new file mode 100644
index 0000000..f618f3e
--- /dev/null
+++ b/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/rebar.config
@@ -0,0 +1,2 @@
+{erl_opts, [debug_info]}.
+{deps, []}. \ No newline at end of file
diff --git a/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/src/tpl.app.src b/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/src/tpl.app.src
new file mode 100644
index 0000000..6c6d811
--- /dev/null
+++ b/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/src/tpl.app.src
@@ -0,0 +1,15 @@
+{application, 'tpl',
+ [{description, "A rebar plugin"},
+ {vsn, "0.1.0"},
+ {registered, []},
+ {applications,
+ [kernel,
+ stdlib
+ ]},
+ {env,[]},
+ {modules, []},
+
+ {contributors, []},
+ {licenses, []},
+ {links, []}
+ ]}.
diff --git a/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/src/tpl.erl b/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/src/tpl.erl
new file mode 100644
index 0000000..529bcb8
--- /dev/null
+++ b/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/src/tpl.erl
@@ -0,0 +1,8 @@
+-module('tpl').
+
+-export([init/1]).
+
+-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
+init(State) ->
+ {ok, State1} = 'tpl_prv':init(State),
+ {ok, State1}.
diff --git a/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/src/tpl_prv.erl b/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/src/tpl_prv.erl
new file mode 100644
index 0000000..c68ffa3
--- /dev/null
+++ b/test/rebar_new_SUITE_data/plugin_tpl/_checkouts/tpl/src/tpl_prv.erl
@@ -0,0 +1,32 @@
+-module('tpl_prv').
+
+-export([init/1, do/1, format_error/1]).
+
+-define(PROVIDER, 'tpl').
+-define(DEPS, [app_discovery]).
+
+%% ===================================================================
+%% Public API
+%% ===================================================================
+-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
+init(State) ->
+ Provider = providers:create([
+ {name, ?PROVIDER}, % The 'user friendly' name of the task
+ {module, ?MODULE}, % The module implementation of the task
+ {bare, true}, % The task can be run by the user, always true
+ {deps, ?DEPS}, % The list of dependencies
+ {example, "rebar3 tpl"}, % How to use the plugin
+ {opts, []}, % list of options understood by the plugin
+ {short_desc, "A rebar plugin"},
+ {desc, "A rebar plugin"}
+ ]),
+ {ok, rebar_state:add_provider(State, Provider)}.
+
+
+-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
+do(State) ->
+ {ok, State}.
+
+-spec format_error(any()) -> iolist().
+format_error(Reason) ->
+ io_lib:format("~p", [Reason]).
diff --git a/test/rebar_new_SUITE_data/plugin_tpl/rebar.config b/test/rebar_new_SUITE_data/plugin_tpl/rebar.config
new file mode 100644
index 0000000..74d1fc5
--- /dev/null
+++ b/test/rebar_new_SUITE_data/plugin_tpl/rebar.config
@@ -0,0 +1,3 @@
+{erl_opts, [debug_info]}.
+{deps, []}.
+{plugins, [tpl]}.
diff --git a/test/rebar_new_SUITE_data/plugin_tpl/src/plugin_tpl.app.src b/test/rebar_new_SUITE_data/plugin_tpl/src/plugin_tpl.app.src
new file mode 100644
index 0000000..8f18874
--- /dev/null
+++ b/test/rebar_new_SUITE_data/plugin_tpl/src/plugin_tpl.app.src
@@ -0,0 +1,15 @@
+{application, 'plugin_tpl',
+ [{description, "An OTP library"},
+ {vsn, "0.1.0"},
+ {registered, []},
+ {applications,
+ [kernel,
+ stdlib
+ ]},
+ {env,[]},
+ {modules, []},
+
+ {contributors, []},
+ {licenses, []},
+ {links, []}
+ ]}.
diff --git a/test/rebar_new_SUITE_data/plugin_tpl/src/plugin_tpl.erl b/test/rebar_new_SUITE_data/plugin_tpl/src/plugin_tpl.erl
new file mode 100644
index 0000000..406bd97
--- /dev/null
+++ b/test/rebar_new_SUITE_data/plugin_tpl/src/plugin_tpl.erl
@@ -0,0 +1,13 @@
+-module('plugin_tpl').
+
+%% API exports
+-export([]).
+
+%%====================================================================
+%% API functions
+%%====================================================================
+
+
+%%====================================================================
+%% Internal functions
+%%====================================================================
diff --git a/test/rebar_pkg_SUITE.erl b/test/rebar_pkg_SUITE.erl
index af6bf42..9f19e0d 100644
--- a/test/rebar_pkg_SUITE.erl
+++ b/test/rebar_pkg_SUITE.erl
@@ -11,7 +11,8 @@
-define(good_checksum, <<"1C6CE379D191FBAB41B7905075E0BF87CBBE23C77CECE775C5A0B786B2244C35">>).
all() -> [good_uncached, good_cached, badindexchk, badpkg,
- bad_to_good, good_disconnect, bad_disconnect, pkgs_provider].
+ bad_to_good, good_disconnect, bad_disconnect, pkgs_provider,
+ find_highest_matching].
init_per_suite(Config) ->
application:start(meck),
@@ -30,8 +31,8 @@ init_per_testcase(pkgs_provider=Name, Config) ->
filelib:ensure_dir(filename:join([CacheDir, "registry"])),
ok = ets:tab2file(Tid, filename:join([CacheDir, "registry"])),
meck:new(rebar_packages, [passthrough]),
- meck:expect(rebar_packages, registry_dir, fun(_) -> CacheDir end),
- meck:expect(rebar_packages, package_dir, fun(_) -> CacheDir end),
+ meck:expect(rebar_packages, registry_dir, fun(_) -> {ok, CacheDir} end),
+ meck:expect(rebar_packages, package_dir, fun(_) -> {ok, CacheDir} end),
rebar_prv_update:hex_to_index(rebar_state:new()),
Config;
init_per_testcase(good_uncached=Name, Config0) ->
@@ -86,7 +87,12 @@ init_per_testcase(bad_disconnect=Name, Config0) ->
meck:unload(httpc),
meck:new(httpc, [passthrough, unsticky]),
meck:expect(httpc, request, fun(_, _, _, _, _) -> {error, econnrefused} end),
- Config.
+ Config;
+init_per_testcase(Name, Config0) ->
+ Config = [{good_cache, false},
+ {pkg, {<<"goodpkg">>, <<"1.0.0">>}}
+ | Config0],
+ mock_config(Name, Config).
end_per_testcase(_, Config) ->
unmock_config(Config),
@@ -172,6 +178,16 @@ pkgs_provider(Config) ->
{ok, []}
).
+find_highest_matching(_Config) ->
+ State = rebar_state:new(),
+ {ok, Vsn} = rebar_packages:find_highest_matching(<<"goodpkg">>, <<"1.0.0">>, package_index, State),
+ ?assertEqual(<<"1.0.1">>, Vsn),
+ {ok, Vsn1} = rebar_packages:find_highest_matching(<<"goodpkg">>, <<"1.0">>, package_index, State),
+ ?assertEqual(<<"1.1.1">>, Vsn1),
+ {ok, Vsn2} = rebar_packages:find_highest_matching(<<"goodpkg">>, <<"2.0">>, package_index, State),
+ ?assertEqual(<<"2.0.0">>, Vsn2).
+
+
%%%%%%%%%%%%%%%
%%% Helpers %%%
%%%%%%%%%%%%%%%
@@ -182,10 +198,13 @@ mock_config(Name, Config) ->
Tid = ets:new(registry_table, [public]),
ets:insert_new(Tid, [
{<<"badindexchk">>,[[<<"1.0.0">>]]},
- {<<"goodpkg">>,[[<<"1.0.0">>]]},
+ {<<"goodpkg">>,[[<<"1.0.0">>, <<"1.0.1">>, <<"1.1.1">>, <<"2.0.0">>]]},
{<<"badpkg">>,[[<<"1.0.0">>]]},
{{<<"badindexchk">>,<<"1.0.0">>}, [[], ?bad_checksum, [<<"rebar3">>]]},
{{<<"goodpkg">>,<<"1.0.0">>}, [[], ?good_checksum, [<<"rebar3">>]]},
+ {{<<"goodpkg">>,<<"1.0.1">>}, [[], ?good_checksum, [<<"rebar3">>]]},
+ {{<<"goodpkg">>,<<"1.1.1">>}, [[], ?good_checksum, [<<"rebar3">>]]},
+ {{<<"goodpkg">>,<<"2.0.0">>}, [[], ?good_checksum, [<<"rebar3">>]]},
{{<<"badpkg">>,<<"1.0.0">>}, [[], ?good_checksum, [<<"rebar3">>]]}
]),
CacheDir = filename:join([CacheRoot, "hex", "com", "test", "packages"]),
@@ -203,8 +222,8 @@ mock_config(Name, Config) ->
meck:expect(rebar_dir, global_cache_dir, fun(_) -> CacheRoot end),
meck:new(rebar_packages, [passthrough]),
- meck:expect(rebar_packages, registry_dir, fun(_) -> CacheDir end),
- meck:expect(rebar_packages, package_dir, fun(_) -> CacheDir end),
+ meck:expect(rebar_packages, registry_dir, fun(_) -> {ok, CacheDir} end),
+ meck:expect(rebar_packages, package_dir, fun(_) -> {ok, CacheDir} end),
rebar_prv_update:hex_to_index(rebar_state:new()),
%% Cache fetches are mocked -- we assume the server and clients are
diff --git a/test/rebar_pkg_alias_SUITE.erl b/test/rebar_pkg_alias_SUITE.erl
index f7fa5d4..fef2310 100644
--- a/test/rebar_pkg_alias_SUITE.erl
+++ b/test/rebar_pkg_alias_SUITE.erl
@@ -98,8 +98,8 @@ mock_config(Name, Config) ->
meck:expect(rebar_dir, global_cache_dir, fun(_) -> CacheRoot end),
meck:new(rebar_packages, [passthrough, no_link]),
- meck:expect(rebar_packages, registry_dir, fun(_) -> CacheDir end),
- meck:expect(rebar_packages, package_dir, fun(_) -> CacheDir end),
+ meck:expect(rebar_packages, registry_dir, fun(_) -> {ok, CacheDir} end),
+ meck:expect(rebar_packages, package_dir, fun(_) -> {ok, CacheDir} end),
rebar_prv_update:hex_to_index(rebar_state:new()),
%% Cache fetches are mocked -- we assume the server and clients are