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