diff --git a/docs-ref/Doxyfile b/docs-ref/Doxyfile index 62a8ff88..34dbcec2 100644 --- a/docs-ref/Doxyfile +++ b/docs-ref/Doxyfile @@ -813,7 +813,7 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = ../src ../src/common ../src/widgets ../src/dialogs ../src/menus +INPUT = ../src ../src/core ../src/common ../src/widgets ../src/dialogs ../src/menus ../src/plugins # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/docs-ref/Makefile b/docs-ref/Makefile index 5012702c..07c78ba3 100644 --- a/docs-ref/Makefile +++ b/docs-ref/Makefile @@ -17,5 +17,11 @@ help: # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). %: Makefile doxygen - python3 apidoc.py -g class,interface,struct,union -f -o source/api doxygen-out/xml/ + python3 apidoc.py @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +clean: + rm -fr doxygen-out + rm -fr source/api + rm -fr build + @$(SPHINXBUILD) -M clean "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) diff --git a/docs-ref/apidoc.py b/docs-ref/apidoc.py index 3b0e508e..d0b67422 100644 --- a/docs-ref/apidoc.py +++ b/docs-ref/apidoc.py @@ -1,82 +1,34 @@ # -*- coding: utf-8 -*- -""" - breathe.apidoc - ~~~~~~~~~~~~~~ - - Parses doxygen XML tree looking for C/C++ modules and creates ReST files - appropriately to create code documentation with Sphinx. It also creates a - modules index (See TYPEDICT below.). - - This is derived from the "sphinx-autopackage" script, which is: - Copyright 2008 Société des arts technologiques (SAT), - http://www.sat.qc.ca/ - - :copyright: Originally by Sphinx Team, C++ modifications by Tatsuyuki Ishi - :license: BSD, see LICENSE for details. -""" -from __future__ import print_function - import os import sys -import argparse import errno import xml.etree.ElementTree -from breathe import __version__ -# Account for FileNotFoundError in Python 2 -# IOError is broader but will hopefully suffice -try: - FileNotFoundError -except NameError: - FileNotFoundError = IOError +ALLOWED_TYPES = ['class', 'interface', 'struct', 'union'] -# Reference: Doxygen XSD schema file, CompoundKind only -# Only what breathe supports are included -# Translates identifier to English -TYPEDICT = {'class': 'Class', - 'interface': 'Interface', - 'struct': 'Struct', - 'union': 'Union', - 'file': 'File', - 'namespace': 'Namespace', - 'group': 'Group'} - - -def print_info(msg, args): - if not args.quiet: - print(msg) - - -def write_file(name, text, args): +def write_file(name, text, destdir): """Write the output file for module/package .""" - fname = os.path.join(args.destdir, '%s.%s' % (name, args.suffix)) - if args.dryrun: - print_info('Would create file %s.' % fname, args) - return - if not args.force and os.path.isfile(fname): - print_info('File %s already exists, skipping.' % fname, args) - else: - print_info('Creating file %s.' % fname, args) - if not os.path.exists(os.path.dirname(fname)): - try: - os.makedirs(os.path.dirname(fname)) - except OSError as exc: # Guard against race condition - if exc.errno != errno.EEXIST: - raise - try: - with open(fname, 'r') as target: - orig = target.read() - if orig == text: - print_info('File %s up to date, skipping.' % fname, args) - return - except FileNotFoundError: - # Don't mind if it isn't there - pass + fname = os.path.join(destdir, '%s.%s' % (name, 'rst')) - with open(fname, 'w') as target: - target.write(text) + if not os.path.exists(os.path.dirname(fname)): + try: + os.makedirs(os.path.dirname(fname)) + except OSError as exc: # Guard against race condition + if exc.errno != errno.EEXIST: + raise + try: + with open(fname, 'r') as target: + orig = target.read() + if orig == text: + return + except FileNotFoundError: + # Don't mind if it isn't there + pass + + with open(fname, 'w') as target: + target.write(text) def format_heading(level, text): @@ -85,7 +37,7 @@ def format_heading(level, text): return '%s\n%s\n\n' % (text, underlining) -def format_directive(package_type, package, project): +def format_directive(package_type, package, project = None): """Create the breathe directive and add the options.""" directive = '.. doxygen%s:: %s\n' % (package_type, package) if project: @@ -93,104 +45,90 @@ def format_directive(package_type, package, project): return directive -def create_package_file(package, package_type, package_id, args): +def create_package_file(package, package_type, package_id, package_folder, rootpath, destdir): """Build the text of the file and write the file.""" - # Skip over types that weren't requested - if package_type not in args.outtypes: - return text = format_heading(1, '%s' % (package)) - text += format_directive(package_type, package, args.project) + text += format_directive(package_type, package) - write_file(os.path.join(package_type, package_id), text, args) + xmlfile = os.path.join(rootpath, package_id + '.xml') + f = xml.etree.ElementTree.parse(os.path.join(xmlfile)) + + write_file(os.path.join(package_folder, package_id), text, destdir) -def create_modules_toc_file(key, value, args): +def create_modules_toc_file(key, value, destdir): """Create the module's index.""" - if not os.path.isdir(os.path.join(args.destdir, key)): - return - text = format_heading(1, '%s list' % value) + text = format_heading(1, '%s' % value) text += '.. toctree::\n' text += ' :glob:\n\n' text += ' %s/*\n' % key - write_file('%slist' % key, text, args) + write_file('%slist' % key, text, destdir) -def recurse_tree(args): +def get_compound_folder(rootpath, compound): + fxml = xml.etree.ElementTree.parse(os.path.join(rootpath, compound.get('refid')) + '.xml') + loc = fxml.getroot()[0].find('location') + dirname = os.path.basename(os.path.split(loc.get('file'))[0]) + return dirname + + +def recurse_tree(rootpath, destdir): """ Look for every file in the directory tree and create the corresponding ReST files. """ - index = xml.etree.ElementTree.parse(os.path.join(args.rootpath, 'index.xml')) + index = xml.etree.ElementTree.parse(os.path.join(rootpath, 'index.xml')) - # Assuming this is a valid Doxygen XML for compound in index.getroot(): + if compound.get('kind') not in ALLOWED_TYPES: + continue create_package_file(compound.findtext('name'), compound.get('kind'), - compound.get('refid'), args) + compound.get('refid'), get_compound_folder(rootpath, compound), + rootpath, destdir) +def get_folders_tree(rootpath): + tmp = [] -class TypeAction(argparse.Action): - def __init__(self, option_strings, dest, **kwargs): - super(TypeAction, self).__init__(option_strings, dest, **kwargs) - self.default = TYPEDICT.keys() - self.metavar = ','.join(TYPEDICT.keys()) + # Retrieve the subfolders indexes + for root, _, files in os.walk(rootpath): + for xmlfile in files: + if not xmlfile.startswith('dir_'): + continue + tmp.append(xmlfile) - def __call__(self, parser, namespace, values, option_string=None): - value_list = values.split(',') - for value in value_list: - if value not in TYPEDICT: - raise ValueError("%s not a valid option" % value) - setattr(namespace, self.dest, value_list) + # Iterate on them + dirs = [] + for xmlfile in tmp: + data = xml.etree.ElementTree.parse(os.path.join(rootpath, xmlfile)) + if not data: + continue + for compound in data.getroot(): + name = compound.findtext('compoundname') + dirs.append(name) + + return dirs def main(): - """Parse and check the command line arguments.""" - parser = argparse.ArgumentParser( - description="""\ -Parse XML created by Doxygen in and create one reST file with -breathe generation directives per definition in the . + rootpath = './doxygen-out/xml' + destdir = './source/api' -Note: By default this script will not overwrite already created files.""", - formatter_class=argparse.RawDescriptionHelpFormatter) + if not os.path.exists(destdir): + os.makedirs(destdir) - parser.add_argument('-o', '--output-dir', action='store', dest='destdir', - help='Directory to place all output', required=True) - parser.add_argument('-f', '--force', action='store_true', dest='force', - help='Overwrite existing files') - parser.add_argument('-n', '--dry-run', action='store_true', dest='dryrun', - help='Run the script without creating files') - parser.add_argument('-T', '--no-toc', action='store_true', dest='notoc', - help='Don\'t create a table of contents file') - parser.add_argument('-s', '--suffix', action='store', dest='suffix', - help='file suffix (default: rst)', default='rst') - parser.add_argument('-p', '--project', action='store', dest='project', - help='project to add to generated directives') - parser.add_argument('-g', '--generate', action=TypeAction, dest='outtypes', - help='types of output to generate, comma-separated list') - parser.add_argument('-q', '--quiet', action='store_true', dest='quiet', - help='suppress informational messages') - parser.add_argument('--version', action='version', - version='Breathe (breathe-apidoc) %s' % __version__) - parser.add_argument('rootpath', type=str, - help='The directory contains index.xml') - args = parser.parse_args() + dirs = sorted(get_folders_tree(rootpath)) + source_root = dirs[0] + source_dirs = dirs[1:] + out_dirs = [os.path.basename(d) for d in dirs] - if args.suffix.startswith('.'): - args.suffix = args.suffix[1:] - if not os.path.isdir(args.rootpath): - print('%s is not a directory.' % args.rootpath, file=sys.stderr) - sys.exit(1) - if 'index.xml' not in os.listdir(args.rootpath): - print('%s does not contain a index.xml' % args.rootpath, file=sys.stderr) - sys.exit(1) - if not os.path.isdir(args.destdir): - if not args.dryrun: - os.makedirs(args.destdir) - args.rootpath = os.path.abspath(args.rootpath) - recurse_tree(args) - if not args.notoc: - for key in args.outtypes: - create_modules_toc_file(key, TYPEDICT[key], args) + # TODO Handle only one level subfolders + for key in out_dirs: + ddir = os.path.join(destdir, key) + if not os.path.exists(ddir): + os.makedirs(ddir) + create_modules_toc_file(key, key.capitalize(), destdir) + recurse_tree(rootpath, destdir) # So program can be started with "python -m breathe.apidoc ..."