ソースを参照

wip: reworking deps fetching/sorting

pull/3/head
Tristan Sloughter 10年前
コミット
ac0d726bb8
4個のファイルの変更115行の追加135行の削除
  1. +24
    -0
      src/rebar_app_info.erl
  2. +48
    -111
      src/rebar_prv_install_deps.erl
  3. +22
    -2
      src/rebar_state.erl
  4. +21
    -22
      src/rebar_topo.erl

+ 24
- 0
src/rebar_app_info.erl ファイルの表示

@ -1,6 +1,8 @@
-module(rebar_app_info).
-export([new/0,
new/1,
new/2,
new/3,
name/1,
name/2,
@ -15,6 +17,8 @@
original_vsn/1,
original_vsn/2,
ebin_dir/1,
deps/1,
deps/2,
dir/1,
dir/2,
source/1,
@ -28,6 +32,7 @@
config :: rebar_config:config() | undefined,
original_vsn :: string(),
app_details :: list(),
deps=[] :: list(),
dir :: file:name(),
source :: string() | undefined}).
@ -45,6 +50,17 @@
new() ->
{ok, #app_info_t{}}.
-spec new(atom() | binary() | string()) ->
{ok, t()}.
new(AppName) ->
{ok, #app_info_t{name=ec_cnv:to_binary(AppName)}}.
-spec new(atom() | binary() | string(), string()) ->
{ok, t()}.
new(AppName, Vsn) ->
{ok, #app_info_t{name=ec_cnv:to_binary(AppName),
original_vsn=Vsn}}.
%% @doc build a complete version of the app info with all fields set.
-spec new(atom() | binary() | string(), string(), file:name()) ->
{ok, t()}.
@ -101,6 +117,14 @@ original_vsn(#app_info_t{original_vsn=Vsn}) ->
original_vsn(AppInfo=#app_info_t{}, Vsn) ->
AppInfo#app_info_t{original_vsn=Vsn}.
-spec deps(t()) -> list().
deps(#app_info_t{deps=Deps}) ->
Deps.
-spec deps(t(), list()) -> t().
deps(AppInfo=#app_info_t{}, Deps) ->
AppInfo#app_info_t{deps=Deps}.
-spec dir(t()) -> file:name().
dir(#app_info_t{dir=Dir}) ->
Dir.

+ 48
- 111
src/rebar_prv_install_deps.erl ファイルの表示

@ -42,10 +42,6 @@
-define(PROVIDER, install_deps).
-define(DEPS, [app_discovery]).
-record(dep, {name :: binary(),
vsn :: binary(),
source :: binary()}).
%% ===================================================================
%% Public API
%% ===================================================================
@ -74,30 +70,30 @@ do(State) ->
Deps ->
%% Split source deps form binary deps, needed to keep backwards compatibility
{SrcDeps, Goals} = parse_deps(Deps),
case update_src_deps(State, SrcDeps, Goals, [], []) of
{State1, SrcDeps1, [], Locked} ->
{ok, rebar_state:set(State1, locks, Locked)};
{State1, SrcDeps1, Goals1, Locked} ->
{ok, Solved} = rlx_depsolver:solve(Graph, Goals1),
M = lists:map(fun({Name, Vsn}) ->
FmtVsn = ec_cnv:to_binary(rlx_depsolver:format_version(Vsn)),
{ok, P} = dict:find({Name, FmtVsn}, Packages),
Link = proplists:get_value(<<"link">>, P),
#dep{name=Name,
vsn=FmtVsn,
source={Name
,FmtVsn
,Link}}
end, Solved),
{State2, Deps1, _, Locked2} = update_deps(State1, M),
State3 = rebar_state:set(State2, locks, Locked++Locked2),
{ok, rebar_state:set(State3, goals, Goals1)}
end
State1 = rebar_state:src_deps(rebar_state:goals(State, Goals), SrcDeps),
State2 = update_src_deps(State1),
Goals1 = rebar_state:goals(State2),
{ok, Solved} = rlx_depsolver:solve(Graph, Goals1),
Final = lists:map(fun({Name, Vsn}) ->
FmtVsn = ec_cnv:to_binary(rlx_depsolver:format_version(Vsn)),
{ok, P} = dict:find({Name, FmtVsn}, Packages),
PkgDeps = proplists:get_value(<<"deps">>, P),
Link = proplists:get_value(<<"link">>, P),
Source = {Name, FmtVsn, Link},
{ok, AppInfo} = rebar_app_info:new(Name, FmtVsn),
AppInfo1 = rebar_app_info:deps(AppInfo, PkgDeps),
rebar_app_info:source(AppInfo1, Source)
end, Solved),
ProjectApps = lists:map(fun(X) ->
rebar_app_info:deps(X, [rebar_app_info:name(Y) || Y <- SrcDeps] ++ [element(1, G) || G <- Goals])
end, rebar_state:apps_to_build(State2)),
FinalDeps = ProjectApps ++ rebar_state:src_deps(State2) ++ Final,
{ok, Sort} = rebar_topo:sort_apps(FinalDeps),
[io:format("Build ~p~n", [rebar_app_info:name(A)]) || A <- Sort],
{ok, State2}
end;
Locks ->
Locks1 = [new(Lock) || Lock <- Locks],
{State2, _, _, _} = update_deps(State, Locks1),
{ok, State2}
_Locks ->
{ok, State}
end.
%% set REBAR_DEPS_DIR and ERL_LIBS environment variables
@ -130,90 +126,30 @@ get_deps_dir(DepsDir, App) ->
%% Internal functions
%% ===================================================================
new({Name, Vsn, Source}) when is_tuple(Source) ->
#dep{name=ec_cnv:to_binary(Name), vsn=ec_cnv:to_binary(Vsn), source=Source};
new({Name, Vsn, Source}) when is_binary(Source) ->
#dep{name=ec_cnv:to_binary(Name)
,vsn=ec_cnv:to_binary(Vsn)
,source={ec_cnv:to_binary(Name), ec_cnv:to_binary(Vsn), Source}};
new(Name) ->
#dep{name=ec_cnv:to_binary(Name)}.
%% Fetch missing binary deps
update_deps(State, Deps) ->
DepsDir = get_deps_dir(State),
%% 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]),
download_missing_deps(State, DepsDir, UnbuiltApps++FoundApps, Deps).
%% Find source deps to build and download
update_src_deps(State, Deps, Goals, SrcApps, Locked) ->
DepsDir = get_deps_dir(State),
%% 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, NewGoals} = handle_src_deps(Deps, UnbuiltApps++FoundApps, Goals),
case download_missing_deps(State, DepsDir, UnbuiltApps++FoundApps, Deps1) of
{State1, [], SrcApps1, []} ->
Locked1 = lists:map(fun(AppSrc) ->
Source = rebar_app_info:source(AppSrc),
Name = rebar_app_info:name(AppSrc),
C = rebar_config:consult(rebar_app_info:dir(AppSrc)),
S = rebar_state:new(rebar_state:new()
,C
,rebar_app_info:dir(AppSrc)),
TargetDir = get_deps_dir(DepsDir, Name),
Ref = rebar_fetch:current_ref(binary_to_list(TargetDir), Source),
AppInfo = rebar_prv_app_builder:build(S, AppSrc),
{Name
,ec_cnv:to_binary(rebar_app_info:original_vsn(AppInfo))
,erlang:setelement(3, Source, Ref)}
end, SrcApps1++SrcApps),
{State1, Deps1, NewGoals, Locked1++Locked};
{State1, Missing, SrcApps1, Locked1} ->
update_src_deps(State1, Missing, NewGoals, SrcApps1++SrcApps, Locked1++Locked)
update_deps(State) ->
State.
update_src_deps(State) ->
SrcDeps = rebar_state:src_deps(State),
case lists:foldl(fun(AppInfo, {StateAcc, SrcDepsAcc, GoalsAcc}) ->
ok = maybe_fetch(StateAcc, 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, []),
{SrcDeps1, Goals} = parse_deps(Deps),
NewSrcDeps = SrcDeps1++SrcDepsAcc,
{StateAcc, NewSrcDeps, Goals++GoalsAcc}
end, {State, [], rebar_state:goals(State)}, SrcDeps) of
{State1, [], NewGoals} ->
rebar_state:goals(State1, NewGoals);
{State1, NewSrcDeps, NewGoals}->
State2 = rebar_state:src_deps(rebar_state:goals(State1, NewGoals), SrcDeps++NewSrcDeps),
update_src_deps(State2)
end.
%% Collect deps of new deps
handle_src_deps(Deps, Found, Goals) ->
lists:foldl(fun(X, {DepsAcc, GoalsAcc}) ->
C = rebar_config:consult(rebar_app_info:dir(X)),
S = rebar_state:new(rebar_state:new(), C, rebar_app_info:dir(X)),
{ParsedDeps, NewGoals} = parse_deps(rebar_state:get(S, deps, [])),
{ParsedDeps++DepsAcc, NewGoals++GoalsAcc}
end, {Deps, Goals}, Found).
%% Fetch missing deps from source
download_missing_deps(State, DepsDir, Found, Deps) ->
Missing =
lists:filter(fun(#dep{name=Name}) ->
not lists:any(fun(F) ->
Name =:= rebar_app_info:name(F)
end, Found)
end, Deps),
{SrcApps, Locked} = lists:foldl(fun(Dep=#dep{name=Name, source=Source}, {SrcAppsAcc, LockedAcc}) ->
TargetDir = get_deps_dir(DepsDir, Name),
?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] ->
{[rebar_app_info:source(AppSrc, Source) | SrcAppsAcc], LockedAcc};
[] ->
{SrcAppsAcc, [Source | LockedAcc]}
end
end, {[], []}, Missing),
{State, Missing, lists:reverse(SrcApps), Locked}.
maybe_fetch(_State, _Dep) ->
ok.
parse_deps(Deps) ->
lists:foldl(fun({Name, Vsn}, {SrcDepsAcc, GoalsAcc}) ->
@ -221,9 +157,10 @@ parse_deps(Deps) ->
,ec_cnv:to_binary(Vsn)) | GoalsAcc]};
(Name, {SrcDepsAcc, GoalsAcc}) when is_atom(Name) ->
{SrcDepsAcc, [ec_cnv:to_binary(Name) | GoalsAcc]};
(SrcDep, {SrcDepsAcc, GoalsAcc}) ->
Dep = new(SrcDep),
{[Dep | SrcDepsAcc], GoalsAcc}
({Name, _, Source}, {SrcDepsAcc, GoalsAcc}) ->
{ok, Dep} = rebar_app_info:new(Name),
Dep1 = rebar_app_info:source(Dep, Source),
{[Dep1 | SrcDepsAcc], GoalsAcc}
end, {[], []}, Deps).
parse_goal(Name, Constraint) ->

+ 22
- 2
src/rebar_state.erl ファイルの表示

@ -9,6 +9,9 @@
apps_to_build/1, apps_to_build/2,
goals/1, goals/2,
src_deps/1, src_deps/2,
providers/1, providers/2, add_provider/2]).
-include("rebar.hrl").
@ -29,6 +32,7 @@
envs = new_env() :: rebar_dict(),
command_args = [] :: list(),
src_deps = [] :: [rebar_app_info:t()],
apps = dict:new() :: rebar_dict(),
goals = [],
providers = [],
@ -101,11 +105,27 @@ command_args(#state_t{command_args=CmdArgs}) ->
command_args(State, CmdArgs) ->
State#state_t{command_args=CmdArgs}.
goals(#state_t{goals=Goals}) ->
Goals.
goals(State=#state_t{goals=Goals}, NewGoals) when is_list(Goals) ->
State#state_t{goals=NewGoals};
goals(State=#state_t{goals=Goals}, Goal) ->
State#state_t{goals=[Goal | Goals]}.
src_deps(#state_t{src_deps=SrcDeps}) ->
SrcDeps.
src_deps(State=#state_t{src_deps=SrcDeps}, NewSrcDeps) when is_list(SrcDeps) ->
State#state_t{src_deps=NewSrcDeps};
src_deps(State=#state_t{src_deps=SrcDeps}, SrcDep) ->
State#state_t{src_deps=[SrcDep | SrcDeps]}.
apps_to_build(#state_t{apps_to_build=Apps}) ->
Apps.
apps_to_build(State=#state_t{apps_to_build=Apps}, Apps) when is_list(Apps) ->
State#state_t{apps_to_build=Apps};
apps_to_build(State=#state_t{apps_to_build=Apps}, NewApps) when is_list(NewApps) ->
State#state_t{apps_to_build=NewApps};
apps_to_build(State=#state_t{apps_to_build=Apps}, App) ->
State#state_t{apps_to_build=[App | Apps]}.

+ 21
- 22
src/rebar_topo.erl ファイルの表示

@ -54,8 +54,8 @@
%% applications. This implies that you have already done the
%% constraint solve before you pass the list of apps here to be
%% sorted.
-spec sort_apps([rlx_app_info:t()]) ->
{ok, [rlx_app_info:t()]} |
-spec sort_apps([rebar_app_info:t()]) ->
{ok, [rebar_app_info:t()]} |
relx:error().
sort_apps(Apps) ->
Pairs = apps_to_pairs(Apps),
@ -78,9 +78,9 @@ format_error({cycle, Pairs}) ->
"before we can continue:\n",
case Pairs of
[{P1, P2}] ->
[rlx_util:indent(2), erlang:atom_to_list(P2), "->", erlang:atom_to_list(P1)];
[rebar_util:indent(2), erlang:atom_to_list(P2), "->", erlang:atom_to_list(P1)];
[{P1, P2} | Rest] ->
[rlx_util:indent(2), erlang:atom_to_list(P2), "->", erlang:atom_to_list(P1),
[rebar_util:indent(2), erlang:atom_to_list(P2), "->", erlang:atom_to_list(P1),
[["-> ", erlang:atom_to_list(PP2), " -> ", erlang:atom_to_list(PP1)] || {PP1, PP2} <- Rest]];
[] ->
[]
@ -89,28 +89,27 @@ format_error({cycle, Pairs}) ->
%%====================================================================
%% Internal Functions
%%====================================================================
-spec names_to_apps([atom()], [rlx_app_info:t()]) -> [rlx_app_info:t()].
-spec names_to_apps([atom()], [rebar_app_info:t()]) -> [rebar_app_info:t()].
names_to_apps(Names, Apps) ->
[find_app_by_name(Name, Apps) || Name <- Names].
-spec find_app_by_name(atom(), [rlx_app_info:t()]) -> rlx_app_info:t().
-spec find_app_by_name(atom(), [rebar_app_info:t()]) -> rebar_app_info:t().
find_app_by_name(Name, Apps) ->
{ok, App1} =
ec_lists:find(fun(App) ->
rlx_app_info:name(App) =:= Name
rebar_app_info:name(App) =:= Name
end, Apps),
App1.
-spec apps_to_pairs([rlx_app_info:t()]) -> [pair()].
-spec apps_to_pairs([rebar_app_info:t()]) -> [pair()].
apps_to_pairs(Apps) ->
lists:flatten([app_to_pairs(App) || App <- Apps]).
-spec app_to_pairs(rlx_app_info:t()) -> [pair()].
-spec app_to_pairs(rebar_app_info:t()) -> [pair()].
app_to_pairs(App) ->
[{DepApp, rlx_app_info:name(App)} ||
[{DepApp, rebar_app_info:name(App)} ||
DepApp <-
rlx_app_info:active_deps(App) ++
rlx_app_info:library_deps(App)].
rebar_app_info:deps(App)].
%% @doc Iterate over the system. @private
@ -195,8 +194,8 @@ topo_pairs_cycle_test() ->
sort(Pairs)).
topo_apps_cycle_test() ->
{ok, App1} = rlx_app_info:new(app1, "0.1", "/no-dir", [app2], [stdlib]),
{ok, App2} = rlx_app_info:new(app2, "0.1", "/no-dir", [app1], []),
{ok, App1} = rebar_app_info:new(app1, "0.1", "/no-dir", [app2], [stdlib]),
{ok, App2} = rebar_app_info:new(app2, "0.1", "/no-dir", [app1], []),
Apps = [App1, App2],
?assertMatch({error, {_, {cycle, [{app2,app1},{app1,app2}]}}},
sort_apps(Apps)).
@ -204,16 +203,16 @@ topo_apps_cycle_test() ->
topo_apps_good_test() ->
Apps = [App ||
{ok, App} <-
[rlx_app_info:new(app1, "0.1", "/no-dir", [app2, zapp1], [stdlib, kernel]),
rlx_app_info:new(app2, "0.1", "/no-dir", [app3], []),
rlx_app_info:new(app3, "0.1", "/no-dir", [kernel], []),
rlx_app_info:new(zapp1, "0.1", "/no-dir", [app2,app3,zapp2], []),
rlx_app_info:new(stdlib, "0.1", "/no-dir", [], []),
rlx_app_info:new(kernel, "0.1", "/no-dir", [], []),
rlx_app_info:new(zapp2, "0.1", "/no-dir", [], [])]],
[rebar_app_info:new(app1, "0.1", "/no-dir", [app2, zapp1], [stdlib, kernel]),
rebar_app_info:new(app2, "0.1", "/no-dir", [app3], []),
rebar_app_info:new(app3, "0.1", "/no-dir", [kernel], []),
rebar_app_info:new(zapp1, "0.1", "/no-dir", [app2,app3,zapp2], []),
rebar_app_info:new(stdlib, "0.1", "/no-dir", [], []),
rebar_app_info:new(kernel, "0.1", "/no-dir", [], []),
rebar_app_info:new(zapp2, "0.1", "/no-dir", [], [])]],
{ok, Sorted} = sort_apps(Apps),
?assertMatch([stdlib, kernel, zapp2,
app3, app2, zapp1, app1],
[rlx_app_info:name(App) || App <- Sorted]).
[rebar_app_info:name(App) || App <- Sorted]).
-endif.

読み込み中…
キャンセル
保存