diff --git a/src/tools/doxproc.py b/src/tools/doxproc.py new file mode 100644 index 000000000..4b497c76c --- /dev/null +++ b/src/tools/doxproc.py @@ -0,0 +1,617 @@ +#!/usr/bin/python +# Copyright 2006 Rene Rivera +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +''' +Processing of Doxygen generated XML. +''' + +import os +import os.path +import sys +import time +import string +import getopt +import glob +import re +import xml.dom.minidom + + +def usage(): + print ''' +Usage: + %s options + +Options: + --xmldir Directory with the Doxygen xml result files. + --output Write the output BoostBook to the given location. + --id The ID of the top level BoostBook section. + --title The title of the top level BoostBook section. + --enable-index Generate additional index sections for classes and + types. +''' % ( sys.argv[0] ) + + +def get_args( argv = sys.argv[1:] ): + spec = [ + 'xmldir=', + 'output=', + 'id=', + 'title=', + 'enable-index', + 'help' ] + options = { + '--xmldir' : 'xml', + '--output' : None, + '--id' : 'dox', + '--title' : 'Doxygen' + } + ( option_pairs, other ) = getopt.getopt( argv, '', spec ) + map( lambda x: options.__setitem__( x[0], x[1] ), option_pairs ) + + if options.has_key( '--help' ): + usage() + sys.exit(1) + + return { + 'xmldir' : options['--xmldir'], + 'output' : options['--output'], + 'id' : options['--id'], + 'title' : options['--title'], + 'index' : options.has_key('--enable-index') + } + +def if_attribute(node, attribute, true_value, false_value=None): + if node.getAttribute(attribute) == 'yes': + return true_value + else: + return false_value + +class Doxygen2BoostBook: + + def __init__( self, + #~ id=None, + #~ title='', + #~ last_revision=None, + **kwargs ): + ## + self.args = kwargs + self.args.setdefault('id','') + self.args.setdefault('title','') + self.args.setdefault('last_revision', time.asctime()) + self.args.setdefault('index', False) + self.id = '%(id)s.reference' % self.args + self.args['id'] = self.id + self.boostbook = xml.dom.minidom.parseString(''' +
+ %(title)s + + Headers + + + Classes + + + Index + +
+''' % self.args ) + self.section = { + 'headers' : self._getChild('library-reference',id='%(id)s.headers' % self.args), + 'classes' : self._getChild('index',id='%(id)s.classes' % self.args), + 'index' : self._getChild('index',id='%(id)s.index' % self.args) + } + if not self.args['index']: + self.section['classes'].parentNode.removeChild(self.section['classes']) + self.section['classes'].unlink() + del self.section['classes'] + self.section['index'].parentNode.removeChild(self.section['index']) + self.section['index'].unlink() + del self.section['index'] + self.symbols = {} + self.generated = False + self.idmap = {} + + def addDox( self, document ): + ## + self._translateNode(document.documentElement) + + def tostring( self ): + self._generate() + #~ return self.boostbook.toprettyxml(' ') + return self.boostbook.toxml('utf-8') + + def _generate( self ): + if not self.generated: + self.generated = True + symbols = self.symbols.keys() + symbols.sort() + for symbol in symbols: + if self.symbols[symbol]['kind'] in ('header'): + self.section['headers'].appendChild(self.symbols[symbol]['dom']) + for symbol in symbols: + if self.symbols[symbol]['kind'] not in ('namespace', 'header'): + container = self._resolveContainer(self.symbols[symbol], + self.symbols[self.symbols[symbol]['header']]['dom']) + if container.nodeName != 'namespace': + ## The current BoostBook to Docbook translation doesn't + ## respect, nor assign, IDs to inner types of any kind. + ## So nuke the ID entry so as not create bogus links. + del self.idmap[self.symbols[symbol]['id']] + container.appendChild(self.symbols[symbol]['dom']) + self._rewriteIDs(self.boostbook.documentElement) + + def _rewriteIDs( self, node ): + if node.nodeName in ('link'): + if (self.idmap.has_key(node.getAttribute('linkend'))): + node.setAttribute('linkend',self.idmap[node.getAttribute('linkend')]) + else: + node.removeAttribute('linkend') + elif hasattr(node,'hasAttribute') and node.hasAttribute('id') and self.idmap.has_key(node.getAttribute('id')): + node.setAttribute('id',self.idmap[node.getAttribute('id')]) + if node.firstChild: + self._rewriteIDs(node.firstChild) + if node.nextSibling: + self._rewriteIDs(node.nextSibling) + + def _resolveContainer( self, cpp, root ): + container = root + for ns in cpp['namespace']: + node = self._getChild('namespace',name=ns,root=container) + if not node: + node = container.appendChild( + self._createNode('namespace',name=ns)) + container = node + for inner in cpp['name'].split('::'): + node = self._getChild(name=inner,root=container) + if not node: + break + container = node + return container + + def _setID( self, id, name ): + self.idmap[id] = name.replace('::','.').replace('/','.') + #~ print '--| setID:',id,'::',self.idmap[id] + + def _translateNode( self, *context, **kwargs ): + node = None + name = '_translate' + for c in context: + if c: + if not isinstance(c,xml.dom.Node): + name += '_'+c + else: + name += '_'+c.nodeName + node = c + name = name.replace('-','_') + #~ print '_translateNode:', name + if node and hasattr(self,name): + return getattr(self,name)(node,**kwargs) + else: + return None + + def _translateChildren( self, parent, **kwargs ): + target = kwargs['target'] + for n in parent.childNodes: + child = self._translateNode(n,target=target) + if child: + target.appendChild(child) + else: + child = n.cloneNode(False) + if hasattr(child,'data'): + child.data = child.data.strip() + target.appendChild(child) + self._translateChildren(n,target=child) + + def _translateDescription( self, node, target=None, tag='description', **kwargs ): + description = self._getChild(tag,root=target) + if not description: + description = target.appendChild(self._createNode(tag)) + self._translateChildren(node,target=description) + return description + + def _translate_doxygen( self, node ): + #~ print '_translate_doxygen:', node.nodeName + result = [] + for n in node.childNodes: + newNode = self._translateNode(n) + if newNode: + result.append(newNode) + return result + + def _translate_doxygenindex( self, node ): + #~ print '_translate_doxygenindex:', node.nodeName + if self.args['index']: + entries = [] + classes = [] + for n in node.childNodes: + if n.nodeName == 'compound': + if n.getAttribute('kind') not in ('file','dir','define'): + cpp = self._cppName(self._getChildData('name',root=n)) + entry = { + 'name' : cpp['name'], + 'compoundname' : cpp['compoundname'], + 'id' : n.getAttribute('refid') + } + if n.getAttribute('kind') in ('class','struct'): + classes.append(entry) + entries.append(entry) + for m in n.childNodes: + if m.nodeName == 'member': + cpp = self._cppName(self._getChildData('name',root=m)) + entry = { + 'name' : cpp['name'], + 'compoundname' : cpp['compoundname'], + 'id' : n.getAttribute('refid') + } + if hasattr(m,'getAttribute') and m.getAttribute('kind') in ('class','struct'): + classes.append(entry) + entries.append(entry) + entries.sort(lambda x,y: cmp(x['name'].lower(),y['name'].lower())) + classes.sort(lambda x,y: cmp(x['name'].lower(),y['name'].lower())) + self._translate_index_(entries,target=self.section['index']) + self._translate_index_(classes,target=self.section['classes']) + return None + + def _translate_index_(self, entries, target=None, **kwargs ): + i = 0 + targetID = target.getAttribute('id') + while i < len(entries): + dividerKey = entries[i]['name'][0].upper() + divider = target.appendChild(self._createNode('indexdiv',id=targetID+'.'+dividerKey)) + divider.appendChild(self._createText('title',dividerKey)) + while i < len(entries) and dividerKey == entries[i]['name'][0].upper(): + iename = entries[i]['name'] + ie = divider.appendChild(self._createNode('indexentry')) + ie = ie.appendChild(self._createText('primaryie',iename)) + while i < len(entries) and entries[i]['name'] == iename: + ie.appendChild(self.boostbook.createTextNode(' (')) + ie.appendChild(self._createText( + 'link',entries[i]['compoundname'],linkend=entries[i]['id'])) + ie.appendChild(self.boostbook.createTextNode(')')) + i += 1 + + def _translate_compounddef( self, node, target=None, **kwargs ): + return self._translateNode(node,node.getAttribute('kind')) + + def _translate_compounddef_namespace( self, node, target=None, **kwargs ): + #~ print '--| _translate_compounddef_namespace:', node.getAttribute('id') + namespace = { + 'id' : node.getAttribute('id'), + 'kind' : 'namespace', + 'name' : self._getChildData('compoundname',root=node), + 'brief' : self._getChildData('briefdescription',root=node), + 'detailed' : self._getChildData('detaileddescription',root=node), + 'parsed' : False + } + if self.symbols.has_key(namespace['name']): + if not self.symbols[namespace['name']]['parsed']: + self.symbols[namespace['name']]['parsed'] = True + #~ for n in node.childNodes: + #~ if hasattr(n,'getAttribute'): + #~ self._translateNode(n,n.getAttribute('kind'),target=target,**kwargs) + else: + self.symbols[namespace['name']] = namespace + #~ self._setID(namespace['id'],namespace['name']) + return None + + def _translate_compounddef_class( self, node, target=None, **kwargs ): + return self._translate_compounddef_struct(node,tag='class',target=target,**kwargs) + + def _translate_compounddef_struct( self, node, tag='struct', target=None, **kwargs ): + #~ print '--| _translate_compounddef_struct:', node.getAttribute('id') + result = None + includes = self._getChild('includes',root=node) + if includes: + ## Add the header into the output table. + self._translate_compounddef_includes_(includes,includes,**kwargs) + ## Compounds are the declared symbols, classes, types, etc. + ## We add them to the symbol table, along with the partial DOM for them + ## so that they can be organized into the output later. + compoundname = self._getChildData('compoundname',root=node) + compoundname = self._cppName(compoundname) + self._setID(node.getAttribute('id'),compoundname['compoundname']) + struct = self._createNode(tag,name=compoundname['name'].split('::')[-1]) + self.symbols[compoundname['compoundname']] = { + 'header' : includes.firstChild.data, + 'namespace' : compoundname['namespace'], + 'id' : node.getAttribute('id'), + 'kind' : tag, + 'name' : compoundname['name'], + 'dom' : struct + } + for n in node.childNodes: + self._translateNode(n,target=struct,scope=compoundname['compoundname']) + result = struct + return result + + def _translate_compounddef_includes_( self, node, target=None, **kwargs ): + name = node.firstChild.data + if not self.symbols.has_key(name): + self._setID(node.getAttribute('refid'),name) + self.symbols[name] = { + 'kind' : 'header', + 'id' : node.getAttribute('refid'), + 'dom' : self._createNode('header', + id=node.getAttribute('refid'), + name=name) + } + return None + + def _translate_basecompoundref( self, ref, target=None, **kwargs ): + inherit = target.appendChild(self._createNode('inherit', + access=ref.getAttribute('prot'))) + self._translateChildren(ref,target=inherit) + return + + def _translate_templateparamlist( self, templateparamlist, target=None, **kwargs ): + template = target.appendChild(self._createNode('template')) + for param in templateparamlist.childNodes: + if param.nodeName == 'param': + paramKind = None + if self._getChildData('type',root=param) in ( + 'class','typename'): + paramKind = 'template-type-parameter' + else: + paramKind = 'template-nontype-parameter' + templateParam = template.appendChild( + self._createNode(paramKind, + name=self._getChildData('declname',root=param))) + defval = self._getChild('defval',root=param) + if defval: + templateParam.appendChild(self._createText('default', + self._getChildData('ref',root=defval.firstChild))) + return template + + def _translate_briefdescription( self, brief, target=None, **kwargs ): + self._translateDescription(brief,target=target,**kwargs) + return self._translateDescription(brief,target=target,tag='purpose',**kwargs) + + def _translate_detaileddescription( self, detailed, target=None, **kwargs ): + return self._translateDescription(detailed,target=target,**kwargs) + + def _translate_sectiondef( self, sectiondef, target=None, **kwargs ): + self._translateNode(sectiondef,sectiondef.getAttribute('kind'),target=target,**kwargs) + + def _translate_sectiondef_x_( self, sectiondef, target=None, **kwargs ): + for n in sectiondef.childNodes: + if hasattr(n,'getAttribute'): + self._translateNode(n,n.getAttribute('kind'),target=target,**kwargs) + return None + + def _translate_sectiondef_func_( self, sectiondef, name='functions', target=None, **kwargs ): + members = target.appendChild(self._createNode('method-group',name=name)) + for n in sectiondef.childNodes: + if hasattr(n,'getAttribute'): + self._translateNode(n,n.getAttribute('kind'),target=members,**kwargs) + return members + + def _translate_sectiondef_public_type( self, sectiondef, target=None, **kwargs ): + return self._translate_sectiondef_x_(sectiondef,target=target,**kwargs) + + def _translate_sectiondef_public_attrib( self, sectiondef, target=None, **kwargs): + return self._translate_sectiondef_x_(sectiondef,target=target,**kwargs) + + def _translate_sectiondef_public_func( self, sectiondef, target=None, **kwargs ): + return self._translate_sectiondef_func_(sectiondef, + name='public member functions',target=target,**kwargs) + + def _translate_sectiondef_public_static_func( self, sectiondef, target=None, **kwargs): + return self._translate_sectiondef_func_(sectiondef, + name='public static functions',target=target,**kwargs) + + def _translate_sectiondef_protected_func( self, sectiondef, target=None, **kwargs ): + return self._translate_sectiondef_func_(sectiondef, + name='protected member functions',target=target,**kwargs) + + def _translate_sectiondef_private_static_func( self, sectiondef, target=None, **kwargs): + return self._translate_sectiondef_func_(sectiondef, + name='private static functions',target=target,**kwargs) + + def _translate_memberdef_typedef( self, memberdef, target=None, scope=None, **kwargs ): + self._setID(memberdef.getAttribute('id'), + scope+'::'+self._getChildData('name',root=memberdef)) + typedef = target.appendChild(self._createNode('typedef', + id=memberdef.getAttribute('id'), + name=self._getChildData('name',root=memberdef))) + typedef_type = typedef.appendChild(self._createNode('type')) + self._translateChildren(self._getChild('type',root=memberdef),target=typedef_type) + return typedef + + def _translate_memberdef_function( self, memberdef, target=None, scope=None, **kwargs ): + ## The current BoostBook to Docbook translator doesn't respect method + ## Ids. Nor does it assign any useable IDs to the individial methods. + # self._setID(memberdef.getAttribute('id'), + # scope+'::'+self._getChildData('name',root=memberdef)) + ## Hence instead of registering an ID for the method we point it at the + ## containing class. + self._setID(memberdef.getAttribute('id'),scope) + method = target.appendChild(self._createNode('method', + # id=memberdef.getAttribute('id'), + name=self._getChildData('name',root=memberdef), + cv=' '.join([ + if_attribute(memberdef,'const','const','').strip() + ]), + specifiers=' '.join([ + if_attribute(memberdef,'static','static',''), + if_attribute(memberdef,'explicit','explicit',''), + if_attribute(memberdef,'inline','inline','') + ]).strip() + )) + for n in memberdef.childNodes: + self._translateNode(memberdef,'function',n,target=method) + return method + + def _translate_memberdef_function_templateparamlist( + self, templateparamlist, target=None, **kwargs ): + return self._translate_templateparamlist(templateparamlist,target=target,**kwargs) + + def _translate_memberdef_function_type( self, resultType, target=None, **kwargs ): + methodType = target.appendChild(self._createNode('type')) + self._translateChildren(resultType,target=methodType) + return methodType + + def _translate_memberdef_function_briefdescription( self, description, target=None, **kwargs ): + self._translateDescription(description,target=target,**kwargs) + return self._translateDescription(description,target=target,tag='purpose',**kwargs) + + def _translate_memberdef_function_detaileddescription( self, description, target=None, **kwargs ): + return self._translateDescription(description,target=target,**kwargs) + + def _translate_memberdef_function_inbodydescription( self, description, target=None, **kwargs ): + return self._translateDescription(description,target=target,**kwargs) + + def _translate_memberdef_function_param( self, param, target=None, **kwargs ): + return self._translate_param(param,target=target,**kwargs) + + def _translate_memberdef_variable( self, memberdef, target=None, scope=None, **kwargs ): + self._setID(memberdef.getAttribute('id'), + scope+'::'+self._getChildData('name',root=memberdef)) + data_member = target.appendChild(self._createNode('data-member', + id=memberdef.getAttribute('id'), + name=self._getChildData('name',root=memberdef))) + data_member_type = data_member.appendChild(self._createNode('type')) + self._translateChildren(self._getChild('type',root=memberdef),target=data_member_type) + + def _translate_memberdef_enum( self, memberdef, target=None, scope=None, **kwargs ): + self._setID(memberdef.getAttribute('id'), + scope+'::'+self._getChildData('name',root=memberdef)) + enum = target.appendChild(self._createNode('enum', + id=memberdef.getAttribute('id'), + name=self._getChildData('name',root=memberdef))) + for n in memberdef.childNodes: + self._translateNode(memberdef,'enum',n,target=enum,scope=scope,**kwargs) + return enum + + def _translate_memberdef_enum_enumvalue( self, enumvalue, target=None, scope=None, **kwargs ): + self._setID(enumvalue.getAttribute('id'), + scope+'::'+self._getChildData('name',root=enumvalue)) + value = target.appendChild(self._createNode('enumvalue', + id=enumvalue.getAttribute('id'), + name=self._getChildData('name',root=enumvalue))) + initializer = self._getChild('initializer',root=enumvalue) + if initializer: + self._translateChildren(initializer, + target=target.appendChild(self._createNode('default'))) + return value + + def _translate_param( self, param, target=None, **kwargs): + parameter = target.appendChild(self._createNode('parameter', + name=self._getChildData('declname',root=param))) + paramtype = parameter.appendChild(self._createNode('paramtype')) + self._translateChildren(self._getChild('type',root=param),target=paramtype) + defval = self._getChild('defval',root=param) + if defval: + self._translateChildren(self._getChild('defval',root=param),target=parameter) + return parameter + + def _translate_ref( self, ref, **kwargs ): + return self._translateNode(ref,ref.getAttribute('kindref')) + + def _translate_ref_compound( self, ref, **kwargs ): + result = self._createNode('link',linkend=ref.getAttribute('refid')) + classname = result.appendChild(self._createNode('classname')) + self._translateChildren(ref,target=classname) + return result + + def _translate_ref_member( self, ref, **kwargs ): + result = self._createNode('link',linkend=ref.getAttribute('refid')) + self._translateChildren(ref,target=result) + return result + + def _getChild( self, tag = None, id = None, name = None, root = None ): + if not root: + root = self.boostbook.documentElement + for n in root.childNodes: + found = True + if tag and found: + found = found and tag == n.nodeName + if id and found: + if n.hasAttribute('id'): + found = found and n.getAttribute('id') == id + else: + found = found and n.hasAttribute('id') and n.getAttribute('id') == id + if name and found: + found = found and n.hasAttribute('name') and n.getAttribute('name') == name + if found: + #~ print '--|', n + return n + return None + + def _getChildData( self, tag, **kwargs ): + child = self._getChild(tag,**kwargs) + if child: + text = self._getChild('#text',root=child) + if text: + return text.data.strip() + return '' + + def _cppName( self, type ): + parts = re.search('^([^<]+)[<]?(.*)[>]?$',type.strip().strip(':')) + result = { + 'compoundname' : parts.group(1), + 'namespace' : parts.group(1).split('::')[0:-1], + 'name' : parts.group(1).split('::')[-1], + 'specialization' : parts.group(2) + } + if result['namespace'] and len(result['namespace']) > 0: + namespace = '::'.join(result['namespace']) + while ( + len(result['namespace']) > 0 and ( + not self.symbols.has_key(namespace) or + self.symbols[namespace]['kind'] != 'namespace') + ): + result['name'] = result['namespace'].pop()+'::'+result['name'] + namespace = '::'.join(result['namespace']) + return result + + def _createNode( self, tag, **kwargs ): + result = self.boostbook.createElement(tag) + for k in kwargs.keys(): + if k == 'id': + result.setAttribute('id',kwargs[k]) + else: + result.setAttribute(k,kwargs[k]) + return result + + def _createText( self, tag, data, **kwargs ): + result = self._createNode(tag,**kwargs) + data = data.strip() + if len(data) > 0: + result.appendChild(self.boostbook.createTextNode(data)) + return result + + +def main( xmldir=None, output=None, id=None, title=None, index=False ): + #~ print '--- main: xmldir = %s, output = %s' % (xmldir,output) + + input = glob.glob( os.path.abspath( os.path.join( xmldir, "*.xml" ) ) ) + input.sort + translator = Doxygen2BoostBook(id=id, title=title, index=index) + #~ Feed in the namespaces first to build up the set of namespaces + #~ and definitions so that lookup is unambiguous when reading in the definitions. + namespace_files = filter( + lambda x: + os.path.basename(x).startswith('namespace_'), + input) + decl_files = filter( + lambda x: + not os.path.basename(x).startswith('namespace_') and not os.path.basename(x).startswith('_'), + input) + for dox in namespace_files: + #~ print '--|',os.path.basename(dox) + translator.addDox(xml.dom.minidom.parse(dox)) + for dox in decl_files: + #~ print '--|',os.path.basename(dox) + translator.addDox(xml.dom.minidom.parse(dox)) + + if output: + output = open(output,'w') + else: + output = sys.stdout + if output: + output.write(translator.tostring()) + + +main( **get_args() ) diff --git a/src/tools/doxygen.jam b/src/tools/doxygen.jam index 52898d1a8..efb3b5c8a 100644 --- a/src/tools/doxygen.jam +++ b/src/tools/doxygen.jam @@ -33,6 +33,8 @@ import project ; import xsltproc ; import make ; import os ; +import toolset : flags ; +import alias ; # Use to specify extra configuration paramters. These get translated # into a doxyfile which configures the building of the docs. @@ -44,17 +46,29 @@ feature.feature prefix : : free ; # Specify the "boost.doxygen.reftitle" XSLT option. feature.feature reftitle : : free ; +# Which processor to use for various translations from Doxygen. +feature.feature doxygen:processor : xsltproc doxproc : implicit ; + +# To generate, or not, index sections. +feature.feature doxygen:doxproc:index : no yes : incidental ; + +# The ID for the resulting BoostBook reference section. +feature.feature doxygen:doxproc:id : : free ; + +# The title for the resulting BoostBook reference section. +feature.feature doxygen:doxproc:title : : free ; + # Doxygen configuration input file. type.register DOXYFILE : doxyfile ; # Doxygen XML multi-file output. -type.register DOXYGEN_XML_MULTIFILE : : XML ; +type.register DOXYGEN_XML_MULTIFILE : xml-dir : XML ; # Doxygen XML coallesed output. type.register DOXYGEN_XML : doxygen : XML ; # Doxygen HTML multifile directory. -type.register DOXYGEN_HTML_MULTIFILE : dir : HTML ; +type.register DOXYGEN_HTML_MULTIFILE : html-dir : HTML ; # Redirection HTML file to HTML multifile directory. type.register DOXYGEN_HTML : : HTML ; @@ -125,13 +139,24 @@ rule init ( name ? ) .doxygen = $(name) ; } .doxygen ?= doxygen ; + + .doxproc = [ modules.binding $(__name__) ] ; + .doxproc = $(.doxproc:D)/doxproc.py ; - generators.register-composing doxygen.headers-to-doxyfile : H HPP CPP : DOXYFILE ; - generators.register-standard doxygen.run : DOXYFILE : DOXYGEN_XML_MULTIFILE ; - generators.register-standard doxygen.xml-to-boostbook : DOXYGEN_XML : BOOSTBOOK ; - generators.register-standard doxygen.collect : DOXYGEN_XML_MULTIFILE : DOXYGEN_XML ; - generators.register-standard doxygen.run : DOXYFILE : DOXYGEN_HTML_MULTIFILE ; - generators.register-standard doxygen.html-redirect : DOXYGEN_HTML_MULTIFILE : DOXYGEN_HTML ; + generators.register-composing doxygen.headers-to-doxyfile + : H HPP CPP : DOXYFILE ; + generators.register-standard doxygen.run + : DOXYFILE : DOXYGEN_XML_MULTIFILE ; + generators.register-standard doxygen.xml-dir-to-boostbook + : DOXYGEN_XML_MULTIFILE : BOOSTBOOK : doxproc ; + generators.register-standard doxygen.xml-to-boostbook + : DOXYGEN_XML : BOOSTBOOK : xsltproc ; + generators.register-standard doxygen.collect + : DOXYGEN_XML_MULTIFILE : DOXYGEN_XML ; + generators.register-standard doxygen.run + : DOXYFILE : DOXYGEN_HTML_MULTIFILE ; + generators.register-standard doxygen.html-redirect + : DOXYGEN_HTML_MULTIFILE : DOXYGEN_HTML ; IMPORT $(__name__) : doxygen : : doxygen ; } @@ -153,18 +178,36 @@ actions doxygen-action "$(NAME:E=doxygen)" $(>) && echo "Stamped" > "$(<)" } +# Runs the Python doxproc XML processor. +actions doxproc +{ + python "$(DOXPROC)" "--xmldir=$(>)" "--output=$(<)" "$(OPTIONS)" "--id=$(ID)" "--title=$(TITLE)" +} + # Generates a doxygen configuration file (doxyfile) given a set of C++ # sources and a property list that may contain # features. rule headers-to-doxyfile ( target : sources * : properties * ) { local text "# Generated by Boost.Build version 2" ; + + local output-dir ; # Translate into command line flags. for local param in [ feature.get-values : $(properties) ] { local namevalue = [ regex.match ([^=]*)=(.*) : $(param) ] ; text += "$(namevalue[1]) = $(namevalue[2])" ; + if $(namevalue[1]) = OUTPUT_DIRECTORY + { + output-dir = "$(namevalue[2])" ; + } + } + + if ! $(output-dir) + { + output-dir = [ on $(target) return $(LOCATE) ] ; + text += "OUTPUT_DIRECTORY = $(output-dir)" ; } local headers = "" ; @@ -248,6 +291,19 @@ rule xml-to-boostbook ( target : source : properties * ) xsltproc.xslt $(target) : $(source) $(d2b-xsl) : $(xslt-properties) ; } +flags doxygen.xml-dir-to-boostbook OPTIONS yes : --enable-index ; +flags doxygen.xml-dir-to-boostbook ID ; +flags doxygen.xml-dir-to-boostbook TITLE ; + +rule xml-dir-to-boostbook ( target : source : properties * ) +{ + DOXPROC on $(target) = $(.doxproc) ; + + LOCATE on $(source:S=) = [ on $(source) return $(LOCATE) ] ; + + doxygen.doxproc $(target) : $(source:S=) ; +} + # Generate the HTML redirect to HTML dir index.html file. rule html-redirect ( target : source : properties * ) { @@ -273,7 +329,7 @@ rule html-redirect ( target : source : properties * ) } # User-level rule to generate BoostBook XML from a set of headers via Doxygen. -rule doxygen ( target : sources * : requirements * : default-build * ) +rule doxygen ( target : sources * : requirements * : default-build * : usage-requirements * ) { local project = [ project.current ] ; @@ -325,24 +381,65 @@ rule doxygen ( target : sources * : requirements * : default-build * ) else { # Build a BoostBook XML file from the sources. - local doxyfile = [ - new typed-target $(target) : $(project) : BOOSTBOOK - : [ targets.main-target-sources $(sources) : $(target) ] + local location-xml = [ feature.get-values : $(requirements) ] ; + requirements = [ property.change $(requirements) : ] ; + local target-xml = $(target:B=$(target:B)-xml) ; + + ## The doxygen configuration file. + targets.main-target-alternative + [ new typed-target $(target-xml:S=.tag) : $(project) : DOXYFILE + : [ targets.main-target-sources $(sources) : $(target-xml:S=.tag) ] : [ targets.main-target-requirements $(requirements) GENERATE_HTML=NO GENERATE_XML=YES + XML_OUTPUT=$(target-xml) : $(project) ] : [ targets.main-target-default-build $(default-build) : $(project) ] ] ; - targets.main-target-alternative $(doxyfile) ; + $(project).mark-target-as-explicit $(target-xml:S=.tag) ; + + ## The Doxygen XML directory of the processed source files. + targets.main-target-alternative + [ new typed-target $(target-xml:S=.dir) : $(project) : DOXYGEN_XML_MULTIFILE + : $(target-xml:S=.tag) + : [ targets.main-target-requirements $(requirements) + : $(project) ] + : [ targets.main-target-default-build $(default-build) : $(project) ] + ] ; + $(project).mark-target-as-explicit $(target-xml:S=.dir) ; + + ## The resulting BoostBook file is generated by the processor tool. The + ## tool can be either the xsltproc plus accompanying XSL scripts. Or it + ## can be the python doxproc script. + targets.main-target-alternative + [ new typed-target $(target-xml) : $(project) : BOOSTBOOK + : $(target-xml:S=.dir) + : [ targets.main-target-requirements $(requirements) + : $(project) ] + : [ targets.main-target-default-build $(default-build) : $(project) ] + ] ; + $(project).mark-target-as-explicit $(target-xml) ; targets.main-target-alternative [ new install-target-class $(target:S=.xml) : $(project) - : [ $(doxyfile).name ] + : $(target-xml) : [ targets.main-target-requirements $(requirements) - . + $(location-xml:E=.) + $(target:S=.xml) : $(project) ] : [ targets.main-target-default-build $(default-build) : $(project) ] ] ; + $(project).mark-target-as-explicit $(target:S=.xml) ; + + targets.main-target-alternative + [ new alias-target-class $(target) : $(project) + : + : [ targets.main-target-requirements $(requirements) + : $(project) ] + : [ targets.main-target-default-build $(default-build) : $(project) ] + : [ targets.main-target-usage-requirements $(usage-requirements) + $(target:S=.xml) + : $(project) ] + ] ; } }