-
Notifications
You must be signed in to change notification settings - Fork 3.3k
/
app.tree.ex
104 lines (78 loc) · 2.93 KB
/
app.tree.ex
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
defmodule Mix.Tasks.App.Tree do
use Mix.Task
@shortdoc "Prints the application tree"
@recursive true
@moduledoc """
Prints the application tree.
$ mix app.tree --exclude logger --exclude elixir
If no application is given, it uses the current application defined
in the `mix.exs` file.
## Command line options
* `--exclude` - exclude applications which you do not want to see printed.
`kernel`, `stdlib` and `compiler` are always excluded from the tree.
* `--format` - Can be set to one of either:
* `pretty` - uses Unicode code points for formatting the tree.
This is the default except on Windows.
* `plain` - does not use Unicode code points for formatting the tree.
This is the default on Windows.
* `dot` - produces a DOT graph description of the application tree
in `app_tree.dot` in the current directory.
Warning: this will overwrite any previously generated file.
"""
@default_excluded [:kernel, :stdlib, :compiler]
@impl true
def run(args) do
Mix.Task.run("compile", args)
{app, opts} =
case OptionParser.parse!(args, strict: [exclude: :keep, format: :string]) do
{opts, []} ->
app =
Mix.Project.config()[:app] ||
Mix.raise("no application given and none found in mix.exs file")
{app, opts}
{opts, [app]} ->
{String.to_atom(app), opts}
end
excluded = Keyword.get_values(opts, :exclude) |> Enum.map(&String.to_atom/1)
excluded = @default_excluded ++ excluded
callback = fn {app, type} ->
if load(app, type) do
{{app, type(type)}, children_for(app, excluded)}
else
{{app, "(optional - missing)"}, []}
end
end
if opts[:format] == "dot" do
root = [{app, :normal}]
Mix.Utils.write_dot_graph!("app_tree.dot", "application tree", root, callback, opts)
"""
Generated "app_tree.dot" in the current directory. To generate a PNG:
dot -Tpng app_tree.dot -o app_tree.png
For more options see http://www.graphviz.org/.
"""
|> String.trim_trailing()
|> Mix.shell().info()
else
Mix.Utils.print_tree([{app, :normal}], callback, opts)
end
end
defp load(app, type) do
case Application.ensure_loaded(app) do
:ok -> true
_ when type == :optional -> false
_ -> Mix.raise("could not find application #{app}")
end
end
defp children_for(app, excluded) do
apps = Application.spec(app, :applications) -- excluded
included_apps = Application.spec(app, :included_applications) -- excluded
optional_apps = Application.spec(app, :optional_applications) || []
Enum.sort(
Enum.map(apps, &{&1, if(&1 in optional_apps, do: :optional, else: :normal)}) ++
Enum.map(included_apps, &{&1, :included})
)
end
defp type(:normal), do: nil
defp type(:included), do: "(included)"
defp type(:optional), do: "(optional)"
end