diff options
Diffstat (limited to 'src/rebar_prv_install_deps.erl')
-rw-r--r-- | src/rebar_prv_install_deps.erl | 329 |
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"}, |