-module(rebar_prv_deps). -behaviour(provider). -export([init/1, do/1, format_error/1]). -include("rebar.hrl"). -define(PROVIDER, deps). -define(DEPS, [install_deps]). -spec init(rebar_state:t()) -> {ok, rebar_state:t()}. init(State) -> State1 = rebar_state:add_provider( State, providers:create([ {name, ?PROVIDER}, {module, ?MODULE}, {bare, true}, {deps, ?DEPS}, {example, "rebar3 deps"}, {short_desc, "List dependencies"}, {desc, "List dependencies. Those not matching " "the config file are followed by " "an asterisk (*)."}, {opts, []}])), {ok, State1}. -spec do(rebar_state:t()) -> {ok, rebar_state:t()} | {error, string()}. do(State) -> Profiles = rebar_state:current_profiles(State), [display(State, Profile) || Profile <- Profiles], {ok, State}. -spec format_error(any()) -> iolist(). format_error(Reason) -> io_lib:format("~p", [Reason]). display(State, Profile = default) -> display_profile_deps(State, Profile), ?CONSOLE("", []); display(State, Profile) -> ?CONSOLE("-- ~p --", [Profile]), display_profile_deps(State, Profile), ?CONSOLE("", []). display_profile_deps(State, Profile) -> DepsDir = rebar_prv_install_deps:profile_dep_dir(State, Profile), ProfileDeps = rebar_state:get(State, {deps, Profile}, []), % ProfileDeps include those deps from rebar.lock that have been % removed from rebar.config ConfiguredDeps = [parse_dep_without_locks(DepsDir, Dep, State) || Dep <- ProfileDeps], LockedDepsMap = locked_deps_map(State, Profile), [display_dep(State, Dep, LockedDepsMap) || Dep <- ConfiguredDeps]. parse_dep_without_locks(DepsDir, Dep, State) -> ParsedDep = rebar_app_utils:parse_dep(Dep, root, DepsDir, State, [], 0), case Dep of {_Name, Src, Level} when is_tuple(Src), is_integer(Level) -> % This Dep is not in rebar.config but in rebar.lock rebar_app_info:source(ParsedDep, undefined); _ -> rebar_app_utils:expand_deps_sources(ParsedDep, State) end. locked_deps_map(State, Profile) -> ParsedDeps = rebar_state:get(State, {parsed_deps, Profile}, []), lists:foldl(fun(Dep, DepsIn) -> case rebar_app_info:is_lock(Dep) of true -> DepName = rebar_app_info:name(Dep), maps:put(rebar_utils:to_binary(DepName), Dep, DepsIn); _ -> DepsIn end end, maps:new(), ParsedDeps). display_dep(State, Dep, LockedDeps) -> Name = rebar_utils:to_binary(rebar_app_info:name(Dep)), NeedsUpdate = rebar_fetch:needs_update(Dep, State), Source = rebar_app_info:source(Dep), LockedSource = case maps:get(Name, LockedDeps, undefined) of undefined -> undefined; LockedDep -> rebar_app_info:source(LockedDep) end, display_dep_line(Name, NeedsUpdate, source_text(LockedSource), source_text(Source)). % Dep is a checkout display_dep_line(Name, _NeedsUpdate, _LockedSource, Source = checkout) -> ?CONSOLE("~ts* (~ts)", [Name, Source]); % Dep exists only in lock file display_dep_line(Name, _NeedsUpdate, LockedSource, _Source = undefined) -> ?CONSOLE("~ts* (locked ~ts <> none)", [Name, LockedSource]); % Dep not locked, report whether the disk copy matches the Source display_dep_line(Name, true, undefined, Source) -> ?CONSOLE("~ts* (~ts)", [Name, Source]); display_dep_line(Name, _, undefined, Source) -> ?CONSOLE("~ts (~ts)", [Name, Source]); % Dep locked, install_deps provider should have had updated the disk copy with % the locked version display_dep_line(Name, false, _LockedSource, Source) -> % dep locked and no need to update (LockedSource and Source might not match % because of one using {ref, X} and the other {tag, Y}) ?CONSOLE("~ts (locked ~ts)", [Name, Source]); display_dep_line(Name, _NeedsUpdate, LockedSource, Source) -> % dep locked with mismatching lock and config files ?CONSOLE("~ts* (locked ~ts <> ~ts)", [Name, LockedSource, Source]). source_text(Source) when is_list(Source); is_atom(Source) -> Source; source_text({pkg, _Name, Vsn, _Hash, _RepoConfig}) -> source_text({pkg, _Name, Vsn, _Hash}); source_text({pkg, _Name, Vsn, _Hash}) -> [<<"package">>, " ", rebar_utils:to_binary(Vsn)]; source_text(Source) when is_tuple(Source), tuple_size(Source) < 3 -> element(1, Source); source_text(Source) when is_tuple(Source) -> Type = element(1, Source), case element(3, Source) of {ref , Ref} -> SmallRef = case rebar_utils:to_binary(Ref) of <> -> <>; R -> R end, [atom_to_binary(Type, unicode), " source ", SmallRef]; {_ , Vsn} -> [atom_to_binary(Type, unicode), " source ", rebar_utils:to_binary(Vsn)]; _ -> [atom_to_binary(Type, unicode), " source"] end.