532 lines
14 KiB
Python
Executable File
532 lines
14 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
# ex:ts=4:sw=4:sts=4:et
|
|
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
|
|
#
|
|
# Copyright (C) 2005 Holger Hans Peter Freyther
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License version 2 as
|
|
# published by the Free Software Foundation.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License along
|
|
# with this program; if not, write to the Free Software Foundation, Inc.,
|
|
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
import optparse, os, sys
|
|
|
|
# bitbake
|
|
sys.path.append(os.path.join(os.path.dirname(os.path.dirname(__file__), 'lib'))
|
|
import bb
|
|
import bb.parse
|
|
from string import split, join
|
|
|
|
__version__ = "0.0.2"
|
|
|
|
class HTMLFormatter:
|
|
"""
|
|
Simple class to help to generate some sort of HTML files. It is
|
|
quite inferior solution compared to docbook, gtkdoc, doxygen but it
|
|
should work for now.
|
|
We've a global introduction site (index.html) and then one site for
|
|
the list of keys (alphabetical sorted) and one for the list of groups,
|
|
one site for each key with links to the relations and groups.
|
|
|
|
index.html
|
|
all_keys.html
|
|
all_groups.html
|
|
groupNAME.html
|
|
keyNAME.html
|
|
"""
|
|
|
|
def replace(self, text, *pairs):
|
|
"""
|
|
From pydoc... almost identical at least
|
|
"""
|
|
while pairs:
|
|
(a, b) = pairs[0]
|
|
text = join(split(text, a), b)
|
|
pairs = pairs[1:]
|
|
return text
|
|
def escape(self, text):
|
|
"""
|
|
Escape string to be conform HTML
|
|
"""
|
|
return self.replace(text,
|
|
('&', '&'),
|
|
('<', '<' ),
|
|
('>', '>' ) )
|
|
def createNavigator(self):
|
|
"""
|
|
Create the navgiator
|
|
"""
|
|
return """<table class="navigation" width="100%" summary="Navigation header" cellpadding="2" cellspacing="2">
|
|
<tr valign="middle">
|
|
<td><a accesskey="g" href="index.html">Home</a></td>
|
|
<td><a accesskey="n" href="all_groups.html">Groups</a></td>
|
|
<td><a accesskey="u" href="all_keys.html">Keys</a></td>
|
|
</tr></table>
|
|
"""
|
|
|
|
def relatedKeys(self, item):
|
|
"""
|
|
Create HTML to link to foreign keys
|
|
"""
|
|
|
|
if len(item.related()) == 0:
|
|
return ""
|
|
|
|
txt = "<p><b>See also:</b><br>"
|
|
txts = []
|
|
for it in item.related():
|
|
txts.append("""<a href="key%(it)s.html">%(it)s</a>""" % vars() )
|
|
|
|
return txt + ",".join(txts)
|
|
|
|
def groups(self, item):
|
|
"""
|
|
Create HTML to link to related groups
|
|
"""
|
|
|
|
if len(item.groups()) == 0:
|
|
return ""
|
|
|
|
|
|
txt = "<p><b>See also:</b><br>"
|
|
txts = []
|
|
for group in item.groups():
|
|
txts.append( """<a href="group%s.html">%s</a> """ % (group, group) )
|
|
|
|
return txt + ",".join(txts)
|
|
|
|
|
|
def createKeySite(self, item):
|
|
"""
|
|
Create a site for a key. It contains the header/navigator, a heading,
|
|
the description, links to related keys and to the groups.
|
|
"""
|
|
|
|
return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
<html><head><title>Key %s</title></head>
|
|
<link rel="stylesheet" href="style.css" type="text/css">
|
|
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
|
%s
|
|
<h2><span class="refentrytitle">%s</span></h2>
|
|
|
|
<div class="refsynopsisdiv">
|
|
<h2>Synopsis</h2>
|
|
<p>
|
|
%s
|
|
</p>
|
|
</div>
|
|
|
|
<div class="refsynopsisdiv">
|
|
<h2>Related Keys</h2>
|
|
<p>
|
|
%s
|
|
</p>
|
|
</div>
|
|
|
|
<div class="refsynopsisdiv">
|
|
<h2>Groups</h2>
|
|
<p>
|
|
%s
|
|
</p>
|
|
</div>
|
|
|
|
|
|
</body>
|
|
""" % (item.name(), self.createNavigator(), item.name(),
|
|
self.escape(item.description()), self.relatedKeys(item), self.groups(item))
|
|
|
|
def createGroupsSite(self, doc):
|
|
"""
|
|
Create the Group Overview site
|
|
"""
|
|
|
|
groups = ""
|
|
sorted_groups = sorted(doc.groups())
|
|
for group in sorted_groups:
|
|
groups += """<a href="group%s.html">%s</a><br>""" % (group, group)
|
|
|
|
return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
<html><head><title>Group overview</title></head>
|
|
<link rel="stylesheet" href="style.css" type="text/css">
|
|
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
|
%s
|
|
<h2>Available Groups</h2>
|
|
%s
|
|
</body>
|
|
""" % (self.createNavigator(), groups)
|
|
|
|
def createIndex(self):
|
|
"""
|
|
Create the index file
|
|
"""
|
|
|
|
return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
<html><head><title>Bitbake Documentation</title></head>
|
|
<link rel="stylesheet" href="style.css" type="text/css">
|
|
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
|
%s
|
|
<h2>Documentation Entrance</h2>
|
|
<a href="all_groups.html">All available groups</a><br>
|
|
<a href="all_keys.html">All available keys</a><br>
|
|
</body>
|
|
""" % self.createNavigator()
|
|
|
|
def createKeysSite(self, doc):
|
|
"""
|
|
Create Overview of all avilable keys
|
|
"""
|
|
keys = ""
|
|
sorted_keys = sorted(doc.doc_keys())
|
|
for key in sorted_keys:
|
|
keys += """<a href="key%s.html">%s</a><br>""" % (key, key)
|
|
|
|
return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
<html><head><title>Key overview</title></head>
|
|
<link rel="stylesheet" href="style.css" type="text/css">
|
|
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
|
%s
|
|
<h2>Available Keys</h2>
|
|
%s
|
|
</body>
|
|
""" % (self.createNavigator(), keys)
|
|
|
|
def createGroupSite(self, gr, items, _description = None):
|
|
"""
|
|
Create a site for a group:
|
|
Group the name of the group, items contain the name of the keys
|
|
inside this group
|
|
"""
|
|
groups = ""
|
|
description = ""
|
|
|
|
# create a section with the group descriptions
|
|
if _description:
|
|
description += "<h2 Description of Grozp %s</h2>" % gr
|
|
description += _description
|
|
|
|
items.sort(lambda x, y:cmp(x.name(), y.name()))
|
|
for group in items:
|
|
groups += """<a href="key%s.html">%s</a><br>""" % (group.name(), group.name())
|
|
|
|
return """<!doctype html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
|
|
<html><head><title>Group %s</title></head>
|
|
<link rel="stylesheet" href="style.css" type="text/css">
|
|
<body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF">
|
|
%s
|
|
%s
|
|
<div class="refsynopsisdiv">
|
|
<h2>Keys in Group %s</h2>
|
|
<pre class="synopsis">
|
|
%s
|
|
</pre>
|
|
</div>
|
|
</body>
|
|
""" % (gr, self.createNavigator(), description, gr, groups)
|
|
|
|
|
|
|
|
def createCSS(self):
|
|
"""
|
|
Create the CSS file
|
|
"""
|
|
return """.synopsis, .classsynopsis
|
|
{
|
|
background: #eeeeee;
|
|
border: solid 1px #aaaaaa;
|
|
padding: 0.5em;
|
|
}
|
|
.programlisting
|
|
{
|
|
background: #eeeeff;
|
|
border: solid 1px #aaaaff;
|
|
padding: 0.5em;
|
|
}
|
|
.variablelist
|
|
{
|
|
padding: 4px;
|
|
margin-left: 3em;
|
|
}
|
|
.variablelist td:first-child
|
|
{
|
|
vertical-align: top;
|
|
}
|
|
table.navigation
|
|
{
|
|
background: #ffeeee;
|
|
border: solid 1px #ffaaaa;
|
|
margin-top: 0.5em;
|
|
margin-bottom: 0.5em;
|
|
}
|
|
.navigation a
|
|
{
|
|
color: #770000;
|
|
}
|
|
.navigation a:visited
|
|
{
|
|
color: #550000;
|
|
}
|
|
.navigation .title
|
|
{
|
|
font-size: 200%;
|
|
}
|
|
div.refnamediv
|
|
{
|
|
margin-top: 2em;
|
|
}
|
|
div.gallery-float
|
|
{
|
|
float: left;
|
|
padding: 10px;
|
|
}
|
|
div.gallery-float img
|
|
{
|
|
border-style: none;
|
|
}
|
|
div.gallery-spacer
|
|
{
|
|
clear: both;
|
|
}
|
|
a
|
|
{
|
|
text-decoration: none;
|
|
}
|
|
a:hover
|
|
{
|
|
text-decoration: underline;
|
|
color: #FF0000;
|
|
}
|
|
"""
|
|
|
|
|
|
|
|
class DocumentationItem:
|
|
"""
|
|
A class to hold information about a configuration
|
|
item. It contains the key name, description, a list of related names,
|
|
and the group this item is contained in.
|
|
"""
|
|
|
|
def __init__(self):
|
|
self._groups = []
|
|
self._related = []
|
|
self._name = ""
|
|
self._desc = ""
|
|
|
|
def groups(self):
|
|
return self._groups
|
|
|
|
def name(self):
|
|
return self._name
|
|
|
|
def description(self):
|
|
return self._desc
|
|
|
|
def related(self):
|
|
return self._related
|
|
|
|
def setName(self, name):
|
|
self._name = name
|
|
|
|
def setDescription(self, desc):
|
|
self._desc = desc
|
|
|
|
def addGroup(self, group):
|
|
self._groups.append(group)
|
|
|
|
def addRelation(self, relation):
|
|
self._related.append(relation)
|
|
|
|
def sort(self):
|
|
self._related.sort()
|
|
self._groups.sort()
|
|
|
|
|
|
class Documentation:
|
|
"""
|
|
Holds the documentation... with mappings from key to items...
|
|
"""
|
|
|
|
def __init__(self):
|
|
self.__keys = {}
|
|
self.__groups = {}
|
|
|
|
def insert_doc_item(self, item):
|
|
"""
|
|
Insert the Doc Item into the internal list
|
|
of representation
|
|
"""
|
|
item.sort()
|
|
self.__keys[item.name()] = item
|
|
|
|
for group in item.groups():
|
|
if not group in self.__groups:
|
|
self.__groups[group] = []
|
|
self.__groups[group].append(item)
|
|
self.__groups[group].sort()
|
|
|
|
|
|
def doc_item(self, key):
|
|
"""
|
|
Return the DocumentationInstance describing the key
|
|
"""
|
|
try:
|
|
return self.__keys[key]
|
|
except KeyError:
|
|
return None
|
|
|
|
def doc_keys(self):
|
|
"""
|
|
Return the documented KEYS (names)
|
|
"""
|
|
return self.__keys.keys()
|
|
|
|
def groups(self):
|
|
"""
|
|
Return the names of available groups
|
|
"""
|
|
return self.__groups.keys()
|
|
|
|
def group_content(self, group_name):
|
|
"""
|
|
Return a list of keys/names that are in a specefic
|
|
group or the empty list
|
|
"""
|
|
try:
|
|
return self.__groups[group_name]
|
|
except KeyError:
|
|
return []
|
|
|
|
|
|
def parse_cmdline(args):
|
|
"""
|
|
Parse the CMD line and return the result as a n-tuple
|
|
"""
|
|
|
|
parser = optparse.OptionParser( version = "Bitbake Documentation Tool Core version %s, %%prog version %s" % (bb.__version__, __version__))
|
|
usage = """%prog [options]
|
|
|
|
Create a set of html pages (documentation) for a bitbake.conf....
|
|
"""
|
|
|
|
# Add the needed options
|
|
parser.add_option( "-c", "--config", help = "Use the specified configuration file as source",
|
|
action = "store", dest = "config", default = os.path.join("conf", "documentation.conf") )
|
|
|
|
parser.add_option( "-o", "--output", help = "Output directory for html files",
|
|
action = "store", dest = "output", default = "html/" )
|
|
|
|
parser.add_option( "-D", "--debug", help = "Increase the debug level",
|
|
action = "count", dest = "debug", default = 0 )
|
|
|
|
parser.add_option( "-v", "--verbose", help = "output more chit-char to the terminal",
|
|
action = "store_true", dest = "verbose", default = False )
|
|
|
|
options, args = parser.parse_args( sys.argv )
|
|
|
|
bb.msg.init_msgconfig(options.verbose, options.debug)
|
|
|
|
return options.config, options.output
|
|
|
|
def main():
|
|
"""
|
|
The main Method
|
|
"""
|
|
|
|
(config_file, output_dir) = parse_cmdline( sys.argv )
|
|
|
|
# right to let us load the file now
|
|
try:
|
|
documentation = bb.parse.handle( config_file, bb.data.init() )
|
|
except IOError:
|
|
bb.fatal( "Unable to open %s" % config_file )
|
|
except bb.parse.ParseError:
|
|
bb.fatal( "Unable to parse %s" % config_file )
|
|
|
|
if isinstance(documentation, dict):
|
|
documentation = documentation[""]
|
|
|
|
# Assuming we've the file loaded now, we will initialize the 'tree'
|
|
doc = Documentation()
|
|
|
|
# defined states
|
|
state_begin = 0
|
|
state_see = 1
|
|
state_group = 2
|
|
|
|
for key in bb.data.keys(documentation):
|
|
data = documentation.getVarFlag(key, "doc", False)
|
|
if not data:
|
|
continue
|
|
|
|
# The Documentation now starts
|
|
doc_ins = DocumentationItem()
|
|
doc_ins.setName(key)
|
|
|
|
|
|
tokens = data.split(' ')
|
|
state = state_begin
|
|
string= ""
|
|
for token in tokens:
|
|
token = token.strip(',')
|
|
|
|
if not state == state_see and token == "@see":
|
|
state = state_see
|
|
continue
|
|
elif not state == state_group and token == "@group":
|
|
state = state_group
|
|
continue
|
|
|
|
if state == state_begin:
|
|
string += " %s" % token
|
|
elif state == state_see:
|
|
doc_ins.addRelation(token)
|
|
elif state == state_group:
|
|
doc_ins.addGroup(token)
|
|
|
|
# set the description
|
|
doc_ins.setDescription(string)
|
|
doc.insert_doc_item(doc_ins)
|
|
|
|
# let us create the HTML now
|
|
bb.utils.mkdirhier(output_dir)
|
|
os.chdir(output_dir)
|
|
|
|
# Let us create the sites now. We do it in the following order
|
|
# Start with the index.html. It will point to sites explaining all
|
|
# keys and groups
|
|
html_slave = HTMLFormatter()
|
|
|
|
f = file('style.css', 'w')
|
|
print >> f, html_slave.createCSS()
|
|
|
|
f = file('index.html', 'w')
|
|
print >> f, html_slave.createIndex()
|
|
|
|
f = file('all_groups.html', 'w')
|
|
print >> f, html_slave.createGroupsSite(doc)
|
|
|
|
f = file('all_keys.html', 'w')
|
|
print >> f, html_slave.createKeysSite(doc)
|
|
|
|
# now for each group create the site
|
|
for group in doc.groups():
|
|
f = file('group%s.html' % group, 'w')
|
|
print >> f, html_slave.createGroupSite(group, doc.group_content(group))
|
|
|
|
# now for the keys
|
|
for key in doc.doc_keys():
|
|
f = file('key%s.html' % doc.doc_item(key).name(), 'w')
|
|
print >> f, html_slave.createKeySite(doc.doc_item(key))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|