Skip to content

Commit

Permalink
Prepare erl_docgen for using Dialyzer specs and types
Browse files Browse the repository at this point in the history
Support for using Dialyzer specifications and types has been added.
This is an experimental release; changes are expected before the new
functionality is used when building the OTP documentation.
  • Loading branch information
uabboli committed Dec 1, 2010
1 parent e2191e8 commit 2f93254
Show file tree
Hide file tree
Showing 16 changed files with 2,326 additions and 276 deletions.
5 changes: 4 additions & 1 deletion Makefile.in
Expand Up @@ -393,7 +393,7 @@ endif
# ---------------------------------------------------------------
# Target only used when building commercial ERTS patches
# ---------------------------------------------------------------
release_docs docs:
release_docs docs: mod2app
ifeq ($(OTP_SMALL_BUILD),true)
cd $(ERL_TOP)/lib && \
ERL_TOP=$(ERL_TOP) $(MAKE) TESTROOT=$(RELEASE_ROOT) $@
Expand All @@ -408,6 +408,9 @@ endif
cd $(ERL_TOP)/system/doc && \
ERL_TOP=$(ERL_TOP) $(MAKE) TESTROOT=$(RELEASE_ROOT) $@

mod2app:
$(ERL_TOP)/lib/erl_docgen/priv/bin/xref_mod_app.escript -topdir $(ERL_TOP) -outfile $(ERL_TOP)/make/$(TARGET)/mod2app.xml

# ----------------------------------------------------------------------
ERLANG_EARS=$(BOOTSTRAP_ROOT)/bootstrap/erts
ELINK=$(BOOTSTRAP_ROOT)/bootstrap/erts/bin/elink
Expand Down
19 changes: 11 additions & 8 deletions lib/erl_docgen/Makefile
@@ -1,19 +1,20 @@
# ``The contents of this file are subject to the Erlang Public License,
#
# %CopyrightBegin%
#
# Copyright Ericsson AB 1996-2010. All Rights Reserved.
#
# The contents of this file are subject to the Erlang Public License,
# Version 1.1, (the "License"); you may not use this file except in
# compliance with the License. You should have received a copy of the
# Erlang Public License along with this software. If not, it can be
# retrieved via the world wide web at http://www.erlang.org/.
# retrieved online at http://www.erlang.org/.
#
# Software distributed under the License is distributed on an "AS IS"
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
# the License for the specific language governing rights and limitations
# under the License.
#
# The Initial Developer of the Original Code is Ericsson Utvecklings AB.
# Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
# AB. All Rights Reserved.''
#
# $Id$
# %CopyrightEnd%
#
include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
Expand All @@ -22,9 +23,11 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk
# Macros
#

SUB_DIRECTORIES = priv
SUB_DIRECTORIES = src priv
#doc/src

include vsn.mk
VSN = $(ERL_DOCGEN_VSN)

SPECIAL_TARGETS =

Expand Down
Empty file added lib/erl_docgen/ebin/.gitignore
Empty file.
129 changes: 129 additions & 0 deletions lib/erl_docgen/priv/bin/specs_gen.escript
@@ -0,0 +1,129 @@
#!/usr/bin/env escript
%% -*- erlang -*-
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%

%%% <script> [-I<dir>]... [-o<dir>] [-module Module] [File]
%%%
%%% Use EDoc and the layout module '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 temporary 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) ->
parse(Opts, [I | 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) ->
Incl = [{includes, InclFs}],
Pre = [{preprocess, true}],
Choice = [{dialyzer_specs, all}],
DirOpt = [{dir, Dir}],
Pretty = [{pretty_print, erl_pp}],
Layout = [{layout, otp_specs},
{file_suffix, ".specs"},
{stylesheet, ""}],
Warn = [{report_missing_type, false},
{report_type_mismatch, false}],
OptionList = (DirOpt ++ Choice ++ Pre ++ Warn ++ Pretty ++ Layout ++ Incl),
{File, TmpFile} = case FileSpec of
{file, File0} ->
{File0, false};
{module, Module} ->
{create_tmp_file(Dir, Module), true}
end,
try edoc:files([File], OptionList) of
ok ->
clean_up(Dir, File, TmpFile),
rename(Dir, File)
catch
_:_ ->
io:format("EDoc could not process file '~s'\n", [File]),
clean_up(Dir, File, TmpFile),
halt(3)
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, TmpFile) ->
[file:delete(File) || TmpFile],
_ = [file:delete(filename:join(Dir, F)) ||
F <- ["packages-frame.html",
"overview-summary.html",
"modules-frame.html",
"index.html", "erlang.png", "edoc-info"]],
ok.

create_tmp_file(Dir, Module) ->
TmpFile = filename:join(Dir, Module++".erl"),
case file:write_file(TmpFile, "-module(" ++ Module ++ ").\n") of
ok ->
TmpFile;
{error, R} ->
R1 = file:format_error(R),
io:format("could not write file '~s': ~s\n", [TmpFile, R1]),
halt(2)
end.
107 changes: 107 additions & 0 deletions lib/erl_docgen/priv/bin/xref_mod_app.escript
@@ -0,0 +1,107 @@
#!/usr/bin/env escript
%% -*- erlang -*-
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights 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],
lists:keysort(1, [{M,A} || {A,Ms} <- AM, M <- Ms]).

%% 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]}.

-include_lib("xmerl/include/xmerl.hrl").

-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.
7 changes: 5 additions & 2 deletions lib/erl_docgen/priv/docbuilder_dtd/common.refs.dtd
Expand Up @@ -26,15 +26,18 @@

<!ELEMENT description (%block;|quote|br|marker|warning|note)* >
<!ELEMENT funcs (func)+ >
<!ELEMENT func (name+,fsummary,type?,desc?) >
<!ELEMENT func (name+,type_desc+,fsummary,type?,desc?) >
<!-- ELEMENT name is defined in each ref dtd -->
<!ELEMENT fsummary (#PCDATA|c|em)* >
<!ELEMENT type (v,d?)+ >
<!ELEMENT v (#PCDATA) >
<!ELEMENT d (#PCDATA|c|em)* >
<!ELEMENT desc (%block;|quote|br|marker|warning|note)* >
<!ELEMENT desc (%block;|quote|br|marker|warning|note|anno)* >
<!ELEMENT authors (aname,email)+ >
<!ELEMENT aname (#PCDATA) >
<!ELEMENT email (#PCDATA) >
<!ELEMENT section (marker*,title,(%block;|quote|br|marker|
warning|note)*) >
<!ELEMENT datatypes (datatype)+ >
<!ELEMENT datatype (name+,desc?) >
<!ELEMENT type_desc (#PCDATA) >
2 changes: 1 addition & 1 deletion lib/erl_docgen/priv/docbuilder_dtd/erlref.dtd
Expand Up @@ -22,7 +22,7 @@
%common.refs;

<!ELEMENT erlref (header,module,modulesummary,description,
(section|funcs)*,authors?) >
(section|funcs|datatypes)*,authors?) >
<!ELEMENT module (#PCDATA) >
<!ELEMENT modulesummary (#PCDATA) >

Expand Down

0 comments on commit 2f93254

Please sign in to comment.