Fixup XML generation to avoid erroneous copying.
authorjcorgan <jcorgan@221aa14e-8319-0410-a670-987f0aec2ac5>
Sun, 24 Aug 2008 19:57:24 +0000 (19:57 +0000)
committerjcorgan <jcorgan@221aa14e-8319-0410-a670-987f0aec2ac5>
Sun, 24 Aug 2008 19:57:24 +0000 (19:57 +0000)
git-svn-id: http://gnuradio.org/svn/gnuradio/trunk@9399 221aa14e-8319-0410-a670-987f0aec2ac5

config/grc_gnuradio_core.m4
gnuradio-core/doc/Makefile.am
gnuradio-core/doc/xml-swig/Makefile.am [new file with mode: 0644]
gnuradio-core/doc/xml-swig/README [new file with mode: 0644]
gnuradio-core/doc/xml-swig/doxy2swig.py [new file with mode: 0644]
gnuradio-core/doc/xml-swig/swig.xsl [new file with mode: 0644]
gnuradio-core/doc/xml/Makefile.am [deleted file]
gnuradio-core/doc/xml/README [deleted file]
gnuradio-core/doc/xml/doxy2swig.py [deleted file]
gnuradio-core/doc/xml/swig.xsl [deleted file]

index 562641ebe9af6d4050f734db73ec54e0776f4ce8..df2ec305de708bdfd5051257dd00b8d4bcdb1a9f 100644 (file)
@@ -80,7 +80,7 @@ AC_DEFUN([GRC_GNURADIO_CORE],[
         gnuradio-core/doc/Doxyfile \
         gnuradio-core/doc/Makefile \
         gnuradio-core/doc/other/Makefile \
-        gnuradio-core/doc/xml/Makefile \
+        gnuradio-core/doc/xml-swig/Makefile \
         gnuradio-core/src/Makefile \
         gnuradio-core/src/gen_interpolator_taps/Makefile \
         gnuradio-core/src/lib/Makefile \
index a499946a1bab030c919eaf7a682c36c926a35599..1fd11a92ab311d17a134b32f8002cbdef98a0efb 100644 (file)
@@ -21,7 +21,7 @@
 
 include $(top_srcdir)/Makefile.common
 
-SUBDIRS = other xml
+SUBDIRS = other xml-swig
 
 docdir  = $(prefix)/share/doc/@PACKAGE@-@VERSION@
 
@@ -36,6 +36,7 @@ html/index.html:
 
 prep:
        mkdir -p html
+       mkdir -p xml
 
 install-data-local:
        $(mkinstalldirs) $(DESTDIR)$(docdir)
diff --git a/gnuradio-core/doc/xml-swig/Makefile.am b/gnuradio-core/doc/xml-swig/Makefile.am
new file mode 100644 (file)
index 0000000..f508deb
--- /dev/null
@@ -0,0 +1,25 @@
+#
+# Copyright 2005 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+# 
+
+EXTRA_DIST = README doxy2swig.py swig.xsl
+
+CLEANFILES = *.xml combine.xslt compound.xsd index.xsd
+
diff --git a/gnuradio-core/doc/xml-swig/README b/gnuradio-core/doc/xml-swig/README
new file mode 100644 (file)
index 0000000..e5187df
--- /dev/null
@@ -0,0 +1,129 @@
+#
+# Copyright 2005 Free Software Foundation, Inc.
+# 
+# This file is part of GNU Radio
+# 
+# GNU Radio is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3, or (at your option)
+# any later version.
+# 
+# GNU Radio is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GNU Radio; see the file COPYING.  If not, write to
+# the Free Software Foundation, Inc., 51 Franklin Street,
+# Boston, MA 02110-1301, USA.
+#
+
+#
+# Generating Python docstrings from C++ code using doxygen
+#
+
+There are at least two strategies for this:
+  - use xsltproc as described below
+  - use doxy2swig.py (included in this directory)
+
+FIXME: get one of these working (probably doxy2swig since it doesn't
+add any additional dependencies).
+
+----------------------------------------------------------------
+
+Note: Robin's patch is in SWIG >= 1.3.23
+
+--------------------------------------------------------------------------------
+From: http://mailman.cs.uchicago.edu/pipermail/swig/2004-October/010604.html
+
+> I applied the docstring patch. '%feature("autodoc",1");' is working as 
+> expected ...
+
+[problem solved. ...]
+
+> I can not agree more with the doxygen idea. I am using doxygen for 
+> documentation and I have been trying to put doxygen output to the 
+> python interface. Automatic generation of %feature("docstring") lines 
+> from doxygen output is the closest solution I can think of but the 
+> workload is still pretty big. How I wish this feature can be 
+> implemented in the near future.
+
+I have successfully extracted function/class description from doxygen 
+generated xml files, using an xslt script. To add doxygen generated 
+description to each class/function, you will need to (tested under 
+linux, note that this works only for Python, using Robin's docstring 
+patch)
+
+1. download swig source, apply Robin's docstring patch from
+   https://sourceforge.net/tracker/index.php?func=detail&aid=1023309&group_id=1645&atid=301645
+   compile and install
+
+2. generate doxygen document with option "GENERATE_XML = YES"
+
+3. copy the attached script (save as swig.xsl) to the doc/xml directory 
+   and run
+
+     > xsltproc swig.xsl index.xml > temp_doc.i
+     > cat temp_doc.i | sed 's/"/\\"/g' | sed 's/__QuOtE__/"/g' > swig_doc.i
+
+   you will get an interface file with lines like
+     %feature("docstring") class "class description";
+     %feature("docstring") class::function "member function 
+         description";
+
+   the second step is necessary since there might be " in descriptions 
+   and I need to backquote them before I replace __QuOtE__ by real 
+   quotes. (xslt experts may know how to post-process <xsl:value-of> and 
+   make the script easier to use.)
+
+4. in your interface file, add
+     %include "siwg_doc.i"
+     %feature("autodoc","1") ;
+
+Hope this helps.
+
+swig.xsl:
+=========================================================
+
+<!-- XSLT script to extract document for class/function for swig docstring
+     If you have xsltproc you could use:
+     xsltproc swig.xsl index.xml > swig_doc.i
+-->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+  <xsl:output method="text"/>
+   <xsl:template match="/">
+      <!-- process each compound -->
+      <xsl:for-each select="doxygenindex/compound">
+        <xsl:apply-templates select="document( concat( @refid, '.xml' ) )/*" />
+      </xsl:for-each>
+   </xsl:template>
+
+  <xsl:template match="doxygen">
+    <xsl:for-each select="compounddef[@kind='class']">  
+      <xsl:text>%feature(__QuOtE__docstring__QuOtE__) </xsl:text>
+      <xsl:value-of select="compoundname"/>
+      <xsl:text> __QuOtE__ &#10;</xsl:text>
+      <xsl:value-of select="briefdescription"/><xsl:text>
+      </xsl:text>
+      <xsl:value-of select="detaileddescription"/>
+      <xsl:text> see also: </xsl:text>
+      <xsl:value-of select="includes"/>
+      <xsl:text>__QuOtE__;&#10;&#10;</xsl:text>
+
+      <!-- output for each function individually -->
+      <xsl:for-each select="*/memberdef[@kind='function' and not(starts-with(name,'operator'))]"> 
+        <xsl:text>%feature(__QuOtE__docstring__QuOtE__) </xsl:text><xsl:value-of select="../../compoundname"/>::<xsl:value-of select="name"/>
+        <xsl:text> __QuOtE__&#10;</xsl:text>
+        <xsl:value-of select="definition"/> <xsl:value-of select="argsstring"/>
+        <xsl:text>
+        </xsl:text><xsl:value-of select="briefdescription"/><xsl:text>
+        </xsl:text><xsl:value-of select="detaileddescription"/>
+        <xsl:text>__QuOtE__; &#10;&#10;</xsl:text>
+      </xsl:for-each>
+    </xsl:for-each>  
+  </xsl:template>
+</xsl:stylesheet>
+
+--
+Bo Peng
\ No newline at end of file
diff --git a/gnuradio-core/doc/xml-swig/doxy2swig.py b/gnuradio-core/doc/xml-swig/doxy2swig.py
new file mode 100644 (file)
index 0000000..1e379d9
--- /dev/null
@@ -0,0 +1,371 @@
+#!/usr/bin/env python
+"""Doxygen XML to SWIG docstring converter.
+
+Converts Doxygen generated XML files into a file containing docstrings
+that can be used by SWIG-1.3.x.  Note that you need to get SWIG
+version > 1.3.23 or use Robin Dunn's docstring patch to be able to use
+the resulting output.
+
+Usage:
+
+  doxy2swig.py input.xml output.i
+
+input.xml is your doxygen generated XML file and output.i is where the
+output will be written (the file will be clobbered).
+
+"""
+
+# This code is implemented using Mark Pilgrim's code as a guideline:
+#   http://www.faqs.org/docs/diveintopython/kgp_divein.html
+#
+# Author: Prabhu Ramachandran
+# License: BSD style
+
+
+from xml.dom import minidom
+import re
+import textwrap
+import sys
+import types
+import os.path
+
+
+def my_open_read(source):
+    if hasattr(source, "read"):
+        return source
+    else:
+        return open(source)
+
+def my_open_write(dest):
+    if hasattr(dest, "write"):
+        return dest
+    else:
+        return open(dest, 'w')
+
+
+class Doxy2SWIG:    
+    """Converts Doxygen generated XML files into a file containing
+    docstrings that can be used by SWIG-1.3.x that have support for
+    feature("docstring").  Once the data is parsed it is stored in
+    self.pieces.
+
+    """    
+    
+    def __init__(self, src):
+        """Initialize the instance given a source object (file or
+        filename).
+
+        """
+        f = my_open_read(src)
+        self.my_dir = os.path.dirname(f.name)
+        self.xmldoc = minidom.parse(f).documentElement
+        f.close()
+
+        self.pieces = []
+        self.pieces.append('\n// File: %s\n'%\
+                           os.path.basename(f.name))
+
+        self.space_re = re.compile(r'\s+')
+        self.lead_spc = re.compile(r'^(%feature\S+\s+\S+\s*?)"\s+(\S)')
+        self.multi = 0
+        self.ignores = ('inheritancegraph', 'param', 'listofallmembers',
+                        'innerclass', 'name', 'declname', 'incdepgraph',
+                        'invincdepgraph', 'programlisting', 'type',
+                        'references', 'referencedby', 'location',
+                        'collaborationgraph', 'reimplements',
+                        'reimplementedby', 'derivedcompoundref',
+                        'basecompoundref')
+        #self.generics = []
+        
+    def generate(self):
+        """Parses the file set in the initialization.  The resulting
+        data is stored in `self.pieces`.
+
+        """
+        self.parse(self.xmldoc)
+    
+    def parse(self, node):
+        """Parse a given node.  This function in turn calls the
+        `parse_<nodeType>` functions which handle the respective
+        nodes.
+
+        """
+        pm = getattr(self, "parse_%s"%node.__class__.__name__)
+        pm(node)
+
+    def parse_Document(self, node):
+        self.parse(node.documentElement)
+
+    def parse_Text(self, node):
+        txt = node.data
+        txt = txt.replace('\\', r'\\\\')
+        txt = txt.replace('"', r'\"')
+        # ignore pure whitespace
+        m = self.space_re.match(txt)
+        if m and len(m.group()) == len(txt):
+            pass
+        else:
+            self.add_text(textwrap.fill(txt))
+
+    def parse_Element(self, node):
+        """Parse an `ELEMENT_NODE`.  This calls specific
+        `do_<tagName>` handers for different elements.  If no handler
+        is available the `generic_parse` method is called.  All
+        tagNames specified in `self.ignores` are simply ignored.
+        
+        """
+        name = node.tagName
+        ignores = self.ignores
+        if name in ignores:
+            return
+        attr = "do_%s" % name
+        if hasattr(self, attr):
+            handlerMethod = getattr(self, attr)
+            handlerMethod(node)
+        else:
+            self.generic_parse(node)
+            #if name not in self.generics: self.generics.append(name)
+
+    def add_text(self, value):
+        """Adds text corresponding to `value` into `self.pieces`."""
+        if type(value) in (types.ListType, types.TupleType):
+            self.pieces.extend(value)
+        else:
+            self.pieces.append(value)
+
+    def get_specific_nodes(self, node, names):
+        """Given a node and a sequence of strings in `names`, return a
+        dictionary containing the names as keys and child
+        `ELEMENT_NODEs`, that have a `tagName` equal to the name.
+
+        """
+        nodes = [(x.tagName, x) for x in node.childNodes \
+                 if x.nodeType == x.ELEMENT_NODE and \
+                 x.tagName in names]
+        return dict(nodes)
+
+    def generic_parse(self, node, pad=0):
+        """A Generic parser for arbitrary tags in a node.
+
+        Parameters:
+
+         - node:  A node in the DOM.
+         - pad: `int` (default: 0)
+
+           If 0 the node data is not padded with newlines.  If 1 it
+           appends a newline after parsing the childNodes.  If 2 it
+           pads before and after the nodes are processed.  Defaults to
+           0.
+
+        """
+        npiece = 0
+        if pad:
+            npiece = len(self.pieces)
+            if pad == 2:
+                self.add_text('\n')                
+        for n in node.childNodes:
+            self.parse(n)
+        if pad:
+            if len(self.pieces) > npiece:
+                self.add_text('\n')
+
+    def space_parse(self, node):
+        self.add_text(' ')
+        self.generic_parse(node)
+
+    do_ref = space_parse
+    do_emphasis = space_parse
+    do_bold = space_parse
+    do_computeroutput = space_parse
+    do_formula = space_parse
+
+    def do_compoundname(self, node):
+        self.add_text('\n\n')
+        data = node.firstChild.data
+        self.add_text('%%feature("docstring") %s "\n'%data)
+
+    def do_compounddef(self, node):
+        kind = node.attributes['kind'].value
+        if kind in ('class', 'struct'):
+            prot = node.attributes['prot'].value
+            if prot <> 'public':
+                return
+            names = ('compoundname', 'briefdescription',
+                     'detaileddescription', 'includes')
+            first = self.get_specific_nodes(node, names)
+            for n in names:
+                if first.has_key(n):
+                    self.parse(first[n])
+            self.add_text(['";','\n'])
+            for n in node.childNodes:
+                if n not in first.values():
+                    self.parse(n)
+        elif kind in ('file', 'namespace'):
+            nodes = node.getElementsByTagName('sectiondef')
+            for n in nodes:
+                self.parse(n)
+
+    def do_includes(self, node):
+        self.add_text('C++ includes: ')
+        self.generic_parse(node, pad=1)
+
+    def do_parameterlist(self, node):
+        self.add_text(['\n', '\n', 'Parameters:', '\n'])
+        self.generic_parse(node, pad=1)
+
+    def do_para(self, node):
+        self.add_text('\n')
+        self.generic_parse(node, pad=1)
+
+    def do_parametername(self, node):
+        self.add_text('\n')
+        self.add_text("%s: "%node.firstChild.data)
+
+    def do_parameterdefinition(self, node):
+        self.generic_parse(node, pad=1)
+
+    def do_detaileddescription(self, node):
+        self.generic_parse(node, pad=1)
+
+    def do_briefdescription(self, node):
+        self.generic_parse(node, pad=1)
+
+    def do_memberdef(self, node):
+        prot = node.attributes['prot'].value
+        id = node.attributes['id'].value
+        kind = node.attributes['kind'].value
+        tmp = node.parentNode.parentNode.parentNode
+        compdef = tmp.getElementsByTagName('compounddef')[0]
+        cdef_kind = compdef.attributes['kind'].value
+        
+        if prot == 'public':
+            first = self.get_specific_nodes(node, ('definition', 'name'))
+            name = first['name'].firstChild.data
+            if name[:8] == 'operator': # Don't handle operators yet.
+                return
+
+            defn = first['definition'].firstChild.data
+            self.add_text('\n')
+            self.add_text('%feature("docstring") ')
+            
+            anc = node.parentNode.parentNode
+            if cdef_kind in ('file', 'namespace'):
+                ns_node = anc.getElementsByTagName('innernamespace')
+                if not ns_node and cdef_kind == 'namespace':
+                    ns_node = anc.getElementsByTagName('compoundname')
+                if ns_node:
+                    ns = ns_node[0].firstChild.data
+                    self.add_text(' %s::%s "\n%s'%(ns, name, defn))
+                else:
+                    self.add_text(' %s "\n%s'%(name, defn))
+            elif cdef_kind in ('class', 'struct'):
+                # Get the full function name.
+                anc_node = anc.getElementsByTagName('compoundname')
+                cname = anc_node[0].firstChild.data
+                self.add_text(' %s::%s "\n%s'%(cname, name, defn))
+
+            for n in node.childNodes:
+                if n not in first.values():
+                    self.parse(n)
+            self.add_text(['";', '\n'])
+        
+    def do_definition(self, node):
+        data = node.firstChild.data
+        self.add_text('%s "\n%s'%(data, data))
+
+    def do_sectiondef(self, node):
+        kind = node.attributes['kind'].value
+        if kind in ('public-func', 'func'):
+            self.generic_parse(node)
+
+    def do_simplesect(self, node):
+        kind = node.attributes['kind'].value
+        if kind in ('date', 'rcs', 'version'):
+            pass
+        elif kind == 'warning':
+            self.add_text(['\n', 'WARNING: '])
+            self.generic_parse(node)
+        elif kind == 'see':
+            self.add_text('\n')
+            self.add_text('See: ')
+            self.generic_parse(node)
+        else:
+            self.generic_parse(node)
+
+    def do_argsstring(self, node):
+        self.generic_parse(node, pad=1)
+
+    def do_member(self, node):
+        kind = node.attributes['kind'].value
+        refid = node.attributes['refid'].value
+        if kind == 'function' and refid[:9] == 'namespace':
+            self.generic_parse(node)
+
+    def do_doxygenindex(self, node):
+        self.multi = 1
+        comps = node.getElementsByTagName('compound')
+        for c in comps:
+            refid = c.attributes['refid'].value
+            fname = refid + '.xml'
+            if not os.path.exists(fname):
+                fname = os.path.join(self.my_dir,  fname)
+            print "parsing file: %s"%fname
+            p = Doxy2SWIG(fname)
+            p.generate()
+            self.pieces.extend(self.clean_pieces(p.pieces))
+
+    def write(self, fname):
+        o = my_open_write(fname)
+        if self.multi:
+            o.write("".join(self.pieces))
+        else:
+            o.write("".join(self.clean_pieces(self.pieces)))
+        o.close()
+
+    def clean_pieces(self, pieces):
+        """Cleans the list of strings given as `pieces`.  It replaces
+        multiple newlines by a maximum of 2 and returns a new list.
+        It also wraps the paragraphs nicely.
+        
+        """
+        ret = []
+        count = 0
+        for i in pieces:
+            if i == '\n':
+                count = count + 1
+            else:
+                if i == '";':
+                    if count:
+                        ret.append('\n')
+                elif count > 2:
+                    ret.append('\n\n')
+                elif count:
+                    ret.append('\n'*count)
+                count = 0
+                ret.append(i)
+
+        _data = "".join(ret)
+        ret = []
+        for i in _data.split('\n\n'):
+            if i == 'Parameters:':
+                ret.extend(['Parameters:\n-----------', '\n\n'])
+            elif i.find('// File:') > -1: # leave comments alone.
+                ret.extend([i, '\n'])
+            else:
+                _tmp = textwrap.fill(i.strip())
+                _tmp = self.lead_spc.sub(r'\1"\2', _tmp)
+                ret.extend([_tmp, '\n\n'])
+        return ret
+
+
+def main(input, output):
+    p = Doxy2SWIG(input)
+    p.generate()
+    p.write(output)
+
+
+if __name__ == '__main__':
+    if len(sys.argv) != 3:
+        print __doc__
+        sys.exit(1)
+    main(sys.argv[1], sys.argv[2])
diff --git a/gnuradio-core/doc/xml-swig/swig.xsl b/gnuradio-core/doc/xml-swig/swig.xsl
new file mode 100644 (file)
index 0000000..6163c2d
--- /dev/null
@@ -0,0 +1,38 @@
+<!-- XSLT script to extract document for class/function for swig docstring
+     If you have xsltproc you could use:
+     xsltproc swig.xsl index.xml > swig_doc.i
+-->
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+  <xsl:output method="text"/>
+   <xsl:template match="/">
+      <!-- process each compound -->
+      <xsl:for-each select="doxygenindex/compound">
+        <xsl:apply-templates select="document( concat( @refid, '.xml' ) )/*" />
+      </xsl:for-each>
+   </xsl:template>
+
+  <xsl:template match="doxygen">
+    <xsl:for-each select="compounddef[@kind='class']">  
+      <xsl:text>%feature(__QuOtE__docstring__QuOtE__) </xsl:text>
+      <xsl:value-of select="compoundname"/>
+      <xsl:text> __QuOtE__ &#10;</xsl:text>
+      <xsl:value-of select="briefdescription"/><xsl:text>
+      </xsl:text>
+      <xsl:value-of select="detaileddescription"/>
+      <xsl:text> see also: </xsl:text>
+      <xsl:value-of select="includes"/>
+      <xsl:text>__QuOtE__;&#10;&#10;</xsl:text>
+
+      <!-- output for each function individually -->
+      <xsl:for-each select="*/memberdef[@kind='function' and not(starts-with(name,'operator'))]"> 
+        <xsl:text>%feature(__QuOtE__docstring__QuOtE__) </xsl:text><xsl:value-of select="../../compoundname"/>::<xsl:value-of select="name"/>
+        <xsl:text> __QuOtE__&#10;</xsl:text>
+        <xsl:value-of select="definition"/> <xsl:value-of select="argsstring"/>
+        <xsl:text>
+        </xsl:text><xsl:value-of select="briefdescription"/><xsl:text>
+        </xsl:text><xsl:value-of select="detaileddescription"/>
+        <xsl:text>__QuOtE__; &#10;&#10;</xsl:text>
+      </xsl:for-each>
+    </xsl:for-each>  
+  </xsl:template>
+</xsl:stylesheet>
diff --git a/gnuradio-core/doc/xml/Makefile.am b/gnuradio-core/doc/xml/Makefile.am
deleted file mode 100644 (file)
index f508deb..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# Copyright 2005 Free Software Foundation, Inc.
-# 
-# This file is part of GNU Radio
-# 
-# GNU Radio is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3, or (at your option)
-# any later version.
-# 
-# GNU Radio is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with GNU Radio; see the file COPYING.  If not, write to
-# the Free Software Foundation, Inc., 51 Franklin Street,
-# Boston, MA 02110-1301, USA.
-# 
-
-EXTRA_DIST = README doxy2swig.py swig.xsl
-
-CLEANFILES = *.xml combine.xslt compound.xsd index.xsd
-
diff --git a/gnuradio-core/doc/xml/README b/gnuradio-core/doc/xml/README
deleted file mode 100644 (file)
index e5187df..0000000
+++ /dev/null
@@ -1,129 +0,0 @@
-#
-# Copyright 2005 Free Software Foundation, Inc.
-# 
-# This file is part of GNU Radio
-# 
-# GNU Radio is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 3, or (at your option)
-# any later version.
-# 
-# GNU Radio is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-# 
-# You should have received a copy of the GNU General Public License
-# along with GNU Radio; see the file COPYING.  If not, write to
-# the Free Software Foundation, Inc., 51 Franklin Street,
-# Boston, MA 02110-1301, USA.
-#
-
-#
-# Generating Python docstrings from C++ code using doxygen
-#
-
-There are at least two strategies for this:
-  - use xsltproc as described below
-  - use doxy2swig.py (included in this directory)
-
-FIXME: get one of these working (probably doxy2swig since it doesn't
-add any additional dependencies).
-
-----------------------------------------------------------------
-
-Note: Robin's patch is in SWIG >= 1.3.23
-
---------------------------------------------------------------------------------
-From: http://mailman.cs.uchicago.edu/pipermail/swig/2004-October/010604.html
-
-> I applied the docstring patch. '%feature("autodoc",1");' is working as 
-> expected ...
-
-[problem solved. ...]
-
-> I can not agree more with the doxygen idea. I am using doxygen for 
-> documentation and I have been trying to put doxygen output to the 
-> python interface. Automatic generation of %feature("docstring") lines 
-> from doxygen output is the closest solution I can think of but the 
-> workload is still pretty big. How I wish this feature can be 
-> implemented in the near future.
-
-I have successfully extracted function/class description from doxygen 
-generated xml files, using an xslt script. To add doxygen generated 
-description to each class/function, you will need to (tested under 
-linux, note that this works only for Python, using Robin's docstring 
-patch)
-
-1. download swig source, apply Robin's docstring patch from
-   https://sourceforge.net/tracker/index.php?func=detail&aid=1023309&group_id=1645&atid=301645
-   compile and install
-
-2. generate doxygen document with option "GENERATE_XML = YES"
-
-3. copy the attached script (save as swig.xsl) to the doc/xml directory 
-   and run
-
-     > xsltproc swig.xsl index.xml > temp_doc.i
-     > cat temp_doc.i | sed 's/"/\\"/g' | sed 's/__QuOtE__/"/g' > swig_doc.i
-
-   you will get an interface file with lines like
-     %feature("docstring") class "class description";
-     %feature("docstring") class::function "member function 
-         description";
-
-   the second step is necessary since there might be " in descriptions 
-   and I need to backquote them before I replace __QuOtE__ by real 
-   quotes. (xslt experts may know how to post-process <xsl:value-of> and 
-   make the script easier to use.)
-
-4. in your interface file, add
-     %include "siwg_doc.i"
-     %feature("autodoc","1") ;
-
-Hope this helps.
-
-swig.xsl:
-=========================================================
-
-<!-- XSLT script to extract document for class/function for swig docstring
-     If you have xsltproc you could use:
-     xsltproc swig.xsl index.xml > swig_doc.i
--->
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
-  <xsl:output method="text"/>
-   <xsl:template match="/">
-      <!-- process each compound -->
-      <xsl:for-each select="doxygenindex/compound">
-        <xsl:apply-templates select="document( concat( @refid, '.xml' ) )/*" />
-      </xsl:for-each>
-   </xsl:template>
-
-  <xsl:template match="doxygen">
-    <xsl:for-each select="compounddef[@kind='class']">  
-      <xsl:text>%feature(__QuOtE__docstring__QuOtE__) </xsl:text>
-      <xsl:value-of select="compoundname"/>
-      <xsl:text> __QuOtE__ &#10;</xsl:text>
-      <xsl:value-of select="briefdescription"/><xsl:text>
-      </xsl:text>
-      <xsl:value-of select="detaileddescription"/>
-      <xsl:text> see also: </xsl:text>
-      <xsl:value-of select="includes"/>
-      <xsl:text>__QuOtE__;&#10;&#10;</xsl:text>
-
-      <!-- output for each function individually -->
-      <xsl:for-each select="*/memberdef[@kind='function' and not(starts-with(name,'operator'))]"> 
-        <xsl:text>%feature(__QuOtE__docstring__QuOtE__) </xsl:text><xsl:value-of select="../../compoundname"/>::<xsl:value-of select="name"/>
-        <xsl:text> __QuOtE__&#10;</xsl:text>
-        <xsl:value-of select="definition"/> <xsl:value-of select="argsstring"/>
-        <xsl:text>
-        </xsl:text><xsl:value-of select="briefdescription"/><xsl:text>
-        </xsl:text><xsl:value-of select="detaileddescription"/>
-        <xsl:text>__QuOtE__; &#10;&#10;</xsl:text>
-      </xsl:for-each>
-    </xsl:for-each>  
-  </xsl:template>
-</xsl:stylesheet>
-
---
-Bo Peng
\ No newline at end of file
diff --git a/gnuradio-core/doc/xml/doxy2swig.py b/gnuradio-core/doc/xml/doxy2swig.py
deleted file mode 100644 (file)
index 1e379d9..0000000
+++ /dev/null
@@ -1,371 +0,0 @@
-#!/usr/bin/env python
-"""Doxygen XML to SWIG docstring converter.
-
-Converts Doxygen generated XML files into a file containing docstrings
-that can be used by SWIG-1.3.x.  Note that you need to get SWIG
-version > 1.3.23 or use Robin Dunn's docstring patch to be able to use
-the resulting output.
-
-Usage:
-
-  doxy2swig.py input.xml output.i
-
-input.xml is your doxygen generated XML file and output.i is where the
-output will be written (the file will be clobbered).
-
-"""
-
-# This code is implemented using Mark Pilgrim's code as a guideline:
-#   http://www.faqs.org/docs/diveintopython/kgp_divein.html
-#
-# Author: Prabhu Ramachandran
-# License: BSD style
-
-
-from xml.dom import minidom
-import re
-import textwrap
-import sys
-import types
-import os.path
-
-
-def my_open_read(source):
-    if hasattr(source, "read"):
-        return source
-    else:
-        return open(source)
-
-def my_open_write(dest):
-    if hasattr(dest, "write"):
-        return dest
-    else:
-        return open(dest, 'w')
-
-
-class Doxy2SWIG:    
-    """Converts Doxygen generated XML files into a file containing
-    docstrings that can be used by SWIG-1.3.x that have support for
-    feature("docstring").  Once the data is parsed it is stored in
-    self.pieces.
-
-    """    
-    
-    def __init__(self, src):
-        """Initialize the instance given a source object (file or
-        filename).
-
-        """
-        f = my_open_read(src)
-        self.my_dir = os.path.dirname(f.name)
-        self.xmldoc = minidom.parse(f).documentElement
-        f.close()
-
-        self.pieces = []
-        self.pieces.append('\n// File: %s\n'%\
-                           os.path.basename(f.name))
-
-        self.space_re = re.compile(r'\s+')
-        self.lead_spc = re.compile(r'^(%feature\S+\s+\S+\s*?)"\s+(\S)')
-        self.multi = 0
-        self.ignores = ('inheritancegraph', 'param', 'listofallmembers',
-                        'innerclass', 'name', 'declname', 'incdepgraph',
-                        'invincdepgraph', 'programlisting', 'type',
-                        'references', 'referencedby', 'location',
-                        'collaborationgraph', 'reimplements',
-                        'reimplementedby', 'derivedcompoundref',
-                        'basecompoundref')
-        #self.generics = []
-        
-    def generate(self):
-        """Parses the file set in the initialization.  The resulting
-        data is stored in `self.pieces`.
-
-        """
-        self.parse(self.xmldoc)
-    
-    def parse(self, node):
-        """Parse a given node.  This function in turn calls the
-        `parse_<nodeType>` functions which handle the respective
-        nodes.
-
-        """
-        pm = getattr(self, "parse_%s"%node.__class__.__name__)
-        pm(node)
-
-    def parse_Document(self, node):
-        self.parse(node.documentElement)
-
-    def parse_Text(self, node):
-        txt = node.data
-        txt = txt.replace('\\', r'\\\\')
-        txt = txt.replace('"', r'\"')
-        # ignore pure whitespace
-        m = self.space_re.match(txt)
-        if m and len(m.group()) == len(txt):
-            pass
-        else:
-            self.add_text(textwrap.fill(txt))
-
-    def parse_Element(self, node):
-        """Parse an `ELEMENT_NODE`.  This calls specific
-        `do_<tagName>` handers for different elements.  If no handler
-        is available the `generic_parse` method is called.  All
-        tagNames specified in `self.ignores` are simply ignored.
-        
-        """
-        name = node.tagName
-        ignores = self.ignores
-        if name in ignores:
-            return
-        attr = "do_%s" % name
-        if hasattr(self, attr):
-            handlerMethod = getattr(self, attr)
-            handlerMethod(node)
-        else:
-            self.generic_parse(node)
-            #if name not in self.generics: self.generics.append(name)
-
-    def add_text(self, value):
-        """Adds text corresponding to `value` into `self.pieces`."""
-        if type(value) in (types.ListType, types.TupleType):
-            self.pieces.extend(value)
-        else:
-            self.pieces.append(value)
-
-    def get_specific_nodes(self, node, names):
-        """Given a node and a sequence of strings in `names`, return a
-        dictionary containing the names as keys and child
-        `ELEMENT_NODEs`, that have a `tagName` equal to the name.
-
-        """
-        nodes = [(x.tagName, x) for x in node.childNodes \
-                 if x.nodeType == x.ELEMENT_NODE and \
-                 x.tagName in names]
-        return dict(nodes)
-
-    def generic_parse(self, node, pad=0):
-        """A Generic parser for arbitrary tags in a node.
-
-        Parameters:
-
-         - node:  A node in the DOM.
-         - pad: `int` (default: 0)
-
-           If 0 the node data is not padded with newlines.  If 1 it
-           appends a newline after parsing the childNodes.  If 2 it
-           pads before and after the nodes are processed.  Defaults to
-           0.
-
-        """
-        npiece = 0
-        if pad:
-            npiece = len(self.pieces)
-            if pad == 2:
-                self.add_text('\n')                
-        for n in node.childNodes:
-            self.parse(n)
-        if pad:
-            if len(self.pieces) > npiece:
-                self.add_text('\n')
-
-    def space_parse(self, node):
-        self.add_text(' ')
-        self.generic_parse(node)
-
-    do_ref = space_parse
-    do_emphasis = space_parse
-    do_bold = space_parse
-    do_computeroutput = space_parse
-    do_formula = space_parse
-
-    def do_compoundname(self, node):
-        self.add_text('\n\n')
-        data = node.firstChild.data
-        self.add_text('%%feature("docstring") %s "\n'%data)
-
-    def do_compounddef(self, node):
-        kind = node.attributes['kind'].value
-        if kind in ('class', 'struct'):
-            prot = node.attributes['prot'].value
-            if prot <> 'public':
-                return
-            names = ('compoundname', 'briefdescription',
-                     'detaileddescription', 'includes')
-            first = self.get_specific_nodes(node, names)
-            for n in names:
-                if first.has_key(n):
-                    self.parse(first[n])
-            self.add_text(['";','\n'])
-            for n in node.childNodes:
-                if n not in first.values():
-                    self.parse(n)
-        elif kind in ('file', 'namespace'):
-            nodes = node.getElementsByTagName('sectiondef')
-            for n in nodes:
-                self.parse(n)
-
-    def do_includes(self, node):
-        self.add_text('C++ includes: ')
-        self.generic_parse(node, pad=1)
-
-    def do_parameterlist(self, node):
-        self.add_text(['\n', '\n', 'Parameters:', '\n'])
-        self.generic_parse(node, pad=1)
-
-    def do_para(self, node):
-        self.add_text('\n')
-        self.generic_parse(node, pad=1)
-
-    def do_parametername(self, node):
-        self.add_text('\n')
-        self.add_text("%s: "%node.firstChild.data)
-
-    def do_parameterdefinition(self, node):
-        self.generic_parse(node, pad=1)
-
-    def do_detaileddescription(self, node):
-        self.generic_parse(node, pad=1)
-
-    def do_briefdescription(self, node):
-        self.generic_parse(node, pad=1)
-
-    def do_memberdef(self, node):
-        prot = node.attributes['prot'].value
-        id = node.attributes['id'].value
-        kind = node.attributes['kind'].value
-        tmp = node.parentNode.parentNode.parentNode
-        compdef = tmp.getElementsByTagName('compounddef')[0]
-        cdef_kind = compdef.attributes['kind'].value
-        
-        if prot == 'public':
-            first = self.get_specific_nodes(node, ('definition', 'name'))
-            name = first['name'].firstChild.data
-            if name[:8] == 'operator': # Don't handle operators yet.
-                return
-
-            defn = first['definition'].firstChild.data
-            self.add_text('\n')
-            self.add_text('%feature("docstring") ')
-            
-            anc = node.parentNode.parentNode
-            if cdef_kind in ('file', 'namespace'):
-                ns_node = anc.getElementsByTagName('innernamespace')
-                if not ns_node and cdef_kind == 'namespace':
-                    ns_node = anc.getElementsByTagName('compoundname')
-                if ns_node:
-                    ns = ns_node[0].firstChild.data
-                    self.add_text(' %s::%s "\n%s'%(ns, name, defn))
-                else:
-                    self.add_text(' %s "\n%s'%(name, defn))
-            elif cdef_kind in ('class', 'struct'):
-                # Get the full function name.
-                anc_node = anc.getElementsByTagName('compoundname')
-                cname = anc_node[0].firstChild.data
-                self.add_text(' %s::%s "\n%s'%(cname, name, defn))
-
-            for n in node.childNodes:
-                if n not in first.values():
-                    self.parse(n)
-            self.add_text(['";', '\n'])
-        
-    def do_definition(self, node):
-        data = node.firstChild.data
-        self.add_text('%s "\n%s'%(data, data))
-
-    def do_sectiondef(self, node):
-        kind = node.attributes['kind'].value
-        if kind in ('public-func', 'func'):
-            self.generic_parse(node)
-
-    def do_simplesect(self, node):
-        kind = node.attributes['kind'].value
-        if kind in ('date', 'rcs', 'version'):
-            pass
-        elif kind == 'warning':
-            self.add_text(['\n', 'WARNING: '])
-            self.generic_parse(node)
-        elif kind == 'see':
-            self.add_text('\n')
-            self.add_text('See: ')
-            self.generic_parse(node)
-        else:
-            self.generic_parse(node)
-
-    def do_argsstring(self, node):
-        self.generic_parse(node, pad=1)
-
-    def do_member(self, node):
-        kind = node.attributes['kind'].value
-        refid = node.attributes['refid'].value
-        if kind == 'function' and refid[:9] == 'namespace':
-            self.generic_parse(node)
-
-    def do_doxygenindex(self, node):
-        self.multi = 1
-        comps = node.getElementsByTagName('compound')
-        for c in comps:
-            refid = c.attributes['refid'].value
-            fname = refid + '.xml'
-            if not os.path.exists(fname):
-                fname = os.path.join(self.my_dir,  fname)
-            print "parsing file: %s"%fname
-            p = Doxy2SWIG(fname)
-            p.generate()
-            self.pieces.extend(self.clean_pieces(p.pieces))
-
-    def write(self, fname):
-        o = my_open_write(fname)
-        if self.multi:
-            o.write("".join(self.pieces))
-        else:
-            o.write("".join(self.clean_pieces(self.pieces)))
-        o.close()
-
-    def clean_pieces(self, pieces):
-        """Cleans the list of strings given as `pieces`.  It replaces
-        multiple newlines by a maximum of 2 and returns a new list.
-        It also wraps the paragraphs nicely.
-        
-        """
-        ret = []
-        count = 0
-        for i in pieces:
-            if i == '\n':
-                count = count + 1
-            else:
-                if i == '";':
-                    if count:
-                        ret.append('\n')
-                elif count > 2:
-                    ret.append('\n\n')
-                elif count:
-                    ret.append('\n'*count)
-                count = 0
-                ret.append(i)
-
-        _data = "".join(ret)
-        ret = []
-        for i in _data.split('\n\n'):
-            if i == 'Parameters:':
-                ret.extend(['Parameters:\n-----------', '\n\n'])
-            elif i.find('// File:') > -1: # leave comments alone.
-                ret.extend([i, '\n'])
-            else:
-                _tmp = textwrap.fill(i.strip())
-                _tmp = self.lead_spc.sub(r'\1"\2', _tmp)
-                ret.extend([_tmp, '\n\n'])
-        return ret
-
-
-def main(input, output):
-    p = Doxy2SWIG(input)
-    p.generate()
-    p.write(output)
-
-
-if __name__ == '__main__':
-    if len(sys.argv) != 3:
-        print __doc__
-        sys.exit(1)
-    main(sys.argv[1], sys.argv[2])
diff --git a/gnuradio-core/doc/xml/swig.xsl b/gnuradio-core/doc/xml/swig.xsl
deleted file mode 100644 (file)
index 6163c2d..0000000
+++ /dev/null
@@ -1,38 +0,0 @@
-<!-- XSLT script to extract document for class/function for swig docstring
-     If you have xsltproc you could use:
-     xsltproc swig.xsl index.xml > swig_doc.i
--->
-<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
-  <xsl:output method="text"/>
-   <xsl:template match="/">
-      <!-- process each compound -->
-      <xsl:for-each select="doxygenindex/compound">
-        <xsl:apply-templates select="document( concat( @refid, '.xml' ) )/*" />
-      </xsl:for-each>
-   </xsl:template>
-
-  <xsl:template match="doxygen">
-    <xsl:for-each select="compounddef[@kind='class']">  
-      <xsl:text>%feature(__QuOtE__docstring__QuOtE__) </xsl:text>
-      <xsl:value-of select="compoundname"/>
-      <xsl:text> __QuOtE__ &#10;</xsl:text>
-      <xsl:value-of select="briefdescription"/><xsl:text>
-      </xsl:text>
-      <xsl:value-of select="detaileddescription"/>
-      <xsl:text> see also: </xsl:text>
-      <xsl:value-of select="includes"/>
-      <xsl:text>__QuOtE__;&#10;&#10;</xsl:text>
-
-      <!-- output for each function individually -->
-      <xsl:for-each select="*/memberdef[@kind='function' and not(starts-with(name,'operator'))]"> 
-        <xsl:text>%feature(__QuOtE__docstring__QuOtE__) </xsl:text><xsl:value-of select="../../compoundname"/>::<xsl:value-of select="name"/>
-        <xsl:text> __QuOtE__&#10;</xsl:text>
-        <xsl:value-of select="definition"/> <xsl:value-of select="argsstring"/>
-        <xsl:text>
-        </xsl:text><xsl:value-of select="briefdescription"/><xsl:text>
-        </xsl:text><xsl:value-of select="detaileddescription"/>
-        <xsl:text>__QuOtE__; &#10;&#10;</xsl:text>
-      </xsl:for-each>
-    </xsl:for-each>  
-  </xsl:template>
-</xsl:stylesheet>