#!/bin/ksh -p # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright (c) 2008, 2013, Oracle and/or its affiliates. All rights reserved. # Use is subject to license terms. # # This script takes a file list and a workspace and builds a set of html files # suitable for doing a code review of source changes via a web page. # Documentation is available via 'webrev -h'. # WEBREV_UPDATED=25.1-hg+openjdk.java.net HTML=' \n' FRAMEHTML=' \n' STDHEAD=' ' # # UDiffs need a slightly different CSS rule for 'new' items (we don't # want them to be bolded as we do in cdiffs or sdiffs). # UDIFFCSS=' ' # # input_cmd | html_quote | output_cmd # or # html_quote filename | output_cmd # # Make a piece of source code safe for display in an HTML
 block.
#
html_quote()
{
	sed -e "s/&/\&/g" -e "s/&#\([x]*[0-9A-Fa-f]\{2,5\}\);/\&#\1;/g" -e "s//\>/g" "$@" | expand
}

#
# input_cmd | html_quote | output_cmd
# or
# html_dequote filename | output_cmd
#
# Replace HTML entities with literals
#
html_dequote()
{
	sed -e "s/"/\"/g" -e "s/'/\'/g" -e "s/&/\&/g" -e "s/</<'/g" -e "s/>/>/g" "$@" | expand
}

#
# input_cmd | bug2url | output_cmd
#
# Scan for bugids and insert  links to the relevent bug database.
#
bug2url()
{
	sed -e 's|[0-9]\{5,\}|&|g'
}

#
# strip_unchanged  | output_cmd
#
# Removes chunks of sdiff documents that have not changed. This makes it
# easier for a code reviewer to find the bits that have changed.
#
# Deleted lines of text are replaced by a horizontal rule. Some
# identical lines are retained before and after the changed lines to
# provide some context.  The number of these lines is controlled by the
# variable C in the $AWK script below.
#
# The script detects changed lines as any line that has a "
"
				inx = c % C
				c = C
			}

			for (i = 0; i < c; i++)
				print ln[(inx + i) % C]
		}
		c = 0;
		print
		next
	}
	{	if (c >= C) {
			ln[c % C] = $0
			c++;
			next;
		}
		c++;
		print
	}
	END	{ if (c > (C * 2)) print "\n

" } ' $1 } # # sdiff_to_html # # This function takes two files as arguments, obtains their diff, and # processes the diff output to present the files as an HTML document with # the files displayed side-by-side, differences shown in color. It also # takes a delta comment, rendered as an HTML snippet, as the third # argument. The function takes two files as arguments, then the name of # file, the path, and the comment. The HTML will be delivered on stdout, # e.g. # # $ sdiff_to_html old/usr/src/tools/scripts/webrev.sh \ # new/usr/src/tools/scripts/webrev.sh \ # webrev.sh usr/src/tools/scripts \ # ' # JDK-1234567 my bugid' > .html # # framed_sdiff() is then called which creates $2.frames.html # in the webrev tree. # # FYI: This function is rather unusual in its use of awk. The initial # diff run produces conventional diff output showing changed lines mixed # with editing codes. The changed lines are ignored - we're interested in # the editing codes, e.g. # # 8c8 # 57a61 # 63c66,76 # 68,93d80 # 106d90 # 108,110d91 # # These editing codes are parsed by the awk script and used to generate # another awk script that generates HTML, e.g the above lines would turn # into something like this: # # BEGIN { printf "
\n" }
#      function sp(n) {for (i=0;i%4d %s \n", n, NR, $0}
#      NR==8           {wl("#7A7ADD");next}
#      NR==54          {wl("#7A7ADD");sp(3);next}
#      NR==56          {wl("#7A7ADD");next}
#      NR==57          {wl("black");printf "\n"; next}
#        :               :
#
#  This script is then run on the original source file to generate the
#  HTML that corresponds to the source file.
#
#  The two HTML files are then combined into a single piece of HTML that
#  uses an HTML table construct to present the files side by side.  You'll
#  notice that the changes are color-coded:
#
#   black     - unchanged lines
#   blue      - changed lines
#   bold blue - new lines
#   brown     - deleted lines
#
#  Blank lines are inserted in each file to keep unchanged lines in sync
#  (side-by-side).  This format is familiar to users of sdiff(1) or
#  Teamware's filemerge tool.
#
sdiff_to_html()
{
	diff -b $1 $2 > /tmp/$$.diffs

	TNAME=$3
	TPATH=$4
	COMMENT=$5

	#
	#  Now we have the diffs, generate the HTML for the old file.
	#
	$AWK '
	BEGIN	{
		printf "function sp(n) {for (i=0;i%%4d %%s\\n\", NR, $0}\n"
		printf "function changed() "
		printf "{printf \"%%4d %%s\\n\", NR, $0}\n"
		printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
}
	/^/	{next}
	/^---/	{next}

	{
	split($1, a, /[cad]/) ;
	if (index($1, "a")) {
		if (a[1] == 0) {
			n = split(a[2], r, /,/);
			if (n == 1)
				printf "BEGIN\t\t{sp(1)}\n"
			else
				printf "BEGIN\t\t{sp(%d)}\n",\
				(r[2] - r[1]) + 1
			next
		}

		printf "NR==%s\t\t{", a[1]
		n = split(a[2], r, /,/);
		s = r[1];
		if (n == 1)
			printf "bl();printf \"\\n\"; next}\n"
		else {
			n = r[2] - r[1]
			printf "bl();sp(%d);next}\n",\
			(r[2] - r[1]) + 1
		}
		next
	}
	if (index($1, "d")) {
		n = split(a[1], r, /,/);
		n1 = r[1]
		n2 = r[2]
		if (n == 1)
			printf "NR==%s\t\t{removed(); next}\n" , n1
		else
			printf "NR==%s,NR==%s\t{removed(); next}\n" , n1, n2
		next
	}
	if (index($1, "c")) {
		n = split(a[1], r, /,/);
		n1 = r[1]
		n2 = r[2]
		final = n2
		d1 = 0
		if (n == 1)
			printf "NR==%s\t\t{changed();" , n1
		else {
			d1 = n2 - n1
			printf "NR==%s,NR==%s\t{changed();" , n1, n2
		}
		m = split(a[2], r, /,/);
		n1 = r[1]
		n2 = r[2]
		if (m > 1) {
			d2  = n2 - n1
			if (d2 > d1) {
				if (n > 1) printf "if (NR==%d)", final
				printf "sp(%d);", d2 - d1
			}
		}
		printf "next}\n" ;

		next
	}
	}

	END	{ printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
	' /tmp/$$.diffs > /tmp/$$.file1

	#
	#  Now generate the HTML for the new file
	#
	$AWK '
	BEGIN	{
		printf "function sp(n) {for (i=0;i%%4d %%s\\n\", NR, $0}\n"
		printf "function changed() "
		printf "{printf \"%%4d %%s\\n\", NR, $0}\n"
		printf "function bl() {printf \"%%4d %%s\\n\", NR, $0}\n"
	}

	/^/	{next}
	/^---/	{next}

	{
	split($1, a, /[cad]/) ;
	if (index($1, "d")) {
		if (a[2] == 0) {
			n = split(a[1], r, /,/);
			if (n == 1)
				printf "BEGIN\t\t{sp(1)}\n"
			else
				printf "BEGIN\t\t{sp(%d)}\n",\
				(r[2] - r[1]) + 1
			next
		}

		printf "NR==%s\t\t{", a[2]
		n = split(a[1], r, /,/);
		s = r[1];
		if (n == 1)
			printf "bl();printf \"\\n\"; next}\n"
		else {
			n = r[2] - r[1]
			printf "bl();sp(%d);next}\n",\
			(r[2] - r[1]) + 1
		}
		next
	}
	if (index($1, "a")) {
		n = split(a[2], r, /,/);
		n1 = r[1]
		n2 = r[2]
		if (n == 1)
			printf "NR==%s\t\t{new() ; next}\n" , n1
		else
			printf "NR==%s,NR==%s\t{new() ; next}\n" , n1, n2
		next
	}
	if (index($1, "c")) {
		n = split(a[2], r, /,/);
		n1 = r[1]
		n2 = r[2]
		final = n2
		d2 = 0;
		if (n == 1) {
			final = n1
			printf "NR==%s\t\t{changed();" , n1
		} else {
			d2 = n2 - n1
			printf "NR==%s,NR==%s\t{changed();" , n1, n2
		}
		m = split(a[1], r, /,/);
		n1 = r[1]
		n2 = r[2]
		if (m > 1) {
			d1  = n2 - n1
			if (d1 > d2) {
				if (n > 1) printf "if (NR==%d)", final
				printf "sp(%d);", d1 - d2
			}
		}
		printf "next}\n" ;
		next
	}
	}
	END	{ printf "{printf \"%%4d %%s\\n\", NR, $0 }\n" }
	' /tmp/$$.diffs > /tmp/$$.file2

	#
	# Post-process the HTML files by running them back through $AWK
	#
	html_quote < $1 | $AWK -f /tmp/$$.file1 > /tmp/$$.file1.html

	html_quote < $2 | $AWK -f /tmp/$$.file2 > /tmp/$$.file2.html

	#
	# Now combine into a valid HTML file and side-by-side into a table
	#
	print "$HTML$STDHEAD"
	print "$WNAME Sdiff $TPATH "
	print ""
	print "

$TPATH/$TNAME

" print "Print this page" print "
$COMMENT
\n" print "" print "" print "
"

	strip_unchanged /tmp/$$.file1.html

	print "
"

	strip_unchanged /tmp/$$.file2.html

	print "
" print "" framed_sdiff $TNAME $TPATH /tmp/$$.file1.html /tmp/$$.file2.html \ "$COMMENT" } # # framed_sdiff # # Expects lefthand and righthand side html files created by sdiff_to_html. # We use insert_anchors() to augment those with HTML navigation anchors, # and then emit the main frame. Content is placed into: # # $WDIR/DIR/$TNAME.lhs.html # $WDIR/DIR/$TNAME.rhs.html # $WDIR/DIR/$TNAME.frames.html # # NOTE: We rely on standard usage of $WDIR and $DIR. # function framed_sdiff { typeset TNAME=$1 typeset TPATH=$2 typeset lhsfile=$3 typeset rhsfile=$4 typeset comments=$5 typeset RTOP # Enable html files to access WDIR via a relative path. RTOP=$(relative_dir $TPATH $WDIR) # Make the rhs/lhs files and output the frameset file. print "$HTML$STDHEAD" > $WDIR/$DIR/$TNAME.lhs.html cat >> $WDIR/$DIR/$TNAME.lhs.html <<-EOF
$comments

EOF cp $WDIR/$DIR/$TNAME.lhs.html $WDIR/$DIR/$TNAME.rhs.html insert_anchors $lhsfile >> $WDIR/$DIR/$TNAME.lhs.html insert_anchors $rhsfile >> $WDIR/$DIR/$TNAME.rhs.html close='' print $close >> $WDIR/$DIR/$TNAME.lhs.html print $close >> $WDIR/$DIR/$TNAME.rhs.html print "$FRAMEHTML$STDHEAD" > $WDIR/$DIR/$TNAME.frames.html print "$WNAME Framed-Sdiff " \ "$TPATH/$TNAME " >> $WDIR/$DIR/$TNAME.frames.html cat >> $WDIR/$DIR/$TNAME.frames.html <<-EOF <body id="SUNWwebrev"> Alas 'frames' webrev requires that your browser supports frames and has the feature enabled. </body> EOF } # # fix_postscript # # Merge codereview output files to a single conforming postscript file, by: # - removing all extraneous headers/trailers # - making the page numbers right # - removing pages devoid of contents which confuse some # postscript readers. # # From Casper. # function fix_postscript { infile=$1 cat > /tmp/$$.crmerge.pl << \EOF print scalar(<>); # %!PS-Adobe--- print "%%Orientation: Landscape\n"; $pno = 0; $doprint = 1; $page = ""; while (<>) { next if (/^%%Pages:\s*\d+/); if (/^%%Page:/) { if ($pno == 0 || $page =~ /\)S/) { # Header or single page containing text print "%%Page: ? $pno\n" if ($pno > 0); print $page; $pno++; } else { # Empty page, skip it. } $page = ""; $doprint = 1; next; } # Skip from %%Trailer of one document to Endprolog # %%Page of the next $doprint = 0 if (/^%%Trailer/); $page .= $_ if ($doprint); } if ($page =~ /\)S/) { print "%%Page: ? $pno\n"; print $page; } else { $pno--; } print "%%Trailer\n%%Pages: $pno\n"; EOF $PERL /tmp/$$.crmerge.pl < $infile } # # input_cmd | insert_anchors | output_cmd # # Flag blocks of difference with sequentially numbered invisible # anchors. These are used to drive the frames version of the # sdiffs output. # # NOTE: Anchor zero flags the top of the file irrespective of changes, # an additional anchor is also appended to flag the bottom. # # The script detects changed lines as any line that has a "", anc, anc++; } BEGIN { anc=1; inblock=1; printf "
\n";
	}
	NF == 0 || /^";
		printf "--- EOF ---"
        	for(i=0;i<8;i++) printf "\n\n\n\n\n\n\n\n\n\n";
		printf "
" printf "
"; printf "", anc - 1; printf "
"; } ' $1 } # # relative_dir # # Print a relative return path from $1 to $2. For example if # $1=/tmp/myreview/raw_files/usr/src/tools/scripts and $2=/tmp/myreview, # this function would print "../../../../". # # In the event that $1 is not in $2 a warning is printed to stderr, # and $2 is returned-- the result of this is that the resulting webrev # is not relocatable. # function relative_dir { d1=$1 d2=$2 if [[ "$d1" == "." ]]; then print "." else typeset cur="${d1##$d2?(/)}" typeset ret="" if [[ $d2 == $cur ]]; then # Should never happen. # Should never happen. print -u2 "\nWARNING: relative_dir: \"$1\" not relative " print -u2 "to \"$2\". Check input paths. Framed webrev " print -u2 "will not be relocatable!" print $2 return fi while [[ -n ${cur} ]]; do cur=${cur%%*(/)*([!/])} if [[ -z $ret ]]; then ret=".." else ret="../$ret" fi done print $ret fi } # # frame_nav_js # # Emit javascript for frame navigation # function frame_nav_js { cat << \EOF var myInt; var scrolling=0; var sfactor = 3; var scount=10; function scrollByPix() { if (scount<=0) { sfactor*=1.2; scount=10; } parent.lhs.scrollBy(0,sfactor); parent.rhs.scrollBy(0,sfactor); scount--; } function scrollToAnc(num) { // Update the value of the anchor in the form which we use as // storage for this value. setAncValue() will take care of // correcting for overflow and underflow of the value and return // us the new value. num = setAncValue(num); // Set location and scroll back a little to expose previous // lines. // // Note that this could be improved: it is possible although // complex to compute the x and y position of an anchor, and to // scroll to that location directly. // parent.lhs.location.replace(parent.lhs.location.pathname + "#" + num); parent.rhs.location.replace(parent.rhs.location.pathname + "#" + num); parent.lhs.scrollBy(0,-30); parent.rhs.scrollBy(0,-30); } function getAncValue() { return (parseInt(parent.nav.document.diff.real.value)); } function setAncValue(val) { if (val <= 0) { val = 0; parent.nav.document.diff.real.value = val; parent.nav.document.diff.display.value = "BOF"; return (val); } // // The way we compute the max anchor value is to stash it // inline in the left and right hand side pages-- it's the same // on each side, so we pluck from the left. // maxval = parent.lhs.document.eof.value.value; if (val < maxval) { parent.nav.document.diff.real.value = val; parent.nav.document.diff.display.value = val.toString(); return (val); } // this must be: val >= maxval val = maxval; parent.nav.document.diff.real.value = val; parent.nav.document.diff.display.value = "EOF"; return (val); } function stopScroll() { if (scrolling==1) { clearInterval(myInt); scrolling=0; } } function startScroll() { stopScroll(); scrolling=1; myInt=setInterval("scrollByPix()",10); } function handlePress(b) { switch (b) { case 1 : scrollToAnc(-1); break; case 2 : scrollToAnc(getAncValue() - 1); break; case 3 : sfactor=-3; startScroll(); break; case 4 : sfactor=3; startScroll(); break; case 5 : scrollToAnc(getAncValue() + 1); break; case 6 : scrollToAnc(999999); break; } } function handleRelease(b) { stopScroll(); } function keypress(ev) { var keynum; var keychar; if (window.event) { // IE keynum = ev.keyCode; } else if (ev.which) { // non-IE keynum = ev.which; } keychar = String.fromCharCode(keynum); if (keychar == "k") { handlePress(2); return (0); } else if (keychar == "j" || keychar == " ") { handlePress(5); return (0); } return (1); } function ValidateDiffNum(){ val = parent.nav.document.diff.display.value; if (val == "EOF") { scrollToAnc(999999); return; } if (val == "BOF") { scrollToAnc(0); return; } i=parseInt(val); if (isNaN(i)) { parent.nav.document.diff.display.value = getAncValue(); } else { scrollToAnc(i); } return false; } EOF } # # frame_navigation # # Output anchor navigation file for framed sdiffs. # function frame_navigation { print "$HTML$STDHEAD" cat << \EOF Anchor Navigation EOF print "" cat << \EOF
Diff navigation: Use 'j' and 'k' for next and previous diffs; or use buttons at right
EOF } # # diff_to_html { U | C } # # Processes the output of diff to produce an HTML file representing either # context or unified diffs. # diff_to_html() { TNAME=$1 TPATH=$2 DIFFTYPE=$3 COMMENT=$4 print "$HTML$STDHEAD" print "$WNAME ${DIFFTYPE}diff $TPATH" if [[ $DIFFTYPE == "U" ]]; then print "$UDIFFCSS" fi cat <<-EOF

$TPATH

Print this page
$COMMENT
EOF

	html_quote | $AWK '
	/^--- new/	{ next }
	/^\+\+\+ new/	{ next }
	/^--- old/	{ next }
	/^\*\*\* old/	{ next }
	/^\*\*\*\*/	{ next }
	/^-------/	{ printf "

%s

\n", $0; next } /^\@\@.*\@\@$/ { printf "

\n";
			  printf "%s\n", $0;
			  next}

	/^\*\*\*/	{ printf "
%s\n", $0; next} /^---/ { printf "%s\n", $0; next} /^\+/ {printf "%s\n", $0; next} /^!/ {printf "%s\n", $0; next} /^-/ {printf "%s\n", $0; next} {printf "%s\n", $0; next} ' print "
\n" } # # source_to_html { new | old } # # Process a plain vanilla source file to transform it into an HTML file. # source_to_html() { WHICH=$1 TNAME=$2 print "$HTML$STDHEAD" print "$WHICH $TNAME" print "" print "
"
	html_quote | $AWK '{line += 1 ; printf "%4d %s\n", line, $0 }'
	print "
" } comments_from_mercurial() { fmt=$1 pfile=$PWS/$2 cfile=$CWS/$3 logdir=`dirname $cfile` logf=`basename $cfile` if [ -d $logdir ]; then ( cd $logdir; active=`hg status $logf 2>/dev/null` # If the output from 'hg status' is not empty, it means the file # hasn't been committed, so don't fetch comments. if [[ -z $active ]] ; then if [[ -n $ALL_CREV ]]; then rev_opt= for rev in $ALL_CREV; do rev_opt="$rev_opt --rev $rev" done comm=`hg log $rev_opt --follow --template 'rev {rev} : {desc}\n' $logf` elif [[ -n $FIRST_CREV ]]; then comm=`hg log --rev $FIRST_CREV:tip --follow --template 'rev {rev} : {desc}\n' $logf` else comm=`hg log -l1 --follow --template 'rev {rev} : {desc}\n' $logf` fi else comm="" fi if [[ $fmt == "text" ]]; then print "$comm" return fi print "$comm" | html_quote | bug2url ) fi } # # getcomments {text|html} filepath parentpath # # Fetch the comments depending on what SCM mode we're in. # getcomments() { typeset fmt=$1 typeset p=$2 typeset pp=$3 comments_from_mercurial $fmt $pp $p } # # printCI # # Print out Code Inspection figures similar to sccs-prt(1) format. # function printCI { integer tot=$1 ins=$2 del=$3 mod=$4 unc=$5 typeset str if (( tot == 1 )); then str="line" else str="lines" fi printf '%d %s changed: %d ins; %d del; %d mod; %d unchg' \ $tot $str $ins $del $mod $unc } # # difflines # # Calculate and emit number of added, removed, modified and unchanged lines, # and total lines changed, the sum of added + removed + modified. # function difflines { integer tot mod del ins unc err typeset filename eval $( diff -e $1 $2 | $AWK ' # Change range of lines: N,Nc /^[0-9]*,[0-9]*c$/ { n=split(substr($1,1,length($1)-1), counts, ","); if (n != 2) { error=2 exit; } # # 3,5c means lines 3 , 4 and 5 are changed, a total of 3 lines. # following would be 5 - 3 = 2! Hence +1 for correction. # r=(counts[2]-counts[1])+1; # # Now count replacement lines: each represents a change instead # of a delete, so increment c and decrement r. # while (getline != /^\.$/) { c++; r--; } # # If there were more replacement lines than original lines, # then r will be negative; in this case there are no deletions, # but there are r changes that should be counted as adds, and # since r is negative, subtract it from a and add it to c. # if (r < 0) { a-=r; c+=r; } # # If there were more original lines than replacement lines, then # r will be positive; in this case, increment d by that much. # if (r > 0) { d+=r; } next; } # Change lines: Nc /^[0-9].*c$/ { # The first line is a replacement; any more are additions. if (getline != /^\.$/) { c++; while (getline != /^\.$/) a++; } next; } # Add lines: both Na and N,Na /^[0-9].*a$/ { while (getline != /^\.$/) a++; next; } # Delete range of lines: N,Nd /^[0-9]*,[0-9]*d$/ { n=split(substr($1,1,length($1)-1), counts, ","); if (n != 2) { error=2 exit; } # # 3,5d means lines 3 , 4 and 5 are deleted, a total of 3 lines. # following would be 5 - 3 = 2! Hence +1 for correction. # r=(counts[2]-counts[1])+1; d+=r; next; } # Delete line: Nd. For example 10d says line 10 is deleted. /^[0-9]*d$/ {d++; next} # Should not get here! { error=1; exit; } # Finish off - print results END { printf("tot=%d;mod=%d;del=%d;ins=%d;err=%d\n", (c+d+a), c, d, a, error); }' ) # End of $AWK, Check to see if any trouble occurred. if (( $? > 0 || err > 0 )); then print "Unexpected Error occurred reading" \ "\`diff -e $1 $2\`: \$?=$?, err=" $err return fi # Accumulate totals (( TOTL += tot )) (( TMOD += mod )) (( TDEL += del )) (( TINS += ins )) # Calculate unchanged lines unc=`wc -l < $1` if (( unc > 0 )); then (( unc -= del + mod )) (( TUNC += unc )) fi # print summary print "\c" printCI $tot $ins $del $mod $unc print "" } function outgoing_from_mercurial_forest { hg foutgoing --template 'rev: {rev}\n' $OUTPWS | $FILTER | $AWK ' BEGIN {ntree=0} /^comparing/ {next} /^no changes/ {next} /^searching/ {next} /^\[.*\]$/ {tree=substr($1,2,length($1)-2); trees[ntree++] = tree; revs[tree]=-1; next} /^rev:/ {rev=$2+0; if (revs[tree] == -1 || rev < revs[tree]) { revs[tree] = rev; }; next;} END {for (tree in trees) { rev=revs[trees[tree]]; if (rev > 0) {printf("%s %d\n",trees[tree],rev-1)} }}' | while read LINE do set - $LINE TREE=$1 REV=$2 A=`hg -R $CWS/$TREE log --rev $REV --template '{node}'` FSTAT_OPT="--rev $A" print "Revision: $A $REV" >> $FLIST treestatus $TREE done } function flist_from_mercurial_forest { rm -f $FLIST if [ -z "$Nflag" ]; then print " File list from hg foutgoing $PWS ..." outgoing_from_mercurial_forest HG_LIST_FROM_COMMIT=1 fi if [ ! -f $FLIST ]; then # hg commit hasn't been run see what is lying around print "\n No outgoing, perhaps you haven't commited." print " File list from hg fstatus -mard ...\c" FSTAT_OPT= fstatus HG_LIST_FROM_COMMIT= fi print " Done." } # # Used when dealing with the result of 'hg foutgoing' # When now go down the tree and generate the change list # function treestatus { TREE=$1 HGCMD="hg -R $CWS/$TREE status $FSTAT_OPT" $HGCMD -mdn 2>/dev/null | $FILTER | while read F do echo $TREE/$F done >> $FLIST # Then all the added files # But some of these could have been "moved" or renamed ones or copied ones # so let's make sure we get the proper info # hg status -aC will produce something like: # A subdir/File3 # A subdir/File4 # File4 # A subdir/File5 # The first and last are simple addition while the middle one # is a move/rename or a copy. We can't distinguish from a rename vs a copy # without also getting the status of removed files. The middle case above # is a rename if File4 is also shown a being removed. If File4 is not a # removed file, then the middle case is a copy from File4 to subdir/File4 # FIXME - we're not distinguishing copy from rename $HGCMD -aC | $FILTER | while read LINE; do ldone="" while [ -z "$ldone" ]; do ldone="1" set - $LINE if [ $# -eq 2 -a "$1" == "A" ]; then AFILE=$2 if read LINE2; then set - $LINE2 if [ $# -eq 1 ]; then echo $TREE/$AFILE $TREE/$1 >>$FLIST elif [ $# -eq 2 ]; then echo $TREE/$AFILE >>$FLIST LINE=$LINE2 ldone="" fi else echo $TREE/$AFILE >>$FLIST fi fi done done $HGCMD -rn | $FILTER | while read RFILE; do grep "$TREE/$RFILE" $FLIST >/dev/null if [ $? -eq 1 ]; then echo $TREE/$RFILE >>$FLIST fi done } function fstatus { # # forest extension is still being changed. For instance the output # of fstatus used to no prepend the tree path to filenames, but # this has changed recently. AWK code below does try to handle both # cases # hg fstatus -mdn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK ' /^\[.*\]$/ {tree=substr($1,2,length($1)-2); next} $1 != "" {n=index($1,tree); if (n == 0) { printf("%s/%s\n",tree,$1)} else { printf("%s\n",$1)}}' >> $FLIST # # There is a bug in the output of fstatus -aC on recent versions: it # inserts a space between the name of the tree and the filename of the # old file. e.g.: # # $ hg fstatus -aC # [.] # # [MyWS] # A MyWS/subdir/File2 # MyWS/ File2 # # [MyWS2] # hg fstatus -aC $FSTAT_OPT 2>/dev/null | $FILTER | $AWK ' /^\[.*\]$/ {tree=substr($1,2,length($1)-2); next} /^A .*/ {n=index($2,tree); if (n == 0) { printf("A %s/%s\n",tree,$2)} else { printf("A %s\n",$2)}; next} /^ / {n=index($1,tree); if (n == 0) { printf("%s/%s\n",tree,$1)} else { if (NF == 2) printf("%s/%s\n",tree,$2) else printf("%s\n",$1) }; next} ' | while read LINE; do ldone="" while [ -z "$ldone" ]; do ldone="1" set - $LINE if [ $# -eq 2 -a "$1" == "A" ]; then AFILE=$2 if read LINE2; then set - $LINE2 if [ $# -eq 1 ]; then echo $AFILE $1 >>$FLIST elif [ $# -eq 2 ]; then echo $AFILE >>$FLIST LINE=$LINE2 ldone="" fi else echo $AFILE >>$FLIST fi fi done done hg fstatus -rn $FSTAT_OPT 2>/dev/null | $FILTER | $AWK ' /^\[.*\]$/ {tree=substr($1,2,length($1)-2); next} $1 != "" {n=index($1,tree); if (n == 0) { printf("%s/%s\n",tree,$1)} else { printf("%s\n",$1)}}' | while read RFILE; do grep "$RFILE" $FLIST >/dev/null if [ $? -eq 1 ]; then echo $RFILE >>$FLIST fi done } # # flist_from_mercurial $PWS # # Only local file based repositories are supported at present # since even though we can determine the list from the parent finding # the changes is harder. # # We first look for any outgoing files, this is for when the user has # run hg commit. If we don't find any then we look with hg status. # # We need at least one of default-push or default paths set in .hg/hgrc # If neither are set we don't know who to compare with. function flist_from_mercurial { # if [ "${PWS##ssh://}" != "$PWS" -o \ # "${PWS##http://}" != "$PWS" -o \ # "${PWS##https://}" != "$PWS" ]; then # print "Remote Mercurial repositories not currently supported." # print "Set default and/or default-push to a local repository" # exit # fi if [[ -n $forestflag ]]; then HG_LIST_FROM_COMMIT= flist_from_mercurial_forest else STATUS_REV= if [[ -n $rflag ]]; then STATUS_REV="--rev $PARENT_REV" elif [[ -n $OUTREV ]]; then STATUS_REV="--rev $OUTREV" else # hg commit hasn't been run see what is lying around print "\n No outgoing, perhaps you haven't commited." fi # First let's list all the modified or deleted files hg status $STATUS_REV -mdn | $FILTER > $FLIST # Then all the added files # But some of these could have been "moved" or renamed ones # so let's make sure we get the proper info # hg status -aC will produce something like: # A subdir/File3 # A subdir/File4 # File4 # A subdir/File5 # The first and last are simple addition while the middle one # is a move/rename or a copy. We can't distinguish from a rename vs a copy # without also getting the status of removed files. The middle case above # is a rename if File4 is also shown a being removed. If File4 is not a # removed file, then the middle case is a copy from File4 to subdir/File4 # FIXME - we're not distinguishing copy from rename hg status $STATUS_REV -aC | $FILTER >$FLIST.temp while read LINE; do ldone="" while [ -z "$ldone" ]; do ldone="1" set - $LINE if [ $# -eq 2 -a "$1" == "A" ]; then AFILE=$2 if read LINE2; then set - $LINE2 if [ $# -eq 1 ]; then echo $AFILE $1 >>$FLIST elif [ $# -eq 2 ]; then echo $AFILE >>$FLIST LINE=$LINE2 ldone="" fi else echo $AFILE >>$FLIST fi fi done done < $FLIST.temp hg status $STATUS_REV -rn | $FILTER > $FLIST.temp while read RFILE; do grep "$RFILE" $FLIST >/dev/null if [ $? -eq 1 ]; then echo $RFILE >>$FLIST fi done < $FLIST.temp rm -f $FLIST.temp fi } function env_from_flist { [[ -r $FLIST ]] || return # # Use "eval" to set env variables that are listed in the file # list. Then copy those into our local versions of those # variables if they have not been set already. # eval `sed -e "s/#.*$//" $FLIST | grep = ` [[ -z $codemgr_ws && -n $CODEMGR_WS ]] && codemgr_ws=$CODEMGR_WS # # Check to see if CODEMGR_PARENT is set in the flist file. # [[ -z $codemgr_parent && -n $CODEMGR_PARENT ]] && \ codemgr_parent=$CODEMGR_PARENT } # # detect_scm # # We dynamically test the SCM type; this allows future extensions to # new SCM types # function detect_scm { if hg root >/dev/null ; then print "mercurial" else print "unknown" fi } function look_for_prog { typeset path typeset ppath typeset progname=$1 DEVTOOLS= OS=`uname` if [[ "$OS" == "SunOS" ]]; then DEVTOOLS="/java/devtools/`uname -p`/bin" elif [[ "$OS" == "Linux" ]]; then DEVTOOLS="/java/devtools/linux/bin" fi ppath=$PATH ppath=$ppath:/usr/sfw/bin:/usr/bin:/usr/sbin ppath=$ppath:/opt/teamware/bin:/opt/onbld/bin ppath=$ppath:/opt/onbld/bin/`uname -p` ppath=$ppath:/java/devtools/share/bin:$DEVTOOLS PATH=$ppath prog=`whence $progname` if [[ -n $prog ]]; then print $prog fi } # # Find the parent for $1 # function find_outrev { crev=$1 prev=`hg log -r $crev --template '{parents}\n'` if [[ -z "$prev" ]] then # No specific parent means previous changeset is parent prev=`expr $crev - 1` else # Format is either of the following two: # 546:7df6fcf1183b # 548:16f1915bb5cd 547:ffaa4e775815 prev=`echo $prev | sed -e 's/\([0-9]*\):.*/\1/'` fi print $prev } function extract_ssh_infos { CMD=$1 if expr "$CMD" : 'ssh://[^/]*@' >/dev/null; then ssh_user=`echo $CMD | sed -e 's/ssh:\/\/\(.*\)@.*/\1/'` ssh_host=`echo $CMD | sed -e 's/ssh:\/\/.*@\([^/]*\)\/.*/\1/'` ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/.*@[^/]*\/\(.*\)/\1/'` else ssh_user= ssh_host=`echo $CMD | sed -e 's/ssh:\/\/\([^/]*\)\/.*/\1/'` ssh_dir=`echo $CMD | sed -e 's/ssh:\/\/[^/]*\/\(.*\)/\1/'` fi } function build_old_new_mercurial { olddir=$1 newdir=$2 DIR=$3 F=$4 # # new version of the file. # rm -rf $newdir/$DIR/$F if [ -f $F ]; then cp $F $newdir/$DIR/$F fi # # Old version of the file. # rm -rf $olddir/$DIR/$F if [ -n "$PWS" ]; then if expr "$PWS" : 'ssh://' >/dev/null then extract_ssh_infos $PWS if [ -n "$ssh_user" ]; then parent="ssh -l $ssh_user $ssh_host hg -R $ssh_dir --cwd $ssh_dir" else parent="ssh $ssh_host hg -R $ssh_dir --cwd $ssh_dir" fi else parent="hg -R $PWS --cwd $PWS" fi else parent="" fi if [ -z "$rename" ]; then if [ -n "$rflag" ]; then parentrev=$PARENT_REV elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then parentrev=$OUTREV else if [[ -n $HG_BRANCH ]]; then parentrev=$HG_BRANCH else parentrev="tip" fi fi if [ -n "$parentrev" ]; then if [ -z "$parent" ]; then hg cat --rev $parentrev --output $olddir/$DIR/$F $F 2>/dev/null else # when specifying a workspace we have to provide # the full path $parent cat --rev $parentrev --output $olddir/$DIR/$F $DIR/$F 2>/dev/null fi fi else # It's a rename (or a move), or a copy, so let's make sure we move # to the right directory first, then restore it once done current_dir=`pwd` hg_root=`hg root` cd $CWS if [ -n "$rflag" ]; then parentrev=$PARENT_REV elif [ "$HG_LIST_FROM_COMMIT" -eq 1 ]; then parentrev=$OUTREV fi if [ -z "$parentrev" ]; then parentrev=`hg log -l1 $PDIR/$PF | $AWK -F: '/changeset/ {print $2}'` fi if [ -n "$parentrev" ]; then mkdir -p $olddir/$PDIR if [ -z "$parent" ]; then hg cat -R $hg_root --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null else $parent cat --rev $parentrev --output $olddir/$PDIR/$PF $PDIR/$PF 2>/dev/null fi fi cd $current_dir fi } function build_old_new { if [[ $SCM_MODE == "mercurial" ]]; then build_old_new_mercurial $@ fi } # # Usage message. # function usage { print "Usage:\twebrev [options] webrev [options] ( | - ) Options: -v: Print the version of this tool. -b: Do not ignore changes in the amount of white space. -c : Include link to CR (aka bugid) in the main page. -i : Include in the index.html file. -o : Output webrev to specified directory. -p : Use specified parent wkspc or basis for comparison -u : Use that username instead of 'guessing' one. -m: Forces the use of Mercurial Mercurial only options: -r rev: Compare against a specified revision -N: Skip 'hg outgoing', use only 'hg status' -f: Use the forest extension Arguments: : Optional file containing list of files to include in webrev -: read list of files to include in webrev from standard input Environment: WDIR: Control the output directory. WEBREV_BUGURL: Control the URL prefix for bugids. " exit 2 } # # # Main program starts here # # LANG="C" LC_ALL="C" export LANG LC_ALL trap "rm -f /tmp/$$.* ; exit" 0 1 2 3 15 set +o noclobber [[ -z $WDIFF ]] && WDIFF=`look_for_prog wdiff` [[ -z $CODEREVIEW ]] && CODEREVIEW=`look_for_prog codereview` [[ -z $PS2PDF ]] && PS2PDF=`look_for_prog ps2pdf` [[ -z $PERL ]] && PERL=`look_for_prog perl` [[ -z $SCCS ]] && SCCS=`look_for_prog sccs` [[ -z $AWK ]] && AWK=`look_for_prog nawk` [[ -z $AWK ]] && AWK=`look_for_prog gawk` [[ -z $AWK ]] && AWK=`look_for_prog awk` [[ -z $JAR ]] && JAR=`look_for_prog jar` [[ -z $ZIP ]] && ZIP=`look_for_prog zip` [[ -z $GETENT ]] && GETENT=`look_for_prog getent` [[ -z $WGET ]] && WGET=`look_for_prog wget` if uname | grep CYGWIN >/dev/null then ISWIN=1 # Under windows mercurial outputs '\' instead of '/' FILTER="tr '\\\\' '/'" else FILTER="cat" fi if [[ ! -x $PERL ]]; then print -u2 "Error: No perl interpreter found. Exiting." exit 1 fi # # These aren't fatal, but we want to note them to the user. # # [[ ! -x $CODEREVIEW ]] && print -u2 "WARNING: codereview(1) not found." # [[ ! -x $PS2PDF ]] && print -u2 "WARNING: ps2pdf(1) not found." # [[ ! -x $WDIFF ]] && print -u2 "WARNING: wdiff not found." # Declare global total counters. integer TOTL TINS TDEL TMOD TUNC flist_mode= flist_file= bflag= iflag= oflag= pflag= uflag= Oflag= rflag= Nflag= forestflag= while getopts "c:i:o:p:r:u:mONvfb" opt do case $opt in b) bflag=1;; i) iflag=1 INCLUDE_FILE=$OPTARG;; o) oflag=1 WDIR=$OPTARG;; p) pflag=1 codemgr_parent=$OPTARG;; u) uflag=1 username=$OPTARG;; c) if [[ -z $CRID ]]; then CRID=$OPTARG else CRID="$CRID $OPTARG" fi;; m) SCM_MODE="mercurial";; O) Oflag=1;; # ignored (bugs are now all visible at bugs.openjdk.java.net) N) Nflag=1;; f) forestflag=1;; r) rflag=1 PARENT_REV=$OPTARG;; v) print "$0 version: $WEBREV_UPDATED";; ?) usage;; esac done FLIST=/tmp/$$.flist HG_LIST_FROM_COMMIT= if [[ -n $forestflag && -n $rflag ]]; then print "The -r flag is incompatible with the use of forests" exit 2 fi # # If this manually set as the parent, and it appears to be an earlier webrev, # then note that fact and set the parent to the raw_files/new subdirectory. # if [[ -n $pflag && -d $codemgr_parent/raw_files/new ]]; then parent_webrev="$codemgr_parent" codemgr_parent="$codemgr_parent/raw_files/new" fi shift $(($OPTIND - 1)) if [[ $1 == "-" ]]; then cat > $FLIST flist_mode="stdin" flist_done=1 shift elif [[ -n $1 ]]; then if [[ ! -r $1 ]]; then print -u2 "$1: no such file or not readable" usage fi cat $1 > $FLIST flist_mode="file" flist_file=$1 flist_done=1 shift else flist_mode="auto" fi # # Before we go on to further consider -l and -w, work out which SCM we think # is in use. # if [[ -z $SCM_MODE ]]; then SCM_MODE=`detect_scm $FLIST` fi if [[ $SCM_MODE == "unknown" ]]; then print -u2 "Unable to determine SCM type currently in use." print -u2 "For mercurial: webrev runs 'hg root'." exit 1 fi print -u2 " SCM detected: $SCM_MODE" if [[ $SCM_MODE == "mercurial" ]]; then # # determine Workspace and parent workspace paths # CWS=`hg root | $FILTER` if [[ -n $pflag && -z "$PWS" ]]; then OUTPWS=$codemgr_parent # Let's try to expand it if it's an alias defined in [paths] tmp=`hg path $OUTPWS 2>/dev/null | $FILTER` if [[ -n $tmp ]]; then OUTPWS="$tmp" fi if [[ -n $rflag ]]; then if expr "$codemgr_parent" : 'ssh://.*' >/dev/null; then PWS=$codemgr_parent else PWS=`hg -R "$codemgr_parent" root 2>/dev/null | $FILTER` fi fi fi # # OUTPWS is the parent repository to use when using 'hg outgoing' # if [[ -z $Nflag ]]; then if [[ -n $forestflag ]]; then # # for forest we have to rely on properly set default and # default-push because they can be different from the top one. # unless of course it was explicitly specified with -p if [[ -z $pflag ]]; then OUTPWS= fi else # # Unfortunately mercurial is bugged and doesn't handle # aliases correctly in 'hg path default' # So let's do it ourselves. Sigh... if [[ -z "$OUTPWS" ]]; then OUTPWS=`grep default-push $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER` fi # Still empty, means no default-push if [[ -z "$OUTPWS" ]]; then OUTPWS=`grep 'default =' $CWS/.hg/hgrc | $AWK '{print $3}' | $FILTER` fi # Let's try to expand it if it's an alias defined in [paths] tmp=`hg path $OUTPWS 2>/dev/null | $FILTER` if [[ -n $tmp ]]; then OUTPWS="$tmp" fi fi fi # # OUTPWS may contain username:password, let's make sure we remove the # sensitive information before we print out anything in the HTML # OUTPWS2=$OUTPWS if [[ -n $OUTPWS ]]; then if [[ `expr "$OUTPWS" : '.*://[^/]*@.*'` -gt 0 ]]; then # Remove everything between '://' and '@' OUTPWS2=`echo $OUTPWS | sed -e 's/\(.*:\/\/\).*@\(.*\)/\1\2/'` fi fi if [[ -z $HG_BRANCH ]]; then HG_BRANCH=`hg branch` if [ "$HG_BRANCH" == "default" ]; then # # 'default' means no particular branch, so let's cancel that # HG_BRANCH= fi fi if [[ -z $forestflag ]]; then if [[ -z $Nflag ]]; then # # If no "-N", always do "hg outgoing" against parent # repository to determine list of outgoing revisions. # ALL_CREV=`hg outgoing -q --template '{rev}\n' $OUTPWS | sort -n` if [[ -n $ALL_CREV ]]; then FIRST_CREV=`echo "$ALL_CREV" | head -1` # # If no "-r", choose revision to compare against by # finding the latest revision not in the outgoing list. # if [[ -z $rflag ]]; then OUTREV=`find_outrev "$FIRST_CREV"` if [[ -n $OUTREV ]]; then HG_LIST_FROM_COMMIT=1 fi fi fi elif [[ -n $rflag ]]; then # # If skipping "hg outgoing" but still comparing against a # specific revision (not the tip), set revision for comment # accumulation. # FIRST_CREV=`hg log --rev $PARENT_REV --template '{rev}'` FIRST_CREV=`expr $FIRST_CREV + 1` fi fi #Let's check if a merge is needed, if so, issue a warning PREV=`hg parent | grep '^tag:.*tip$'` if [[ -z $PREV ]]; then print "WARNING: parent rev is not tip. Maybe an update or merge is needed" fi fi if [[ $flist_mode == "stdin" ]]; then print -u2 " File list from: standard input" elif [[ $flist_mode == "file" ]]; then print -u2 " File list from: $flist_file" fi if [[ $# -gt 0 ]]; then print -u2 "WARNING: unused arguments: $*" fi if [[ $SCM_MODE == "mercurial" ]]; then if [[ -z $flist_done ]]; then flist_from_mercurial $PWS fi fi # # If the user didn't specify a -i option, check to see if there is a # webrev-info file in the workspace directory. # if [[ -z $iflag && -r "$CWS/webrev-info" ]]; then iflag=1 INCLUDE_FILE="$CWS/webrev-info" fi if [[ -n $iflag ]]; then if [[ ! -r $INCLUDE_FILE ]]; then print -u2 "include file '$INCLUDE_FILE' does not exist or is" \ "not readable." exit 1 else # # $INCLUDE_FILE may be a relative path, and the script alters # PWD, so we just stash a copy in /tmp. # cp $INCLUDE_FILE /tmp/$$.include fi fi # # Output directory. # if [[ -z $WDIR ]]; then WDIR=$CWS/webrev else # If the output directory doesn't end with '/webrev' or '/webrev/' # then add '/webrev'. This is for backward compatibility if ! expr $WDIR : '.*/webrev/\?$' >/dev/null then WDIR=$WDIR/webrev fi fi # WDIR=${WDIR:-$CWS/webrev} # # Name of the webrev, derived from the workspace name; in the # future this could potentially be an option. # # Let's keep what's after the last '/' WNAME=${CWS##*/} # # If WDIR doesn't start with '/' or 'x:' prepend the current dir # if [ ${WDIR%%/*} ]; then if [[ -n $ISWIN ]]; then if [ ${WDIR%%[A-Za-z]:*} ]; then WDIR=$PWD/$WDIR fi else WDIR=$PWD/$WDIR fi fi if [[ ! -d $WDIR ]]; then mkdir -p $WDIR [[ $? != 0 ]] && exit 1 fi # # Summarize what we're going to do. # print " Workspace: $CWS" if [[ -n $parent_webrev ]]; then print "Compare against: webrev at $parent_webrev" elif [[ -n $OUTPWS2 ]]; then print "Compare against: $OUTPWS2" fi if [[ -n $HG_BRANCH ]]; then print " Branch: $HG_BRANCH" fi if [[ -n $rflag ]]; then print "Compare against version: $PARENT_REV" fi [[ -n $INCLUDE_FILE ]] && print " Including: $INCLUDE_FILE" print " Output to: $WDIR" # # Save the file list in the webrev dir # [[ ! $FLIST -ef $WDIR/file.list ]] && cp $FLIST $WDIR/file.list # # Bug IDs will be replaced by a URL. Order of precedence # is: default location, $WEBREV_BUGURL # BUGURL='https://bugs.openjdk.java.net/browse/' [[ -n $WEBREV_BUGURL ]] && BUGURL="$WEBREV_BUGURL" IDPREFIX='JDK-' rm -f $WDIR/$WNAME.patch rm -f $WDIR/$WNAME.changeset rm -f $WDIR/$WNAME.ps rm -f $WDIR/$WNAME.pdf touch $WDIR/$WNAME.patch print " Output Files:" # # Clean up the file list: Remove comments, blank lines and env variables. # sed -e "s/#.*$//" -e "/=/d" -e "/^[ ]*$/d" $FLIST > /tmp/$$.flist.clean FLIST=/tmp/$$.flist.clean # # Clean up residual raw files # if [ -d $WDIR/raw_files ]; then rm -rf $WDIR/raw_files 2>/dev/null fi # # Should we ignore changes in white spaces when generating diffs? # if [[ -n $bflag ]]; then DIFFOPTS="-t" else DIFFOPTS="-bt" fi # # First pass through the files: generate the per-file webrev HTML-files. # while read LINE do set - $LINE P=$1 if [[ $1 == "Revision:" ]]; then OUTREV=$2 continue fi # # Normally, each line in the file list is just a pathname of a # file that has been modified or created in the child. A file # that is renamed in the child workspace has two names on the # line: new name followed by the old name. # oldname="" oldpath="" rename= if [[ $# -eq 2 ]]; then PP=$2 # old filename oldname=" (was $PP)" oldpath="$PP" rename=1 PDIR=${PP%/*} if [[ $PDIR == $PP ]]; then PDIR="." # File at root of workspace fi PF=${PP##*/} DIR=${P%/*} if [[ $DIR == $P ]]; then DIR="." # File at root of workspace fi F=${P##*/} else DIR=${P%/*} if [[ "$DIR" == "$P" ]]; then DIR="." # File at root of workspace fi F=${P##*/} PP=$P PDIR=$DIR PF=$F fi # Make the webrev directory if necessary as it may have been # removed because it was empty if [ ! -d $CWS/$DIR ]; then mkdir -p $CWS/$DIR fi COMM=`getcomments html $P $PP` print "\t$P$oldname\n\t\t\c" # Make the webrev mirror directory if necessary mkdir -p $WDIR/$DIR # cd to the directory so the names are short cd $CWS/$DIR # # We stash old and new files into parallel directories in /tmp # and do our diffs there. This makes it possible to generate # clean looking diffs which don't have absolute paths present. # olddir=$WDIR/raw_files/old newdir=$WDIR/raw_files/new mkdir -p $olddir mkdir -p $newdir mkdir -p $olddir/$PDIR mkdir -p $newdir/$DIR build_old_new $olddir $newdir $DIR $F if [[ ! -f $F && ! -f $olddir/$DIR/$F ]]; then print "*** Error: file not in parent or child" continue fi cd $WDIR/raw_files ofile=old/$PDIR/$PF nfile=new/$DIR/$F mv_but_nodiff= cmp $ofile $nfile > /dev/null 2>&1 if [[ $? == 0 && $rename == 1 ]]; then mv_but_nodiff=1 fi # # Cleaning up # rm -f $WDIR/$DIR/$F.cdiff.html rm -f $WDIR/$DIR/$F.udiff.html rm -f $WDIR/$DIR/$F.wdiff.html rm -f $WDIR/$DIR/$F.sdiff.html rm -f $WDIR/$DIR/$F-.html rm -f $WDIR/$DIR/$F.html its_a_jar= if expr $F : '.*\.jar' \| $F : '.*\.zip' >/dev/null; then its_a_jar=1 # It's a JAR or ZIP file, let's do it differently if [[ -z $JAR ]]; then print "No access to jar, so can't produce diffs for jar or zip files" else if [ -f $ofile ]; then $JAR -tvf $ofile >"$ofile".lst fi if [ -f $nfile ]; then $JAR -tvf $nfile >"$nfile".lst fi if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then ${CDIFFCMD:-diff -bt -C 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.cdiff diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \ > $WDIR/$DIR/$F.cdiff.html print " cdiffs\c" ${UDIFFCMD:-diff -bt -U 5} $ofile.lst $nfile.lst > $WDIR/$DIR/$F.udiff diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \ > $WDIR/$DIR/$F.udiff.html print " udiffs\c" if [[ -x $WDIFF ]]; then $WDIFF -c "$COMM" \ -t "$WNAME Wdiff $DIR/$F" $ofile.lst $nfile.lst > \ $WDIR/$DIR/$F.wdiff.html 2>/dev/null if [[ $? -eq 0 ]]; then print " wdiffs\c" else print " wdiffs[fail]\c" fi fi sdiff_to_html $ofile $nfile $F $DIR "$COMM" \ > $WDIR/$DIR/$F.sdiff.html print " sdiffs\c" print " frames\c" rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then # renamed file: may also have differences difflines $ofile.lst $nfile.lst > $WDIR/$DIR/$F.count elif [[ -f $nfile ]]; then # new file: count added lines difflines /dev/null $nfile.lst > $WDIR/$DIR/$F.count elif [[ -f $ofile ]]; then # old file: count deleted lines difflines $ofile.lst /dev/null > $WDIR/$DIR/$F.count fi fi else # # If we have old and new versions of the file then run the # appropriate diffs. This is complicated by a couple of factors: # # - renames must be handled specially: we emit a 'remove' # diff and an 'add' diff # - new files and deleted files must be handled specially # - Solaris patch(1m) can't cope with file creation # (and hence renames) as of this writing. # - To make matters worse, gnu patch doesn't interpret the # output of Solaris diff properly when it comes to # adds and deletes. We need to do some "cleansing" # transformations: # [to add a file] @@ -1,0 +X,Y @@ --> @@ -0,0 +X,Y @@ # [to del a file] @@ -X,Y +1,0 @@ --> @@ -X,Y +0,0 @@ # cleanse_rmfile="sed 's/^\(@@ [0-9+,-]*\) [0-9+,-]* @@$/\1 +0,0 @@/'" cleanse_newfile="sed 's/^@@ [0-9+,-]* \([0-9+,-]* @@\)$/@@ -0,0 \1/'" if [[ ! "$HG_LIST_FROM_COMMIT" -eq 1 || ! $flist_mode == "auto" ]]; then # Only need to generate a patch file here if there are no commits in outgoing # or if we've specified a file list rm -f $WDIR/$DIR/$F.patch if [[ -z $rename ]]; then if [ ! -f $ofile ]; then diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \ > $WDIR/$DIR/$F.patch elif [ ! -f $nfile ]; then diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \ > $WDIR/$DIR/$F.patch else diff -u $ofile $nfile > $WDIR/$DIR/$F.patch fi else diff -u $ofile /dev/null | sh -c "$cleanse_rmfile" \ > $WDIR/$DIR/$F.patch diff -u /dev/null $nfile | sh -c "$cleanse_newfile" \ >> $WDIR/$DIR/$F.patch fi # # Tack the patch we just made onto the accumulated patch for the # whole wad. # cat $WDIR/$DIR/$F.patch >> $WDIR/$WNAME.patch fi print " patch\c" if [[ -f $ofile && -f $nfile && -z $mv_but_nodiff ]]; then ${CDIFFCMD:-diff -bt -C 5} $ofile $nfile > $WDIR/$DIR/$F.cdiff diff_to_html $F $DIR/$F "C" "$COMM" < $WDIR/$DIR/$F.cdiff \ > $WDIR/$DIR/$F.cdiff.html print " cdiffs\c" ${UDIFFCMD:-diff -bt -U 5} $ofile $nfile > $WDIR/$DIR/$F.udiff diff_to_html $F $DIR/$F "U" "$COMM" < $WDIR/$DIR/$F.udiff \ > $WDIR/$DIR/$F.udiff.html print " udiffs\c" if [[ -x $WDIFF ]]; then $WDIFF -c "$COMM" \ -t "$WNAME Wdiff $DIR/$F" $ofile $nfile > \ $WDIR/$DIR/$F.wdiff.html 2>/dev/null if [[ $? -eq 0 ]]; then print " wdiffs\c" else print " wdiffs[fail]\c" fi fi sdiff_to_html $ofile $nfile $F $DIR "$COMM" \ > $WDIR/$DIR/$F.sdiff.html print " sdiffs\c" print " frames\c" rm -f $WDIR/$DIR/$F.cdiff $WDIR/$DIR/$F.udiff difflines $ofile $nfile > $WDIR/$DIR/$F.count elif [[ -f $ofile && -f $nfile && -n $mv_but_nodiff ]]; then # renamed file: may also have differences difflines $ofile $nfile > $WDIR/$DIR/$F.count elif [[ -f $nfile ]]; then # new file: count added lines difflines /dev/null $nfile > $WDIR/$DIR/$F.count elif [[ -f $ofile ]]; then # old file: count deleted lines difflines $ofile /dev/null > $WDIR/$DIR/$F.count fi fi # # Now we generate the postscript for this file. We generate diffs # only in the event that there is delta, or the file is new (it seems # tree-killing to print out the contents of deleted files). # if [[ -f $nfile ]]; then ocr=$ofile [[ ! -f $ofile ]] && ocr=/dev/null if [[ -z $mv_but_nodiff ]]; then textcomm=`getcomments text $P $PP` if [[ -x $CODEREVIEW ]]; then $CODEREVIEW -y "$textcomm" \ -e $ocr $nfile \ > /tmp/$$.psfile 2>/dev/null && cat /tmp/$$.psfile >> $WDIR/$WNAME.ps if [[ $? -eq 0 ]]; then print " ps\c" else print " ps[fail]\c" fi fi fi fi if [[ -f $ofile && -z $mv_but_nodiff ]]; then if [[ -n $its_a_jar ]]; then source_to_html Old $P < $ofile.lst > $WDIR/$DIR/$F-.html else source_to_html Old $P < $ofile > $WDIR/$DIR/$F-.html fi print " old\c" fi if [[ -f $nfile ]]; then if [[ -n $its_a_jar ]]; then source_to_html New $P < $nfile.lst > $WDIR/$DIR/$F.html else source_to_html New $P < $nfile > $WDIR/$DIR/$F.html fi print " new\c" fi print done < $FLIST # Create the new style mercurial patch here using hg export -r [all-revs] -g -o $CHANGESETPATH if [[ $SCM_MODE == "mercurial" ]]; then if [[ "$HG_LIST_FROM_COMMIT" -eq 1 && $flist_mode == "auto" ]]; then EXPORTCHANGESET="$WNAME.changeset" CHANGESETPATH=${WDIR}/${EXPORTCHANGESET} rm -f $CHANGESETPATH touch $CHANGESETPATH if [[ -n $ALL_CREV ]]; then rev_opt= for rev in $ALL_CREV; do rev_opt="$rev_opt --rev $rev" done elif [[ -n $FIRST_CREV ]]; then rev_opt="--rev $FIRST_CREV" fi if [[ -n $rev_opt ]]; then (cd $CWS;hg export -g $rev_opt -o $CHANGESETPATH) echo "Created changeset: $CHANGESETPATH" 1>&2 # Use it in place of the jdk.patch created above rm -f $WDIR/$WNAME.patch fi set +x fi fi frame_nav_js > $WDIR/ancnav.js frame_navigation > $WDIR/ancnav.html if [[ -f $WDIR/$WNAME.ps && -x $CODEREVIEW && -x $PS2PDF ]]; then print " Generating PDF: \c" fix_postscript $WDIR/$WNAME.ps | $PS2PDF - > $WDIR/$WNAME.pdf print "Done." fi # Now build the index.html file that contains # links to the source files and their diffs. cd $CWS # Save total changed lines for Code Inspection. print "$TOTL" > $WDIR/TotalChangedLines print " index.html: \c" INDEXFILE=$WDIR/index.html exec 3<&1 # duplicate stdout to FD3. exec 1<&- # Close stdout. exec > $INDEXFILE # Open stdout to index file. print "$HTML" print "" print "$STDHEAD" print "$WNAME" print "" print "" print "
" print "

Code Review for $WNAME

" print "" if [[ -z $uflag ]]; then if [[ $SCM_MODE == "mercurial" ]]; then # # Let's try to extract the user name from the .hgrc file # username=`grep '^username' $HOME/.hgrc | sed 's/^username[ ]*=[ ]*\(.*\)/\1/'` fi if [[ -z $username ]]; then # # Figure out the username and gcos name. To maintain compatibility # with passwd(4), we must support '&' substitutions. # username=`id | cut -d '(' -f 2 | cut -d ')' -f 1` if [[ -x $GETENT ]]; then realname=`$GETENT passwd $username | cut -d':' -f 5 | cut -d ',' -f 1` fi userupper=`print "$username" | sed 's/\<./\u&/g'` realname=`print $realname | sed s/\&/$userupper/` fi fi date="on `date`" if [[ -n "$username" && -n "$realname" ]]; then print "" print "" elif [[ -n "$username" ]]; then print "" fi print "" if [[ -n $parent_webrev ]]; then print "" if [[ -n $rflag ]]; then print "" elif [[ -n $OUTREV ]]; then if [[ -z $forestflag ]]; then print "" fi fi if [[ -n $HG_BRANCH ]]; then print "" fi print "" if [[ -f $WDIR/$WNAME.patch ]]; then print "" elif [[ -f $CHANGESETPATH ]]; then print "" fi if [[ -f $WDIR/$WNAME.pdf ]]; then print "" fi if [[ -n "$iflag" ]]; then print "" fi # Add links to referenced CRs, if any # URL has a like: # <title>[#JDK-8024688] b106-lambda: j.u.Map.merge doesn't work as specified if contains key:null pair - Java Bug System # we format this to: # JDK-8024688: b106-lambda: j.u.Map.merge doesn't work as specified if contains key:null pair if [[ -n $CRID ]]; then for id in $CRID do #add "JDK-" to raw bug id for openjdk.java.net links. id=`echo ${id} | sed 's/^\([0-9]\{5,\}\)$/JDK-\1/'` print "
Prepared by:$realname ($username) $date
Prepared by:$username $date
Workspace:$CWS
Compare against:" print "webrev at $parent_webrev" else if [[ -n $OUTPWS2 ]]; then print "
Compare against:" print "$OUTPWS2" fi fi print "
Compare against version:$PARENT_REV
Compare against version:$OUTREV
Branch:$HG_BRANCH
Summary of changes:" printCI $TOTL $TINS $TDEL $TMOD $TUNC print "
Patch of changes:" print "$WNAME.patch
Changeset:" print "$EXPORTCHANGESET
Printable review:" print "$WNAME.pdf
Author comments:
" cat /tmp/$$.include print "
Bug id:" url="${BUGURL}${id}" if [[ -n $WGET ]]; then msg=`$WGET --timeout=10 --tries=1 -q $url -O - | grep '' | sed 's/<title>\[#\(.*\)\] \(.*\) - Java Bug System<\/title>/\1 : \2/' | html_dequote | html_quote` fi if [[ -z $msg ]]; then msg="${id}" fi print "<a href=\"$url\">$msg</a>" print "</td></tr>" done fi print "<tr><th>Legend:</th><td>" print "<b>Modified file</b><br><font color=red><b>Deleted file</b></font><br><font color=green><b>New file</b></font></td></tr>" print "</table>" print "</div>" # # Second pass through the files: generate the rest of the index file # while read LINE do set - $LINE if [[ $1 == "Revision:" ]]; then FIRST_CREV=`expr $3 + 1` continue fi P=$1 if [[ $# == 2 ]]; then PP=$2 oldname=" <i>(was $PP)</i>" else PP=$P oldname="" fi DIR=${P%/*} if [[ $DIR == $P ]]; then DIR="." # File at root of workspace fi # Avoid processing the same file twice. # It's possible for renamed files to # appear twice in the file list F=$WDIR/$P print "<p><code>" # If there's a diffs file, make diffs links NODIFFS= if [[ -f $F.cdiff.html ]]; then print "<a href=\"$P.cdiff.html\">Cdiffs</a>" print "<a href=\"$P.udiff.html\">Udiffs</a>" if [[ -f $F.wdiff.html && -x $WDIFF ]]; then print "<a href=\"$P.wdiff.html\">Wdiffs</a>" fi print "<a href=\"$P.sdiff.html\">Sdiffs</a>" print "<a href=\"$P.frames.html\">Frames</a>" else NODIFFS=1 print " ------ ------ ------" if [[ -x $WDIFF ]]; then print " ------" fi print " ------" fi # If there's an old file, make the link NOOLD= if [[ -f $F-.html ]]; then print "<a href=\"$P-.html\">Old</a>" else NOOLD=1 print " ---" fi # If there's an new file, make the link NONEW= if [[ -f $F.html ]]; then print "<a href=\"$P.html\">New</a>" else NONEW=1 print " ---" fi if [[ -f $F.patch ]]; then print "<a href=\"$P.patch\">Patch</a>" else print " -----" fi if [[ -f $WDIR/raw_files/new/$P ]]; then print "<a href=\"raw_files/new/$P\">Raw</a>" else print " ---" fi print "</code>" if [[ -n $NODIFFS && -z $oldname ]]; then if [[ -n $NOOLD ]]; then print "<font color=green><b>$P</b></font>" elif [[ -n $NONEW ]]; then print "<font color=red><b>$P</b></font>" fi else print "<b>$P</b> $oldname" fi print "</p><blockquote>\c" # Insert delta comments if any comments=`getcomments html $P $PP` if [ -n "$comments" ]; then print "<pre>$comments</pre>" fi # Add additional comments comment print "<!-- Add comments to explain changes in $P here -->" # Add count of changes. if [[ -f $F.count ]]; then cat $F.count rm $F.count fi print "</blockquote>" done < $FLIST print print print "<hr />" print "<p style=\"font-size: small\">" print "This code review page was prepared using <b>$0</b>" print "(vers $WEBREV_UPDATED)." print "</body>" print "</html>" if [[ -n $ZIP ]]; then # Let's generate a zip file for convenience cd $WDIR/.. if [ -f webrev.zip ]; then rm webrev.zip fi $ZIP -r webrev webrev >/dev/null 2>&1 fi exec 1<&- # Close FD 1. exec 1<&3 # dup FD 3 to restore stdout. exec 3<&- # close FD 3. print "Done." print "Output to: $WDIR"