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