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