[PATCH v4 wayland 3/3] doc: generate doxygen html output from the scanner

Peter Hutterer peter.hutterer at who-t.net
Mon Feb 29 23:26:21 UTC 2016


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. That documentation is now in
Client/ and Server/, respectively.

GENERATE_HTML is on by default and must be disabled for the xml/man targets to
avoid messing up the new documentation. We disable all three three targets in
the doxyfile (xml and man default to NO anyway) to make it obvious that they
need to be set in the per-target instructions.

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. EXTRACT_STATIC is needed since most of the protocol
hooks are static.

Signed-off-by: Peter Hutterer <peter.hutterer at who-t.net>
---
Changes to v3:
- disable GENERATE_HTML/XML/MAN in the doxyfile

 doc/doxygen/Makefile.am        |  27 ++++-
 doc/doxygen/mainpage.dox       |  13 +++
 doc/doxygen/wayland.doxygen.in |   6 ++
 src/scanner.c                  | 220 +++++++++++++++++++++++++++--------------
 4 files changed, 188 insertions(+), 78 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..9d7fa0c 100644
--- a/doc/doxygen/wayland.doxygen.in
+++ b/doc/doxygen/wayland.doxygen.in
@@ -13,4 +13,10 @@ MACRO_EXPANSION        = YES
 EXPAND_ONLY_PREDEF     = YES
 DOT_MULTI_TARGETS      = YES
 ALIASES                += comment{1}="/* \1 *<!-- -->/"
+OPTIMIZE_OUTPUT_FOR_C  = YES
+EXTRACT_ALL            = YES
+EXTRACT_STATIC         = YES
+# These must be set in the Makefile
 GENERATE_HTML          = NO
+GENERATE_XML           = NO
+GENERATE_MAN           = NO
diff --git a/src/scanner.c b/src/scanner.c
index d3e2328..5fbc2df 100644
--- a/src/scanner.c
+++ b/src/scanner.c
@@ -877,6 +877,34 @@ character_data(void *data, const XML_Char *s, int len)
 }
 
 static void
+format_text_to_comment(const char *text, bool standalone_comment)
+{
+	int bol = 1, start = 0, i, length;
+	bool comment_started = !standalone_comment;
+
+	length = strlen(text);
+	for (i = 0; i <= length; i++) {
+		if (bol && (text[i] == ' ' || text[i] == '\t')) {
+			continue;
+		} else if (bol) {
+			bol = 0;
+			start = i;
+		}
+		if (text[i] == '\n' ||
+		    (text[i] == '\0' && !(start == i))) {
+			printf("%s%s%.*s\n",
+			       comment_started ? " *" : "/*",
+			       i > start ? " " : "",
+			       i - start, text + start);
+			bol = 1;
+			comment_started = true;
+		}
+	}
+	if (comment_started && standalone_comment)
+		printf(" */\n\n");
+}
+
+static void
 emit_opcodes(struct wl_list *message_list, struct interface *interface)
 {
 	struct message *m;
@@ -898,9 +926,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");
 }
@@ -940,6 +970,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"
@@ -948,6 +979,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"
@@ -981,7 +1013,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"
@@ -990,6 +1023,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;
@@ -1009,6 +1043,11 @@ emit_stubs(struct wl_list *message_list, struct interface *interface)
 				ret = a;
 		}
 
+		printf("/**\n"
+		       " * @ingroup iface_%s\n", interface->name);
+		if (m->description && m->description->text)
+			format_text_to_comment(m->description->text, false);
+		printf(" */\n");
 		if (ret && ret->interface_name == NULL)
 			printf("static inline void *\n");
 		else if (ret)
@@ -1104,6 +1143,17 @@ 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,
+		       m->name);
+		printf("* @param resource_ The client's resource\n");
+		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);
@@ -1150,28 +1200,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);
 			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);
@@ -1188,20 +1233,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");
 
@@ -1209,24 +1245,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);
@@ -1266,6 +1302,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"
@@ -1282,34 +1321,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, length;
-	bool comment_started = false;
-
-	length = strlen(copyright);
-	for (i = 0; i <= length; i++) {
-		if (bol && (copyright[i] == ' ' || copyright[i] == '\t')) {
-			continue;
-		} else if (bol) {
-			bol = 0;
-			start = i;
-		}
-		if (copyright[i] == '\n' ||
-		    (copyright[i] == '\0' && !(start == i))) {
-			printf("%s%s%.*s\n",
-			       comment_started ? " *" : "/*",
-			       i > start ? " " : "",
-			       i - start, copyright + start);
-			bol = 1;
-			comment_started = true;
-		}
-	}
-	if (comment_started)
-		printf(" */\n\n");
-}
-
-static void
 emit_types_forward_declarations(struct protocol *protocol,
 				struct wl_list *message_list,
 				struct wl_array *types)
@@ -1362,6 +1373,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;
@@ -1369,9 +1420,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"
@@ -1388,6 +1436,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);
@@ -1411,6 +1461,24 @@ 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"
+		       " */\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);
 	}
@@ -1554,7 +1622,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.5.0



More information about the wayland-devel mailing list