[gtk-doc] Add support for inline program documentation



commit a9864c47fdd8abfd52978f100d654d6880908d66
Author: Thomas Wood <thomas wood intel com>
Date:   Wed Jul 8 14:27:46 2015 +0100

    Add support for inline program documentation
    
    Create program documentation from documentation comments in source
    files.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=752126

 configure.ac                           |    3 +
 gtkdoc-mkdb.in                         |  214 +++++++++++++++++++++++++++++++-
 tests/Makefile.am                      |    5 +-
 tests/program.sh                       |    4 +
 tests/program/Makefile.am              |   11 ++
 tests/program/docs/Makefile.am         |   81 ++++++++++++
 tests/program/docs/tester-docs.xml     |   37 ++++++
 tests/program/src/Makefile.am          |   10 ++
 tests/program/src/test-program.c       |   19 +++
 9 files changed, 381 insertions(+), 3 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 1046d27..7dfae29 100644
--- a/configure.ac
+++ b/configure.ac
@@ -261,6 +261,9 @@ tests/fail/docs/Makefile
 tests/empty/Makefile
 tests/empty/src/Makefile
 tests/empty/docs/Makefile
+tests/program/Makefile
+tests/program/src/Makefile
+tests/program/docs/Makefile
 ])
 
 dnl run chmod on these after parsing them.
diff --git a/gtkdoc-mkdb.in b/gtkdoc-mkdb.in
index 8dd6d5e..40db385 100755
--- a/gtkdoc-mkdb.in
+++ b/gtkdoc-mkdb.in
@@ -2585,6 +2585,160 @@ EOF
     return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
 }
 
+#############################################################################
+# Function    : OutputProgramDBFile
+# Description : Outputs the final DocBook file for one program.
+# Arguments   : $file - the name of the file.
+#               $section_id - the id to use for the toplevel tag.
+#############################################################################
+
+sub OutputProgramDBFile {
+    my ($program, $section_id) = @_;
+
+    @TRACE@("Output program docbook for $program\n");
+
+    my $short_desc = $SourceSymbolDocs{"$TMPL_DIR/$program:Short_Description"};
+    if (!defined ($short_desc) || $short_desc =~ m/^\s*$/) {
+        $short_desc = "";
+    } else {
+        # Don't use ConvertMarkDown here for now since we don't want blocks
+        $short_desc = &ExpandAbbreviations("$program", $short_desc);
+        @TRACE@("Found short_desc: $short_desc");
+    }
+
+    my $synopsis = $SourceSymbolDocs{"$TMPL_DIR/$program:Synopsis"};
+    if (defined ($synopsis) && $synopsis !~ m/^\s*$/) {
+        my $i;
+        my @items = split(' ', $synopsis);
+        for ($i = 0; $i <= $#items; $i++) {
+            my $parameter = $items[$i];
+            my $choice = "plain";
+            my $rep = "";
+
+            # first parameter is the command name
+            if ($i == 0) {
+                $synopsis = "<command>$parameter</command>\n";
+                next;
+            }
+
+            # square brackets indicate optional parameters, curly brackets
+            # indicate required parameters ("plain" parameters are also
+            # mandatory, but do not get extra decoration)
+            if ($parameter =~ s/^\[(.+?)\]$/$1/) {
+                $choice = "opt";
+            } elsif ($parameter =~ s/^\{(.+?)\}$/$1/) {
+                $choice = "req";
+            }
+
+            # parameters ending in "..." are repeatable
+            if ($parameter =~ s/\.\.\.$//) {
+                $rep = " rep=\"repeat\"";
+            }
+
+            # italic parameters are replaceable parameters
+            if ($parameter =~ s/\*(.+?)\*/$1/) {
+                $parameter = "<replaceable>$parameter</replaceable>";
+            }
+
+            $synopsis .= "<arg choice=\"$choice\"$rep>";
+            $synopsis .= $parameter;
+            $synopsis .= "</arg>\n";
+        }
+
+        @TRACE@("Found synopsis: $synopsis");
+    } else {
+        $synopsis = "<command>$program</command>";
+    }
+
+    my $long_desc = $SourceSymbolDocs{"$TMPL_DIR/$program:Long_Description"};
+    if (!defined ($long_desc) || $long_desc =~ m/^\s*$/) {
+        $long_desc = "";
+    } else {
+        $long_desc = &ConvertMarkDown("$program:Long_description", $long_desc);
+        @TRACE@("Found long_desc: $long_desc");
+    }
+
+    my $options = "";
+    if (defined ($SourceSymbolDocs{"$TMPL_DIR/$program:Options"})) {
+        my @opts = @{ $SourceSymbolDocs{"$TMPL_DIR/$program:Options"} };
+        my $k;
+
+        $options = "<refsect1>\n<title>Options</title>\n<variablelist>\n";
+        for ($k = 0; $k <= $#opts; $k += 2) {
+            my $opt_desc = $opts[$k+1];
+            my @opt_names;
+            my $i;
+
+            $opt_desc =~ s/\*(.+?)\*/<replaceable>$1<\/replaceable>/g;
+
+            $options .= "<varlistentry>\n<term>";
+            @opt_names = split (',', $opts[$k]);
+            for ($i = 0; $i <= $#opt_names; $i++) {
+                my $prefix = ($i > 0) ? ", " : "";
+                $opt_names[$i] =~ s/\*(.+?)\*/<replaceable>$1<\/replaceable>/g;
+
+                $options .= "$prefix<option>$opt_names[$i]</option>\n";
+            }
+            $options .= "</term>\n";
+            $options .= "<listitem><para>$opt_desc</para></listitem>\n";
+            $options .= "</varlistentry>\n";
+        }
+        $options .= "</variablelist></refsect1>\n";
+    }
+
+    my $exit_status = $SourceSymbolDocs{"$TMPL_DIR/$program:Returns"};
+    if (defined ($exit_status) && $exit_status ne "") {
+        $exit_status = &ConvertMarkDown("$program:Returns", $exit_status);
+        $exit_status = "<refsect1 id=\"$section_id.exit-status\">\n<title>Exit 
Status</title>\n$exit_status\n</refsect1>\n";
+    } else {
+        $exit_status = "";
+    }
+
+    my $see_also = $SourceSymbolDocs{"$TMPL_DIR/$program:See_Also"};
+    if (!defined ($see_also) || $see_also =~ m%^\s*(<para>)?\s*(</para>)?\s*$%) {
+        $see_also = "";
+    } else {
+        $see_also = &ConvertMarkDown("$program:See_Also", $see_also);
+        @TRACE@("Found see_also: $see_also");
+    }
+    if ($see_also) {
+        $see_also = "<refsect1 id=\"$section_id.see-also\">\n<title>See 
Also</title>\n$see_also\n</refsect1>\n";
+    }
+
+    my $old_db_file = "$DB_OUTPUT_DIR/$program.xml";
+    my $new_db_file = "$DB_OUTPUT_DIR/$program.xml.new";
+
+    open (OUTPUT, ">$new_db_file")
+        || die "Can't create $new_db_file: $!";
+
+    print OUTPUT <<EOF;
+${\( MakeDocHeader ("refentry") )}
+<refentry id="$section_id">
+<refmeta>
+<refentrytitle role="top_of_page" id="$section_id.top_of_page">$program</refentrytitle>
+<manvolnum>1</manvolnum>
+<refmiscinfo>User Commands</refmiscinfo>
+</refmeta>
+<refnamediv>
+<refname>$program</refname>
+<refpurpose>$short_desc</refpurpose>
+</refnamediv>
+<refsynopsisdiv>
+<cmdsynopsis>$synopsis</cmdsynopsis>
+</refsynopsisdiv>
+<refsect1 id="$section_id.description" role="desc">
+<title role="desc.title">Description</title>
+$long_desc
+</refsect1>
+$options$exit_status$see_also
+</refentry>
+EOF
+    close (OUTPUT);
+
+    return &UpdateFileIfChanged ($old_db_file, $new_db_file, 0);
+}
+
+
 
 #############################################################################
 # Function    : OutputExtraFile
@@ -4056,6 +4210,61 @@ sub ScanSourceFile {
                     $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
                     $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
                     #$SourceSymbolTypes{$symbol} = "SECTION";
+                } elsif ($symbol =~ m/PROGRAM:\s*(.*)/) {
+                    my $real_symbol = $1;
+                    my $key;
+                    my $section_id;
+
+                    @TRACE@("PROGRAM DOCS found in source for '$real_symbol'\n");
+                    for ($k = 0; $k <= $#params; $k += $PARAM_FIELD_COUNT) {
+                        @TRACE@("   '".$params[$k]."'\n");
+                        $params[$k] = "\L$params[$k]";
+                        undef $key;
+
+                        if ($params[$k] eq "short_description") {
+                            $key = "$TMPL_DIR/$real_symbol:Short_Description";
+                        } elsif ($params[$k] eq "see_also") {
+                            $key = "$TMPL_DIR/$real_symbol:See_Also";
+                        } elsif ($params[$k] eq "section_id") {
+                            $key = "$TMPL_DIR/$real_symbol:Section_Id";
+                        } elsif ($params[$k] eq "synopsis") {
+                            $key = "$TMPL_DIR/$real_symbol:Synopsis";
+                        } elsif ($params[$k] eq "returns") {
+                            $key = "$TMPL_DIR/$real_symbol:Returns";
+                        } elsif ($params[$k] =~ m/^(-.*)/) {
+                            $key = "$TMPL_DIR/$real_symbol:Options";
+                            my $opts;
+                            if (defined($SourceSymbolDocs{$key})) {
+                                $opts = $SourceSymbolDocs{$key};
+                            } else {
+                                $opts = [];
+                            }
+                            push (@{ $opts }, $1);
+                            push (@{ $opts }, $params[$k+1]);
+
+                            $SourceSymbolDocs{$key} = $opts;
+                            next;
+                        }
+                        if (defined($key)) {
+                            $SourceSymbolDocs{$key}=$params[$k+1];
+                            $SourceSymbolSourceFile{$key} = $file;
+                            $SourceSymbolSourceLine{$key} = $.;
+                        }
+                    }
+                    $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Long_Description"}=$description;
+                    $SourceSymbolSourceFile{"$TMPL_DIR/$real_symbol:Long_Description"} = $file;
+                    $SourceSymbolSourceLine{"$TMPL_DIR/$real_symbol:Long_Description"} = $.;
+
+                    $section_id = $SourceSymbolDocs{"$TMPL_DIR/$real_symbol:Section_Id"};
+                    if (defined ($section_id) && $section_id !~ m/^\s*$/) {
+                        # Remove trailing blanks and use as is
+                        $section_id =~ s/\s+$//;
+                    } else {
+                        $section_id = &CreateValidSGMLID ("$MODULE-$real_symbol");
+                    }
+
+                    &OutputProgramDBFile ($real_symbol, $section_id);
+
                 } else {
                     @TRACE@("SYMBOL DOCS found in source for : '$symbol' ",length($description), "\n");
                     $SourceSymbolDocs{$symbol} = $description;
@@ -4115,6 +4324,9 @@ sub ScanSourceFile {
             if (m%^\s*(SECTION:\s*\S+)%) {
                 $symbol = $1;
                 @TRACE@("SECTION DOCS found in source for : '$symbol'\n");
+            } elsif (m%^\s*(PROGRAM:\s*\S+)%) {
+                $symbol = $1;
+                @TRACE@("PROGRAM DOCS found in source for : '$symbol'\n");
             } elsif (m%^\s*([\w:-]*\w)\s*:?\s*(\([-A-Za-z0-9._() ]+?\)\s*)*$%) {
                 $symbol = $1;
                 my $annotation = $2;
@@ -4186,7 +4398,7 @@ sub ScanSourceFile {
         }
 
         # Look for a parameter name.
-        if (m%^\s*@(\S+)\s*:\s*%) {
+        if (m%^\s*@(.+?)\s*:\s*%) {
             my $param_name = $1;
             my $param_desc = $';
 
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 7e23744..5d1d2d6 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -1,12 +1,13 @@
 ## Process this file with automake to produce Makefile.in
 
-SUBDIRS = gobject bugs annotations fail empty .
+SUBDIRS = gobject bugs annotations fail empty program .
 
 if BUILD_TESTS
 
 TESTS = \
   gtkdoc-common.t gtkdoc-fixxref.t gtkdoc-mkdb.t gtkdoc-rebase.t gtkdoc-scan.t \
-  tools.sh gobject.sh bugs.sh annotations.sh fail.sh empty.sh sanity.sh
+  tools.sh gobject.sh bugs.sh annotations.sh fail.sh empty.sh sanity.sh \
+  program.sh
 TESTS_ENVIRONMENT = \
        BUILDDIR=$(abs_builddir) \
        SRCDIR=$(abs_srcdir) \
diff --git a/tests/program.sh b/tests/program.sh
new file mode 100755
index 0000000..eb5d511
--- /dev/null
+++ b/tests/program.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+gtkdoctest.sh program
+
diff --git a/tests/program/Makefile.am b/tests/program/Makefile.am
new file mode 100644
index 0000000..1653011
--- /dev/null
+++ b/tests/program/Makefile.am
@@ -0,0 +1,11 @@
+## Process this file with automake to produce Makefile.in
+
+SUBDIRS = . src docs
+
+if BUILD_TESTS
+
+check-local: clean
+
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/tests/program/docs/Makefile.am b/tests/program/docs/Makefile.am
new file mode 100644
index 0000000..7659310
--- /dev/null
+++ b/tests/program/docs/Makefile.am
@@ -0,0 +1,81 @@
+## Process this file with automake to produce Makefile.in
+
+# We require automake 1.6 at least.
+AUTOMAKE_OPTIONS = 1.6
+
+# The name of the module, e.g. 'glib'.
+DOC_MODULE=tester
+
+# The top-level SGML file. You can change this if you want to.
+DOC_MAIN_SGML_FILE=$(DOC_MODULE)-docs.xml
+
+# The directory containing the source code. Relative to $(srcdir).
+# gtk-doc will search all .c & .h files beneath here for inline comments
+# documenting the functions and macros.
+DOC_SOURCE_DIR=$(top_srcdir)/tests/program/src
+
+# Extra options to pass to gtkdoc-scangobj. Not normally needed.
+SCANGOBJ_OPTIONS=
+
+# Extra options to supply to gtkdoc-scan.
+SCAN_OPTIONS=--deprecated-guards="GTKDOC_TESTER_DISABLE_DEPRECATED" \
+  --rebuild-types
+
+# Extra options to supply to gtkdoc-mkdb.
+MKDB_OPTIONS=--xml-mode
+
+# Extra options to supply to gtkdoc-mktmpl
+# e.g. MKTMPL_OPTIONS=--only-section-tmpl
+MKTMPL_OPTIONS=
+
+# Extra options to supply to gtkdoc-mkhtml
+MKHTML_OPTIONS=
+
+# Extra options to supply to gtkdoc-fixref. Not normally needed.
+# --html-dir=$(HTML_DIR)
+FIXXREF_OPTIONS=--extra-dir=$(glib_prefix)/share/gtk-doc/html
+
+# Used for dependencies. The docs will be rebuilt if any of these change.
+HFILE_GLOB=
+CFILE_GLOB=$(top_srcdir)/tests/program/src/*.c
+
+# Header files to ignore when scanning.
+IGNORE_HFILES=config.h
+
+# Images to copy into HTML directory.
+HTML_IMAGES =
+
+# Extra SGML files that are included by $(DOC_MAIN_SGML_FILE).
+# e.g. content_files=running.sgml building.sgml changes-2.0.sgml
+content_files =
+
+# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
+# These files must be listed here *and* in content_files
+# e.g. expand_content_files=running.sgml
+expand_content_files=
+
+# CFLAGS and LDFLAGS for compiling gtkdoc-scangobj with your library.
+# Only needed if you are using gtkdoc-scangobj to dynamically query widget
+# signals and properties.
+GTKDOC_CFLAGS =
+GTKDOC_LIBS =
+
+# include generic part
+include $(top_srcdir)/tests/gtk-doc.notmpl.make
+
+# Other files to distribute
+# e.g. EXTRA_DIST += version.xml.in
+EXTRA_DIST +=
+
+CLEANFILES += \
+  $(DOC_MODULE)-overrides.txt \
+  $(DOC_MODULE).types
+
+if BUILD_TESTS
+TESTS_ENVIRONMENT = \
+       DOC_MODULE=$(DOC_MODULE) DOC_MAIN_SGML_FILE=$(DOC_MAIN_SGML_FILE) \
+       PATH=$(abs_top_builddir):$(srcdir):$(PATH) \
+       PERL5LIB=$(abs_top_builddir):$(PERL5LIB)
+endif
+
+-include $(top_srcdir)/git.mk
diff --git a/tests/program/docs/tester-docs.xml b/tests/program/docs/tester-docs.xml
new file mode 100644
index 0000000..1bb342f
--- /dev/null
+++ b/tests/program/docs/tester-docs.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.3//EN"
+               "http://www.oasis-open.org/docbook/xml/4.3/docbookx.dtd";
+[
+  <!ENTITY % local.common.attrib "xmlns:xi  CDATA  #FIXED 'http://www.w3.org/2003/XInclude'">
+  <!ENTITY % gtkdocentities SYSTEM "xml/gtkdocentities.ent">
+  %gtkdocentities;
+]>
+<book id="index" xmlns:xi="http://www.w3.org/2003/XInclude";>
+  <bookinfo>
+    <title>&package_name; Reference Manual</title>
+    <releaseinfo>
+      for &package_string;.
+      The latest version of this documentation can be found on-line at
+      <ulink role="online-location" 
url="http://[SERVER]/&package_name;/index.html";>http://[SERVER]/&package_name;/</ulink>.
+    </releaseinfo>
+  </bookinfo>
+
+  <chapter>
+    <title>Programs</title>
+    <xi:include href="xml/test-program.xml"/>
+  </chapter>
+
+  <index id="api-index">
+    <title>API Index</title>
+    <xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
+  </index>
+  <index id="deprecated-api-index" role="deprecated">
+    <title>Index of deprecated API</title>
+    <xi:include href="xml/api-index-deprecated.xml"><xi:fallback /></xi:include>
+  </index>
+  <index id="api-index-0-1" role="0.1">
+    <title>Index of new API in 0.1</title>
+    <xi:include href="xml/api-index-0.1.xml"><xi:fallback /></xi:include>
+  </index>
+
+</book>
diff --git a/tests/program/docs/tester-sections.txt b/tests/program/docs/tester-sections.txt
new file mode 100644
index 0000000..e69de29
diff --git a/tests/program/src/Makefile.am b/tests/program/src/Makefile.am
new file mode 100644
index 0000000..041a70c
--- /dev/null
+++ b/tests/program/src/Makefile.am
@@ -0,0 +1,10 @@
+## Process this file with automake to produce Makefile.in
+
+if BUILD_TESTS
+
+noinst_PROGRAMS = test-program
+
+endif
+
+
+-include $(top_srcdir)/git.mk
diff --git a/tests/program/src/test-program.c b/tests/program/src/test-program.c
new file mode 100644
index 0000000..fa60fef
--- /dev/null
+++ b/tests/program/src/test-program.c
@@ -0,0 +1,19 @@
+
+/**
+ * PROGRAM:test-program
+ * @short_description: A test program
+ * @synopsis: test-program [*OPTIONS*...] --arg1 *arg* *FILE*
+ * @see_also: test(1)
+ * @--arg1 *arg*: set arg1 to *arg*
+ * @-v, --version: Print the version number
+ * @-h, --help: Print the help message
+ *
+ * Long description of program.
+ *
+ * Returns: Zero on success, non-zero on failure
+ */
+
+int main(int argc, char **argv)
+{
+       return 0;
+}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]