diff options
40 files changed, 705 insertions, 109 deletions
@@ -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}. @@ -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 |