Commit FS#9462: an semi-automatic plugin API documentation generator

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@18722 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Maurus Cuelenaere 2008-10-06 22:19:54 +00:00
parent 34148b9a21
commit 20fb47ec77
7 changed files with 3330 additions and 0 deletions

2564
docs/PLUGIN_API.new Normal file

File diff suppressed because it is too large Load diff

29
utils/rockbox_api/README Normal file
View file

@ -0,0 +1,29 @@
==============================
generate.php
==============================
php generate.php > output.txt
--------------
Generates output.txt of apps/plugin.h
==============================
update.php
==============================
php update.php input_file.txt > output.txt
--------------
Updates input_file.txt with newer values of apps/plugin.h
==============================
gen_html.php
==============================
php gen_html.php input_file.txt
--------------
Generates HTML output of input_file.txt in output/*.html
Known issues:
* [F[function]] doesn't work with functions outside of the current .html

337
utils/rockbox_api/functions.php Executable file
View file

@ -0,0 +1,337 @@
<?
$svn = "http://svn.rockbox.org/viewvc.cgi/trunk/";
$wiki = "http://www.rockbox.org/wiki/";
function func_sort($a, $b)
{
$a = preg_replace('/^((unsigned|const|struct|enum) [^ ]*|[a-z0-9 \*_]*) [\*]?/i', '', $a);
$b = preg_replace('/^((unsigned|const|struct|enum) [^ ]*|[a-z0-9 \*_]*) [\*]?/i', '', $b);
return strnatcasecmp($a, $b);
}
function get_newest()
{
global $svn;
$text = file_get_contents("../../apps/plugin.h");
$text = str_replace(array("\r\n", "\r"), "\n", $text);
/* Located plugin_api struct */
foreach(explode("\n", $text) as $line_nr => $line)
{
if(trim($line) == "struct plugin_api {")
{
$text = explode("\n", $text);
$text = array_slice($text, $line_nr+1);
break;
}
}
foreach($text as $line_nr => $line)
{
if(trim($line) == "};")
{
$text = array_slice($text, 0, $line_nr-1);
break;
}
}
/* Locating done */
/* Clean up stuff a bit .. */
for($i=0; $i<count($text); $i++)
$text[$i] = trim($text[$i]);
/* Fake preprocesser */
$ret = array();
$_groups = array();
$conditions = array();
$strip_next = 0;
$group = "";
for($i=0; $i<count($text); $i++)
{
$tmp = trim($text[$i]);
if(substr($tmp, 0, 1) == '#')
{
$tmp = trim(substr($tmp, 1));
if(strtolower(substr($tmp, 0, 2)) == "if")
{
if(strtolower(substr($tmp, 2, 3)) == "def")
$conditions[] = "defined(".substr($tmp, 6).")";
else if(strtolower(substr($tmp, 2, 4)) == "ndef")
$conditions[] = "!defined(".substr($tmp, 7).")";
else
{
while(substr($tmp, strlen($tmp)-1, 1) == "\\")
{
$i++;
$tmp = substr($tmp, 0, strlen($tmp)-1)." ".trim($text[$i]);
}
$conditions[] = substr($tmp, 3);
}
}
else if(strtolower(substr($tmp, 0, 4)) == "elif")
{
while(substr($tmp, strlen($tmp)-1, 1) == "\\")
{
$i++;
$tmp = substr($tmp, 0, strlen($tmp)-1)." ".trim($text[$i]);
}
$conditions[count($conditions)-1] = substr($tmp, 5);
}
else if(strtolower(substr($tmp, 0, 4)) == "else")
$conditions[count($conditions)-1] = "!( ".$conditions[count($conditions)-1]." )";
else if(strtolower(substr($tmp, 0, 5)) == "endif")
array_pop($conditions);
}
else if(strlen($tmp) == 0)
$group = "";
else if(substr($tmp, 0, 2) == "/*")
{
while(strpos($tmp, "*/") === false)
{
$i++;
$tmp .= " ".trim($text[$i]);
}
$group = explode("/*", trim($tmp));
$group = explode("*/", $group[1]);
$group = trim($group[0]);
}
else
{
while(strpos($tmp, ";") === false)
{
$i++;
$tmp .= " ".trim($text[$i]);
}
/* Replace those (*func)(int args) with func(int args) */
$tmp = preg_replace('/\(\*([^\)]*)\)/i', '\1', $tmp, 1);
$tmp = substr($tmp, 0, strlen($tmp)-1);
$ret[$tmp] = array("func" => $tmp, "cond" => "(".implode(") && (", $conditions).")", "group" => $group);
}
}
uksort($ret, "func_sort");
return $ret;
}
function parse_documentation($data)
{
$data = explode("\n", $data);
$ret = array();
$cur_func = "";
foreach($data as $line)
{
if(substr($line, 0, 1) == "#")
continue;
else if(substr($line, 0, 4) == " ")
{
$tmp = trim($line);
if(strpos($tmp, " ") !== false)
$tmp = array(substr($tmp, 1, strpos($tmp, " ")-1), substr($tmp, strpos($tmp, " ")) );
else
$tmp = array(substr($tmp, 1), "");
$ret[$cur_func][$tmp[0]][] = $tmp[1];
}
else if(strlen($line) == 0)
continue;
else
$cur_func = substr($line, 0);
}
$_ret = array();
foreach($ret as $func => $el)
{
if(isset($el["group"]))
$group = trim($el["group"][0]);
else
$group = "misc";
$_ret[$group][$func] = $el;
}
return $_ret;
}
function get_func($func)
{
$func = preg_replace('/^((unsigned|const|struct|enum) [^ ]*|[a-z0-9 \*_]*) [\*]?/i', '', $func);
if(strpos($func, "PREFIX") !== false)
$func = substr($func, 0, strrpos($func, "("));
else if(strpos($func, "(") !== false)
$func = substr($func, 0, strpos($func, "("));
return $func;
}
function get_args($func)
{
/* Check if this _is_ a function */
if(strpos($func, "(") === false)
return array();
/* Get rid of return value */
$func = preg_replace('/^((unsigned|const|struct|enum) [^ ]*|[a-z0-9 \*_]*) [\*]?/i', '', $func);
/* Get rid of function name */
if(strpos($func, "(") !== false)
$func = substr($func, strpos($func, "("));
/* Get rid of ATTRIBUTE_PRINTF */
if(strpos($func, "ATTRIBUTE_PRINTF") !== false)
$func = substr($func, 0, strpos($func, "ATTRIBUTE_PRINTF"));
$level = 0;
$args = array();
$buffer = "";
for($i=0; $i<strlen($func); $i++)
{
switch($func{$i})
{
case "(":
$level++;
if($level > 1)
$buffer .= "(";
break;
case ")":
$level--;
if($level > 0)
{
$buffer .= ")";
break;
}
case ",":
if($level <= 1)
{
if(strpos($buffer, "(,") !== false)
{
$tmp = array();
preg_match_all('/[^ ]*, [^)]*\)/', $buffer, $tmp);
$tmp = $tmp[0];
foreach($tmp as $el)
{
if(strlen($el) > 0)
$args[] = trim($el);
}
$tmp = preg_replace('/[^ ]*, [^)]*\)/', '', $buffer);
$args[] = trim($tmp);
}
else
$args[] = trim($buffer);
$buffer = "";
}
else
$buffer .= ",";
break;
default:
$buffer .= $func{$i};
break;
}
}
/* Filter out void */
for($i=0; $i<count($args); $i++)
{
if($args[$i] == "void")
unset($args[$i]);
}
return $args;
}
function get_return($func)
{
$ret = array();
preg_match('/^((unsigned|const|struct|enum) [^ ]*|[a-z0-9 \*_]*) [\*]?/i', $func, $ret);
if(trim($ret[0]) == "void")
return false;
else
return trim($ret[0]);
}
function split_var($var)
{
if(strpos($var, "(,") !== false)
{
$p1 = substr($var, 0, strrpos($var, " "));
$p2 = substr($var, strrpos($var, " "));
$p2 = substr($p2, 0, strlen($p2)-1);
}
else if(strpos($var, "(*") !== false)
{
$p2 = array();
preg_match('/\(\*\w*\)/', $var, $p2);
$p2 = $p2[0];
$p1 = substr($var, strpos($var, $p2));
$p2 = substr($p2, 2, strlen($p2)-3);
}
else
{
$p1 = substr($var, 0, strrpos($var, " "));
$p2 = substr($var, strrpos($var, " "));
}
if(strpos($p2, "*") !== false)
{
for($i=0; $i<substr_count($p2, "*"); $i++)
$p1 .= "*";
$p2 = str_replace("*", "", $p2);
}
return array(trim($p1), trim($p2));
}
function _simplify($text)
{
$text = ereg_replace('\(!\( (.*)[ ]?\)\)', '!\1', $text);
$text = ereg_replace('\(\(([^ ])\)\)', '\1', $text);
return $text;
}
function clean_func($func)
{
$func = str_replace(array(" ", " "), " ", $func);
$func = str_replace(" ", " ", $func);
return $func;
}
function do_see_markup($data)
{
$ret = array();
foreach($data as $el)
{
$el = trim($el);
if(substr($el, 0, 1) != "[")
$ret[] = do_markup("[F[".$el."]]");
else
$ret[] = do_markup($el);
}
return implode(" &amp; ", $ret);
}
function do_markup($data)
{
global $svn, $wiki;
$data = ereg_replace('=([^=]*)=', '<code>\1</code>', $data);
$data = ereg_replace('\[W\[([^#\[]*)([^\[]*)\]\]', '<a href="'.$wiki.'\1\2">\1</a>', $data);
$data = ereg_replace('\[S\[([^\[]*)\]\]', '<a href="'.$svn.'\1?content-type=text%2Fplain">\1</a>', $data);
$data = ereg_replace('\[F\[([^\[]*)\]\]', '<a href="#\1">\1</a>', $data);
$data = ereg_replace('\[\[([^#\[]*)([^\[]*)\]\]', '<a href="\1\2">\1</a>', $data);
$data = str_replace("%BR%", "<br />", $data);
$data = nl2br($data);
return $data;
}
?>

110
utils/rockbox_api/gen_html.php Executable file
View file

@ -0,0 +1,110 @@
#!/usr/bin/php
<?
require_once("functions.php");
function get_group($text)
{
return str_replace(array(" ", "/"), "_", $text);
}
$input = file_get_contents($argv[1]);
$inh = parse_documentation($input);
@mkdir("output");
$h = fopen("output/index.html", "w");
fwrite($h, '<html><head><link href="layout.css" rel="stylesheet" type="text/css" /><title>Plugin API - INDEX</title></head><body>');
fwrite($h, "<h1>Plugin API reference</h1>");
fwrite($h, "<ul>");
foreach($inh as $group_name => $group)
{
if(strlen($group_name) > 0)
{
fwrite($h, '<li>'.ucwords($group_name)."<ul>");
foreach($group as $el_name => $el)
fwrite($h, "<li><a href=\"".get_group($group_name).".html#".get_func($el_name)."\">".$el_name."</a></li>");
fwrite($h, "</ul></li>");
}
}
fwrite($h, "</ul></body></html>");
fclose($h);
$menu = '<ul><li><a href="index.html">INDEX</a></li><ul>';
$_menu = array();
foreach($inh as $group_name => $group)
{
if(strlen($group_name) > 0)
$_menu[strtolower($group_name)] = '<li><a href="'.get_group($group_name).'.html">'.ucwords($group_name).'</a></li>';
}
ksort($_menu);
$menu .= implode("\n", $_menu);
$menu .= "</ul></ul>";
foreach($inh as $group_name => $group)
{
$h = fopen("output/".get_group($group_name).".html", "w");
fwrite($h, '<html><head><link href="layout.css" rel="stylesheet" type="text/css" /><title>Plugin API - '.ucwords($group_name).'</title></head><body>');
fwrite($h, '<div id="menu">'.ucwords($menu).'</div>');
fwrite($h, '<div id="content">');
fwrite($h, '<a link="top"></a>');
fwrite($h, "<h2>".ucwords($group_name)."</h2>");
fwrite($h, '<span class="group">');
foreach($group as $func_name => $func)
{
fwrite($h, '<a id="'.get_func($func_name).'"></a>');
fwrite($h, "<h3>$func_name</h3>");
if(strlen($func["description"][0]) > 0)
fwrite($h, do_markup($func["description"][0])."<br /><br />");
if(isset($func["param"]))
{
$params = "";
foreach($func["param"] as $param)
{
$param = trim($param);
$p1 = substr($param, 0, strpos($param, " "));
$p2 = substr($param, strpos($param, " "));
if(strlen($p1) > 0 && strlen($p2) > 0)
$params .= '<dt>'.$p1.'</dt><dd> '.do_markup($p2).'</dd>';
}
if(strlen($params) > 0)
{
fwrite($h, '<span class="extra">Parameters:</span><dl>');
fwrite($h, $params);
fwrite($h, "</dl>");
}
}
if(isset($func["return"]) && strlen($func["return"][0]) > 0)
fwrite($h, '<span class="extra">Returns:</span> '.do_markup($func["return"][0]).'<br /><br />');
if(isset($func["conditions"]))
fwrite($h, '<span class="extra">Conditions:</span> '.$func["conditions"][0].'<br /><br />');
if(isset($func["see"]))
fwrite($h, '<span class="see">Also see '.do_see_markup(explode(" ", trim($func["see"][0]))).'</span><br /><br />');
fwrite($h, '<a href="#top" class="top">To top</a><hr />');
}
fwrite($h, "</span>");
fwrite($h, "</div></body></html>");
fclose($h);
}
copy("layout.css", "output/layout.css");
?>

63
utils/rockbox_api/generate.php Executable file
View file

@ -0,0 +1,63 @@
#!/usr/bin/php
<?
require_once("functions.php");
echo '# Auto generated documentation by Rockbox plugin API generator v2'."\n";
echo '# Made by Maurus Cuelenaere'."\n";
echo <<<MOO
# __________ __ ___.
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
# \/ \/ \/ \/ \/
# \$Id$
#
# Generated from $svn\x61pps/plugin.h
#
# Format:
# \\group memory and strings
# \\conditions defined(HAVE_BACKLIGHT)
# \\param fmt
# \\return
# \\description
# \\see func1 func2 [S[apps/plugin.c]]
#
# Markup:
# [W[wiki url]]
# [S[svn url]]
# [F[function]]
# [[url]]
# %BR%
# =code=
MOO;
foreach(get_newest() as $line)
{
echo "\n".clean_func($line["func"])."\n";
if(strlen($line["group"]) > 0)
echo " \\group ".$line["group"]."\n";
if(strlen($line["cond"]) > 2)
echo " \\conditions "._simplify($line["cond"])."\n";
foreach(get_args($line["func"]) as $param)
{
if(strlen($param) > 0 && $param != "...")
{
$param = split_var($param);
$param = $param[1];
echo " \\param $param\n";
}
}
if(get_return($line["func"]) !== false)
echo " \\return\n";
echo " \\description\n";
}
echo "\n# END\n";
?>

View file

@ -0,0 +1,108 @@
body
{
font-family: Verdana;
}
li
{
font-size: 10px;
}
code
{
color: #00A;
}
.see, .see a
{
color: #559;
}
.group
{
font-size: 12px;
}
.group h3
{
font-size: 14px;
}
h2
{
color: #D11;
}
a
{
color: blue;
}
a:hover
{
color: red;
}
.top
{
font-size: 10px;
color: green;
}
.extra
{
font-weight: bold;
color: #992;
}
dl
{
border-left: 1px solid #CCC;
padding-left: 10px;
}
dt, dd
{
font-style: normal;
}
dd
{
margin-left: 12em;
}
dt
{
clear: left;
display: block;
float: left;
font-weight: bold;
width: 12em;
}
hr
{
border: 1px solid #CCCCCC;
margin: 1em 0;
}
#content
{
margin-left: 14em;
margin-right: 1em;
padding: 0;
}
#menu
{
position: fixed;
top: 0px;
left: 0px;
width: 14em;
padding: 0;
}
#menu ul
{
padding-left: 1em;
}

119
utils/rockbox_api/update.php Executable file
View file

@ -0,0 +1,119 @@
#!/usr/bin/php
<?
require_once("functions.php");
$input = file_get_contents($argv[1]);
$input = parse_documentation($input);
foreach($input as $rootname => $rootel)
{
foreach($rootel as $name => $el)
$input[$name] = $el;
unset($input[$rootname]);
}
$new = get_newest();
foreach($new as $name => $el)
{
unset($new[$name]);
$name = clean_func($el["func"]);
$new[$name] = array(
"group" => array($el["group"]),
"description" => array("")
);
if(strlen($el["cond"]) > 2)
$new[$name]["conditions"][0] = $el["cond"];
$args = get_args($el["func"]);
if(count($args) > 0)
{
foreach($args as $n => $arg)
{
$tmp = split_var($arg);
$args[$n] = $tmp[1];
}
$new[$name]["param"] = $args;
}
if(get_return($el["func"]) !== false)
$new[$name]["return"][0] = "";
}
$merged = array_merge($new, $input);
uksort($merged, "func_sort");
echo '# Auto generated documentation by Rockbox plugin API generator v2'."\n";
echo '# Made by Maurus Cuelenaere'."\n";
echo <<<MOO
# __________ __ ___.
# Open \______ \ ____ ____ | | _\_ |__ _______ ___
# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
# \/ \/ \/ \/ \/
# \$Id$
#
# Generated from $svn\x61pps/plugin.h
#
# Format:
# \\group memory and strings
# \\conditions defined(HAVE_BACKLIGHT)
# \\param fmt
# \\return
# \\description
# \\see func1 func2 [S[apps/plugin.c]]
#
# Markup:
# [W[wiki url]]
# [S[svn url]]
# [F[function]]
# [[url]]
# %BR%
# =code=
MOO;
foreach($merged as $func => $line)
{
echo "\n".clean_func($func)."\n";
if(strlen($line["group"]) > 0)
echo " \\group ".trim($line["group"][0])."\n";
if(strlen($line["conditions"]) > 2)
echo " \\conditions ".trim(_simplify($line["conditions"][0]))."\n";
if(isset($line["param"]))
{
foreach($line["param"] as $param)
{
if($param != "...")
echo " \\param ".trim($param)."\n";
}
}
if(isset($line["return"]))
{
if(trim($line["return"]) == "")
echo " \\return\n";
else
echo " \\return ".trim($line["return"][0])."\n";
}
if(trim($line["description"]) == "")
echo " \\description\n";
else
echo " \\description ".trim($line["description"][0])."\n";
if(isset($line["see"]))
echo " \\see ".trim($line["see"][0])."\n";
}
echo "\n# END\n";
?>