diff options
-rw-r--r-- | src/rebar_prv_deps.erl | 205 |
1 files changed, 115 insertions, 90 deletions
diff --git a/src/rebar_prv_deps.erl b/src/rebar_prv_deps.erl index 4d72133..0e5cb31 100644 --- a/src/rebar_prv_deps.erl +++ b/src/rebar_prv_deps.erl @@ -42,6 +42,10 @@ -define(PROVIDER, deps). -define(DEPS, [app_discovery]). +-record(dep, {name :: binary(), + vsn :: binary(), + source :: binary()}). + %% =================================================================== %% Public API %% =================================================================== @@ -54,7 +58,7 @@ init(State) -> deps = ?DEPS, example = "rebar deps", short_desc = "Install dependencies", - desc = info_help("Install dependencies"), + desc = info("Install dependencies"), opts = []}), {ok, State1}. @@ -62,34 +66,32 @@ init(State) -> do(State) -> %% Read in package index and dep graph {Packages, Graph} = get_packages(State), - PtDeps = rebar_state:get(State, pt_deps, []), - SrcDeps = rebar_state:get(State, src_deps, []), - {State1, _PtDeps1} = update_deps(State, PtDeps), - {State1, SrcDeps1} = update_deps(State, SrcDeps), - case rebar_state:get(State1, deps, []) of + case rebar_state:get(State, deps, []) of [] -> - {ok, rebar_state:set(State1, deps, SrcDeps1)}; + {ok, State}; Deps -> - Goals = lists:map(fun({Name, Vsn}) -> - {atom_to_binary(Name, utf8), Vsn}; - (Name) -> - atom_to_binary(Name, utf8) - end, Deps), - {ok, Solved} = rlx_depsolver:solve(Graph, Goals), - M = lists:map(fun({Name, Vsn}) -> - FmtVsn = to_binary(rlx_depsolver:format_version(Vsn)), - {ok, P} = dict:find({Name, FmtVsn}, Packages), - Link = proplists:get_value(<<"link">>, P), - {Name, Vsn, {Name - ,FmtVsn - ,Link}} - end, Solved), - - {State2, Deps1} = update_deps(State1, M), - State3 = rebar_state:set(State2, deps, Deps1), - - {ok, rebar_state:set(State3, goals, Goals)} + %% Split source deps form binary deps, needed to keep backwards compatibility + case parse_deps(Deps) of + {SrcDeps, []} -> + {State1, SrcDeps1} = update_src_deps(State, SrcDeps), + {ok, rebar_state:set(State1, deps, SrcDeps1)}; + {SrcDeps, Goals} -> + {State1, _SrcDeps1} = update_src_deps(State, SrcDeps), + {ok, Solved} = rlx_depsolver:solve(Graph, Goals), + M = lists:map(fun({Name, Vsn}) -> + FmtVsn = to_binary(rlx_depsolver:format_version(Vsn)), + {ok, P} = dict:find({Name, FmtVsn}, Packages), + Link = proplists:get_value(<<"link">>, P), + {Name, Vsn, {Name + ,FmtVsn + ,Link}} + end, Solved), + + {State2, Deps1} = update_deps(State1, M), + State3 = rebar_state:set(State2, deps, Deps1), + {ok, rebar_state:set(State3, goals, Goals)} + end end. update_deps(State, Deps) -> @@ -100,75 +102,64 @@ update_deps(State, Deps) -> UnbuiltApps = rebar_app_discover:find_unbuilt_apps([DepsDir]), FoundApps = rebar_app_discover:find_apps([DepsDir]), - %% Resolve deps and their dependencies - Deps1 = handle_deps(Deps, UnbuiltApps++FoundApps), + download_missing_deps(State, DepsDir, FoundApps, UnbuiltApps, Deps). - case download_missing_deps(State, DepsDir, FoundApps, UnbuiltApps, Deps1) of - {State1, []} -> - {State1, Deps1}; - {State1, _} -> - update_deps(State1, Deps1) - end. +update_src_deps(State, Deps) -> + DepsDir = get_deps_dir(State), -handle_deps(Deps, Found) -> - NewDeps = - lists:foldl(fun(X, DepsAcc) -> - C = rebar_config:consult(rebar_app_info:dir(X)), - S = rebar_state:new(rebar_state:new(), C, rebar_app_info:dir(X)), - LocalDeps = rebar_state:get(S, deps, []), - [LocalDeps | DepsAcc] - end, [], Found), - NewDeps1 = lists:flatten(NewDeps), - - %% Weed out duplicates - lists:umerge(fun(A, B) -> - dep_name(A) =:= dep_name(B) - end, lists:usort(Deps), lists:usort(NewDeps1)). - -dep_name({Name, _, _}) -> - Name; -dep_name({Name, _}) -> - Name; -dep_name(Name) -> - Name. + %% Find available apps to fulfill dependencies + %% Should only have to do this once, not every iteration + UnbuiltApps = rebar_app_discover:find_unbuilt_apps([DepsDir]), + FoundApps = rebar_app_discover:find_apps([DepsDir]), + %% Resolve deps and their dependencies + Deps1 = handle_src_deps(Deps, UnbuiltApps++FoundApps), + {State1, Missing} = download_missing_deps(State, DepsDir, FoundApps, UnbuiltApps, Deps1), + case dict:is_empty(Missing) of + true -> + {State1, Deps1}; + false -> + update_src_deps(State1, Missing) + end. -to_binary(X) when is_binary(X) -> - X; -to_binary(X) when is_atom(X) -> - atom_to_binary(X, utf8); -to_binary(X) when is_list(X) -> - iolist_to_binary(X). +handle_src_deps(Deps, Found) -> + lists:foldl(fun(X, DepsAcc) -> + C = rebar_config:consult(rebar_app_info:dir(X)), + S = rebar_state:new(rebar_state:new(), C, rebar_app_info:dir(X)), + {ParsedDeps, _Goals} = parse_deps(rebar_state:get(S, deps, [])), + dict:merge(fun(_K, V1, _V2) -> V1 end, DepsAcc, ParsedDeps) + end, Deps, Found). download_missing_deps(State, DepsDir, Found, Unbuilt, Deps) -> - Missing = lists:filter(fun(X) -> - not lists:any(fun(F) -> - to_binary(dep_name(X)) =:= to_binary(rebar_app_info:name(F)) - end, Found++Unbuilt) - end, Deps), - lists:map(fun({DepName, _DepVsn, DepSource}) -> - TargetDir = get_deps_dir(DepsDir, DepName), - case filelib:is_dir(TargetDir) of - true -> - ok; - false -> - ?INFO("Fetching ~s ~s~n", [DepName - ,element(2, DepSource)]), - rebar_fetch:download_source(TargetDir, DepSource), - case rebar_app_discover:find_unbuilt_apps([TargetDir]) of - [AppSrc] -> - C = rebar_config:consult(rebar_app_info:dir(AppSrc)), - S = rebar_state:new(rebar_state:new() - ,C - ,rebar_app_info:dir(AppSrc)), - _AppInfo1 = rebar_prv_app_builder:build(S, AppSrc); - [] -> - [] - end - end - end, Missing), - - {State, []}. + Missing = + dict:filter(fun(Key, _) -> + not lists:any(fun(F) -> + Key =:= to_binary(rebar_app_info:name(F)) + end, Found++Unbuilt) + end, Deps), + dict:map(fun(_Key, #dep{name=Name, source=Source}) -> + TargetDir = get_deps_dir(DepsDir, Name), + case filelib:is_dir(TargetDir) of + true -> + ok; + false -> + ?INFO("Fetching ~s ~s~n", [Name + ,element(2, Source)]), + rebar_fetch:download_source(TargetDir, Source), + case rebar_app_discover:find_unbuilt_apps([TargetDir]) of + [AppSrc] -> + C = rebar_config:consult(rebar_app_info:dir(AppSrc)), + S = rebar_state:new(rebar_state:new() + ,C + ,rebar_app_info:dir(AppSrc)), + rebar_prv_app_builder:build(S, AppSrc); + [] -> + [] + end + end + end, Missing), + + {State, Missing}. %% set REBAR_DEPS_DIR and ERL_LIBS environment variables setup_env(State) -> @@ -200,6 +191,40 @@ get_deps_dir(DepsDir, App) -> %% Internal functions %% =================================================================== +new({Name, Vsn, Source})-> + #dep{name=to_binary(Name), vsn=to_binary(Vsn), source=Source}; +new(Name) -> + #dep{name=to_binary(Name)}. + +-spec name(record(dep)) -> binary(). +name(#dep{name=Name}) -> + Name. + +-spec vsn(record(dep)) -> binary(). +vsn(#dep{vsn=Vsn}) -> + Vsn. + +-spec source(record(dep)) -> tuple(). +source(#dep{source=Source}) -> + Source. + +to_binary(X) when is_binary(X) -> + X; +to_binary(X) when is_atom(X) -> + atom_to_binary(X, utf8); +to_binary(X) when is_list(X) -> + iolist_to_binary(X). + +parse_deps(Deps) -> + lists:foldl(fun({Name, Vsn}, {SrcDepsAcc, GoalsAcc}) -> + {SrcDepsAcc, [{to_binary(Name), to_binary(Vsn)} | GoalsAcc]}; + (Name, {SrcDepsAcc, GoalsAcc}) when is_atom(Name) -> + {SrcDepsAcc, [to_binary(Name) | GoalsAcc]}; + (SrcDep, {SrcDepsAcc, GoalsAcc}) -> + Dep = new(SrcDep), + {dict:store(name(Dep), Dep, SrcDepsAcc), GoalsAcc} + end, {dict:new(), []}, Deps). + get_packages(State) -> RebarDir = rebar_state:get(State, global_rebar_dir, filename:join(os:getenv("HOME"), ".rebar")), PackagesFile = filename:join(RebarDir, "packages"), @@ -217,7 +242,7 @@ get_packages(State) -> {[], rlx_depsolver:new()} end. -info_help(Description) -> +info(Description) -> io_lib:format("~s.~n" "~n" "Valid rebar.config options:~n" |