#!/usr/bin/python # -*- coding: utf-8 -*- # Copyright (C) 2017 Belledonne Communications SARL # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. import argparse import hashlib import logging import os import pystache import sys sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..', '..', 'tools')) import abstractapi import genapixml as capi import metaname import metadoc def md5sum(file): hasher = hashlib.md5() with open(file, mode='rb') as f: hasher.update(f.read()) return hasher.hexdigest() class RstTools: @staticmethod def make_part(text): return RstTools.make_section(text, char='#', overline=True) @staticmethod def make_chapter(text): return RstTools.make_section(text, char='*', overline=True) @staticmethod def make_section(text, char='=', overline=False): size = len(text) underline = (char*size) lines = [text, underline] if overline: lines.insert(0, underline) return '\n'.join(lines) @staticmethod def make_subsection(text): return RstTools.make_section(text, char='-') @staticmethod def make_subsubsection(text): return RstTools.make_section(text, char='^') class Table: def __init__(self): self._rows = [] self._widths = [] self._heights = [] @property def rows(self): return self._rows def addrow(self, row): if len(self._widths) == 0: self._widths.append(0) self._widths *= len(row) elif len(row) != len(self._widths): raise ValueError('row width mismatch table width') height = 0 row2 = [] i = 0 while i 0 @property def hasClassMethods(self): return len(self.classMethods) > 0 @property def hasProperties(self): return len(self.properties) > 0 @property def hasEnums(self): return len(self.enums) > 0 def _translate_properties(self, properties): translatedProperties = [] for property_ in properties: propertyAttr = { 'name' : property_.name.translate(self.lang.nameTranslator), 'getter' : self._translate_method(property_.getter) if property_.getter is not None else None, 'setter' : self._translate_method(property_.setter) if property_.setter is not None else None } propertyAttr['title'] = RstTools.make_subsubsection(propertyAttr['name']) propertyAttr['ref_label'] = (self.lang.langCode + '_') propertyAttr['ref_label'] += (property_.getter.name.to_snake_case(fullName=True) if property_.getter is not None else property_.setter.name.to_snake_case(fullName=True)) translatedProperties.append(propertyAttr) return translatedProperties def _translate_methods(self, methods): translatedMethods = [] for method in methods: translatedMethods.append(self._translate_method(method)) return translatedMethods def _translate_method(self, method): namespace = method.find_first_ancestor_by_type(abstractapi.Class) methAttr = { 'prototype' : method.translate_as_prototype(self.lang.langTranslator, namespace=namespace), 'briefDoc' : method.briefDescription.translate(self.docTranslator), 'detailedDoc' : method.detailedDescription.translate(self.docTranslator), 'selector' : self._make_selector(method) } reference = metadoc.FunctionReference(None) reference.relatedObject = method methAttr['link'] = reference.translate(self.lang.docTranslator, namespace=method.find_first_ancestor_by_type(abstractapi.Class, abstractapi.Interface)) return methAttr @property def enumsSummary(self): table = RstTools.Table() for enum in self.enums: briefDoc = '\n'.join([line['line'] for line in enum.briefDesc['lines']]) table.addrow((enum.link, briefDoc)) return table @property def propertiesSummary(self): table = RstTools.Table() for property_ in self.properties: reference = ':ref:`{0}`'.format(property_['ref_label']) briefDoc = property_['getter']['briefDoc'] if property_['getter'] is not None else property_['setter']['briefDoc'] briefDoc = '\n'.join([line['line'] for line in briefDoc['lines']]) table.addrow([reference, briefDoc]) return table @property def instanceMethodsSummary(self): table = RstTools.Table() for method in self.methods: briefDoc = '\n'.join([line['line'] for line in method['briefDoc']['lines']]) table.addrow([method['link'], briefDoc]) return table @property def classMethodsSummary(self): table = RstTools.Table() for method in self.classMethods: briefDoc = '\n'.join([line['line'] for line in method['briefDoc']['lines']]) table.addrow([method['link'], briefDoc]) return table class OldFilesCleaner: def __init__(self, rootDirectory): self._filesToKeep = set() self.root = rootDirectory def add_directory(self, directory): self._filesToKeep.add(directory) def clean(self): self._clean(self.root) def _clean(self, dir_): if os.path.isdir(dir_): for filename in os.listdir(dir_): self._clean(os.path.join(dir_, filename)) elif dir_ not in self._filesToKeep: os.remove(dir_) class DocGenerator: def __init__(self, api): self.api = api self.languages = [ LangInfo('C'), LangInfo('Cpp'), LangInfo('Java'), LangInfo('CSharp') ] def generate(self, outputdir): for lang in self.languages: directory = os.path.join(args.outputdir, lang.directory) cleaner = OldFilesCleaner(directory) indexPage = IndexPage(lang, 'index.rst') if not os.path.exists(directory): os.mkdir(directory) for enum in self.api.namespace.enums: page = EnumPage(enum, lang, self.languages) filepath = page.write(directory) indexPage.add_entry(page.filename) cleaner.add_directory(filepath) for class_ in self.api.namespace.classes: page = ClassPage(class_, lang, self.languages) filepath = page.write(directory) indexPage.add_entry(page.filename) cleaner.add_directory(filepath) filepath = indexPage.write(directory) cleaner.add_directory(filepath) cleaner.clean() if __name__ == '__main__': argparser = argparse.ArgumentParser(description='Generate a sphinx project to generate the documentation of Linphone Core API.') argparser.add_argument('xmldir', type=str, help='directory holding the XML documentation of the C API generated by Doxygen') argparser.add_argument('-o --output', type=str, help='directory into where Sphinx source files will be written', dest='outputdir', default='.') argparser.add_argument('-v --verbose', action='store_true', default=False, dest='verbose_mode', help='Show warning and info messages') args = argparser.parse_args() loglevel = logging.INFO if args.verbose_mode else logging.ERROR logging.basicConfig(format='%(levelname)s[%(name)s]: %(message)s', level=loglevel) cProject = capi.Project() cProject.initFromDir(args.xmldir) cProject.check() absApiParser = abstractapi.CParser(cProject) absApiParser.parse_all() docGenerator = DocGenerator(absApiParser) docGenerator.generate(args.outputdir)