2 # -*- python; coding: utf-8 -*-
4 # gtk-doc - GTK DocBook documentation generator.
5 # Copyright (C) 2018 Stefan Sauer
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 """Prototype for builtin docbook processing
24 The tool loads the main xml document (<module>-docs.xml) and chunks it
25 like the xsl-stylesheets would do. For that it resolves all the xml-includes.
26 Each chunk is converted to htnml using python functions.
28 In contrast to our previous approach of running gtkdoc-mkhtml + gtkdoc-fixxref,
29 this tools will replace both without relying on external tools such as xsltproc
33 - more chunk converters
34 - check each docbook tag if it can contain #PCDATA, if not don't check for
37 - as a step, we could run FixHTMLFile() on each output file
38 - integrate syntax-highlighing from fixxref
39 - maybe handle the combination <informalexample><programlisting> directly
40 - switch to http://pygments.org/docs/quickstart/?
41 - integrate MakeXRef from fixxref
42 - create devhelp2 output
45 - minify html: https://pypi.python.org/pypi/htmlmin/
48 sudo pip3 install anytree lxml
51 ./gtkdoc-mkhtml2 tests/gobject/docs/tester-docs.xml
52 ll tests/gobject/docs/db2html
54 ./gtkdoc-mkhtml2 tests/bugs/docs/tester-docs.xml
55 ll tests/bugs/docs/db2html
56 cp tests/bugs/docs/html/*.{css,png} tests/bugs/docs/db2html/
57 xdg-open tests/bugs/docs/db2html/index.html
58 meld tests/bugs/docs/{html,db2html}
61 (cd tests/bugs/docs/; rm html-build.stamp; time make html-build.stamp)
70 from anytree
import Node
, PreOrderIter
71 from lxml
import etree
73 from .fixxref
import NoLinks
75 # http://www.sagehill.net/docbookxsl/Chunking.html
79 'bibliography', # in article or book
83 'glossary', # in article or book
84 'index', # in article or book
89 'sect1', # except first
90 'section', # if equivalent to sect1
96 class ChunkParams(object):
97 def __init__(self
, prefix
, parent
=None):
103 # TODO: look up the abbrevs and hierarchy for other tags
104 # http://www.sagehill.net/docbookxsl/Chunking.html#GeneratedFilenames
105 # https://github.com/oreillymedia/HTMLBook/blob/master/htmlbook-xsl/chunk.xsl#L33
107 'appendix': ChunkParams('app', 'book'),
108 'book': ChunkParams('bk'),
109 'chapter': ChunkParams('ch', 'book'),
110 'index': ChunkParams('ix', 'book'),
111 'part': ChunkParams('pt', 'book'),
112 'sect1': ChunkParams('s', 'chapter'),
113 'section': ChunkParams('s', 'chapter'),
117 '_': (etree
.XPath('./title'), None),
118 'book': (etree
.XPath('./bookinfo/title'), None),
120 etree
.XPath('./refmeta/refentrytitle'),
121 etree
.XPath('./refnamediv/refpurpose')
126 def gen_chunk_name(node
):
127 if 'id' in node
.attrib
:
128 return node
.attrib
['id']
131 if tag
not in CHUNK_PARAMS
:
132 CHUNK_PARAMS
[tag
] = ChunkParams(node
.tag
[:2])
133 logging
.warning('Add CHUNK_PARAMS for "%s"', tag
)
135 naming
= CHUNK_PARAMS
[tag
]
137 name
= ('%s%02d' % (naming
.prefix
, naming
.count
))
138 # handle parents to make names of nested tags unique
139 # TODO: we only need to prepend the parent if there are > 1 of them in the
141 # while naming.parent:
142 # parent = naming.parent
143 # if parent not in CHUNK_PARAMS:
145 # naming = CHUNK_PARAMS[parent]
146 # name = ('%s%02d' % (naming.prefix, naming.count)) + name
150 def get_chunk_titles(node
):
152 if tag
not in TITLE_XPATHS
:
154 (title
, subtitle
) = TITLE_XPATHS
['_']
156 (title
, subtitle
) = TITLE_XPATHS
[tag
]
162 if xml
.tag
!= 'title':
163 result
['title_tag'] = xml
.tag
165 result
['title_tag'] = tag
168 xml
= subtitle(node
)[0]
169 result
['subtitle'] = xml
.text
170 result
['subtitle_tag'] = xml
.tag
172 result
['subtitle'] = None
173 result
['subtitle_tag'] = None
177 def chunk(xml_node
, parent
=None):
180 The first time, we're called with parent=None and in that case we return
181 the new_node as the root of the tree
183 # print('<%s %s>' % (xml_node.tag, xml_node.attrib))
184 if xml_node
.tag
in CHUNK_TAGS
:
185 # TODO: do we need to remove the xml-node from the parent?
187 # from copy import deepcopy
188 # sub_tree = deepcopy(xml_node)
189 # xml_node.getparent().remove(xml_node)
191 # sub_tree = etree.ElementTree(xml_node).getroot()
192 title_args
= get_chunk_titles(xml_node
)
193 parent
= Node(xml_node
.tag
, parent
=parent
, xml
=xml_node
,
194 filename
=gen_chunk_name(xml_node
) + '.html',
196 for child
in xml_node
:
204 def escape_entities(text
):
205 return text
.replace('&', '&').replace('<', '<').replace('>', '>')
208 def convert_inner(ctx
, xml
, result
):
210 result
.extend(convert_tags
.get(child
.tag
, convert__unknown
)(ctx
, child
))
213 def convert_ignore(ctx
, xml
):
215 convert_inner(ctx
, xml
, result
)
219 def convert_skip(ctx
, xml
):
226 def convert__unknown(ctx
, xml
):
227 # don't recurse on subchunks
228 if xml
.tag
in CHUNK_TAGS
:
231 if xml
.tag
not in missing_tags
:
232 logging
.warning('Add tag converter for "%s"', xml
.tag
)
233 missing_tags
[xml
.tag
] = True
234 result
= ['<!-- ' + xml
.tag
+ '-->\n']
235 convert_inner(ctx
, xml
, result
)
236 result
.append('<!-- /' + xml
.tag
+ '-->\n')
240 def convert_refsect(ctx
, xml
, h_tag
, inner_func
=convert_inner
):
241 result
= ['<div class="%s">\n' % xml
.tag
]
242 title
= xml
.find('title')
243 if title
is not None:
244 if 'id' in xml
.attrib
:
245 result
.append('<a name="%s"></a>' % xml
.attrib
['id'])
246 result
.append('<%s>%s</%s>' % (h_tag
, title
.text
, h_tag
))
249 result
.append(xml
.text
)
250 inner_func(ctx
, xml
, result
)
251 result
.append('</div>')
253 result
.append(xml
.tail
)
257 def xml_get_title(xml
):
258 title
= xml
.find('title')
259 if title
is not None:
262 # TODO(ensonic): any way to get the file (inlcudes) too?
263 logging
.warning('%s: Expected title tag under "%s"', xml
.sourceline
, xml
.tag
)
269 def convert_bookinfo(ctx
, xml
):
270 result
= ['<div class="titlepage">']
271 for releaseinfo
in xml
.findall('releaseinfo'):
272 result
.extend(convert_para(ctx
, releaseinfo
))
273 result
.append("""<hr>
276 result
.append(xml
.tail
)
280 def convert_colspec(ctx
, xml
):
284 result
.append(' class="%s"' % a
['colname'])
286 result
.append(' width="%s"' % a
['colwidth'])
288 # is in tgroup and there can be no 'text'
292 def convert_div(ctx
, xml
):
293 result
= ['<div class="%s">\n' % xml
.tag
]
295 result
.append(xml
.text
)
296 convert_inner(ctx
, xml
, result
)
297 result
.append('</div>')
299 result
.append(xml
.tail
)
303 def convert_em_class(ctx
, xml
):
304 result
= ['<em class="%s"><code>' % xml
.tag
]
306 result
.append(xml
.text
)
307 convert_inner(ctx
, xml
, result
)
308 result
.append('</code></em>')
310 result
.append(xml
.tail
)
314 def convert_entry(ctx
, xml
):
316 if 'role' in xml
.attrib
:
317 result
.append(' class="%s">' % xml
.attrib
['role'])
321 result
.append(xml
.text
)
322 convert_inner(ctx
, xml
, result
)
323 result
.append('</td>')
325 result
.append(xml
.tail
)
329 def convert_indexdiv(ctx
, xml
):
330 title_tag
= xml
.find('title')
331 title
= title_tag
.text
332 xml
.remove(title_tag
)
334 '<a name="idx%s"></a><h3 class="title">%s</h3>' % (title
, title
)
336 convert_inner(ctx
, xml
, result
)
340 def convert_informaltable(ctx
, xml
):
341 result
= ['<div class="informaltable"><table class="informaltable"']
343 if 'pgwide' in a
and a
['pgwide'] == '1':
344 result
.append(' width="100%"')
345 if 'frame' in a
and a
['frame'] == 'none':
346 result
.append(' border="0"')
348 convert_inner(ctx
, xml
, result
)
349 result
.append('</table></div>')
351 result
.append(xml
.tail
)
355 def convert_itemizedlist(ctx
, xml
):
356 result
= ['<div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; ">']
357 convert_inner(ctx
, xml
, result
)
358 result
.append('</ul></div>')
360 result
.append(xml
.tail
)
364 def convert_link(ctx
, xml
):
365 # TODO: inline more fixxref functionality
366 # TODO: need to build an 'id' map and resolve against internal links too
367 linkend
= xml
.attrib
['linkend']
368 if linkend
in NoLinks
:
372 result
= ['<!-- GTKDOCLINK HREF="%s" -->' % linkend
]
374 result
.append(xml
.text
)
375 convert_inner(ctx
, xml
, result
)
377 result
.append('<!-- /GTKDOCLINK -->')
379 result
.append(xml
.tail
)
383 def convert_listitem(ctx
, xml
):
384 result
= ['<li class="listitem">']
385 convert_inner(ctx
, xml
, result
)
386 result
.append('</li>')
387 # is in itemizedlist and there can be no 'text'
391 def convert_literal(ctx
, xml
):
392 result
= ['<code class="%s">' % xml
.tag
]
394 result
.append(xml
.text
)
395 convert_inner(ctx
, xml
, result
)
396 result
.append('</code>')
398 result
.append(xml
.tail
)
402 def convert_para(ctx
, xml
):
404 if xml
.tag
!= 'para':
405 result
= ['<p class="%s">' % xml
.tag
]
407 result
.append(xml
.text
)
408 convert_inner(ctx
, xml
, result
)
409 result
.append('</p>')
411 result
.append(xml
.tail
)
415 def convert_phrase(ctx
, xml
):
417 if 'role' in xml
.attrib
:
418 result
.append(' class="%s">' % xml
.attrib
['role'])
422 result
.append(xml
.text
)
423 convert_inner(ctx
, xml
, result
)
424 result
.append('</span>')
426 result
.append(xml
.tail
)
430 def convert_primaryie(ctx
, xml
):
432 convert_inner(ctx
, xml
, result
)
433 result
.append('</dt>\n<dd></dd>\n')
437 def convert_programlisting(ctx
, xml
):
438 result
= ['<pre class="programlisting">']
440 result
.append(escape_entities(xml
.text
))
441 convert_inner(ctx
, xml
, result
)
442 result
.append('</pre>')
444 result
.append(xml
.tail
)
448 def convert_refsect1(ctx
, xml
):
449 # Add a divider between two consequitive refsect2
450 def convert_inner(ctx
, xml
, result
):
453 if child
.tag
== 'refsect2' and prev
is not None and prev
.tag
== child
.tag
:
454 result
.append('<hr>\n')
455 result
.extend(convert_tags
.get(child
.tag
, convert__unknown
)(ctx
, child
))
457 return convert_refsect(ctx
, xml
, 'h2', convert_inner
)
460 def convert_refsect2(ctx
, xml
):
461 return convert_refsect(ctx
, xml
, 'h3')
464 def convert_refsect3(ctx
, xml
):
465 return convert_refsect(ctx
, xml
, 'h4')
468 def convert_row(ctx
, xml
):
470 convert_inner(ctx
, xml
, result
)
471 result
.append('</tr>\n')
475 def convert_span(ctx
, xml
):
476 result
= ['<span class="%s">' % xml
.tag
]
478 result
.append(xml
.text
)
479 convert_inner(ctx
, xml
, result
)
480 result
.append('</span>')
482 result
.append(xml
.tail
)
486 def convert_tbody(ctx
, xml
):
488 convert_inner(ctx
, xml
, result
)
489 result
.append('</tbody>')
490 # is in tgroup and there can be no 'text'
494 def convert_tgroup(ctx
, xml
):
495 # tgroup does not expand to anything, but the nested colspecs need to
496 # be put into a colgroup
497 cols
= xml
.findall('colspec')
500 result
.append('<colgroup>\n')
502 result
.extend(convert_colspec(ctx
, col
))
504 result
.append('</colgroup>\n')
505 convert_inner(ctx
, xml
, result
)
506 # is in informaltable and there can be no 'text'
510 def convert_ulink(ctx
, xml
):
511 result
= ['<a class="%s" href="%s">%s</a>' % (xml
.tag
, xml
.attrib
['url'], xml
.text
)]
513 result
.append(xml
.tail
)
517 # TODO(ensonic): turn into class with converters as functions and ctx as self
519 'bookinfo': convert_bookinfo
,
520 'colspec': convert_colspec
,
521 'entry': convert_entry
,
522 'function': convert_span
,
523 'indexdiv': convert_indexdiv
,
524 'indexentry': convert_ignore
,
525 'indexterm': convert_skip
,
526 'informalexample': convert_div
,
527 'informaltable': convert_informaltable
,
528 'itemizedlist': convert_itemizedlist
,
529 'link': convert_link
,
530 'listitem': convert_listitem
,
531 'literal': convert_literal
,
532 'para': convert_para
,
533 'parameter': convert_em_class
,
534 'phrase': convert_phrase
,
535 'primaryie': convert_primaryie
,
536 'programlisting': convert_programlisting
,
537 'releaseinfo': convert_para
,
538 'refsect1': convert_refsect1
,
539 'refsect2': convert_refsect2
,
540 'refsect3': convert_refsect3
,
541 'returnvalue': convert_span
,
543 'structfield': convert_em_class
,
544 'tbody': convert_tbody
,
545 'tgroup': convert_tgroup
,
546 'type': convert_span
,
547 'ulink': convert_ulink
,
548 'warning': convert_div
,
553 HTML_HEADER
= """<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
556 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
558 %s<link rel="stylesheet" href="style.css" type="text/css">
560 <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
564 def generate_head_links(ctx
):
567 '<link rel="home" href="%s" title="%s">\n' % (n
.filename
, n
.title
)
571 result
.append('<link rel="up" href="%s" title="%s">\n' % (n
.filename
, n
.title
))
572 if 'nav_prev' in ctx
:
574 result
.append('<link rel="prev" href="%s" title="%s">\n' % (n
.filename
, n
.title
))
575 if 'nav_next' in ctx
:
577 result
.append('<link rel="next" href="%s" title="%s">\n' % (n
.filename
, n
.title
))
578 return ''.join(result
)
581 def generate_nav_links(ctx
):
584 '<td><a accesskey="h" href="%s"><img src="home.png" width="16" height="16" border="0" alt="Home"></a></td>' % n
.filename
589 '<td><a accesskey="u" href="%s"><img src="up.png" width="16" height="16" border="0" alt="Up"></a></td>' % n
.filename
)
591 result
.append('<td><img src="up-insensitive.png" width="16" height="16" border="0"></td>')
592 if 'nav_prev' in ctx
:
595 '<td><a accesskey="p" href="%s"><img src="left.png" width="16" height="16" border="0" alt="Prev"></a></td>' % n
.filename
)
597 result
.append('<td><img src="left-insensitive.png" width="16" height="16" border="0"></td>')
598 if 'nav_next' in ctx
:
601 '<td><a accesskey="n" href="%s"><img src="right.png" width="16" height="16" border="0" alt="Next"></a></td>' % n
.filename
)
603 result
.append('<td><img src="right-insensitive.png" width="16" height="16" border="0"></td>')
605 return ''.join(result
)
608 def generate_toc(ctx
, node
):
610 for c
in node
.children
:
611 # TODO: urlencode the filename: urllib.parse.quote_plus()
612 result
.append('<dt><span class="%s"><a href="%s">%s</a></span>\n' % (
613 c
.title_tag
, c
.filename
, c
.title
))
615 result
.append('<span class="%s"> — %s</span>' % (c
.subtitle_tag
, c
.subtitle
))
616 result
.append('</dt>\n')
618 result
.append('<dd><dl>')
619 result
.extend(generate_toc(ctx
, c
))
620 result
.append('</dl></dd>')
624 def generate_basic_nav(ctx
):
625 return """<table class="navigation" id="top" width="100%%" cellpadding="2" cellspacing="5">
627 <td width="100%%" align="left" class="shortcuts"></td>
631 """ % generate_nav_links(ctx
)
634 def generate_index_nav(ctx
, indexdivs
):
637 title
= xml_get_title(s
)
638 ix_nav
.append('<a class="shortcut" href="#idx%s">%s</a>' % (title
, title
))
640 return """<table class="navigation" id="top" width="100%%" cellpadding="2" cellspacing="5">
642 <td width="100%%" align="left" class="shortcuts">
643 <span id="nav_index">
650 """ % ('\n<span class="dim">|</span>\n'.join(ix_nav
), generate_nav_links(ctx
))
653 def generate_refentry_nav(ctx
, refsect1s
, result
):
654 result
.append("""<table class="navigation" id="top" width="100%%" cellpadding="2" cellspacing="5">
656 <td width="100%%" align="left" class="shortcuts">
657 <a href="#" class="shortcut">Top</a>""")
660 # don't list TOC sections (role="xxx_proto")
661 if s
.attrib
.get('role', '').endswith("_proto"):
664 title
= xml_get_title(s
)
666 <span id="nav_description">
667 <span class="dim">|</span>
668 <a href="#%s" class="shortcut">%s</a>
669 </span>""" % (s
.attrib
['id'], title
))
675 """ % generate_nav_links(ctx
))
680 node_id
= xml
.attrib
.get('id', None)
684 logging
.warning('%d: No "id" attribute on "%s"', xml
.sourceline
, xml
.tag
)
686 # Generate the 'id'. We need to walk up the xml-tree and check the positions
688 parent
= xml
.getparent()
689 while parent
is not None:
690 children
= parent
.getchildren()
691 ix
.insert(0, str(children
.index(xml
) + 1))
693 parent
= xml
.getparent()
694 # logging.warning('%s: id indexes: %s', node.filename, str(ix))
695 return 'id-1.' + '.'.join(ix
)
701 def convert_book(ctx
):
704 HTML_HEADER
% (node
.title
, generate_head_links(ctx
)),
705 """<table class="navigation" id="top" width="100%%" cellpadding="2" cellspacing="0">
706 <tr><th valign="middle"><p class="title">%s</p></th></tr>
711 bookinfo
= node
.xml
.findall('bookinfo')[0]
712 result
.extend(convert_bookinfo(ctx
, bookinfo
))
713 result
.append("""<div class="toc">
716 result
.extend(generate_toc(ctx
, node
.root
))
717 result
.append("""</dl>
725 def convert_chapter(ctx
):
728 HTML_HEADER
% (node
.title
+ ": " + node
.root
.title
, generate_head_links(ctx
)),
729 generate_basic_nav(ctx
),
730 '<div class="chapter">',
732 title
= node
.xml
.find('title')
733 if title
is not None:
734 result
.append('<div class="titlepage"><h1 class="title"><a name="%s"></a>%s</h1></div>' % (
735 get_id(node
), title
.text
))
736 node
.xml
.remove(title
)
737 convert_inner(ctx
, node
.xml
, result
)
738 result
.append("""<div class="toc">
741 result
.extend(generate_toc(ctx
, node
))
742 result
.append("""</dl>
750 def convert_index(ctx
):
752 node_id
= get_id(node
)
753 # Get all indexdivs under indexdiv
754 indexdivs
= node
.xml
.find('indexdiv').findall('indexdiv')
757 HTML_HEADER
% (node
.title
+ ": " + node
.root
.title
, generate_head_links(ctx
)),
758 generate_index_nav(ctx
, indexdivs
),
759 """<div class="index">
760 <div class="titlepage"><h1 class="title">
761 <a name="%s"></a>%s</h1>
762 </div>""" % (node_id
, node
.title
)
765 result
.extend(convert_indexdiv(ctx
, i
))
766 result
.append("""</div>
772 def convert_refentry(ctx
):
774 node_id
= get_id(node
)
775 refsect1s
= node
.xml
.findall('refsect1')
778 HTML_HEADER
% (node
.title
+ ": " + node
.root
.title
, generate_head_links(ctx
))
780 generate_refentry_nav(ctx
, refsect1s
, result
)
782 <div class="refentry">
784 <div class="refnamediv">
785 <table width="100%%"><tr>
787 <h2><span class="refentrytitle"><a name="%s.top_of_page"></a>%s</span></h2>
788 <p>%s — module for gtk-doc unit test</p>
790 <td class="gallery_image" valign="top" align="right"></td>
793 """ % (node_id
, node_id
, node
.title
, node
.title
))
796 result
.extend(convert_refsect1(ctx
, s
))
797 result
.append("""</div>
803 # TODO(ensonic): turn into class with converters as functions and ctx as self
805 'book': convert_book
,
806 'chapter': convert_chapter
,
807 'index': convert_index
,
808 'refentry': convert_refentry
,
812 def generate_nav_nodes(files
, node
):
814 'nav_home': node
.root
,
816 # nav params: up, prev, next
818 nav
['nav_up'] = node
.parent
819 ix
= files
.index(node
)
821 nav
['nav_prev'] = files
[ix
- 1]
822 if ix
< len(files
) - 1:
823 nav
['nav_next'] = files
[ix
+ 1]
827 def convert(out_dir
, files
, node
):
828 """Convert the docbook chunks to a html file.
831 out_dir: already created output dir
832 files: list of nodes in the tree in pre-order
833 node: current tree node
836 logging
.info('Writing: %s', node
.filename
)
837 with
open(os
.path
.join(out_dir
, node
.filename
), 'wt') as html
:
842 ctx
.update(generate_nav_nodes(files
, node
))
844 if node
.name
in convert_chunks
:
845 for line
in convert_chunks
[node
.name
](ctx
):
848 logging
.warning('Add converter/template for "%s"', node
.name
)
851 def create_devhelp2(out_dir
, module
, xml
):
852 with
open(os
.path
.join(out_dir
, module
+ '.devhelp2'), 'wt') as idx
:
853 bookinfo_nodes
= xml
.xpath('/book/bookinfo')
855 if bookinfo_nodes
is not None:
856 bookinfo
= bookinfo_nodes
[0]
857 title
= bookinfo
.xpath('./title/text()')[0]
858 online_url
= bookinfo
.xpath('./releaseinfo/ulink[@role="online-location"]/@url')[0]
859 # TODO: support author too (see devhelp2.xsl)
860 # TODO: fixxref uses '--src-lang' to set the language
862 """<?xml version="1.0" encoding="utf-8" standalone="no"?>
863 <book xmlns="http://www.devhelp.net/book" title="%s" link="index.html" author="" name="%s" version="2" language="c" online="%s">
865 """ % (title
, module
, online_url
)
867 # TODO: toc under 'chapter'
868 result
.append(""" </chapters>
871 # TODO: keywords under 'functions' from all refsect2 and refsect3
873 result
.append(""" </functions>
880 def main(module
, index_file
):
881 tree
= etree
.parse(index_file
)
884 dir_name
= os
.path
.dirname(index_file
)
886 # for testing: dump to output file
887 # out_file = os.path.join(dir_name, 'db2html.xml')
888 # tree.write(out_file)
890 # TODO: rename to 'html' later on
891 # - right now in mkhtml, the dir is created by the Makefile and mkhtml
892 # outputs into the working directory
893 out_dir
= os
.path
.join(dir_name
, 'db2html')
897 if e
.errno
!= errno
.EEXIST
:
900 # We do multiple passes:
901 # 1) recursively walk the tree and chunk it into a python tree so that we
902 # can generate navigation and link tags.
903 # TODO: also collect all 'id' attributes on the way and build map of
904 # id:rel-link (in fixxref it is called Links[])
905 files
= chunk(tree
.getroot())
906 # 2) iterate the tree and output files
907 # TODO: use multiprocessing
908 files
= list(PreOrderIter(files
))
910 convert(out_dir
, files
, node
)
911 # 3) create a xxx.devhelp2 file
912 create_devhelp2(out_dir
, module
, tree
.getroot())
916 logging
.info('options: %s', str(options
.__dict
__))
917 module
= options
.args
[0]
918 document
= options
.args
[1]
919 sys
.exit(main(module
, document
))