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 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
|
#!/usr/bin/env escript
%% -*- erlang -*-
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2011-2021. 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%
%%% <script> [-I<dir>]... [-o<dir>] [-module Module] [File]
%%%
%%% Use EDoc and the layout module 'docgen_otp_specs' to create an XML file
%%% containing Dialyzer types and specifications (-type, -spec).
%%%
%%% Options:
%%%
%%% "-o<dir>" The output directory for the created file.
%%% Default is ".".
%%% "-I<dir>" Directory to be searched when including a file.
%%% "-module Module"
%%% Module name to use when there is no File argument.
%%% A empty specifications file will be created.
%%% Exactly one of -module Module and File must be given.
%%%
%%% The name of the generated file is "specs_<module>.xml". Its exact
%%% format is not further described here.
main(Args) ->
case catch parse(Args, [], ".", no_module) of
{ok, FileSpec, InclFs, Dir} ->
call_edoc(FileSpec, InclFs, Dir);
{error, Msg} ->
io:format("~s\n", [Msg]),
usage()
end.
parse(["-o"++Dir | Opts], InclFs, _, Module) ->
parse(Opts, InclFs, Dir, Module);
parse(["-I"++I | Opts], InclFs, Dir, Module) ->
Is = filelib:wildcard(I),
parse(Opts, Is ++ InclFs, Dir, Module);
parse(["-module", Module | Opts], InclFs, Dir, _) ->
parse(Opts, InclFs, Dir, Module);
parse([File], InclFs, Dir, no_module) ->
{ok, {file, File}, lists:reverse(InclFs), Dir};
parse([_], _, _, _) ->
{error, io_lib:format("Cannot have both -module option and file", [])};
parse([], _, _, no_module) ->
{error, io_lib:format("Missing -module option or file", [])};
parse([], InclFs, Dir, Module) ->
{ok, {module, Module}, lists:reverse(InclFs), Dir};
parse(Args, _, _, _) ->
{error, io_lib:format("Bad arguments: ~p", [Args])}.
usage() ->
io:format("usage: ~s [-I<include_dir>]... [-o<out_dir>] "
"[-module <module>] [file]\n", [escript:script_name()]),
halt(1).
call_edoc(FileSpec, InclFs, Dir) ->
ReadOpts = [{includes, InclFs}, {preprocess, true}],
ExtractOpts = [{report_missing_type, false}, {link_predefined_types, true}],
LayoutOpts = [{pretty_printer, erl_pp}, {layout, docgen_otp_specs}],
File = case FileSpec of
{file, File0} -> File0;
{module, Module0} -> Module0
end,
try
Fs = case FileSpec of
{file, _} ->
read_file(File, ReadOpts);
{module, Module} ->
[{attribute,erl_anno:new(0),module,list_to_atom(Module)}]
end,
Doc = extract(File, Fs, ExtractOpts),
Text = edoc:layout(Doc, LayoutOpts),
ok = write_text(Text, File, Dir),
rename(Dir, File)
catch
E:R:ST ->
io:format("EDoc could not process file '~s'\n", [File]),
io:format("~p:~p ~p\n", [E,R,ST]),
clean_up(Dir),
halt(3)
end.
read_file(File, Opts) ->
edoc:read_source(File, Opts).
extract(File, Forms, Opts) ->
Env = edoc_lib:get_doc_env([], [], [{app_default,"specs:/"}]),
{_Module, Doc} = edoc_extract:source(Forms, File, Env, Opts),
Doc.
write_text(Text, File, Dir) ->
Base = filename:basename(File, ".erl"),
OutFile = filename:join(Dir, Base) ++ ".specs",
case file:write_file(OutFile, Text) of
ok ->
ok;
{error, R} ->
R1 = file:format_error(R),
io:format("could not write file '~s': ~s\n", [OutFile, R1]),
halt(2)
end.
rename(Dir, F) ->
Mod = filename:basename(F, ".erl"),
Old = filename:join(Dir, Mod ++ ".specs"),
New = filename:join(Dir, "specs_" ++ Mod ++ ".xml"),
case file:rename(Old, New) of
ok ->
ok;
{error, R} ->
R1 = file:format_error(R),
io:format("could not rename file '~s': ~s\n", [New, R1]),
halt(2)
end.
clean_up(Dir) ->
_ = [file:delete(filename:join(Dir, F)) ||
F <- ["packages-frame.html",
"overview-summary.html",
"modules-frame.html",
"index.html", "erlang.png", "edoc-info"]],
ok.
|