diff options
-rw-r--r-- | src/rebar.app.src | 2 | ||||
-rw-r--r-- | src/rebar3.erl | 9 | ||||
-rw-r--r-- | src/rebar_dir.erl | 2 | ||||
-rw-r--r-- | src/rebar_plugins.erl | 50 | ||||
-rw-r--r-- | src/rebar_prv_as.erl | 7 | ||||
-rw-r--r-- | src/rebar_prv_install_deps.erl | 47 | ||||
-rw-r--r-- | src/rebar_prv_plugins.erl | 67 | ||||
-rw-r--r-- | src/rebar_prv_plugins_upgrade.erl | 96 | ||||
-rw-r--r-- | src/rebar_state.erl | 52 | ||||
-rw-r--r-- | test/rebar_compile_SUITE.erl | 133 | ||||
-rw-r--r-- | test/rebar_plugins_SUITE.erl | 201 |
11 files changed, 470 insertions, 196 deletions
diff --git a/src/rebar.app.src b/src/rebar.app.src index e9f0677..6d081a2 100644 --- a/src/rebar.app.src +++ b/src/rebar.app.src @@ -44,6 +44,8 @@ rebar_prv_lock, rebar_prv_new, rebar_prv_packages, + rebar_prv_plugins, + rebar_prv_plugins_upgrade, rebar_prv_release, rebar_prv_report, rebar_prv_shell, diff --git a/src/rebar3.erl b/src/rebar3.erl index e324687..5c0559f 100644 --- a/src/rebar3.erl +++ b/src/rebar3.erl @@ -95,12 +95,12 @@ run_aux(State, RawArgs) -> %% 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)), + filename:join(filename:absname(rebar_state:dir(State2)), 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:install(State4), + State5 = rebar_plugins:project_apps_install(State4), State6 = rebar_state:default(State5, rebar_state:opts(State5)), {Task, Args} = parse_args(RawArgs), @@ -133,13 +133,14 @@ init_config() -> %% We don't want to worry about global plugin install state effecting later %% usage. So we throw away the global profile state used for plugin install. - GlobalConfigThrowAway = rebar_state:current_profiles(GlobalConfig, ["global"]), + GlobalConfigThrowAway = rebar_state:current_profiles(GlobalConfig, [global]), GlobalState = rebar_plugins:handle_plugins(global, rebar_state:get(GlobalConfigThrowAway, plugins, []), GlobalConfigThrowAway), GlobalPlugins = rebar_state:providers(GlobalState), GlobalConfig2 = rebar_state:set(GlobalConfig, plugins, []), - rebar_state:providers(rebar_state:new(GlobalConfig2, Config1), GlobalPlugins); + GlobalConfig3 = rebar_state:set(GlobalConfig2, {plugins, global}, rebar_state:get(GlobalConfigThrowAway, plugins, [])), + rebar_state:providers(rebar_state:new(GlobalConfig3, Config1), GlobalPlugins); false -> rebar_state:new(Config1) end, diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl index cb643ab..e226633 100644 --- a/src/rebar_dir.erl +++ b/src/rebar_dir.erl @@ -35,6 +35,7 @@ base_dir(State) -> profile_dir(State, Profiles) -> {BaseDir, ProfilesStrings} = case [ec_cnv:to_list(P) || P <- Profiles] of ["global" | _] -> {?MODULE:global_cache_dir(State), [""]}; + ["bootstrap", "default"] -> {rebar_state:get(State, base_dir, ?DEFAULT_BASE_DIR), ["default"]}; ["default"] -> {rebar_state:get(State, base_dir, ?DEFAULT_BASE_DIR), ["default"]}; %% drop `default` from the profile dir if it's implicit and reverse order %% of profiles to match order passed to `as` @@ -43,7 +44,6 @@ profile_dir(State, Profiles) -> ProfilesDir = string:join(ProfilesStrings, "+"), filename:join(BaseDir, ProfilesDir). - -spec deps_dir(rebar_state:t()) -> file:filename_all(). deps_dir(State) -> filename:join(base_dir(State), rebar_state:get(State, deps_dir, ?DEFAULT_DEPS_DIR)). diff --git a/src/rebar_plugins.erl b/src/rebar_plugins.erl index 88cb8b8..a267b9d 100644 --- a/src/rebar_plugins.erl +++ b/src/rebar_plugins.erl @@ -3,7 +3,10 @@ -module(rebar_plugins). --export([install/1, handle_plugins/2, handle_plugins/3]). +-export([project_apps_install/1 + ,install/1 + ,handle_plugins/3 + ,handle_plugins/4]). -include("rebar.hrl"). @@ -11,26 +14,35 @@ %% Public API %% =================================================================== --spec install(rebar_state:t()) -> rebar_state:t(). -install(State) -> - Plugins = rebar_state:get(State, plugins, []), - +-spec project_apps_install(rebar_state:t()) -> rebar_state:t(). +project_apps_install(State) -> + Profiles = rebar_state:current_profiles(State), ProjectApps = rebar_state:project_apps(State), - OtherPlugins = lists:flatmap(fun(App) -> - AppDir = rebar_app_info:dir(App), - C = rebar_config:consult(AppDir), - S = rebar_state:new(rebar_state:new(), C, AppDir), - rebar_state:get(S, plugins, []) - end, ProjectApps), - - handle_plugins(Plugins++OtherPlugins, State). + lists:foldl(fun(Profile, StateAcc) -> + Plugins = rebar_state:get(State, {plugins, Profile}, []), + handle_plugins(Profile, Plugins, StateAcc), + lists:foldl(fun(App, StateAcc1) -> + AppDir = rebar_app_info:dir(App), + C = rebar_config:consult(AppDir), + S = rebar_state:new(rebar_state:new(), C, AppDir), + Plugins2 = rebar_state:get(S, {plugins, Profile}, []), + handle_plugins(Profile, Plugins2, StateAcc1) + end, StateAcc, ProjectApps) + end, State, Profiles). --spec handle_plugins([rebar_prv_install_deps:dep()], rebar_state:t()) -> rebar_state:t(). -handle_plugins(Plugins, State) -> - handle_plugins(default, Plugins, State). +-spec install(rebar_state:t()) -> rebar_state:t(). +install(State) -> + Profiles = rebar_state:current_profiles(State), + lists:foldl(fun(Profile, StateAcc) -> + Plugins = rebar_state:get(State, {plugins, Profile}, []), + handle_plugins(Profile, Plugins, StateAcc) + end, State, Profiles). handle_plugins(Profile, Plugins, State) -> + handle_plugins(Profile, Plugins, State, false). + +handle_plugins(Profile, Plugins, State, Upgrade) -> %% Set deps dir to plugins dir so apps are installed there Locks = rebar_state:lock(State), DepsDir = rebar_state:get(State, deps_dir, ?DEFAULT_DEPS_DIR), @@ -39,7 +51,7 @@ handle_plugins(Profile, Plugins, State) -> %% Install each plugin individually so if one fails to install it doesn't effect the others {PluginProviders, State2} = lists:foldl(fun(Plugin, {PluginAcc, StateAcc}) -> - {NewPlugins, NewState} = handle_plugin(Profile, Plugin, StateAcc), + {NewPlugins, NewState} = handle_plugin(Profile, Plugin, StateAcc, Upgrade), {PluginAcc++NewPlugins, NewState} end, {[], State1}, Plugins), @@ -49,9 +61,9 @@ handle_plugins(Profile, Plugins, State) -> rebar_state:create_logic_providers(PluginProviders, State4). -handle_plugin(Profile, Plugin, State) -> +handle_plugin(Profile, Plugin, State, Upgrade) -> try - {ok, Apps, State2} = rebar_prv_install_deps:handle_deps(Profile, State, [Plugin]), + {ok, Apps, State2} = rebar_prv_install_deps:handle_deps(Profile, State, [Plugin], Upgrade), {no_cycle, Sorted} = rebar_prv_install_deps:find_cycles(Apps), ToBuild = rebar_prv_install_deps:cull_compile(Sorted, []), diff --git a/src/rebar_prv_as.erl b/src/rebar_prv_as.erl index d8f19d4..ead7b01 100644 --- a/src/rebar_prv_as.erl +++ b/src/rebar_prv_as.erl @@ -38,13 +38,14 @@ do(State) -> {error, "At least one profile must be specified when using `as`"}; _ -> State1 = rebar_state:apply_profiles(State, [list_to_atom(X) || X <- Profiles]), + State2 = rebar_plugins:project_apps_install(State1), {FirstTask, FirstTaskArgs} = hd(Tasks), FirstTaskAtom = list_to_atom(FirstTask), - case rebar_core:process_namespace(State1, FirstTaskAtom) of - {ok, State2, NewTask} -> + case rebar_core:process_namespace(State2, FirstTaskAtom) of + {ok, State3, NewTask} -> rebar_prv_do:do_tasks( [{atom_to_list(NewTask),FirstTaskArgs}|tl(Tasks)], - State2 + State3 ); {error, Reason} -> {error, Reason} diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl index 61bbb62..806293b 100644 --- a/src/rebar_prv_install_deps.erl +++ b/src/rebar_prv_install_deps.erl @@ -234,22 +234,26 @@ handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Fetched, Seen, Locks, S BaseDir = rebar_state:get(State, base_dir, []), S1 = rebar_state:new(rebar_state:set(rebar_state:new(), base_dir, BaseDir), C, rebar_app_info:dir(AppInfo1)), - S2 = rebar_state:apply_profiles(S1, Profiles), - S3 = rebar_state:apply_overrides(S2, Name), - AppInfo2 = rebar_app_info:state(AppInfo1, S3), + S2 = rebar_state:apply_overrides(S1, Name), + + Plugins = rebar_state:get(S2, plugins, []), + S3 = rebar_state:set(S2, {plugins, Profile}, Plugins), + + S4 = rebar_state:apply_profiles(S3, Profiles), + AppInfo2 = rebar_app_info:state(AppInfo1, S4), %% Dep may have plugins to install. Find and install here. - S4 = rebar_plugins:handle_plugins(rebar_state:get(S3, plugins, []), S3), - AppInfo3 = rebar_app_info:state(AppInfo2, S4), + S5 = rebar_plugins:install(S4), + AppInfo3 = rebar_app_info:state(AppInfo2, S5), {[AppInfo3 | Fetched], NewSeen, NewState}. maybe_lock(Profile, AppInfo, Seen, State, Level) -> + Name = rebar_app_info:name(AppInfo), case rebar_app_info:is_checkout(AppInfo) of false -> case Profile of default -> - Name = rebar_app_info:name(AppInfo), case sets:is_element(Name, Seen) of false -> Locks = rebar_state:lock(State), @@ -264,10 +268,10 @@ maybe_lock(Profile, AppInfo, Seen, State, Level) -> {Seen, State} end; _ -> - {Seen, State} + {sets:add_element(Name, Seen), State} end; true -> - {Seen, State} + {sets:add_element(Name, Seen), State} end. package_to_app(DepsDir, Packages, {Name, Vsn, Level}, IsLock, State) -> @@ -367,7 +371,6 @@ handle_upgrade(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Locks) {true, AppInfo1} -> handle_dep(AppInfo1, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Locks); - {false, AppInfo1} -> {[AppInfo1|SrcDeps], PkgDeps, SrcApps, State, Locks} end; @@ -378,7 +381,7 @@ handle_upgrade(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Locks) handle_dep(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Locks) -> DepsDir = profile_dep_dir(State, Profile), {AppInfo1, NewSrcDeps, NewPkgDeps, NewLocks, State1} = - handle_dep(State, DepsDir, AppInfo, Locks, Level), + handle_dep(State, Profile, DepsDir, AppInfo, Locks, Level), AppInfo2 = rebar_app_info:dep_level(AppInfo1, Level), {NewSrcDeps ++ SrcDeps ,NewPkgDeps++PkgDeps @@ -386,9 +389,9 @@ handle_dep(AppInfo, Profile, SrcDeps, PkgDeps, SrcApps, Level, State, Locks) -> ,State1 ,NewLocks}. --spec handle_dep(rebar_state:t(), file:filename_all(), rebar_app_info:t(), list(), integer()) -> +-spec handle_dep(rebar_state:t(), atom(), file:filename_all(), rebar_app_info:t(), list(), integer()) -> {rebar_app_info:t(), [rebar_app_info:t()], [pkg_dep()], [integer()]}. -handle_dep(State, DepsDir, AppInfo, Locks, Level) -> +handle_dep(State, Profile, DepsDir, AppInfo, Locks, Level) -> Profiles = rebar_state:current_profiles(State), Name = rebar_app_info:name(AppInfo), @@ -396,20 +399,24 @@ handle_dep(State, DepsDir, AppInfo, Locks, Level) -> S = rebar_app_info:state(AppInfo), S1 = rebar_state:new(S, C, rebar_app_info:dir(AppInfo)), - S2 = rebar_state:apply_profiles(S1, Profiles), - S3 = rebar_state:apply_overrides(S2, Name), - AppInfo1 = rebar_app_info:state(AppInfo, S3), + S2 = rebar_state:apply_overrides(S1, Name), + + Plugins = rebar_state:get(S2, plugins, []), + S3 = rebar_state:set(S2, {plugins, Profile}, Plugins), + + S4 = rebar_state:apply_profiles(S3, Profiles), + AppInfo1 = rebar_app_info:state(AppInfo, S4), %% Dep may have plugins to install. Find and install here. - S4 = rebar_plugins:handle_plugins(rebar_state:get(S3, plugins, []), S3), - AppInfo2 = rebar_app_info:state(AppInfo1, S4), + S5 = rebar_plugins:install(S4), + AppInfo2 = rebar_app_info:state(AppInfo1, S5), %% Upgrade lock level to be the level the dep will have in this dep tree - Deps = rebar_state:get(S4, deps, []), + Deps = rebar_state:get(S5, deps, []), NewLocks = [{DepName, Source, LockLevel+Level} || - {DepName, Source, LockLevel} <- rebar_state:get(S4, {locks, default}, [])], + {DepName, Source, LockLevel} <- rebar_state:get(S5, {locks, default}, [])], AppInfo3 = rebar_app_info:deps(AppInfo2, rebar_state:deps_names(Deps)), - {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, S4, Locks, Level+1), + {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, S5, Locks, Level+1), {AppInfo3, SrcDeps, PkgDeps, Locks++NewLocks, State}. -spec maybe_fetch(rebar_app_info:t(), atom(), boolean(), diff --git a/src/rebar_prv_plugins.erl b/src/rebar_prv_plugins.erl new file mode 100644 index 0000000..328958e --- /dev/null +++ b/src/rebar_prv_plugins.erl @@ -0,0 +1,67 @@ +-module(rebar_prv_plugins). + +-behaviour(provider). + +-export([init/1, + do/1, + format_error/1]). + +-include("rebar.hrl"). +-include_lib("providers/include/providers.hrl"). + +-define(PROVIDER, list). +-define(NAMESPACE, plugins). +-define(DEPS, []). + +-spec init(rebar_state:t()) -> {ok, rebar_state:t()}. +init(State) -> + State1 = rebar_state:add_provider( + State, + providers:create([ + {name, ?PROVIDER}, + {module, ?MODULE}, + {namespace, ?NAMESPACE}, + {bare, true}, + {deps, ?DEPS}, + {example, "rebar3 plugins list"}, + {short_desc, "List local and global plugins for this project"}, + {desc, "List local and global plugins for this project"}, + {opts, []}])), + {ok, State1}. + +-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. +do(State) -> + GlobalConfigFile = rebar_dir:global_config(), + GlobalConfig = rebar_state:new(rebar_config:consult_file(GlobalConfigFile)), + GlobalPlugins = rebar_state:get(GlobalConfig, plugins, []), + GlobalPluginsDir = filename:join(rebar_dir:global_cache_dir(State), "plugins"), + display_plugins("Global plugins", GlobalPluginsDir, GlobalPlugins), + + Plugins = rebar_state:get(State, plugins, []), + PluginsDir =rebar_dir:plugins_dir(State), + display_plugins("Local plugins", PluginsDir, Plugins), + {ok, State}. + +-spec format_error(any()) -> iolist(). +format_error(Reason) -> + io_lib:format("~p", [Reason]). + +display_plugins(_Header, _Dir, []) -> + ok; +display_plugins(Header, Dir, Plugins) -> + ?CONSOLE("--- ~s ---", [Header]), + display_plugins(Dir, Plugins), + ?CONSOLE("", []). + +display_plugins(Dir, Plugins) -> + lists:foreach(fun(Plugin) -> + Name = if is_atom(Plugin) -> Plugin; + is_tuple(Plugin) -> element(1, Plugin) + end, + case rebar_app_info:discover(filename:join(Dir, Name)) of + {ok, _App} -> + ?CONSOLE("~s", [Name]); + not_found -> + ?DEBUG("Unable to find plugin ~s", [Name]) + end + end, Plugins). diff --git a/src/rebar_prv_plugins_upgrade.erl b/src/rebar_prv_plugins_upgrade.erl new file mode 100644 index 0000000..fbd8365 --- /dev/null +++ b/src/rebar_prv_plugins_upgrade.erl @@ -0,0 +1,96 @@ +-module(rebar_prv_plugins_upgrade). + +-behaviour(provider). + +-export([init/1, + do/1, + format_error/1]). + +-include("rebar.hrl"). +-include_lib("providers/include/providers.hrl"). + +-define(PROVIDER, upgrade). +-define(NAMESPACE, plugins). +-define(DEPS, []). + +-spec init(rebar_state:t()) -> {ok, rebar_state:t()}. +init(State) -> + State1 = rebar_state:add_provider( + State, + providers:create([ + {name, ?PROVIDER}, + {module, ?MODULE}, + {namespace, ?NAMESPACE}, + {bare, true}, + {deps, ?DEPS}, + {example, "rebar3 plugins upgrade <plugin>"}, + {short_desc, "Uprade plugins"}, + {desc, "List or upgrade plugins"}, + {opts, [{plugin, undefined, undefined, string, + "Plugin to upgrade"}]}])), + {ok, State1}. + +-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. +do(State) -> + {Args, _} = rebar_state:command_parsed_args(State), + Plugin = proplists:get_value(plugin, Args, <<"">>), + upgrade(Plugin, State). + +-spec format_error(any()) -> iolist(). +format_error({not_found, Plugin}) -> + io_lib:format("Plugin to upgrade not found: ~s", [Plugin]); +format_error(Reason) -> + io_lib:format("~p", [Reason]). + +upgrade(Plugin, State) -> + Profiles = rebar_state:current_profiles(State), + Dep = ec_lists:search(fun(Profile) -> + Plugins = rebar_state:get(State, {plugins, Profile}, []), + case find(list_to_atom(Plugin), Plugins) of + false -> + not_found; + P -> + {ok, P} + end + end, Profiles), + + case Dep of + not_found -> + ?PRV_ERROR({not_found, Plugin}); + {ok, P, Profile} -> + State1 = rebar_state:set(State, deps_dir, ?DEFAULT_PLUGINS_DIR), + {ok, Apps, _State2} = rebar_prv_install_deps:handle_deps(Profile + ,State1 + ,[P] + ,true), + + {no_cycle, Sorted} = rebar_prv_install_deps:find_cycles(Apps), + ToBuild = rebar_prv_install_deps:cull_compile(Sorted, []), + + %% Add already built plugin deps to the code path + CodePaths = [rebar_app_info:ebin_dir(A) || A <- Apps -- ToBuild], + code:add_pathsa(CodePaths), + + %% Build plugin and its deps + [build_plugin(AppInfo, Apps, State) || AppInfo <- ToBuild], + {ok, State} + end. + +find(_Plugin, []) -> + false; +find(Plugin, [Plugin | _Plugins]) -> + Plugin; +find(Plugin, [Plugin1 | Plugins]) when is_tuple(Plugin1) -> + case element(1, Plugin1) =:= Plugin of + true -> + Plugin1; + false -> + find(Plugin, Plugins) + end. + +build_plugin(AppInfo, Apps, State) -> + Providers = rebar_state:providers(State), + AppDir = rebar_app_info:dir(AppInfo), + C = rebar_config:consult(AppDir), + S = rebar_state:new(rebar_state:all_deps(rebar_state:new(), Apps), C, AppDir), + rebar_prv_compile:compile(S, Providers, AppInfo). diff --git a/src/rebar_state.erl b/src/rebar_state.erl index 7c61db2..8125026 100644 --- a/src/rebar_state.erl +++ b/src/rebar_state.erl @@ -82,7 +82,8 @@ new() -> new(Config) when is_list(Config) -> BaseState = base_state(), Deps = proplists:get_value(deps, Config, []), - Terms = [{{deps, default}, Deps} | Config], + Plugins = proplists:get_value(plugins, Config, []), + Terms = [{{deps, default}, Deps}, {{plugins, default}, Plugins} | Config], true = rebar_config:verify_config_format(Terms), Opts = dict:from_list(Terms), BaseState#state_t { dir = rebar_dir:get_cwd(), @@ -95,7 +96,8 @@ new(Profile, Config) when is_atom(Profile) BaseState = base_state(), Deps = proplists:get_value(deps, Config, []), - Terms = [{{deps, default}, Deps} | Config], + Plugins = proplists:get_value(plugins, Config, []), + Terms = [{{deps, default}, Deps}, {{plugins, default}, Plugins} | Config], true = rebar_config:verify_config_format(Terms), Opts = dict:from_list(Terms), BaseState#state_t { dir = rebar_dir:get_cwd(), @@ -112,18 +114,19 @@ new(ParentState, Config, Dir) -> Opts = ParentState#state_t.opts, LocalOpts = case rebar_config:consult_lock_file(filename:join(Dir, ?LOCK_FILE)) of [D] -> - %% We want the top level deps only from the lock file. - %% This ensures deterministic overrides for configs. - Deps = [X || X <- D, element(3, X) =:= 0], - - Terms = [{{locks, default}, D}, {{deps, default}, Deps} | Config], - true = rebar_config:verify_config_format(Terms), - dict:from_list(Terms); + %% We want the top level deps only from the lock file. + %% This ensures deterministic overrides for configs. + Deps = [X || X <- D, element(3, X) =:= 0], + Plugins = proplists:get_value(plugins, Config, []), + Terms = [{{locks, default}, D}, {{deps, default}, Deps}, {{plugins, default}, Plugins} | Config], + true = rebar_config:verify_config_format(Terms), + dict:from_list(Terms); _ -> - D = proplists:get_value(deps, Config, []), - Terms = [{{deps, default}, D} | Config], - true = rebar_config:verify_config_format(Terms), - dict:from_list(Terms) + D = proplists:get_value(deps, Config, []), + Plugins = proplists:get_value(plugins, Config, []), + Terms = [{{deps, default}, D}, {{plugins, default}, Plugins} | Config], + true = rebar_config:verify_config_format(Terms), + dict:from_list(Terms) end, NewOpts = merge_opts(LocalOpts, Opts), @@ -257,14 +260,17 @@ apply_overrides(State=#state_t{overrides=Overrides}, AppName) -> StateAcc end, State1, Overrides), - lists:foldl(fun({add, N, O}, StateAcc) when N =:= Name -> + State3 = lists:foldl(fun({add, N, O}, StateAcc) when N =:= Name -> lists:foldl(fun({Key, Value}, StateAcc1) -> OldValue = rebar_state:get(StateAcc1, Key, []), rebar_state:set(StateAcc1, Key, Value++OldValue) end, StateAcc, O); (_, StateAcc) -> StateAcc - end, State2, Overrides). + end, State2, Overrides), + + Opts = opts(State3), + State3#state_t{default=Opts}. add_to_profile(State, Profile, KVs) when is_atom(Profile), is_list(KVs) -> Profiles = rebar_state:get(State, profiles, []), @@ -280,6 +286,7 @@ apply_profiles(State, [default]) -> apply_profiles(State=#state_t{default = Defaults, current_profiles=CurrentProfiles}, Profiles) -> AppliedProfiles = deduplicate(CurrentProfiles ++ Profiles), ConfigProfiles = rebar_state:get(State, profiles, []), + NewOpts = lists:foldl(fun(default, OptsAcc) -> OptsAcc; @@ -303,11 +310,18 @@ do_deduplicate([Head | Rest], Acc) -> merge_opts(Profile, NewOpts, OldOpts) -> Opts = merge_opts(NewOpts, OldOpts), - case dict:find(deps, NewOpts) of + Opts2 = case dict:find(plugins, NewOpts) of {ok, Value} -> - dict:store({deps, Profile}, Value, Opts); + dict:store({plugins, Profile}, Value, Opts); error -> Opts + end, + + case dict:find(deps, NewOpts) of + {ok, Value2} -> + dict:store({deps, Profile}, Value2, Opts2); + error -> + Opts2 end. merge_opts(NewOpts, OldOpts) -> @@ -315,6 +329,10 @@ merge_opts(NewOpts, OldOpts) -> NewValue; ({deps, _}, NewValue, _OldValue) -> NewValue; + (plugins, NewValue, _OldValue) -> + NewValue; + ({plugins, _}, NewValue, _OldValue) -> + NewValue; (profiles, NewValue, OldValue) -> dict:to_list(merge_opts(dict:from_list(NewValue), dict:from_list(OldValue))); (_Key, NewValue, OldValue) when is_list(NewValue) -> diff --git a/test/rebar_compile_SUITE.erl b/test/rebar_compile_SUITE.erl index 7025eef..7e72404 100644 --- a/test/rebar_compile_SUITE.erl +++ b/test/rebar_compile_SUITE.erl @@ -18,9 +18,6 @@ deps_in_path/1, delete_beam_if_source_deleted/1, checkout_priority/1, - compile_plugins/1, - compile_global_plugins/1, - complex_plugins/1, highest_version_of_pkg_dep/1, parse_transform_test/1]). @@ -49,8 +46,7 @@ all() -> build_all_srcdirs, recompile_when_hrl_changes, recompile_when_opts_change, dont_recompile_when_opts_dont_change, dont_recompile_yrl_or_xrl, delete_beam_if_source_deleted, - deps_in_path, checkout_priority, compile_plugins, compile_global_plugins, - complex_plugins, highest_version_of_pkg_dep, parse_transform_test]. + deps_in_path, checkout_priority, highest_version_of_pkg_dep, parse_transform_test]. build_basic_app(Config) -> AppDir = ?config(apps, Config), @@ -399,133 +395,6 @@ checkout_priority(Config) -> ?assertEqual(Vsn2, proplists:get_value(vsn, DepProps)), ?assertEqual(Vsn2, proplists:get_value(vsn, PkgProps)). -%% Tests that compiling a project installs and compiles the plugins of deps -compile_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 = rebar_test_utils:create_random_name("plugin1_"), - - Plugins = rebar_test_utils:expand_deps(git, [{PluginName, Vsn, []}]), - mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Plugins)}]), - - 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) - ]}]), - {ok, RConf} = file:consult(RConfFile), - - %% Build with deps. - rebar_test_utils:run_and_check( - Config, RConf, ["compile"], - {ok, [{app, Name}, {plugin, PluginName}, {dep, DepName}]} - ). - -%% Tests that compiling a project installs and compiles the global plugins -compile_global_plugins(Config) -> - AppDir = ?config(apps, Config), - GlobalDir = filename:join(AppDir, "global"), - GlobalConfigDir = filename:join([GlobalDir, ".config", "rebar3"]), - GlobalConfig = filename:join([GlobalDir, ".config", "rebar3", "rebar.config"]), - - meck:new(rebar_dir, [passthrough]), - meck:expect(rebar_dir, global_config, fun() -> GlobalConfig end), - meck:expect(rebar_dir, global_cache_dir, fun(_) -> GlobalDir end), - - Name = rebar_test_utils:create_random_name("app1_"), - Vsn = rebar_test_utils:create_random_vsn(), - Vsn2 = 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 = rebar_test_utils:create_random_name("plugin1_"), - - mock_git_resource:mock([{deps, [{list_to_atom(PluginName), Vsn}, - {list_to_atom(PluginName), Vsn2}, - {{iolist_to_binary(DepName), iolist_to_binary(Vsn)}, []}]}]), - - - rebar_test_utils:create_config(GlobalConfigDir, - [{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), {git, "http://site.com/user/"++DepName++".git", {tag, Vsn}}} - ]}, - {plugins, [ - {list_to_atom(PluginName), {git, "http://site.com/user/"++PluginName++".git", {tag, Vsn2}}} - ]}]), - {ok, RConf} = file:consult(RConfFile), - - %% Runs global plugin install - rebar3:init_config(), - - %% Build with deps. - rebar_test_utils:run_and_check( - Config, RConf, ["compile"], - {ok, [{app, Name}, - {global_plugin, PluginName, Vsn}, - {plugin, PluginName, Vsn2}, - {dep, DepName}]} - ), - - meck:unload(rebar_dir). - -%% Tests installing of plugin with transitive deps -complex_plugins(Config) -> - AppDir = ?config(apps, Config), - - meck:new(rebar_dir, [passthrough]), - - Name = rebar_test_utils:create_random_name("app1_"), - Vsn = rebar_test_utils:create_random_vsn(), - Vsn2 = rebar_test_utils:create_random_vsn(), - rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), - - DepName = rebar_test_utils:create_random_name("dep1_"), - DepName2 = rebar_test_utils:create_random_name("dep2_"), - DepName3 = rebar_test_utils:create_random_name("dep3_"), - PluginName = rebar_test_utils:create_random_name("plugin1_"), - - Deps = rebar_test_utils:expand_deps(git, [{PluginName, Vsn2, [{DepName2, Vsn, - [{DepName3, Vsn, []}]}]} - ,{DepName, Vsn, []}]), - mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Deps)}]), - - RConfFile = - rebar_test_utils:create_config(AppDir, - [{deps, [ - {list_to_atom(DepName), {git, "http://site.com/user/"++DepName++".git", {tag, Vsn}}} - ]}, - {plugins, [ - {list_to_atom(PluginName), {git, "http://site.com/user/"++PluginName++".git", {tag, Vsn2}}} - ]}]), - {ok, RConf} = file:consult(RConfFile), - - %% Build with deps. - rebar_test_utils:run_and_check( - Config, RConf, ["compile"], - {ok, [{app, Name}, - {plugin, PluginName, Vsn2}, - {plugin, DepName2}, - {plugin, DepName3}, - {dep, DepName}]} - ), - - meck:unload(rebar_dir). highest_version_of_pkg_dep(Config) -> AppDir = ?config(apps, Config), diff --git a/test/rebar_plugins_SUITE.erl b/test/rebar_plugins_SUITE.erl new file mode 100644 index 0000000..adfeafe --- /dev/null +++ b/test/rebar_plugins_SUITE.erl @@ -0,0 +1,201 @@ +-module(rebar_plugins_SUITE). + +-export([suite/0, + init_per_suite/1, + end_per_suite/1, + init_per_testcase/2, + end_per_testcase/2, + all/0, + compile_plugins/1, + compile_global_plugins/1, + complex_plugins/1, + upgrade/1]). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("eunit/include/eunit.hrl"). +-include_lib("kernel/include/file.hrl"). + +suite() -> + []. + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_testcase(_, Config) -> + rebar_test_utils:init_rebar_state(Config). + +end_per_testcase(_, _Config) -> + catch meck:unload(). + +all() -> + [compile_plugins, compile_global_plugins, complex_plugins, upgrade]. + +%% Tests that compiling a project installs and compiles the plugins of deps +compile_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 = rebar_test_utils:create_random_name("plugin1_"), + + Plugins = rebar_test_utils:expand_deps(git, [{PluginName, Vsn, []}]), + mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Plugins)}]), + + 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) + ]}]), + {ok, RConf} = file:consult(RConfFile), + + %% Build with deps. + rebar_test_utils:run_and_check( + Config, RConf, ["compile"], + {ok, [{app, Name}, {plugin, PluginName}, {dep, DepName}]} + ). + +%% Tests that compiling a project installs and compiles the global plugins +compile_global_plugins(Config) -> + AppDir = ?config(apps, Config), + GlobalDir = filename:join(AppDir, "global"), + GlobalConfigDir = filename:join([GlobalDir, ".config", "rebar3"]), + GlobalConfig = filename:join([GlobalDir, ".config", "rebar3", "rebar.config"]), + + meck:new(rebar_dir, [passthrough]), + meck:expect(rebar_dir, global_config, fun() -> GlobalConfig end), + meck:expect(rebar_dir, global_cache_dir, fun(_) -> GlobalDir end), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + Vsn2 = 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 = rebar_test_utils:create_random_name("plugin1_"), + + mock_git_resource:mock([{deps, [{list_to_atom(PluginName), Vsn}, + {list_to_atom(PluginName), Vsn2}, + {{iolist_to_binary(DepName), iolist_to_binary(Vsn)}, []}]}]), + + + rebar_test_utils:create_config(GlobalConfigDir, + [{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), {git, "http://site.com/user/"++DepName++".git", {tag, Vsn}}} + ]}, + {plugins, [ + {list_to_atom(PluginName), {git, "http://site.com/user/"++PluginName++".git", {tag, Vsn2}}} + ]}]), + {ok, RConf} = file:consult(RConfFile), + + %% Runs global plugin install + rebar3:init_config(), + + %% Build with deps. + rebar_test_utils:run_and_check( + Config, RConf, ["compile"], + {ok, [{app, Name}, + {global_plugin, PluginName, Vsn}, + {plugin, PluginName, Vsn2}, + {dep, DepName}]} + ), + + meck:unload(rebar_dir). + +%% Tests installing of plugin with transitive deps +complex_plugins(Config) -> + AppDir = ?config(apps, Config), + + meck:new(rebar_dir, [passthrough]), + + Name = rebar_test_utils:create_random_name("app1_"), + Vsn = rebar_test_utils:create_random_vsn(), + Vsn2 = rebar_test_utils:create_random_vsn(), + rebar_test_utils:create_app(AppDir, Name, Vsn, [kernel, stdlib]), + + DepName = rebar_test_utils:create_random_name("dep1_"), + DepName2 = rebar_test_utils:create_random_name("dep2_"), + DepName3 = rebar_test_utils:create_random_name("dep3_"), + PluginName = rebar_test_utils:create_random_name("plugin1_"), + + Deps = rebar_test_utils:expand_deps(git, [{PluginName, Vsn2, [{DepName2, Vsn, + [{DepName3, Vsn, []}]}]} + ,{DepName, Vsn, []}]), + mock_git_resource:mock([{deps, rebar_test_utils:flat_deps(Deps)}]), + + RConfFile = + rebar_test_utils:create_config(AppDir, + [{deps, [ + {list_to_atom(DepName), {git, "http://site.com/user/"++DepName++".git", {tag, Vsn}}} + ]}, + {plugins, [ + {list_to_atom(PluginName), {git, "http://site.com/user/"++PluginName++".git", {tag, Vsn2}}} + ]}]), + {ok, RConf} = file:consult(RConfFile), + + %% Build with deps. + rebar_test_utils:run_and_check( + Config, RConf, ["compile"], + {ok, [{app, Name}, + {plugin, PluginName, Vsn2}, + {plugin, DepName2}, + {plugin, DepName3}, + {dep, DepName}]} + ), + + meck:unload(rebar_dir). + +upgrade(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]), + + PkgName = rebar_test_utils:create_random_name("pkg1_"), + mock_git_resource:mock([]), + mock_pkg_resource:mock([ + {pkgdeps, [{{iolist_to_binary(PkgName), <<"0.1.0">>}, []}, + {{iolist_to_binary(PkgName), <<"0.0.1">>}, []}, + {{iolist_to_binary(PkgName), <<"0.1.1">>}, []}]} + ]), + + RConfFile = rebar_test_utils:create_config(AppDir, [{plugins, [list_to_atom(PkgName)]}]), + {ok, RConf} = file:consult(RConfFile), + + %% Build with deps. + rebar_test_utils:run_and_check( + Config, RConf, ["compile"], + {ok, [{app, Name}, {plugin, PkgName, <<"0.1.1">>}]} + ), + + catch mock_pkg_resource:unmock(), + mock_pkg_resource:mock([ + {pkgdeps, [{{iolist_to_binary(PkgName), <<"0.1.0">>}, []}, + {{iolist_to_binary(PkgName), <<"0.0.1">>}, []}, + {{iolist_to_binary(PkgName), <<"0.1.3">>}, []}, + {{iolist_to_binary(PkgName), <<"0.1.1">>}, []}]}, + {upgrade, [PkgName]} + ]), + + %% Build with deps. + rebar_test_utils:run_and_check( + Config, RConf, ["plugins", "upgrade", PkgName], + {ok, [{app, Name}, {plugin, PkgName, <<"0.1.3">>}]} + ). |