[PATCH wayland 3/3] doc: generate doxygen html output from the scanner
Bryce Harrington
bryce at osg.samsung.com
Mon Nov 16 12:35:50 PST 2015
On Fri, Nov 06, 2015 at 08:02:01AM +1000, Peter Hutterer wrote:
> This switches the scanner to generate doxygen-compatible tags for the
> generated protocol headers, and hooks up the doxygen build to generate server
> and client-side API documentation.
>
> Each protocol is a separate doxygen @page, with each interface a @subpage.
> Wayland only has one protocol, wayland-protocols will have these nested.
> Each protocol page has a list of interfaces and the copyright and description
> where available.
> All interfaces are grouped by doxygen @defgroup and @ingroups and appear in
> "Modules" in the generated output. Each interface subpage has the description
> and a link to the actual API doc.
> Function, struct and #defines are documented in doxygen style and associated
> with the matching interface.
>
> Note that pages and groups have fixed HTML file names and are directly
> linkable/bookmark-able.
>
> The @mainpage is a separate file that's included at build time. It doesn't
> contain much other than links to where the interesting bits are. It's a static
> file though that supports markdown, so we can extend it easily in the future.
>
> For doxygen we need the new options EXTRACT_ALL and OPTIMIZE_OUTPUT_FOR_C so
> it scans C code properly. WARN_IF_DOC_ERROR is required to silence the
> warnings when some parameters are documented but others aren't.
>
> Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
Reviewed-by: Bryce Harrington <bryce at osg.samsung.com>
This looks fine to me in general, but would you mind respinning it
to apply against current HEAD?
There's a couple really minor questions below.
Bryce
> ---
> Generated output for wayland and wayland-protocols is here:
> http://people.freedesktop.org/~whot/wayland-doxygen/
>
> * as discussed in the other thread, this doesn't touch the current generated
> xml files, the current doc doesn't actually include the protocol API
> anyway.
> * mainpage is separate because it's easier this way for wayland-protocols
> * the generated code contains a lot of comment now but is still readable.
> Only duplication is the copyright (once at file level, once as protocol
> section), but that shouldn't hurt anyone.
> * we should start including <description> tags at the <protocol> level. the
> scanner supports it and it provides extra info (see the tablet
> protocol in wayland-protocols)
>
> doc/doxygen/Makefile.am | 27 +++++-
> doc/doxygen/mainpage.dox | 13 +++
> doc/doxygen/wayland.doxygen.in | 4 +-
> src/scanner.c | 207 +++++++++++++++++++++++++++--------------
> 4 files changed, 176 insertions(+), 75 deletions(-)
> create mode 100644 doc/doxygen/mainpage.dox
>
> diff --git a/doc/doxygen/Makefile.am b/doc/doxygen/Makefile.am
> index a8bb95f..e80c401 100644
> --- a/doc/doxygen/Makefile.am
> +++ b/doc/doxygen/Makefile.am
> @@ -1,7 +1,11 @@
>
> .SUFFIXES = .gv .png .map
>
> -noinst_DATA = xml/Client/index.xml xml/Server/index.xml
> +noinst_DATA = \
> + xml/Client/index.xml \
> + xml/Server/index.xml \
> + html/Client/index.html \
> + html/Server/index.html
> dist_noinst_DATA = wayland.doxygen.in
>
> scanned_src_files_shared = \
> @@ -27,6 +31,17 @@ scanned_src_files_man = \
> $(top_srcdir)/src/wayland-client.h \
> $(top_srcdir)/src/wayland-client-core.h
>
> +extra_doxygen = \
> + mainpage.dox
> +
> +extra_doxygen_Server = \
> + $(top_builddir)/protocol/wayland-server-protocol.h \
> + $(extra_doxygen)
> +
> +extra_doxygen_Client = \
> + $(top_builddir)/protocol/wayland-client-protocol.h \
> + $(extra_doxygen)
> +
> diagramsdir := $(srcdir)/dot
> diagramssrc := $(wildcard $(diagramsdir)/*.gv)
> diagrams := $(patsubst $(diagramsdir)/%,xml/%,$(diagramssrc:.gv=.png))
> @@ -38,7 +53,7 @@ diagram_maps := $(patsubst $(diagramsdir)/%,xml/%,$(diagramssrc:.gv=.map))
> dist_man3_MANS = $(shell test -d man && find man/man3 -name "wl_*.3" -printf "man/man3/%P\n")
>
> # Listing various directories that might need to be created.
> -alldirs := xml xml/Client xml/Server man/man3
> +alldirs := xml xml/Client xml/Server man/man3 html/Client html/Server
>
> $(diagrams): $(diagramssrc)
>
> @@ -51,6 +66,13 @@ xml/%/index.xml: $(top_srcdir)/src/scanner.c $(scanned_src_files_%) wayland.doxy
> echo "INPUT= $(scanned_src_files_$*)"; \
> ) | $(DOXYGEN) -
>
> +html/%/index.html: $(scanned_src_files_%) wayland.doxygen $(diagrams) $(diagram_maps) | html/%
> + $(AM_V_GEN)(cat wayland.doxygen; \
> + echo "GENERATE_HTML=YES"; \
> + echo "HTML_OUTPUT=html/$*"; \
> + echo "INPUT= $(scanned_src_files_$*) $(extra_doxygen_$*)"; \
> + ) | $(DOXYGEN) -
> +
> man/man3/wl_display.3: $(top_srcdir)/src/scanner.c $(scanned_src_files_man) wayland.doxygen | man/man3
> $(AM_V_GEN)(cat wayland.doxygen; \
> echo "GENERATE_MAN=YES"; \
> @@ -74,6 +96,7 @@ all-local: man/man3/wl_display.3
>
> clean-local:
> rm -rf xml/
> + rm -rf html/
> rm -rf man/
>
> EXTRA_DIST = $(diagramssrc)
> diff --git a/doc/doxygen/mainpage.dox b/doc/doxygen/mainpage.dox
> new file mode 100644
> index 0000000..8f9bf03
> --- /dev/null
> +++ b/doc/doxygen/mainpage.dox
> @@ -0,0 +1,13 @@
> +/**
> + * @mainpage
> + * Wayland protocol API documentation.
> + *
> + * @section ifaces Interfaces
> + * For the list of available interfaces, please see the
> + * <a href="modules.html">modules</a> list.
> + *
> + * @section protocols Protocols
> + * For the list of protocols, please see the
> + * <a href="pages.html">Related Pages</a>.
> + *
> + */
> diff --git a/doc/doxygen/wayland.doxygen.in b/doc/doxygen/wayland.doxygen.in
> index fb76b12..3ca2d00 100644
> --- a/doc/doxygen/wayland.doxygen.in
> +++ b/doc/doxygen/wayland.doxygen.in
> @@ -13,4 +13,6 @@ MACRO_EXPANSION = YES
> EXPAND_ONLY_PREDEF = YES
> DOT_MULTI_TARGETS = YES
> ALIASES += comment{1}="/* \1 *<!-- -->/"
> -GENERATE_HTML = NO
> +OPTIMIZE_OUTPUT_FOR_C = YES
> +EXTRACT_ALL = YES
> +WARN_IF_DOC_ERROR = NO
> diff --git a/src/scanner.c b/src/scanner.c
> index 7b0f482..c2b8378 100644
> --- a/src/scanner.c
> +++ b/src/scanner.c
> @@ -762,9 +762,11 @@ emit_opcode_versions(struct wl_list *message_list, struct interface *interface)
> {
> struct message *m;
>
> - wl_list_for_each(m, message_list, link)
> + wl_list_for_each(m, message_list, link) {
> + printf("/**\n * @ingroup iface_%s\n */\n", interface->name);
> printf("#define %s_%s_SINCE_VERSION\t%d\n",
> interface->uppercase_name, m->uppercase_name, m->since);
> + }
>
> printf("\n");
> }
> @@ -804,6 +806,7 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
> struct arg *a, *ret;
> int has_destructor, has_destroy;
>
> + printf("/** @ingroup iface_%s */\n", interface->name);
> printf("static inline void\n"
> "%s_set_user_data(struct %s *%s, void *user_data)\n"
> "{\n"
> @@ -812,6 +815,7 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
> interface->name, interface->name, interface->name,
> interface->name);
>
> + printf("/** @ingroup iface_%s */\n", interface->name);
> printf("static inline void *\n"
> "%s_get_user_data(struct %s *%s)\n"
> "{\n"
> @@ -837,7 +841,8 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
> exit(EXIT_FAILURE);
> }
>
> - if (!has_destroy && strcmp(interface->name, "wl_display") != 0)
> + if (!has_destroy && strcmp(interface->name, "wl_display") != 0) {
> + printf("/** @ingroup iface_%s */\n", interface->name);
> printf("static inline void\n"
> "%s_destroy(struct %s *%s)\n"
> "{\n"
> @@ -846,6 +851,7 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
> "}\n\n",
> interface->name, interface->name, interface->name,
> interface->name);
> + }
>
> if (wl_list_empty(message_list))
> return;
> @@ -865,6 +871,7 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
> ret = a;
> }
>
> + printf("/** @ingroup iface_%s */\n", interface->name);
> if (ret && ret->interface_name == NULL)
> printf("static inline void *\n");
> else if (ret)
> @@ -950,6 +957,16 @@ emit_event_wrappers(struct wl_list *message_list, struct interface *interface)
> return;
>
> wl_list_for_each(m, message_list, link) {
> + printf("/**\n"
> + " * @ingroup iface_%s\n"
> + " * Sends an %s event to the client owning the resource.\n",
> + interface->name,
"an xxx event" presumes the event name always starts with a vowel,
doesn't it? If that's not always going to be true, could this be worded
in a way that we don't require a/an here?
> + m->name);
> + wl_list_for_each(a, &m->arg_list, link) {
> + if (a->summary)
> + printf(" * @param %s %s\n", a->name, a->summary);
> + }
> + printf(" */\n");
> printf("static inline void\n"
> "%s_send_%s(struct wl_resource *resource_",
> interface->name, m->name);
> @@ -981,6 +998,31 @@ emit_event_wrappers(struct wl_list *message_list, struct interface *interface)
> }
>
> static void
> +format_text_to_comment(const char *text, bool standalone_comment)
> +{
> + int bol = 1, start = 0, i;
> +
> + for (i = 0; text[i]; i++) {
> + if (bol && (text[i] == ' ' || text[i] == '\t')) {
> + continue;
> + } else if (bol) {
> + bol = 0;
> + start = i;
> + }
Maybe slightly clearer?
if (bol) {
if (text[i] == ' ' || text[i] == '\t')
continue;
bol = 0;
start = i;
}
> +
> + if (text[i] == '\n' || text[i] == '\0') {
> + printf("%s%s%.*s\n",
> + i == 0 && standalone_comment ? "/*" : " *",
> + (i - start) == 0 ? "" : " ",
> + i - start, text + start);
> + bol = 1;
> + }
> + }
> + if (standalone_comment)
> + printf(" */\n\n");
> +}
> +
> +static void
> emit_enumerations(struct interface *interface)
> {
> struct enumeration *e;
> @@ -996,28 +1038,23 @@ emit_enumerations(struct interface *interface)
>
> if (desc) {
> printf("/**\n");
> - desc_dump(desc->summary,
> - " * %s_%s - ",
> - interface->name, e->name);
> - wl_list_for_each(entry, &e->entry_list, link) {
> - desc_dump(entry->summary,
> - " * @%s_%s_%s: ",
> - interface->uppercase_name,
> - e->uppercase_name,
> - entry->uppercase_name);
> - }
> - if (desc->text) {
> - printf(" *\n");
> - desc_dump(desc->text, " * ");
> - }
> + printf(" * @ingroup iface_%s\n", interface->name);
> + format_text_to_comment(desc->summary, false);
> + if (desc->text)
> + format_text_to_comment(desc->text, false);
> printf(" */\n");
> }
> printf("enum %s_%s {\n", interface->name, e->name);
> - wl_list_for_each(entry, &e->entry_list, link)
> + wl_list_for_each(entry, &e->entry_list, link) {
> + if (entry->summary)
> + printf("\t/**\n"
> + "\t * %s\n"
> + "\t */\n", entry->summary);
Do we always know that the string will be non-blank if it is non-NULL?
I'm assuming that's true, but if blank summaries, etc. are possible then
might be cleaner to also check strlen? Anyway, just a nit, and suitable
for followup if the issue actually crops up in practice.
> printf("\t%s_%s_%s = %s,\n",
> interface->uppercase_name,
> e->uppercase_name,
> entry->uppercase_name, entry->value);
> + }
> printf("};\n");
> printf("#endif /* %s_%s_ENUM */\n\n",
> interface->uppercase_name, e->uppercase_name);
> @@ -1034,20 +1071,11 @@ emit_structs(struct wl_list *message_list, struct interface *interface, enum sid
> if (wl_list_empty(message_list))
> return;
>
> - if (interface->description) {
> - struct description *desc = interface->description;
> - printf("/**\n");
> - desc_dump(desc->summary, " * %s - ", interface->name);
> - wl_list_for_each(m, message_list, link) {
> - struct description *mdesc = m->description;
> - desc_dump(mdesc ? mdesc->summary : "(none)",
> - " * @%s: ",
> - m->name);
> - }
> - printf(" *\n");
> - desc_dump(desc->text, " * ");
> - printf(" */\n");
> - }
> + printf("/**\n");
> + printf(" * @ingroup iface_%s\n", interface->name);
> + printf(" * @struct %s_%s\n", interface->name,
> + (side == SERVER) ? "interface" : "listener");
> + printf(" */\n");
> printf("struct %s_%s {\n", interface->name,
> (side == SERVER) ? "interface" : "listener");
>
> @@ -1055,24 +1083,24 @@ emit_structs(struct wl_list *message_list, struct interface *interface, enum sid
> struct description *mdesc = m->description;
>
> printf("\t/**\n");
> - desc_dump(mdesc ? mdesc->summary : "(none)",
> - "\t * %s - ", m->name);
> - wl_list_for_each(a, &m->arg_list, link) {
> - if (side == SERVER && a->type == NEW_ID &&
> - a->interface_name == NULL)
> - printf("\t * @interface: name of the objects interface\n"
> - "\t * @version: version of the objects interface\n");
> -
> -
> - desc_dump(a->summary ? a->summary : "(none)",
> - "\t * @%s: ", a->name);
> - }
> if (mdesc) {
> + if (mdesc->summary)
> + printf("\t * %s\n", mdesc->summary);
> printf("\t *\n");
> desc_dump(mdesc->text, "\t * ");
> }
> + wl_list_for_each(a, &m->arg_list, link) {
> + if (side == SERVER && a->type == NEW_ID &&
> + a->interface_name == NULL)
> + printf("\t * @param interface name of the objects interface\n"
> + "\t * @param version version of the objects interface\n");
> +
> + if (a->summary)
> + printf("\t * @param %s %s\n", a->name,
> + a->summary);
> + }
> if (m->since > 1) {
> - printf("\t * @since: %d\n", m->since);
> + printf("\t * @since %d\n", m->since);
> }
> printf("\t */\n");
> printf("\tvoid (*%s)(", m->name);
> @@ -1112,6 +1140,9 @@ emit_structs(struct wl_list *message_list, struct interface *interface, enum sid
> printf("};\n\n");
>
> if (side == CLIENT) {
> + printf("/**\n"
> + " * @ingroup %s_iface\n"
> + " */\n", interface->name);
> printf("static inline int\n"
> "%s_add_listener(struct %s *%s,\n"
> "%sconst struct %s_listener *listener, void *data)\n"
> @@ -1128,30 +1159,6 @@ emit_structs(struct wl_list *message_list, struct interface *interface, enum sid
> }
>
> static void
> -format_copyright(const char *copyright)
> -{
> - int bol = 1, start = 0, i;
> -
> - for (i = 0; copyright[i]; i++) {
> - if (bol && (copyright[i] == ' ' || copyright[i] == '\t')) {
> - continue;
> - } else if (bol) {
> - bol = 0;
> - start = i;
> - }
> -
> - if (copyright[i] == '\n' || copyright[i] == '\0') {
> - printf("%s%s%.*s\n",
> - i == 0 ? "/*" : " *",
> - (i - start) == 0 ? "" : " ",
Unfortunately since Jon got his fix in prior to yours, this patch breaks
at this point.
> - i - start, copyright + start);
> - bol = 1;
> - }
> - }
> - printf(" */\n\n");
> -}
> -
> -static void
> emit_types_forward_declarations(struct protocol *protocol,
> struct wl_list *message_list,
> struct wl_array *types)
> @@ -1204,6 +1211,46 @@ get_include_name(bool core, enum side side)
> }
>
> static void
> +emit_mainpage_blurb(const struct protocol *protocol, enum side side)
> +{
> + struct interface *i;
> +
> + printf("/**\n"
> + " * @page page_%s The %s protocol\n",
> + protocol->name, protocol->name);
> +
> + if (protocol->description) {
> + if (protocol->description->summary) {
> + printf(" * %s\n"
> + " *\n", protocol->description->summary);
> + }
> +
> + if (protocol->description->text) {
> + printf(" * @section page_desc_%s Description\n", protocol->name);
> + format_text_to_comment(protocol->description->text, false);
> + printf(" *\n");
> + }
> + }
> +
> + printf(" * @section page_ifaces_%s Interfaces\n", protocol->name);
> + wl_list_for_each(i, &protocol->interface_list, link) {
> + printf(" * - @subpage page_iface_%s - %s\n",
> + i->name,
> + i->description && i->description->summary ? i->description->summary : "");
> + }
> +
> + if (protocol->copyright) {
> + printf(" * @section page_copyright_%s Copyright\n",
> + protocol->name);
> + printf(" * <pre>\n");
> + format_text_to_comment(protocol->copyright, false);
> + printf(" * </pre>\n");
> + }
> +
> + printf(" */\n");
> +}
> +
> +static void
> emit_header(struct protocol *protocol, enum side side)
> {
> struct interface *i, *i_next;
> @@ -1211,9 +1258,6 @@ emit_header(struct protocol *protocol, enum side side)
> const char *s = (side == SERVER) ? "SERVER" : "CLIENT";
> char **p, *prev;
>
> - if (protocol->copyright)
> - format_copyright(protocol->copyright);
> -
> printf("#ifndef %s_%s_PROTOCOL_H\n"
> "#define %s_%s_PROTOCOL_H\n"
> "\n"
> @@ -1230,6 +1274,8 @@ emit_header(struct protocol *protocol, enum side side)
> protocol->uppercase_name, s,
> get_include_name(protocol->core_headers, side));
>
> + emit_mainpage_blurb(protocol, side);
> +
> wl_array_init(&types);
> wl_list_for_each(i, &protocol->interface_list, link) {
> emit_types_forward_declarations(protocol, &i->request_list, &types);
> @@ -1253,6 +1299,23 @@ emit_header(struct protocol *protocol, enum side side)
> printf("\n");
>
> wl_list_for_each(i, &protocol->interface_list, link) {
> + printf("/**\n"
> + " * @page page_iface_%s %s\n",
> + i->name, i->name);
> + if (i->description && i->description->text) {
> + printf(" * @section page_iface_%s_desc Description\n",
> + i->name);
> + format_text_to_comment(i->description->text, false);
> + }
> + printf(" * @section page_iface_%s_api API\n"
> + " * See @ref iface_%s.\n",
> + i->name, i->name);
> + printf(" *\n"
> + " * @defgroup iface_%s The %s interface\n",
> + i->name, i->name);
> + if (i->description && i->description->text)
> + format_text_to_comment(i->description->text, false);
> + printf(" */\n");
> printf("extern const struct wl_interface "
> "%s_interface;\n", i->name);
> }
> @@ -1396,7 +1459,7 @@ emit_code(struct protocol *protocol)
> char **p, *prev;
>
> if (protocol->copyright)
> - format_copyright(protocol->copyright);
> + format_text_to_comment(protocol->copyright, true);
>
> printf("#include <stdlib.h>\n"
> "#include <stdint.h>\n"
> --
> 2.4.3
>
> _______________________________________________
> wayland-devel mailing list
> wayland-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/wayland-devel
More information about the wayland-devel
mailing list