summaryrefslogtreecommitdiff
path: root/src/rebar_prv_install_deps.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/rebar_prv_install_deps.erl')
-rw-r--r--src/rebar_prv_install_deps.erl329
1 files changed, 196 insertions, 133 deletions
diff --git a/src/rebar_prv_install_deps.erl b/src/rebar_prv_install_deps.erl
index d20f2b5..522420d 100644
--- a/src/rebar_prv_install_deps.erl
+++ b/src/rebar_prv_install_deps.erl
@@ -26,14 +26,16 @@
%% -------------------------------------------------------------------
-module(rebar_prv_install_deps).
--behaviour(rebar_provider).
+-behaviour(provider).
-export([init/1,
- do/1]).
+ do/1,
+ format_error/2]).
-include("rebar.hrl").
--export([setup_env/1]).
+-export([handle_deps/2,
+ handle_deps/3]).
%% for internal use only
-export([get_deps_dir/1]).
@@ -43,9 +45,9 @@
-define(DEPS, [app_discovery]).
-type src_dep() :: {atom(), string(), {atom(), string(), string()}}.
--type binary_dep() :: {atom(), binary()} | atom().
+-type pkg_dep() :: {atom(), binary()} | atom().
--type dep() :: src_dep() | binary_dep().
+-type dep() :: src_dep() | pkg_dep().
%% ===================================================================
%% Public API
@@ -53,174 +55,237 @@
-spec init(rebar_state:t()) -> {ok, rebar_state:t()}.
init(State) ->
- State1 = rebar_state:add_provider(State, #provider{name = ?PROVIDER,
- provider_impl = ?MODULE,
- bare = true,
- deps = ?DEPS,
- example = undefined,
- short_desc = "Install dependencies",
- desc = info("Install dependencies"),
- opts = []}),
+ State1 = rebar_state:add_provider(State, providers:create([{name, ?PROVIDER},
+ {module, ?MODULE},
+ {bare, true},
+ {deps, ?DEPS},
+ {example, undefined},
+ {short_desc, "Install dependencies"},
+ {desc, info("Install dependencies")},
+ {opts, []}])),
{ok, State1}.
--spec do(rebar_state:t()) -> {ok, rebar_state:t()}.
+-spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}.
do(State) ->
- case rebar_state:get(State, locks, []) of
- [] ->
- handle_deps(State, ordsets:from_list(rebar_state:get(State, deps, [])));
- Locks ->
- handle_deps(State, ordsets:from_list(Locks))
+ ProjectApps = rebar_state:project_apps(State),
+ {ok, State1} = case rebar_state:get(State, locks, []) of
+ [] ->
+ handle_deps(State, rebar_state:get(State, deps, []));
+ Locks ->
+ handle_deps(State, Locks)
+ end,
+
+ Source = ProjectApps ++ rebar_state:src_apps(State1),
+ case rebar_topo:sort_apps(Source) of
+ {ok, Sort} ->
+ {ok, rebar_state:set(State1, deps_to_build, lists:dropwhile(fun rebar_app_info:valid/1, Sort -- ProjectApps))};
+ {error, Error} ->
+ {error, Error}
end.
-%% set REBAR_DEPS_DIR and ERL_LIBS environment variables
-setup_env(State) ->
- DepsDir = get_deps_dir(State),
- %% include rebar's DepsDir in ERL_LIBS
- Separator = case os:type() of
- {win32, nt} ->
- ";";
- _ ->
- ":"
- end,
- ERL_LIBS = case os:getenv("ERL_LIBS") of
- false ->
- {"ERL_LIBS", DepsDir};
- PrevValue ->
- {"ERL_LIBS", DepsDir ++ Separator ++ PrevValue}
- end,
- [{"REBAR_DEPS_DIR", DepsDir}, ERL_LIBS].
+-spec format_error(any(), rebar_state:t()) -> {iolist(), rebar_state:t()}.
+format_error(Reason, State) ->
+ {io_lib:format("~p", [Reason]), State}.
-spec get_deps_dir(rebar_state:t()) -> file:filename_all().
get_deps_dir(State) ->
BaseDir = rebar_state:get(State, base_dir, ""),
- get_deps_dir(BaseDir, "deps").
+ DepsDir = rebar_state:get(State, deps_dir, ?DEFAULT_DEPS_DIR),
+ get_deps_dir(BaseDir, DepsDir).
--spec get_deps_dir(file:filename_all(), rebar_state:t()) -> file:filename_all().
+-spec get_deps_dir(file:filename_all(), file:filename_all()) -> file:filename_all().
get_deps_dir(DepsDir, App) ->
filename:join(DepsDir, App).
-%% ===================================================================
-%% Internal functions
-%% ===================================================================
-
-spec handle_deps(rebar_state:t(), [dep()]) -> {ok, rebar_state:t()}.
-handle_deps(State, []) ->
- {ok, State};
handle_deps(State, Deps) ->
+ handle_deps(State, Deps, false).
+
+-spec handle_deps(rebar_state:t(), [dep()], boolean() | {true, binary(), integer()})
+ -> {ok, rebar_state:t()}.
+handle_deps(State, [], _) ->
+ {ok, State};
+handle_deps(State, Deps, Update) ->
%% Read in package index and dep graph
{Packages, Graph} = rebar_packages:get_packages(State),
- ProjectApps = rebar_state:project_apps(State),
- %% Split source deps form binary deps, needed to keep backwards compatibility
+ %% Split source deps from pkg deps, needed to keep backwards compatibility
DepsDir = get_deps_dir(State),
- {SrcDeps, BinaryDeps} = parse_deps(DepsDir, Deps),
- State1 = rebar_state:src_deps(rebar_state:binary_deps(State, BinaryDeps),
+ {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps),
+ State1 = rebar_state:src_deps(rebar_state:pkg_deps(State, PkgDeps),
SrcDeps),
%% Fetch transitive src deps
- State2 = update_src_deps(State1),
- Solved = case rebar_state:binary_deps(State2) of
- [] -> %% No binary deps
- [];
- BinaryDeps1 ->
- %% Find binary deps needed
- {ok, S} = rlx_depsolver:solve(Graph, BinaryDeps1),
- %% Create app_info record for each binary dep
- lists:map(fun({Name, Vsn}) ->
- AppInfo = package_to_app(DepsDir
- ,Packages
- ,Name
- ,Vsn),
- ok = maybe_fetch(AppInfo),
- AppInfo
- end, S)
+ State2 = update_src_deps(0, State1, Update),
+ Solved = case rebar_state:pkg_deps(State2) of
+ [] -> %% No pkg deps
+ [];
+ PkgDeps1 ->
+ %% Find pkg deps needed
+ {ok, S} = rlx_depsolver:solve(Graph, PkgDeps1),
+ %% Create app_info record for each pkg dep
+ [AppInfo || Pkg <- S,
+ AppInfo <- package_to_app(DepsDir
+ ,Packages
+ ,Pkg),
+ maybe_fetch(AppInfo, Update)]
end,
- Source = ProjectApps ++ ordsets:to_list(rebar_state:src_deps(State2)),
- AllDeps = ordsets:union([ordsets:to_list(rebar_state:src_deps(State2))
- ,ordsets:from_list(Solved)]),
-
+ AllDeps = lists:ukeymerge(2
+ ,lists:ukeysort(2, rebar_state:src_apps(State2))
+ ,lists:ukeysort(2, Solved)),
%% Sort all apps to build order
State3 = rebar_state:set(State2, all_deps, AllDeps),
- {ok, Sort} = rebar_topo:sort_apps(ordsets:to_list(Source)),
- {ok, rebar_state:set(State3, deps_to_build, lists:dropwhile(fun is_valid/1, Sort -- ProjectApps))}.
+ {ok, State3}.
+
+%% ===================================================================
+%% Internal functions
+%% ===================================================================
--spec is_valid(rebar_app_info:t()) -> boolean().
-is_valid(App) ->
- rebar_app_info:valid(App).
+package_to_app(DepsDir, Packages, Pkg={_, Vsn}) ->
+ Name = ec_cnv:to_binary(rlx_depsolver:dep_pkg(Pkg)),
+ FmtVsn = iolist_to_binary(rlx_depsolver:format_version(Vsn)),
+ case dict:find({Name, FmtVsn}, Packages) of
+ error ->
+ [];
+ {ok, P} ->
+ PkgDeps = proplists:get_value(<<"deps">>, P, []),
+ Link = proplists:get_value(<<"link">>, P, ""),
+ {ok, AppInfo} = rebar_app_info:new(Name, FmtVsn),
+ AppInfo1 = rebar_app_info:deps(AppInfo, PkgDeps),
+ AppInfo2 = rebar_app_info:dir(AppInfo1, get_deps_dir(DepsDir, Name)),
+ [rebar_app_info:source(AppInfo2, Link)]
+ end.
--spec package_to_app(file:name(), rlx_depsolver:t(), binary(), binary()) -> rebar_app_info:t().
-package_to_app(DepsDir, Packages, Name, Vsn) ->
- FmtVsn = ec_cnv:to_binary(rlx_depsolver:format_version(Vsn)),
+-spec update_src_deps(integer(), rebar_state:t(), boolean()) -> rebar_state:t().
+update_src_deps(Level, State, Update) ->
+ SrcDeps = rebar_state:src_deps(State),
+ case lists:foldl(fun(AppInfo, {SrcDepsAcc, PkgDepsAcc, StateAcc}) ->
+ case Update of
+ {true, UpdateName, UpdateLevel} ->
+ handle_update(AppInfo
+ ,UpdateName
+ ,UpdateLevel
+ ,SrcDepsAcc
+ ,PkgDepsAcc
+ ,Level
+ ,StateAcc);
+ _ ->
+ maybe_fetch(AppInfo, false),
+ handle_dep(AppInfo
+ ,SrcDepsAcc
+ ,PkgDepsAcc
+ ,Level
+ ,StateAcc)
+ end
+ end, {[], rebar_state:pkg_deps(State), State}, SrcDeps) of
+ {[], NewPkgDeps, State1} ->
+ rebar_state:pkg_deps(State1, NewPkgDeps);
+ {NewSrcDeps, NewPkgDeps, State1} ->
+ State2 = rebar_state:pkg_deps(State1, NewPkgDeps),
+ State3 = rebar_state:src_deps(State2, NewSrcDeps),
+ update_src_deps(Level+1, State3, Update)
+ end.
- {ok, P} = dict:find({Name, FmtVsn}, Packages),
- PkgDeps = proplists:get_value(<<"deps">>, P),
- Link = proplists:get_value(<<"link">>, P),
+handle_update(AppInfo, UpdateName, UpdateLevel, SrcDeps, PkgDeps, Level, State) ->
+ Name = rebar_app_info:name(AppInfo),
+ Locks = rebar_state:get(State, locks, []),
+ {_, _, _, DepLevel} = lists:keyfind(Name, 1, Locks),
+ case UpdateLevel < DepLevel
+ orelse Name =:= UpdateName of
+ true ->
+ case maybe_fetch(AppInfo, true) of
+ true ->
+ handle_dep(AppInfo
+ ,SrcDeps
+ ,PkgDeps
+ ,Level
+ ,State);
- {ok, AppInfo} = rebar_app_info:new(Name, FmtVsn),
- AppInfo1 = rebar_app_info:deps(AppInfo, PkgDeps),
- AppInfo2 =
- rebar_app_info:dir(AppInfo1, get_deps_dir(DepsDir, <<Name/binary, "-", FmtVsn/binary>>)),
- rebar_app_info:source(AppInfo2, Link).
+ false ->
+ {SrcDeps, PkgDeps, State}
+ end;
+ false ->
+ {SrcDeps, PkgDeps, State}
+ end.
--spec update_src_deps(rebar_state:t()) -> rebat_state:t().
-update_src_deps(State) ->
- SrcDeps = rebar_state:src_deps(State),
+handle_dep(AppInfo, SrcDeps, PkgDeps, Level, State) ->
DepsDir = get_deps_dir(State),
- case lists:foldl(fun(AppInfo, {SrcDepsAcc, BinaryDepsAcc}) ->
- ok = maybe_fetch(AppInfo),
- {AppInfo1, NewSrcDeps, NewBinaryDeps} = handle_dep(DepsDir, AppInfo),
- {ordsets:union(ordsets:add_element(AppInfo1, SrcDepsAcc), NewSrcDeps)
- ,NewBinaryDeps++BinaryDepsAcc}
- end, {ordsets:new(), rebar_state:binary_deps(State)}, SrcDeps) of
- {NewSrcDeps, NewBinaryDeps} when length(SrcDeps) =:= length(NewSrcDeps) ->
- rebar_state:src_deps(rebar_state:binary_deps(State, NewBinaryDeps), NewSrcDeps);
- {NewSrcDeps, NewBinaryDeps} ->
- State1 = rebar_state:src_deps(rebar_state:binary_deps(State, NewBinaryDeps), NewSrcDeps),
- update_src_deps(State1)
- end.
+ {AppInfo1, NewSrcDeps, NewPkgDeps} =
+ handle_dep(DepsDir, AppInfo),
+ AppInfo2 = rebar_app_info:dep_level(AppInfo1, Level),
+ {NewSrcDeps ++ SrcDeps
+ ,NewPkgDeps++PkgDeps
+ ,rebar_state:src_apps(State, AppInfo2)}.
--spec handle_dep(binary(), rebar_state:t()) -> {[rebar_app_info:t()], [binary_dep()]}.
+-spec handle_dep(file:filename_all(), rebar_app_info:t()) ->
+ {rebar_app_info:t(), [rebar_app_info:t()], [pkg_dep()]}.
handle_dep(DepsDir, AppInfo) ->
C = rebar_config:consult(rebar_app_info:dir(AppInfo)),
S = rebar_state:new(rebar_state:new(), C, rebar_app_info:dir(AppInfo)),
Deps = rebar_state:get(S, deps, []),
AppInfo1 = rebar_app_info:deps(AppInfo, rebar_state:deps_names(S)),
- {SrcDeps, BinaryDeps} = parse_deps(DepsDir, Deps),
- {AppInfo1, SrcDeps, BinaryDeps}.
+ {SrcDeps, PkgDeps} = parse_deps(DepsDir, Deps),
+ {AppInfo1, SrcDeps, PkgDeps}.
--spec maybe_fetch(rebar_app_info:t()) -> ok.
-maybe_fetch(AppInfo) ->
- AppDir = rebar_app_info:dir(AppInfo),
- case filelib:is_dir(AppDir) of
- false ->
- ?INFO("Fetching ~s~n", [rebar_app_info:name(AppInfo)]),
- Source = rebar_app_info:source(AppInfo),
- rebar_fetch:download_source(AppDir, Source);
- true ->
- ok
+-spec maybe_fetch(rebar_app_info:t(), boolean() | {true, binary(), integer()}) -> boolean().
+maybe_fetch(AppInfo, Update) ->
+ AppDir = ec_cnv:to_list(rebar_app_info:dir(AppInfo)),
+ Apps = rebar_app_discover:find_apps(["_checkouts"], all),
+ case rebar_app_utils:find(rebar_app_info:name(AppInfo), Apps) of
+ {ok, _} ->
+ %% Don't fetch dep if it exists in the _checkouts dir
+ false;
+ error ->
+ Exists = case rebar_app_utils:is_app_dir(filename:absname(AppDir)++"-*") of
+ {true, _} ->
+ true;
+ _ ->
+ case rebar_app_utils:is_app_dir(filename:absname(AppDir)) of
+ {true, _} ->
+ true;
+ _ ->
+ false
+ end
+ end,
+
+ case not Exists orelse Update of
+ true ->
+ ?INFO("Fetching ~s~n", [rebar_app_info:name(AppInfo)]),
+ Source = rebar_app_info:source(AppInfo),
+ rebar_fetch:download_source(AppDir, Source),
+ true;
+ _ ->
+ false
+ end
end.
--spec parse_deps(binary(), [dep()]) -> {ordsets:ordset(rebar_app_info:t()), [binary_dep()]}.
+-spec parse_deps(binary(), [dep()]) -> {[rebar_app_info:t()], [pkg_dep()]}.
parse_deps(DepsDir, Deps) ->
- lists:foldl(fun({Name, Vsn}, {SrcDepsAcc, BinaryDepsAcc}) ->
+ lists:foldl(fun({Name, Vsn}, {SrcDepsAcc, PkgDepsAcc}) ->
{SrcDepsAcc, [parse_goal(ec_cnv:to_binary(Name)
- ,ec_cnv:to_binary(Vsn)) | BinaryDepsAcc]};
- (Name, {SrcDepsAcc, BinaryDepsAcc}) when is_atom(Name) ->
- {SrcDepsAcc, [ec_cnv:to_binary(Name) | BinaryDepsAcc]};
- ({Name, Vsn, Source}, {SrcDepsAcc, BinaryDepsAcc}) when is_tuple (Source) ->
- Dir = ec_cnv:to_list(get_deps_dir(DepsDir, Name)),
- {ok, Dep} = case rebar_app_info:discover(Dir) of
- {ok, App} ->
- {ok, App};
- not_found ->
- rebar_app_info:new(Name, Vsn, Dir)
- end,
- Dep1 = rebar_app_info:source(Dep, Source),
- {ordsets:add_element(Dep1, SrcDepsAcc), BinaryDepsAcc}
- end, {ordsets:new(), []}, Deps).
-
--spec parse_goal(binary(), binary()) -> binary_dep().
+ ,ec_cnv:to_binary(Vsn)) | PkgDepsAcc]};
+ (Name, {SrcDepsAcc, PkgDepsAcc}) when is_atom(Name) ->
+ {SrcDepsAcc, [ec_cnv:to_binary(Name) | PkgDepsAcc]};
+ ({Name, Vsn, Source}, {SrcDepsAcc, PkgDepsAcc}) when is_tuple (Source) ->
+ Dep = new_dep(DepsDir, Name, Vsn, Source),
+ {[Dep | SrcDepsAcc], PkgDepsAcc};
+ ({Name, Vsn, Source, _Level}, {SrcDepsAcc, PkgDepsAcc}) when is_tuple (Source) ->
+ Dep = new_dep(DepsDir, Name, Vsn, Source),
+ {[Dep | SrcDepsAcc], PkgDepsAcc}
+ end, {[], []}, Deps).
+
+new_dep(DepsDir, Name, Vsn, Source) ->
+ Dir = ec_cnv:to_list(get_deps_dir(DepsDir, Name)),
+ {ok, Dep} = case rebar_app_info:discover(Dir) of
+ {ok, App} ->
+ {ok, App};
+ not_found ->
+ rebar_app_info:new(Name, Vsn, Dir)
+ end,
+ rebar_app_info:source(Dep, Source).
+
+-spec parse_goal(binary(), binary()) -> pkg_dep().
parse_goal(Name, Constraint) ->
case re:run(Constraint, "([^\\d]*)(\\d.*)", [{capture, [1,2], binary}]) of
{match, [<<>>, Vsn]} ->
@@ -236,9 +301,7 @@ info(Description) ->
"~n"
"Valid rebar.config options:~n"
" ~p~n"
- " ~p~n"
- "Valid command line options:~n"
- " deps_dir=\"deps\" (override default or rebar.config deps_dir)~n",
+ " ~p~n",
[
Description,
{deps_dir, "deps"},