diff options
-rw-r--r-- | THANKS | 3 | ||||
-rw-r--r-- | rebar.config.sample | 2 | ||||
-rw-r--r-- | src/rebar3.erl | 10 | ||||
-rw-r--r-- | src/rebar_packages.erl | 40 | ||||
-rw-r--r-- | src/rebar_plugins.erl | 13 | ||||
-rw-r--r-- | src/rebar_prv_shell.erl | 16 | ||||
-rw-r--r-- | src/rebar_prv_update.erl | 122 | ||||
-rw-r--r-- | src/rebar_state.erl | 24 | ||||
-rw-r--r-- | test/mock_git_resource.erl | 11 | ||||
-rw-r--r-- | test/rebar_deps_SUITE.erl | 112 | ||||
-rw-r--r-- | test/rebar_pkg_SUITE.erl | 9 | ||||
-rw-r--r-- | test/rebar_plugins_SUITE.erl | 52 | ||||
-rw-r--r-- | test/rebar_test_utils.erl | 29 |
13 files changed, 377 insertions, 66 deletions
@@ -135,4 +135,5 @@ Pierre Fenoll David Kubecka Stefan Grundmann Carlos Eduardo de Paula -Derek Brown
\ No newline at end of file +Derek Brown +Heinz N. Gies diff --git a/rebar.config.sample b/rebar.config.sample index de70998..f57f8dc 100644 --- a/rebar.config.sample +++ b/rebar.config.sample @@ -211,7 +211,7 @@ %% apps to auto-boot with `rebar3 shell'; defaults to apps %% specified in a `relx' tuple, if any. -{shell_apps, [app1, app2]} +{shell, [{apps, [app1, app2]}]}. %% == xref == diff --git a/src/rebar3.erl b/src/rebar3.erl index 879378e..10eccfd 100644 --- a/src/rebar3.erl +++ b/src/rebar3.erl @@ -126,14 +126,16 @@ run_aux(State, RawArgs) -> {ok, Providers} = application:get_env(rebar, providers), %% Providers can modify profiles stored in opts, so set default after initializing providers State5 = rebar_state:create_logic_providers(Providers, State4), - State6 = rebar_plugins:top_level_install(State5), - State7 = rebar_state:default(State6, rebar_state:opts(State6)), + %% Initializing project_plugins which can override default providers + State6 = rebar_plugins:project_plugins_install(State5), + State7 = rebar_plugins:top_level_install(State6), + State8 = rebar_state:default(State7, rebar_state:opts(State7)), {Task, Args} = parse_args(RawArgs), - State8 = rebar_state:code_paths(State7, default, code:get_path()), + State9 = rebar_state:code_paths(State8, default, code:get_path()), - rebar_core:init_command(rebar_state:command_args(State8, Args), Task). + rebar_core:init_command(rebar_state:command_args(State9, Args), Task). init_config() -> %% Initialize logging system diff --git a/src/rebar_packages.erl b/src/rebar_packages.erl index c56009e..b5e0923 100644 --- a/src/rebar_packages.erl +++ b/src/rebar_packages.erl @@ -7,7 +7,9 @@ ,registry_dir/1 ,package_dir/1 ,registry_checksum/2 + ,find_highest_matching/6 ,find_highest_matching/4 + ,find_all/3 ,verify_table/1 ,format_error/1]). @@ -139,16 +141,26 @@ registry_checksum({pkg, Name, Vsn}, State) -> %% `~> 2.0` | `>= 2.0.0 and < 3.0.0` %% `~> 2.1` | `>= 2.1.0 and < 3.0.0` find_highest_matching(Dep, Constraint, Table, State) -> + find_highest_matching(undefined, undefined, Dep, Constraint, Table, State). + +find_highest_matching(Pkg, PkgVsn, Dep, Constraint, Table, State) -> + try find_all(Dep, Table, State) of + {ok, [Vsn]} -> + handle_single_vsn(Pkg, PkgVsn, Dep, Vsn, Constraint); + {ok, [HeadVsn | VsnTail]} -> + {ok, handle_vsns(Constraint, HeadVsn, VsnTail)} + catch + error:badarg -> + none + end. + +find_all(Dep, Table, State) -> ?MODULE:verify_table(State), try ets:lookup_element(Table, Dep, 2) of - [[HeadVsn | VsnTail]] -> - {ok, handle_vsns(Constraint, HeadVsn, VsnTail)}; - [[Vsn]] -> - handle_single_vsn(Dep, Vsn, Constraint); - [Vsn] -> - handle_single_vsn(Dep, Vsn, Constraint); - [HeadVsn | VsnTail] -> - {ok, handle_vsns(Constraint, HeadVsn, VsnTail)} + [Vsns] when is_list(Vsns)-> + {ok, Vsns}; + Vsns -> + {ok, Vsns} catch error:badarg -> none @@ -165,13 +177,19 @@ handle_vsns(Constraint, HeadVsn, VsnTail) -> end end, HeadVsn, VsnTail). -handle_single_vsn(Dep, Vsn, Constraint) -> +handle_single_vsn(Pkg, PkgVsn, Dep, Vsn, Constraint) -> case ec_semver:pes(Vsn, Constraint) of true -> {ok, Vsn}; false -> - ?WARN("Only existing version of ~s is ~s which does not match constraint ~~> ~s. " - "Using anyway, but it is not guaranteed to work.", [Dep, Vsn, Constraint]), + case {Pkg, PkgVsn} of + {undefined, undefined} -> + ?WARN("Only existing version of ~s is ~s which does not match constraint ~~> ~s. " + "Using anyway, but it is not guaranteed to work.", [Dep, Vsn, Constraint]); + _ -> + ?WARN("[~s:~s] Only existing version of ~s is ~s which does not match constraint ~~> ~s. " + "Using anyway, but it is not guaranteed to work.", [Pkg, PkgVsn, Dep, Vsn, Constraint]) + end, {ok, Vsn} end. diff --git a/src/rebar_plugins.erl b/src/rebar_plugins.erl index 3c33498..68ba6da 100644 --- a/src/rebar_plugins.erl +++ b/src/rebar_plugins.erl @@ -3,7 +3,8 @@ -module(rebar_plugins). --export([top_level_install/1 +-export([project_plugins_install/1 + ,top_level_install/1 ,project_apps_install/1 ,install/2 ,handle_plugins/3 @@ -15,6 +16,16 @@ %% Public API %% =================================================================== +-spec project_plugins_install(rebar_state:t()) -> rebar_state:t(). +project_plugins_install(State) -> + Profiles = rebar_state:current_profiles(State), + State1 = rebar_state:allow_provider_overrides(State, true), + State2 = lists:foldl(fun(Profile, StateAcc) -> + Plugins = rebar_state:get(State, {project_plugins, Profile}, []), + handle_plugins(Profile, Plugins, StateAcc) + end, State1, Profiles), + rebar_state:allow_provider_overrides(State2, false). + -spec top_level_install(rebar_state:t()) -> rebar_state:t(). top_level_install(State) -> Profiles = rebar_state:current_profiles(State), diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl index ea759fc..430d1e8 100644 --- a/src/rebar_prv_shell.erl +++ b/src/rebar_prv_shell.erl @@ -64,6 +64,8 @@ init(State) -> "Gives a long name to the node."}, {sname, undefined, "sname", atom, "Gives a short name to the node."}, + {setcookie, undefined, "setcookie", atom, + "Sets the cookie if the node is distributed."}, {script_file, undefined, "script", string, "Path to an escript file to run before " "starting the project apps. Defaults to " @@ -258,9 +260,11 @@ setup_name(State) -> {undefined, undefined} -> ok; {Name, undefined} -> - check_epmd(net_kernel:start([Name, longnames])); + check_epmd(net_kernel:start([Name, longnames])), + setup_cookie(Opts); {undefined, SName} -> - check_epmd(net_kernel:start([SName, shortnames])); + check_epmd(net_kernel:start([SName, shortnames])), + setup_cookie(Opts); {_, _} -> ?ABORT("Cannot have both short and long node names defined", []) end. @@ -271,6 +275,14 @@ check_epmd({error,{{shutdown, {_,net_kernel,{'EXIT',nodistribution}}},_}}) -> check_epmd(_) -> ok. +setup_cookie(Opts) -> + case {node(), proplists:get_value(setcookie, Opts, nocookie)} of + {'nonode@nohost', _} -> nocookie; + {_, nocookie} -> nocookie; + {Node, Name} -> erlang:set_cookie(Node, Name) + end. + + find_apps_to_boot(State) -> %% Try the shell_apps option case first_value([fun find_apps_option/1, diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl index 0e3b9a0..5e1e253 100644 --- a/src/rebar_prv_update.erl +++ b/src/rebar_prv_update.erl @@ -11,6 +11,10 @@ -export([hex_to_index/1]). +-ifdef(TEST). +-export([cmp_/6, cmpl_/6, valid_vsn/1]). +-endif. + -include("rebar.hrl"). -include_lib("providers/include/providers.hrl"). @@ -99,7 +103,7 @@ hex_to_index(State) -> ets:foldl(fun({{Pkg, PkgVsn}, [Deps, Checksum, BuildTools | _]}, _) when is_list(BuildTools) -> case lists:any(fun is_supported/1, BuildTools) of true -> - DepsList = update_deps_list(Deps, Registry, State), + DepsList = update_deps_list(Pkg, PkgVsn, Deps, Registry, State), ets:insert(?PACKAGE_TABLE, {{Pkg, PkgVsn}, DepsList, Checksum}); false -> true @@ -137,20 +141,114 @@ hex_to_index(State) -> fail end. -update_deps_list(Deps, HexRegistry, State) -> +update_deps_list(Pkg, PkgVsn, Deps, HexRegistry, State) -> lists:foldl(fun([Dep, DepVsn, false, _AppName | _], DepsListAcc) -> - case DepVsn of - <<"~> ", Vsn/binary>> -> - case rebar_packages:find_highest_matching(Dep, Vsn, HexRegistry, State) of - {ok, HighestDepVsn} -> - [{Dep, HighestDepVsn} | DepsListAcc]; - none -> - ?WARN("Missing registry entry for package ~s. Try to fix with `rebar3 update`", [Dep]), - DepsListAcc - end; - Vsn -> + Dep1 = {Pkg, PkgVsn, Dep}, + case {valid_vsn(DepVsn), DepVsn} of + %% Those are all not perfectly implemented! + %% and doubled since spaces seem not to be + %% enforced + {false, Vsn} -> + ?WARN("[~s:~s], Bad dependency version for ~s: ~s.", + [Pkg, PkgVsn, Dep, Vsn]), + DepsListAcc; + {_, <<"~>", Vsn/binary>>} -> + highest_matching(Dep1, rm_ws(Vsn), HexRegistry, + State, DepsListAcc); + {_, <<">=", Vsn/binary>>} -> + cmp(Dep1, rm_ws(Vsn), HexRegistry, State, + DepsListAcc, fun ec_semver:gte/2); + {_, <<">", Vsn/binary>>} -> + cmp(Dep1, rm_ws(Vsn), HexRegistry, State, + DepsListAcc, fun ec_semver:gt/2); + {_, <<"<=", Vsn/binary>>} -> + cmpl(Dep1, rm_ws(Vsn), HexRegistry, State, + DepsListAcc, fun ec_semver:lte/2); + {_, <<"<", Vsn/binary>>} -> + cmpl(Dep1, rm_ws(Vsn), HexRegistry, State, + DepsListAcc, fun ec_semver:lt/2); + {_, <<"==", Vsn/binary>>} -> + [{Dep, Vsn} | DepsListAcc]; + {_, Vsn} -> [{Dep, Vsn} | DepsListAcc] end; ([_Dep, _DepVsn, true, _AppName | _], DepsListAcc) -> DepsListAcc end, [], Deps). + +rm_ws(<<" ", R/binary>>) -> + rm_ws(R); +rm_ws(R) -> + R. + +valid_vsn(Vsn) -> + %% Regepx from https://github.com/sindresorhus/semver-regex/blob/master/index.js + SemVerRegExp = "v?(0|[1-9][0-9]*)\\.(0|[1-9][0-9]*)(\\.(0|[1-9][0-9]*))?" + "(-[0-9a-z-]+(\\.[0-9a-z-]+)*)?(\\+[0-9a-z-]+(\\.[0-9a-z-]+)*)?", + SupportedVersions = "^(>=?|<=?|~>|==)?\\s*" ++ SemVerRegExp ++ "$", + re:run(Vsn, SupportedVersions) =/= nomatch. + +highest_matching({Pkg, PkgVsn, Dep}, Vsn, HexRegistry, State, DepsListAcc) -> + case rebar_packages:find_highest_matching(Pkg, PkgVsn, Dep, Vsn, HexRegistry, State) of + {ok, HighestDepVsn} -> + [{Dep, HighestDepVsn} | DepsListAcc]; + none -> + ?WARN("[~s:~s] Missing registry entry for package ~s. Try to fix with `rebar3 update`", + [Pkg, PkgVsn, Dep]), + DepsListAcc + end. + +cmp({_Pkg, _PkgVsn, Dep} = Dep1, Vsn, HexRegistry, State, DepsListAcc, CmpFun) -> + {ok, Vsns} = rebar_packages:find_all(Dep, HexRegistry, State), + cmp_(undefined, Vsn, Vsns, DepsListAcc, Dep1, CmpFun). + + +cmp_(undefined, _MinVsn, [], DepsListAcc, {Pkg, PkgVsn, Dep}, _CmpFun) -> + ?WARN("[~s:~s] Missing registry entry for package ~s. Try to fix with `rebar3 update`", + [Pkg, PkgVsn, Dep]), + DepsListAcc; +cmp_(HighestDepVsn, _MinVsn, [], DepsListAcc, {_Pkg, _PkgVsn, Dep}, _CmpFun) -> + [{Dep, HighestDepVsn} | DepsListAcc]; + +cmp_(BestMatch, MinVsn, [Vsn | R], DepsListAcc, Dep, CmpFun) -> + case CmpFun(Vsn, MinVsn) of + true -> + cmp_(Vsn, Vsn, R, DepsListAcc, Dep, CmpFun); + false -> + cmp_(BestMatch, MinVsn, R, DepsListAcc, Dep, CmpFun) + end. + +%% We need to treat this differently since we want a version that is LOWER but +%% the higest possible one. +cmpl({_Pkg, _PkgVsn, Dep} = Dep1, Vsn, HexRegistry, State, DepsListAcc, CmpFun) -> + {ok, Vsns} = rebar_packages:find_all(Dep, HexRegistry, State), + cmpl_(undefined, Vsn, Vsns, DepsListAcc, Dep1, CmpFun). + +cmpl_(undefined, _MaxVsn, [], DepsListAcc, {Pkg, PkgVsn, Dep}, _CmpFun) -> + ?WARN("[~s:~s] Missing registry entry for package ~s. Try to fix with `rebar3 update`", + [Pkg, PkgVsn, Dep]), + DepsListAcc; + +cmpl_(HighestDepVsn, _MaxVsn, [], DepsListAcc, {_Pkg, _PkgVsn, Dep}, _CmpFun) -> + [{Dep, HighestDepVsn} | DepsListAcc]; + +cmpl_(undefined, MaxVsn, [Vsn | R], DepsListAcc, Dep, CmpFun) -> + case CmpFun(Vsn, MaxVsn) of + true -> + cmpl_(Vsn, MaxVsn, R, DepsListAcc, Dep, CmpFun); + false -> + cmpl_(undefined, MaxVsn, R, DepsListAcc, Dep, CmpFun) + end; + +cmpl_(BestMatch, MaxVsn, [Vsn | R], DepsListAcc, Dep, CmpFun) -> + case CmpFun(Vsn, MaxVsn) of + true -> + case ec_semver:gte(Vsn, BestMatch) of + true -> + cmpl_(Vsn, MaxVsn, R, DepsListAcc, Dep, CmpFun); + false -> + cmpl_(BestMatch, MaxVsn, R, DepsListAcc, Dep, CmpFun) + end; + false -> + cmpl_(BestMatch, MaxVsn, R, DepsListAcc, Dep, CmpFun) + end. diff --git a/src/rebar_state.erl b/src/rebar_state.erl index 5ec2aef..30eff2f 100644 --- a/src/rebar_state.erl +++ b/src/rebar_state.erl @@ -36,9 +36,10 @@ deps_names/1, - resources/1, resources/2, add_resource/2, - providers/1, providers/2, add_provider/2]). + providers/1, providers/2, add_provider/2, + allow_provider_overrides/1, allow_provider_overrides/2 + ]). -include("rebar.hrl"). -include_lib("providers/include/providers.hrl"). @@ -63,7 +64,8 @@ all_deps = [] :: [rebar_app_info:t()], resources = [], - providers = []}). + providers = [], + allow_provider_overrides = false :: boolean()}). -export_type([t/0]). @@ -103,7 +105,8 @@ new(ParentState, Config, Dir) -> new(ParentState, Config, Deps, Dir) -> Opts = ParentState#state_t.opts, Plugins = proplists:get_value(plugins, Config, []), - Terms = Deps++[{{plugins, default}, Plugins} | Config], + ProjectPlugins = proplists:get_value(project_plugins, Config, []), + Terms = Deps++[{{project_plugins, default}, ProjectPlugins}, {{plugins, default}, Plugins} | Config], true = rebar_config:verify_config_format(Terms), LocalOpts = dict:from_list(Terms), @@ -136,7 +139,8 @@ base_state() -> base_opts(Config) -> Deps = proplists:get_value(deps, Config, []), Plugins = proplists:get_value(plugins, Config, []), - Terms = [{{deps, default}, Deps}, {{plugins, default}, Plugins} | Config], + ProjectPlugins = proplists:get_value(project_plugins, Config, []), + Terms = [{{deps, default}, Deps}, {{plugins, default}, Plugins}, {{project_plugins, default}, ProjectPlugins} | Config], true = rebar_config:verify_config_format(Terms), dict:from_list(Terms). @@ -368,8 +372,16 @@ providers(#state_t{providers=Providers}) -> providers(State, NewProviders) -> State#state_t{providers=NewProviders}. +allow_provider_overrides(#state_t{allow_provider_overrides=Allow}) -> + Allow. + +allow_provider_overrides(State, Allow) -> + State#state_t{allow_provider_overrides=Allow}. + -spec add_provider(t(), providers:t()) -> t(). -add_provider(State=#state_t{providers=Providers}, Provider) -> +add_provider(State=#state_t{providers=Providers, allow_provider_overrides=true}, Provider) -> + State#state_t{providers=[Provider | Providers]}; +add_provider(State=#state_t{providers=Providers, allow_provider_overrides=false}, Provider) -> Name = providers:impl(Provider), Namespace = providers:namespace(Provider), Module = providers:module(Provider), diff --git a/test/mock_git_resource.erl b/test/mock_git_resource.erl index 0f4aff6..e922af3 100644 --- a/test/mock_git_resource.erl +++ b/test/mock_git_resource.erl @@ -1,6 +1,6 @@ %%% Mock a git resource and create an app magically for each URL submitted. -module(mock_git_resource). --export([mock/0, mock/1, unmock/0]). +-export([mock/0, mock/1, mock/2, unmock/0]). -define(MOD, rebar_git_resource). %%%%%%%%%%%%%%%%% @@ -24,11 +24,14 @@ mock() -> mock([]). | {pkg, App, term()}, Vsn :: string(). mock(Opts) -> + mock(Opts, create_app). + +mock(Opts, CreateType) -> meck:new(?MOD, [no_link]), mock_lock(Opts), mock_update(Opts), mock_vsn(Opts), - mock_download(Opts), + mock_download(Opts, CreateType), ok. unmock() -> @@ -98,7 +101,7 @@ mock_vsn(Opts) -> %% `{deps, [{"app1", [{app2, ".*", {git, ...}}]}]}' -- basically %% the `deps' option takes a key/value list of terms to output directly %% into a `rebar.config' file to describe dependencies. -mock_download(Opts) -> +mock_download(Opts, CreateType) -> Deps = proplists:get_value(deps, Opts, []), Config = proplists:get_value(config, Opts, []), Default = proplists:get_value(default_vsn, Opts, "0.0.0"), @@ -110,7 +113,7 @@ mock_download(Opts) -> {git, Url, {_, Vsn}} = normalize_git(Git, Overrides, Default), App = app(Url), AppDeps = proplists:get_value({App,Vsn}, Deps, []), - rebar_test_utils:create_app( + rebar_test_utils:CreateType( Dir, App, Vsn, [kernel, stdlib] ++ [element(1,D) || D <- AppDeps] ), diff --git a/test/rebar_deps_SUITE.erl b/test/rebar_deps_SUITE.erl index fcc46c3..c95854a 100644 --- a/test/rebar_deps_SUITE.erl +++ b/test/rebar_deps_SUITE.erl @@ -3,7 +3,7 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). -all() -> [sub_app_deps, newly_added_dep, newly_added_after_empty_lock, http_proxy_settings, https_proxy_settings, {group, git}, {group, pkg}]. +all() -> [sub_app_deps, newly_added_dep, newly_added_after_empty_lock, http_proxy_settings, https_proxy_settings, semver_matching_lt, semver_matching_lte, semver_matching_gt, valid_version, {group, git}, {group, pkg}]. groups() -> [{all, [], [flat, pick_highest_left, pick_highest_right, @@ -29,6 +29,14 @@ init_per_group(_, Config) -> end_per_group(_, Config) -> Config. +init_per_testcase(valid_version, Config) -> + rebar_test_utils:init_rebar_state(Config); +init_per_testcase(semver_matching_lt, Config) -> + rebar_test_utils:init_rebar_state(Config); +init_per_testcase(semver_matching_lte, Config) -> + rebar_test_utils:init_rebar_state(Config); +init_per_testcase(semver_matching_gt, Config) -> + rebar_test_utils:init_rebar_state(Config); init_per_testcase(newly_added_after_empty_lock, Config) -> rebar_test_utils:init_rebar_state(Config); init_per_testcase(newly_added_dep, Config) -> @@ -49,14 +57,14 @@ init_per_testcase(http_proxy_settings, Config) -> %% Insert proxy variables into config rebar_test_utils:create_config(GlobalConfigDir, [{http_proxy, "http://localhost:1234"} - ]), + ]), rebar_test_utils:init_rebar_state(Config); init_per_testcase(https_proxy_settings, Config) -> SupportsHttpsProxy = case erlang:system_info(otp_release) of - "R16"++_ -> true; - "R"++_ -> false; - _ -> true % 17 and up don't have a "R" in the version - end, + "R16"++_ -> true; + "R"++_ -> false; + _ -> true % 17 and up don't have a "R" in the version + end, if not SupportsHttpsProxy -> {skip, https_proxy_unsupported_before_R16}; SupportsHttpsProxy -> @@ -73,20 +81,20 @@ init_per_testcase(https_proxy_settings, Config) -> %% Insert proxy variables into config rebar_test_utils:create_config(GlobalConfigDir, [{https_proxy, "http://localhost:1234"} - ]), + ]), rebar_test_utils:init_rebar_state(Config) end; init_per_testcase(Case, Config) -> {Deps, Warnings, Expect} = deps(Case), Expected = case Expect of - {ok, List} -> {ok, format_expected_deps(List)}; - {error, Reason} -> {error, Reason} - end, + {ok, List} -> {ok, format_expected_deps(List)}; + {error, Reason} -> {error, Reason} + end, DepsType = ?config(deps_type, Config), mock_warnings(), [{expect, Expected}, {warnings, Warnings} - | setup_project(Case, Config, rebar_test_utils:expand_deps(DepsType, Deps))]. + | setup_project(Case, Config, rebar_test_utils:expand_deps(DepsType, Deps))]. end_per_testcase(https_proxy_settings, Config) -> meck:unload(rebar_dir), @@ -100,8 +108,8 @@ end_per_testcase(_, Config) -> format_expected_deps(Deps) -> [case Dep of - {N,V} -> {dep, N, V}; - N -> {dep, N} + {N,V} -> {dep, N, V}; + N -> {dep, N} end || Dep <- Deps]. %% format: @@ -208,7 +216,7 @@ sub_app_deps(Config) -> SubAppsDir = filename:join([AppDir, "apps", Name]), SubDeps = rebar_test_utils:top_level_deps(rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []} - ,{"b", "2.0.0", []}])), + ,{"b", "2.0.0", []}])), rebar_test_utils:create_app(SubAppsDir, Name, Vsn, [kernel, stdlib]), rebar_test_utils:create_config(SubAppsDir, [{deps, SubDeps}]), @@ -242,12 +250,12 @@ newly_added_dep(Config) -> %% Add a and c to top level TopDeps2 = rebar_test_utils:top_level_deps(rebar_test_utils:expand_deps(git, [{"a", "1.0.0", []} - ,{"c", "2.0.0", []} - ,{"b", "1.0.0", []}])), + ,{"c", "2.0.0", []} + ,{"b", "1.0.0", []}])), {ok, RebarConfig2} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, TopDeps2}])), LockFile = filename:join(AppDir, "rebar.lock"), RebarConfig3 = rebar_config:merge_locks(RebarConfig2, - rebar_config:consult_lock_file(LockFile)), + rebar_config:consult_lock_file(LockFile)), %% a should now be installed and c should not change rebar_test_utils:run_and_check( @@ -277,7 +285,7 @@ newly_added_after_empty_lock(Config) -> {ok, RebarConfig2} = file:consult(rebar_test_utils:create_config(AppDir, [{deps, TopDeps2}])), LockFile = filename:join(AppDir, "rebar.lock"), RebarConfig3 = rebar_config:merge_locks(RebarConfig2, - rebar_config:consult_lock_file(LockFile)), + rebar_config:consult_lock_file(LockFile)), %% a should now be installed and c should not change rebar_test_utils:run_and_check( @@ -304,6 +312,74 @@ https_proxy_settings(_Config) -> httpc:get_option(https_proxy, rebar)). +semver_matching_lt(_Config) -> + Dep = <<"test">>, + Dep1 = {Dep, <<"1.0.0">>, Dep}, + MaxVsn = <<"0.2.0">>, + Vsns = [<<"0.1.7">>, <<"0.1.9">>, <<"0.1.8">>, <<"0.2.0">>, <<"0.2.1">>], + ?assertEqual([{Dep, <<"0.1.9">>}], + rebar_prv_update:cmpl_(undefined, MaxVsn, Vsns, [], Dep1, + fun ec_semver:lt/2)). + +semver_matching_lte(_Config) -> + Dep = <<"test">>, + Dep1 = {Dep, <<"1.0.0">>, Dep}, + MaxVsn = <<"0.2.0">>, + Vsns = [<<"0.1.7">>, <<"0.1.9">>, <<"0.1.8">>, <<"0.2.0">>, <<"0.2.1">>], + ?assertEqual([{Dep, <<"0.2.0">>}], + rebar_prv_update:cmpl_(undefined, MaxVsn, Vsns, [], Dep1, + fun ec_semver:lte/2)). + +semver_matching_gt(_Config) -> + Dep = <<"test">>, + Dep1 = {Dep, <<"1.0.0">>, Dep}, + MaxVsn = <<"0.2.0">>, + Vsns = [<<"0.1.7">>, <<"0.1.9">>, <<"0.1.8">>, <<"0.2.0">>, <<"0.2.1">>], + ?assertEqual([{Dep, <<"0.2.1">>}], + rebar_prv_update:cmp_(undefined, MaxVsn, Vsns, [], Dep1, + fun ec_semver:gt/2)). +semver_matching_gte(_Config) -> + Dep = <<"test">>, + Dep1 = {Dep, <<"1.0.0">>, Dep}, + MaxVsn = <<"0.2.0">>, + Vsns = [<<"0.1.7">>, <<"0.1.9">>, <<"0.1.8">>, <<"0.2.0">>], + ?assertEqual([{Dep, <<"0.2.0">>}], + rebar_prv_update:cmp_(undefined, MaxVsn, Vsns, [], Dep1, + fun ec_semver:gt/2)). + +valid_version(_Config) -> + ?assert(rebar_prv_update:valid_vsn(<<"0.1">>)), + ?assert(rebar_prv_update:valid_vsn(<<"0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<" 0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<" 0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<"<0.1">>)), + ?assert(rebar_prv_update:valid_vsn(<<"<0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<"< 0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<"< 0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<">0.1">>)), + ?assert(rebar_prv_update:valid_vsn(<<">0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<"> 0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<"> 0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<"<=0.1">>)), + ?assert(rebar_prv_update:valid_vsn(<<"<=0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<"<= 0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<"<= 0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<">=0.1">>)), + ?assert(rebar_prv_update:valid_vsn(<<">=0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<">= 0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<">= 0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<"==0.1">>)), + ?assert(rebar_prv_update:valid_vsn(<<"==0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<"== 0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<"== 0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<"~>0.1">>)), + ?assert(rebar_prv_update:valid_vsn(<<"~>0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<"~> 0.1.0">>)), + ?assert(rebar_prv_update:valid_vsn(<<"~> 0.1.0">>)), + ?assertNot(rebar_prv_update:valid_vsn(<<"> 0.1.0 and < 0.2.0">>)), + ok. + + run(Config) -> {ok, RebarConfig} = file:consult(?config(rebarconfig, Config)), rebar_test_utils:run_and_check( diff --git a/test/rebar_pkg_SUITE.erl b/test/rebar_pkg_SUITE.erl index 9f19e0d..6a75f32 100644 --- a/test/rebar_pkg_SUITE.erl +++ b/test/rebar_pkg_SUITE.erl @@ -180,11 +180,14 @@ pkgs_provider(Config) -> find_highest_matching(_Config) -> State = rebar_state:new(), - {ok, Vsn} = rebar_packages:find_highest_matching(<<"goodpkg">>, <<"1.0.0">>, package_index, State), + {ok, Vsn} = rebar_packages:find_highest_matching( + <<"test">>, <<"1.0.0">>, <<"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), + {ok, Vsn1} = rebar_packages:find_highest_matching( + <<"test">>, <<"1.0.0">>, <<"goodpkg">>, <<"1.0">>, package_index, State), ?assertEqual(<<"1.1.1">>, Vsn1), - {ok, Vsn2} = rebar_packages:find_highest_matching(<<"goodpkg">>, <<"2.0">>, package_index, State), + {ok, Vsn2} = rebar_packages:find_highest_matching( + <<"test">>, <<"1.0.0">>, <<"goodpkg">>, <<"2.0">>, package_index, State), ?assertEqual(<<"2.0.0">>, Vsn2). diff --git a/test/rebar_plugins_SUITE.erl b/test/rebar_plugins_SUITE.erl index c1a98de..355e156 100644 --- a/test/rebar_plugins_SUITE.erl +++ b/test/rebar_plugins_SUITE.erl @@ -12,7 +12,8 @@ list/1, upgrade/1, sub_app_plugins/1, - sub_app_plugin_overrides/1]). + sub_app_plugin_overrides/1, + project_plugins/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("eunit/include/eunit.hrl"). @@ -34,7 +35,7 @@ end_per_testcase(_, _Config) -> catch meck:unload(). all() -> - [compile_plugins, compile_global_plugins, complex_plugins, list, upgrade, sub_app_plugins, sub_app_plugin_overrides]. + [compile_plugins, compile_global_plugins, complex_plugins, list, upgrade, sub_app_plugins, sub_app_plugin_overrides, project_plugins]. %% Tests that compiling a project installs and compiles the plugins of deps compile_plugins(Config) -> @@ -281,3 +282,50 @@ sub_app_plugin_overrides(Config) -> Config, RConf, ["compile"], {ok, [{app, Name}, {dep, Dep2Name, Vsn}, {plugin, DepName, Vsn2}, {plugin, PluginName}]} ). + +%% Check that project plugins are first in providers even if they override defaults but that +%% normal plugins do not +project_plugins(Config) -> + AppDir = ?config(apps, Config), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + DepName = rebar_test_utils:create_random_name("dep1_"), + PluginName = "compile", + PluginName2 = "release", + + Plugins = rebar_test_utils:expand_deps(git, [{PluginName, Vsn, []}, {PluginName2, Vsn, []}]), + {SrcDeps, _} = rebar_test_utils:flat_deps(Plugins), + mock_git_resource:mock([{deps, SrcDeps}], create_plugin), + + mock_pkg_resource:mock([{pkgdeps, [{{list_to_binary(DepName), list_to_binary(Vsn)}, []}]}, + {config, [{plugins, [ + {list_to_atom(PluginName), + {git, "http://site.com/user/"++PluginName++".git", + {tag, Vsn}}}]}]}]), + + RConfFile = + rebar_test_utils:create_config(AppDir, + [{deps, [ + list_to_atom(DepName) + ]}, + {project_plugins, [ + {list_to_atom(PluginName2), + {git, "http://site.com/user/"++PluginName2++".git", + {tag, Vsn}}}]}]), + {ok, RConf} = file:consult(RConfFile), + + %% Build with deps. + {ok, State} = rebar_test_utils:run_and_check( + Config, RConf, ["compile"], + {ok, [{app, Name}, {plugin, PluginName}, {plugin, PluginName2}, {dep, DepName}]} + ), + + %% Should have 2 release providers but only 1 compile provider + Release = [P || P <- rebar_state:providers(State), providers:impl(P) =:= release, providers:namespace(P) =:= default], + Compile = [P || P <- rebar_state:providers(State), providers:impl(P) =:= compile, providers:namespace(P) =:= default], + + ?assertEqual(length(Release), 2), + ?assertEqual(length(Compile), 1). diff --git a/test/rebar_test_utils.erl b/test/rebar_test_utils.erl index 5187bda..23b0178 100644 --- a/test/rebar_test_utils.erl +++ b/test/rebar_test_utils.erl @@ -3,7 +3,7 @@ -include_lib("eunit/include/eunit.hrl"). -export([init_rebar_state/1, init_rebar_state/2, run_and_check/4, check_results/3]). -export([expand_deps/2, flat_deps/1, top_level_deps/1]). --export([create_app/4, create_eunit_app/4, create_empty_app/4, +-export([create_app/4, create_plugin/4, create_eunit_app/4, create_empty_app/4, create_config/2, create_config/3, package_app/3]). -export([create_random_name/1, create_random_vsn/0, write_src_file/2]). @@ -82,6 +82,16 @@ create_app(AppDir, Name, Vsn, Deps) -> write_app_src_file(AppDir, Name, Vsn, Deps), rebar_app_info:new(Name, Vsn, AppDir, Deps). +%% @doc Creates a dummy plugin including: +%% - src/<file>.erl +%% - src/<file>.app.src +%% And returns a `rebar_app_info' object. +create_plugin(AppDir, Name, Vsn, Deps) -> + write_plugin_file(AppDir, Name ++ ".erl"), + write_src_file(AppDir, "not_a_real_src_" ++ Name ++ ".erl"), + write_app_src_file(AppDir, Name, Vsn, Deps), + rebar_app_info:new(Name, Vsn, AppDir, Deps). + %% @doc Creates a dummy application including: %% - src/<file>.erl %% - src/<file>.app.src @@ -365,6 +375,11 @@ check_results(AppDir, Expected, ProfileRun) -> ?assert(filelib:is_dir(Dirname)) end, Expected). +write_plugin_file(Dir, Name) -> + Erl = filename:join([Dir, "src", Name]), + ok = filelib:ensure_dir(Erl), + ok = ec_file:write(Erl, plugin_src_file(Name)). + write_src_file(Dir, Name) -> Erl = filename:join([Dir, "src", Name]), ok = filelib:ensure_dir(Erl), @@ -395,6 +410,18 @@ erl_src_file(Name) -> "-export([main/0]).\n" "main() -> ok.\n", [filename:basename(Name, ".erl")]). +plugin_src_file(Name) -> + io_lib:format("-module('~s').\n" + "-export([init/1]).\n" + "init(State) -> \n" + "Provider = providers:create([\n" + "{name, '~s'},\n" + "{module, '~s'}\n" + "]),\n" + "{ok, rebar_state:add_provider(State, Provider)}.\n", [filename:basename(Name, ".erl"), + filename:basename(Name, ".erl"), + filename:basename(Name, ".erl")]). + erl_eunitized_src_file(Name) -> io_lib:format("-module('~s').\n" "-export([main/0]).\n" |