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