from .finder.core import FinderFactory from .parser import DoxygenParserFactory, CacheFactory from .renderer.rst.doxygen import DoxygenToRstRendererFactoryCreatorConstructor, \ RstContentCreator, RenderContext from .renderer.rst.doxygen.filter import FilterFactory, GlobFactory from .renderer.rst.doxygen.target import TargetHandlerFactory from .renderer.rst.doxygen.mask import MaskFactory, NullMaskFactory, NoParameterNamesMask from .finder.doxygen.core import DoxygenItemFinderFactoryCreator from .directive.base import BaseDirective, create_warning from .directive.index import DoxygenIndexDirective, AutoDoxygenIndexDirective from .directive.file import DoxygenFileDirective, AutoDoxygenFileDirective from .process import AutoDoxygenProcessHandle from .exception import BreatheError from .project import ProjectInfoFactory, ProjectError from docutils.parsers.rst.directives import unchanged_required, unchanged, flag from docutils.statemachine import ViewList from sphinx.domains import cpp, c, python from sphinx.writers.text import TextWriter from sphinx.builders.text import TextBuilder import docutils.nodes import sphinx.addnodes import sphinx.ext.mathbase import os import fnmatch import re import textwrap import collections import subprocess # Somewhat outrageously, reach in and fix a Sphinx regex cpp._identifier_re = re.compile(r'(~?\b[a-zA-Z_][a-zA-Z0-9_]*)\b') class NoMatchingFunctionError(BreatheError): pass class UnableToResolveFunctionError(BreatheError): def __init__(self, signatures): self.signatures = signatures class NodeNotFoundError(BreatheError): pass class FakeDestination(object): def write(self, output): return output class TextRenderer(object): def __init__(self, app): self.app = app def render(self, nodes, document): new_document = document.copy() new_document.children = nodes writer = TextWriter(TextBuilder(self.app)) output = writer.write(new_document, FakeDestination()) return output.strip() # Directives # ---------- class DoxygenFunctionDirective(BaseDirective): required_arguments = 1 option_spec = { "path": unchanged_required, "project": unchanged_required, "outline": flag, "no-link": flag, } has_content = False final_argument_whitespace = True def __init__(self, node_factory, text_renderer, *args, **kwargs): BaseDirective.__init__(self, *args, **kwargs) self.node_factory = node_factory self.text_renderer = text_renderer def run(self): # Separate possible arguments (delimited by a "(") from the namespace::name match = re.match(r"([^(]*)(.*)", self.arguments[0]) namespaced_function, args = match.group(1), match.group(2) # Split the namespace and the function name try: (namespace, function_name) = namespaced_function.rsplit("::", 1) except ValueError: (namespace, function_name) = "", namespaced_function try: project_info = self.project_info_factory.create_project_info(self.options) except ProjectError as e: warning = create_warning(None, self.state, self.lineno) return warning.warn('doxygenfunction: %s' % e) try: finder = self.finder_factory.create_finder(project_info) except MTimerError as e: warning = create_warning(None, self.state, self.lineno) return warning.warn('doxygenfunction: %s' % e) # Extract arguments from the function name. args = self.parse_args(args) finder_filter = self.filter_factory.create_member_finder_filter( namespace, function_name, 'function') matches = [] finder.filter_(finder_filter, matches) # Create it ahead of time as it is cheap and it is ugly to declare it for both exception # clauses below warning = create_warning( project_info, self.state, self.lineno, namespace='%s::' % namespace if namespace else '', function=function_name, args=', '.join(args) ) try: node_stack = self.resolve_function(matches, args, project_info) except NoMatchingFunctionError: return warning.warn('doxygenfunction: Cannot find function "{namespace}{function}" ' '{tail}') except UnableToResolveFunctionError as error: message = 'doxygenfunction: Unable to resolve multiple matches for function ' \ '"{namespace}{function}" with arguments ({args}) {tail}.\n' \ 'Potential matches:\n' # We want to create a raw_text string for the console output and a set of docutils nodes # for rendering into the final output. We handle the final output as a literal string # with a txt based list of the options. raw_text = message literal_text = '' # TODO: We're cheating here with the set() as signatures has repeating entries for some # reason (failures in the matcher_stack code) so we consolidate them by shoving them in # a set to remove duplicates. Should be fixed! for i, entry in enumerate(set(error.signatures)): if i: literal_text += '\n' # Replace new lines with a new line & enough spacing to reach the appropriate # alignment for our simple plain text list literal_text += '- %s' % entry.replace('\n', '\n ') raw_text += ' - %s\n' % entry.replace('\n', '\n ') block = self.node_factory.literal_block('', '', self.node_factory.Text(literal_text)) formatted_message = warning.format(message) warning_nodes = [ self.node_factory.paragraph( "", "", self.node_factory.Text(formatted_message) ), block ] result = warning.warn(raw_text, warning_nodes) return result target_handler = self.target_handler_factory.create_target_handler( self.options, project_info, self.state.document ) filter_ = self.filter_factory.create_outline_filter(self.options) return self.render(node_stack, project_info, self.options, filter_, target_handler, NullMaskFactory()) def parse_args(self, function_description): # Strip off trailing qualifiers pattern = re.compile(r'''(?<= \)) \s* (?: const)? \s* (?: volatile)? \s* (?: = \s* 0)? \s* $ ''', re.VERBOSE) function_description = re.sub(pattern, '', function_description) paren_index = function_description.find('(') if paren_index == -1: return [] # If it is empty parenthesis, then return empty list as we want empty parenthesis coming # from the xml file to match the user's function when the user doesn't provide parenthesis # ie. when there are no args anyway elif function_description == '()': return [] else: # Parse the function name string, eg. f(int, float) to # extract the types so we can use them for matching args = [] num_open_brackets = -1 start = paren_index + 1 for i in range(paren_index, len(function_description)): c = function_description[i] if c == '(' or c == '<': num_open_brackets += 1 elif c == ')' or c == '>': num_open_brackets -= 1 elif c == ',' and num_open_brackets == 0: args.append(function_description[start:i].strip()) start = i + 1 args.append(function_description[start:-1].strip()) return args def resolve_function(self, matches, args, project_info): if not matches: raise NoMatchingFunctionError() if len(matches) == 1: return matches[0] node_stack = None signatures = [] # Iterate over the potential matches for entry in matches: text_options = {'no-link': u'', 'outline': u''} # Render the matches to docutils nodes target_handler = self.target_handler_factory.create_target_handler( {'no-link': u''}, project_info, self.state.document ) filter_ = self.filter_factory.create_outline_filter(text_options) mask_factory = MaskFactory({'param': NoParameterNamesMask}) nodes = self.render(entry, project_info, text_options, filter_, target_handler, mask_factory) # Render the nodes to text signature = self.text_renderer.render(nodes, self.state.document) signatures.append(signature) match = re.match(r"([^(]*)(.*)", signature) match_args = match.group(2) # Parse the text to find the arguments match_args = self.parse_args(match_args) # Match them against the arg spec if args == match_args: node_stack = entry break if not node_stack: raise UnableToResolveFunctionError(signatures) return node_stack class DoxygenClassLikeDirective(BaseDirective): required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True option_spec = { "path": unchanged_required, "project": unchanged_required, "members": unchanged, "protected-members": flag, "private-members": flag, "undoc-members": flag, "show": unchanged_required, "outline": flag, "no-link": flag, } has_content = False def run(self): name = self.arguments[0] try: project_info = self.project_info_factory.create_project_info(self.options) except ProjectError as e: warning = create_warning(None, self.state, self.lineno, kind=self.kind) return warning.warn('doxygen{kind}: %s' % e) try: finder = self.finder_factory.create_finder(project_info) except MTimerError as e: warning = create_warning(None, self.state, self.lineno, kind=self.kind) return warning.warn('doxygen{kind}: %s' % e) finder_filter = self.filter_factory.create_compound_finder_filter(name, self.kind) matches = [] finder.filter_(finder_filter, matches) if len(matches) == 0: warning = create_warning(project_info, self.state, self.lineno, name=name, kind=self.kind) return warning.warn('doxygen{kind}: Cannot find class "{name}" {tail}') target_handler = self.target_handler_factory.create_target_handler( self.options, project_info, self.state.document ) filter_ = self.filter_factory.create_class_filter(name, self.options) mask_factory = NullMaskFactory() return self.render(matches[0], project_info, self.options, filter_, target_handler, mask_factory) class DoxygenClassDirective(DoxygenClassLikeDirective): kind = "class" class DoxygenStructDirective(DoxygenClassLikeDirective): kind = "struct" class DoxygenContentBlockDirective(BaseDirective): """Base class for namespace and group directives which have very similar behaviours""" required_arguments = 1 optional_arguments = 1 option_spec = { "path": unchanged_required, "project": unchanged_required, "content-only": flag, "outline": flag, "members": flag, "protected-members": flag, "private-members": flag, "undoc-members": flag, "no-link": flag } has_content = False def run(self): name = self.arguments[0] try: project_info = self.project_info_factory.create_project_info(self.options) except ProjectError as e: warning = create_warning(None, self.state, self.lineno, kind=self.kind) return warning.warn('doxygen{kind}: %s' % e) try: finder = self.finder_factory.create_finder(project_info) except MTimerError as e: warning = create_warning(None, self.state, self.lineno, kind=self.kind) return warning.warn('doxygen{kind}: %s' % e) finder_filter = self.filter_factory.create_finder_filter(self.kind, name) matches = [] finder.filter_(finder_filter, matches) # It shouldn't be possible to have too many matches as namespaces & groups in their nature # are merged together if there are multiple declarations, so we only check for no matches if not matches: warning = create_warning(project_info, self.state, self.lineno, name=name, kind=self.kind) return warning.warn('doxygen{kind}: Cannot find namespace "{name}" {tail}') if 'content-only' in self.options: # Unpack the single entry in the matches list (node_stack,) = matches filter_ = self.filter_factory.create_content_filter(self.kind, self.options) # Having found the compound node for the namespace or group in the index we want to grab # the contents of it which match the filter contents_finder = self.finder_factory.create_finder_from_root(node_stack[0], project_info) contents = [] contents_finder.filter_(filter_, contents) # Replaces matches with our new starting points matches = contents target_handler = self.target_handler_factory.create_target_handler( self.options, project_info, self.state.document ) filter_ = self.filter_factory.create_render_filter(self.kind, self.options) renderer_factory_creator = self.renderer_factory_creator_constructor.create_factory_creator( project_info, self.state.document, self.options, target_handler ) node_list = [] for node_stack in matches: renderer_factory = renderer_factory_creator.create_factory( node_stack, self.state, self.state.document, filter_, target_handler, ) mask_factory = NullMaskFactory() context = RenderContext(node_stack, mask_factory, self.directive_args) object_renderer = renderer_factory.create_renderer(context) node_list.extend(object_renderer.render()) return node_list class DoxygenNamespaceDirective(DoxygenContentBlockDirective): kind = "namespace" class DoxygenGroupDirective(DoxygenContentBlockDirective): kind = "group" # This class was the same as the DoxygenBaseDirective above, except that it # wraps the output in a definition_list before passing it back. This should be # abstracted in a far nicer way to avoid repeating so much code # # Now we've removed the definition_list wrap so we really need to refactor this! class DoxygenBaseItemDirective(BaseDirective): required_arguments = 1 optional_arguments = 1 option_spec = { "path": unchanged_required, "project": unchanged_required, "outline": flag, "no-link": flag, } has_content = False def create_finder_filter(self, namespace, name): """Creates a filter to find the node corresponding to this item.""" return self.filter_factory.create_member_finder_filter( namespace, name, self.kind) def run(self): try: namespace, name = self.arguments[0].rsplit("::", 1) except ValueError: namespace, name = "", self.arguments[0] try: project_info = self.project_info_factory.create_project_info(self.options) except ProjectError as e: warning = create_warning(None, self.state, self.lineno, kind=self.kind) return warning.warn('doxygen{kind}: %s' % e) try: finder = self.finder_factory.create_finder(project_info) except MTimerError as e: warning = create_warning(None, self.state, self.lineno, kind=self.kind) return warning.warn('doxygen{kind}: %s' % e) finder_filter = self.create_finder_filter(namespace, name) matches = [] finder.filter_(finder_filter, matches) if len(matches) == 0: display_name = "%s::%s" % (namespace, name) if namespace else name warning = create_warning(project_info, self.state, self.lineno, kind=self.kind, display_name=display_name) return warning.warn('doxygen{kind}: Cannot find {kind} "{display_name}" {tail}') target_handler = self.target_handler_factory.create_target_handler( self.options, project_info, self.state.document ) filter_ = self.filter_factory.create_outline_filter(self.options) node_stack = matches[0] mask_factory = NullMaskFactory() return self.render(node_stack, project_info, self.options, filter_, target_handler, mask_factory) class DoxygenVariableDirective(DoxygenBaseItemDirective): kind = "variable" def render(self, node_stack, project_info, options, filter_, target_handler, mask_factory): # Remove 'extern' keyword as Sphinx doesn't support it. definition = node_stack[0].definition extern = 'extern ' if definition.startswith(extern): definition = definition[len(extern):] self.directive_args[1] = [definition] return DoxygenBaseItemDirective.render(self, node_stack, project_info, options, filter_, target_handler, mask_factory) class DoxygenDefineDirective(DoxygenBaseItemDirective): kind = "define" class DoxygenEnumDirective(DoxygenBaseItemDirective): kind = "enum" class DoxygenEnumValueDirective(DoxygenBaseItemDirective): kind = "enumvalue" def create_finder_filter(self, namespace, name): return self.filter_factory.create_enumvalue_finder_filter(name) class DoxygenTypedefDirective(DoxygenBaseItemDirective): kind = "typedef" class DoxygenUnionDirective(DoxygenBaseItemDirective): kind = "union" def create_finder_filter(self, namespace, name): # Unions are stored in the xml file with their fully namespaced name # We're using C++ namespaces here, it might be best to make this file # type dependent # xml_name = "%s::%s" % (namespace, name) if namespace else name return self.filter_factory.create_compound_finder_filter(xml_name, 'union') # Setup Administration # -------------------- class DirectiveContainer(object): def __init__(self, directive, *args): self.directive = directive self.args = args # 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 self.final_argument_whitespace = directive.final_argument_whitespace def __call__(self, *args): call_args = [] call_args.extend(self.args) call_args.extend(args) return self.directive(*call_args) class DoxygenDirectiveFactory(object): directives = { "doxygenindex": DoxygenIndexDirective, "autodoxygenindex": AutoDoxygenIndexDirective, "doxygenfunction": DoxygenFunctionDirective, "doxygenstruct": DoxygenStructDirective, "doxygenclass": DoxygenClassDirective, "doxygenvariable": DoxygenVariableDirective, "doxygendefine": DoxygenDefineDirective, "doxygenenum": DoxygenEnumDirective, "doxygenenumvalue": DoxygenEnumValueDirective, "doxygentypedef": DoxygenTypedefDirective, "doxygenunion": DoxygenUnionDirective, "doxygennamespace": DoxygenNamespaceDirective, "doxygengroup": DoxygenGroupDirective, "doxygenfile": DoxygenFileDirective, "autodoxygenfile": AutoDoxygenFileDirective, } def __init__(self, node_factory, text_renderer, root_data_object, renderer_factory_creator_constructor, finder_factory, project_info_factory, filter_factory, target_handler_factory, parser_factory): self.node_factory = node_factory self.text_renderer = text_renderer self.root_data_object = root_data_object self.renderer_factory_creator_constructor = renderer_factory_creator_constructor self.finder_factory = finder_factory self.project_info_factory = project_info_factory self.filter_factory = filter_factory self.target_handler_factory = target_handler_factory self.parser_factory = parser_factory # TODO: This methods should be scrapped as they are only called in one place. We should just # inline the code at the call site def create_index_directive_container(self): return self.create_directive_container("doxygenindex") def create_function_directive_container(self): # Pass text_renderer to the function directive return DirectiveContainer( self.directives["doxygenfunction"], self.node_factory, self.text_renderer, self.root_data_object, self.renderer_factory_creator_constructor, self.finder_factory, self.project_info_factory, self.filter_factory, self.target_handler_factory, self.parser_factory ) 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_enumvalue_directive_container(self): return self.create_directive_container("doxygenenumvalue") def create_typedef_directive_container(self): return self.create_directive_container("doxygentypedef") def create_union_directive_container(self): return self.create_directive_container("doxygenunion") def create_class_directive_container(self): return self.create_directive_container("doxygenclass") def create_file_directive_container(self): return self.create_directive_container("doxygenfile") def create_namespace_directive_container(self): return self.create_directive_container("doxygennamespace") def create_group_directive_container(self): return self.create_directive_container("doxygengroup") def create_variable_directive_container(self): return self.create_directive_container("doxygenvariable") def create_define_directive_container(self): return self.create_directive_container("doxygendefine") def create_auto_index_directive_container(self): return self.create_directive_container("autodoxygenindex") def create_auto_file_directive_container(self): return self.create_directive_container("autodoxygenfile") def create_directive_container(self, type_): return DirectiveContainer( self.directives[type_], self.root_data_object, self.renderer_factory_creator_constructor, self.finder_factory, self.project_info_factory, self.filter_factory, self.target_handler_factory, self.parser_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, app.config.breathe_domain_by_extension, app.config.breathe_domain_by_file_pattern, app.config.breathe_projects_source, app.config.breathe_build_directory ) 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) class RootDataObject(object): node_type = "root" class PathHandler(object): def __init__(self, config_directory, sep, basename, join): self.config_directory = config_directory self.sep = sep self.basename = basename self.join = join def includes_directory(self, file_path): # Check for backslash or forward slash as we don't know what platform we're on and sometimes # the doxygen paths will have forward slash even on Windows. return bool(file_path.count('\\')) or bool(file_path.count('/')) def resolve_path(self, directory, filename): """Returns a full path to the filename in the given directory assuming that if the directory path is relative, then it is relative to the conf.py directory. """ # os.path.join does the appropriate handling if _project_path is an absolute path return self.join(self.config_directory, directory, filename) def write_file(directory, filename, content): # Check the directory exists if not os.path.exists(directory): os.makedirs(directory) # Write the file with the provided contents with open(os.path.join(directory, filename), "w") as f: f.write(content) class MTimerError(Exception): pass class MTimer(object): def __init__(self, getmtime): self.getmtime = getmtime def get_mtime(self, filename): try: return self.getmtime(filename) except OSError: raise MTimerError('Cannot find file: %s' % os.path.realpath(filename)) class FileStateCache(object): """ Stores the modified time of the various doxygen xml files against the reStructuredText file that they are referenced from so that we know which reStructuredText files to rebuild if the doxygen xml is modified. We store the information in the environment object so that it is pickled down and stored between builds as Sphinx is designed to do. """ def __init__(self, mtimer, app): self.app = app self.mtimer = mtimer def update(self, source_file): if not hasattr(self.app.env, "breathe_file_state"): self.app.env.breathe_file_state = {} new_mtime = self.mtimer.get_mtime(source_file) mtime, docnames = self.app.env.breathe_file_state.setdefault( source_file, (new_mtime, set()) ) docnames.add(self.app.env.docname) self.app.env.breathe_file_state[source_file] = (new_mtime, docnames) def get_outdated(self, app, env, added, changed, removed): if not hasattr(self.app.env, "breathe_file_state"): return [] stale = [] for filename, info in self.app.env.breathe_file_state.iteritems(): old_mtime, docnames = info if self.mtimer.get_mtime(filename) > old_mtime: stale.extend(docnames) return list(set(stale).difference(removed)) def purge_doc(self, app, env, docname): if not hasattr(self.app.env, "breathe_file_state"): return toremove = [] for filename, info in self.app.env.breathe_file_state.iteritems(): _, docnames = info docnames.discard(docname) if not docnames: toremove.append(filename) for filename in toremove: del self.app.env.breathe_file_state[filename] class DomainDirectiveFactory(object): # A mapping from node kinds to cpp domain classes and directive names. cpp_classes = { 'class': (cpp.CPPClassObject, 'class'), 'struct': (cpp.CPPClassObject, 'class'), 'function': (cpp.CPPFunctionObject, 'function'), 'friend': (cpp.CPPFunctionObject, 'function'), 'slot': (cpp.CPPFunctionObject, 'function'), 'enum': (cpp.CPPTypeObject, 'type'), 'typedef': (cpp.CPPTypeObject, 'type'), 'union': (cpp.CPPTypeObject, 'type'), 'namespace': (cpp.CPPTypeObject, 'type'), # Use CPPClassObject for enum values as the cpp domain doesn't have a directive for # enum values and CPPMemberObject requires a type. 'enumvalue': (cpp.CPPClassObject, 'member'), 'define': (c.CObject, 'macro') } python_classes = { 'function': (python.PyModulelevel, 'function'), 'variable': (python.PyClassmember, 'attribute') } @staticmethod def fix_python_signature(sig): def_ = 'def ' if sig.startswith(def_): sig = sig[len(def_):] # Doxygen uses an invalid separator ('::') in Python signatures. Replace them with '.'. return sig.replace('::', '.') @staticmethod def create(domain, args): if domain == 'c': return c.CObject(*args) if domain == 'py': cls, name = DomainDirectiveFactory.python_classes.get( args[0], (python.PyClasslike, 'class')) args[1] = [DomainDirectiveFactory.fix_python_signature(n) for n in args[1]] else: cls, name = DomainDirectiveFactory.cpp_classes.get( args[0], (cpp.CPPMemberObject, 'member')) # Replace the directive name because domain directives don't know how to handle # Breathe's "doxygen" directives. args = [name] + args[1:] return cls(*args) # Setup # ----- def setup(app): cache_factory = CacheFactory() cache = cache_factory.create_cache() path_handler = PathHandler(app.confdir, os.sep, os.path.basename, os.path.join) mtimer = MTimer(os.path.getmtime) file_state_cache = FileStateCache(mtimer, app) parser_factory = DoxygenParserFactory(cache, path_handler, file_state_cache) glob_factory = GlobFactory(fnmatch.fnmatch) filter_factory = FilterFactory(glob_factory, path_handler) item_finder_factory_creator = DoxygenItemFinderFactoryCreator(parser_factory, filter_factory) index_parser = parser_factory.create_index_parser() finder_factory = FinderFactory(index_parser, item_finder_factory_creator) # Create a math_nodes object with a displaymath member for the displaymath # node so that we can treat it in the same way as the nodes & addnodes # modules in the NodeFactory math_nodes = collections.namedtuple("MathNodes", ["displaymath"]) math_nodes.displaymath = sphinx.ext.mathbase.displaymath node_factory = NodeFactory(docutils.nodes, sphinx.addnodes, math_nodes) rst_content_creator = RstContentCreator(ViewList, textwrap.dedent) renderer_factory_creator_constructor = DoxygenToRstRendererFactoryCreatorConstructor( node_factory, parser_factory, DomainDirectiveFactory, rst_content_creator ) # Assume general build directory is the doctree directory without the last component. We strip # off any trailing slashes so that dirname correctly drops the last part. This can be overriden # with the breathe_build_directory config variable build_dir = os.path.dirname(app.doctreedir.rstrip(os.sep)) project_info_factory = ProjectInfoFactory(app.srcdir, build_dir, app.confdir, fnmatch.fnmatch) target_handler_factory = TargetHandlerFactory(node_factory) root_data_object = RootDataObject() text_renderer = TextRenderer(app) directive_factory = DoxygenDirectiveFactory( node_factory, text_renderer, root_data_object, renderer_factory_creator_constructor, finder_factory, project_info_factory, filter_factory, target_handler_factory, parser_factory ) DoxygenFunctionDirective.app = app 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( "doxygenenumvalue", directive_factory.create_enumvalue_directive_container(), ) app.add_directive( "doxygentypedef", directive_factory.create_typedef_directive_container(), ) app.add_directive( "doxygenunion", directive_factory.create_union_directive_container(), ) app.add_directive( "doxygenclass", directive_factory.create_class_directive_container(), ) app.add_directive( "doxygenfile", directive_factory.create_file_directive_container(), ) app.add_directive( "doxygennamespace", directive_factory.create_namespace_directive_container(), ) app.add_directive( "doxygengroup", directive_factory.create_group_directive_container(), ) app.add_directive( "doxygenvariable", directive_factory.create_variable_directive_container(), ) app.add_directive( "doxygendefine", directive_factory.create_define_directive_container(), ) app.add_directive( "autodoxygenindex", directive_factory.create_auto_index_directive_container(), ) app.add_directive( "autodoxygenfile", directive_factory.create_auto_file_directive_container(), ) app.add_config_value("breathe_projects", {}, True) app.add_config_value("breathe_default_project", "", True) # Provide reasonable defaults for domain_by_extension mapping. Can be overridden by users. app.add_config_value("breathe_domain_by_extension", {'py': 'py'}, True) app.add_config_value("breathe_domain_by_file_pattern", {}, True) app.add_config_value("breathe_projects_source", {}, True) app.add_config_value("breathe_build_directory", '', True) app.add_config_value("breathe_default_members", (), True) app.add_config_value("breathe_implementation_filename_extensions", ['.c', '.cc', '.cpp'], True) breathe_css = "breathe.css" if (os.path.exists(os.path.join(app.confdir, "_static", breathe_css))): app.add_stylesheet(breathe_css) doxygen_handle = AutoDoxygenProcessHandle( path_handler, subprocess.check_call, write_file, project_info_factory ) app.connect("builder-inited", doxygen_handle.generate_xml) app.connect("builder-inited", directive_factory.get_config_values) app.connect("builder-inited", filter_factory.get_config_values) app.connect("env-get-outdated", file_state_cache.get_outdated) app.connect("env-purge-doc", file_state_cache.purge_doc)