blob: 27446788243270d9fcf8180f52b7a85cc41c915b [file] [log] [blame]
Patrick Williamsc0f7c042017-02-23 20:41:17 -06001#!/usr/bin/env python3
Patrick Williamsc124f4f2015-09-15 14:41:29 -05002# ex:ts=4:sw=4:sts=4:et
3# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
4#
5# Copyright (C) 2005 Holger Hans Peter Freyther
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License version 2 as
9# published by the Free Software Foundation.
10#
11# This program is distributed in the hope that it will be useful,
12# but WITHOUT ANY WARRANTY; without even the implied warranty of
13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14# GNU General Public License for more details.
15#
16# You should have received a copy of the GNU General Public License along
17# with this program; if not, write to the Free Software Foundation, Inc.,
18# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
20import optparse, os, sys
21
22# bitbake
23sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__), 'lib'))
24import bb
25import bb.parse
26from string import split, join
27
28__version__ = "0.0.2"
29
30class HTMLFormatter:
31 """
32 Simple class to help to generate some sort of HTML files. It is
33 quite inferior solution compared to docbook, gtkdoc, doxygen but it
34 should work for now.
35 We've a global introduction site (index.html) and then one site for
36 the list of keys (alphabetical sorted) and one for the list of groups,
37 one site for each key with links to the relations and groups.
38
39 index.html
40 all_keys.html
41 all_groups.html
42 groupNAME.html
43 keyNAME.html
44 """
45
46 def replace(self, text, *pairs):
47 """
48 From pydoc... almost identical at least
49 """
50 while pairs:
51 (a, b) = pairs[0]
52 text = join(split(text, a), b)
53 pairs = pairs[1:]
54 return text
55 def escape(self, text):
56 """
57 Escape string to be conform HTML
58 """
59 return self.replace(text,
60 ('&', '&'),
61 ('<', '&lt;' ),
62 ('>', '&gt;' ) )
63 def createNavigator(self):
64 """
65 Create the navgiator
66 """
67 return """<table class="navigation" width="100%" summary="Navigation header" cellpadding="2" cellspacing="2">
68<tr valign="middle">
69<td><a accesskey="g" href="index.html">Home</a></td>
70<td><a accesskey="n" href="all_groups.html">Groups</a></td>
71<td><a accesskey="u" href="all_keys.html">Keys</a></td>
72</tr></table>
73"""
74
75 def relatedKeys(self, item):
76 """
77 Create HTML to link to foreign keys
78 """
79
80 if len(item.related()) == 0:
81 return ""
82
83 txt = "<p><b>See also:</b><br>"
84 txts = []
85 for it in item.related():
86 txts.append("""<a href="key%(it)s.html">%(it)s</a>""" % vars() )
87
88 return txt + ",".join(txts)
89
90 def groups(self, item):
91 """
92 Create HTML to link to related groups
93 """
94
95 if len(item.groups()) == 0:
96 return ""
97
98
99 txt = "<p><b>See also:</b><br>"
100 txts = []
101 for group in item.groups():
102 txts.append( """<a href="group%s.html">%s</a> """ % (group, group) )
103
104 return txt + ",".join(txts)
105
106
107 def createKeySite(self, item):
108 """
109 Create a site for a key. It contains the header/navigator, a heading,
110 the description, links to related keys and to the groups.
111 """
112
113 return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
114<html><head><title>Key %s</title></head>
115<link rel="stylesheet" href="style.css" type="text/css">
116<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
117%s
118<h2><span class="refentrytitle">%s</span></h2>
119
120<div class="refsynopsisdiv">
121<h2>Synopsis</h2>
122<p>
123%s
124</p>
125</div>
126
127<div class="refsynopsisdiv">
128<h2>Related Keys</h2>
129<p>
130%s
131</p>
132</div>
133
134<div class="refsynopsisdiv">
135<h2>Groups</h2>
136<p>
137%s
138</p>
139</div>
140
141
142</body>
143""" % (item.name(), self.createNavigator(), item.name(),
144 self.escape(item.description()), self.relatedKeys(item), self.groups(item))
145
146 def createGroupsSite(self, doc):
147 """
148 Create the Group Overview site
149 """
150
151 groups = ""
152 sorted_groups = sorted(doc.groups())
153 for group in sorted_groups:
154 groups += """<a href="group%s.html">%s</a><br>""" % (group, group)
155
156 return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
157<html><head><title>Group overview</title></head>
158<link rel="stylesheet" href="style.css" type="text/css">
159<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
160%s
161<h2>Available Groups</h2>
162%s
163</body>
164""" % (self.createNavigator(), groups)
165
166 def createIndex(self):
167 """
168 Create the index file
169 """
170
171 return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
172<html><head><title>Bitbake Documentation</title></head>
173<link rel="stylesheet" href="style.css" type="text/css">
174<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
175%s
176<h2>Documentation Entrance</h2>
177<a href="all_groups.html">All available groups</a><br>
178<a href="all_keys.html">All available keys</a><br>
179</body>
180""" % self.createNavigator()
181
182 def createKeysSite(self, doc):
183 """
184 Create Overview of all avilable keys
185 """
186 keys = ""
187 sorted_keys = sorted(doc.doc_keys())
188 for key in sorted_keys:
189 keys += """<a href="key%s.html">%s</a><br>""" % (key, key)
190
191 return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
192<html><head><title>Key overview</title></head>
193<link rel="stylesheet" href="style.css" type="text/css">
194<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
195%s
196<h2>Available Keys</h2>
197%s
198</body>
199""" % (self.createNavigator(), keys)
200
201 def createGroupSite(self, gr, items, _description = None):
202 """
203 Create a site for a group:
204 Group the name of the group, items contain the name of the keys
205 inside this group
206 """
207 groups = ""
208 description = ""
209
210 # create a section with the group descriptions
211 if _description:
212 description += "<h2 Description of Grozp %s</h2>" % gr
213 description += _description
214
215 items.sort(lambda x, y:cmp(x.name(), y.name()))
216 for group in items:
217 groups += """<a href="key%s.html">%s</a><br>""" % (group.name(), group.name())
218
219 return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
220<html><head><title>Group %s</title></head>
221<link rel="stylesheet" href="style.css" type="text/css">
222<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
223%s
224%s
225<div class="refsynopsisdiv">
226<h2>Keys in Group %s</h2>
227<pre class="synopsis">
228%s
229</pre>
230</div>
231</body>
232""" % (gr, self.createNavigator(), description, gr, groups)
233
234
235
236 def createCSS(self):
237 """
238 Create the CSS file
239 """
240 return """.synopsis, .classsynopsis
241{
242 background: #eeeeee;
243 border: solid 1px #aaaaaa;
244 padding: 0.5em;
245}
246.programlisting
247{
248 background: #eeeeff;
249 border: solid 1px #aaaaff;
250 padding: 0.5em;
251}
252.variablelist
253{
254 padding: 4px;
255 margin-left: 3em;
256}
257.variablelist td:first-child
258{
259 vertical-align: top;
260}
261table.navigation
262{
263 background: #ffeeee;
264 border: solid 1px #ffaaaa;
265 margin-top: 0.5em;
266 margin-bottom: 0.5em;
267}
268.navigation a
269{
270 color: #770000;
271}
272.navigation a:visited
273{
274 color: #550000;
275}
276.navigation .title
277{
278 font-size: 200%;
279}
280div.refnamediv
281{
282 margin-top: 2em;
283}
284div.gallery-float
285{
286 float: left;
287 padding: 10px;
288}
289div.gallery-float img
290{
291 border-style: none;
292}
293div.gallery-spacer
294{
295 clear: both;
296}
297a
298{
299 text-decoration: none;
300}
301a:hover
302{
303 text-decoration: underline;
304 color: #FF0000;
305}
306"""
307
308
309
310class DocumentationItem:
311 """
312 A class to hold information about a configuration
313 item. It contains the key name, description, a list of related names,
314 and the group this item is contained in.
315 """
316
317 def __init__(self):
318 self._groups = []
319 self._related = []
320 self._name = ""
321 self._desc = ""
322
323 def groups(self):
324 return self._groups
325
326 def name(self):
327 return self._name
328
329 def description(self):
330 return self._desc
331
332 def related(self):
333 return self._related
334
335 def setName(self, name):
336 self._name = name
337
338 def setDescription(self, desc):
339 self._desc = desc
340
341 def addGroup(self, group):
342 self._groups.append(group)
343
344 def addRelation(self, relation):
345 self._related.append(relation)
346
347 def sort(self):
348 self._related.sort()
349 self._groups.sort()
350
351
352class Documentation:
353 """
354 Holds the documentation... with mappings from key to items...
355 """
356
357 def __init__(self):
358 self.__keys = {}
359 self.__groups = {}
360
361 def insert_doc_item(self, item):
362 """
363 Insert the Doc Item into the internal list
364 of representation
365 """
366 item.sort()
367 self.__keys[item.name()] = item
368
369 for group in item.groups():
370 if not group in self.__groups:
371 self.__groups[group] = []
372 self.__groups[group].append(item)
373 self.__groups[group].sort()
374
375
376 def doc_item(self, key):
377 """
378 Return the DocumentationInstance describing the key
379 """
380 try:
381 return self.__keys[key]
382 except KeyError:
383 return None
384
385 def doc_keys(self):
386 """
387 Return the documented KEYS (names)
388 """
389 return self.__keys.keys()
390
391 def groups(self):
392 """
393 Return the names of available groups
394 """
395 return self.__groups.keys()
396
397 def group_content(self, group_name):
398 """
399 Return a list of keys/names that are in a specefic
400 group or the empty list
401 """
402 try:
403 return self.__groups[group_name]
404 except KeyError:
405 return []
406
407
408def parse_cmdline(args):
409 """
410 Parse the CMD line and return the result as a n-tuple
411 """
412
413 parser = optparse.OptionParser( version = "Bitbake Documentation Tool Core version %s, %%prog version %s" % (bb.__version__, __version__))
414 usage = """%prog [options]
415
416Create a set of html pages (documentation) for a bitbake.conf....
417"""
418
419 # Add the needed options
420 parser.add_option( "-c", "--config", help = "Use the specified configuration file as source",
421 action = "store", dest = "config", default = os.path.join("conf", "documentation.conf") )
422
423 parser.add_option( "-o", "--output", help = "Output directory for html files",
424 action = "store", dest = "output", default = "html/" )
425
426 parser.add_option( "-D", "--debug", help = "Increase the debug level",
427 action = "count", dest = "debug", default = 0 )
428
429 parser.add_option( "-v", "--verbose", help = "output more chit-char to the terminal",
430 action = "store_true", dest = "verbose", default = False )
431
432 options, args = parser.parse_args( sys.argv )
433
434 bb.msg.init_msgconfig(options.verbose, options.debug)
435
436 return options.config, options.output
437
438def main():
439 """
440 The main Method
441 """
442
443 (config_file, output_dir) = parse_cmdline( sys.argv )
444
445 # right to let us load the file now
446 try:
447 documentation = bb.parse.handle( config_file, bb.data.init() )
448 except IOError:
449 bb.fatal( "Unable to open %s" % config_file )
450 except bb.parse.ParseError:
451 bb.fatal( "Unable to parse %s" % config_file )
452
453 if isinstance(documentation, dict):
454 documentation = documentation[""]
455
456 # Assuming we've the file loaded now, we will initialize the 'tree'
457 doc = Documentation()
458
459 # defined states
460 state_begin = 0
461 state_see = 1
462 state_group = 2
463
464 for key in bb.data.keys(documentation):
Patrick Williamsd8c66bc2016-06-20 12:57:21 -0500465 data = documentation.getVarFlag(key, "doc", False)
Patrick Williamsc124f4f2015-09-15 14:41:29 -0500466 if not data:
467 continue
468
469 # The Documentation now starts
470 doc_ins = DocumentationItem()
471 doc_ins.setName(key)
472
473
474 tokens = data.split(' ')
475 state = state_begin
476 string= ""
477 for token in tokens:
478 token = token.strip(',')
479
480 if not state == state_see and token == "@see":
481 state = state_see
482 continue
483 elif not state == state_group and token == "@group":
484 state = state_group
485 continue
486
487 if state == state_begin:
488 string += " %s" % token
489 elif state == state_see:
490 doc_ins.addRelation(token)
491 elif state == state_group:
492 doc_ins.addGroup(token)
493
494 # set the description
495 doc_ins.setDescription(string)
496 doc.insert_doc_item(doc_ins)
497
498 # let us create the HTML now
499 bb.utils.mkdirhier(output_dir)
500 os.chdir(output_dir)
501
502 # Let us create the sites now. We do it in the following order
503 # Start with the index.html. It will point to sites explaining all
504 # keys and groups
505 html_slave = HTMLFormatter()
506
507 f = file('style.css', 'w')
508 print >> f, html_slave.createCSS()
509
510 f = file('index.html', 'w')
511 print >> f, html_slave.createIndex()
512
513 f = file('all_groups.html', 'w')
514 print >> f, html_slave.createGroupsSite(doc)
515
516 f = file('all_keys.html', 'w')
517 print >> f, html_slave.createKeysSite(doc)
518
519 # now for each group create the site
520 for group in doc.groups():
521 f = file('group%s.html' % group, 'w')
522 print >> f, html_slave.createGroupSite(group, doc.group_content(group))
523
524 # now for the keys
525 for key in doc.doc_keys():
526 f = file('key%s.html' % doc.doc_item(key).name(), 'w')
527 print >> f, html_slave.createKeySite(doc.doc_item(key))
528
529
530if __name__ == "__main__":
531 main()