summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/r3.erl7
-rw-r--r--src/rebar.app.src6
-rw-r--r--src/rebar3.erl66
-rw-r--r--src/rebar_agent.erl118
-rw-r--r--src/rebar_app_discover.erl149
-rw-r--r--src/rebar_app_info.erl80
-rw-r--r--src/rebar_config.erl43
-rw-r--r--src/rebar_core.erl18
-rw-r--r--src/rebar_digraph.erl8
-rw-r--r--src/rebar_dir.erl58
-rw-r--r--src/rebar_erlc_compiler.erl3
-rw-r--r--src/rebar_fetch.erl2
-rw-r--r--src/rebar_file_utils.erl108
-rw-r--r--src/rebar_git_resource.erl2
-rw-r--r--src/rebar_hooks.erl81
-rw-r--r--src/rebar_otp_app.erl17
-rw-r--r--src/rebar_pkg_resource.erl3
-rw-r--r--src/rebar_plugins.erl107
-rw-r--r--src/rebar_prv_app_discovery.erl13
-rw-r--r--src/rebar_prv_as.erl9
-rw-r--r--src/rebar_prv_clean.erl6
-rw-r--r--src/rebar_prv_common_test.erl70
-rw-r--r--src/rebar_prv_compile.erl57
-rw-r--r--src/rebar_prv_cover.erl2
-rw-r--r--src/rebar_prv_dialyzer.erl243
-rw-r--r--src/rebar_prv_do.erl18
-rw-r--r--src/rebar_prv_edoc.erl2
-rw-r--r--src/rebar_prv_escriptize.erl11
-rw-r--r--src/rebar_prv_eunit.erl69
-rw-r--r--src/rebar_prv_help.erl10
-rw-r--r--src/rebar_prv_install_deps.erl180
-rw-r--r--src/rebar_prv_lock.erl2
-rw-r--r--src/rebar_prv_new.erl2
-rw-r--r--src/rebar_prv_packages.erl25
-rw-r--r--src/rebar_prv_plugins.erl67
-rw-r--r--src/rebar_prv_plugins_upgrade.erl110
-rw-r--r--src/rebar_prv_release.erl28
-rw-r--r--src/rebar_prv_relup.erl40
-rw-r--r--src/rebar_prv_report.erl2
-rw-r--r--src/rebar_prv_shell.erl189
-rw-r--r--src/rebar_prv_tar.erl25
-rw-r--r--src/rebar_prv_unlock.erl2
-rw-r--r--src/rebar_prv_update.erl2
-rw-r--r--src/rebar_prv_upgrade.erl23
-rw-r--r--src/rebar_prv_version.erl2
-rw-r--r--src/rebar_prv_xref.erl2
-rw-r--r--src/rebar_relx.erl69
-rw-r--r--src/rebar_state.erl101
-rw-r--r--src/rebar_templater.erl2
-rw-r--r--src/rebar_utils.erl49
50 files changed, 1631 insertions, 677 deletions
diff --git a/src/r3.erl b/src/r3.erl
new file mode 100644
index 0000000..5e8b26d
--- /dev/null
+++ b/src/r3.erl
@@ -0,0 +1,7 @@
+%%% external alias for rebar_agent
+-module(r3).
+-export([do/1, do/2]).
+
+do(Command) -> rebar_agent:do(Command).
+
+do(Namespace, Command) -> rebar_agent:do(Namespace, Command).
diff --git a/src/rebar.app.src b/src/rebar.app.src
index f753784..e5d56bb 100644
--- a/src/rebar.app.src
+++ b/src/rebar.app.src
@@ -3,7 +3,7 @@
{application, rebar,
[{description, "Rebar: Erlang Build Tool"},
- {vsn, "3.0.0-alpha-5"},
+ {vsn, "3.0.0-beta-1"},
{modules, []},
{registered, []},
{applications, [kernel,
@@ -17,6 +17,7 @@
common_test,
erlware_commons,
providers,
+ bbmustache,
relx,
inets]},
{env, [
@@ -44,7 +45,10 @@
rebar_prv_lock,
rebar_prv_new,
rebar_prv_packages,
+ rebar_prv_plugins,
+ rebar_prv_plugins_upgrade,
rebar_prv_release,
+ rebar_prv_relup,
rebar_prv_report,
rebar_prv_shell,
rebar_prv_tar,
diff --git a/src/rebar3.erl b/src/rebar3.erl
index 1a02407..c501709 100644
--- a/src/rebar3.erl
+++ b/src/rebar3.erl
@@ -56,7 +56,7 @@ main(Args) ->
%% Erlang-API entry point
run(BaseState, Commands) ->
- _ = application:load(rebar),
+ start_and_load_apps(),
BaseState1 = rebar_state:set(BaseState, task, Commands),
BaseState2 = rebar_state:set(BaseState1, caller, api),
run_aux(BaseState2, Commands).
@@ -66,7 +66,7 @@ run(BaseState, Commands) ->
%% ====================================================================
run(RawArgs) ->
- _ = application:load(rebar),
+ start_and_load_apps(),
BaseState = init_config(),
BaseState1 = rebar_state:set(BaseState, caller, command_line),
@@ -83,16 +83,6 @@ run(RawArgs) ->
run_aux(BaseState2, RawArgs).
run_aux(State, RawArgs) ->
- %% Make sure crypto is running
- case crypto:start() of
- ok -> ok;
- {error,{already_started,crypto}} -> ok
- end,
- application:start(asn1),
- application:start(public_key),
- application:start(ssl),
- inets:start(),
-
State2 = case os:getenv("REBAR_PROFILE") of
false ->
State;
@@ -105,13 +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),
- State4 = rebar_plugins:install(State3),
-
%% Providers can modify profiles stored in opts, so set default after initializing providers
- State5 = rebar_state:create_logic_providers(Providers, State4),
+ State4 = rebar_state:create_logic_providers(Providers, State3),
+ State5 = rebar_plugins:project_apps_install(State4),
State6 = rebar_state:default(State5, rebar_state:opts(State5)),
{Task, Args} = parse_args(RawArgs),
@@ -131,8 +120,7 @@ init_config() ->
ConfigFile ->
rebar_config:consult_file(ConfigFile)
end,
-
- Config1 = rebar_config:merge_locks(Config, rebar_config:consult_file(?LOCK_FILE)),
+ Config1 = rebar_config:merge_locks(Config, rebar_config:consult_lock_file(?LOCK_FILE)),
%% If $HOME/.config/rebar3/config exists load and use as global config
GlobalConfigFile = rebar_dir:global_config(),
@@ -140,8 +128,19 @@ init_config() ->
true ->
?DEBUG("Load global config file ~p",
[GlobalConfigFile]),
- GlobalConfig = rebar_state:new(rebar_config:consult_file(GlobalConfigFile)),
- rebar_state:new(GlobalConfig, Config1);
+ GlobalConfigTerms = rebar_config:consult_file(GlobalConfigFile),
+ GlobalConfig = rebar_state:new(GlobalConfigTerms),
+
+ %% 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]),
+ GlobalState = rebar_plugins:handle_plugins(global,
+ rebar_state:get(GlobalConfigThrowAway, plugins, []),
+ GlobalConfigThrowAway),
+ GlobalPlugins = rebar_state:providers(GlobalState),
+ GlobalConfig2 = rebar_state:set(GlobalConfig, plugins, []),
+ 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,
@@ -255,5 +254,32 @@ handle_error(Error) ->
%% Dump this error to console
?ERROR("Uncaught error in rebar_core. Run with DEBUG=1 to see stacktrace", []),
?DEBUG("Uncaught error: ~p", [Error]),
+ case erlang:get_stacktrace() of
+ [] -> ok;
+ Trace ->
+ ?DEBUG("Stack trace to the error location: ~p", [Trace])
+ end,
?INFO("When submitting a bug report, please include the output of `rebar3 report \"your command\"`", []),
erlang:halt(1).
+
+start_and_load_apps() ->
+ _ = application:load(rebar),
+ %% Make sure crypto is running
+ case crypto:start() of
+ ok -> ok;
+ {error,{already_started,crypto}} -> ok
+ end,
+ application:start(asn1),
+ application:start(public_key),
+ application:start(ssl),
+ inets:start(),
+ inets:start(httpc, [{profile, hex}]),
+ http_opts().
+
+http_opts() ->
+ Opts = [{max_sessions, 4},
+ {max_keep_alive_length, 4},
+ {keep_alive_timeout, 120000},
+ {max_pipeline_length, 4},
+ {pipeline_timeout, 60000}],
+ httpc:set_options(Opts, hex).
diff --git a/src/rebar_agent.erl b/src/rebar_agent.erl
new file mode 100644
index 0000000..24ac626
--- /dev/null
+++ b/src/rebar_agent.erl
@@ -0,0 +1,118 @@
+-module(rebar_agent).
+-export([start_link/1, do/1, do/2]).
+-export([init/1,
+ handle_call/3, handle_cast/2, handle_info/2,
+ code_change/3, terminate/2]).
+
+-include("rebar.hrl").
+
+-record(state, {state,
+ cwd,
+ show_warning=true}).
+
+start_link(State) ->
+ gen_server:start_link({local, ?MODULE}, ?MODULE, State, []).
+
+do(Command) when is_atom(Command) ->
+ gen_server:call(?MODULE, {cmd, Command}, infinity).
+
+do(Namespace, Command) when is_atom(Namespace), is_atom(Command) ->
+ gen_server:call(?MODULE, {cmd, Namespace, Command}, infinity).
+
+init(State) ->
+ Cwd = rebar_dir:get_cwd(),
+ {ok, #state{state=State, cwd=Cwd}}.
+
+handle_call({cmd, Command}, _From, State=#state{state=RState, cwd=Cwd}) ->
+ MidState = maybe_show_warning(State),
+ {Res, NewRState} = run(default, Command, RState, Cwd),
+ {reply, Res, MidState#state{state=NewRState}};
+handle_call({cmd, Namespace, Command}, _From, State = #state{state=RState, cwd=Cwd}) ->
+ MidState = maybe_show_warning(State),
+ {Res, NewRState} = run(Namespace, Command, RState, Cwd),
+ {reply, Res, MidState#state{state=NewRState}};
+handle_call(_Call, _From, State) ->
+ {noreply, State}.
+
+handle_cast(_Cast, State) ->
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+run(Namespace, Command, RState, Cwd) ->
+ try
+ case rebar_dir:get_cwd() of
+ Cwd ->
+ Args = [atom_to_list(Namespace), atom_to_list(Command)],
+ CmdState0 = refresh_state(RState, Cwd),
+ CmdState1 = rebar_state:set(CmdState0, task, atom_to_list(Command)),
+ CmdState = rebar_state:set(CmdState1, caller, api),
+ case rebar3:run(CmdState, Args) of
+ {ok, TmpState} ->
+ refresh_paths(TmpState),
+ {ok, CmdState};
+ {error, Err} when is_list(Err) ->
+ refresh_paths(CmdState),
+ {{error, lists:flatten(Err)}, CmdState};
+ {error, Err} ->
+ refresh_paths(CmdState),
+ {{error, Err}, CmdState}
+ end;
+ _ ->
+ {{error, cwd_changed}, RState}
+ end
+ catch
+ Type:Reason ->
+ ?DEBUG("Agent Stacktrace: ~p", [erlang:get_stacktrace()]),
+ {{error, {Type, Reason}}, RState}
+ end.
+
+maybe_show_warning(S=#state{show_warning=true}) ->
+ ?WARN("This feature is experimental and may be modified or removed at any time.", []),
+ S#state{show_warning=false};
+maybe_show_warning(State) ->
+ State.
+
+refresh_paths(RState) ->
+ ToRefresh = (rebar_state:code_paths(RState, all_deps)
+ ++ [filename:join([rebar_app_info:out_dir(App), "test"])
+ || App <- rebar_state:project_apps(RState)]
+ %% make sure to never reload self; halt()s the VM
+ ) -- [filename:dirname(code:which(?MODULE))],
+ %% Similar to rebar_utils:update_code/1, but also forces a reload
+ %% of used modules. Also forces to reload all of ebin/ instead
+ %% of just the modules in the .app file, because 'extra_src_dirs'
+ %% allows to load and compile files that are not to be kept
+ %% in the app file.
+ lists:foreach(fun(Path) ->
+ Name = filename:basename(Path, "/ebin"),
+ Files = filelib:wildcard(filename:join([Path, "*.beam"])),
+ Modules = [list_to_atom(filename:basename(F, ".beam"))
+ || F <- Files],
+ App = list_to_atom(Name),
+ application:load(App),
+ case application:get_key(App, modules) of
+ undefined ->
+ code:add_patha(Path),
+ ok;
+ {ok, _} ->
+ ?DEBUG("reloading ~p from ~s", [Modules, Path]),
+ code:replace_path(Name, Path),
+ [begin code:purge(M), code:delete(M), code:load_file(M) end
+ || M <- Modules]
+ end
+ end, ToRefresh).
+
+refresh_state(RState, _Dir) ->
+ lists:foldl(
+ fun(F, State) -> F(State) end,
+ rebar3:init_config(),
+ [fun(S) -> rebar_state:apply_profiles(S, rebar_state:current_profiles(RState)) end]
+ ).
diff --git a/src/rebar_app_discover.erl b/src/rebar_app_discover.erl
index 73401bc..9c4a5ff 100644
--- a/src/rebar_app_discover.erl
+++ b/src/rebar_app_discover.erl
@@ -7,6 +7,7 @@
find_apps/2,
find_app/2]).
+-include("rebar.hrl").
-include_lib("providers/include/providers.hrl").
do(State, LibDirs) ->
@@ -19,13 +20,19 @@ do(State, LibDirs) ->
%% Sort apps so we get the same merged deps config everytime
SortedApps = rebar_utils:sort_deps(Apps),
lists:foldl(fun(AppInfo, StateAcc) ->
- {AppInfo1, StateAcc1} = merge_deps(AppInfo, StateAcc),
Name = rebar_app_info:name(AppInfo),
- OutDir = filename:join(DepsDir, Name),
- AppInfo2 = rebar_app_info:out_dir(AppInfo1, OutDir),
- ProjectDeps1 = lists:delete(Name, ProjectDeps),
- rebar_state:project_apps(StateAcc1
- ,rebar_app_info:deps(AppInfo2, ProjectDeps1))
+ case enable(State, AppInfo) of
+ true ->
+ {AppInfo1, StateAcc1} = merge_deps(AppInfo, StateAcc),
+ OutDir = filename:join(DepsDir, Name),
+ AppInfo2 = rebar_app_info:out_dir(AppInfo1, OutDir),
+ ProjectDeps1 = lists:delete(Name, ProjectDeps),
+ rebar_state:project_apps(StateAcc1
+ ,rebar_app_info:deps(AppInfo2, ProjectDeps1));
+ false ->
+ ?INFO("Ignoring ~s", [Name]),
+ StateAcc
+ end
end, State, SortedApps).
format_error({module_list, File}) ->
@@ -44,7 +51,8 @@ merge_deps(AppInfo, State) ->
rebar_state:apply_profiles(
rebar_state:new(reset_hooks(rebar_state:opts(State, Default)), C,
rebar_app_info:dir(AppInfo)), CurrentProfiles), Name),
- AppInfo1 = rebar_app_info:state(AppInfo, AppState),
+ AppState1 = rebar_state:set(AppState, artifacts, []),
+ AppInfo1 = rebar_app_info:state(AppInfo, AppState1),
State1 = lists:foldl(fun(Profile, StateAcc) ->
AppProfDeps = rebar_state:get(AppState, {deps, Profile}, []),
@@ -65,10 +73,10 @@ project_app_config(AppInfo, State) ->
%% Here we check if the app is at the root of the project.
%% If it is, then drop the hooks from the config so they aren't run twice
maybe_reset_hooks(C, Dir, State) ->
- case filename:dirname(rebar_dir:root_dir(State)) of
+ case ec_file:real_dir_path(rebar_dir:root_dir(State)) of
Dir ->
C1 = proplists:delete(provider_hooks, C),
- proplists:delete(hooks, C1);
+ proplists:delete(post_hooks, proplists:delete(pre_hooks, C1));
_ ->
C
end.
@@ -123,9 +131,42 @@ find_apps(LibDirs, Validate) ->
find_app(AppDir, Validate) ->
AppFile = filelib:wildcard(filename:join([AppDir, "ebin", "*.app"])),
AppSrcFile = filelib:wildcard(filename:join([AppDir, "src", "*.app.src"])),
- case AppFile of
- [File] ->
- AppInfo = create_app_info(AppDir, File),
+ AppInfo = try_handle_app_file(AppFile, AppDir, AppSrcFile, Validate),
+ AppInfo.
+
+app_dir(AppFile) ->
+ filename:join(rebar_utils:droplast(filename:split(filename:dirname(AppFile)))).
+
+-spec create_app_info(file:name(), file:name()) -> rebar_app_info:t() | {error, term()}.
+create_app_info(AppDir, AppFile) ->
+ [{application, AppName, AppDetails}] = rebar_config:consult_app_file(AppFile),
+ AppVsn = proplists:get_value(vsn, AppDetails),
+ Applications = proplists:get_value(applications, AppDetails, []),
+ IncludedApplications = proplists:get_value(included_applications, AppDetails, []),
+ {ok, AppInfo} = rebar_app_info:new(AppName, AppVsn, AppDir, []),
+ AppInfo1 = rebar_app_info:applications(
+ rebar_app_info:app_details(AppInfo, AppDetails),
+ IncludedApplications++Applications),
+ Valid = case rebar_app_utils:validate_application_info(AppInfo1) of
+ true ->
+ true;
+ _ ->
+ false
+ end,
+ rebar_app_info:dir(rebar_app_info:valid(AppInfo1, Valid), AppDir).
+
+dedup([]) -> [];
+dedup([A]) -> [A];
+dedup([H,H|T]) -> dedup([H|T]);
+dedup([H|T]) -> [H|dedup(T)].
+
+%% Read in and parse the .app file if it is availabe. Do the same for
+%% the .app.src file if it exists.
+try_handle_app_file([], AppDir, AppSrcFile, Validate) ->
+ try_handle_app_src_file([], AppDir, AppSrcFile, Validate);
+try_handle_app_file([File], AppDir, AppSrcFile, Validate) ->
+ try create_app_info(AppDir, File) of
+ AppInfo ->
AppInfo1 = rebar_app_info:app_file(AppInfo, File),
AppInfo2 = case AppSrcFile of
[F] ->
@@ -134,7 +175,7 @@ find_app(AppDir, Validate) ->
AppInfo1;
Other when is_list(Other) ->
throw({error, {multiple_app_files, Other}})
- end,
+ end,
case Validate of
valid ->
case rebar_app_utils:validate_application_info(AppInfo2) of
@@ -152,57 +193,35 @@ find_app(AppDir, Validate) ->
end;
all ->
{true, AppInfo2}
- end;
- [] ->
- case AppSrcFile of
- [File] ->
- case Validate of
- V when V =:= invalid ; V =:= all ->
- AppInfo = create_app_info(AppDir, File),
- case AppInfo of
- {error, Reason} ->
- throw({error, {invalid_app_file, File, Reason}});
- _ ->
- {true, rebar_app_info:app_file_src(AppInfo, File)}
- end;
- valid ->
- false
- end;
- [] ->
- false;
- Other when is_list(Other) ->
- throw({error, {multiple_app_files, Other}})
- end;
- Other when is_list(Other) ->
- throw({error, {multiple_app_files, Other}})
- end.
-
-app_dir(AppFile) ->
- filename:join(rebar_utils:droplast(filename:split(filename:dirname(AppFile)))).
-
--spec create_app_info(file:name(), file:name()) -> rebar_app_info:t() | {error, term()}.
-create_app_info(AppDir, AppFile) ->
- case file:consult(AppFile) of
- {ok, [{application, AppName, AppDetails}]} ->
- AppVsn = proplists:get_value(vsn, AppDetails),
- Applications = proplists:get_value(applications, AppDetails, []),
- IncludedApplications = proplists:get_value(included_applications, AppDetails, []),
- {ok, AppInfo} = rebar_app_info:new(AppName, AppVsn, AppDir, []),
- AppInfo1 = rebar_app_info:applications(
- rebar_app_info:app_details(AppInfo, AppDetails),
- IncludedApplications++Applications),
- Valid = case rebar_app_utils:validate_application_info(AppInfo1) of
- true ->
- true;
- _ ->
- false
- end,
- rebar_app_info:dir(rebar_app_info:valid(AppInfo1, Valid), AppDir);
+ end
+ catch
+ throw:{error, {Module, Reason}} ->
+ ?DEBUG("Falling back to app.src file because .app failed: ~s", [Module:format_error(Reason)]),
+ try_handle_app_src_file(File, AppDir, AppSrcFile, Validate)
+ end;
+try_handle_app_file(Other, _AppDir, _AppSrcFile, _Validate) ->
+ throw({error, {multiple_app_files, Other}}).
+
+%% Read in the .app.src file if we aren't looking for a valid (already built) app
+try_handle_app_src_file(_, _AppDir, [], _Validate) ->
+ false;
+try_handle_app_src_file(_, _AppDir, _AppSrcFile, valid) ->
+ false;
+try_handle_app_src_file(_, AppDir, [File], Validate) when Validate =:= invalid
+ ; Validate =:= all ->
+ AppInfo = create_app_info(AppDir, File),
+ case AppInfo of
{error, Reason} ->
- {error, Reason}
- end.
+ throw({error, {invalid_app_file, File, Reason}});
+ _ ->
+ {true, rebar_app_info:app_file_src(AppInfo, File)}
+ end;
+try_handle_app_src_file(_, _AppDir, Other, _Validate) ->
+ throw({error, {multiple_app_files, Other}}).
-dedup([]) -> [];
-dedup([A]) -> [A];
-dedup([H,H|T]) -> dedup([H|T]);
-dedup([H|T]) -> [H|dedup(T)].
+enable(State, AppInfo) ->
+ not lists:member(to_atom(rebar_app_info:name(AppInfo)),
+ rebar_state:get(State, excluded_apps, [])).
+
+to_atom(Bin) ->
+ list_to_atom(binary_to_list(Bin)).
diff --git a/src/rebar_app_info.erl b/src/rebar_app_info.erl
index 91640f2..6e35b8f 100644
--- a/src/rebar_app_info.erl
+++ b/src/rebar_app_info.erl
@@ -34,29 +34,35 @@
source/2,
state/1,
state/2,
+ state_or_new/2,
+ is_lock/1,
+ is_lock/2,
is_checkout/1,
is_checkout/2,
valid/1,
valid/2]).
+-include("rebar.hrl").
+
-export_type([t/0]).
--record(app_info_t, {name :: binary(),
- app_file_src :: file:filename_all() | undefined,
- app_file :: file:filename_all() | undefined,
- config :: rebar_state:t() | undefined,
- original_vsn :: binary() | string() | undefined,
- app_details=[] :: list(),
- applications=[] :: list(),
- deps=[] :: list(),
+-record(app_info_t, {name :: binary(),
+ app_file_src :: file:filename_all() | undefined,
+ app_file :: file:filename_all() | undefined,
+ config :: rebar_state:t() | undefined,
+ original_vsn :: binary() | string() | undefined,
+ app_details=[] :: list(),
+ applications=[] :: list(),
+ deps=[] :: list(),
profiles=[default] :: [atom()],
- dep_level=0 :: integer(),
- dir :: file:name(),
- out_dir :: file:name(),
- source :: string() | tuple() | undefined,
- state :: rebar_state:t() | undefined,
- is_checkout=false :: boolean(),
- valid :: boolean()}).
+ dep_level=0 :: integer(),
+ dir :: file:name(),
+ out_dir :: file:name(),
+ source :: string() | tuple() | undefined,
+ state :: rebar_state:t() | undefined,
+ is_lock=false :: boolean(),
+ is_checkout=false :: boolean(),
+ valid :: boolean()}).
%%============================================================================
%% types
@@ -158,17 +164,18 @@ app_file(AppInfo=#app_info_t{}, AppFile) ->
-spec app_details(t()) -> list().
app_details(AppInfo=#app_info_t{app_details=[]}) ->
- AppFile = case app_file(AppInfo) of
- undefined ->
- app_file_src(AppInfo);
- File ->
- File
- end,
- case file:consult(AppFile) of
- {ok, [{application, _, AppDetails}]} ->
- AppDetails;
- _ ->
- []
+ case app_file(AppInfo) of
+ undefined ->
+ rebar_file_utils:try_consult(app_file_src(AppInfo));
+ AppFile ->
+ try
+ rebar_file_utils:try_consult(AppFile)
+ catch
+ throw:{error, {Module, Reason}} ->
+ ?DEBUG("Warning, falling back to .app.src because of: ~s",
+ [Module:format_error(Reason)]),
+ rebar_file_utils:try_consult(app_file_src(AppInfo))
+ end
end;
app_details(#app_info_t{app_details=AppDetails}) ->
AppDetails.
@@ -254,6 +261,22 @@ state(AppInfo=#app_info_t{}, State) ->
state(#app_info_t{state=State}) ->
State.
+-spec state_or_new(rebar_state:t(), t()) -> rebar_state:t().
+state_or_new(State, AppInfo=#app_info_t{state=undefined}) ->
+ AppDir = dir(AppInfo),
+ C = rebar_config:consult(AppDir),
+ rebar_state:new(State, C, AppDir);
+state_or_new(_State, #app_info_t{state=State}) ->
+ State.
+
+-spec is_lock(t(), boolean()) -> t().
+is_lock(AppInfo=#app_info_t{}, IsLock) ->
+ AppInfo#app_info_t{is_lock=IsLock}.
+
+-spec is_lock(t()) -> boolean().
+is_lock(#app_info_t{is_lock=IsLock}) ->
+ IsLock.
+
-spec is_checkout(t(), boolean()) -> t().
is_checkout(AppInfo=#app_info_t{}, IsCheckout) ->
AppInfo#app_info_t{is_checkout=IsCheckout}.
@@ -263,8 +286,9 @@ is_checkout(#app_info_t{is_checkout=IsCheckout}) ->
IsCheckout.
-spec valid(t()) -> boolean().
-valid(AppInfo=#app_info_t{valid=undefined}) ->
- case rebar_app_utils:validate_application_info(AppInfo) of
+valid(AppInfo=#app_info_t{valid=undefined, state=State}) ->
+ case rebar_app_utils:validate_application_info(AppInfo)
+ andalso rebar_state:has_all_artifacts(State) =:= true of
true ->
true;
_ ->
diff --git a/src/rebar_config.erl b/src/rebar_config.erl
index c858fef..3e06c38 100644
--- a/src/rebar_config.erl
+++ b/src/rebar_config.erl
@@ -27,7 +27,10 @@
-module(rebar_config).
-export([consult/1
+ ,consult_app_file/1
,consult_file/1
+ ,consult_lock_file/1
+ ,verify_config_format/1
,format_error/1
,merge_locks/2]).
@@ -43,10 +46,21 @@
consult(Dir) ->
consult_file(filename:join(Dir, ?DEFAULT_CONFIG_FILE)).
--spec consult_file(file:name()) -> [any()].
-consult_file(File) when is_binary(File) ->
- consult_file(binary_to_list(File));
+consult_app_file(File) ->
+ consult_file_(File).
+
+consult_lock_file(File) ->
+ consult_file_(File).
+
consult_file(File) ->
+ Terms = consult_file_(File),
+ true = verify_config_format(Terms),
+ Terms.
+
+-spec consult_file_(file:name()) -> [any()].
+consult_file_(File) when is_binary(File) ->
+ consult_file_(binary_to_list(File));
+consult_file_(File) ->
case filename:extension(File) of
".script" ->
consult_and_eval(remove_script_ext(File), File);
@@ -57,10 +71,17 @@ consult_file(File) ->
{ok, Terms} = consult_and_eval(File, Script),
Terms;
false ->
- try_consult(File)
+ rebar_file_utils:try_consult(File)
end
end.
+verify_config_format([]) ->
+ true;
+verify_config_format([{_Key, _Value} | T]) ->
+ verify_config_format(T);
+verify_config_format([Term | _]) ->
+ throw(?PRV_ERROR({bad_config_format, Term})).
+
%% no lockfile
merge_locks(Config, []) ->
Config;
@@ -78,6 +99,8 @@ merge_locks(Config, [Locks]) ->
NewDeps = find_newly_added(ConfigDeps, Locks),
[{{locks, default}, Locks}, {{deps, default}, NewDeps++Deps} | Config].
+format_error({bad_config_format, Term}) ->
+ io_lib:format("Unable to parse config. Term is not in {Key, Value} format:~n~p", [Term]);
format_error({bad_dep_name, Dep}) ->
io_lib:format("Dependency name must be an atom, instead found: ~p", [Dep]).
@@ -87,22 +110,12 @@ format_error({bad_dep_name, Dep}) ->
consult_and_eval(File, Script) ->
?DEBUG("Evaluating config script ~p", [Script]),
- StateData = try_consult(File),
+ StateData = rebar_file_utils:try_consult(File),
file:script(Script, bs([{'CONFIG', StateData}, {'SCRIPT', Script}])).
remove_script_ext(F) ->
filename:rootname(F, ".script").
-try_consult(File) ->
- case file:consult(File) of
- {ok, Terms} ->
- Terms;
- {error, enoent} ->
- [];
- {error, Reason} ->
- ?ABORT("Failed to read config file ~s:~n ~p", [File, Reason])
- end.
-
bs(Vars) ->
lists:foldl(fun({K,V}, Bs) ->
erl_eval:add_binding(K, V, Bs)
diff --git a/src/rebar_core.erl b/src/rebar_core.erl
index 7fe7332..b1647f0 100644
--- a/src/rebar_core.erl
+++ b/src/rebar_core.erl
@@ -26,9 +26,10 @@
%% -------------------------------------------------------------------
-module(rebar_core).
--export([init_command/2, process_namespace/2, process_command/2, do/2]).
+-export([init_command/2, process_namespace/2, process_command/2, do/2, format_error/1]).
-include("rebar.hrl").
+-include_lib("providers/include/providers.hrl").
init_command(State, do) ->
process_command(rebar_state:namespace(State, default), do);
@@ -119,9 +120,22 @@ do([ProviderName | Rest], State) ->
,rebar_state:providers(State)
,rebar_state:namespace(State))
end,
- case providers:do(Provider, State) of
+
+ try providers:do(Provider, State) of
{ok, State1} ->
do(Rest, State1);
{error, Error} ->
{error, Error}
+ catch
+ error:undef ->
+ %% This should really only happen if a plugin provider doesn't export do/1
+ ?DEBUG("Undefined call to provider's do function:~n~p", [erlang:get_stacktrace()]),
+ ?PRV_ERROR({bad_provider_namespace, ProviderName});
+ error:{badrecord,provider} ->
+ {error, ProviderName}
end.
+
+format_error({bad_provider_namespace, {Namespace, Name}}) ->
+ io_lib:format("Undefined command ~s in namespace ~s", [Name, Namespace]);
+format_error({bad_provider_namespace, Name}) ->
+ io_lib:format("Undefined command ~s", [Name]).
diff --git a/src/rebar_digraph.erl b/src/rebar_digraph.erl
index 9e10d49..d52a811 100644
--- a/src/rebar_digraph.erl
+++ b/src/rebar_digraph.erl
@@ -124,6 +124,10 @@ find_app_by_name(Name, Apps) ->
rebar_app_info:name(App) =:= Name
end, Apps).
+%% The union of all entries in the applications list for an app and
+%% the deps listed in its rebar.config is all deps that may be needed
+%% for building the app.
all_apps_deps(App) ->
- Applications = [atom_to_binary(X, utf8) || X <- rebar_app_info:applications(App)],
- lists:usort(rebar_app_info:deps(App) ++ Applications).
+ Applications = lists:usort([atom_to_binary(X, utf8) || X <- rebar_app_info:applications(App)]),
+ Deps = lists:usort(lists:map(fun({Name, _}) -> Name; (Name) -> Name end, rebar_app_info:deps(App))),
+ lists:umerge(Deps, Applications).
diff --git a/src/rebar_dir.erl b/src/rebar_dir.erl
index a94c72d..7af94ea 100644
--- a/src/rebar_dir.erl
+++ b/src/rebar_dir.erl
@@ -20,7 +20,10 @@
template_dir/1,
processing_base_dir/1,
processing_base_dir/2,
- make_relative_path/2]).
+ make_relative_path/2,
+ src_dirs/1, src_dirs/2,
+ extra_src_dirs/1, extra_src_dirs/2,
+ all_src_dirs/1, all_src_dirs/3]).
-include("rebar.hrl").
@@ -30,15 +33,16 @@ base_dir(State) ->
-spec profile_dir(rebar_state:t(), [atom()]) -> file:filename_all().
profile_dir(State, Profiles) ->
- ProfilesStrings = case [ec_cnv:to_list(P) || P <- Profiles] of
- ["default"] -> ["default"];
+ {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`
- ["default"|Rest] -> Rest
+ ["default"|Rest] -> {rebar_state:get(State, base_dir, ?DEFAULT_BASE_DIR), Rest}
end,
ProfilesDir = string:join(ProfilesStrings, "+"),
- filename:join(rebar_state:get(State, base_dir, ?DEFAULT_BASE_DIR), ProfilesDir).
-
+ filename:join(BaseDir, ProfilesDir).
-spec deps_dir(rebar_state:t()) -> file:filename_all().
deps_dir(State) ->
@@ -81,11 +85,11 @@ global_config_dir(State) ->
rebar_state:get(State, global_rebar_dir, filename:join([Home, ".config", "rebar3"])).
global_config(State) ->
- filename:join(global_config_dir(State), "config").
+ filename:join(global_config_dir(State), "rebar.config").
global_config() ->
Home = home_dir(),
- filename:join([Home, ".config", "rebar3", "config"]).
+ filename:join([Home, ".config", "rebar3", "rebar.config"]).
global_cache_dir(State) ->
Home = home_dir(),
@@ -96,7 +100,11 @@ local_cache_dir(Dir) ->
get_cwd() ->
{ok, Dir} = file:get_cwd(),
- Dir.
+ %% On windows cwd may return capital letter for drive,
+ %% for example C:/foobar. But as said in http://www.erlang.org/doc/man/filename.html#join-1
+ %% filename:join/1,2 anyway will convert drive-letter to lowercase, so we have to "internalize"
+ %% cwd as soon as it possible.
+ filename:join([Dir]).
template_globals(State) ->
filename:join([global_config_dir(State), "templates", "globals"]).
@@ -120,3 +128,35 @@ do_make_relative_path([H|T1], [H|T2]) ->
do_make_relative_path(Source, Target) ->
Base = lists:duplicate(max(length(Target) - 1, 0), ".."),
filename:join(Base ++ Source).
+
+-spec src_dirs(rebar_state:t()) -> list(file:filename_all()).
+src_dirs(State) -> src_dirs(State, []).
+
+-spec src_dirs(rebar_state:t(), list(file:filename_all())) -> list(file:filename_all()).
+src_dirs(State, Default) ->
+ ErlOpts = rebar_utils:erl_opts(State),
+ Vs = proplists:get_all_values(src_dirs, ErlOpts),
+ case lists:append([rebar_state:get(State, src_dirs, []) | Vs]) of
+ [] -> Default;
+ Dirs -> Dirs
+ end.
+
+-spec extra_src_dirs(rebar_state:t()) -> list(file:filename_all()).
+extra_src_dirs(State) -> extra_src_dirs(State, []).
+
+-spec extra_src_dirs(rebar_state:t(), list(file:filename_all())) -> list(file:filename_all()).
+extra_src_dirs(State, Default) ->
+ ErlOpts = rebar_utils:erl_opts(State),
+ Vs = proplists:get_all_values(extra_src_dirs, ErlOpts),
+ case lists:append([rebar_state:get(State, extra_src_dirs, []) | Vs]) of
+ [] -> Default;
+ Dirs -> Dirs
+ end.
+
+-spec all_src_dirs(rebar_state:t()) -> list(file:filename_all()).
+all_src_dirs(State) -> all_src_dirs(State, [], []).
+
+-spec all_src_dirs(rebar_state:t(), list(file:filename_all()), list(file:filename_all())) ->
+ list(file:filename_all()).
+all_src_dirs(State, SrcDefault, ExtraDefault) ->
+ src_dirs(State, SrcDefault) ++ extra_src_dirs(State, ExtraDefault).
diff --git a/src/rebar_erlc_compiler.erl b/src/rebar_erlc_compiler.erl
index c7a3474..b9072a3 100644
--- a/src/rebar_erlc_compiler.erl
+++ b/src/rebar_erlc_compiler.erl
@@ -143,8 +143,7 @@ doterl_compile(Config, Dir, OutDir, MoreSources, ErlOpts) ->
%% Support the src_dirs option allowing multiple directories to
%% contain erlang source. This might be used, for example, should
%% eunit tests be separated from the core application source.
- SrcDirs = [filename:join(Dir, X) || X <- proplists:get_value(src_dirs, ErlOpts, ["src"]) ++
- proplists:get_value(extra_src_dirs, ErlOpts, [])],
+ SrcDirs = [filename:join(Dir, X) || X <- rebar_dir:all_src_dirs(Config, ["src"], [])],
AllErlFiles = gather_src(SrcDirs, []) ++ MoreSources,
%% Make sure that ebin/ exists and is on the path
diff --git a/src/rebar_fetch.erl b/src/rebar_fetch.erl
index 0aca308..64c5380 100644
--- a/src/rebar_fetch.erl
+++ b/src/rebar_fetch.erl
@@ -72,7 +72,7 @@ format_error({failed_extract, CachePath}) ->
format_error({bad_etag, Source}) ->
io_lib:format("MD5 Checksum comparison failed for: ~s", [Source]);
format_error({fetch_fail, Source}) ->
- io_lib:format("Failed to fetch and copy dep: ~s", [Source]);
+ io_lib:format("Failed to fetch and copy dep: ~p", [Source]);
format_error({bad_checksum, File}) ->
io_lib:format("Checksum mismatch against tarball in ~s", [File]);
format_error({bad_registry_checksum, File}) ->
diff --git a/src/rebar_file_utils.erl b/src/rebar_file_utils.erl
index ef2c70f..3fc5698 100644
--- a/src/rebar_file_utils.erl
+++ b/src/rebar_file_utils.erl
@@ -26,7 +26,9 @@
%% -------------------------------------------------------------------
-module(rebar_file_utils).
--export([symlink_or_copy/2,
+-export([try_consult/1,
+ format_error/1,
+ symlink_or_copy/2,
rm_rf/1,
cp_r/2,
mv/2,
@@ -34,14 +36,32 @@
write_file_if_contents_differ/2,
system_tmpdir/0,
system_tmpdir/1,
- reset_dir/1]).
+ reset_dir/1,
+ touch/1]).
-include("rebar.hrl").
+-include_lib("providers/include/providers.hrl").
+-include_lib("kernel/include/file.hrl").
+
+
%% ===================================================================
%% Public API
%% ===================================================================
+try_consult(File) ->
+ case file:consult(File) of
+ {ok, Terms} ->
+ Terms;
+ {error, enoent} ->
+ [];
+ {error, Reason} ->
+ throw(?PRV_ERROR({bad_term_file, File, Reason}))
+ end.
+
+format_error({bad_term_file, AppFile, Reason}) ->
+ io_lib:format("Error reading file ~s: ~s", [AppFile, file:format_error(Reason)]).
+
symlink_or_copy(Source, Target) ->
Link = case os:type() of
{win32, _} ->
@@ -55,9 +75,40 @@ symlink_or_copy(Source, Target) ->
{error, eexist} ->
ok;
{error, _} ->
- cp_r([Source], Target)
+ case os:type() of
+ {win32, _} ->
+ S = unicode:characters_to_list(Source),
+ T = unicode:characters_to_list(Target),
+ case filelib:is_dir(S) of
+ true ->
+ win32_symlink(S, T);
+ false ->
+ cp_r([S], T)
+ end;
+ _ ->
+ case filelib:is_dir(Target) of
+ true ->
+ ok;
+ false ->
+ cp_r([Source], Target)
+ end
+ end
end.
+win32_symlink(Source, Target) ->
+ Res = rebar_utils:sh(
+ ?FMT("cmd /c mklink /j \"~s\" \"~s\"",
+ [filename:nativename(Target), filename:nativename(Source)]),
+ [{use_stdout, false}, return_on_error]),
+ case win32_ok(Res) of
+ true -> ok;
+ false ->
+ {error, lists:flatten(
+ io_lib:format("Failed to symlink ~s to ~s~n",
+ [Source, Target]))}
+ end.
+
+
%% @doc Remove files and directories.
%% Target is a single filename, directoryname or wildcard expression.
-spec rm_rf(string()) -> 'ok'.
@@ -104,21 +155,24 @@ mv(Source, Dest) ->
[{use_stdout, false}, abort_on_error]),
ok;
{win32, _} ->
- {ok, R} = rebar_utils:sh(
- ?FMT("move /y \"~s\" \"~s\" 1> nul",
+ Res = rebar_utils:sh(
+ ?FMT("robocopy /move /s \"~s\" \"~s\" 1> nul",
[filename:nativename(Source),
filename:nativename(Dest)]),
[{use_stdout, false}, return_on_error]),
- case R of
- [] ->
- ok;
- _ ->
+ case win32_ok(Res) of
+ true -> ok;
+ false ->
{error, lists:flatten(
io_lib:format("Failed to move ~s to ~s~n",
[Source, Dest]))}
end
end.
+win32_ok({ok, _}) -> true;
+win32_ok({error, {Rc, _}}) when Rc<9; Rc=:=16 -> true;
+win32_ok(_) -> false.
+
delete_each([]) ->
ok;
delete_each([File | Rest]) ->
@@ -170,6 +224,17 @@ reset_dir(Path) ->
%% recreate the directory
filelib:ensure_dir(filename:join([Path, "dummy.beam"])).
+
+%% Linux touch but using erlang functions to work in bot *nix os and
+%% windows
+-spec touch(Path) -> ok | {error, Reason} when
+ Path :: file:name(),
+ Reason :: file:posix().
+touch(Path) ->
+ {ok, A} = file:read_file_info(Path),
+ ok = file:write_file_info(Path, A#file_info{mtime = calendar:local_time(),
+ atime = calendar:local_time()}).
+
%% ===================================================================
%% Internal functions
%% ===================================================================
@@ -182,28 +247,27 @@ delete_each_dir_win32([Dir | Rest]) ->
delete_each_dir_win32(Rest).
xcopy_win32(Source,Dest)->
- {ok, R} = rebar_utils:sh(
- ?FMT("xcopy \"~s\" \"~s\" /q /y /e 2> nul",
+ %% "xcopy \"~s\" \"~s\" /q /y /e 2> nul", Chanegd to robocopy to
+ %% handle long names. May have issues with older windows.
+ Res = rebar_utils:sh(
+ ?FMT("robocopy \"~s\" \"~s\" /e /is /purge 2> nul",
[filename:nativename(Source), filename:nativename(Dest)]),
[{use_stdout, false}, return_on_error]),
- case length(R) > 0 of
- %% when xcopy fails, stdout is empty and and error message is printed
- %% to stderr (which is redirected to nul)
- true -> ok;
- false ->
- {error, lists:flatten(
- io_lib:format("Failed to xcopy from ~s to ~s~n",
- [Source, Dest]))}
+ case win32_ok(Res) of
+ true -> ok;
+ false ->
+ {error, lists:flatten(
+ io_lib:format("Failed to copy ~s to ~s~n",
+ [Source, Dest]))}
end.
cp_r_win32({true, SourceDir}, {true, DestDir}) ->
%% from directory to directory
- SourceBase = filename:basename(SourceDir),
- ok = case file:make_dir(filename:join(DestDir, SourceBase)) of
+ ok = case file:make_dir(DestDir) of
{error, eexist} -> ok;
Other -> Other
end,
- ok = xcopy_win32(SourceDir, filename:join(DestDir, SourceBase));
+ ok = xcopy_win32(SourceDir, DestDir);
cp_r_win32({false, Source} = S,{true, DestDir}) ->
%% from file to directory
cp_r_win32(S, {false, filename:join(DestDir, filename:basename(Source))});
diff --git a/src/rebar_git_resource.erl b/src/rebar_git_resource.erl
index 2d83579..dfec86a 100644
--- a/src/rebar_git_resource.erl
+++ b/src/rebar_git_resource.erl
@@ -109,7 +109,7 @@ download(Dir, {git, Url, Rev}, _State) ->
rebar_utils:sh(?FMT("git checkout -q ~s", [Rev]), [{cd, Dir}]).
make_vsn(Dir) ->
- {ok, Cwd} = file:get_cwd(),
+ Cwd = rebar_dir:get_cwd(),
try
ok = file:set_cwd(Dir),
{Vsn, RawRef, RawCount} = collect_default_refcount(),
diff --git a/src/rebar_hooks.erl b/src/rebar_hooks.erl
index e144a8e..56c4e91 100644
--- a/src/rebar_hooks.erl
+++ b/src/rebar_hooks.erl
@@ -1,6 +1,10 @@
-module(rebar_hooks).
--export([run_all_hooks/5]).
+-export([run_all_hooks/5
+ ,format_error/1]).
+
+-include("rebar.hrl").
+-include_lib("providers/include/providers.hrl").
-spec run_all_hooks(file:filename_all(), pre | post,
atom() | {atom(), atom()} | string(),
@@ -10,12 +14,50 @@ run_all_hooks(Dir, Type, Command, Providers, State) ->
run_hooks(Dir, Type, Command, State).
run_provider_hooks(Dir, Type, Command, Providers, State) ->
- State1 = rebar_state:providers(rebar_state:dir(State, Dir), Providers),
+ PluginDepsPaths = rebar_state:code_paths(State, all_plugin_deps),
+ code:add_pathsa(PluginDepsPaths),
+ Providers1 = rebar_state:providers(State),
+ State1 = rebar_state:providers(rebar_state:dir(State, Dir), Providers++Providers1),
AllHooks = rebar_state:get(State1, provider_hooks, []),
TypeHooks = proplists:get_value(Type, AllHooks, []),
HookProviders = proplists:get_all_values(Command, TypeHooks),
- rebar_core:do(HookProviders, State1).
+ case rebar_core:do(HookProviders, State1) of
+ {error, ProviderName} ->
+ ?DEBUG(format_error({bad_provider, Type, Command, ProviderName}), []),
+ throw(?PRV_ERROR({bad_provider, Type, Command, ProviderName}));
+ {ok, _} ->
+ rebar_utils:remove_from_code_path(PluginDepsPaths)
+ end.
+
+format_error({bad_provider, Type, Command, {Name, Namespace}}) ->
+ io_lib:format("Unable to run ~s hooks for '~p', command '~p' in namespace '~p' not found.", [Type, Command, Namespace, Name]);
+format_error({bad_provider, Type, Command, Name}) ->
+ io_lib:format("Unable to run ~s hooks for '~p', command '~p' not found.", [Type, Command, Name]).
+
+%% @doc The following environment variables are exported when running
+%% a hook (absolute paths):
+%%
+%% REBAR_DEPS_DIR = rebar_dir:deps_dir/1
+%% REBAR_BUILD_DIR = rebar_dir:base_dir/1
+%% REBAR_ROOT_DIR = rebar_dir:root_dir/1
+%% REBAR_CHECKOUTS_DIR = rebar_dir:checkouts_dir/1
+%% REBAR_PLUGINS_DIR = rebar_dir:plugins_dir/1
+%% REBAR_GLOBAL_CONFIG_DIR = rebar_dir:global_config_dir/1
+%% REBAR_GLOBAL_CACHE_DIR = rebar_dir:global_cache_dir/1
+%% REBAR_TEMPLATE_DIR = rebar_dir:template_dir/1
+%% REBAR_APP_DIRS = rebar_dir:lib_dirs/1
+%% REBAR_SRC_DIRS = rebar_dir:src_dirs/1
+%%
+%% autoconf compatible variables
+%% (see: http://www.gnu.org/software/autoconf/manual/autoconf.html#Erlang-Libraries):
+%% ERLANG_ERTS_VER = erlang:system_info(version)
+%% ERLANG_ROOT_DIR = code:root_dir/0
+%% ERLANG_LIB_DIR_erl_interface = code:lib_dir(erl_interface)
+%% ERLANG_LIB_VER_erl_interface = version part of path returned by code:lib_dir(erl_interface)
+%% ERL = ERLANG_ROOT_DIR/bin/erl
+%% ERLC = ERLANG_ROOT_DIR/bin/erl
+%%
run_hooks(Dir, Type, Command, State) ->
Hooks = case Type of
pre ->
@@ -25,7 +67,8 @@ run_hooks(Dir, Type, Command, State) ->
_ ->
[]
end,
- Env = [{"REBAR_DEPS_DIR", filename:absname(rebar_dir:deps_dir(State))}],
+
+ Env = create_env(State),
lists:foreach(fun({_, C, _}=Hook) when C =:= Command ->
apply_hook(Dir, Env, Hook);
({C, _}=Hook) when C =:= Command ->
@@ -44,3 +87,33 @@ apply_hook(Dir, Env, {Arch, Command, Hook}) ->
apply_hook(Dir, Env, {Command, Hook}) ->
Msg = lists:flatten(io_lib:format("Hook for ~p failed!~n", [Command])),
rebar_utils:sh(Hook, [use_stdout, {cd, Dir}, {env, Env}, {abort_on_error, Msg}]).
+
+create_env(State) ->
+ BaseDir = rebar_state:dir(State),
+ [
+ {"REBAR_DEPS_DIR", filename:absname(rebar_dir:deps_dir(State))},
+ {"REBAR_BUILD_DIR", filename:absname(rebar_dir:base_dir(State))},
+ {"REBAR_ROOT_DIR", filename:absname(rebar_dir:root_dir(State))},
+ {"REBAR_CHECKOUTS_DIR", filename:absname(rebar_dir:checkouts_dir(State))},
+ {"REBAR_PLUGINS_DIR", filename:absname(rebar_dir:plugins_dir(State))},
+ {"REBAR_GLOBAL_CONFIG_DIR", filename:absname(rebar_dir:global_config_dir(State))},
+ {"REBAR_GLOBAL_CACHE_DIR", filename:absname(rebar_dir:global_cache_dir(State))},
+ {"REBAR_TEMPLATE_DIR", filename:absname(rebar_dir:template_dir(State))},
+ {"REBAR_APP_DIRS", join_dirs(BaseDir, rebar_dir:lib_dirs(State))},
+ {"REBAR_SRC_DIRS", join_dirs(BaseDir, rebar_dir:all_src_dirs(State))},
+ {"ERLANG_ERTS_VER", erlang:system_info(version)},
+ {"ERLANG_ROOT_DIR", code:root_dir()},
+ {"ERLANG_LIB_DIR_erl_interface", code:lib_dir(erl_interface)},
+ {"ERLANG_LIB_VER_erl_interface", re_version(code:lib_dir(erl_interface))},
+ {"ERL", filename:join([code:root_dir(), "bin", "erl"])},
+ {"ERLC", filename:join([code:root_dir(), "bin", "erlc"])}
+ ].
+
+join_dirs(BaseDir, Dirs) ->
+ string:join([ filename:join(BaseDir, Dir) || Dir <- Dirs ], ":").
+
+re_version(Path) ->
+ case re:run(Path, "^.*-(?<VER>[^/-]*)$", [{capture, [1], list}]) of
+ nomatch -> "";
+ {match, [Ver]} -> Ver
+ end.
diff --git a/src/rebar_otp_app.erl b/src/rebar_otp_app.erl
index e5ad1d2..9f61e71 100644
--- a/src/rebar_otp_app.erl
+++ b/src/rebar_otp_app.erl
@@ -51,10 +51,10 @@ compile(State, App) ->
%% Load the app file and validate it.
validate_app(State, App1).
-format_error(invalid_app_file) ->
- "Failed to read app file";
+format_error({missing_app_file, Filename}) ->
+ io_lib:format("App file is missing: ~s", [Filename]);
format_error({file_read, File, Reason}) ->
- io_lib:format("Failed to read ~s for processing: ~p", [File, Reason]);
+ io_lib:format("Failed to read app file ~s for processing: ~p", [File, file:format_error(Reason)]);
format_error({invalid_name, File, AppName}) ->
io_lib:format("Invalid ~s: name of application (~p) must match filename.", [File, AppName]).
@@ -160,9 +160,8 @@ ebin_modules(State, App, Dir) ->
[rebar_utils:beam_to_mod(N) || N <- Filtered].
extra_dirs(State) ->
- ErlOpts = rebar_utils:erl_opts(State),
- Extras = proplists:get_value(extra_src_dirs, ErlOpts, []),
- SrcDirs = proplists:get_value(src_dirs, ErlOpts, ["src"]),
+ Extras = rebar_dir:extra_src_dirs(State),
+ SrcDirs = rebar_dir:src_dirs(State, ["src"]),
%% remove any dirs that are defined in `src_dirs` from `extra_src_dirs`
Extras -- SrcDirs.
@@ -198,13 +197,13 @@ ensure_registered(AppData) ->
consult_app_file(Filename) ->
case filelib:is_file(Filename) of
false ->
- throw(?PRV_ERROR(invalid_app_file));
+ {error, enoent};
true ->
case lists:suffix(".app.src", Filename) of
false ->
file:consult(Filename);
true ->
- {ok, rebar_config:consult_file(Filename)}
+ {ok, rebar_config:consult_app_file(Filename)}
end
end.
@@ -215,7 +214,7 @@ app_vsn(AppFile, State) ->
Resources = rebar_state:resources(State),
rebar_utils:vcs_vsn(get_value(vsn, AppData, AppFile), AppDir, Resources);
{error, Reason} ->
- ?ABORT("Failed to consult app file ~s: ~p\n", [AppFile, Reason])
+ throw(?PRV_ERROR({file_read, AppFile, Reason}))
end.
get_value(Key, AppInfo, AppFile) ->
diff --git a/src/rebar_pkg_resource.erl b/src/rebar_pkg_resource.erl
index 59ce0dc..5b37788 100644
--- a/src/rebar_pkg_resource.erl
+++ b/src/rebar_pkg_resource.erl
@@ -95,7 +95,8 @@ make_vsn(_) ->
request(Url, ETag) ->
case httpc:request(get, {Url, [{"if-none-match", ETag} || ETag =/= false]},
[{relaxed, true}],
- [{body_format, binary}]) of
+ [{body_format, binary}],
+ hex) of
{ok, {{_Version, 200, _Reason}, Headers, Body}} ->
?DEBUG("Successfully downloaded ~s", [Url]),
{"etag", ETag1} = lists:keyfind("etag", 1, Headers),
diff --git a/src/rebar_plugins.erl b/src/rebar_plugins.erl
index c16223e..7e12324 100644
--- a/src/rebar_plugins.erl
+++ b/src/rebar_plugins.erl
@@ -3,7 +3,10 @@
-module(rebar_plugins).
--export([install/1, handle_plugins/2]).
+-export([project_apps_install/1
+ ,install/1
+ ,handle_plugins/3
+ ,handle_plugins/4]).
-include("rebar.hrl").
@@ -11,52 +14,90 @@
%% Public API
%% ===================================================================
+-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),
+
+ lists:foldl(fun(Profile, StateAcc) ->
+ Plugins = rebar_state:get(State, {plugins, Profile}, []),
+ StateAcc1 = handle_plugins(Profile, Plugins, StateAcc),
+
+ lists:foldl(fun(App, StateAcc2) ->
+ 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, StateAcc2)
+ end, StateAcc1, ProjectApps)
+ end, State, Profiles).
+
-spec install(rebar_state:t()) -> rebar_state:t().
install(State) ->
- Plugins = rebar_state:get(State, plugins, []),
+ 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).
- ProjectApps = rebar_state:project_apps(State),
+handle_plugins(Profile, Plugins, State) ->
+ handle_plugins(Profile, Plugins, State, false).
- 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(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),
+ State1 = rebar_state:set(State, deps_dir, ?DEFAULT_PLUGINS_DIR),
- handle_plugins(Plugins++OtherPlugins, 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, Upgrade),
+ NewState1 = rebar_state:create_logic_providers(NewPlugins, NewState),
+ {PluginAcc++NewPlugins, NewState1}
+ end, {[], State1}, Plugins),
--spec handle_plugins([rebar_prv_install_deps:dep()], rebar_state:t()) -> rebar_state:t().
-handle_plugins(Plugins, State) ->
- PluginProviders = lists:flatmap(fun(Plugin) ->
- handle_plugin(Plugin, State)
- end, Plugins),
- rebar_state:create_logic_providers(PluginProviders, State).
+ %% reset deps dir
+ State3 = rebar_state:set(State2, deps_dir, DepsDir),
+ rebar_state:lock(State3, Locks).
-handle_plugin(Plugin, State) ->
+handle_plugin(Profile, Plugin, State, Upgrade) ->
try
- %% Set deps dir to plugins dir so apps are installed there
- State1 = rebar_state:set(State, deps_dir, ?DEFAULT_PLUGINS_DIR),
- {ok, _, State2} = rebar_prv_install_deps:handle_deps(default, State1, [Plugin]),
-
- Apps = rebar_state:all_deps(State2),
- ToBuild = lists:dropwhile(fun rebar_app_info:valid/1, Apps),
- [build_plugin(AppInfo) || AppInfo <- ToBuild],
- [true = code:add_patha(filename:join(rebar_app_info:dir(AppInfo), "ebin")) || AppInfo <- Apps],
- plugin_providers(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, []),
+
+ %% 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, State2) || AppInfo <- ToBuild],
+
+ %% Add newly built deps and plugin to code path
+ State3 = rebar_state:update_all_plugin_deps(State2, Apps),
+ NewCodePaths = [rebar_app_info:ebin_dir(A) || A <- ToBuild],
+ code:add_pathsa(CodePaths),
+
+ %% Store plugin code paths so we can remove them when compiling project apps
+ State4 = rebar_state:update_code_paths(State3, all_plugin_deps, CodePaths++NewCodePaths),
+
+ {plugin_providers(Plugin), State4}
catch
C:T ->
- ?DEBUG("~p ~p", [C, T]),
+ ?DEBUG("~p ~p ~p", [C, T, erlang:get_stacktrace()]),
?WARN("Plugin ~p not available. It will not be used.", [Plugin]),
- []
+ {[], State}
end.
-build_plugin(AppInfo) ->
- AppDir = rebar_app_info:dir(AppInfo),
- C = rebar_config:consult(AppDir),
- S = rebar_state:new(rebar_state:new(), C, AppDir),
- rebar_prv_compile:compile(S, AppInfo).
+build_plugin(AppInfo, Apps, State) ->
+ Providers = rebar_state:providers(State),
+ Providers1 = rebar_state:providers(rebar_app_info:state(AppInfo)),
+ S = rebar_state:all_deps(rebar_app_info:state_or_new(State, AppInfo), Apps),
+ rebar_prv_compile:compile(S, Providers++Providers1, AppInfo).
+plugin_providers({Plugin, _, _, _}) when is_atom(Plugin) ->
+ validate_plugin(Plugin);
plugin_providers({Plugin, _, _}) when is_atom(Plugin) ->
validate_plugin(Plugin);
plugin_providers({Plugin, _}) when is_atom(Plugin) ->
diff --git a/src/rebar_prv_app_discovery.erl b/src/rebar_prv_app_discovery.erl
index 97862c1..ea55e11 100644
--- a/src/rebar_prv_app_discovery.erl
+++ b/src/rebar_prv_app_discovery.erl
@@ -23,7 +23,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, true},
+ {bare, false},
{deps, ?DEPS},
{example, ""},
{short_desc, ""},
@@ -38,7 +38,7 @@ do(State) ->
State1 = rebar_app_discover:do(State, LibDirs),
{ok, State1}
catch
- throw:{error, Error}->
+ throw:{error, Error} ->
?PRV_ERROR(Error)
end.
@@ -46,12 +46,17 @@ do(State) ->
format_error({multiple_app_files, Files}) ->
io_lib:format("Multiple app files found in one app dir: ~s", [string:join(Files, " and ")]);
format_error({invalid_app_file, File, Reason}) ->
- case Reason of
+ case Reason of
{Line, erl_parse, Description} ->
- io_lib:format("Invalid app file ~s at line ~b: ~p",
+ io_lib:format("Invalid app file ~s at line ~b: ~p",
[File, Line, lists:flatten(Description)]);
_ ->
io_lib:format("Invalid app file ~s: ~p", [File, Reason])
end;
+%% Provide a slightly more informative error message for consult of app file failure
+format_error({rebar_file_utils, {bad_term_file, AppFile, Reason}}) ->
+ io_lib:format("Error in app file ~s: ~s", [rebar_dir:make_relative_path(AppFile,
+ rebar_dir:get_cwd()),
+ file:format_error(Reason)]);
format_error(Reason) ->
io_lib:format("~p", [Reason]).
diff --git a/src/rebar_prv_as.erl b/src/rebar_prv_as.erl
index 64ad951..ead7b01 100644
--- a/src/rebar_prv_as.erl
+++ b/src/rebar_prv_as.erl
@@ -22,7 +22,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 as <profile1>,<profile2>,... <task1>, <task2>, ..."},
{short_desc, "Higher order provider for running multiple tasks in a sequence as a certain profiles."},
@@ -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_clean.erl b/src/rebar_prv_clean.erl
index 8fafe23..e3cb84e 100644
--- a/src/rebar_prv_clean.erl
+++ b/src/rebar_prv_clean.erl
@@ -22,7 +22,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 clean"},
{short_desc, "Remove compiled beam files from apps."},
@@ -67,11 +67,9 @@ format_error(Reason) ->
clean_apps(State, Providers, Apps) ->
lists:foreach(fun(AppInfo) ->
AppDir = rebar_app_info:dir(AppInfo),
- C = rebar_config:consult(AppDir),
- S = rebar_state:new(State, C, AppDir),
+ S = rebar_app_info:state_or_new(State, AppInfo),
?INFO("Cleaning out ~s...", [rebar_app_info:name(AppInfo)]),
- %% Legacy hook support
rebar_hooks:run_all_hooks(AppDir, pre, ?PROVIDER, Providers, S),
rebar_erlc_compiler:clean(State, rebar_app_info:out_dir(AppInfo)),
rebar_hooks:run_all_hooks(AppDir, post, ?PROVIDER, Providers, S)
diff --git a/src/rebar_prv_common_test.erl b/src/rebar_prv_common_test.erl
index eb51d8d..2b024cf 100644
--- a/src/rebar_prv_common_test.erl
+++ b/src/rebar_prv_common_test.erl
@@ -25,7 +25,7 @@ init(State) ->
Provider = providers:create([{name, ?PROVIDER},
{module, ?MODULE},
{deps, ?DEPS},
- {bare, false},
+ {bare, true},
{example, "rebar3 ct"},
{short_desc, "Run Common Tests."},
{desc, "Run Common Tests."},
@@ -38,7 +38,7 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
?INFO("Running Common Test suites...", []),
- code:add_pathsa(rebar_state:code_paths(State, all_deps)),
+ rebar_utils:update_code(rebar_state:code_paths(State, all_deps)),
%% Run ct provider prehooks
Providers = rebar_state:providers(State),
@@ -49,7 +49,7 @@ do(State) ->
{ok, State1} = Result ->
%% Run ct provider posthooks
rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State1),
- rebar_utils:cleanup_code_path(rebar_state:code_paths(State1, default)),
+ rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)),
Result;
?PRV_ERROR(_) = Error ->
rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)),
@@ -105,14 +105,18 @@ run_test_verbose(Opts) -> handle_results(ct:run_test(Opts)).
run_test_quiet(Opts) ->
Pid = self(),
+ Ref = erlang:make_ref(),
LogDir = proplists:get_value(logdir, Opts),
- erlang:spawn_monitor(fun() ->
+ {_, Monitor} = erlang:spawn_monitor(fun() ->
{ok, F} = file:open(filename:join([LogDir, "ct.latest.log"]),
[write]),
true = group_leader(F, self()),
- Pid ! ct:run_test(Opts)
+ Pid ! {Ref, ct:run_test(Opts)}
end),
- receive Result -> handle_quiet_results(Opts, Result) end.
+ receive
+ {Ref, Result} -> handle_quiet_results(Opts, Result);
+ {'DOWN', Monitor, _, _, Reason} -> handle_results(?PRV_ERROR(Reason))
+ end.
handle_results(Results) when is_list(Results) ->
Result = lists:foldl(fun sum_results/2, {0, 0, {0,0}}, Results),
@@ -132,8 +136,6 @@ sum_results({Passed, Failed, {UserSkipped, AutoSkipped}},
handle_quiet_results(_, {error, _} = Result) ->
handle_results(Result);
-handle_quiet_results(_, {'DOWN', _, _, _, Reason}) ->
- handle_results(?PRV_ERROR(Reason));
handle_quiet_results(CTOpts, Results) when is_list(Results) ->
_ = [format_result(Result) || Result <- Results],
case handle_results(Results) of
@@ -247,7 +249,7 @@ copy_and_compile_test_suites(State, Opts) ->
Dirs = find_suite_dirs(AllSuites),
lists:foreach(fun(S) ->
NewPath = copy(State, S),
- compile_dir(State, S, NewPath)
+ compile_dir(State, NewPath)
end, Dirs),
NewSuites = lists:map(fun(S) -> retarget_path(State, S) end, AllSuites),
[{suite, NewSuites}|lists:keydelete(suite, 1, Opts)]
@@ -259,12 +261,12 @@ copy_and_compile_test_dirs(State, Opts) ->
%% dir is a single directory
Dir when is_list(Dir), is_integer(hd(Dir)) ->
NewPath = copy(State, Dir),
- [{dir, compile_dir(State, Dir, NewPath)}|lists:keydelete(dir, 1, Opts)];
+ [{dir, compile_dir(State, NewPath)}|lists:keydelete(dir, 1, Opts)];
%% dir is a list of directories
Dirs when is_list(Dirs) ->
NewDirs = lists:map(fun(Dir) ->
NewPath = copy(State, Dir),
- compile_dir(State, Dir, NewPath)
+ compile_dir(State, NewPath)
end, Dirs),
[{dir, NewDirs}|lists:keydelete(dir, 1, Opts)]
end.
@@ -301,11 +303,11 @@ copy(State, Dir) ->
Target
end.
-compile_dir(State, Dir, OutDir) ->
- NewState = replace_src_dirs(State, [Dir]),
- ok = rebar_erlc_compiler:compile(NewState, rebar_state:dir(State), OutDir),
+compile_dir(State, Dir) ->
+ NewState = replace_src_dirs(State, [filename:absname(Dir)]),
+ ok = rebar_erlc_compiler:compile(NewState, rebar_dir:base_dir(State), Dir),
ok = maybe_cover_compile(State, Dir),
- OutDir.
+ Dir.
retarget_path(State, Path) ->
ProjectApps = rebar_state:project_apps(State),
@@ -345,31 +347,39 @@ reduce_path([_|Acc], [".."|Rest]) -> reduce_path(Acc, Rest);
reduce_path([], [".."|Rest]) -> reduce_path([], Rest);
reduce_path(Acc, [Component|Rest]) -> reduce_path([Component|Acc], Rest).
-remove_links(Path) ->
- case ec_file:is_dir(Path) of
- false -> ok;
- true -> remove_links1(Path)
- end.
-remove_links1(Path) ->
+remove_links(Path) ->
+ IsDir = ec_file:is_dir(Path),
case ec_file:is_symlink(Path) of
- true ->
- file:delete(Path);
- false ->
- lists:foreach(fun(ChildPath) ->
- remove_links(ChildPath)
- end, sub_dirs(Path))
+ true when IsDir ->
+ delete_dir_link(Path);
+ false when IsDir ->
+ lists:foreach(fun(ChildPath) ->
+ remove_links(ChildPath)
+ end, dir_entries(Path));
+ _ -> file:delete(Path)
end.
-sub_dirs(Path) ->
+delete_dir_link(Path) ->
+ case os:type() of
+ {unix, _} -> file:delete(Path);
+ {win32, _} -> file:del_dir(Path)
+ end.
+
+dir_entries(Path) ->
{ok, SubDirs} = file:list_dir(Path),
[filename:join(Path, SubDir) || SubDir <- SubDirs].
replace_src_dirs(State, Dirs) ->
%% replace any `src_dirs` with the test dirs
ErlOpts = rebar_state:get(State, erl_opts, []),
- StrippedOpts = lists:keydelete(src_dirs, 1, ErlOpts),
- rebar_state:set(State, erl_opts, [{src_dirs, Dirs}|StrippedOpts]).
+ StrippedErlOpts = filter_src_dirs(ErlOpts),
+ State1 = rebar_state:set(State, erl_opts, StrippedErlOpts),
+ State2 = rebar_state:set(State1, src_dirs, []),
+ rebar_state:set(State2, extra_src_dirs, Dirs).
+
+filter_src_dirs(ErlOpts) ->
+ lists:filter(fun({src_dirs, _}) -> false; ({extra_src_dirs, _}) -> false; (_) -> true end, ErlOpts).
test_dirs(State, Opts) ->
BareTest = filename:join([rebar_state:dir(State), "test"]),
diff --git a/src/rebar_prv_compile.erl b/src/rebar_prv_compile.erl
index f70ca28..6eb8a4f 100644
--- a/src/rebar_prv_compile.erl
+++ b/src/rebar_prv_compile.erl
@@ -6,8 +6,9 @@
do/1,
format_error/1]).
--export([compile/2]).
+-export([compile/3]).
+-include_lib("providers/include/providers.hrl").
-include("rebar.hrl").
-define(PROVIDER, compile).
@@ -21,7 +22,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 compile"},
{short_desc, "Compile apps .app.src and .erl files."},
@@ -32,17 +33,21 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
DepsPaths = rebar_state:code_paths(State, all_deps),
+ PluginDepsPaths = rebar_state:code_paths(State, all_plugin_deps),
+ rebar_utils:remove_from_code_path(PluginDepsPaths),
code:add_pathsa(DepsPaths),
ProjectApps = rebar_state:project_apps(State),
Providers = rebar_state:providers(State),
Deps = rebar_state:deps_to_build(State),
- Cwd = rebar_dir:get_cwd(),
+ Cwd = rebar_state:dir(State),
- %% Need to allow global config vars used on deps
- %% Right now no way to differeniate and just give deps a new state
+ %% Need to allow global config vars used on deps.
+ %% Right now no way to differeniate and just give deps a new state.
+ %% But need an account of "all deps" for some hooks to use.
EmptyState = rebar_state:new(),
- build_apps(EmptyState, Providers, Deps),
+ build_apps(rebar_state:all_deps(EmptyState,
+ rebar_state:all_deps(State)), Providers, Deps),
{ok, ProjectApps1} = rebar_digraph:compile_order(ProjectApps),
@@ -56,12 +61,15 @@ do(State) ->
State3 = rebar_state:code_paths(State2, all_deps, DepsPaths ++ ProjAppsPaths),
rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State2),
+ has_all_artifacts(State3),
rebar_utils:cleanup_code_path(rebar_state:code_paths(State3, default)),
{ok, State3}.
-spec format_error(any()) -> iolist().
+format_error({missing_artifact, File}) ->
+ io_lib:format("Missing artifact ~s", [File]);
format_error(Reason) ->
io_lib:format("~p", [Reason]).
@@ -71,29 +79,22 @@ build_apps(State, Providers, Apps) ->
build_app(State, Providers, AppInfo) ->
AppDir = rebar_app_info:dir(AppInfo),
OutDir = rebar_app_info:out_dir(AppInfo),
-
copy_app_dirs(State, AppDir, OutDir),
- S = case rebar_app_info:state(AppInfo) of
- undefined ->
- C = rebar_config:consult(AppDir),
- rebar_state:new(State, C, AppDir);
- AppState ->
- AppState
- end,
-
- %% Legacy hook support
- rebar_hooks:run_all_hooks(AppDir, pre, ?PROVIDER, Providers, S),
- AppInfo1 = compile(S, AppInfo),
- rebar_hooks:run_all_hooks(AppDir, post, ?PROVIDER, Providers, S),
+ S = rebar_app_info:state_or_new(State, AppInfo),
+ S1 = rebar_state:all_deps(S, rebar_state:all_deps(State)),
+ compile(S1, Providers, AppInfo).
- AppInfo1.
-
-compile(State, AppInfo) ->
+compile(State, Providers, AppInfo) ->
?INFO("Compiling ~s", [rebar_app_info:name(AppInfo)]),
+ AppDir = rebar_app_info:dir(AppInfo),
+ rebar_hooks:run_all_hooks(AppDir, pre, ?PROVIDER, Providers, State),
+
rebar_erlc_compiler:compile(State, ec_cnv:to_list(rebar_app_info:out_dir(AppInfo))),
case rebar_otp_app:compile(State, AppInfo) of
{ok, AppInfo1} ->
+ rebar_hooks:run_all_hooks(AppDir, post, ?PROVIDER, Providers, State),
+ has_all_artifacts(State),
AppInfo1;
Error ->
throw(Error)
@@ -103,6 +104,14 @@ compile(State, AppInfo) ->
%% Internal functions
%% ===================================================================
+has_all_artifacts(State) ->
+ case rebar_state:has_all_artifacts(State) of
+ {false, File} ->
+ throw(?PRV_ERROR({missing_artifact, File}));
+ true ->
+ true
+ end.
+
copy_app_dirs(State, OldAppDir, AppDir) ->
case ec_cnv:to_binary(filename:absname(OldAppDir)) =/=
ec_cnv:to_binary(filename:absname(AppDir)) of
@@ -119,9 +128,7 @@ copy_app_dirs(State, OldAppDir, AppDir) ->
end,
filelib:ensure_dir(filename:join(AppDir, "dummy")),
%% link to src_dirs to be adjacent to ebin is needed for R15 use of cover/xref
- ErlOpts = rebar_utils:erl_opts(State),
- SrcDirs = proplists:get_value(src_dirs, ErlOpts, ["src"]) ++
- proplists:get_value(extra_src_dirs, ErlOpts, []),
+ SrcDirs = rebar_dir:all_src_dirs(State, ["src"], ["test"]),
[symlink_or_copy(OldAppDir, AppDir, Dir) || Dir <- ["priv", "include"] ++ SrcDirs];
false ->
ok
diff --git a/src/rebar_prv_cover.erl b/src/rebar_prv_cover.erl
index 900c569..8c26521 100644
--- a/src/rebar_prv_cover.erl
+++ b/src/rebar_prv_cover.erl
@@ -25,7 +25,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 cover"},
{short_desc, "Perform coverage analysis."},
diff --git a/src/rebar_prv_dialyzer.erl b/src/rebar_prv_dialyzer.erl
index 96e2277..1cf7b71 100644
--- a/src/rebar_prv_dialyzer.erl
+++ b/src/rebar_prv_dialyzer.erl
@@ -14,6 +14,7 @@
-define(PROVIDER, dialyzer).
-define(DEPS, [compile]).
+-define(PLT_PREFIX, "rebar3").
%% ===================================================================
%% Public API
@@ -25,7 +26,7 @@ init(State) ->
{succ_typings, $s, "succ-typings", boolean, "Enable success typing analysis. Default: true"}],
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 dialyzer"},
{short_desc, short_desc()},
@@ -39,33 +40,42 @@ desc() ->
"This command will build, and keep up-to-date, a suitable PLT and will use "
"it to carry out success typing analysis on the current project.\n"
"\n"
- "The following (optional) configurations can be added to a rebar.config:\n"
- "`dialyzer_warnings` - a list of dialyzer warnings\n"
- "`dialyzer_plt` - the PLT file to use\n"
- "`dialyzer_plt_apps` - a list of applications to include in the PLT file*\n"
- "`dialyzer_plt_warnings` - display warnings when updating a PLT file "
- "(boolean)\n"
- "`dialyzer_base_plt` - the base PLT file to use**\n"
- "`dialyzer_base_plt_dir` - the base PLT directory**\n"
- "`dialyzer_base_plt_apps` - a list of applications to include in the base "
- "PLT file**\n"
+ "The following (optional) configurations can be added to a `proplist` of "
+ "options `dialyzer` in rebar.config:\n"
+ "`warnings` - a list of dialyzer warnings\n"
+ "`get_warnings` - display warnings when altering a PLT file (boolean)\n"
+ "`plt_extra_apps` - a list of applications to include in the PLT file*\n"
+ "`plt_location` - the location of the PLT file, `local` to store in the "
+ "profile's base directory (default) or a custom directory.\n"
+ "`plt_prefix` - the prefix to the PLT file, defaults to \"rebar3\"**\n"
+ "`base_plt_apps` - a list of applications to include in the base "
+ "PLT file***\n"
+ "`base_plt_location` - the location of base PLT file, `global` to store in "
+ "$HOME/.cache/rebar3 (default) or a custom directory***\n"
+ "`base_plt_prefix` - the prefix to the base PLT file, defaults to "
+ "\"rebar3\"** ***\n"
+ "\n"
+ "For example, to warn on unmatched returns: \n"
+ "{dialyzer, [{warnings, [unmatched_returns]}]}.\n"
"\n"
"*The applications in `dialyzer_base_plt_apps` and any `applications` and "
"`included_applications` listed in their .app files will be added to the "
"list.\n"
- "**The base PLT is a PLT containing the core OTP applications often "
- "required for a project's PLT. One base PLT is created per OTP version and "
- "stored in `dialyzer_base_plt_dir` (defaults to $HOME/.rebar3/). A base "
- "PLT is used to create a project's initial PLT.".
+ "**PLT files are named \"<prefix>_<otp_release>_plt\".\n"
+ "***The base PLT is a PLT containing the core applications often required "
+ "for a project's PLT. One base PLT is created per OTP version and "
+ "stored in `base_plt_location`. A base PLT is used to build project PLTs."
+ "\n".
short_desc() ->
"Run the Dialyzer analyzer on the project.".
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
+ maybe_fix_env(),
?INFO("Dialyzer starting, this may take a while...", []),
code:add_pathsa(rebar_state:code_paths(State, all_deps)),
- Plt = get_plt_location(State),
+ Plt = get_plt(State),
try
do(State, Plt)
@@ -74,17 +84,28 @@ do(State) ->
?PRV_ERROR({error_processing_apps, Error});
throw:{dialyzer_warnings, Warnings} ->
?PRV_ERROR({dialyzer_warnings, Warnings});
+ throw:{unknown_application, _} = Error ->
+ ?PRV_ERROR(Error);
throw:{output_file_error, _, _} = Error ->
?PRV_ERROR(Error)
after
rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default))
end.
+%% This is used to workaround dialyzer quirk discussed here
+%% https://github.com/rebar/rebar3/pull/489#issuecomment-107953541
+%% Dialyzer gets default plt location wrong way by peeking HOME environment
+%% variable which usually is not defined on Windows.
+maybe_fix_env() ->
+ os:putenv("DIALYZER_PLT", filename:join(rebar_dir:home_dir(), ".dialyzer_plt")).
+
-spec format_error(any()) -> iolist().
format_error({error_processing_apps, Error}) ->
io_lib:format("Error in dialyzing apps: ~s", [Error]);
format_error({dialyzer_warnings, Warnings}) ->
io_lib:format("Warnings occured running dialyzer: ~b", [Warnings]);
+format_error({unknown_application, App}) ->
+ io_lib:format("Could not find application: ~s", [App]);
format_error({output_file_error, File, Error}) ->
Error1 = file:format_error(Error),
io_lib:format("Failed to write to ~s: ~s", [File, Error1]);
@@ -93,13 +114,19 @@ format_error(Reason) ->
%% Internal functions
-get_plt_location(State) ->
- BaseDir = rebar_dir:base_dir(State),
- DefaultPlt = filename:join(BaseDir, default_plt()),
- rebar_state:get(State, dialyzer_plt, DefaultPlt).
+get_plt(State) ->
+ Prefix = get_config(State, plt_prefix, ?PLT_PREFIX),
+ Name = plt_name(Prefix),
+ case get_config(State, plt_location, local) of
+ local ->
+ BaseDir = rebar_dir:base_dir(State),
+ filename:join(BaseDir, Name);
+ Dir ->
+ filename:join(Dir, Name)
+ end.
-default_plt() ->
- rebar_utils:otp_release() ++ ".plt".
+plt_name(Prefix) ->
+ Prefix ++ "_" ++ rebar_utils:otp_release() ++ "_plt".
do(State, Plt) ->
Output = get_output_file(State),
@@ -138,21 +165,17 @@ update_proj_plt(State, Plt, Output) ->
do_update_proj_plt(State, Plt, Output) ->
?INFO("Updating plt...", []),
- {Files, Warnings} = proj_plt_files(State),
- Warnings2 = format_warnings(Output, Warnings),
- {Warnings3, State2} = case read_plt(State, Plt) of
- {ok, OldFiles} ->
- check_plt(State, Plt, Output, OldFiles,
- Files);
- {error, no_such_file} ->
- build_proj_plt(State, Plt, Output, Files)
- end,
- {Warnings2 + Warnings3, State2}.
+ Files = proj_plt_files(State),
+ case read_plt(State, Plt) of
+ {ok, OldFiles} ->
+ check_plt(State, Plt, Output, OldFiles, Files);
+ {error, no_such_file} ->
+ build_proj_plt(State, Plt, Output, Files)
+ end.
proj_plt_files(State) ->
- BasePltApps = rebar_state:get(State, dialyzer_base_plt_apps,
- default_plt_apps()),
- PltApps = rebar_state:get(State, dialyzer_plt_apps, []),
+ BasePltApps = get_config(State, base_plt_apps, default_plt_apps()),
+ PltApps = get_config(State, plt_extra_apps, []),
Apps = rebar_state:project_apps(State),
DepApps = lists:flatmap(fun rebar_app_info:applications/1, Apps),
get_plt_files(BasePltApps ++ PltApps ++ DepApps, Apps).
@@ -165,23 +188,18 @@ default_plt_apps() ->
get_plt_files(DepApps, Apps) ->
?INFO("Resolving files...", []),
- get_plt_files(DepApps, Apps, [], [], []).
+ get_plt_files(DepApps, Apps, [], []).
-get_plt_files([], _, _, Files, Warnings) ->
- {Files, Warnings};
-get_plt_files([AppName | DepApps], Apps, PltApps, Files, Warnings) ->
+get_plt_files([], _, _, Files) ->
+ Files;
+get_plt_files([AppName | DepApps], Apps, PltApps, Files) ->
case lists:member(AppName, PltApps) orelse app_member(AppName, Apps) of
true ->
- get_plt_files(DepApps, Apps, PltApps, Files, Warnings);
+ get_plt_files(DepApps, Apps, PltApps, Files);
false ->
- {DepApps2, Files2, Warnings2} = app_name_to_info(AppName),
- ?DEBUG("~s dependencies: ~p", [AppName, DepApps2]),
+ Files2 = app_files(AppName),
?DEBUG("~s files: ~p", [AppName, Files2]),
- DepApps3 = DepApps2 ++ DepApps,
- PltApps2 = [AppName | PltApps],
- Files3 = Files2 ++ Files,
- Warnings3 = Warnings2 ++ Warnings,
- get_plt_files(DepApps3, Apps, PltApps2, Files3, Warnings3)
+ get_plt_files(DepApps, Apps, [AppName | PltApps], Files2 ++ Files)
end.
app_member(AppName, Apps) ->
@@ -192,71 +210,28 @@ app_member(AppName, Apps) ->
false
end.
-app_name_to_info(AppName) ->
- case app_name_to_ebin(AppName) of
- {error, _} ->
- {[], [], [{unknown_application, {"", 0}, [AppName]}]};
- EbinDir ->
- ebin_to_info(EbinDir, AppName)
+app_files(AppName) ->
+ case app_ebin(AppName) of
+ {ok, EbinDir} ->
+ ebin_files(EbinDir);
+ {error, bad_name} ->
+ throw({unknown_application, AppName})
end.
-app_name_to_ebin(AppName) ->
+app_ebin(AppName) ->
case code:lib_dir(AppName, ebin) of
- {error, bad_name} ->
- search_ebin(AppName);
+ {error, bad_name} = Error ->
+ Error;
EbinDir ->
- check_ebin(EbinDir, AppName)
+ check_ebin(EbinDir)
end.
-check_ebin(EbinDir, AppName) ->
+check_ebin(EbinDir) ->
case filelib:is_dir(EbinDir) of
true ->
- EbinDir;
- false ->
- search_ebin(AppName)
- end.
-
-search_ebin(AppName) ->
- case code:where_is_file(atom_to_list(AppName) ++ ".app") of
- non_existing ->
- {error, bad_name};
- AppFile ->
- filename:dirname(AppFile)
- end.
-
-ebin_to_info(EbinDir, AppName) ->
- AppFile = filename:join(EbinDir, atom_to_list(AppName) ++ ".app"),
- ?DEBUG("Consulting app file ~p", [AppFile]),
- case file:consult(AppFile) of
- {ok, [{application, AppName, AppDetails}]} ->
- DepApps = proplists:get_value(applications, AppDetails, []),
- IncApps = proplists:get_value(included_applications, AppDetails,
- []),
- Modules = proplists:get_value(modules, AppDetails, []),
- {Files, Warnings} = modules_to_files(Modules, EbinDir),
- {IncApps ++ DepApps, Files, Warnings};
- {error, enoent} when AppName =:= erts ->
- {[], ebin_files(EbinDir), []};
- _ ->
- Error = io_lib:format("Could not parse ~p", [AppFile]),
- throw({dialyzer_error, Error})
- end.
-
-modules_to_files(Modules, EbinDir) ->
- Ext = code:objfile_extension(),
- Result = [module_to_file(Module, EbinDir, Ext) || Module <- Modules],
- Files = [File || {_, File} <- Result, File =/= unknown],
- Warnings = [{unknown_module, {"", 0}, [Module]} ||
- {Module, unknown} <- Result],
- {Files, Warnings}.
-
-module_to_file(Module, EbinDir, Ext) ->
- File = filename:join(EbinDir, atom_to_list(Module) ++ Ext),
- case filelib:is_file(File) of
- true ->
- {Module, File};
+ {ok, EbinDir};
false ->
- {Module, unknown}
+ {error, bad_name}
end.
ebin_files(EbinDir) ->
@@ -306,43 +281,46 @@ add_plt(State, Plt, Output, Files) ->
run_plt(State, Plt, Output, plt_add, Files).
run_plt(State, Plt, Output, Analysis, Files) ->
- GetWarnings = rebar_state:get(State, dialyzer_plt_warnings, false),
+ GetWarnings = get_config(State, get_warnings, false),
Opts = [{analysis_type, Analysis},
{get_warnings, GetWarnings},
{init_plt, Plt},
+ {output_plt, Plt},
{from, byte_code},
{files, Files}],
run_dialyzer(State, Opts, Output).
build_proj_plt(State, Plt, Output, Files) ->
- BasePlt = get_base_plt_location(State),
+ BasePlt = get_base_plt(State),
?INFO("Updating base plt...", []),
- {BaseFiles, BaseWarnings} = base_plt_files(State),
- BaseWarnings2 = format_warnings(Output, BaseWarnings),
- {BaseWarnings3, State1} = update_base_plt(State, BasePlt, Output,
- BaseFiles),
+ BaseFiles = base_plt_files(State),
+ {BaseWarnings, State1} = update_base_plt(State, BasePlt, Output, BaseFiles),
?INFO("Copying ~p to ~p...", [BasePlt, Plt]),
_ = filelib:ensure_dir(Plt),
case file:copy(BasePlt, Plt) of
{ok, _} ->
{CheckWarnings, State2} = check_plt(State1, Plt, Output, BaseFiles,
Files),
- {BaseWarnings2 + BaseWarnings3 + CheckWarnings, State2};
+ {BaseWarnings + CheckWarnings, State2};
{error, Reason} ->
Error = io_lib:format("Could not copy PLT from ~p to ~p: ~p",
[BasePlt, Plt, file:format_error(Reason)]),
throw({dialyzer_error, Error})
end.
-get_base_plt_location(State) ->
- GlobalCacheDir = rebar_dir:global_cache_dir(State),
- BaseDir = rebar_state:get(State, dialyzer_base_plt_dir, GlobalCacheDir),
- BasePlt = rebar_state:get(State, dialyzer_base_plt, default_plt()),
- filename:join(BaseDir, BasePlt).
+get_base_plt(State) ->
+ Prefix = get_config(State, base_plt_prefix, ?PLT_PREFIX),
+ Name = plt_name(Prefix),
+ case get_config(State, base_plt_location, global) of
+ global ->
+ GlobalCacheDir = rebar_dir:global_cache_dir(State),
+ filename:join(GlobalCacheDir, Name);
+ Dir ->
+ filename:join(Dir, Name)
+ end.
base_plt_files(State) ->
- BasePltApps = rebar_state:get(State, dialyzer_base_plt_apps,
- default_plt_apps()),
+ BasePltApps = get_config(State, base_plt_apps, default_plt_apps()),
Apps = rebar_state:project_apps(State),
get_plt_files(BasePltApps, Apps).
@@ -357,7 +335,7 @@ update_base_plt(State, BasePlt, Output, BaseFiles) ->
build_plt(State, Plt, Output, Files) ->
?INFO("Adding ~b files to ~p...", [length(Files), Plt]),
- GetWarnings = rebar_state:get(State, dialyzer_plt_warnings, false),
+ GetWarnings = get_config(State, get_warnings, false),
Opts = [{analysis_type, plt_build},
{get_warnings, GetWarnings},
{output_plt, Plt},
@@ -376,36 +354,29 @@ succ_typings(State, Plt, Output) ->
succ_typings(State, Plt, Output, Apps) ->
?INFO("Doing success typing analysis...", []),
- {Files, Warnings} = apps_to_files(Apps),
- Warnings2 = format_warnings(Output, Warnings),
+ Files = apps_to_files(Apps),
?INFO("Analyzing ~b files with ~p...", [length(Files), Plt]),
Opts = [{analysis_type, succ_typings},
{get_warnings, true},
{from, byte_code},
{files, Files},
{init_plt, Plt}],
- {Warnings3, State2} = run_dialyzer(State, Opts, Output),
- {Warnings2 + Warnings3, State2}.
+ run_dialyzer(State, Opts, Output).
apps_to_files(Apps) ->
?INFO("Resolving files...", []),
- Result = [{Files, Warnings} ||
- App <- Apps,
- {Files, Warnings} <- [app_to_files(App)]],
- Files = [File || {Files, _} <- Result, File <- Files],
- Warnings = [Warning || {_, Warnings} <- Result, Warning <- Warnings],
- {Files, Warnings}.
+ [File || App <- Apps,
+ File <- app_to_files(App)].
app_to_files(App) ->
AppName = ec_cnv:to_atom(rebar_app_info:name(App)),
- {_, Files, Warnings} = app_name_to_info(AppName),
- {Files, Warnings}.
+ app_files(AppName).
run_dialyzer(State, Opts, Output) ->
%% dialyzer may return callgraph warnings when get_warnings is false
case proplists:get_bool(get_warnings, Opts) of
true ->
- WarningsList = rebar_state:get(State, dialyzer_warnings, []),
+ WarningsList = get_config(State, warnings, []),
Opts2 = [{warnings, WarningsList},
{check_plt, false} |
Opts],
@@ -417,7 +388,7 @@ run_dialyzer(State, Opts, Output) ->
{check_plt, false} |
Opts],
?DEBUG("Running dialyzer with options: ~p~n", [Opts2]),
- _ = dialyzer:run(Opts2),
+ dialyzer:run(Opts2),
{0, State}
end.
@@ -430,10 +401,6 @@ format_warnings(Output, Warnings) ->
format_warnings(Warnings) ->
[format_warning(Warning) || Warning <- Warnings].
-format_warning({unknown_application, _, [AppName]}) ->
- io_lib:format("Unknown application: ~s", [AppName]);
-format_warning({unknown_module, _, [Module]}) ->
- io_lib:format("Unknown module: ~s", [Module]);
format_warning(Warning) ->
case strip(dialyzer:format_warning(Warning, fullpath)) of
":0: " ++ Unknown ->
@@ -471,3 +438,7 @@ no_warnings() ->
no_contracts,
no_behaviours,
no_undefined_callbacks].
+
+get_config(State, Key, Default) ->
+ Config = rebar_state:get(State, dialyzer, []),
+ proplists:get_value(Key, Config, Default).
diff --git a/src/rebar_prv_do.erl b/src/rebar_prv_do.erl
index aee3a27..f850135 100644
--- a/src/rebar_prv_do.erl
+++ b/src/rebar_prv_do.erl
@@ -23,7 +23,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 do <task1>, <task2>, ..."},
{short_desc, "Higher order provider for running multiple tasks in a sequence."},
@@ -33,8 +33,16 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
- Tasks = rebar_utils:args_to_tasks(rebar_state:command_args(State)),
- do_tasks(Tasks, State).
+ case rebar_utils:args_to_tasks(rebar_state:command_args(State)) of
+ [] ->
+ AllProviders = rebar_state:providers(State),
+ Namespace = rebar_state:namespace(State),
+ Providers = providers:get_providers_by_namespace(Namespace, AllProviders),
+ providers:help(Providers),
+ {ok, State};
+ Tasks ->
+ do_tasks(Tasks, State)
+ end.
do_tasks([], State) ->
{ok, State};
@@ -47,6 +55,8 @@ do_tasks([{TaskStr, Args}|Tail], State) ->
default ->
%% The first task we hit might be a namespace!
case maybe_namespace(State2, Task, Args) of
+ {ok, FinalState} when Tail =:= [] ->
+ {ok, FinalState};
{ok, _} ->
do_tasks(Tail, State);
{error, Reason} ->
@@ -56,6 +66,8 @@ do_tasks([{TaskStr, Args}|Tail], State) ->
%% We're already in a non-default namespace, check the
%% task directly.
case rebar_core:process_command(State2, Task) of
+ {ok, FinalState} when Tail =:= [] ->
+ {ok, FinalState};
{ok, _} ->
do_tasks(Tail, State);
{error, Reason} ->
diff --git a/src/rebar_prv_edoc.erl b/src/rebar_prv_edoc.erl
index efcfeb5..14df269 100644
--- a/src/rebar_prv_edoc.erl
+++ b/src/rebar_prv_edoc.erl
@@ -19,7 +19,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 edoc"},
{short_desc, "Generate documentation using edoc."},
diff --git a/src/rebar_prv_escriptize.erl b/src/rebar_prv_escriptize.erl
index 03332a0..3cdc9bf 100644
--- a/src/rebar_prv_escriptize.erl
+++ b/src/rebar_prv_escriptize.erl
@@ -47,7 +47,7 @@ init(State) ->
Provider = providers:create([
{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 escriptize"},
{opts, []},
@@ -72,7 +72,7 @@ do(State) ->
end;
Name ->
AllApps = rebar_state:all_deps(State)++rebar_state:project_apps(State),
- AppInfo = rebar_app_utils:find(Name, AllApps),
+ {ok, AppInfo} = rebar_app_utils:find(ec_cnv:to_binary(Name), AllApps),
escriptize(State, AppInfo)
end.
@@ -83,14 +83,15 @@ escriptize(State0, App) ->
%% Get the output filename for the escript -- this may include dirs
Filename = filename:join([rebar_dir:base_dir(State0), "bin",
rebar_state:get(State0, escript_name, AppName)]),
+ ?DEBUG("Creating escript file ~s", [Filename]),
ok = filelib:ensure_dir(Filename),
State = rebar_state:escript_path(State0, Filename),
%% Look for a list of other applications (dependencies) to include
%% in the output file. We then use the .app files for each of these
%% to pull in all the .beam files.
- InclApps = lists:usort(rebar_state:get(State, escript_incl_apps, [])
- ++ all_deps(State)),
+ InclApps = lists:usort([ec_cnv:to_atom(AppName) | rebar_state:get(State, escript_incl_apps, [])
+ ++ all_deps(State)]),
AllApps = rebar_state:all_deps(State)++rebar_state:project_apps(State),
InclBeams = get_apps_beams(InclApps, AllApps),
@@ -134,7 +135,7 @@ format_error({bad_name, App}) ->
io_lib:format("Failed to get ebin/ directory for "
"escript_incl_app: ~p", [App]);
format_error(no_main_app) ->
- io_lib:format("Multiple project apps and {rebar_escript_plugin, [{main_app, atom()}]}."
+ io_lib:format("Multiple project apps and {escript_main_app, atom()}."
" not set in rebar.config", []).
%% ===================================================================
diff --git a/src/rebar_prv_eunit.erl b/src/rebar_prv_eunit.erl
index 8eaa926..28c0ed6 100644
--- a/src/rebar_prv_eunit.erl
+++ b/src/rebar_prv_eunit.erl
@@ -24,7 +24,7 @@ init(State) ->
Provider = providers:create([{name, ?PROVIDER},
{module, ?MODULE},
{deps, ?DEPS},
- {bare, false},
+ {bare, true},
{example, "rebar3 eunit"},
{short_desc, "Run EUnit Tests."},
{desc, "Run EUnit Tests."},
@@ -37,7 +37,8 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
?INFO("Performing EUnit tests...", []),
- code:add_pathsa(rebar_state:code_paths(State, all_deps)),
+ rebar_utils:update_code(rebar_state:code_paths(State, all_deps)),
+
%% Run eunit provider prehooks
Providers = rebar_state:providers(State),
Cwd = rebar_dir:get_cwd(),
@@ -49,7 +50,7 @@ do(State) ->
{ok, State1} ->
%% Run eunit provider posthooks
rebar_hooks:run_all_hooks(Cwd, post, ?PROVIDER, Providers, State1),
- rebar_utils:cleanup_code_path(rebar_state:code_paths(State1, default)),
+ rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)),
{ok, State1};
Error ->
rebar_utils:cleanup_code_path(rebar_state:code_paths(State, default)),
@@ -84,8 +85,7 @@ format_error({error_running_tests, Reason}) ->
test_state(State) ->
ErlOpts = rebar_state:get(State, eunit_compile_opts, []),
TestOpts = safe_define_test_macro(ErlOpts),
- TestDir = [{extra_src_dirs, ["test"]}],
- first_files(State) ++ [{erl_opts, TestOpts ++ TestDir}].
+ first_files(State) ++ [{erl_opts, TestOpts}].
safe_define_test_macro(Opts) ->
%% defining a compile macro twice results in an exception so
@@ -106,38 +106,49 @@ first_files(State) ->
prepare_tests(State) ->
{RawOpts, _} = rebar_state:command_parsed_args(State),
- ok = maybe_cover_compile(State, RawOpts),
- ProjectApps = project_apps(State),
- resolve_apps(ProjectApps, RawOpts).
-
-maybe_cover_compile(State, Opts) ->
- State1 = case proplists:get_value(cover, Opts, false) of
- true -> rebar_state:set(State, cover_enabled, true);
- false -> State
- end,
- rebar_prv_cover:maybe_cover_compile(State1).
+ resolve_apps(State, RawOpts).
-resolve_apps(ProjectApps, RawOpts) ->
+resolve_apps(State, RawOpts) ->
case proplists:get_value(app, RawOpts) of
- undefined -> resolve_suites(ProjectApps, RawOpts);
+ undefined -> resolve_suites(State, RawOpts);
%% convert app name strings to `rebar_app_info` objects
Apps -> AppNames = string:tokens(Apps, [$,]),
+ ProjectApps = project_apps(State),
case filter_apps_by_name(AppNames, ProjectApps) of
- {ok, TestApps} -> resolve_suites(TestApps, RawOpts);
+ {ok, TestApps} -> resolve_suites(State, TestApps, RawOpts);
Error -> Error
end
end.
-resolve_suites(Apps, RawOpts) ->
+resolve_suites(State, RawOpts) -> resolve_suites(State, project_apps(State), RawOpts).
+
+resolve_suites(State, Apps, RawOpts) ->
case proplists:get_value(suite, RawOpts) of
- undefined -> test_set(Apps, all);
+ undefined -> compile_tests(State, Apps, all, RawOpts);
Suites -> SuiteNames = string:tokens(Suites, [$,]),
case filter_suites_by_apps(SuiteNames, Apps) of
- {ok, S} -> test_set(Apps, S);
+ {ok, S} -> compile_tests(State, Apps, S, RawOpts);
Error -> Error
end
end.
+compile_tests(State, TestApps, Suites, RawOpts) ->
+ F = fun(AppInfo) ->
+ S = rebar_app_info:state_or_new(State, AppInfo),
+ ok = rebar_erlc_compiler:compile(replace_src_dirs(S),
+ ec_cnv:to_list(rebar_app_info:out_dir(AppInfo)))
+ end,
+ lists:foreach(F, TestApps),
+ ok = maybe_cover_compile(State, RawOpts),
+ {ok, test_set(TestApps, Suites)}.
+
+maybe_cover_compile(State, Opts) ->
+ State1 = case proplists:get_value(cover, Opts, false) of
+ true -> rebar_state:set(State, cover_enabled, true);
+ false -> State
+ end,
+ rebar_prv_cover:maybe_cover_compile(State1).
+
project_apps(State) ->
filter_checkouts(rebar_state:project_apps(State)).
@@ -204,8 +215,20 @@ app_modules([App|Rest], Acc) ->
app_modules(Rest, NewAcc)
end.
-test_set(Apps, all) -> {ok, set_apps(Apps, [])};
-test_set(_Apps, Suites) -> {ok, set_suites(Suites, [])}.
+replace_src_dirs(State) ->
+ %% replace any `src_dirs` with the test dirs
+ ErlOpts = rebar_state:get(State, erl_opts, []),
+ StrippedOpts = filter_src_dirs(ErlOpts),
+ case rebar_dir:extra_src_dirs(State) of
+ [] -> rebar_state:set(State, erl_opts, [{src_dirs, ["test"]}|StrippedOpts]);
+ _ -> rebar_state:set(State, erl_opts, StrippedOpts)
+ end.
+
+filter_src_dirs(ErlOpts) ->
+ lists:filter(fun({src_dirs, _}) -> false; (_) -> true end, ErlOpts).
+
+test_set(Apps, all) -> set_apps(Apps, []);
+test_set(_Apps, Suites) -> set_suites(Suites, []).
set_apps([], Acc) -> lists:reverse(Acc);
set_apps([App|Rest], Acc) ->
diff --git a/src/rebar_prv_help.erl b/src/rebar_prv_help.erl
index be5717f..c028264 100644
--- a/src/rebar_prv_help.erl
+++ b/src/rebar_prv_help.erl
@@ -22,7 +22,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 help <task>"},
{short_desc, "Display a list of tasks or help for a given task or subtask."},
@@ -65,7 +65,13 @@ task_help(Namespace, Name, State) ->
Providers = rebar_state:providers(State),
case providers:get_provider(Name, Providers, Namespace) of
not_found ->
- {error, io_lib:format("Unknown task ~p", [Name])};
+ case providers:get_providers_by_namespace(Name, Providers) of
+ [] ->
+ {error, io_lib:format("Unknown task ~p", [Name])};
+ NSProviders ->
+ providers:help(NSProviders),
+ {ok, State}
+ end;
Provider ->
providers:help(Provider),
{ok, State}
diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl
index ba49532..768d41a 100644
--- a/src/rebar_prv_install_deps.erl
+++ b/src/rebar_prv_install_deps.erl
@@ -37,7 +37,10 @@
-export([handle_deps/3,
handle_deps/4,
- handle_deps/5]).
+ handle_deps/5,
+
+ find_cycles/1,
+ cull_compile/2]).
-export_type([dep/0]).
@@ -58,7 +61,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, true},
+ {bare, false},
{deps, ?DEPS},
{example, undefined},
{short_desc, ""},
@@ -76,6 +79,10 @@ do(State) ->
{Apps, State1} =
lists:foldl(fun deps_per_profile/2, {[], State}, lists:reverse(Profiles)),
+ State2 = rebar_state:update_all_deps(State1, Apps),
+ CodePaths = [rebar_app_info:ebin_dir(A) || A <- Apps],
+ State3 = rebar_state:update_code_paths(State2, all_deps, CodePaths),
+
Source = ProjectApps ++ Apps,
case find_cycles(Source) of
{cycles, Cycles} ->
@@ -84,7 +91,7 @@ do(State) ->
{error, Error};
{no_cycle, Sorted} ->
ToCompile = cull_compile(Sorted, ProjectApps),
- {ok, rebar_state:deps_to_build(State1, ToCompile)}
+ {ok, rebar_state:deps_to_build(State3, ToCompile)}
end
catch
%% maybe_fetch will maybe_throw an exception to break out of some loops
@@ -158,11 +165,7 @@ handle_deps(Profile, State0, Deps, Upgrade, Locks) ->
,lists:ukeysort(2, SrcApps)
,lists:ukeysort(2, Solved)),
- State5 = rebar_state:update_all_deps(State4, AllDeps),
- CodePaths = [rebar_app_info:ebin_dir(A) || A <- AllDeps],
- State6 = rebar_state:update_code_paths(State5, all_deps, CodePaths),
-
- {ok, AllDeps, State6}.
+ {ok, AllDeps, State4}.
%% ===================================================================
%% Internal functions
@@ -202,31 +205,40 @@ update_pkg_deps(Profile, Packages, PkgDeps, Graph, Upgrade, Seen, State, Locks)
update_pkg_deps(Profile, S, Packages, Upgrade, Seen, State, Locks)
end.
+pkg_locked({Name, _, _}, Locks) ->
+ pkg_locked(Name, Locks);
pkg_locked({Name, _}, Locks) ->
+ pkg_locked(Name, Locks);
+pkg_locked(Name, Locks) ->
false =/= lists:keyfind(Name, 1, Locks).
-update_pkg_deps(Profile, Pkgs, Packages, Upgrade, Seen, State, _Locks) ->
+update_pkg_deps(Profile, Pkgs, Packages, Upgrade, Seen, State, Locks) ->
%% Create app_info record for each pkg dep
DepsDir = profile_dep_dir(State, Profile),
{Solved, _, State1}
= lists:foldl(fun(Pkg, {Acc, SeenAcc, StateAcc}) ->
- handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Acc, SeenAcc, StateAcc)
+ handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Acc, SeenAcc, Locks, StateAcc)
end, {[], Seen, State}, Pkgs),
{Solved, State1}.
-handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Fetched, Seen, State) ->
- AppInfo = package_to_app(DepsDir, Packages, Pkg, State),
+handle_pkg_dep(Profile, Pkg, Packages, Upgrade, DepsDir, Fetched, Seen, Locks, State) ->
+ IsLock = pkg_locked(Pkg, Locks),
+ AppInfo = package_to_app(DepsDir, Packages, Pkg, IsLock, State),
+ Deps = rebar_app_info:deps(AppInfo),
Level = rebar_app_info:dep_level(AppInfo),
{NewSeen, NewState} = maybe_lock(Profile, AppInfo, Seen, State, Level),
{_, AppInfo1} = maybe_fetch(AppInfo, Profile, Upgrade, Seen, NewState),
- {[AppInfo1 | Fetched], NewSeen, NewState}.
+ {AppInfo2, _, _, _, _} =
+ handle_dep(NewState, Profile, DepsDir, AppInfo1, Locks, Level),
+ AppInfo3 = rebar_app_info:deps(AppInfo2, Deps),
+ {[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),
@@ -241,13 +253,13 @@ 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}, State) ->
+package_to_app(DepsDir, Packages, {Name, Vsn, Level}, IsLock, State) ->
case dict:find({Name, Vsn}, Packages) of
error ->
case rebar_packages:check_registry(Name, Vsn, State) of
@@ -256,14 +268,13 @@ package_to_app(DepsDir, Packages, {Name, Vsn, Level}, State) ->
false ->
throw(?PRV_ERROR({missing_package, Name, Vsn}))
end;
- {ok, P} ->
- PkgDeps = [{PkgName, PkgVsn}
- || {PkgName,PkgVsn} <- proplists:get_value(<<"deps">>, P, [])],
- {ok, AppInfo} = rebar_app_info:new(Name, Vsn),
- AppInfo1 = rebar_app_info:deps(AppInfo, PkgDeps),
- AppInfo2 = rebar_app_info:dir(AppInfo1, filename:join([DepsDir, Name])),
- AppInfo3 = rebar_app_info:dep_level(AppInfo2, Level),
- rebar_app_info:source(AppInfo3, {pkg, Name, Vsn})
+ {ok, PkgDeps} ->
+ Source = {pkg, Name, Vsn},
+ AppInfo = new_dep(DepsDir, Name, Vsn, Source, IsLock, State),
+ AppInfo1 = rebar_app_info:dep_level(rebar_app_info:deps(AppInfo, PkgDeps), Level),
+ BaseDir = rebar_state:get(State, base_dir, []),
+ AppState1 = rebar_state:set(rebar_app_info:state(AppInfo1), base_dir, BaseDir),
+ rebar_app_info:state(AppInfo1, AppState1)
end.
-spec update_src_deps(atom(), non_neg_integer(), list(), list(), list(), rebar_state:t(), boolean(), sets:set(binary()), list()) -> {rebar_state:t(), list(), list(), sets:set(binary())}.
@@ -345,7 +356,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;
@@ -356,7 +366,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
@@ -364,9 +374,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),
@@ -374,22 +384,26 @@ 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),
+
+ S3 = rebar_state:apply_profiles(S2, Profiles),
+ Plugins = rebar_state:get(S3, plugins, []),
+ S4 = rebar_state:set(S3, {plugins, Profile}, Plugins),
+ AppInfo1 = rebar_app_info:state(AppInfo, S4),
%% Dep may have plugins to install. Find and install here.
- State1 = rebar_plugins:handle_plugins(rebar_state:get(S3, plugins, []), State),
+ S5 = rebar_plugins:install(S4),
+ AppInfo2 = rebar_app_info:state(AppInfo1, S5),
- Deps = rebar_state:get(S3, deps, []),
%% Upgrade lock level to be the level the dep will have in this dep tree
+ Deps = rebar_state:get(S5, deps, []),
NewLocks = [{DepName, Source, LockLevel+Level} ||
- {DepName, Source, LockLevel} <- rebar_state:get(S3, {locks, default}, [])],
- AppInfo2 = rebar_app_info:deps(AppInfo1, rebar_state:deps_names(Deps)),
- {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps, S3, Locks, Level),
- {AppInfo2, SrcDeps, PkgDeps, Locks++NewLocks, State1}.
+ {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, S5, Locks, Level+1),
+ {AppInfo3, SrcDeps, PkgDeps, Locks++NewLocks, State}.
--spec maybe_fetch(rebar_app_info:t(), atom(), boolean() | {true, binary(), integer()},
+-spec maybe_fetch(rebar_app_info:t(), atom(), boolean(),
sets:set(binary()), rebar_state:t()) -> {boolean(), rebar_app_info:t()}.
maybe_fetch(AppInfo, Profile, Upgrade, Seen, State) ->
AppDir = ec_cnv:to_list(rebar_app_info:dir(AppInfo)),
@@ -405,7 +419,7 @@ maybe_fetch(AppInfo, Profile, Upgrade, Seen, State) ->
case fetch_app(AppInfo, AppDir, State) of
true ->
maybe_symlink_default(State, Profile, AppDir, AppInfo),
- {true, update_app_info(AppInfo)};
+ {true, update_app_info(AppDir, AppInfo)};
Other ->
{Other, AppInfo}
end;
@@ -432,7 +446,7 @@ maybe_fetch(AppInfo, Profile, Upgrade, Seen, State) ->
already_in_default(AppInfo, State) ->
Name = ec_cnv:to_list(rebar_app_info:name(AppInfo)),
- DefaultAppDir = filename:join([rebar_state:get(State, base_dir), "default", "lib", Name]),
+ DefaultAppDir = filename:join([rebar_state:get(State, base_dir, []), "default", "lib", Name]),
rebar_app_discover:find_app(DefaultAppDir, all).
needs_symlinking(State, Profile) ->
@@ -477,69 +491,69 @@ parse_deps(DepsDir, Deps, State, Locks, Level) ->
end,
case lists:keyfind(ec_cnv:to_binary(Name), 1, Locks) of
false ->
- parse_dep(Dep, Acc, DepsDir, State);
+ parse_dep(Dep, Acc, DepsDir, false, State);
LockedDep ->
LockedLevel = element(3, LockedDep),
case LockedLevel > Level of
true ->
- parse_dep(Dep, Acc, DepsDir, State);
+ parse_dep(Dep, Acc, DepsDir, false, State);
false ->
- parse_dep(LockedDep, Acc, DepsDir, State)
+ parse_dep(LockedDep, Acc, DepsDir, true, State)
end
end
end, {[], []}, Deps).
-parse_dep({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_list(Vsn) ->
+parse_dep({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_list(Vsn) ->
CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)),
case rebar_app_info:discover(CheckoutsDir) of
{ok, _App} ->
- Dep = new_dep(DepsDir, Name, [], [], State),
+ Dep = new_dep(DepsDir, Name, [], [], IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
not_found ->
{SrcDepsAcc, [parse_goal(ec_cnv:to_binary(Name)
,ec_cnv:to_binary(Vsn)) | PkgDepsAcc]}
end;
-parse_dep(Name, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_atom(Name) ->
+parse_dep(Name, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_atom(Name) ->
{PkgName, PkgVsn} = get_package(ec_cnv:to_binary(Name), State),
CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)),
case rebar_app_info:discover(CheckoutsDir) of
{ok, _App} ->
- Dep = new_dep(DepsDir, Name, [], [], State),
+ Dep = new_dep(DepsDir, Name, [], [], IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
not_found ->
{SrcDepsAcc, [{PkgName, PkgVsn} | PkgDepsAcc]}
end;
-parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) ->
- Dep = new_dep(DepsDir, Name, [], Source, State),
+parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) ->
+ Dep = new_dep(DepsDir, Name, [], Source, IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
-parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) ->
- Dep = new_dep(DepsDir, Name, [], Source, State),
+parse_dep({Name, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) ->
+ Dep = new_dep(DepsDir, Name, [], Source, IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
-parse_dep({Name, _Vsn, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) ->
- Dep = new_dep(DepsDir, Name, [], Source, State),
+parse_dep({Name, _Vsn, Source}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) ->
+ Dep = new_dep(DepsDir, Name, [], Source, IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
-parse_dep({Name, _Vsn, Source, Opts}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source) ->
+parse_dep({Name, _Vsn, Source, Opts}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source) ->
?WARN("Dependency option list ~p in ~p is not supported and will be ignored", [Opts, Name]),
- Dep = new_dep(DepsDir, Name, [], Source, State),
+ Dep = new_dep(DepsDir, Name, [], Source, IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
-parse_dep({_Name, {pkg, Name, Vsn}, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_integer(Level) ->
+parse_dep({_Name, {pkg, Name, Vsn}, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_integer(Level) ->
CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)),
case rebar_app_info:discover(CheckoutsDir) of
{ok, _App} ->
- Dep = new_dep(DepsDir, Name, [], [], State),
+ Dep = new_dep(DepsDir, Name, [], [], IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
not_found ->
{SrcDepsAcc, [{Name, Vsn} | PkgDepsAcc]}
end;
-parse_dep({Name, Source, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, State) when is_tuple(Source)
+parse_dep({Name, Source, Level}, {SrcDepsAcc, PkgDepsAcc}, DepsDir, IsLock, State) when is_tuple(Source)
, is_integer(Level) ->
- Dep = new_dep(DepsDir, Name, [], Source, State),
+ Dep = new_dep(DepsDir, Name, [], Source, IsLock, State),
{[Dep | SrcDepsAcc], PkgDepsAcc};
-parse_dep(Dep, _, _, _) ->
+parse_dep(Dep, _, _, _, _) ->
throw(?PRV_ERROR({parse_dep, Dep})).
-new_dep(DepsDir, Name, Vsn, Source, State) ->
+new_dep(DepsDir, Name, Vsn, Source, IsLock, State) ->
CheckoutsDir = ec_cnv:to_list(rebar_dir:checkouts_dir(State, Name)),
{ok, Dep} = case rebar_app_info:discover(CheckoutsDir) of
{ok, App} ->
@@ -560,7 +574,7 @@ new_dep(DepsDir, Name, Vsn, Source, State) ->
ParentOverrides = rebar_state:overrides(State),
Dep1 = rebar_app_info:state(Dep,
rebar_state:overrides(S, ParentOverrides++Overrides)),
- rebar_app_info:source(Dep1, Source).
+ rebar_app_info:is_lock(rebar_app_info:source(Dep1, Source), IsLock).
fetch_app(AppInfo, AppDir, State) ->
?INFO("Fetching ~s (~p)", [rebar_app_info:name(AppInfo), rebar_app_info:source(AppInfo)]),
@@ -572,8 +586,12 @@ fetch_app(AppInfo, AppDir, State) ->
throw(Error)
end.
-update_app_info(AppInfo) ->
- AppDetails = rebar_app_info:app_details(AppInfo),
+%% This is called after the dep has been downloaded and unpacked, if it hadn't been already.
+%% So this is the first time for newly downloaded apps that its .app/.app.src data can
+%% be read in an parsed.
+update_app_info(AppDir, AppInfo) ->
+ {ok, Found} = rebar_app_info:discover(AppDir),
+ AppDetails = rebar_app_info:app_details(Found),
Applications = proplists:get_value(applications, AppDetails, []),
IncludedApplications = proplists:get_value(included_applications, AppDetails, []),
AppInfo1 = rebar_app_info:applications(
@@ -581,22 +599,29 @@ update_app_info(AppInfo) ->
IncludedApplications++Applications),
rebar_app_info:valid(AppInfo1, false).
-maybe_upgrade(AppInfo, AppDir, false, State) ->
- Source = rebar_app_info:source(AppInfo),
- rebar_fetch:needs_update(AppDir, Source, State);
-maybe_upgrade(AppInfo, AppDir, true, State) ->
+maybe_upgrade(AppInfo, AppDir, Upgrade, State) ->
Source = rebar_app_info:source(AppInfo),
- case rebar_fetch:needs_update(AppDir, Source, State) of
+ case Upgrade orelse rebar_app_info:is_lock(AppInfo) of
true ->
- ?INFO("Upgrading ~s", [rebar_app_info:name(AppInfo)]),
- case rebar_fetch:download_source(AppDir, Source, State) of
+ case rebar_fetch:needs_update(AppDir, Source, State) of
true ->
- true;
- Error ->
- throw(Error)
+ ?INFO("Upgrading ~s", [rebar_app_info:name(AppInfo)]),
+ case rebar_fetch:download_source(AppDir, Source, State) of
+ true ->
+ true;
+ Error ->
+ throw(Error)
+ end;
+ false ->
+ case Upgrade of
+ true ->
+ ?INFO("No upgrade needed for ~s", [rebar_app_info:name(AppInfo)]),
+ false;
+ false ->
+ false
+ end
end;
false ->
- ?INFO("No upgrade needed for ~s", [rebar_app_info:name(AppInfo)]),
false
end.
@@ -632,7 +657,8 @@ warn_skip_pkg({Name, Source}, State) ->
not_needs_compile(App) ->
not(rebar_app_info:is_checkout(App))
- andalso rebar_app_info:valid(App).
+ andalso rebar_app_info:valid(App)
+ andalso rebar_state:has_all_artifacts(rebar_app_info:state(App)) =:= true.
get_package(Dep, State) ->
case rebar_state:registry(State) of
diff --git a/src/rebar_prv_lock.erl b/src/rebar_prv_lock.erl
index 6fdd743..058846a 100644
--- a/src/rebar_prv_lock.erl
+++ b/src/rebar_prv_lock.erl
@@ -19,7 +19,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, true},
+ {bare, false},
{deps, ?DEPS},
{example, ""},
{short_desc, "Locks dependencies."},
diff --git a/src/rebar_prv_new.erl b/src/rebar_prv_new.erl
index 6bc9f53..0528f44 100644
--- a/src/rebar_prv_new.erl
+++ b/src/rebar_prv_new.erl
@@ -20,7 +20,7 @@ init(State) ->
Provider = providers:create([
{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 new <template>"},
{short_desc, "Create new project from templates."},
diff --git a/src/rebar_prv_packages.erl b/src/rebar_prv_packages.erl
index c7c0d50..8ba66de 100644
--- a/src/rebar_prv_packages.erl
+++ b/src/rebar_prv_packages.erl
@@ -17,7 +17,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 pkgs"},
{short_desc, "List available packages."},
@@ -27,9 +27,9 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
- case rebar_packages:registry(State) of
- {ok, Registry} ->
- print_packages(Registry),
+ case rebar_packages:get_packages(State) of
+ {Dict, _} ->
+ print_packages(Dict),
{ok, State};
error ->
?PRV_ERROR(load_registry_fail)
@@ -39,13 +39,16 @@ do(State) ->
format_error(load_registry_fail) ->
"Failed to load package regsitry. Try running 'rebar3 update' to fix".
-print_packages(Table) ->
- MS = ets:fun2ms(fun({Key, [Value]}) when is_binary(Key) -> {Key, Value} end),
- Pkgs = ets:select(Table, MS),
- lists:foreach(fun({Name, Vsns}) ->
- VsnStr = join(Vsns, <<", ">>),
- io:format("~s:~n Versions: ~s~n~n", [Name, VsnStr])
- end, Pkgs).
+print_packages(Dict) ->
+ Pkgs = lists:keysort(1, dict:fetch_keys(Dict)),
+ SortedPkgs = lists:foldl(fun({Pkg, Vsn}, Acc) ->
+ orddict:append_list(Pkg, [Vsn], Acc)
+ end, orddict:new(), Pkgs),
+
+ orddict:map(fun(Name, Vsns) ->
+ VsnStr = join(Vsns, <<", ">>),
+ io:format("~s:~n Versions: ~s~n~n", [Name, VsnStr])
+ end, SortedPkgs).
-spec join([binary()], binary()) -> binary().
join([Bin], _Sep) ->
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..5ccd054
--- /dev/null
+++ b/src/rebar_prv_plugins_upgrade.erl
@@ -0,0 +1,110 @@
+-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),
+ case proplists:get_value(plugin, Args, none) of
+ none ->
+ ?PRV_ERROR(no_plugin_arg);
+ Plugin ->
+ upgrade(Plugin, State)
+ end.
+
+-spec format_error(any()) -> iolist().
+format_error(no_plugin_arg) ->
+ io_lib:format("Must give an installed plugin to upgrade as an argument", []);
+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),
+ case find_plugin(Plugin, Profiles, State) of
+ not_found ->
+ Dep = find_plugin(Plugin, [global], State);
+ Dep ->
+ Dep
+ end,
+
+ 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(Plugin, Profiles, State) ->
+ 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).
+
+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_prv_release.erl b/src/rebar_prv_release.erl
index 595280c..2cf9b23 100644
--- a/src/rebar_prv_release.erl
+++ b/src/rebar_prv_release.erl
@@ -22,7 +22,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 release"},
{short_desc, "Build release of project."},
@@ -32,31 +32,7 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
- Caller = rebar_state:get(State, caller, api),
- Options = rebar_state:command_args(State),
- DepsDir = rebar_dir:deps_dir(State),
- ProjectAppDirs = lists:delete(".", ?DEFAULT_PROJECT_APP_DIRS),
- LibDirs = rebar_utils:filtermap(fun ec_file:exists/1,
- [?DEFAULT_CHECKOUTS_DIR, DepsDir | ProjectAppDirs]),
- OutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR),
- AllOptions = string:join(["release" | Options], " "),
- try
- case rebar_state:get(State, relx, []) of
- [] ->
- relx:main([{lib_dirs, LibDirs}
- ,{output_dir, OutputDir}
- ,{caller, Caller}], AllOptions);
- Config ->
- relx:main([{lib_dirs, LibDirs}
- ,{config, lists:reverse(Config)}
- ,{output_dir, OutputDir}
- ,{caller, Caller}], AllOptions)
- end,
- {ok, State}
- catch
- throw:T ->
- {error, {rlx_prv_release, T}}
- end.
+ rebar_relx:do(rlx_prv_release, "release", ?PROVIDER, State).
-spec format_error(any()) -> iolist().
format_error(Reason) ->
diff --git a/src/rebar_prv_relup.erl b/src/rebar_prv_relup.erl
new file mode 100644
index 0000000..a4cd8ae
--- /dev/null
+++ b/src/rebar_prv_relup.erl
@@ -0,0 +1,40 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+
+-module(rebar_prv_relup).
+
+-behaviour(provider).
+
+-export([init/1,
+ do/1,
+ format_error/1]).
+
+-include("rebar.hrl").
+
+-define(PROVIDER, relup).
+-define(DEPS, [release]).
+
+%% ===================================================================
+%% Public API
+%% ===================================================================
+
+-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
+init(State) ->
+ Provider = providers:create([{name, ?PROVIDER},
+ {module, ?MODULE},
+ {bare, true},
+ {deps, ?DEPS},
+ {example, "rebar3 relup"},
+ {short_desc, "Create relup of releases."},
+ {desc, "Create relup of releases."},
+ {opts, relx:opt_spec_list()}]),
+ State1 = rebar_state:add_provider(State, Provider),
+ {ok, State1}.
+
+-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
+do(State) ->
+ rebar_relx:do(rlx_prv_release, "relup", ?PROVIDER, State).
+
+-spec format_error(any()) -> iolist().
+format_error(Reason) ->
+ io_lib:format("~p", [Reason]).
diff --git a/src/rebar_prv_report.erl b/src/rebar_prv_report.erl
index e052998..587fad7 100644
--- a/src/rebar_prv_report.erl
+++ b/src/rebar_prv_report.erl
@@ -23,7 +23,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 report \"<task>\""},
{short_desc, "Provide a crash report to be sent to the rebar3 issues page."},
diff --git a/src/rebar_prv_shell.erl b/src/rebar_prv_shell.erl
index ec2f692..84ad723 100644
--- a/src/rebar_prv_shell.erl
+++ b/src/rebar_prv_shell.erl
@@ -45,14 +45,25 @@
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
- State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
- {module, ?MODULE},
- {bare, false},
- {deps, ?DEPS},
- {example, "rebar3 shell"},
- {short_desc, "Run shell with project apps and deps in path."},
- {desc, info()},
- {opts, [{config, undefined, "config", string, "Path to the config file to use. Defaults to the sys_config defined for relx, if present."}]}])),
+ State1 = rebar_state:add_provider(
+ State,
+ providers:create([
+ {name, ?PROVIDER},
+ {module, ?MODULE},
+ {bare, true},
+ {deps, ?DEPS},
+ {example, "rebar3 shell"},
+ {short_desc, "Run shell with project apps and deps in path."},
+ {desc, info()},
+ {opts, [{config, undefined, "config", string,
+ "Path to the config file to use. Defaults to the "
+ "sys_config defined for relx, if present."},
+ {name, undefined, "name", atom,
+ "Gives a long name to the node."},
+ {sname, undefined, "sname", atom,
+ "Gives a short name to the node."}]}
+ ])
+ ),
{ok, State1}.
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
@@ -72,6 +83,21 @@ format_error(Reason) ->
%% immediately kill the script. ctrl-g, however, works fine
shell(State) ->
+ setup_name(State),
+ setup_paths(State),
+ setup_shell(),
+ %% apps must be started after the change in shell because otherwise
+ %% their application masters never gets the new group leader (held in
+ %% their internal state)
+ maybe_boot_apps(State),
+ rebar_agent:start_link(State),
+ %% this call never returns (until user quits shell)
+ timer:sleep(infinity).
+
+info() ->
+ "Start a shell with project and deps preloaded similar to~n'erl -pa ebin -pa deps/*/ebin'.~n".
+
+setup_shell() ->
%% scan all processes for any with references to the old user and save them to
%% update later
NeedsUpdate = [Pid || Pid <- erlang:processes(),
@@ -84,25 +110,100 @@ shell(State) ->
%% wait until user_drv and user have been registered (max 3 seconds)
ok = wait_until_user_started(3000),
%% set any process that had a reference to the old user's group leader to the
- %% new user process
- _ = [erlang:group_leader(whereis(user), Pid) || Pid <- NeedsUpdate],
- %% enable error_logger's tty output
- ok = error_logger:swap_handler(tty),
- %% disable the simple error_logger (which may have been added multiple
- %% times). removes at most the error_logger added by init and the
- %% error_logger added by the tty handler
- ok = remove_error_handler(3),
+ %% new user process. Catch the race condition when the Pid exited after the
+ %% liveness check.
+ _ = [catch erlang:group_leader(whereis(user), Pid) || Pid <- NeedsUpdate,
+ is_process_alive(Pid)],
+ try
+ %% enable error_logger's tty output
+ error_logger:swap_handler(tty),
+ %% disable the simple error_logger (which may have been added multiple
+ %% times). removes at most the error_logger added by init and the
+ %% error_logger added by the tty handler
+ remove_error_handler(3)
+ catch
+ E:R -> % may fail with custom loggers
+ ?DEBUG("Logger changes failed for ~p:~p (~p)", [E,R,erlang:get_stacktrace()]),
+ hope_for_best
+ end.
+
+setup_paths(State) ->
%% Add deps to path
code:add_pathsa(rebar_state:code_paths(State, all_deps)),
%% add project app test paths
- ok = add_test_paths(State),
- %% try to read in sys.config file
- ok = reread_config(State),
- %% this call never returns (until user quits shell)
- timer:sleep(infinity).
+ ok = add_test_paths(State).
-info() ->
- "Start a shell with project and deps preloaded similar to~n'erl -pa ebin -pa deps/*/ebin'.~n".
+maybe_boot_apps(State) ->
+ case find_apps_to_boot(State) of
+ undefined ->
+ %% try to read in sys.config file
+ ok = reread_config(State);
+ Apps ->
+ %% load apps, then check config, then boot them.
+ load_apps(Apps),
+ ok = reread_config(State),
+ boot_apps(Apps)
+ end.
+
+setup_name(State) ->
+ {Opts, _} = rebar_state:command_parsed_args(State),
+ case {proplists:get_value(name, Opts), proplists:get_value(sname, Opts)} of
+ {undefined, undefined} ->
+ ok;
+ {Name, undefined} ->
+ net_kernel:start([Name, longnames]);
+ {undefined, SName} ->
+ net_kernel:start([SName, shortnames]);
+ {_, _} ->
+ ?ABORT("Cannot have both short and long node names defined", [])
+ end.
+
+find_apps_to_boot(State) ->
+ %% Try the shell_apps option
+ case rebar_state:get(State, shell_apps, undefined) of
+ undefined ->
+ %% Get to the relx tuple instead
+ case lists:keyfind(release, 1, rebar_state:get(State, relx, [])) of
+ {_, _, Apps} -> Apps;
+ false -> undefined
+ end;
+ Apps ->
+ Apps
+ end.
+
+load_apps(Apps) ->
+ [case application:load(App) of
+ ok ->
+ {ok, Ks} = application:get_all_key(App),
+ load_apps(proplists:get_value(applications, Ks));
+ _ ->
+ error % will be caught when starting the app
+ end || App <- Apps,
+ not lists:keymember(App, 1, application:loaded_applications())],
+ ok.
+
+reread_config(State) ->
+ case find_config(State) of
+ no_config ->
+ ok;
+ ConfigList ->
+ _ = [application:set_env(Application, Key, Val)
+ || {Application, Items} <- ConfigList,
+ {Key, Val} <- Items],
+ ok
+ end.
+
+boot_apps(Apps) ->
+ ?WARN("The rebar3 shell is a development tool; to deploy "
+ "applications in production, consider using releases "
+ "(http://www.rebar3.org/v3.0/docs/releases)", []),
+ Res = [application:ensure_all_started(App) || App <- Apps],
+ _ = [?INFO("Booted ~p", [App])
+ || {ok, Booted} <- Res,
+ App <- Booted],
+ _ = [?ERROR("Failed to boot ~p for reason ~p", [App, Reason])
+ || {error, {App, Reason}} <- Res],
+ ok.
remove_error_handler(0) ->
?WARN("Unable to remove simple error_logger handler", []);
@@ -124,32 +225,16 @@ wait_until_user_started(Timeout) ->
end.
add_test_paths(State) ->
- lists:foreach(fun(App) ->
- AppDir = rebar_app_info:out_dir(App),
- %% ignore errors resulting from non-existent directories
- _ = code:add_path(filename:join([AppDir, "test"]))
- end, rebar_state:project_apps(State)),
+ _ = [begin
+ AppDir = rebar_app_info:out_dir(App),
+ %% ignore errors resulting from non-existent directories
+ _ = code:add_path(filename:join([AppDir, "test"]))
+ end || App <- rebar_state:project_apps(State)],
_ = code:add_path(filename:join([rebar_dir:base_dir(State), "test"])),
ok.
-reread_config(State) ->
- case find_config(State) of
- no_config ->
- ok;
- {ok, ConfigList} ->
- lists:foreach(fun ({Application, Items}) ->
- lists:foreach(fun ({Key, Val}) ->
- application:set_env(Application, Key, Val)
- end,
- Items)
- end,
- ConfigList);
- {error, Error} ->
- ?ABORT("Error while attempting to read configuration file: ~p", [Error])
- end.
-
% First try the --config flag, then try the relx sys_config
--spec find_config(rebar_state:t()) -> {ok, [tuple()]}|no_config|{error, tuple()}.
+-spec find_config(rebar_state:t()) -> [tuple()] | no_config.
find_config(State) ->
case find_config_option(State) of
no_config ->
@@ -158,7 +243,7 @@ find_config(State) ->
Result
end.
--spec find_config_option(rebar_state:t()) -> {ok, [tuple()]}|no_config|{error, tuple()}.
+-spec find_config_option(rebar_state:t()) -> [tuple()] | no_config.
find_config_option(State) ->
{Opts, _} = rebar_state:command_parsed_args(State),
case proplists:get_value(config, Opts) of
@@ -168,7 +253,7 @@ find_config_option(State) ->
consult_config(State, Filename)
end.
--spec find_config_relx(rebar_state:t()) -> {ok, [tuple()]}|no_config|{error, tuple()}.
+-spec find_config_relx(rebar_state:t()) -> [tuple()] | no_config.
find_config_relx(State) ->
case proplists:get_value(sys_config, rebar_state:get(State, relx, [])) of
undefined ->
@@ -181,11 +266,7 @@ find_config_relx(State) ->
consult_config(State, Filename) ->
Fullpath = filename:join(rebar_dir:root_dir(State), Filename),
?DEBUG("Loading configuration from ~p", [Fullpath]),
- case file:consult(Fullpath) of
- {ok, [Config]} ->
- {ok, Config};
- {ok, []} ->
- {ok, []};
- {error, Error} ->
- {error, {Error, Fullpath}}
+ case rebar_file_utils:try_consult(Fullpath) of
+ [T] -> T;
+ [] -> []
end.
diff --git a/src/rebar_prv_tar.erl b/src/rebar_prv_tar.erl
index f7557bd..b3a12c0 100644
--- a/src/rebar_prv_tar.erl
+++ b/src/rebar_prv_tar.erl
@@ -12,7 +12,7 @@
-include("rebar.hrl").
-define(PROVIDER, tar).
--define(DEPS, [compile]).
+-define(DEPS, [release]).
%% ===================================================================
%% Public API
@@ -22,7 +22,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 tar"},
{short_desc, "Tar archive of release built of project."},
@@ -32,26 +32,7 @@ init(State) ->
-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
- Caller = rebar_state:get(State, caller, api),
- Options = rebar_state:command_args(State),
- DepsDir = rebar_dir:deps_dir(State),
- ProjectAppDirs = lists:delete(".", ?DEFAULT_PROJECT_APP_DIRS),
- LibDirs = rebar_utils:filtermap(fun ec_file:exists/1,
- [?DEFAULT_CHECKOUTS_DIR, DepsDir | ProjectAppDirs]),
- OutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR),
- AllOptions = string:join(["release", "tar" | Options], " "),
- case rebar_state:get(State, relx, []) of
- [] ->
- relx:main([{lib_dirs, LibDirs}
- ,{output_dir, OutputDir}
- ,{caller, Caller}], AllOptions);
- Config ->
- relx:main([{lib_dirs, LibDirs}
- ,{config, Config}
- ,{output_dir, OutputDir}
- ,{caller, Caller}], AllOptions)
- end,
- {ok, State}.
+ rebar_relx:do(rlx_prv_release, "tar", ?PROVIDER, State).
-spec format_error(any()) -> iolist().
format_error(Reason) ->
diff --git a/src/rebar_prv_unlock.erl b/src/rebar_prv_unlock.erl
index 9790fcf..b049c92 100644
--- a/src/rebar_prv_unlock.erl
+++ b/src/rebar_prv_unlock.erl
@@ -22,7 +22,7 @@ init(State) ->
State,
providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, ""},
{short_desc, "Unlock dependencies."},
diff --git a/src/rebar_prv_update.erl b/src/rebar_prv_update.erl
index dfb719a..6838bab 100644
--- a/src/rebar_prv_update.erl
+++ b/src/rebar_prv_update.erl
@@ -23,7 +23,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 update"},
{short_desc, "Update package index."},
diff --git a/src/rebar_prv_upgrade.erl b/src/rebar_prv_upgrade.erl
index 05845e4..49d5674 100644
--- a/src/rebar_prv_upgrade.erl
+++ b/src/rebar_prv_upgrade.erl
@@ -28,7 +28,7 @@ init(State) ->
rebar_state:add_provider(State,
providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 upgrade [cowboy[,ranch]]"},
{short_desc, "Upgrade dependencies."},
@@ -94,10 +94,15 @@ prepare_locks([Name|Names], Deps, Locks, Unlocks) ->
AtomName = binary_to_atom(Name, utf8),
case lists:keyfind(Name, 1, Locks) of
{_, _, 0} = Lock ->
- case lists:keyfind(AtomName, 1, Deps) of
- false ->
+ case {lists:keyfind(AtomName, 1, Deps), lists:member(AtomName, Deps)} of
+ {false, false} ->
?PRV_ERROR({unknown_dependency, Name});
- Dep ->
+ {Dep, false} ->
+ {Source, NewLocks, NewUnlocks} = prepare_lock(Dep, Lock, Locks),
+ prepare_locks(Names, Deps, NewLocks,
+ [{Name, Source, 0} | NewUnlocks ++ Unlocks]);
+ {false, true} -> % package as a single atom
+ Dep = AtomName,
{Source, NewLocks, NewUnlocks} = prepare_lock(Dep, Lock, Locks),
prepare_locks(Names, Deps, NewLocks,
[{Name, Source, 0} | NewUnlocks ++ Unlocks])
@@ -116,9 +121,13 @@ prepare_locks([Name|Names], Deps, Locks, Unlocks) ->
end.
prepare_lock(Dep, Lock, Locks) ->
- Source = Source = case Dep of
- {_, Src} -> Src;
- {_, _, Src} -> Src
+ Source = case Dep of
+ {_, SrcOrVsn} -> SrcOrVsn;
+ {_, _, Src} -> Src;
+ _ when is_atom(Dep) ->
+ %% version-free package. Must unlock whatever matches in locks
+ {_, Vsn, _} = lists:keyfind(ec_cnv:to_binary(Dep), 1, Locks),
+ Vsn
end,
{NewLocks, NewUnlocks} = unlock_higher_than(0, Locks -- [Lock]),
{Source, NewLocks, NewUnlocks}.
diff --git a/src/rebar_prv_version.erl b/src/rebar_prv_version.erl
index 5edcc85..bcc5f6c 100644
--- a/src/rebar_prv_version.erl
+++ b/src/rebar_prv_version.erl
@@ -22,7 +22,7 @@
init(State) ->
State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
{module, ?MODULE},
- {bare, false},
+ {bare, true},
{deps, ?DEPS},
{example, "rebar3 version"},
{short_desc, "Print version for rebar and current Erlang."},
diff --git a/src/rebar_prv_xref.erl b/src/rebar_prv_xref.erl
index ed53da6..623e946 100644
--- a/src/rebar_prv_xref.erl
+++ b/src/rebar_prv_xref.erl
@@ -27,7 +27,7 @@ init(State) ->
Provider = providers:create([{name, ?PROVIDER},
{module, ?MODULE},
{deps, ?DEPS},
- {bare, false},
+ {bare, true},
{example, "rebar3 xref"},
{short_desc, short_desc()},
{desc, desc()}]),
diff --git a/src/rebar_relx.erl b/src/rebar_relx.erl
new file mode 100644
index 0000000..a3adedd
--- /dev/null
+++ b/src/rebar_relx.erl
@@ -0,0 +1,69 @@
+%% -*- erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ts=4 sw=4 et
+
+-module(rebar_relx).
+
+-export([do/4,
+ format_error/1]).
+
+-include("rebar.hrl").
+
+%% ===================================================================
+%% Public API
+%% ===================================================================
+
+-spec do(atom(), string(), atom(), rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
+do(Module, Command, Provider, State) ->
+ Options = rebar_state:command_args(State),
+ DepsDir = rebar_dir:deps_dir(State),
+ ProjectAppDirs = lists:delete(".", ?DEFAULT_PROJECT_APP_DIRS),
+ LibDirs = rebar_utils:filtermap(fun ec_file:exists/1,
+ [?DEFAULT_CHECKOUTS_DIR, DepsDir | ProjectAppDirs]),
+ OutputDir = filename:join(rebar_dir:base_dir(State), ?DEFAULT_RELEASE_DIR),
+ AllOptions = string:join([Command | Options], " "),
+ Cwd = rebar_state:dir(State),
+ Providers = rebar_state:providers(State),
+ rebar_hooks:run_all_hooks(Cwd, pre, Provider, Providers, State),
+ try
+ case rebar_state:get(State, relx, []) of
+ [] ->
+ relx:main([{lib_dirs, LibDirs}
+ ,{output_dir, OutputDir}
+ ,{caller, api}], AllOptions);
+ Config ->
+ Config1 = update_config(Config),
+ relx:main([{lib_dirs, LibDirs}
+ ,{config, Config1}
+ ,{output_dir, OutputDir}
+ ,{caller, api}], AllOptions)
+ end,
+ rebar_hooks:run_all_hooks(Cwd, post, Provider, Providers, State),
+ {ok, State}
+ catch
+ throw:T ->
+ {error, {Module, T}}
+ end.
+
+-spec format_error(any()) -> iolist().
+format_error(Reason) ->
+ io_lib:format("~p", [Reason]).
+
+%% To handle profiles rebar3 expects the provider to use the first entry
+%% in a configuration key-value list as the value of a key if dups exist.
+%% This does not work with relx. Some config options must not lose their
+%% order (release which has an extends option is one). So here we pull out
+%% options that are special so we can reverse the rest so what we expect
+%% from a rebar3 profile is what we get on the relx side.
+-define(SPECIAL_KEYS, [release, vm_args, sys_config, overlay_vars, lib_dirs]).
+
+update_config(Config) ->
+ {Special, Other} =
+ lists:foldl(fun(Tuple, {SpecialAcc, OtherAcc}) when is_tuple(Tuple) ->
+ case lists:member(element(1, Tuple), ?SPECIAL_KEYS) of
+ true ->
+ {[Tuple | SpecialAcc], OtherAcc};
+ false ->
+ {SpecialAcc, [Tuple | OtherAcc]}
+ end
+ end, {[], []}, Config),
+ lists:reverse(Special) ++ Other.
diff --git a/src/rebar_state.erl b/src/rebar_state.erl
index 7a6e60d..d0b28de 100644
--- a/src/rebar_state.erl
+++ b/src/rebar_state.erl
@@ -1,8 +1,13 @@
-module(rebar_state).
-export([new/0, new/1, new/2, new/3,
+
get/2, get/3, set/3,
+ format_error/1,
+
+ has_all_artifacts/1,
+
code_paths/2, code_paths/3, update_code_paths/3,
opts/1, opts/2,
@@ -12,7 +17,7 @@
lock/1, lock/2,
- current_profiles/1,
+ current_profiles/1, current_profiles/2,
command_args/1, command_args/2,
command_parsed_args/1, command_parsed_args/2,
@@ -24,6 +29,7 @@
project_apps/1, project_apps/2,
deps_to_build/1, deps_to_build/2,
+ all_plugin_deps/1, all_plugin_deps/2, update_all_plugin_deps/2,
all_deps/1, all_deps/2, update_all_deps/2,
namespace/1, namespace/2,
@@ -39,6 +45,7 @@
providers/1, providers/2, add_provider/2]).
-include("rebar.hrl").
+-include_lib("providers/include/providers.hrl").
-record(state_t, {dir :: file:name(),
opts = dict:new() :: rebar_dict(),
@@ -48,13 +55,14 @@
lock = [],
current_profiles = [default] :: [atom()],
- namespace = undefined :: atom(),
+ namespace = default :: atom(),
command_args = [],
- command_parsed_args = [],
+ command_parsed_args = {[], []},
project_apps = [] :: [rebar_app_info:t()],
deps_to_build = [] :: [rebar_app_info:t()],
+ all_plugin_deps = [] :: [rebar_app_info:t()],
all_deps = [] :: [rebar_app_info:t()],
packages = undefined :: {rebar_dict(), rebar_digraph()} | undefined,
@@ -76,7 +84,10 @@ new() ->
new(Config) when is_list(Config) ->
BaseState = base_state(),
Deps = proplists:get_value(deps, Config, []),
- Opts = dict:from_list([{{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(),
default = Opts,
opts = Opts }.
@@ -86,7 +97,11 @@ new(Profile, Config) when is_atom(Profile)
, is_list(Config) ->
BaseState = base_state(),
Deps = proplists:get_value(deps, Config, []),
- Opts = dict:from_list([{{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(),
current_profiles = [Profile],
default = Opts,
@@ -99,15 +114,21 @@ new(ParentState=#state_t{}, Config) ->
-spec new(t(), list(), file:name()) -> t().
new(ParentState, Config, Dir) ->
Opts = ParentState#state_t.opts,
- LocalOpts = case rebar_config:consult_file(filename:join(Dir, ?LOCK_FILE)) of
+ 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],
- dict:from_list([{{locks, default}, D}, {{deps, default}, Deps} | Config]);
+ 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, []),
- dict:from_list([{{deps, default}, D} | 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),
@@ -147,6 +168,26 @@ default(#state_t{default=Opts}) ->
default(State, Opts) ->
State#state_t{default=Opts}.
+format_error({profile_not_list, Profile, Other}) ->
+ io_lib:format("Profile config must be a list but for profile '~p' config given as:~n~p", [Profile, Other]).
+
+-spec has_all_artifacts(rebar_app_info:t()) -> true | providers:error().
+has_all_artifacts(State) ->
+ Artifacts = rebar_state:get(State, artifacts, []),
+ Dir = rebar_dir:base_dir(State),
+ all(Dir, Artifacts).
+
+all(_, []) ->
+ true;
+all(Dir, [File|Artifacts]) ->
+ case filelib:is_regular(filename:join(Dir, File)) of
+ false ->
+ ?DEBUG("Missing artifact ~s", [filename:join(Dir, File)]),
+ {false, File};
+ true ->
+ all(Dir, Artifacts)
+ end.
+
code_paths(#state_t{code_paths=CodePaths}, Key) ->
case dict:find(Key, CodePaths) of
{ok, CodePath} ->
@@ -175,6 +216,9 @@ opts(State, Opts) ->
current_profiles(#state_t{current_profiles=Profiles}) ->
Profiles.
+current_profiles(State, Profiles) ->
+ State#state_t{current_profiles=Profiles}.
+
lock(#state_t{lock=Lock}) ->
Lock.
@@ -221,14 +265,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, []),
@@ -244,12 +291,18 @@ 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;
(Profile, OptsAcc) ->
- ProfileOpts = dict:from_list(proplists:get_value(Profile, ConfigProfiles, [])),
- merge_opts(Profile, ProfileOpts, OptsAcc)
+ case proplists:get_value(Profile, ConfigProfiles, []) of
+ OptsList when is_list(OptsList) ->
+ ProfileOpts = dict:from_list(OptsList),
+ merge_opts(Profile, ProfileOpts, OptsAcc);
+ Other ->
+ throw(?PRV_ERROR({profile_not_list, Profile, Other}))
+ end
end, Defaults, AppliedProfiles),
State#state_t{current_profiles = AppliedProfiles, opts=NewOpts}.
@@ -267,11 +320,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) ->
@@ -279,6 +339,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) ->
@@ -344,6 +408,15 @@ all_deps(#state_t{all_deps=Apps}) ->
all_deps(State=#state_t{}, NewApps) ->
State#state_t{all_deps=NewApps}.
+all_plugin_deps(#state_t{all_plugin_deps=Apps}) ->
+ Apps.
+
+all_plugin_deps(State=#state_t{}, NewApps) ->
+ State#state_t{all_plugin_deps=NewApps}.
+
+update_all_plugin_deps(State=#state_t{all_plugin_deps=Apps}, NewApps) ->
+ State#state_t{all_plugin_deps=Apps++NewApps}.
+
update_all_deps(State=#state_t{all_deps=Apps}, NewApps) ->
State#state_t{all_deps=Apps++NewApps}.
diff --git a/src/rebar_templater.erl b/src/rebar_templater.erl
index 353fa36..3aa6e90 100644
--- a/src/rebar_templater.erl
+++ b/src/rebar_templater.erl
@@ -380,4 +380,4 @@ write_file(Output, Data, Force) ->
%% Render a binary to a string, using mustache and the specified context
%%
render(Bin, Context) ->
- mustache:render(ec_cnv:to_binary(Bin), Context, [{key_type, atom}]).
+ bbmustache:render(ec_cnv:to_binary(Bin), Context, [{key_type, atom}]).
diff --git a/src/rebar_utils.erl b/src/rebar_utils.erl
index 160d547..0cbc7c2 100644
--- a/src/rebar_utils.erl
+++ b/src/rebar_utils.erl
@@ -47,6 +47,8 @@
deprecated/4,
erl_opts/1,
indent/1,
+ update_code/1,
+ remove_from_code_path/1,
cleanup_code_path/1,
args_to_tasks/1,
expand_env_variable/3,
@@ -152,7 +154,7 @@ sh(Command0, Options0) ->
Command = lists:flatten(patch_on_windows(Command0, proplists:get_value(env, Options, []))),
PortSettings = proplists:get_all_values(port_settings, Options) ++
- [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide],
+ [exit_status, {line, 16384}, use_stdio, stderr_to_stdout, hide, eof],
?DEBUG("Port Cmd: ~s\nPort Opts: ~p\n", [Command, PortSettings]),
Port = open_port({spawn, Command}, PortSettings),
@@ -433,10 +435,14 @@ sh_loop(Port, Fun, Acc) ->
sh_loop(Port, Fun, Fun(Line ++ "\n", Acc));
{Port, {data, {noeol, Line}}} ->
sh_loop(Port, Fun, Fun(Line, Acc));
- {Port, {exit_status, 0}} ->
- {ok, lists:flatten(lists:reverse(Acc))};
- {Port, {exit_status, Rc}} ->
- {error, {Rc, lists:flatten(lists:reverse(Acc))}}
+ {Port, eof} ->
+ Data = lists:flatten(lists:reverse(Acc)),
+ receive
+ {Port, {exit_status, 0}} ->
+ {ok, Data};
+ {Port, {exit_status, Rc}} ->
+ {error, {Rc, Data}}
+ end
end.
beam_to_mod(Dir, Filename) ->
@@ -563,6 +569,39 @@ filter_defines([Opt | Rest], Acc) ->
indent(Amount) when erlang:is_integer(Amount) ->
[?ONE_LEVEL_INDENT || _ <- lists:seq(1, Amount)].
+%% Replace code paths with new paths for existing apps and
+%% purge code of the old modules from those apps.
+update_code(Paths) ->
+ lists:foreach(fun(Path) ->
+ Name = filename:basename(Path, "/ebin"),
+ App = list_to_atom(Name),
+ application:load(App),
+ case application:get_key(App, modules) of
+ undefined ->
+ code:add_patha(Path),
+ ok;
+ {ok, Modules} ->
+ code:replace_path(Name, Path),
+ [begin code:purge(M), code:delete(M) end || M <- Modules]
+ end
+ end, Paths).
+
+remove_from_code_path(Paths) ->
+ lists:foreach(fun(Path) ->
+ Name = filename:basename(Path, "/ebin"),
+ App = list_to_atom(Name),
+ application:load(App),
+ case application:get_key(App, modules) of
+ undefined ->
+ application:unload(App),
+ ok;
+ {ok, Modules} ->
+ application:unload(App),
+ [begin code:purge(M), code:delete(M) end || M <- Modules]
+ end,
+ code:del_path(Path)
+ end, Paths).
+
cleanup_code_path(OrigPath) ->
CurrentPath = code:get_path(),
AddedPaths = CurrentPath -- OrigPath,