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