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