diff options
| author | Fred Hebert <mononcqc@ferd.ca> | 2015-06-19 10:19:12 -0400 | 
|---|---|---|
| committer | Fred Hebert <mononcqc@ferd.ca> | 2015-06-19 10:19:12 -0400 | 
| commit | 70dbbf95c24c054514e0ef2ba28c765d33bbf5d7 (patch) | |
| tree | 8b1bcec2ea3ed1a3adf3a8211d59bab985a4464b | |
| parent | 4edfed60a201921995cea987494839eeb6b75638 (diff) | |
| parent | f7ff07a87d3e282b4016c1725d149389b0d3b6f9 (diff) | |
Merge pull request #505 from tsloughter/plugins_provider
plugins provider
| -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">>}]} +     ). | 
