Salome HOME
IMP: generation of Python docstrings from Doxygen output. abn/doxy2swig
authorabn <adrien.bruneton@cea.fr>
Tue, 30 Jun 2015 13:35:56 +0000 (15:35 +0200)
committerabn <adrien.bruneton@cea.fr>
Tue, 30 Jun 2015 13:35:56 +0000 (15:35 +0200)
For now only a few classes from MEDLoader and MEDCoupling are
included.

doc/doxygen/CMakeLists.txt
doc/doxygen/Doxyfile_med_user.in
doc/doxygen/doxy2swig/MEDCoupling_doc.i.in [new file with mode: 0644]
doc/doxygen/doxy2swig/MEDLoader_doc.i.in [new file with mode: 0644]
doc/doxygen/doxy2swig/doxy2swig.cmake [new file with mode: 0644]
doc/doxygen/doxy2swig/doxy2swig.py [new file with mode: 0755]
src/MEDCoupling/MEDCouplingUMesh.cxx
src/MEDCoupling_Swig/CMakeLists.txt
src/MEDCoupling_Swig/MEDCouplingCommon.i
src/MEDLoader/Swig/CMakeLists.txt
src/MEDLoader/Swig/MEDLoaderCommon.i

index 558964b9437a7bb009c469791d9d54727c2c7b72..99392afa3e936a32811ab16b89bcad731527a5db 100644 (file)
@@ -22,6 +22,8 @@ SALOME_CONFIGURE_FILE(static/header.html.in static/header.html)
 
   
 IF(SALOME_MED_ENABLE_PYTHON)
+  INCLUDE(doxy2swig/doxy2swig.cmake)
+
   FILE(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}/tmp/medcouplingexamples.in" input)
   FILE(TO_NATIVE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/BuildPyExamplesFromCPP.py" pythondocexamplesgenerator)
   FILE(TO_NATIVE_PATH "${CMAKE_CURRENT_BINARY_DIR}" output)
@@ -62,6 +64,13 @@ IF(SALOME_MED_ENABLE_PYTHON)
     WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
   )
   
+  # Create dummy target gathering the generation of all .i files:
+  ADD_CUSTOM_TARGET(swig_ready)
+  ADD_DEPENDENCIES(swig_ready usr_docs)
+  
+  SALOME_MED_SWIG_DOCSTRING_CONFIGURE(usr_docs swig_ready MEDCoupling)
+  SALOME_MED_SWIG_DOCSTRING_CONFIGURE(usr_docs swig_ready MEDLoader)
+    
   SET(doxyfile_med_user ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile_med_user)
   FILE(STRINGS ${doxyfile_med_user} enabled_sections REGEX "ENABLED_SECTIONS")
   IF(enabled_sections)
@@ -73,6 +82,9 @@ IF(SALOME_MED_ENABLE_PYTHON)
       "#Temporary variable to enable python documentation sections\nENABLED_SECTIONS = ENABLE_EXAMPLES")
   ENDIF()
   
+  # Swig generation to have docstrings correctly populated:
+  INCLUDE(doxy2swig/doxy2swig.cmake)
+  
 ELSE()
   ADD_CUSTOM_TARGET(usr_docs ALL
     COMMAND ${DOXYGEN_EXECUTABLE} Doxyfile_med_user
@@ -81,7 +93,7 @@ ELSE()
   )
 ENDIF()
  
-INSTALL(CODE "EXECUTE_PROCESS(COMMAND \"${CMAKE_COMMAND}\" --build ${PROJECT_BINARY_DIR} --target usr_docs)")
+#INSTALL(CODE "EXECUTE_PROCESS(COMMAND \"${CMAKE_COMMAND}\" --build ${PROJECT_BINARY_DIR} --target usr_docs)")
 INSTALL(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc_ref_user/html/ DESTINATION ${SALOME_INSTALL_DOC}/gui/MED)
 INSTALL(FILES images/head.png DESTINATION ${SALOME_INSTALL_DOC}/gui/MED) 
 
index 5845b2807df12e023f8568b705d594667b273ef0..1ecbb3e27e2e41ddd95748643039398f095cee1f 100644 (file)
@@ -226,7 +226,9 @@ MAN_LINKS              = NO
 #---------------------------------------------------------------------------
 # configuration options related to the XML output
 #---------------------------------------------------------------------------
-GENERATE_XML           = NO
+GENERATE_XML           = YES
+XML_OUTPUT             = xml
+XML_PROGRAMLISTING     = NO
 #---------------------------------------------------------------------------
 # Configuration options related to the preprocessor
 #---------------------------------------------------------------------------
diff --git a/doc/doxygen/doxy2swig/MEDCoupling_doc.i.in b/doc/doxygen/doxy2swig/MEDCoupling_doc.i.in
new file mode 100644 (file)
index 0000000..0ca88d7
--- /dev/null
@@ -0,0 +1 @@
+@_swig_include_set@
\ No newline at end of file
diff --git a/doc/doxygen/doxy2swig/MEDLoader_doc.i.in b/doc/doxygen/doxy2swig/MEDLoader_doc.i.in
new file mode 100644 (file)
index 0000000..0ca88d7
--- /dev/null
@@ -0,0 +1 @@
+@_swig_include_set@
\ No newline at end of file
diff --git a/doc/doxygen/doxy2swig/doxy2swig.cmake b/doc/doxygen/doxy2swig/doxy2swig.cmake
new file mode 100644 (file)
index 0000000..0e96b36
--- /dev/null
@@ -0,0 +1,112 @@
+# Copyright (C) 2012-2015  CEA/DEN, EDF R&D
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+#
+# See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
+#
+
+##
+## This module is dedicated to the generation of specific SWIG files (".i") containing
+## the docstrings built from the C++ doxygen documentation.
+##
+
+SET(_DOXY2SWIG ${PROJECT_SOURCE_DIR}/doc/doxygen/doxy2swig/doxy2swig.py)
+SET(_SWIG_DOC_SUFFIX "doc_class_")
+
+#
+# MEDCoupling classes to include
+#
+SET(_classes_MEDCoupling
+    ParaMEDMEM_1_1MEDCouplingUMesh
+    ParaMEDMEM_1_1MEDCouplingCMesh
+    ParaMEDMEM_1_1MEDCouplingRemapper
+    ParaMEDMEM_1_1DataArray
+    ParaMEDMEM_1_1DataArrayInt
+    ParaMEDMEM_1_1DataArrayDouble
+    )
+
+#
+# MEDLoader classes to include
+#
+SET(_classes_MEDLoader
+    MEDLoader
+    ParaMEDMEM_1_1MEDFileMeshes
+    ParaMEDMEM_1_1MEDFileMesh
+    ParaMEDMEM_1_1MEDFileUMesh
+    ParaMEDMEM_1_1MEDFileCMesh
+    )
+
+##
+## Generates the ".i" files from a list of C++ classes.
+##
+## \param[in] target_doc main target for the stantard doxygen generation
+## \param[in] target_swig dummy target encompassing the final build of all SWIG files
+## \param[in] cls_list list of classes for which to generate SWIG files
+## \param[in] swig_main_file main SWIG file including the other generated SWIG files
+## \param[out] swig_files list of generated SWIG files 
+##
+MACRO(SALOME_MED_SWIG_DOCSTRING_GENERATE target_doc target_swig cls_list swig_main_file swig_files)
+    # List of generated SWIG files (.i) for doc purposes only:
+    SET(${swig_files})
+    FOREACH(_cls IN LISTS ${cls_list})
+      SET(_xml_file "${CMAKE_CURRENT_BINARY_DIR}/../doxygen/doc_ref_user/xml/class${_cls}.xml")
+      SET(_swig_file_base "${_SWIG_DOC_SUFFIX}${_cls}.i")
+      SET(_swig_file "${PROJECT_BINARY_DIR}/doc/${_swig_file_base}" )
+  
+      # SWIG doc files will always be generated *after* Doxygen is run:
+      ### WARNING: ADD_CUSTOM_COMMAND(TARGET xxx POST_BUILD ...) command
+      ### must be in exactly the same subdir as the initial target construction command.
+      ### That's why this file is included with an INCLUDE() rather than using ADD_SUBDIRECTORY
+      # Note: we touch the main .i file to be sure to retrigger swig when the doc in a included 
+      # class changes. 
+      ADD_CUSTOM_COMMAND(OUTPUT ${_swig_file}
+        COMMAND ${PYTHON_EXECUTABLE} ${_DOXY2SWIG} "-n" ${_xml_file} ${_swig_file}
+        COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${swig_main_file}
+        DEPENDS ${_xml_file}
+        COMMENT "Generating docstring SWIG file (from Doxygen's XML): ${_swig_file_base}"
+        VERBATIM
+      )
+      ADD_CUSTOM_TARGET(${_swig_file_base} DEPENDS ${_swig_file})
+      # The doxy2swig script is executed once the doxygen documentation has been generated ...
+      ADD_DEPENDENCIES(${_swig_file_base} ${target_doc})
+      # ... and the meta target 'swig_ready' (here ${target_swig}) is ready when all .i files 
+      # have been generated:
+      ADD_DEPENDENCIES(${target_swig} ${_swig_file_base})
+       
+      LIST(APPEND ${swig_files} ${_swig_file_base})
+    ENDFOREACH()
+ENDMACRO(SALOME_MED_SWIG_DOCSTRING_GENERATE)
+
+
+##
+## Configures the MEDCoupling_doc.i or MEDLoader_doc.i file so that they include
+## the list of SWIG files generated by the macro above.
+##
+## \param[in] target_doc main target for the stantard doxygen generation
+## \param[in] target_swig dummy target encompassing the final build of all SWIG files
+## \param[in] root_name either 'MEDCoupling' or 'MEDLoader'
+## 
+MACRO(SALOME_MED_SWIG_DOCSTRING_CONFIGURE target_doc target_swig root_name)
+    SET(_all_swig_docs)
+    SET(_swig_include_set)
+    SET(_in_file doxy2swig/${root_name}_doc.i.in)
+    SET(_out_file ${PROJECT_BINARY_DIR}/doc/${root_name}_doc.i)
+    SALOME_MED_SWIG_DOCSTRING_GENERATE(${target_doc} ${target_swig} _classes_${root_name} ${_out_file} _all_swig_docs)  
+    FOREACH(f IN LISTS _all_swig_docs)
+        SET(_swig_include_set "${_swig_include_set}\n%include \"${f}\"")
+    ENDFOREACH()
+    CONFIGURE_FILE(${_in_file} ${_out_file} @ONLY)
+ENDMACRO(SALOME_MED_SWIG_DOCSTRING_CONFIGURE)
+
diff --git a/doc/doxygen/doxy2swig/doxy2swig.py b/doc/doxygen/doxy2swig/doxy2swig.py
new file mode 100755 (executable)
index 0000000..7a3b3ea
--- /dev/null
@@ -0,0 +1,455 @@
+#!/usr/bin/env python
+"""Doxygen XML to SWIG docstring converter.
+
+Usage:
+
+  doxy2swig.py [options] input.xml output.i
+
+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.
+
+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
+#
+# Thanks:
+#   Johan Hake:  the include_function_definition feature
+#   Bill Spotz:  bug reports and testing.
+#   Sebastian Henschel:   Misc. enhancements.
+#
+######################################################################
+
+from xml.dom import minidom
+import re
+import textwrap
+import sys
+import types
+import os.path
+import optparse
+
+
+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, include_function_definition=True, quiet=False):
+        """Initialize the instance given a source object.  `src` can
+        be a file or filename.  If you do not want to include function
+        definitions from doxygen then set
+        `include_function_definition` to `False`.  This is handy since
+        this allows you to use the swig generated function definition
+        using %feature("autodoc", [0,1]).
+
+        """
+        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 = []
+        self.include_function_definition = include_function_definition
+        if not include_function_definition:
+            self.ignores.append('argsstring')
+
+        self.quiet = quiet
+            
+        
+    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, break_long_words=False))
+
+    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 parse_Comment(self, node):
+        """Parse a `COMMENT_NODE`.  This does nothing for now."""
+        return
+
+    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):
+        text='unknown'
+        for key, val in node.attributes.items():
+            if key == 'kind':
+                if val == 'param': text = 'Parameters'
+                elif val == 'exception': text = 'Exceptions'
+                else: text = val
+                break
+        self.add_text(['\n', '\n', text, ':', '\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')
+        try:
+            data=node.firstChild.data
+        except AttributeError: # perhaps a <ref> tag in it
+            data=node.firstChild.firstChild.data
+        if data.find('Exception') != -1:
+            self.add_text(data)
+        else:
+            self.add_text("%s: "%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
+
+            if not first.has_key('definition') or \
+                   kind in ['variable', 'typedef']:
+                return
+
+            if self.include_function_definition:
+                defn = first['definition'].firstChild.data
+            else:
+                defn = ""
+            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', 'user-defined', ''):
+            self.generic_parse(node)
+
+    def do_header(self, node):
+        """For a user defined section def a header field is present
+        which should not be printed as such, so we comment it in the
+        output."""
+        data = node.firstChild.data
+        self.add_text('\n/*\n %s \n*/\n'%data)
+        # If our immediate sibling is a 'description' node then we
+        # should comment that out also and remove it from the parent
+        # node's children.
+        parent = node.parentNode
+        idx = parent.childNodes.index(node)
+        if len(parent.childNodes) >= idx + 2:
+            nd = parent.childNodes[idx+2]
+            if nd.nodeName == 'description':
+                nd = parent.removeChild(nd)
+                self.add_text('\n/*')
+                self.generic_parse(nd)
+                self.add_text('\n*/\n')
+
+    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)
+        elif kind == 'return':
+            self.add_text(['\n', '===> Returns: '])
+            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)
+            if not self.quiet:
+                print "parsing file: %s"%fname
+            p = Doxy2SWIG(fname, self.include_function_definition, self.quiet)
+            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:' or i == 'Exceptions:':
+                ret.extend([i, '\n-----------', '\n\n'])
+            elif i.find('// File:') > -1: # leave comments alone.
+                ret.extend([i, '\n'])
+            else:
+                _tmp = textwrap.fill(i.strip(), break_long_words=False)
+                _tmp = self.lead_spc.sub(r'\1"\2', _tmp)
+                ret.extend([_tmp, '\n\n'])
+        return ret
+
+
+def convert(input, output, include_function_definition=True, quiet=False):
+    p = Doxy2SWIG(input, include_function_definition, quiet)
+    p.generate()
+    p.write(output)
+
+def main():
+    usage = __doc__
+    parser = optparse.OptionParser(usage)
+    parser.add_option("-n", '--no-function-definition',
+                      action='store_true',
+                      default=False,
+                      dest='func_def',
+                      help='do not include doxygen function definitions')
+    parser.add_option("-q", '--quiet',
+                      action='store_true',
+                      default=False,
+                      dest='quiet',
+                      help='be quiet and minimize output')
+    
+    options, args = parser.parse_args()
+    if len(args) != 2:
+        parser.error("error: no input and output specified")
+
+    convert(args[0], args[1], not options.func_def, options.quiet)
+    
+
+if __name__ == '__main__':
+    main()
+
index 70db4e5881db34e0b0ba0f952401a2504e171b79..4efb00a5e11a6443b3e246f9099efe8c6b26e3a3 100644 (file)
@@ -316,8 +316,8 @@ void MEDCouplingUMesh::setMeshDimension(int meshDim)
 }
 
 /*!
- * Allocates memory to store an estimation of the given number of cells. Closer is the estimation to the number of cells effectively inserted,
- * less will be the needs to realloc. If the number of cells to be inserted is not known simply put 0 to this parameter.
+ * Allocates memory to store an estimation of the given number of cells. The closer is the estimation to the number of cells effectively inserted,
+ * the less will the library need to reallocate memory. If the number of cells to be inserted is not known simply put 0 to this parameter.
  * If a nodal connectivity previouly existed before the call of this method, it will be reset.
  *
  *  \param [in] nbOfCells - estimation of the number of cell \a this mesh will contain.
index 4d218def54503e5e2b0f6f443dca94ca176e8094..07929a88dcfb20a4b5c16ac69b79eb1e7d8cd1f6 100644 (file)
@@ -43,6 +43,7 @@ INCLUDE_DIRECTORIES(
   ${CMAKE_CURRENT_SOURCE_DIR}/../INTERP_KERNEL/Geometric2D
   ${CMAKE_CURRENT_SOURCE_DIR}/../INTERP_KERNEL/ExprEval
   ${CMAKE_CURRENT_SOURCE_DIR}/../INTERP_KERNEL/GaussPoints
+  ${PROJECT_BINARY_DIR}/doc
   )
 
 # _ABR_ Ensure dependency mechanism on all SWIG files and headers
@@ -50,6 +51,15 @@ SET (SWIG_MODULE_MEDCoupling_EXTRA_DEPS ${MEDCoupling_SWIG_DPYS_FILES}
     ${medcoupling_HEADERS_HXX} ${medcoupling_HEADERS_TXX}
     ${interpkernel_HEADERS_HXX} ${interpkernel_HEADERS_TXX})
 
+# SWIG must run after the doc if we want to have the docstrings extracted from Doxygen
+# into the Python module:
+IF(SALOME_BUILD_DOC)
+    LIST(APPEND SWIG_MODULE_MEDCoupling_EXTRA_FLAGS -DWITH_DOCSTRINGS)
+    LIST(APPEND SWIG_MODULE_MEDCoupling_EXTRA_DEPS 
+        ${PROJECT_BINARY_DIR}/doc/MEDCoupling_doc.i
+        swig_ready)
+ENDIF()
+
 SWIG_ADD_MODULE(MEDCoupling python MEDCoupling.i)
 SWIG_LINK_LIBRARIES(MEDCoupling ${PYTHON_LIBRARIES} ${PLATFORM_LIBS} medcoupling)
 
index 24920cc8e8bb4bb09f50232747827ae2827883d4..8f1717456a1d5fded43745877c8181c9d90f996b 100644 (file)
 
 %module MEDCoupling
 
+#ifdef WITH_DOCSTRINGS
+%include MEDCoupling_doc.i
+#endif
+
 %include std_vector.i
 %include std_string.i
 
index 4fcecf52dc5cae092297c791b2547c7f65d80b81..6cef3ea9af3cfc9b183fa1303feafe7ec38d2603 100644 (file)
@@ -44,6 +44,7 @@ INCLUDE_DIRECTORIES(
   ${CMAKE_CURRENT_SOURCE_DIR}/../../INTERP_KERNEL/Geometric2D
   ${CMAKE_CURRENT_SOURCE_DIR}/../../INTERP_KERNEL/ExprEval
   ${CMAKE_CURRENT_SOURCE_DIR}/../../INTERP_KERNEL/GaussPoints
+  ${PROJECT_BINARY_DIR}/doc
 )
 
 SET (SWIG_MODULE_MEDLoader_EXTRA_DEPS ${MEDLoader_SWIG_DPYS_FILES}
@@ -51,13 +52,22 @@ SET (SWIG_MODULE_MEDLoader_EXTRA_DEPS ${MEDLoader_SWIG_DPYS_FILES}
     ${medcoupling_HEADERS_HXX} ${medcoupling_HEADERS_TXX}
     ${interpkernel_HEADERS_HXX} ${interpkernel_HEADERS_TXX})
 
+# SWIG must run after the doc if we want to have the docstrings extracted from Doxygen
+# into the Python module:
+IF(SALOME_BUILD_DOC)
+    LIST(APPEND SWIG_MODULE_MEDLoader_EXTRA_FLAGS -DWITH_DOCSTRINGS)
+    LIST(APPEND SWIG_MODULE_MEDLoader_EXTRA_DEPS 
+        ${PROJECT_BINARY_DIR}/doc/MEDLoader_doc.i
+        swig_ready)
+ENDIF()
+
 SWIG_ADD_MODULE(MEDLoader python MEDLoader.i)
 SWIG_LINK_LIBRARIES(MEDLoader ${PYTHON_LIBRARIES} ${PLATFORM_LIBS} medloader medcoupling)
 IF(WIN32)
   SET_TARGET_PROPERTIES(_MEDLoader PROPERTIES DEBUG_OUTPUT_NAME _MEDLoader_d)
 ENDIF(WIN32)
-INSTALL(TARGETS _MEDLoader DESTINATION ${SALOME_INSTALL_PYTHON})
 
+INSTALL(TARGETS _MEDLoader DESTINATION ${SALOME_INSTALL_PYTHON})
 INSTALL(FILES MEDLoader.i MEDLoaderTypemaps.i MEDLoaderCommon.i DESTINATION ${SALOME_INSTALL_HEADERS})
 
 SET(PYFILES_TO_INSTALL ${CMAKE_CURRENT_BINARY_DIR}/MEDLoader.py)
index b4132f8ca797f04b315fb2a235cbd9b131525f8f..2512825f8e8a427b0b43c2e46c5f772c5f661f38 100644 (file)
 #define MEDCOUPLING_EXPORT
 #define MEDLOADER_EXPORT
 
+#ifdef WITH_DOCSTRINGS
+%include "MEDLoader_doc.i"
+#endif
+
 %include "MEDCouplingCommon.i"
 
 %{