doxygen for XC specific objects like interfaces, chanends

Technical questions regarding the XTC tools and programming with XMOS.
sprajagopal
Member++
Posts: 18
Joined: Thu Jul 23, 2015 4:22 pm

doxygen for XC specific objects like interfaces, chanends

Post by sprajagopal »

Hi,
I'm trying to use doxygen for my XC codebase. Most of the usual header functions, source functions, structures come out fine in the documentation. One thing that doesn't play well is the interface function definitions. Doxygen seems to skip the first function of an interface definition. Has anybody figured out a doxygen configuration in which interfaces can be documented nicely?
Thanks,
P.


User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

Hi,
I believe for the Xmos documentation toolset, a forked version of Breathe was used (bridges between doxygen and sphynx) which was modified to extend the struct directive to add in interfaces. This is where classes, typdefs and enums are defined too.
sprajagopal
Member++
Posts: 18
Joined: Thu Jul 23, 2015 4:22 pm

Post by sprajagopal »

infiniteimprobability wrote:Hi,
I believe for the Xmos documentation toolset, a forked version of Breathe was used (bridges between doxygen and sphynx) which was modified to extend the struct directive to add in interfaces. This is where classes, typdefs and enums are defined too.
Hi,
I'm desperately looking for this. Any links you could provide? Would be great!
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

It's a private repo containing xmos internal infrastructure tools so there is no external link. Let me check if I can share a snippet..
User avatar
infiniteimprobability
XCore Legend
Posts: 1126
Joined: Thu May 27, 2010 10:08 am

Post by infiniteimprobability »

Hi - OK there is nothing sensitive about this file and the license is OK so here it is. Please understand that we cannot offer any support on this - we're not in the business of customising documentation tools but hopefully the file is helpful!

__init__.py:

Code: Select all

from docutils import nodes
from docutils.parsers.rst.directives import unchanged_required

import os
import sys
import copy

from docutils.parsers import rst
from breathe.builder import RstBuilder, BuilderFactory
from breathe.finder import FinderFactory, NoMatchesError, MultipleMatchesError
from breathe.parser import DoxygenParserFactory, DoxygenIndexParser
from breathe.renderer.rst.doxygen import DoxygenToRstRendererFactoryCreator
from breathe.finder.doxygen import DoxygenItemFinderFactoryCreator, ItemMatcherFactory

import docutils.nodes
import sphinx.addnodes

class BaseDirective(rst.Directive):

    def __init__(self, builder_factory, finder_factory, matcher_factory, project_info_factory, *args):
        rst.Directive.__init__(self, *args)

        self.builder_factory = builder_factory
        self.finder_factory = finder_factory
        self.matcher_factory = matcher_factory
        self.project_info_factory = project_info_factory


# Directives
# ----------

class DoxygenIndexDirective(BaseDirective):

    required_arguments = 0
    optional_arguments = 2
    option_spec = {
            "path" : unchanged_required,
            "project" : unchanged_required,
            }
    has_content = False

    def run(self):


        project_info = self.project_info_factory.create_project_info(self.options)

        finder = self.finder_factory.create_finder(project_info)


        # try:
        data_object = finder.root()
        # except

#        self.state.document._current_state = self.state
#        self.state.document._current_content = self.content
#        self.state.document._current_content_offset = self.content_offset

        

        builder = self.builder_factory.create_builder(project_info, self.state.document)


        nodes = builder.build(data_object, self.state, self.content, self.content_offset)

        return nodes

class DoxygenFunctionDirective(BaseDirective):

    required_arguments = 1
    optional_arguments = 1
    option_spec = {
            "path" : unchanged_required,
            "project" : unchanged_required,
            }
    has_content = False

    def run(self):


        function_name = self.arguments[0]


        project_info = self.project_info_factory.create_project_info(self.options)


        finder = self.finder_factory.create_finder(project_info)


        matcher = self.matcher_factory.create_name_type_matcher(function_name, "function")


        try:
            data_object = finder.find_one(matcher)
        except NoMatchesError, e:
            warning = 'doxygenfunction: Cannot find function "%s" in doxygen xml output' % function_name
            return [ docutils.nodes.warning( "", docutils.nodes.paragraph("", "", docutils.nodes.Text(warning))),
                    self.state.document.reporter.warning( warning, line=self.lineno) ]


#        self.state.document._current_state = self.state
#        self.state.document._current_content = self.content
#        self.state.document._current_content_offset = self.content_offset
        builder = self.builder_factory.create_builder(project_info, self.state.document)


        nodes = builder.build(data_object, self.state, self.content, self.content_offset)


        return nodes



class DoxygenStructDirective(BaseDirective):

    kind = "struct"

    required_arguments = 1
    optional_arguments = 1
    option_spec = {
            "path" : unchanged_required,
            "project" : unchanged_required,
            }
    has_content = False

    def run(self):

        struct_name = self.arguments[0]

        project_info = self.project_info_factory.create_project_info(self.options)

        finder = self.finder_factory.create_finder(project_info)

        def do_build(matcher, do_warning):
            try:
                data_object = finder.find_one(matcher)
            except NoMatchesError, e:
                if do_warning:
                    warning = 'doxygen%s: Cannot find %s "%s" in doxygen xml output' % (self.kind, self.kind, struct_name)
                    return [ docutils.nodes.warning( "", docutils.nodes.paragraph("", "", docutils.nodes.Text(warning))),
                             self.state.document.reporter.warning( warning, line=self.lineno) ]
                else:
                    return []
#        self.state.document._current_state = self.state
#        self.state.document._current_content = self.content
#        self.state.document._current_content_offset = self.content_offset
            builder = self.builder_factory.create_builder(project_info, self.state.document)


            nodes = builder.build(data_object, self.state, self.content, self.content_offset)
            return nodes

        # try:
        print self.kind
        if self.kind == 'interface':
            matcher1 = self.matcher_factory.create_name_type_matcher(struct_name,
                                                                    'class')
            print '__extends__' + struct_name
            matcher2 = self.matcher_factory.create_name_type_matcher('__extends__' + struct_name,
                                                                    'class')

            return do_build(matcher1, True) + do_build(matcher2, False)
        else:
            matcher = self.matcher_factory.create_name_type_matcher(struct_name, self.kind)
            return do_build(matcher, True)


class DoxygenClassDirective(DoxygenStructDirective):

    kind = "class"

class DoxygenInterfaceDirective(DoxygenStructDirective):

    kind = "interface"


class DoxygenEnumDirective(DoxygenStructDirective):

    kind = "enum"


class DoxygenTypedefDirective(DoxygenStructDirective):

    kind = "typedef"

class DoxygenDefineDirective(DoxygenStructDirective):

    kind = "define"


# Setup Administration
# --------------------

class DirectiveContainer(object):

    def __init__(self, directive, builder, finder_factory, matcher_factory, project_info_factory):

        self.directive = directive
        self.builder = builder
        self.finder_factory = finder_factory
        self.matcher_factory = matcher_factory
        self.project_info_factory = project_info_factory

        # Required for sphinx to inspect
        self.required_arguments = directive.required_arguments
        self.optional_arguments = directive.optional_arguments
        self.option_spec = directive.option_spec
        self.has_content = directive.has_content

    def __call__(self, *args):

        return self.directive(self.builder, self.finder_factory, self.matcher_factory, self.project_info_factory, *args)


class ProjectInfo(object):

    def __init__(self, name, path):

        self._name = name
        self._path = path

    def name(self):
        return self._name

    def path(self):
        return self._path

class ProjectInfoFactory(object):

    def __init__(self):

        self.projects = {}
        self.default_project = None

        self.project_count = 0
        self.project_info_store = {}

    def update(self, projects, default_project):

        self.projects = projects
        self.default_project = default_project

    def default_path(self):

        return self.projects[self.default_project]

    def create_project_info(self, options):

        name = ""
        path = self.default_path()

        if options.has_key("project"):
            try:
                path = self.projects[ options["project"] ]
                name = options["project"]
            except KeyError, e:
                sys.stderr.write(
                        "Unable to find project '%s' in breathe_projects dictionary" % options["project"]
                        )

        if options.has_key("path"):
            path = options["path"]


        try:
            return self.project_info_store[path]
        except KeyError:
            if not name:
                name = "project%s" % self.project_count
                self.project_count += 1

            project_info = ProjectInfo(name, path)

            self.project_info_store[path] = project_info

            return project_info



class DoxygenDirectiveFactory(object):

    directives = {
            "doxygenindex" : DoxygenIndexDirective,
            "doxygenfunction" : DoxygenFunctionDirective,
            "doxygenstruct" : DoxygenStructDirective,
            "doxygenclass" : DoxygenClassDirective,
            "doxygeninterface" : DoxygenInterfaceDirective,
            "doxygenenum" : DoxygenEnumDirective,
            "doxygentypedef" : DoxygenTypedefDirective,
            "doxygendefine" : DoxygenDefineDirective,
            }

    def __init__(self, builder_factory, finder_factory, matcher_factory, project_info_factory):
        self.builder_factory = builder_factory
        self.finder_factory = finder_factory
        self.matcher_factory = matcher_factory
        self.project_info_factory = project_info_factory

    def create_index_directive_container(self):
        return self.create_directive_container("doxygenindex")

    def create_function_directive_container(self):
        return self.create_directive_container("doxygenfunction")

    def create_struct_directive_container(self):
        return self.create_directive_container("doxygenstruct")

    def create_enum_directive_container(self):
        return self.create_directive_container("doxygenenum")

    def create_typedef_directive_container(self):
        return self.create_directive_container("doxygentypedef")

    def create_define_directive_container(self):
        return self.create_directive_container("doxygendefine")

    def create_class_directive_container(self):
        return self.create_directive_container("doxygenclass")

    def create_interface_directive_container(self):
        return self.create_directive_container("doxygeninterface")

    def create_directive_container(self, type_):

        return DirectiveContainer(
                self.directives[type_],
                self.builder_factory,
                self.finder_factory,
                self.matcher_factory,
                self.project_info_factory
                )

    def get_config_values(self, app):

        # All DirectiveContainers maintain references to this project info factory
        # so we can update this to update them
        self.project_info_factory.update(
                app.config.breathe_projects,
                app.config.breathe_default_project
                )

class NodeFactory(object):

    def __init__(self, *args):

        self.sources = args

    def __getattr__(self, node_name):

        for source in self.sources:
            try:
                return getattr(source, node_name)
            except AttributeError:
                pass

        raise NodeNotFoundError(node_name)


# Setup
# -----

def setup(app):

    parser_factory = DoxygenParserFactory()
    matcher_factory = ItemMatcherFactory()
    item_finder_factory_creator = DoxygenItemFinderFactoryCreator(parser_factory, matcher_factory)
    index_parser = DoxygenIndexParser()
    finder_factory = FinderFactory(index_parser, item_finder_factory_creator)

    node_factory = NodeFactory(docutils.nodes, sphinx.addnodes)
    renderer_factory_creator = DoxygenToRstRendererFactoryCreator(node_factory, parser_factory)
    builder_factory = BuilderFactory(RstBuilder, renderer_factory_creator)

    project_info_factory = ProjectInfoFactory()
    directive_factory = DoxygenDirectiveFactory(builder_factory, finder_factory, matcher_factory, project_info_factory)

    app.add_directive(
            "doxygenindex",
            directive_factory.create_index_directive_container(),
            )

    app.add_directive(
            "doxygenfunction",
            directive_factory.create_function_directive_container(),
            )

    app.add_directive(
            "doxygenstruct",
            directive_factory.create_struct_directive_container(),
            )

    app.add_directive(
            "doxygenenum",
            directive_factory.create_enum_directive_container(),
            )

    app.add_directive(
            "doxygentypedef",
            directive_factory.create_typedef_directive_container(),
            )

    app.add_directive(
            "doxygendefine",
            directive_factory.create_define_directive_container(),
            )

    app.add_directive(
            "doxygenclass",
            directive_factory.create_class_directive_container(),
            )

    app.add_directive(
            "doxygeninterface",
            directive_factory.create_interface_directive_container(),
            )

    app.add_config_value("breathe_projects", {}, True)
    app.add_config_value("breathe_default_project", "", True)

    app.connect("builder-inited", directive_factory.get_config_values)