From mboxrd@z Thu Jan 1 00:00:00 1970 From: Ian Jackson Subject: [PATCH 1/4] docs/html/hcall: Initial cut of header documentation massager Date: Sun, 13 Nov 2011 19:21:03 +0000 Message-ID: <1321212066-10648-2-git-send-email-ian.jackson@eu.citrix.com> References: <1321212066-10648-1-git-send-email-ian.jackson@eu.citrix.com> Mime-Version: 1.0 Content-Type: text/plain Return-path: In-Reply-To: <1321212066-10648-1-git-send-email-ian.jackson@eu.citrix.com> List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , Sender: xen-devel-bounces@lists.xensource.com Errors-To: xen-devel-bounces@lists.xensource.com To: xen-devel@lists.xensource.com Cc: Ian Jackson List-Id: xen-devel@lists.xenproject.org "xen-headers" generates HTML from header files. So far this generates just some type cross-references, if you say make -C docs html/hcall/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 --- docs/Makefile | 8 ++ docs/xen-headers | 281 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 289 insertions(+), 0 deletions(-) create mode 100755 docs/xen-headers diff --git a/docs/Makefile b/docs/Makefile index 2054541..fc42859 100644 --- a/docs/Makefile +++ b/docs/Makefile @@ -129,6 +129,14 @@ html/%.html: %.markdown $(MARKDOWN) $< > $@.tmp ; \ $(call move-if-changed,$@.tmp,$@) ; fi +html/hcall/stamp: + @$(INSTALL_DIR) $(@D) + ./xen-headers -O $(@D) \ + -T 'arch-x86_64 - Xen public headers' \ + -X arch-ia64 -X arch-x86_32 -X xen-x86_32 \ + ../xen include/public + touch $@ + txt/%.txt: %.txt $(INSTALL_DIR) $(@D) cp $< $@.tmp diff --git a/docs/xen-headers b/docs/xen-headers new file mode 100755 index 0000000..6918380 --- /dev/null +++ b/docs/xen-headers @@ -0,0 +1,281 @@ +#!/usr/bin/perl -w +# usage: xen-headers [ -X GLOB -I GLOB ... [-D...] ] \ +# -O HTML-DIR PUBLIC-INCLUDE-DIR + +# enum values --> selected function or struct +# type & function names --> definition +# function or struct selected by enum ++> ref to enum value + +# definitions must start in LH column +# extra syntax: +# /* ` } parse as if +# * ` } was not commented +# enum { // * => () } cross-reference +# enum { // * => struct } 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 "$ytext"; +} + +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, "$text", $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 $valname]"; + $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 .= "$leaf - $xtitle
\n";
+    
+    while (<$in>) {
+	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 (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 .= "
"; + 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); +} -- 1.7.2.5