mklog 9.8 KB

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