Re: [PATCH 1/4] docs/html/: Initial cut of header documentation massager

Ian Campbell
Tue, 29 Nov 2011 15:19:27 -0000

On Tue, 2011-11-29 at 15:01 +0000, Ian Jackson wrote:
> "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>

> [...]

> +GetOptions("O|output-dir=s" => \$outdir,
> +           "D+" => \$debug,
> +           "T=s" => \$xtitle,
> +	   "I=s" => sub { includeexclude(1, @_); },
> +	   "X=s" => sub { includeexclude(0, @_); })

Indentation is messed up (you''ve got a hard tab from somewhere).

With that fixed:
Acked-by: Ian Campbell <ian.campbell@citrix.com>

(insofar as I can review Perl code...)

> +    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";
> +}
> +
> +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 <a
href=\"$leaf_opath#EnumVal_$valname\">$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/\&/\&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 (!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+)\* \=\&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 .= " ";
> +	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);
> +}