mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
"xen-headers" generates HTML from header files. So far this generates just some type cross-references, if you say make -C docs html/hypercall/stamp An index page, proper wiring into the build system, and a few more annotations in the headers, and will be forthcoming. Signed-off-by: Ian Jackson <ian.jackson@eu.citrix.com> Acked-by: Ian Campbell <ian.campbell@citrix.com> Committed-by: Ian Jackson <Ian.Jackson@eu.citrix.com>
293 lines
7.5 KiB
Perl
Executable File
293 lines
7.5 KiB
Perl
Executable File
#!/usr/bin/perl -w
|
|
# usage: xen-headers OPTIONS... BASE-DIR INPUT-SUB-DIR...
|
|
# INPUT-SUB-DIR must be a relative path, and is interpreted
|
|
# relative to BASE-DIR. Only files whose names end .h are processed
|
|
# options:
|
|
# -O HTML-DIR write html to this directory (mandatory)
|
|
# -T EXTRA-TITLE-HTML tail of title string (used in <title>)
|
|
# -X GLOB | -I GLOB include/exclude files matching;
|
|
# glob patterns matched against /INPUT-SUB-FILE
|
|
# first match wins; if no match, files included
|
|
# -D increase debug
|
|
|
|
# Functionality:
|
|
# enum values --> selected function or struct
|
|
# type & function names, macro definitions --> definition
|
|
# function or struct selected by enum ++> ref to enum value
|
|
|
|
# definitions must start in LH column
|
|
# extra syntax:
|
|
# /* ` <definition> } parse as if <definition>
|
|
# * ` <definition> } was not commented
|
|
# enum <name> { // <pattern>* => <func>() } cross-reference
|
|
# enum <name> { // <pattern>* => struct <s> } enum values
|
|
#
|
|
|
|
# 1st pass: find where things are defined and what references are wanted
|
|
# 2rd pass: write out output
|
|
|
|
use strict;
|
|
use warnings;
|
|
|
|
use Getopt::Long;
|
|
use File::Find;
|
|
use IO::File;
|
|
|
|
Getopt::Long::Configure('bundling');
|
|
|
|
our $outdir;
|
|
our $debug=0;
|
|
our $xtitle='';
|
|
our @fglobs;
|
|
|
|
sub includeexclude {
|
|
my ($yn, $optobj, $value) = @_;
|
|
push @fglobs, [ $value, $yn ];
|
|
}
|
|
|
|
GetOptions("O|output-dir=s" => \$outdir,
|
|
"D+" => \$debug,
|
|
"T=s" => \$xtitle,
|
|
"I=s" => sub { includeexclude(1, @_); },
|
|
"X=s" => sub { includeexclude(0, @_); })
|
|
or die;
|
|
|
|
die unless defined $outdir;
|
|
@ARGV>=2 or die;
|
|
|
|
my ($basedir,@indirs) = @ARGV;
|
|
|
|
# general globals
|
|
our $pass;
|
|
our %sdef;
|
|
# $sdef{$type}{$name} => {
|
|
# DefLocs => { "$leaf_path:$lineno" => $leaf_opath ,... }
|
|
# Xrefs => { "$leaf_path,$lineno" => "$xref", ... }
|
|
# Used => 1
|
|
# }
|
|
# $type might be Func Struct Union Enum EnumVal
|
|
|
|
# provided by the find() function
|
|
our $leaf;
|
|
our $leaf_opath;
|
|
|
|
# reset at the start of each file
|
|
our $o;
|
|
our $in_enum;
|
|
our @pending_xrefs;
|
|
|
|
sub compile_fglobs () {
|
|
local ($_);
|
|
my $f = "sub file_wanted (\$) {\n local (\$_) = \"/\$leaf\";\n";
|
|
foreach my $fglob (@fglobs) {
|
|
$_ = $fglob->[0];
|
|
$_ = "**$_**" unless m/[?*]/;
|
|
s/\W/\\$&/g;
|
|
s,\\\*\\\*,.*,g;
|
|
s,\\\*,[^/]*,g;
|
|
s,\\\?,[^/],g;
|
|
$f .= " return $fglob->[1] if m,$_,o;\n";
|
|
}
|
|
$f .= " return 1;\n}\n1;\n";
|
|
debug(3, $f);
|
|
eval $f or die "$@ ";
|
|
}
|
|
|
|
compile_fglobs();
|
|
|
|
|
|
sub warning {
|
|
print STDERR "$leaf:$.: @_\n";
|
|
}
|
|
|
|
sub debug {
|
|
my $msglevel = scalar shift @_;
|
|
return unless $debug >= $msglevel;
|
|
print STDERR "DEBUG $pass $msglevel @_\n" or die $!;
|
|
}
|
|
|
|
sub in_enum ($$$) { $in_enum = [ @_ ]; } # [ $enumvalpfx, RefType, $refnamepfx ]
|
|
|
|
sub aelem ($$$) {
|
|
my ($ntext,$ytext,$hparams) = @_;
|
|
return $ntext unless $hparams =~ m/\S/;
|
|
return "<a $hparams>$ytext</a>";
|
|
}
|
|
|
|
sub defn ($$$;$) {
|
|
my ($text,$type,$name,$hparams) = @_;
|
|
$hparams='' if !defined $hparams;
|
|
debug(2,"DEFN $. $type $name $hparams");
|
|
$sdef{$type}{$name}{DefLocs}{"$leaf:$."} = $leaf_opath;
|
|
my $xrefs = $sdef{$type}{$name}{Xrefs};
|
|
push @pending_xrefs, values %$xrefs if $xrefs;
|
|
$hparams .= " name=\"${type}_$name\"" if $sdef{$type}{$name}{Used};
|
|
return aelem($text, "<strong>$text</strong>", $hparams);
|
|
}
|
|
|
|
sub norm ($) {
|
|
local ($_) = @_;
|
|
my $no = '';
|
|
while (length) {
|
|
if (s/^(?:\s|^\W)+//) {
|
|
$no .= $&;
|
|
} elsif (s/^(struct|union|enum)\s+(\w+)\b//) {
|
|
$no .= ahref($&, (ucfirst $1), $2);
|
|
} elsif (s/^\w+\b//) {
|
|
$no .= ahref($&, 'Func', $&);
|
|
} else {
|
|
die "$_ ?";
|
|
}
|
|
}
|
|
return $no;
|
|
}
|
|
|
|
sub refhref ($$) {
|
|
my ($type,$name) = @_;
|
|
$sdef{$type}{$name}{Used} = 1;
|
|
my $locs = $sdef{$type}{$name}{DefLocs};
|
|
return '' unless $locs;
|
|
if ((scalar keys %$locs) != 1 && !$sdef{$type}{$name}{MultiWarned}) {
|
|
warning("multiple definitions of $type $name: $_")
|
|
foreach keys %$locs;
|
|
$sdef{$type}{$name}{MultiWarned}=1;
|
|
}
|
|
my ($loc) = values %$locs;
|
|
return "href=\"$loc#${type}_$name\"";
|
|
}
|
|
|
|
sub ahref ($$$) {
|
|
my ($text,$type,$name) = @_;
|
|
return aelem($text,$text, refhref($type,$name));
|
|
}
|
|
|
|
sub defmacro ($) {
|
|
my ($valname) = @_;
|
|
if (!$in_enum) {
|
|
return $valname;
|
|
} elsif (substr($valname, 0, (length $in_enum->[0])) ne $in_enum->[0]) {
|
|
warning("in enum expecting $in_enum->[0]* got $valname");
|
|
return $valname;
|
|
} else {
|
|
my $reftype = $in_enum->[1];
|
|
my $refname = $in_enum->[2].substr($valname, (length $in_enum->[0]));
|
|
$sdef{$reftype}{$refname}{Xrefs}{$leaf,$.} =
|
|
"[see <a href=\"$leaf_opath#EnumVal_$valname\">$valname</a>]";
|
|
$sdef{EnumVal}{$valname}{Used} = 1;
|
|
return defn($valname,'EnumVal',$valname, refhref($reftype,$refname));
|
|
}
|
|
}
|
|
|
|
sub out_xrefs ($) {
|
|
my ($linemapfunc) = @_;
|
|
foreach my $xref (@pending_xrefs) {
|
|
$o .= $linemapfunc->($xref);
|
|
$o .= "\n";
|
|
}
|
|
@pending_xrefs = ();
|
|
}
|
|
|
|
sub write_file ($$) {
|
|
my ($opath, $odata) = @_;
|
|
my $out = new IO::File "$opath.new", '>' or die "$opath $!";
|
|
print $out $odata or die $!;
|
|
rename "$opath.new", "$opath" or die "$opath $!";
|
|
}
|
|
|
|
sub process_file ($$) {
|
|
my ($infile, $outfile) = @_;
|
|
debug(1,"$pass $infile => $outfile");
|
|
my $in = new IO::File "$infile", '<' or die "$infile $!";
|
|
|
|
$o = '';
|
|
$in_enum = undef;
|
|
@pending_xrefs = ();
|
|
|
|
$o .= "<html><head><title>$leaf - $xtitle</title></head><body><pre>\n";
|
|
|
|
while (<$in>) {
|
|
s/\&/\&/g;
|
|
s/\</\</g;
|
|
s/\>/\>/g;
|
|
|
|
if (m/^(.*\`)[ \t]*$/) {
|
|
my $lhs = $1;
|
|
out_xrefs(sub { "$1 $_[0]"; });
|
|
} elsif (m/^\s*$/) {
|
|
out_xrefs(sub { sprintf "/* %70s */", $_[0]; });
|
|
}
|
|
|
|
# In case of comments, strip " /* ` " and " * ` ";
|
|
my $lstripped = s,^ \s* /? \* \s* \` \ ,,x ? $&: '';
|
|
|
|
# Strip trailing whitespace and perhaps trailing "*/" or "*"
|
|
s,(?: \s* \* /? )? \s* $,,x or die;
|
|
my $rstripped = $&;
|
|
|
|
# Now the actual functionality:
|
|
|
|
debug(3,"$. $_");
|
|
|
|
if (!m/^(?: __attribute__ | __pragma__ )\b/x &&
|
|
s/^( (?: \w+\ )? ) (\w+[a-z]\w+) ( \( .*)$
|
|
/ $1.defn($2,'Func',$2).norm($3) /xe) {
|
|
} elsif (s/^((struct|union|enum) \ (\w+)) ( \s+ \{ .* )$
|
|
/ defn($1,(ucfirst $2),$3).norm($4) /xe) {
|
|
if ($2 eq 'enum') {
|
|
if (m,/[/*] (\w+)\* \=\>\; (\w+)\*\(\),) {
|
|
in_enum($1,'Func',$2)
|
|
} elsif (m,/[/*] (\w+)\* \=\>\; (struct) (\w+)\*,) {
|
|
in_enum($1,(ucfirst $2),$3);
|
|
}
|
|
}
|
|
} elsif (s/^( \s* \#define \s+ ) (\w+) ( \s+\S )
|
|
/ $1.defmacro($2).norm($3) /xe) {
|
|
} else {
|
|
if (m/^\s*\}/) {
|
|
$in_enum = undef;
|
|
}
|
|
$_ = norm($_);
|
|
}
|
|
|
|
# Write the line out
|
|
|
|
if ($pass == 2) {
|
|
$o .= $lstripped;
|
|
$o .= $_;
|
|
$o .= $rstripped;
|
|
}
|
|
}
|
|
|
|
warning("pending xrefs at end of file") if @pending_xrefs;
|
|
|
|
if ($pass == 2) {
|
|
$o .= "</pre></body></html>";
|
|
write_file($outfile, $o);
|
|
}
|
|
}
|
|
|
|
|
|
foreach $pass (qw(1 2)) {
|
|
find({ wanted =>
|
|
sub {
|
|
return unless m/\.h$/;
|
|
lstat $File::Find::name or die "$File::Find::name $!";
|
|
-f _ or die "$File::Find::name";
|
|
substr($File::Find::name, 0, 1+length $basedir)
|
|
eq "$basedir/"
|
|
or die "$File::Find::name $basedir";
|
|
$leaf = substr($File::Find::name, 1+length $basedir);
|
|
if (!file_wanted()) {
|
|
debug(1,"$pass $File::Find::name excluded");
|
|
return;
|
|
}
|
|
$leaf_opath = $leaf;
|
|
$leaf_opath =~ s#/#,#g;
|
|
$leaf_opath .= ".html";
|
|
process_file($File::Find::name, $outdir.'/'.$leaf_opath);
|
|
},
|
|
no_chdir => 1,
|
|
},
|
|
map { "$basedir/$_" } @indirs);
|
|
}
|