| #!/usr/bin/perl |
| # Copyright (C) 2012-2014 Free Software Foundation, Inc. |
| # |
| # This file is part of GCC. |
| # |
| # GCC is free software; you can redistribute it and/or modify |
| # it under the terms of the GNU General Public License as published by |
| # the Free Software Foundation; either version 3, or (at your option) |
| # any later version. |
| # |
| # GCC is distributed in the hope that it will be useful, |
| # but WITHOUT ANY WARRANTY; without even the implied warranty of |
| # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| # GNU General Public License for more details. |
| # |
| # You should have received a copy of the GNU General Public License |
| # along with GCC; see the file COPYING. If not, write to |
| # the Free Software Foundation, 51 Franklin Street, Fifth Floor, |
| # Boston, MA 02110-1301, USA. |
| |
| # This script parses a .diff file generated with 'diff -up' or 'diff -cp' |
| # and adds a skeleton ChangeLog file to the file. It does not try to be |
| # very smart when parsing function names, but it produces a reasonable |
| # approximation. |
| # |
| # Author: Diego Novillo <dnovillo@google.com> and |
| # Cary Coutant <ccoutant@google.com> |
| |
| # Change these settings to reflect your profile. |
| $username = $ENV{'USER'}; |
| $name = `finger $username | grep -o 'Name: .*'`; |
| @n = split(/: /, $name); |
| $name = @n[1]; chop($name); |
| $addr = $username . "\@my.domain.org"; |
| $date = `date +%Y-%m-%d`; chop ($date); |
| |
| $gcc_root = $0; |
| $gcc_root =~ s/[^\\\/]+$/../; |
| chdir $gcc_root; |
| |
| |
| #----------------------------------------------------------------------------- |
| # Program starts here. You should not need to edit anything below this |
| # line. |
| #----------------------------------------------------------------------------- |
| if ($#ARGV != 0) { |
| $prog = `basename $0`; chop ($prog); |
| print "usage: $prog file.diff\n\n"; |
| print "Adds a ChangeLog template to the start of file.diff\n"; |
| print "It assumes that file.diff has been created with -up or -cp.\n"; |
| exit 1; |
| } |
| |
| $diff = $ARGV[0]; |
| $dir = `dirname $diff`; chop ($dir); |
| $basename = `basename $diff`; chop ($basename); |
| $hdrline = "$date $name <$addr>"; |
| |
| sub get_clname ($) { |
| return ('ChangeLog', $_[0]) if ($_[0] !~ /[\/\\]/); |
| |
| my $dirname = $_[0]; |
| while ($dirname) { |
| my $clname = "$dirname/ChangeLog"; |
| if (-f $clname) { |
| my $relname = substr ($_[0], length ($dirname) + 1); |
| return ($clname, $relname); |
| } else { |
| $dirname =~ s/[\/\\]?[^\/\\]*$//; |
| } |
| } |
| |
| return ('Unknown ChangeLog', $_[0]); |
| } |
| |
| sub remove_suffixes ($) { |
| my $filename = $_[0]; |
| $filename =~ s/^[ab]\///; |
| $filename =~ s/\.jj$//; |
| return $filename; |
| } |
| |
| # Check if line is a top-level declaration. |
| # TODO: ignore preprocessor directives except maybe #define ? |
| sub is_top_level { |
| my ($function, $is_context_diff) = (@_); |
| if ($is_context_diff) { |
| $function =~ s/^..//; |
| } else { |
| $function =~ s/^.//; |
| } |
| return $function && $function !~ /^[\s{}]/; |
| } |
| |
| # For every file in the .diff print all the function names in ChangeLog |
| # format. |
| %cl_entries = (); |
| $change_msg = undef; |
| $look_for_funs = 0; |
| $clname = get_clname(''); |
| open (DFILE, $diff) or die "Could not open file $diff for reading"; |
| chomp (my @diff_lines = <DFILE>); |
| close (DFILE); |
| $line_idx = 0; |
| foreach (@diff_lines) { |
| # Stop processing functions if we found a new file. |
| # Remember both left and right names because one may be /dev/null. |
| # Don't be fooled by line markers in case of context diff. |
| if (!/\*\*\*$/ && /^[+*][+*][+*] +(\S+)/) { |
| $left = remove_suffixes ($1); |
| $look_for_funs = 0; |
| } |
| if (!/---$/ && /^--- +(\S+)?/) { |
| $right = remove_suffixes ($1); |
| $look_for_funs = 0; |
| } |
| |
| # Check if the body of diff started. |
| # We should now have both left and right name, |
| # so we can decide filename. |
| |
| if ($left && (/^\*{15}/ || /^@@ /)) { |
| # If we have not seen any function names in the previous file (ie, |
| # $change_msg is empty), we just write out a ':' before starting the next |
| # file. |
| if ($clname) { |
| $cl_entries{$clname} .= $change_msg ? "$change_msg" : ":\n"; |
| } |
| |
| if ($left eq $right) { |
| $filename = $left; |
| } elsif($left eq '/dev/null') { |
| $filename = $right; |
| } elsif($right eq '/dev/null') { |
| $filename = $left; |
| } else { |
| print STDERR "Error: failed to parse diff for $left and $right\n"; |
| exit 1; |
| } |
| $left = $right = undef; |
| ($clname, $relname) = get_clname ($filename); |
| $cl_entries{$clname} .= "\t* $relname"; |
| $change_msg = ''; |
| $look_for_funs = $filename =~ '\.(c|cpp|C|cc|h|inc|def)$'; |
| } |
| |
| # Context diffs have extra whitespace after first char; |
| # remove it to make matching easier. |
| if ($is_context_diff) { |
| s/^([-+! ]) /\1/; |
| } |
| |
| # Remember the last line in a diff block that might start |
| # a new function. |
| if (/^[-+! ]([a-zA-Z0-9_].*)/) { |
| $save_fn = $1; |
| } |
| |
| # Check if file is newly added. |
| # Two patterns: for context and unified diff. |
| if (/^\*\*\* 0 \*\*\*\*/ |
| || /^@@ -0,0 \+1.* @@/) { |
| $change_msg = $filename =~ /testsuite.*(?<!\.exp)$/ ? ": New test.\n" : ": New file.\n"; |
| $look_for_funs = 0; |
| } |
| |
| # Check if file was removed. |
| # Two patterns: for context and unified diff. |
| if (/^--- 0 ----/ |
| || /^@@ -1.* \+0,0 @@/) { |
| $change_msg = ": Remove.\n"; |
| $look_for_funs = 0; |
| } |
| |
| # Mark if we met doubtfully changed function. |
| $doubtfunc = 0; |
| if ($diff_lines[$line_idx] =~ /^@@ .* @@ ([a-zA-Z0-9_].*)/) { |
| $doubtfunc = 1; |
| $is_context_diff = 0; |
| } |
| elsif ($diff_lines[$line_idx] =~ /^\*\*\*\*\*\** ([a-zA-Z0-9_].*)/) { |
| $doubtfunc = 1; |
| $is_context_diff = 1; |
| } |
| |
| # If we find a new function, print it in brackets. Special case if |
| # this is the first function in a file. |
| # |
| # Note that we don't try too hard to find good matches. This should |
| # return a superset of the actual set of functions in the .diff file. |
| # |
| # The first pattern works with context diff files (diff -c). The |
| # second pattern works with unified diff files (diff -u). |
| # |
| # The third pattern looks for the starts of functions or classes |
| # within a diff block both for context and unified diff files. |
| |
| if ($look_for_funs |
| && (/^\*\*\*\*\*\** ([a-zA-Z0-9_].*)/ |
| || /^@@ .* @@ ([a-zA-Z0-9_].*)/ |
| || /^[-+! ](\{)/)) |
| { |
| $_ = $1; |
| my $fn; |
| if (/^\{/) { |
| # Beginning of a new function. |
| $_ = $save_fn; |
| } else { |
| $save_fn = ""; |
| } |
| if (/;$/) { |
| # No usable function name found. |
| } elsif (/^((class|struct|union|enum) [a-zA-Z0-9_]+)/) { |
| # Discard stuff after the class/struct/etc. tag. |
| $fn = $1; |
| } elsif (/([a-zA-Z0-9_][^(]*)\(/) { |
| # Discard template and function parameters. |
| $fn = $1; |
| 1 while ($fn =~ s/<[^<>]*>//); |
| $fn =~ s/[ \t]*$//; |
| } |
| # Check is function really modified |
| $no_real_change = 0; |
| if ($doubtfunc) { |
| $idx = $line_idx; |
| # Skip line info in context diffs. |
| while ($is_context_diff && $diff_lines[$idx + 1] =~ /^[-\*]{3} [0-9]/) { |
| ++$idx; |
| } |
| # Check all lines till the first change |
| # for the presence of really changed function |
| do { |
| ++$idx; |
| $no_real_change = is_top_level ($diff_lines[$idx], $is_context_diff); |
| } while (!$no_real_change && ($diff_lines[$idx] !~ /^[-+!]/)) |
| } |
| if ($fn && !$seen_names{$fn} && !$no_real_change) { |
| # If this is the first function in the file, we display it next |
| # to the filename, so we need an extra space before the opening |
| # brace. |
| if (!$change_msg) { |
| $change_msg .= " "; |
| } else { |
| $change_msg .= "\t"; |
| } |
| |
| $change_msg .= "($fn):\n"; |
| $seen_names{$fn} = 1; |
| } |
| } |
| $line_idx++; |
| } |
| |
| # If we have not seen any function names (ie, $change_msg is empty), we just |
| # write out a ':'. This happens when there is only one file with no |
| # functions. |
| $cl_entries{$clname} .= $change_msg ? "$change_msg\n" : ":\n"; |
| |
| $temp = `mktemp /tmp/$basename.XXXXXX` || exit 1; chop ($temp); |
| open (CLFILE, ">$temp") or die "Could not open file $temp for writing"; |
| |
| foreach my $clname (keys %cl_entries) { |
| print CLFILE "$clname:\n\n$hdrline\n\n$cl_entries{$clname}\n"; |
| } |
| |
| # Concatenate the ChangeLog template and the original .diff file. |
| system ("cat $diff >>$temp && mv $temp $diff") == 0 |
| or die "Could not add the ChangeLog entry to $diff"; |
| |
| exit 0; |