bitbake/bin/bitdoc:
[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 from   bb import make
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         keys.html
49         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="groups.html">Groups</a></td>
79 <td><a accesskey="u" href="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         for it in item.related():
93             txt += """<a href="key%s.html">%s</a>, """ % (it, it)
94
95         return txt
96
97     def groups(self,item):
98         """
99         Create HTML to link to related groups
100         """
101
102         if len(item.groups()) == 0:
103             return ""
104
105
106         txt = "<p><b>Seel also:</b><br>"
107         for group in item.groups():
108             txt += """<a href="group%s.html">%s</a>, """ % (group,group)
109
110         return txt
111
112
113     def createKeySite(self,item):
114         """
115         Create a site for a key. It contains the header/navigator, a heading,
116         the description, links to related keys and to the groups.
117         """
118
119         return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
120 <html><head><title>Key %s</title></head>
121 <link rel="stylesheet" href="style.css" type="text/css">
122 <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
123 %s
124 <h2><span class="refentrytitle">%s</span></h2>
125
126 <div class="refsynopsisdiv">
127 <h2>Synopsis</h2>
128 <pre class="synopsis">
129 %s
130 </pre>
131 </div>
132
133 <div class="refsynopsisdiv">
134 <h2>Related Keys</h2>
135 <pre class="synopsis">
136 %s
137 </pre>
138 </div>
139
140 <div class="refsynopsisdiv">
141 <h2>Groups</h2>
142 <pre class="synopsis">
143 %s
144 </pre>
145 </div>
146
147
148 </body>
149 """     % (item.name(), self.createNavigator(), item.name(), 
150            self.escape(item.description()), self.relatedKeys(item), self.groups(item))
151
152     def createGroupsSite(self, doc):
153         """
154         Create the Group Overview site
155         """
156
157         groups = ""
158         sorted_groups = doc.groups()
159         sorted_groups.sort()
160         for group in sorted_groups:
161             groups += """<a href="group%s.html">%s</a><br>""" % (group, group)
162
163         return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
164 <html><head><title>Group overview</title></head>
165 <link rel="stylesheet" href="style.css" type="text/css">
166 <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
167 %s
168 <h2>Available Groups</h2>
169 %s
170 </body>
171 """ % (self.createNavigator(), groups)
172
173     def createIndex(self):
174         """
175         Create the index file
176         """
177
178         return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
179 <html><head><title>Bitbake Documentation</title></head>
180 <link rel="stylesheet" href="style.css" type="text/css">
181 <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
182 %s
183 <h2>Documentation Entrance</h2>
184 <a href="groups.html">All available groups</a><br>
185 <a href="keys.html">All available keys</a><br>
186 </body>
187 """ % self.createNavigator()
188
189     def createKeysSite(self, doc):
190         """
191         Create Overview of all avilable keys
192         """
193         keys = ""
194         sorted_keys = doc.doc_keys()
195         sorted_keys.sort()
196         for key in sorted_keys:
197             keys += """<a href="key%s.html">%s</a><br>""" % (key, key)
198
199         return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
200 <html><head><title>Key overview</title></head>
201 <link rel="stylesheet" href="style.css" type="text/css">
202 <body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
203 %s
204 <h2>Available Keys</h2>
205 %s
206 </body>
207 """ % (self.createNavigator(), keys)
208
209     def createGroupSite(self,gr, items):
210         """
211         Create a site for a group:
212         Group the name of the group, items contain the name of the keys
213         inside this group
214         """
215         groups = ""
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 <div class="refsynopsisdiv">
225 <h2>Keys in Group %s</h2>
226 <pre class="synopsis">
227 %s
228 </pre>
229 </div>
230 </body>
231 """ % (gr, self.createNavigator(), gr, groups)
232
233
234
235     def createCSS(self):
236         """
237         Create the CSS file
238         """
239         return """.synopsis, .classsynopsis
240 {
241   background: #eeeeee;
242   border: solid 1px #aaaaaa;
243   padding: 0.5em;
244 }
245 .programlisting
246 {
247   background: #eeeeff;
248   border: solid 1px #aaaaff;
249   padding: 0.5em;
250 }
251 .variablelist
252 {
253   padding: 4px;
254   margin-left: 3em;
255 }
256 .variablelist td:first-child
257 {
258   vertical-align: top;
259 }
260 table.navigation
261 {
262   background: #ffeeee;
263   border: solid 1px #ffaaaa;
264   margin-top: 0.5em;
265   margin-bottom: 0.5em;
266 }
267 .navigation a
268 {
269   color: #770000;
270 }
271 .navigation a:visited
272 {
273   color: #550000;
274 }
275 .navigation .title
276 {
277   font-size: 200%;
278 }
279 div.refnamediv
280 {
281   margin-top: 2em;
282 }
283 div.gallery-float
284 {
285   float: left;
286   padding: 10px;
287 }
288 div.gallery-float img
289 {
290   border-style: none;
291 }
292 div.gallery-spacer
293 {
294   clear: both;
295 }
296 a
297 {
298   text-decoration: none;
299 }
300 a:hover
301 {
302   text-decoration: underline;
303   color: #FF0000;
304 }
305 """
306
307
308
309 class DocumentationItem:
310     """
311     A class to hold information about a configuration
312     item. It contains the key name, description, a list of related names,
313     and the group this item is contained in.
314     """
315
316     def __init__(self):
317         self._groups  = []
318         self._related = []
319         self._name    = ""
320         self._desc    = ""
321
322     def groups(self):
323         return self._groups
324
325     def name(self):
326         return self._name
327
328     def description(self):
329         return self._desc
330
331     def related(self):
332         return self._related
333
334     def setName(self, name):
335         self._name = name
336
337     def setDescription(self, desc):
338         self._desc = desc
339
340     def addGroup(self, group):
341         self._groups.append(group)
342
343     def addRelation(self,relation):
344         self._related.append(relation)
345
346     def sort(self):
347         self._related.sort()
348         self._groups.sort()
349
350
351 class Documentation:
352     """
353     Holds the documentation... with mappings from key to items...
354     """
355
356     def __init__(self):
357         self.__keys   = {}
358         self.__groups = {}
359
360     def insert_doc_item(self, item):
361         """
362         Insert the Doc Item into the internal list
363         of representation
364         """
365         item.sort()
366         self.__keys[item.name()] = item
367
368         for group in item.groups():
369             if not group in self.__groups:
370                 self.__groups[group] = []
371             self.__groups[group].append(item)
372             self.__groups[group].sort()
373
374
375     def doc_item(self, key):
376         """
377         Return the DocumentationInstance describing the key
378         """
379         try:
380             return self.__keys[key]
381         except KeyError:
382             return None
383
384     def doc_keys(self):
385         """
386         Return the documented KEYS (names)
387         """
388         return self.__keys.keys()
389
390     def groups(self):
391         """
392         Return the names of available groups
393         """
394         return self.__groups.keys()
395
396     def group_content(self,group_name):
397         """
398         Return a list of keys/names that are in a specefic
399         group or the empty list
400         """
401         try:
402             return self.__groups[group_name]
403         except KeyError:
404             return []
405
406
407 def parse_cmdline(args):
408     """
409     Parse the CMD line and return the result as a n-tuple
410     """
411
412     parser = optparse.OptionParser( version = "Bitbake Documentation Tool Core version %s, %%prog version %s" % (bb.__version__,__version__))
413     usage  = """%prog [options]
414
415 Create a set of html pages (documentation) for a bitbake.conf....
416 """
417
418     # Add the needed options
419     parser.add_option( "-c", "--config", help = "Use the specified configuration file as source",
420                        action = "store", dest = "config", default = os.path.join("conf", "documentation.conf") )
421
422     parser.add_option( "-o", "--output", help = "Output directory for html files",
423                        action = "store", dest = "output", default = "html/" )
424
425     parser.add_option( "-D",  "--debug", help = "Increase the debug level",
426                        action = "count", dest = "debug", default = 0 )
427
428     parser.add_option( "-v","--verbose", help = "output more chit-char to the terminal",
429                        action = "store_true", dest = "verbose", default = False )
430
431     options, args = parser.parse_args( sys.argv )
432
433     if options.debug:
434         bb.debug_level = options.debug
435
436     return options.config, options.output
437
438 def 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
454     # Assuming we've the file loaded now, we will initialize the 'tree'
455     doc = Documentation()
456
457     # defined states
458     state_begin = 0
459     state_see   = 1
460     state_group = 2
461
462     for key in bb.data.keys(documentation):
463         data   = bb.data.getVarFlag(key, "doc", documentation)
464         if not data:
465             continue
466
467         # The Documentation now starts
468         doc_ins = DocumentationItem()
469         doc_ins.setName(key)
470
471
472         tokens = data.split(' ')
473         state = state_begin
474         string= ""
475         for token in tokens:
476             token = token.strip(',')
477
478             if not state == state_see and token == "@see":
479                 state = state_see
480                 continue
481             elif not state == state_group and token  == "@group":
482                 state = state_group
483                 continue
484
485             if state == state_begin:
486                 string += " %s" % token
487             elif state == state_see:
488                 doc_ins.addRelation(token)
489             elif state == state_group:
490                 doc_ins.addGroup(token)
491
492         # set the description
493         doc_ins.setDescription(string)
494         doc.insert_doc_item(doc_ins)
495
496     # let us create the HTML now
497     bb.mkdirhier(output_dir)
498     os.chdir(output_dir)
499
500     # Let us create the sites now. We do it in the following order
501     # Start with the index.html. It will point to sites explaining all
502     # keys and groups
503     html_slave = HTMLFormatter()
504
505     f = file('style.css', 'w')
506     print >> f, html_slave.createCSS()
507
508     f = file('index.html', 'w')
509     print >> f, html_slave.createIndex()
510
511     f = file('groups.html', 'w')
512     print >> f, html_slave.createGroupsSite(doc)
513
514     f = file('keys.html', 'w')
515     print >> f, html_slave.createKeysSite(doc)
516
517     # now for each group create the site
518     for group in doc.groups():
519         f = file('group%s.html' % group, 'w')
520         print >> f, html_slave.createGroupSite(group, doc.group_content(group))
521
522     # now for the keys
523     for key in doc.doc_keys():
524         f = file('key%s.html' % doc.doc_item(key).name(), 'w')
525         print >> f, html_slave.createKeySite(doc.doc_item(key))
526
527
528 if __name__ == "__main__":
529     main()