xref: /netbsd-src/external/gpl3/gcc/dist/contrib/legacy/mklog.pl (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1*b1e83836Smrg#!/usr/bin/perl
2*b1e83836Smrg# Copyright (C) 2012-2017 Free Software Foundation, Inc.
3*b1e83836Smrg#
4*b1e83836Smrg# This file is part of GCC.
5*b1e83836Smrg#
6*b1e83836Smrg# GCC is free software; you can redistribute it and/or modify
7*b1e83836Smrg# it under the terms of the GNU General Public License as published by
8*b1e83836Smrg# the Free Software Foundation; either version 3, or (at your option)
9*b1e83836Smrg# any later version.
10*b1e83836Smrg#
11*b1e83836Smrg# GCC is distributed in the hope that it will be useful,
12*b1e83836Smrg# but WITHOUT ANY WARRANTY; without even the implied warranty of
13*b1e83836Smrg# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*b1e83836Smrg# GNU General Public License for more details.
15*b1e83836Smrg#
16*b1e83836Smrg# You should have received a copy of the GNU General Public License
17*b1e83836Smrg# along with GCC; see the file COPYING.  If not, write to
18*b1e83836Smrg# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
19*b1e83836Smrg# Boston, MA 02110-1301, USA.
20*b1e83836Smrg
21*b1e83836Smrg# This script parses a .diff file generated with 'diff -up' or 'diff -cp'
22*b1e83836Smrg# and adds a skeleton ChangeLog file to the file. It does not try to be
23*b1e83836Smrg# very smart when parsing function names, but it produces a reasonable
24*b1e83836Smrg# approximation.
25*b1e83836Smrg#
26*b1e83836Smrg# Author: Diego Novillo <dnovillo@google.com> and
27*b1e83836Smrg#         Cary Coutant <ccoutant@google.com>
28*b1e83836Smrg
29*b1e83836Smrguse File::Temp;
30*b1e83836Smrguse File::Copy qw(cp mv);
31*b1e83836Smrg
32*b1e83836Smrg$date = `date +%Y-%m-%d`; chop ($date);
33*b1e83836Smrg
34*b1e83836Smrg$dot_mklog_format_msg =
35*b1e83836Smrg    "The .mklog format is:\n"
36*b1e83836Smrg    . "NAME = ...\n"
37*b1e83836Smrg    . "EMAIL = ...\n";
38*b1e83836Smrg
39*b1e83836Smrg# Create a .mklog to reflect your profile, if necessary.
40*b1e83836Smrgmy $conf = "$ENV{HOME}/.mklog";
41*b1e83836Smrgif (-f "$conf") {
42*b1e83836Smrg    open (CONF, "$conf")
43*b1e83836Smrg	or die "Could not open file '$conf' for reading: $!\n";
44*b1e83836Smrg    while (<CONF>) {
45*b1e83836Smrg	if (m/^\s*NAME\s*=\s*(.*?)\s*$/) {
46*b1e83836Smrg	    $name = $1;
47*b1e83836Smrg	} elsif (m/^\s*EMAIL\s*=\s*(.*?)\s*$/) {
48*b1e83836Smrg	    $addr = $1;
49*b1e83836Smrg	}
50*b1e83836Smrg    }
51*b1e83836Smrg    if (!($name && $addr)) {
52*b1e83836Smrg	die "Could not read .mklog settings.\n"
53*b1e83836Smrg	    . $dot_mklog_format_msg;
54*b1e83836Smrg    }
55*b1e83836Smrg} else {
56*b1e83836Smrg    $name = `git config user.name`;
57*b1e83836Smrg    chomp($name);
58*b1e83836Smrg    $addr = `git config user.email`;
59*b1e83836Smrg    chomp($addr);
60*b1e83836Smrg
61*b1e83836Smrg    if (!($name && $addr)) {
62*b1e83836Smrg	die "Could not read git user.name and user.email settings.\n"
63*b1e83836Smrg	    . "Please add missing git settings, or create a .mklog file in"
64*b1e83836Smrg	    . " $ENV{HOME}.\n"
65*b1e83836Smrg	    . $dot_mklog_format_msg;
66*b1e83836Smrg    }
67*b1e83836Smrg}
68*b1e83836Smrg
69*b1e83836Smrg$gcc_root = $0;
70*b1e83836Smrg$gcc_root =~ s/[^\\\/]+$/../;
71*b1e83836Smrg
72*b1e83836Smrg#-----------------------------------------------------------------------------
73*b1e83836Smrg# Program starts here. You should not need to edit anything below this
74*b1e83836Smrg# line.
75*b1e83836Smrg#-----------------------------------------------------------------------------
76*b1e83836Smrg$inline = 0;
77*b1e83836Smrgif ($#ARGV == 1 && ("$ARGV[0]" eq "-i" || "$ARGV[0]" eq "--inline")) {
78*b1e83836Smrg	shift;
79*b1e83836Smrg	$inline = 1;
80*b1e83836Smrg} elsif ($#ARGV != 0) {
81*b1e83836Smrg    $prog = `basename $0`; chop ($prog);
82*b1e83836Smrg    print <<EOF;
83*b1e83836Smrgusage: $prog [ -i | --inline ] file.diff
84*b1e83836Smrg
85*b1e83836SmrgGenerate ChangeLog template for file.diff.
86*b1e83836SmrgIt assumes that patch has been created with -up or -cp.
87*b1e83836SmrgWhen -i is used, the ChangeLog template is followed by the contents of
88*b1e83836Smrgfile.diff.
89*b1e83836SmrgWhen file.diff is -, read standard input.
90*b1e83836SmrgWhen -i is used and file.diff is not -, it writes to file.diff, otherwise it
91*b1e83836Smrgwrites to stdout.
92*b1e83836SmrgEOF
93*b1e83836Smrg    exit 1;
94*b1e83836Smrg}
95*b1e83836Smrg
96*b1e83836Smrg$diff = $ARGV[0];
97*b1e83836Smrg$dir = `dirname $diff`; chop ($dir);
98*b1e83836Smrg$basename = `basename $diff`; chop ($basename);
99*b1e83836Smrg$hdrline = "$date  $name  <$addr>";
100*b1e83836Smrg
101*b1e83836Smrgsub get_clname ($) {
102*b1e83836Smrg	return ('ChangeLog', $_[0]) if ($_[0] !~ /[\/\\]/);
103*b1e83836Smrg
104*b1e83836Smrg	my $dirname = $_[0];
105*b1e83836Smrg	while ($dirname) {
106*b1e83836Smrg		my $clname = "$dirname/ChangeLog";
107*b1e83836Smrg		if (-f "$gcc_root/$clname" || -f "$clname") {
108*b1e83836Smrg			my $relname = substr ($_[0], length ($dirname) + 1);
109*b1e83836Smrg			return ($clname, $relname);
110*b1e83836Smrg		} else {
111*b1e83836Smrg			$dirname =~ s/[\/\\]?[^\/\\]*$//;
112*b1e83836Smrg		}
113*b1e83836Smrg	}
114*b1e83836Smrg
115*b1e83836Smrg	return ('Unknown ChangeLog', $_[0]);
116*b1e83836Smrg}
117*b1e83836Smrg
118*b1e83836Smrgsub remove_suffixes ($) {
119*b1e83836Smrg	my $filename = $_[0];
120*b1e83836Smrg	$filename =~ s/^[ab]\///;
121*b1e83836Smrg	$filename =~ s/\.jj$//;
122*b1e83836Smrg	return $filename;
123*b1e83836Smrg}
124*b1e83836Smrg
125*b1e83836Smrgsub is_context_hunk_start {
126*b1e83836Smrg	return @_[0] =~ /^\*\*\*\*\*\** ([a-zA-Z0-9_].*)/;
127*b1e83836Smrg}
128*b1e83836Smrg
129*b1e83836Smrgsub is_unified_hunk_start {
130*b1e83836Smrg	return @_[0] =~ /^@@ .* @@ ([a-zA-Z0-9_].*)/;
131*b1e83836Smrg}
132*b1e83836Smrg
133*b1e83836Smrg# Check if line is a top-level declaration.
134*b1e83836Smrgsub is_top_level {
135*b1e83836Smrg	my ($function, $is_context_diff) = (@_);
136*b1e83836Smrg	if (is_unified_hunk_start ($function)
137*b1e83836Smrg	    || is_context_hunk_start ($function)) {
138*b1e83836Smrg	    return 1;
139*b1e83836Smrg	}
140*b1e83836Smrg	if ($is_context_diff) {
141*b1e83836Smrg		$function =~ s/^..//;
142*b1e83836Smrg	} else {
143*b1e83836Smrg		$function =~ s/^.//;
144*b1e83836Smrg	}
145*b1e83836Smrg	return $function && $function !~ /^[\s{#]/;
146*b1e83836Smrg}
147*b1e83836Smrg
148*b1e83836Smrg# Read contents of .diff file
149*b1e83836Smrgopen (DFILE, $diff) or die "Could not open file $diff for reading";
150*b1e83836Smrgchomp (my @diff_lines = <DFILE>);
151*b1e83836Smrgclose (DFILE);
152*b1e83836Smrg
153*b1e83836Smrg# Array diff_lines is modified by the log generation, so save a copy in
154*b1e83836Smrg# orig_diff_lines if needed.
155*b1e83836Smrgif ($inline) {
156*b1e83836Smrg    @orig_diff_lines = @diff_lines;
157*b1e83836Smrg}
158*b1e83836Smrg
159*b1e83836Smrg# For every file in the .diff print all the function names in ChangeLog
160*b1e83836Smrg# format.
161*b1e83836Smrg%cl_entries = ();
162*b1e83836Smrg$change_msg = undef;
163*b1e83836Smrg$look_for_funs = 0;
164*b1e83836Smrg$clname = get_clname('');
165*b1e83836Smrg$line_idx = 0;
166*b1e83836Smrgforeach (@diff_lines) {
167*b1e83836Smrg    # Stop processing functions if we found a new file.
168*b1e83836Smrg	# Remember both left and right names because one may be /dev/null.
169*b1e83836Smrg    # Don't be fooled by line markers in case of context diff.
170*b1e83836Smrg    if (!/\*\*\*$/ && /^[+*][+*][+*] +(\S+)/) {
171*b1e83836Smrg		$left = remove_suffixes ($1);
172*b1e83836Smrg		$look_for_funs = 0;
173*b1e83836Smrg	}
174*b1e83836Smrg    if (!/---$/ && /^--- +(\S+)?/) {
175*b1e83836Smrg		$right = remove_suffixes ($1);
176*b1e83836Smrg		$look_for_funs = 0;
177*b1e83836Smrg	}
178*b1e83836Smrg
179*b1e83836Smrg	# Check if the body of diff started.
180*b1e83836Smrg	# We should now have both left and right name,
181*b1e83836Smrg	# so we can decide filename.
182*b1e83836Smrg
183*b1e83836Smrg    if ($left && (/^\*{15}/ || /^@@ /)) {
184*b1e83836Smrg	# If we have not seen any function names in the previous file (ie,
185*b1e83836Smrg	# $change_msg is empty), we just write out a ':' before starting the next
186*b1e83836Smrg	# file.
187*b1e83836Smrg	if ($clname) {
188*b1e83836Smrg		$cl_entries{$clname} .= $change_msg ? "$change_msg" : ":\n";
189*b1e83836Smrg	}
190*b1e83836Smrg
191*b1e83836Smrg	if ($left eq $right) {
192*b1e83836Smrg		$filename = $left;
193*b1e83836Smrg	} elsif($left eq '/dev/null') {
194*b1e83836Smrg		$filename = $right;
195*b1e83836Smrg	} elsif($right eq '/dev/null') {
196*b1e83836Smrg		$filename = $left;
197*b1e83836Smrg	} else {
198*b1e83836Smrg		my @ldirs = split /[\/\\]/, $left;
199*b1e83836Smrg		my @rdirs = split /[\/\\]/, $right;
200*b1e83836Smrg
201*b1e83836Smrg		$filename = '';
202*b1e83836Smrg		while ((my $l = pop @ldirs) && (my $r = pop @rdirs)) {
203*b1e83836Smrg			last if ($l ne $r);
204*b1e83836Smrg			$filename = "$l/$filename";
205*b1e83836Smrg		}
206*b1e83836Smrg		$filename =~ s/\/$//;
207*b1e83836Smrg
208*b1e83836Smrg		if (!$filename) {
209*b1e83836Smrg			print STDERR "Error: failed to parse diff for $left and $right\n";
210*b1e83836Smrg			exit 1;
211*b1e83836Smrg		}
212*b1e83836Smrg	}
213*b1e83836Smrg	$left = $right = undef;
214*b1e83836Smrg	($clname, $relname) = get_clname ($filename);
215*b1e83836Smrg	$cl_entries{$clname} .= "\t* $relname";
216*b1e83836Smrg	$change_msg = '';
217*b1e83836Smrg	$look_for_funs = $filename =~ '\.(c|cpp|C|cc|h|inc|def)$';
218*b1e83836Smrg    }
219*b1e83836Smrg
220*b1e83836Smrg    # Context diffs have extra whitespace after first char;
221*b1e83836Smrg    # remove it to make matching easier.
222*b1e83836Smrg    if ($is_context_diff) {
223*b1e83836Smrg      s/^([-+! ]) /\1/;
224*b1e83836Smrg    }
225*b1e83836Smrg
226*b1e83836Smrg    # Remember the last line in a diff block that might start
227*b1e83836Smrg    # a new function.
228*b1e83836Smrg    if (/^[-+! ]([a-zA-Z0-9_].*)/) {
229*b1e83836Smrg        $save_fn = $1;
230*b1e83836Smrg    }
231*b1e83836Smrg
232*b1e83836Smrg    # Check if file is newly added.
233*b1e83836Smrg    # Two patterns: for context and unified diff.
234*b1e83836Smrg    if (/^\*\*\* 0 \*\*\*\*/
235*b1e83836Smrg        || /^@@ -0,0 \+1.* @@/) {
236*b1e83836Smrg        $change_msg = $filename =~ /testsuite.*(?<!\.exp)$/ ? ": New test.\n" : ": New file.\n";
237*b1e83836Smrg        $look_for_funs = 0;
238*b1e83836Smrg    }
239*b1e83836Smrg
240*b1e83836Smrg    # Check if file was removed.
241*b1e83836Smrg    # Two patterns: for context and unified diff.
242*b1e83836Smrg    if (/^--- 0 ----/
243*b1e83836Smrg        || /^@@ -1.* \+0,0 @@/) {
244*b1e83836Smrg        $change_msg = ": Remove.\n";
245*b1e83836Smrg        $look_for_funs = 0;
246*b1e83836Smrg    }
247*b1e83836Smrg
248*b1e83836Smrg    if (is_unified_hunk_start ($diff_lines[$line_idx])) {
249*b1e83836Smrg        $is_context_diff = 0;
250*b1e83836Smrg    }
251*b1e83836Smrg    elsif (is_context_hunk_start ($diff_lines[$line_idx])) {
252*b1e83836Smrg	    $is_context_diff = 1;
253*b1e83836Smrg    }
254*b1e83836Smrg
255*b1e83836Smrg    # If we find a new function, print it in brackets.  Special case if
256*b1e83836Smrg    # this is the first function in a file.
257*b1e83836Smrg    #
258*b1e83836Smrg    # Note that we don't try too hard to find good matches.  This should
259*b1e83836Smrg    # return a superset of the actual set of functions in the .diff file.
260*b1e83836Smrg    #
261*b1e83836Smrg    # The first pattern works with context diff files (diff -c). The
262*b1e83836Smrg    # second pattern works with unified diff files (diff -u).
263*b1e83836Smrg    #
264*b1e83836Smrg    # The third pattern looks for the starts of functions or classes
265*b1e83836Smrg    # within a diff block both for context and unified diff files.
266*b1e83836Smrg    if ($look_for_funs
267*b1e83836Smrg        && (/^\*\*\*\*\*\** ([a-zA-Z0-9_].*)/
268*b1e83836Smrg	|| /^@@ .* @@ ([a-zA-Z0-9_].*)/
269*b1e83836Smrg	|| /^[-+! ](\{)/))
270*b1e83836Smrg      {
271*b1e83836Smrg	$_ = $1;
272*b1e83836Smrg	my $fn;
273*b1e83836Smrg	if (/^\{/) {
274*b1e83836Smrg	    # Beginning of a new function.
275*b1e83836Smrg	    $_ = $save_fn;
276*b1e83836Smrg	} else {
277*b1e83836Smrg	    $save_fn = "";
278*b1e83836Smrg	}
279*b1e83836Smrg	if (/;$/) {
280*b1e83836Smrg	    # No usable function name found.
281*b1e83836Smrg	} elsif (/^((class|struct|union|enum) [a-zA-Z0-9_]+)/) {
282*b1e83836Smrg	    # Discard stuff after the class/struct/etc. tag.
283*b1e83836Smrg	    $fn = $1;
284*b1e83836Smrg	} elsif (/([a-zA-Z0-9_][^(]*)\(/) {
285*b1e83836Smrg	    # Discard template and function parameters.
286*b1e83836Smrg	    $fn = $1;
287*b1e83836Smrg	    1 while ($fn =~ s/<[^<>]*>//);
288*b1e83836Smrg	    $fn =~ s/[ \t]*$//;
289*b1e83836Smrg	}
290*b1e83836Smrg	# Check is function really modified
291*b1e83836Smrg	$no_real_change = 0;
292*b1e83836Smrg	$idx = $line_idx;
293*b1e83836Smrg	# Skip line info in context diffs.
294*b1e83836Smrg	while ($idx <= $#diff_lines && $is_context_diff
295*b1e83836Smrg               && $diff_lines[$idx + 1] =~ /^[-\*]{3} [0-9]/) {
296*b1e83836Smrg		++$idx;
297*b1e83836Smrg	}
298*b1e83836Smrg	# Check all lines till the first change
299*b1e83836Smrg	# for the presence of really changed function
300*b1e83836Smrg	do {
301*b1e83836Smrg		++$idx;
302*b1e83836Smrg		$no_real_change = $idx > $#diff_lines
303*b1e83836Smrg				  || is_top_level ($diff_lines[$idx], $is_context_diff);
304*b1e83836Smrg	} while (!$no_real_change && ($diff_lines[$idx] !~ /^[-+!]/));
305*b1e83836Smrg	if ($fn && !$seen_names{$fn} && !$no_real_change) {
306*b1e83836Smrg	    # If this is the first function in the file, we display it next
307*b1e83836Smrg	    # to the filename, so we need an extra space before the opening
308*b1e83836Smrg	    # brace.
309*b1e83836Smrg	    if (!$change_msg) {
310*b1e83836Smrg		$change_msg .= " ";
311*b1e83836Smrg	    } else {
312*b1e83836Smrg		$change_msg .= "\t";
313*b1e83836Smrg	    }
314*b1e83836Smrg
315*b1e83836Smrg		$change_msg .= "($fn):\n";
316*b1e83836Smrg	    $seen_names{$fn} = 1;
317*b1e83836Smrg	}
318*b1e83836Smrg    }
319*b1e83836Smrg	$line_idx++;
320*b1e83836Smrg}
321*b1e83836Smrg
322*b1e83836Smrg# If we have not seen any function names (ie, $change_msg is empty), we just
323*b1e83836Smrg# write out a ':'. This happens when there is only one file with no
324*b1e83836Smrg# functions.
325*b1e83836Smrg$cl_entries{$clname} .= $change_msg ? "$change_msg\n" : ":\n";
326*b1e83836Smrg
327*b1e83836Smrgif ($inline && $diff ne "-") {
328*b1e83836Smrg	# Get a temp filename, rather than an open filehandle, because we use
329*b1e83836Smrg	# the open to truncate.
330*b1e83836Smrg	$tmp = mktemp("tmp.XXXXXXXX") or die "Could not create temp file: $!";
331*b1e83836Smrg
332*b1e83836Smrg	# Copy the permissions to the temp file (in File::Copy module version
333*b1e83836Smrg	# 2.15 and later).
334*b1e83836Smrg	cp $diff, $tmp or die "Could not copy patch file to temp file: $!";
335*b1e83836Smrg
336*b1e83836Smrg	# Open the temp file, clearing contents.
337*b1e83836Smrg	open (OUTPUTFILE, '>', $tmp) or die "Could not open temp file: $!";
338*b1e83836Smrg} else {
339*b1e83836Smrg	*OUTPUTFILE = STDOUT;
340*b1e83836Smrg}
341*b1e83836Smrg
342*b1e83836Smrg# Print the log
343*b1e83836Smrgforeach my $clname (keys %cl_entries) {
344*b1e83836Smrg	print OUTPUTFILE "$clname:\n\n$hdrline\n\n$cl_entries{$clname}\n";
345*b1e83836Smrg}
346*b1e83836Smrg
347*b1e83836Smrgif ($inline) {
348*b1e83836Smrg	# Append the patch to the log
349*b1e83836Smrg	foreach (@orig_diff_lines) {
350*b1e83836Smrg		print OUTPUTFILE "$_\n";
351*b1e83836Smrg	}
352*b1e83836Smrg}
353*b1e83836Smrg
354*b1e83836Smrgif ($inline && $diff ne "-") {
355*b1e83836Smrg	# Close $tmp
356*b1e83836Smrg	close(OUTPUTFILE);
357*b1e83836Smrg
358*b1e83836Smrg	# Write new contents to $diff atomically
359*b1e83836Smrg	mv $tmp, $diff or die "Could not move temp file to patch file: $!";
360*b1e83836Smrg}
361*b1e83836Smrg
362*b1e83836Smrgexit 0;
363