Skip to content

Commit 27a5ef8

Browse files
committed
doc: create man pages for apps with source files split up in sub dirs
1 parent e3bda07 commit 27a5ef8

File tree

7 files changed

+178
-82
lines changed

7 files changed

+178
-82
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ JAVADOC-GENERATED
116116

117117
*.1
118118
*.3
119+
*.4
120+
*.6
121+
*.7
119122

120123
# Anchored from $ERL_TOP
121124
/bin

lib/stdlib/src/man_docs.erl

Lines changed: 63 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -20,31 +20,37 @@
2020
%% %CopyrightEnd%
2121
%%
2222
-module(man_docs).
23+
-moduledoc false.
24+
2325
-include_lib("kernel/include/eep48.hrl").
2426

25-
-export([module_to_manpage/2, module_to_manpage/3, markdown_to_manpage/2]).
27+
-export([module_to_manpage/3, module_to_manpage/4, markdown_to_manpage/3]).
2628

2729
%% Formats a module documentation as a roff man page.
2830
%% Fetches the documentation for a module with `code:get_doc/1`
29-
-spec module_to_manpage(Module, Path) -> unicode:chardata() when
31+
-spec module_to_manpage(Module, Path, Section) -> unicode:chardata() when
3032
Module :: module(),
31-
Path :: string().
32-
module_to_manpage(Module, Path) when is_atom(Module) ->
33+
Path :: string(),
34+
Section :: string().
35+
module_to_manpage(Module, Path, Section) when is_atom(Module) ->
3336
case code:get_doc(Module) of
3437
{ok, Docs} ->
35-
module_to_manpage(Module, Path, Docs);
38+
module_to_manpage(Module, Path, Docs, Section);
3639
_Error ->
3740
~""
3841
end.
39-
-spec module_to_manpage(Module, Path, Docs) -> unicode:chardata() when
42+
-spec module_to_manpage(Module, Path, Docs, Section) -> unicode:chardata() when
4043
Module :: module(),
4144
Path :: string(),
42-
Docs :: #docs_v1{}.
43-
module_to_manpage(_Module, _Path, #docs_v1{module_doc = None}) when None =:= none; None =:= hidden ->
45+
Docs :: #docs_v1{},
46+
Section :: string().
47+
module_to_manpage(_Module, _Path, #docs_v1{module_doc = None}, _Section) when None =:= none; None =:= hidden ->
4448
~"";
45-
module_to_manpage(Module, Path, #docs_v1{module_doc = #{~"en" := ModuleDoc}, docs = AllDocs})
49+
module_to_manpage(Module, Path, #docs_v1{module_doc = #{~"en" := ModuleDoc}, docs = AllDocs}, Section)
4650
when is_atom(Module) ->
47-
PreludeNDescription = markdown_to_manpage(ModuleDoc, Path),
51+
PreludeNDescription = if is_binary(ModuleDoc) -> markdown_to_manpage(ModuleDoc, Path, Section);
52+
true -> markdown_to_manpage1(ModuleDoc, Path, Section)
53+
end,
4854

4955
Types = [Doc || {{type,_,_},_,_,_,_}=Doc <- AllDocs],
5056
TypesSection = format_section("DATA TYPES", Types, Module, AllDocs),
@@ -56,10 +62,10 @@ module_to_manpage(Module, Path, #docs_v1{module_doc = #{~"en" := ModuleDoc}, doc
5662
iolist_to_binary([PreludeNDescription, TypesSection, FunctionsSection, CallbacksSection]).
5763

5864
%% Formats markdown as a roff man page.
59-
-spec markdown_to_manpage(binary() | shell_docs:chunk_elements(), file:filename()) -> binary().
60-
markdown_to_manpage(Markdown, Path) when is_binary(Markdown) ->
61-
markdown_to_manpage(shell_docs_markdown:parse_md(Markdown), Path);
62-
markdown_to_manpage(MarkdownChunks, Path) ->
65+
-spec markdown_to_manpage(binary(), file:filename(), string()) -> binary().
66+
markdown_to_manpage(Markdown, Path, Section) ->
67+
markdown_to_manpage1(shell_docs_markdown:parse_md(Markdown), Path, Section).
68+
markdown_to_manpage1(MarkdownChunks, Path, Section) ->
6369
Path1 = filename:absname(Path),
6470
App = case filename:split(string:prefix(Path1, os:getenv("ERL_TOP"))) of
6571
["/", "lib", AppStr | _] ->
@@ -80,8 +86,8 @@ markdown_to_manpage(MarkdownChunks, Path) ->
8086
Extension = filename:extension(Path),
8187
FileName = list_to_binary(filename:rootname(filename:basename(Path), Extension)),
8288
Name = get_name(MarkdownChunks, FileName),
83-
Prelude = io_lib:format(".TH ~s 3 \"~s ~s\" \"Ericsson AB\" \"Erlang Module Definition\"\n",
84-
[Name, atom_to_binary(App), Version]),
89+
Prelude = io_lib:format(".TH ~s ~s \"~s ~s\" \"Ericsson AB\" \"Erlang Module Definition\"\n",
90+
[Name, Section, atom_to_binary(App), Version]),
8591
I = conv(MarkdownChunks, Name),
8692
iolist_to_binary([Prelude|I]).
8793

@@ -113,10 +119,11 @@ conv([{h1,_,[Head]}|T],_) ->
113119
Name = ~".SH NAME\n",
114120
Desc = ~".SH DESCRIPTION\n",
115121
[Name,Head,$\n,Desc|format(T)];
116-
conv([H|T], Head) ->
122+
conv([{p,_,_}=ShortDesc0|T], Head) ->
117123
Name = ~".SH NAME\n",
118124
Desc = ~".SH DESCRIPTION\n",
119-
[Name,Head,~" - ",format_one(H),$\n,Desc|format(T)].
125+
[~".PP\n"|ShortDesc] = format_one(ShortDesc0),
126+
[Name,Head,~B" \- ",ShortDesc,$\n,Desc|format(T)].
120127

121128
escape(Text) when is_list(Text) ->
122129
escape(iolist_to_binary(Text));
@@ -136,10 +143,12 @@ format_one({h1,_,Hs}) ->
136143
[~'.SH "',Hs,~'"\n'];
137144
format_one({h2,_,Hs}) ->
138145
[~'.SS "',Hs,~'"\n'];
146+
format_one({h3,item,H}) ->
147+
[~"\\fB",format_p_item(H),"\\fR"];
139148
format_one({h3,_,[Hs]}) when is_binary(Hs) ->
140149
[~'.PP\n\\fB',Hs,~'\\fR\n'];
141-
format_one({h3,_,Hs}) ->
142-
[~'.PP\n\\fB',format_p(Hs),~'\\fR\n'];
150+
format_one({h3,_,Hs}) when is_list(Hs) ->
151+
[~'.PP\n',[format_one({h3,item,Hi})||Hi<-Hs],~"\n"];
143152
format_one({h4,_,Hs}) ->
144153
format_one({h3,[],Hs});
145154
format_one({h5,_,Hs}) ->
@@ -152,16 +161,18 @@ format_one({ol,_,Ol}) ->
152161
format_ol(Ol);
153162
format_one({ul,_,Ul}) ->
154163
format_ul(Ul);
164+
format_one({a,_,[{code,_,Text}]}) ->
165+
[~B"\fI",format_p_item(Text),~B"\fR"];
155166
format_one({a,_,Text}) ->
156-
[~B"\fI",format(Text),~B"\fR"];
167+
[~B"\fI",format_p_item(Text),~B"\fR"];
157168
format_one({code,_,Text}) ->
158-
[~B"\fI",format(Text),~B"\fR"];
169+
[~B"\fI",format_p_item(Text),~B"\fR"];
159170
format_one({strong,_,Text}) ->
160-
["\\fB", Text, "\\fR"];
171+
[~B"\fB", format_p_item(Text), ~B"\fR"];
161172
format_one({em,_,Text}) ->
162-
[~B"\fB",format_one(Text),~B"\fR"];
173+
[~B"\fB",format_p_item(Text),~B"\fR"];
163174
format_one({i,_,Text}) ->
164-
[~B"\fI",format_one(Text),~B"\fR"];
175+
[~B"\fI",format_p_item(Text),~B"\fR"];
165176
format_one({dl,_,Content}) ->
166177
format_dl(Content);
167178
format_one([Text]) when is_binary(Text) ->
@@ -172,27 +183,27 @@ format_one(Text) when is_binary(Text) ->
172183
format_dl(Is) ->
173184
[~".RS 4\n", [format_dl_item(I) || I <- Is], ~".RE\n"].
174185
format_dl_item({dt,_,Content}) ->
175-
[~".TP\n", "\\fB", format(Content), "\\fR", $\n];
186+
[~".TP\n", "\\fB", format_p_item(Content), "\\fR", $\n];
176187
format_dl_item({dd,_,Content}) ->
177-
format(Content).
178-
188+
[format_dd_item(Content), $\n].
189+
format_dd_item([{ul,_,_}=UL|Rest]) ->
190+
[format([UL]), format_dd_item(Rest)];
191+
format_dd_item([{p,_,Content}|Rest]) ->
192+
[format_p(Content)|format_dd_item(Rest)];
193+
format_dd_item([TextItem|Rest]) ->
194+
[format_p_item(TextItem),format_dd_item(Rest)];
195+
format_dd_item([]) -> [].
179196
format_p(Text) when is_binary(Text) ->
180197
format_p([Text]);
181198
format_p(Is0) ->
182199
Text0 = iolist_to_binary([format_p_item(I) || I <- Is0]),
183200
Text = string:trim(Text0, leading),
184201
[~".PP\n",Text,$\n].
185202

186-
format_p_item({code,_,Text}) ->
203+
format_p_item({Fi,_,Text}) when Fi =:= code; Fi =:= i; Fi =:= a ->
187204
[~B"\fI",format_p_item(Text),~B"\fR"];
188-
format_p_item({em,_,Text}) ->
205+
format_p_item({Fb,_,Text}) when Fb =:= em; Fb =:= strong ->
189206
[~B"\fB",format_p_item(Text),~B"\fR"];
190-
format_p_item({i,_,Text}) ->
191-
[~B"\fI",format_p_item(Text),~B"\fR"];
192-
format_p_item({a,_,Text}) ->
193-
[~B"\fI",format_p_item(Text),~B"\fR"];
194-
format_p_item({strong,_,Text}) ->
195-
["\\fB", format_p_item(Text), "\\fR"];
196207
format_p_item([H|T]) ->
197208
[format_p_item(H)|format_p_item(T)];
198209
format_p_item([]) ->
@@ -205,8 +216,7 @@ format_pre(Ps0) ->
205216
[~".IP\n.nf\n",Ps,$\n,~".fi\n"].
206217

207218
format_pre_item({code,[{class,<<"table">>}],Text}) ->
208-
Text2 = to_tbl(parse(extract(iolist_to_binary(Text)))),
209-
escape_backslashes(Text2);
219+
to_tbl(parse(extract(iolist_to_binary(Text))));
210220
format_pre_item({code,_,Text}) ->
211221
escape_backslashes(Text);
212222
format_pre_item(Text) ->
@@ -257,7 +267,13 @@ format_ul_item({li,_,Ps0}) ->
257267
.IP \(bu 2.3
258268
.\}
259269
""",
260-
[B,format(Ps0),~".RE\n"]
270+
case Ps0 of
271+
[Text|_] when is_binary(Text);
272+
element(1,Text) =:= code;
273+
element(1,Text) =:= a ->
274+
[B,format_p(Ps0),~".RE\n"];
275+
_ -> [B,format(Ps0),~".RE\n"]
276+
end
261277
end.
262278

263279
strip_formatting({_,_,[_|_]=L}) ->
@@ -341,7 +357,10 @@ parse_row(Line) ->
341357
Cells = binary:split(NoOuterPipes, <<"|">>, [global]),
342358

343359
%% 4. Trim whitespace from each individual cell.
344-
[string:trim(Cell) || Cell <- Cells].
360+
[format_cell(string:trim(Cell)) || Cell <- Cells].
361+
362+
format_cell(B) ->
363+
re:replace(B,"`(.+)`",<<"\\\\fI\\g{1}\\\\fR">>, [{return, binary},global]).
345364

346365
%% Helper to safely remove the first and last characters if they are pipes.
347366
strip_outer_pipes(Bin) ->
@@ -415,9 +434,10 @@ format_ast(AST) ->
415434
BinSpec = unicode:characters_to_binary(string:trim(Spec, trailing, "\n")),
416435

417436
BinSpec2 = re:replace(BinSpec, "-((type)|(spec)|(callback)) ", ""),
418-
419-
["\\fB", escape(BinSpec2), "\\fR", "\n"].
437+
BinSpec3 = string:replace(escape(BinSpec2),"\n","\\fR\n\\fB",all),
438+
["\\fB", BinSpec3, "\\fR", "\n"].
420439

421440
format_meta(#{ deprecated := Depr } = M) ->
422-
["\n.br\nDeprecated: ", unicode:characters_to_binary(Depr) | format_meta(maps:remove(deprecated, M))];
441+
[~"\n.RS\n.LP\nDeprecated: ",
442+
unicode:characters_to_binary(Depr), ~"\n.RE\n" | format_meta(maps:remove(deprecated, M))];
423443
format_meta(_) -> [].

lib/stdlib/src/shell_docs.erl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,7 @@ render_type(Module, D) ->
618618
render_type(Module, D, #{}).
619619

620620
%% extract AST raw type specifications.
621+
-doc false.
621622
-spec extract_type_specs(module()) -> map().
622623
extract_type_specs(Module) ->
623624
maybe

lib/stdlib/test/shell_docs_SUITE.erl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,7 @@ render_man(_Config) ->
326326
#{source_path := Path} -> Path;
327327
#{} -> proplists:get_value(source, proplists:get_value(compile, Mod:module_info()))
328328
end,
329-
man_docs:module_to_manpage(Mod, Path1, D)
329+
man_docs:module_to_manpage(Mod, Path1, D, "3")
330330
catch _E:R:ST ->
331331
io:format("Failed to render man page for ~p~n~p:~p~n~p~n",
332332
[Mod,R,ST,D]),

make/doc.mk

Lines changed: 78 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,39 @@ endif
5050
# ----------------------------------------------------
5151
# Man dependencies
5252
# ----------------------------------------------------
53-
ERL_FILES := $(wildcard $(APP_SRC_DIR)/*.erl)
54-
ERL_FILES_WITH_DOC := $(shell grep -L '-moduledoc false.' $(ERL_FILES))
53+
ERL_FILES := $(wildcard $(APP_SRC_DIR)/*.erl) $(wildcard $(APP_SRC_DIR)/*/*.erl) $(wildcard $(APP_DIR)/preloaded/src/*.erl)
54+
ERL_STRIP := $(strip $(ERL_FILES))
55+
ifneq ($(ERL_STRIP),)
56+
ERL_FILES_WITH_DOC := $(shell grep -L "moduledoc false." $(ERL_FILES))
57+
else
58+
ERL_FILES_WITH_DOC :=
59+
endif
60+
ERL_FILENAMES_ONLY := $(notdir $(ERL_FILES_WITH_DOC))
5561
MAN1_DEPS?=$(wildcard */*_cmd.md)
56-
MAN3_DEPS?=$(wildcard */references/*.md) $(wildcard references/*.md) \
57-
$(wildcard */src/*.md) $(wildcard src/*.md) \
58-
$(ERL_FILES_WITH_DOC)
59-
MAN3_DEPS_FILTERED=$(filter-out $(wildcard */references/*_cmd.md) $(wildcard references/*_cmd.md),$(MAN3_DEPS))
60-
62+
MAN3_DEPS_UNFILTERED?=$(wildcard */src/*.md) $(wildcard src/*.md) \
63+
$(wildcard */references/*.md) $(wildcard references/*.md) \
64+
$(ERL_FILENAMES_ONLY)
65+
MAN4_DEPS=$(wildcard references/app.md references/config.md references/appup.md references/rel.md \
66+
references/relup.md references/script.md references/diameter_dict.md references/erlang.el.md)
67+
MAN3_DEPS=$(filter-out $(wildcard */references/*_cmd.md) $(wildcard references/*_cmd.md) $(MAN4_DEPS),$(MAN3_DEPS_UNFILTERED))
68+
MAN6_DEPS=$(wildcard *_app.md)
69+
MAN7_DEPS=$(wildcard $(APP_DIR)/mibs/*.mib)
6170
MAN1_PAGES=$(MAN1_DEPS:references/%_cmd.md=$(MAN1DIR)/%.1)
62-
MAN3_PAGES=$(MAN3_DEPS_FILTERED:$(APP_SRC_DIR)/%.erl=$(MAN3DIR)/%.3)
63-
MAN3_PAGES+=$(MAN3_DEPS_FILTERED:references/%.md=$(MAN3DIR)/%.3)
64-
MAN3_PAGES+=$(MAN3_DEPS_FILTERED:src/%.md=$(MAN3DIR)/%.3)
65-
71+
MAN3_PAGES=$(MAN3_DEPS:%.erl=$(MAN3DIR)/%.3)
72+
MAN3_PAGES+=$(MAN3_DEPS:src/%.md=$(MAN3DIR)/%.3)
73+
MAN3_PAGES+=$(MAN3_DEPS:references/%.md=$(MAN3DIR)/%.3)
74+
MAN4_PAGES=$(MAN4_DEPS:references/%.md=$(MAN4DIR)/%.4)
75+
MAN6_PAGES=$(MAN6_DEPS:%_app.md=$(MAN6DIR)/%.6)
76+
MAN7_PAGES=$(MAN7_DEPS:$(APP_DIR)/mibs/%.mib=$(MAN7DIR)/%.7)
77+
78+
# 1. Find all possible source directories recursively
79+
ifneq ($(wildcard $(APP_SRC_DIR)),)
80+
ERL_SRC_DIRS := $(shell find $(APP_SRC_DIR) -type d)
81+
else
82+
ERL_SRC_DIRS :=
83+
endif
84+
# 2. Tell make to search for .erl files in all those directories
85+
vpath %.erl $(ERL_SRC_DIRS) $(APP_DIR)/preloaded/src
6686

6787
# ----------------------------------------------------
6888
# Targets
@@ -74,9 +94,19 @@ endif
7494
ifneq ($(MAN1_DEPS),)
7595
DEFAULT_DOC_TARGETS+=man
7696
endif
77-
ifneq ($(MAN3_DEPS_FILTERED),)
97+
ifneq ($(MAN3_DEPS),)
7898
DEFAULT_DOC_TARGETS+=man
7999
endif
100+
ifneq ($(MAN4_DEPS),)
101+
DEFAULT_DOC_TARGETS+=man
102+
endif
103+
ifneq ($(MAN6_DEPS),)
104+
DEFAULT_DOC_TARGETS+=man
105+
endif
106+
ifneq ($(MAN7_DEPS),)
107+
DEFAULT_DOC_TARGETS+=man
108+
endif
109+
80110
DOC_TARGETS?=$(DEFAULT_DOC_TARGETS)
81111

82112
EX_DOC_WARNINGS_AS_ERRORS?=default
@@ -93,21 +123,36 @@ $(HTMLDIR)/index.html: $(HTML_DEPS) docs.exs $(ERL_TOP)/make/ex_doc.exs
93123

94124
html: $(HTMLDIR)/index.html
95125

96-
man: $(MAN1_PAGES) $(MAN3_PAGES)
126+
man: $(MAN1_PAGES) $(MAN3_PAGES) $(MAN4_PAGES) $(MAN6_PAGES) $(MAN7_PAGES)
97127

98128
MARKDOWN_TO_MAN=$(ERL_TOP)/make/markdown_to_man.escript
99129

100130
man1/%.1: references/%_cmd.md $(MARKDOWN_TO_MAN)
101-
escript$(EXEEXT) $(MARKDOWN_TO_MAN) -o $(MAN1DIR) $<
131+
@escript$(EXEEXT) $(MARKDOWN_TO_MAN) -o $(MAN1DIR) $<
102132

103133
man3/%.3: src/%.md $(MARKDOWN_TO_MAN)
104-
escript$(EXEEXT) $(MARKDOWN_TO_MAN) -o $(MAN3DIR) $<
134+
@escript$(EXEEXT) $(MARKDOWN_TO_MAN) -o $(MAN3DIR) $<
105135

106136
man3/%.3: references/%.md $(MARKDOWN_TO_MAN)
107-
escript$(EXEEXT) $(MARKDOWN_TO_MAN) -o $(MAN3DIR) $<
137+
@escript$(EXEEXT) $(MARKDOWN_TO_MAN) -o $(MAN3DIR) $<
138+
139+
man3/%.3: %.erl $(MARKDOWN_TO_MAN)
140+
@escript$(EXEEXT) $(MARKDOWN_TO_MAN) -o $(MAN3DIR) $<
141+
142+
man4/%.4: references/%.md $(MARKDOWN_TO_MAN)
143+
@escript$(EXEEXT) $(MARKDOWN_TO_MAN) -o $(MAN4DIR) -s 4 $<
144+
145+
man6/%.6: %_app.md $(MARKDOWN_TO_MAN)
146+
@escript$(EXEEXT) $(MARKDOWN_TO_MAN) -o $(MAN6DIR) $<
108147

109-
man3/%.3: ../src/%.erl $(MARKDOWN_TO_MAN)
110-
escript$(EXEEXT) $(MARKDOWN_TO_MAN) -o $(MAN3DIR) $<
148+
man7/%.7: $(APP_DIR)/mibs/%.mib
149+
@mkdir -p man7
150+
$(eval REL_PATH := $(patsubst $(ERL_TOP)/lib/%,%,$(abspath $<)))
151+
$(eval APP_NAME := $(shell echo $(firstword $(subst /, ,$(REL_PATH))) | tr '[:lower:]' '[:upper:]'))
152+
$(eval MIB_NAME := $(basename $(notdir $<)))
153+
@echo .TH $(MIB_NAME) 7 \"$(APP_NAME)\" \"Erlang/OTP\" \"MIB\" > $@
154+
@echo .nf >> $@
155+
@grep -v '^--' $< >> $@
111156

112157
# ----------------------------------------------------
113158

@@ -137,10 +182,24 @@ ifneq ($(MAN1_DEPS),)
137182
$(INSTALL_DIR) "$(RELSYS_MANDIR)/man1"
138183
$(INSTALL_DIR_DATA) "$(MAN1DIR)" "$(RELSYS_MANDIR)/man1"
139184
endif
140-
ifneq ($(MAN3_DEPS_FILTERED),)
185+
ifneq ($(MAN3_DEPS),)
141186
$(INSTALL_DIR) "$(RELSYS_MANDIR)/man3"
142187
$(INSTALL_DIR_DATA) "$(MAN3DIR)" "$(RELSYS_MANDIR)/man3"
143188
endif
189+
ifneq ($(MAN4_DEPS),)
190+
$(INSTALL_DIR) "$(RELSYS_MANDIR)/man4"
191+
$(INSTALL_DIR_DATA) "$(MAN4DIR)" "$(RELSYS_MANDIR)/man4"
192+
endif
193+
ifneq ($(MAN6_DEPS),)
194+
$(INSTALL_DIR) "$(RELSYS_MANDIR)/man6"
195+
$(INSTALL_DIR_DATA) "$(MAN6DIR)" "$(RELSYS_MANDIR)/man6"
196+
endif
197+
ifneq ($(MAN7_DEPS),)
198+
$(INSTALL_DIR) "$(RELSYS_MANDIR)/man7"
199+
$(INSTALL_DIR_DATA) "$(MAN7DIR)" "$(RELSYS_MANDIR)/man7"
200+
endif
201+
202+
144203

145204
release_docs_spec: $(DOC_TARGETS:%=release_%_spec)
146205
ifneq ($(STANDARDS),)

0 commit comments

Comments
 (0)