2016-09-08 11:34:42 +02:00
|
|
|
defmodule Mix.Tasks.Ejabberd.Deps.Tree do
|
|
|
|
use Mix.Task
|
|
|
|
|
|
|
|
alias Ejabberd.Config.EjabberdModule
|
|
|
|
|
|
|
|
@shortdoc "Lists all ejabberd modules and their dependencies"
|
|
|
|
|
|
|
|
@moduledoc """
|
|
|
|
Lists all ejabberd modules and their dependencies.
|
|
|
|
|
|
|
|
The project must have ejabberd as a dependency.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def run(_argv) do
|
|
|
|
# First we need to start manually the store to be available
|
|
|
|
# during the compilation of the config file.
|
|
|
|
Ejabberd.Config.Store.start_link
|
2019-08-05 10:55:30 +02:00
|
|
|
Ejabberd.Config.init(:ejabberd_config.path())
|
2016-09-08 11:34:42 +02:00
|
|
|
|
|
|
|
Mix.shell.info "ejabberd modules"
|
|
|
|
|
|
|
|
Ejabberd.Config.Store.get(:modules)
|
|
|
|
|> Enum.reverse # Because of how mods are stored inside the store
|
|
|
|
|> format_mods
|
|
|
|
|> Mix.shell.info
|
|
|
|
end
|
|
|
|
|
|
|
|
defp format_mods(mods) when is_list(mods) do
|
|
|
|
deps_tree = build_dependency_tree(mods)
|
|
|
|
mods_used_as_dependency = get_mods_used_as_dependency(deps_tree)
|
|
|
|
|
|
|
|
keep_only_mods_not_used_as_dep(deps_tree, mods_used_as_dependency)
|
|
|
|
|> format_mods_into_string
|
|
|
|
end
|
|
|
|
|
|
|
|
defp build_dependency_tree(mods) do
|
|
|
|
Enum.map mods, fn %EjabberdModule{module: mod, attrs: attrs} ->
|
|
|
|
deps = attrs[:dependency]
|
|
|
|
build_dependency_tree(mods, mod, deps)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-12-26 17:32:39 +01:00
|
|
|
defp build_dependency_tree(_mods, mod, []), do: %{module: mod, dependency: []}
|
2016-09-08 11:34:42 +02:00
|
|
|
defp build_dependency_tree(mods, mod, deps) when is_list(deps) do
|
|
|
|
dependencies = Enum.map deps, fn dep ->
|
|
|
|
dep_deps = get_dependencies_of_mod(mods, dep)
|
|
|
|
build_dependency_tree(mods, dep, dep_deps)
|
|
|
|
end
|
|
|
|
|
|
|
|
%{module: mod, dependency: dependencies}
|
|
|
|
end
|
|
|
|
|
|
|
|
defp get_mods_used_as_dependency(mods) when is_list(mods) do
|
|
|
|
Enum.reduce mods, [], fn(mod, acc) ->
|
|
|
|
case mod do
|
|
|
|
%{dependency: []} -> acc
|
|
|
|
%{dependency: deps} -> get_mod_names(deps) ++ acc
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defp get_mod_names([]), do: []
|
|
|
|
defp get_mod_names(mods) when is_list(mods), do: Enum.map(mods, &get_mod_names/1) |> List.flatten
|
|
|
|
defp get_mod_names(%{module: mod, dependency: deps}), do: [mod | get_mod_names(deps)]
|
|
|
|
|
|
|
|
defp keep_only_mods_not_used_as_dep(mods, mods_used_as_dep) do
|
|
|
|
Enum.filter mods, fn %{module: mod} ->
|
2019-01-02 13:22:35 +01:00
|
|
|
not (mod in mods_used_as_dep)
|
2016-09-08 11:34:42 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defp get_dependencies_of_mod(deps, mod_name) do
|
|
|
|
Enum.find(deps, &(Map.get(&1, :module) == mod_name))
|
|
|
|
|> Map.get(:attrs)
|
|
|
|
|> Keyword.get(:dependency)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp format_mods_into_string(mods), do: format_mods_into_string(mods, 0)
|
|
|
|
defp format_mods_into_string([], _indentation), do: ""
|
|
|
|
defp format_mods_into_string(mods, indentation) when is_list(mods) do
|
|
|
|
Enum.reduce mods, "", fn(mod, acc) ->
|
|
|
|
acc <> format_mods_into_string(mod, indentation)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
defp format_mods_into_string(%{module: mod, dependency: deps}, 0) do
|
|
|
|
"\n├── #{mod}" <> format_mods_into_string(deps, 2)
|
|
|
|
end
|
|
|
|
|
|
|
|
defp format_mods_into_string(%{module: mod, dependency: deps}, indentation) do
|
|
|
|
spaces = Enum.reduce 0..indentation, "", fn(_, acc) -> " " <> acc end
|
|
|
|
"\n│#{spaces}└── #{mod}" <> format_mods_into_string(deps, indentation + 4)
|
|
|
|
end
|
|
|
|
end
|