xen-devel.lists.xenproject.org archive mirror
 help / color / mirror / Atom feed
From: Ian Jackson <ian.jackson@eu.citrix.com>
To: xen-devel@lists.xensource.com
Cc: Ian Jackson <ian.jackson@eu.citrix.com>
Subject: [PATCH 1/4] docs/html/hcall: Initial cut of header documentation massager
Date: Sun, 13 Nov 2011 19:21:03 +0000	[thread overview]
Message-ID: <1321212066-10648-2-git-send-email-ian.jackson@eu.citrix.com> (raw)
In-Reply-To: <1321212066-10648-1-git-send-email-ian.jackson@eu.citrix.com>

"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 <ian.jackson@eu.citrix.com>
---
 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:
+#    /* ` <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/\&/\&amp;/g;
+	s/\</\&lt;/g;
+	s/\>/\&gt;/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+)\* \=\&gt\; (\w+)\*\(\),) { 
+		    in_enum($1,'Func',$2)
+		} elsif (m,/[/*] (\w+)\* \=\&gt\; (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);
+}
-- 
1.7.2.5

  reply	other threads:[~2011-11-13 19:21 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2011-11-13 19:21 [PATCH 0/4] docs/html/hcall: Hypercall docs headers Ian Jackson
2011-11-13 19:21 ` Ian Jackson [this message]
2011-11-14  9:59   ` [PATCH 1/4] docs/html/hcall: Initial cut of header documentation massager Ian Campbell
2011-11-14 17:20     ` Ian Jackson
2011-11-13 19:21 ` [PATCH 2/4] docs/html/hcall: Annotations for two hypercalls Ian Jackson
2011-11-14 10:12   ` Ian Campbell
2011-11-14 17:22     ` Ian Jackson
2011-11-14 18:40   ` Konrad Rzeszutek Wilk
2011-11-15 14:47     ` Ian Jackson
2011-11-15 15:00       ` Konrad Rzeszutek Wilk
2011-11-29 14:21         ` Ian Jackson
2011-11-13 19:21 ` [PATCH 3/4] docs/html/hcall: Generate an "index.html" Ian Jackson
2011-11-14 10:13   ` Ian Campbell
2011-11-14 17:23     ` Ian Jackson
2011-11-13 19:21 ` [PATCH 4/4] docs/html/hcall: Arrange for automatic build Ian Jackson
2011-11-14 10:13 ` [PATCH 0/4] docs/html/hcall: Hypercall docs headers Ian Campbell
2011-11-14 17:24   ` Ian Jackson

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1321212066-10648-2-git-send-email-ian.jackson@eu.citrix.com \
    --to=ian.jackson@eu.citrix.com \
    --cc=xen-devel@lists.xensource.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).