#!/usr/bin/env escript
|
|
%% -*- erlang -*-
|
|
%%% Run with 'escript app_deps.erl'
|
|
%%% Change the path in filelib:wildcard/1 as required to capture
|
|
%%% all your dependencies.
|
|
%%%
|
|
%%% Rectangular nodes will represent library apps (no processes
|
|
%%% involved) and the circular nodes will represent regular apps.
|
|
%%% An arrow going from 'A -> B' means 'A depends on B'.
|
|
%%%
|
|
%%% This script depends on graphviz being present on the system.
|
|
-module(app_deps).
|
|
-export([main/1]).
|
|
|
|
main(_) ->
|
|
AppFiles = filelib:wildcard("deps/*/ebin/*.app")
|
|
++
|
|
filelib:wildcard("apps/*/ebin/*.app")
|
|
++
|
|
filelib:wildcard("ebin/*.app")
|
|
++
|
|
filelib:wildcard("_build/default/lib/*/ebin/*.app"),
|
|
to_graphviz(read_deps(AppFiles)).
|
|
|
|
read_deps(AppFiles) ->
|
|
[{App,
|
|
proplists:get_value(applications, Props, []),
|
|
apptype(Props)}
|
|
|| {ok, [{_,App,Props}]} <-
|
|
[file:consult(AppFile) || AppFile <- AppFiles]].
|
|
|
|
apptype(Props) ->
|
|
case proplists:get_value(mod, Props) of
|
|
undefined -> library;
|
|
_ -> regular
|
|
end.
|
|
|
|
to_graphviz(Deps) ->
|
|
AllApps = lists:usort(lists:flatten(
|
|
[[{App,Type},DepList] || {App,DepList,Type} <- Deps]
|
|
)),
|
|
Bytes = ["digraph G { ",
|
|
"K=0.25; ratio=0.75; overlap=\"9:prism\"; ",
|
|
[io_lib:format("~p [shape=box] ", [App])
|
|
|| App <- libapps(AllApps -- [kernel,stdlib])],
|
|
[[io_lib:format("~p->~p ", [App,Dep])
|
|
|| Dep <- DepList -- [kernel, stdlib]]
|
|
|| {App, DepList, _} <- Deps],
|
|
"}"],
|
|
file:write_file("app-deps.dot", Bytes),
|
|
os:cmd("dot app-deps.dot -Tpng -o app-deps.png").
|
|
|
|
libapps([]) -> [];
|
|
libapps([{App,library}|Apps]) -> [App|libapps(Apps)];
|
|
libapps([{_,_}|Apps]) -> libapps(Apps);
|
|
libapps([App|Apps]) ->
|
|
Dir = case code:lib_dir(App) of
|
|
{error, _} -> ""; % not an OTP app
|
|
DirPath -> DirPath
|
|
end,
|
|
Path = filename:join([Dir, "ebin", atom_to_list(App)++".app"]),
|
|
case lists:prefix(code:lib_dir(), Path) of
|
|
false ->
|
|
[App|libapps(Apps)]; % not OTP app, we don't care
|
|
true -> % deps of OTP deps: we don't care either.
|
|
{ok, [{_,App,Props}]} = file:consult(Path),
|
|
case apptype(Props) of
|
|
library -> [App | libapps(Apps)];
|
|
regular -> libapps(Apps)
|
|
end
|
|
end.
|