Reviewed by Darin.
[WebKit-https.git] / WebKitTools / Scripts / prepare-ChangeLog
index c0be272..7d71afa 100755 (executable)
@@ -56,24 +56,37 @@ use warnings;
 use File::Basename;
 use File::Spec;
 use File::Temp;
+use FindBin;
 use Getopt::Long;
 
+sub canonicalizePath($);
+sub get_function_line_ranges($$);
+sub get_function_line_ranges_for_c($$);
+sub get_function_line_ranges_for_java($$);
+sub method_decl_to_selector($);
+sub processPaths(\@);
+
 my $openChangeLogs = 0;
 my $showHelp = 0;
+my $updateChangeLogs = 1;
 my $spewDiff = $ENV{"PREPARE_CHANGELOG_DIFF"};
 my $parseOptionsResult =
     GetOptions("diff|d!" => \$spewDiff,
                "help|h!" => \$showHelp,
-               "open|o!" => \$openChangeLogs);
+               "open|o!" => \$openChangeLogs,
+               "update!" => \$updateChangeLogs);
 if (!$parseOptionsResult || $showHelp)
   {
-    print STDERR basename($0) . " [-d|--diff] [-h|--help] [-o|--open]\n";
-    print STDERR "  -d|--diff  Spew diff to stdout when running\n";
-    print STDERR "  -h|--help  Show this help message\n";
-    print STDERR "  -o|--open  Open ChangeLogs in an editor when done\n";
+    print STDERR basename($0) . " [-d|--diff] [-h|--help] [-o|--open] [svndir1 [svndir2 ...]]\n";
+    print STDERR "  -d|--diff      Spew diff to stdout when running\n";
+    print STDERR "  -h|--help      Show this help message\n";
+    print STDERR "  -o|--open      Open ChangeLogs in an editor when done\n";
+    print STDERR "  --[no-]update  Update ChangeLogs from svn before adding entry (default: update)\n";
     exit 1;
   }
 
+my %paths = processPaths(@ARGV);
+
 # Find the list of modified files
 my @changed_files;
 my $changed_files_string;
@@ -99,7 +112,8 @@ my $diffTempFile = $DIFFOUT->filename();
 my @diffFiles;
 
 print STDERR "  Running 'svn diff' to find changed, added, or removed files.\n";
-open SVNDIFF, "$SVN diff --diff-cmd diff -x -N |" or die "The svn diff failed: $!.\n";
+open SVNDIFF, "$SVN diff --diff-cmd diff -x -N '" . join("' '", keys %paths) . "'|"
+    or die "The svn diff failed: $!.\n";
 while (<SVNDIFF>)
   {
     print $DIFFOUT $_;
@@ -279,7 +293,7 @@ foreach my $prefix (sort keys %files)
   {
     $logs .= " ${prefix}ChangeLog";
   }
-if ($logs)
+if ($logs && $updateChangeLogs)
   {
     print STDERR "  Running 'svn update' to update ChangeLog files.\n";
     open ERRORS, "$SVN update -q$logs |" or die "The svn update of ChangeLog files failed: $!.\n";
@@ -316,7 +330,7 @@ foreach my $prefix (sort keys %files)
 if ($spewDiff && @changed_files)
   {
     print STDERR "  Running 'svn diff' to help you write the ChangeLog entries.\n";
-    open DIFF, "$SVN diff $changed_files_string |" or die "The svn diff failed: $!.\n";
+    open DIFF, "'$FindBin::Bin/svn-create-patch' $changed_files_string |" or die "The svn diff failed: $!.\n";
     while (<DIFF>) { print; }
     close DIFF;
   }
@@ -336,7 +350,30 @@ if ($openChangeLogs && $logs)
 # Done.
 exit;
 
-sub get_function_line_ranges
+sub canonicalizePath($)
+  {
+    my ($file) = @_;
+
+    # Remove extra slashes and '.' directories in path
+    $file = File::Spec->canonpath($file);
+
+    # Remove '..' directories in path
+    my @dirs = ();
+    foreach my $dir (File::Spec->splitdir($file))
+      {
+        if ($dir eq '..' && $#dirs >= 0 && $dirs[$#dirs] ne '..')
+          {
+            pop(@dirs);
+          }
+        else
+          {
+            push(@dirs, $dir);
+          }
+      }
+    return ($#dirs >= 0) ? File::Spec->catdir(@dirs) : ".";
+  }
+
+sub get_function_line_ranges($$)
   {
     my ($file_handle, $file_name) = @_;
 
@@ -349,7 +386,7 @@ sub get_function_line_ranges
   }
 
 
-sub method_decl_to_selector
+sub method_decl_to_selector($)
   {
     (my $method_decl) = @_;
 
@@ -385,7 +422,7 @@ sub method_decl_to_selector
 #
 # Result is a list of triples: [ start_line, end_line, function_name ].
 
-sub get_function_line_ranges_for_c
+sub get_function_line_ranges_for_c($$)
   {
     my ($file_handle, $file_name) = @_;
 
@@ -494,6 +531,10 @@ sub get_function_line_ranges_for_c
   
                   $_ = $original;
                   s/^[^;{]*//;
+                } elsif (/\@end/) {
+                  $in_method_declaration = 0;
+                  $interface_name = "";
+                  $_ = $original;
                 } else {
                   next;
                 }
@@ -527,6 +568,10 @@ sub get_function_line_ranges_for_c
               $in_method_declaration = 0;
               $_ = $original;
               s/^[^{]*//;
+            } elsif (/\@end/) {
+              $in_method_declaration = 0;
+              $interface_name = "";
+              $_ = $original;
             } else {
               next;
             }
@@ -691,7 +736,7 @@ sub get_function_line_ranges_for_c
 #
 # Result is a list of triples: [ start_line, end_line, function_name ].
 
-sub get_function_line_ranges_for_java
+sub get_function_line_ranges_for_java($$)
   {
     my ($file_handle, $file_name) = @_;
 
@@ -900,3 +945,43 @@ sub get_function_line_ranges_for_java
 
     return @ranges;
   }
+
+sub processPaths(\@)
+  {
+    my ($paths) = @_;
+    return ("." => 1) if (!@{$paths});
+
+    my %result = ();
+
+    for my $file (@{$paths})
+      {
+        die "can't handle absolute paths like \"$file\"\n" if File::Spec->file_name_is_absolute($file);
+        die "can't handle empty string path\n" if $file eq "";
+        die "can't handle path with single quote in the name like \"$file\"\n" if $file =~ /'/; # ' (keep Xcode syntax highlighting happy)
+
+        my $untouchedFile = $file;
+
+        $file = canonicalizePath($file);
+
+        die "can't handle paths with .. like \"$untouchedFile\"\n" if $file =~ m|/\.\./|;
+
+        $result{$file} = 1;
+      }
+
+    return ("." => 1) if ($result{"."});
+
+    # Remove any paths that also have a parent listed.
+    for my $path (keys %result)
+      {
+        for (my $parent = dirname($path); $parent ne '.'; $parent = dirname($parent))
+         {
+            if ($result{$parent})
+              {
+                delete $result{$path};
+                last;
+              }
+          }
+      }
+
+    return %result;
+  }