mirror of
https://github.com/rizinorg/cutter.git
synced 2025-01-18 18:38:51 +00:00
Made apidoc.py handle subfolders
Breathe apidoc.py used to classify by type (class, struct, etc.) This is not useful when you have too many classes, so it's now ordered by subfolders.
This commit is contained in:
parent
f67e34697f
commit
2b85d7bfaf
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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 <name>."""
|
||||
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 <rootpath> and create one reST file with
|
||||
breathe generation directives per definition in the <DESTDIR>.
|
||||
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 ..."
|
||||
|
Loading…
Reference in New Issue
Block a user