bitbake/bin/bitbake:
[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
373
374     def doc_item(self, key):
375         """
376         Return the DocumentationInstance describing the key
377         """
378         try:
379             return self.__keys[key]
380         except KeyError:
381             return None
382
383     def doc_keys(self):
384         """
385         Return the documented KEYS (names)
386         """
387         return self.__keys.keys()
388
389     def groups(self):
390         """
391         Return the names of available groups
392         """
393         return self.__groups.keys()
394
395     def group_content(self,group_name):
396         """
397         Return a list of keys/names that are in a specefic
398         group or the empty list
399         """
400         try:
401             return self.__groups[group_name]
402         except KeyError:
403             return []
404
405
406 def parse_cmdline(args):
407     """
408     Parse the CMD line and return the result as a n-tuple
409     """
410
411     parser = optparse.OptionParser( version = "Bitbake Documentation Tool Core version %s, %%prog version %s" % (bb.__version__,__version__))
412     usage  = """%prog [options]
413
414 Create a set of html pages (documentation) for a bitbake.conf....
415 """
416
417     # Add the needed options
418     parser.add_option( "-c", "--config", help = "Use the specified configuration file as source",
419                        action = "store", dest = "config", default = os.path.join("conf", "documentation.conf") )
420
421     parser.add_option( "-o", "--output", help = "Output directory for html files",
422                        action = "store", dest = "output", default = "html/" )
423
424     parser.add_option( "-D",  "--debug", help = "Increase the debug level",
425                        action = "count", dest = "debug", default = 0 )
426
427     parser.add_option( "-v","--verbose", help = "output more chit-char to the terminal",
428                        action = "store_true", dest = "verbose", default = False )
429
430     options, args = parser.parse_args( sys.argv )
431
432     if options.debug:
433         bb.debug_level = options.debug
434
435     return options.config, options.output
436
437 def main():
438     """
439     The main Method
440     """
441
442     (config_file,output_dir) = parse_cmdline( sys.argv )
443
444     # right to let us load the file now
445     try:
446         documentation = bb.parse.handle( config_file, bb.data.init() )
447     except IOError:
448         bb.fatal( "Unable to open %s" % config_file )
449     except bb.parse.ParseError:
450         bb.fatal( "Unable to parse %s" % config_file )
451
452
453     # Assuming we've the file loaded now, we will initialize the 'tree'
454     doc = Documentation()
455
456     # defined states
457     state_begin = 0
458     state_see   = 1
459     state_group = 2
460
461     for key in bb.data.keys(documentation):
462         data   = bb.data.getVarFlag(key, "doc", documentation)
463         if not data:
464             continue
465
466         # The Documentation now starts
467         doc_ins = DocumentationItem()
468         doc_ins.setName(key)
469
470
471         tokens = data.split(' ')
472         state = state_begin
473         string= ""
474         for token in tokens:
475             token = token.strip(',')
476
477             if not state == state_see and token == "@see":
478                 state = state_see
479                 continue
480             elif not state == state_group and token  == "@group":
481                 state = state_group
482                 continue
483
484             if state == state_begin:
485                 string += " %s" % token
486             elif state == state_see:
487                 doc_ins.addRelation(token)
488             elif state == state_group:
489                 doc_ins.addGroup(token)
490
491         # set the description
492         doc_ins.setDescription(string)
493         doc.insert_doc_item(doc_ins)
494
495     # let us create the HTML now
496     bb.mkdirhier(output_dir)
497     os.chdir(output_dir)
498
499     # Let us create the sites now. We do it in the following order
500     # Start with the index.html. It will point to sites explaining all
501     # keys and groups
502     html_slave = HTMLFormatter()
503
504     f = file('style.css', 'w')
505     print >> f, html_slave.createCSS()
506
507     f = file('index.html', 'w')
508     print >> f, html_slave.createIndex()
509
510     f = file('groups.html', 'w')
511     print >> f, html_slave.createGroupsSite(doc)
512
513     f = file('keys.html', 'w')
514     print >> f, html_slave.createKeysSite(doc)
515
516     # now for each group create the site
517     for group in doc.groups():
518         f = file('group%s.html' % group, 'w')
519         print >> f, html_slave.createGroupSite(group, doc.group_content(group))
520
521     # now for the keys
522     for key in doc.doc_keys():
523         f = file('key%s.html' % doc.doc_item(key).name(), 'w')
524         print >> f, html_slave.createKeySite(doc.doc_item(key))
525
526
527 if __name__ == "__main__":
528     main()