aboutsummaryrefslogtreecommitdiffstats
path: root/lib/erl_docgen/priv/bin/xref_mod_app.escript
blob: 4a418fe144a10fea87ac0e03579882ad0b68cc9f (plain) (blame)
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
105
106
107
108
109
110
111
#!/usr/bin/env escript
%% -*- erlang -*-
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2010-2016. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%

%%% Find all applications and all modules given a root directory.
%%% Output an XML file that can be used for finding which application
%%% a given module belongs to.
%%%
%%% Options:
%%%
%%%  "-topdir <D>"  Applications are found under D/lib/.
%%%                 The default value is $ERL_TOP.
%%%
%%%  "-outfile <F>" Output is written onto F.
%%%                 The default value is "mod2app.xml".
%%%
%%% The output file has the following format:
%%%
%%% <?xml version="1.0"?>
%%% <mod2app>
%%%    <module name="ModName1">AppName1</module>
%%%    ...
%%% <mod2app>
%%%
%%% meaning that module ModName1 resides in application AppName1.

main(Args) ->
    case catch parse(Args, os:getenv("ERL_TOP"), "mod2app.xml") of
        {ok, TopDir, OutFile} ->
            case modapp(TopDir) of
                [] ->
                    io:format("no applications found\n"),
                    halt(3);
                MA ->
                    Layout = layout(MA),
                    XML = xmerl:export_simple(Layout, xmerl_xml),
                    write_file(XML, OutFile)
            end;
        {error, Msg} ->
	    io:format("~s\n", [Msg]),
	    usage()
    end.

parse(["-topdir", TopDir | Opts], _, OutFile) ->
    parse(Opts, TopDir, OutFile);
parse(["-outfile", OutFile | Opts], TopDir, _) ->
    parse(Opts, TopDir, OutFile);
parse([], TopDir, OutFile) ->
    {ok, TopDir, OutFile};
parse([Opt | _], _, _) ->
    {error, io_lib:format("Bad option: ~p", [Opt])}.

usage() ->
    io:format("usage:  ~s [-topdir <dir>] [-outfile <file>]\n",
             [escript:script_name()]),
    halt(1).

modapp(TopDir) ->
    AppDirs = filelib:wildcard(filename:join([TopDir,"lib","*"])),
    AM = [appmods(D) || D <- AppDirs],
    ERTS = [preloaded(TopDir) || lists:keyfind("erts", 1, AM) =:= false],
    lists:keysort(1, [{M,A} || {A,Ms} <- ERTS++AM, M <- Ms]).

preloaded(TopDir) ->
    {"preloaded",Mods} = appmods(filename:join([TopDir,"erts","preloaded"])),
    {"erts",Mods}.

%% It's OK if too much data is generated as long as all applications
%% and all modules are mentioned.
appmods(D) ->
    ErlFiles = filelib:wildcard(filename:join([D,"src","**","*.erl"])),
    AppV = filename:basename(D),
    App = case string:rstr(AppV, "-") of
              0 -> AppV;
              P -> string:sub_string(AppV, 1, P-1)
          end,
    {App, [filename:basename(EF, ".erl") || EF <- ErlFiles]}.

-define(IND(N), lists:duplicate(N, $\s)).
-define(NL, "\n").

layout(MAL) ->
    ML = lists:append([[?IND(2),{module,[{name,M}],[A]},?NL] || {M,A} <- MAL]),
    [?NL,{mod2app,[?NL|ML]},?NL].

write_file(Text, File) ->
    case file:open(File, [write]) of
	{ok, FD} ->
	    io:put_chars(FD, Text),
	    ok = file:close(FD);
	{error, R} ->
	    R1 = file:format_error(R),
	    io:format("could not write file '~s': ~s\n", [File, R1]),
	    halt(2)
    end.