You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2013/07/25 17:29:53 UTC

svn commit: r1507012 [11/11] - in /subversion/branches/fsfs-format7: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ build/generator/templates/ contrib/client-side/emacs/ doc/programmer/ notes/http-and-webdav/ subversion/ subversion/b...

Modified: subversion/branches/fsfs-format7/tools/dev/build-svn-deps-win.pl
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/tools/dev/build-svn-deps-win.pl?rev=1507012&r1=1507011&r2=1507012&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/tools/dev/build-svn-deps-win.pl (original)
+++ subversion/branches/fsfs-format7/tools/dev/build-svn-deps-win.pl Thu Jul 25 15:29:49 2013
@@ -51,7 +51,6 @@
 # Usage/help output from the usual flags/on error input.
 # Make SQLITE_VER friendly since we're using no dots right now.
 # Work out the fixes to the projects' sources and contribute them back.
-# Allow selection of Release/Debug builds.
 # Allow selection of Arch (x86 and x64)
 # ZLib support for OpenSSL (have to patch openssl)
 # Use CMake zlib build instead.
@@ -135,7 +134,8 @@ our $SRCDIR; # directory where we store 
 # Some other options
 our $VS_VER;
 our $NEON;
-our $SVN_VER = '1.8.x';
+our $SVN_VER = '1.9.x';
+our $DEBUG = 0;
 
 # Utility function to remove dots from a string
 sub remove_dots {
@@ -351,6 +351,7 @@ sub clean_structure {
   rmtree($INCDIR);
   rmtree($LIBDIR);
   rmtree("$INSTDIR\\serf");
+  rmtree("$INSTDIR\\neon");
   rmtree("$INSTDIR\\sqlite-amalgamation");
 
   # Dirs created indirectly by the install targets
@@ -422,7 +423,7 @@ sub download_dependencies {
   download_file($PCRE_URL, "$SRCDIR\\pcre.zip", \$PCRE_FILE);
   download_file($SQLITE_URL, "$SRCDIR\\sqlite-amalgamation.zip", \$SQLITE_FILE);
   download_file($SERF_URL, "$SRCDIR\\serf.zip", \$SERF_FILE);
-  download_file($NEON_URL, "$SRCDIR\\neon.tar.gz", \$NEON_FILE) if defined($NEON);
+  download_file($NEON_URL, "$SRCDIR\\neon.tar.gz", \$NEON_FILE) if $NEON;
 }
 
 ##############
@@ -488,7 +489,7 @@ sub extract_dependencies {
   extract_file($SERF_FILE, $INSTDIR,
                "$INSTDIR\\serf-$SERF_VER", "$INSTDIR\\serf");
   extract_file($NEON_FILE, $INSTDIR,
-               "$INSTDIR\\neon-$NEON_VER", "$INSTDIR\\neon") if defined($NEON);
+               "$INSTDIR\\neon-$NEON_VER", "$INSTDIR\\neon") if $NEON;
 }
 
 #########
@@ -499,7 +500,7 @@ sub build_pcre {
   chdir_or_die("$SRCLIB\\pcre");
   my $pcre_generator = 'NMake Makefiles';
   # Have to use RelWithDebInfo since httpd looks for the pdb files
-  my $pcre_build_type = '-DCMAKE_BUILD_TYPE:STRING=RelWithDebInfo';
+  my $pcre_build_type = '-DCMAKE_BUILD_TYPE:STRING=' . ($DEBUG ? 'Debug' : 'RelWithDebInfo');
   my $pcre_shared_libs = '-DBUILD_SHARED_LIBS:BOOL=ON';
   my $pcre_install_prefix = "-DCMAKE_INSTALL_PREFIX:PATH=$INSTDIR";
   my $cmake_cmd = qq("$CMAKE" -G "$pcre_generator" "$pcre_build_type" "$pcre_shared_libs" "$pcre_install_prefix" .); 
@@ -514,7 +515,7 @@ sub build_pcre {
 # build generates, it it doesn't match that then Subversion will fail to build.
 sub build_zlib {
   chdir_or_die("$SRCLIB\\zlib");
-  $ENV{CC_OPTS} = '/MD /02 /Zi';
+  $ENV{CC_OPTS} = $DEBUG ? '/MDd /Gm /ZI /Od /GZ /D_DEBUG' : '/MD /02 /Zi';
   $ENV{COMMON_CC_OPTS} = '/nologo /W3 /DWIN32 /D_WINDOWS';
   
   system_or_die("Failure building zilb", qq("$NMAKE" /nologo -f win32\\Makefile.msc STATICLIB=zlibstat.lib all));
@@ -543,8 +544,9 @@ sub build_openssl {
   # The apache build docs suggest no-rc5 no-idea enable-mdc2 on top of what
   # is used below, the primary driver behind that is patents, but I believe
   # the rc5 and idea patents have expired.
+  my $platform = $DEBUG ? 'debug-VC-WIN32' : 'VC-WIN32';
   system_or_die("Failure configuring openssl",
-                qq("$PERL" Configure no-asm "--prefix=$INSTDIR" VC-WIN32));
+                qq("$PERL" Configure no-asm "--prefix=$INSTDIR" $platform));
   system_or_die("Failure building openssl (bat)", 'ms\do_ms.bat');
   system_or_die("Failure building openssl (nmake)", qq("$NMAKE" /f ms\\ntdll.mak));
   system_or_die("Failure testing openssl", qq("$NMAKE" /f ms\\ntdll.mak test));
@@ -601,6 +603,9 @@ sub httpd_fix_makefile {
 
   modify_file_in_place($file, sub {
       s/\.vcproj/.vcxproj/i;
+      # below fixes that installd breaks when trying to install pcre because 
+      # dll is named pcred.dll when a Debug build. 
+      s/^(\s*copy srclib\\pcre\\pcre\.\$\(src_dll\)\s+"\$\(inst_dll\)"\s+<\s*\.y\s*)$/!IF EXISTS("srclib\\pcre\\pcre\.\$(src_dll)")\n$1!ENDIF\n!IF EXISTS("srclib\\pcre\\pcred\.\$(src_dll)")\n\tcopy srclib\\pcre\\pcred.\$(src_dll)\t\t\t"\$(inst_dll)" <.y\n!ENDIF\n/;
     });
 }
 
@@ -650,8 +655,9 @@ sub get_output_file {
 # Find the name of the bdb library we've installed in our LIBDIR.
 sub find_bdb_lib {
   my $result;
+  my $debug = $DEBUG ? 'd' : '';
   find(sub {
-         if (not defined($result) and /^libdb\d+\.lib$/) {
+         if (not defined($result) and /^libdb\d+$debug\.lib$/) {
            $result = $_;
          }
        }, $LIBDIR);
@@ -688,12 +694,25 @@ sub httpd_enable_bdb {
   insert_dependency_in_proj('support\htdbm.vcxproj', $bdb_lib, '.bdb');
 }
 
+# Apply the same fix as found in r1486937 on httpd 2.4.x branch.
+sub httpd_fix_debug {
+  my ($httpd_major, $httpd_minor, $httpd_patch) = $HTTPD_VER =~ /^(\d+)\.(\d+)\.(.+)$/;
+  return unless ($httpd_major <= 2 && $httpd_minor <= 4 && $httpd_patch < 5);
+
+  modify_file_in_place('libhttpd.dsp', sub {
+      s/^(!MESSAGE "libhttpd - Win32 Debug" \(based on "Win32 \(x86\) Dynamic-Link Library"\))$/$1\n!MESSAGE "libhttpd - Win32 Lexical" (based on "Win32 (x86) Dynamic-Link Library")/;
+      s/^(# Begin Group "headers")$/# Name "libhttpd - Win32 Lexical"\n$1/;
+    }, '.lexical');
+}
+
 sub build_httpd {
   chdir_or_die($HTTPD);
 
   my $vs_2012 = $VS_VER eq '2012';
   my $vs_2010 = $VS_VER eq '2010';
 
+  httpd_fix_debug();
+
   # I don't think cvtdsp.pl is necessary with Visual Studio 2012
   # but it shouldn't hurt anything either.  Including it allows
   # for the possibility that this may work for older Visual Studio
@@ -763,8 +782,9 @@ sub build_httpd {
   # configurations inside the project since we get them from the environment.
   # Once all that is done the BuildBin project should be buildable for you to
   # diagnose the problem.
+  my $target = $DEBUG ? "installd" : "installr";
   system_or_die("Failed building/installing httpd/apr/apu/api",
-    qq("$NMAKE" /f Makefile.win installr "DBM_LIST=db" "INSTDIR=$INSTDIR"));
+    qq("$NMAKE" /f Makefile.win $target "DBM_LIST=db" "INSTDIR=$INSTDIR"));
 
   chdir_or_die($TOPDIR);
 }
@@ -776,13 +796,15 @@ sub build_bdb {
   my $sln = 'build_windows\Berkeley_DB_vs2010.sln'; 
   upgrade_solution($sln);
 
+  my $platform = $DEBUG ? 'Debug|Win32' : 'Release|Win32';
+
   # Build the db Project first since the full solution fails due to a broken
   # dependency with the current version of BDB if we don't.
   system_or_die("Failed building DBD (Project db)",
-                qq("$DEVENV" "$sln" /Build "Release|Win32" /Project db));
+                qq("$DEVENV" "$sln" /Build "$platform" /Project db));
 
   system_or_die("Failed building DBD",
-                qq("$DEVENV" "$sln" /Build "Release|Win32"));
+                qq("$DEVENV" "$sln" /Build "$platform"));
 
   # BDB doesn't seem to have it's own install routines so we'll do it ourselves
   copy_or_die('build_windows\db.h', $INCDIR);
@@ -792,16 +814,31 @@ sub build_bdb {
      } elsif (/\.lib$/) {
        copy_or_die($_, $LIBDIR);
      }
-   }, 'build_windows\Win32\Release');
+   }, 'build_windows\\Win32\\' . ($DEBUG ? 'Debug' : 'Release'));
 
   chdir_or_die($TOPDIR);
 }
 
+# Right now this doesn't actually build serf but just patches it so that it
+# can build against a debug build of OpenSSL.
+sub build_serf {
+  chdir_or_die("$TOPDIR\\serf");
+
+  modify_file_in_place('serf.mak', sub {
+      s/^(INTDIR = Release)$/$1\nOPENSSL_OUT_SUFFIX =/;
+      s/^(INTDIR = Debug)$/$1\nOPENSSL_OUT_SUFFIX = .dbg/;
+      s/(\$\(OPENSSL_SRC\)\\out32(?:dll)?)/$1\$(OPENSSL_OUT_SUFFIX)/g;
+    }, '.debug');
+  
+  chdir_or_die($TOPDIR);
+}
+
 sub build_dependencies {
   build_bdb();
   build_zlib();
   build_pcre();
   build_openssl();
+  build_serf();
   build_httpd();
 }
 

Modified: subversion/branches/fsfs-format7/tools/dev/svnraisetreeconflict/svnraisetreeconflict.c
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/tools/dev/svnraisetreeconflict/svnraisetreeconflict.c?rev=1507012&r1=1507011&r2=1507012&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/tools/dev/svnraisetreeconflict/svnraisetreeconflict.c (original)
+++ subversion/branches/fsfs-format7/tools/dev/svnraisetreeconflict/svnraisetreeconflict.c Thu Jul 25 15:29:49 2013
@@ -187,7 +187,7 @@ raise_tree_conflict(int argc, const char
 {
   int i = 0;
   svn_wc_conflict_version_t *left, *right;
-  svn_wc_conflict_description2_t *c;
+  svn_wc_conflict_description3_t *c;
   svn_wc_context_t *wc_ctx;
 
   /* Conflict description parameters */
@@ -223,7 +223,7 @@ raise_tree_conflict(int argc, const char
                                          peg_rev1, kind1, pool);
   right = svn_wc_conflict_version_create2(repos_url2, NULL, path_in_repos2,
                                           peg_rev2, kind2, pool);
-  c = svn_wc_conflict_description_create_tree2(wc_abspath, kind,
+  c = svn_wc_conflict_description_create_tree3(wc_abspath, kind,
                                               operation, left, right, pool);
   c->action = (svn_wc_conflict_action_t)action;
   c->reason = (svn_wc_conflict_reason_t)reason;

Modified: subversion/branches/fsfs-format7/tools/dev/unix-build/Makefile.svn
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/tools/dev/unix-build/Makefile.svn?rev=1507012&r1=1507011&r2=1507012&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/tools/dev/unix-build/Makefile.svn (original)
+++ subversion/branches/fsfs-format7/tools/dev/unix-build/Makefile.svn Thu Jul 25 15:29:49 2013
@@ -70,7 +70,7 @@ APR_VER		= 1.4.6
 APR_ICONV_VER	= 1.2.1
 GNU_ICONV_VER	= 1.14
 APR_UTIL_VER	= 1.4.1
-HTTPD_VER	= 2.2.22
+HTTPD_VER	= 2.2.25
 NEON_VER	= 0.29.6
 SERF_VER	= 1.2.1
 SERF_OLD_VER	= 0.3.1
@@ -548,15 +548,35 @@ httpd-reset:
 
 httpd-clean:
 	-(cd $(HTTPD_OBJDIR) && make clean)
+	rm -f $(HTTPD_OBJDIR)/no_ssl_v2.diff
 
 # fetch distfile for httpd
 $(DISTDIR)/$(HTTPD_DIST):
 	cd $(DISTDIR) && $(FETCH_CMD) $(HTTPD_URL)
 
+$(HTTPD_OBJDIR)/no_ssl_v2.diff:
+	mkdir -p $(dir $@)
+	echo > $@.tmp '--- modules/ssl/ssl_engine_io.c.orig	Sat Jul 13 16:49:52 2013'
+	echo >> $@.tmp '+++ modules/ssl/ssl_engine_io.c	Sat Jul 13 16:50:10 2013'
+	echo >> $@.tmp '@@ -1079,7 +1079,9 @@'
+	echo >> $@.tmp '          * IPv4 and IPv6 addresses are not permitted".)'
+	echo >> $@.tmp '          */'
+	echo >> $@.tmp '         if (hostname_note &&'
+	echo >> $@.tmp '+#ifndef OPENSSL_NO_SSL2'
+	echo >> $@.tmp '             sc->proxy->protocol != SSL_PROTOCOL_SSLV2 &&'
+	echo >> $@.tmp '+#endif'
+	echo >> $@.tmp '             sc->proxy->protocol != SSL_PROTOCOL_SSLV3 &&'
+	echo >> $@.tmp '             apr_ipsubnet_create(&ip, hostname_note, NULL,'
+	echo >> $@.tmp '                                 c->pool) != APR_SUCCESS) {'
+	mv -f $@.tmp $@
+
 # retrieve httpd
-$(HTTPD_OBJDIR)/.retrieved: $(DISTDIR)/$(HTTPD_DIST)
+$(HTTPD_OBJDIR)/.retrieved: $(DISTDIR)/$(HTTPD_DIST) \
+				$(HTTPD_OBJDIR)/no_ssl_v2.diff
 	[ -d $(HTTPD_OBJDIR) ] || mkdir -p $(HTTPD_OBJDIR)
 	tar -C $(SRCDIR) -jxf $(DISTDIR)/$(HTTPD_DIST)
+	cd $(SRCDIR)/httpd-$(HTTPD_VER) && \
+		patch -p0 < $(HTTPD_OBJDIR)/no_ssl_v2.diff
 	touch $@
 
 # configure httpd

Modified: subversion/branches/fsfs-format7/tools/dist/backport.pl
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/tools/dist/backport.pl?rev=1507012&r1=1507011&r2=1507012&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/tools/dist/backport.pl (original)
+++ subversion/branches/fsfs-format7/tools/dist/backport.pl Thu Jul 25 15:29:49 2013
@@ -1,4 +1,4 @@
-#!/usr/bin/perl -l
+#!/usr/bin/perl
 use warnings;
 use strict;
 use feature qw/switch say/;
@@ -20,65 +20,142 @@ use feature qw/switch say/;
 # specific language governing permissions and limitations
 # under the License.
 
+use Digest ();
 use Term::ReadKey qw/ReadMode ReadKey/;
+use File::Copy qw/copy move/;
 use File::Temp qw/tempfile/;
 use POSIX qw/ctermid/;
 
+############### Start of reading values from environment ###############
+
+# Programs we use.
 my $SVN = $ENV{SVN} || 'svn'; # passed unquoted to sh
+my $SHELL = $ENV{SHELL} // '/bin/sh';
 my $VIM = 'vim';
-my $STATUS = './STATUS';
-my $BRANCHES = '^/subversion/branches';
+my $EDITOR = $ENV{SVN_EDITOR} // $ENV{VISUAL} // $ENV{EDITOR} // 'ed';
+my $PAGER = $ENV{PAGER} // 'less -F' // 'cat';
 
-my $YES = $ENV{YES}; # batch mode: eliminate prompts, add sleeps
-my $MAY_COMMIT = qw[false true][0];
-my $DEBUG = qw[false true][0]; # 'set -x', etc
-$DEBUG = 'true' if exists $ENV{DEBUG};
+# Mode flags.
+#    svn-role:      YES=1 MAY_COMMIT=1
+#    conflicts-bot: YES=1 MAY_COMMIT=0
+#    interactive:   YES=0 MAY_COMMIT=0      (default)
+my $YES = ($ENV{YES} // 0) ? 1 : 0; # batch mode: eliminate prompts, add sleeps
+my $MAY_COMMIT = 'false';
 $MAY_COMMIT = 'true' if ($ENV{MAY_COMMIT} // "false") =~ /^(1|yes|true)$/i;
 
-# derived values
+# Other knobs.
+my $VERBOSE = 0;
+my $DEBUG = (exists $ENV{DEBUG}) ? 'true' : 'false'; # 'set -x', etc
+
+# Username for entering votes.
+my ($AVAILID) = $ENV{AVAILID} // do {
+  my $SVN_A_O_REALM = 'd3c8a345b14f6a1b42251aef8027ab57';
+  open USERNAME, '<', "$ENV{HOME}/.subversion/auth/svn.simple/$SVN_A_O_REALM";
+  1 until <USERNAME> eq "username\n";
+  <USERNAME>;
+  local $_ = <USERNAME>;
+  chomp;
+  $_
+}
+// warn "Username for commits (of votes/merges) not found";
+
+############## End of reading values from the environment ##############
+
+# Constants.
+my $STATUS = './STATUS';
+my $STATEFILE = './.backports1';
+my $BRANCHES = '^/subversion/branches';
+
+# Globals.
+my %ERRORS = ();
+my $MERGED_SOMETHING = 0;
 my $SVNq;
+
+# Derived values.
 my $SVNvsn = do {
   my ($major, $minor, $patch) = `$SVN --version -q` =~ /^(\d+)\.(\d+)\.(\d+)/;
   1e6*$major + 1e3*$minor + $patch;
 };
-
 $SVN .= " --non-interactive" if $YES or not defined ctermid;
 $SVNq = "$SVN -q ";
 $SVNq =~ s/-q// if $DEBUG eq 'true';
 
+
 sub usage {
   my $basename = $0;
   $basename =~ s#.*/##;
   print <<EOF;
-Run this from the root of your release branch (e.g., 1.6.x) working copy.  Use
-a working copy 'svn revert -R .' can be run on at any time, as this script
-will run revert prior to every merge.
-
-For each entry in STATUS, you will be prompted whether to merge it.  The
-merge will not be committed.
+backport.pl: a tool for reviewing and merging STATUS entries.  Run this with
+CWD being the root of the stable branch (e.g., 1.8.x).  The ./STATUS file
+should be at HEAD.
+
+In interactive mode (the default), you will be prompted once per STATUS entry.
+At a prompt, you have the following options:
+
+y:   Run a merge.  It will not be committed.
+     WARNING: This will run 'update' and 'revert -R ./'.
+l:   Show logs for the entries being nominated.
+q:   Quit the "for each nomination" loop.
+±1:  Enter a +1 or -1 vote
+     You will be prompted to commit your vote at the end.
+±0:  Enter a +0 or -0 vote
+     You will be prompted to commit your vote at the end.
+a:   Move the entry to the "Approved changes" section.
+     When both approving and voting on an entry, approve first: for example,
+     to enter a third +1 vote, type "a" "+" "1".
+e:   Edit the entry in $EDITOR.
+     You will be prompted to commit your edits at the end.
+N:   Move to the next entry.  Cache the entry in '$STATEFILE' and do not
+     prompt for it again (even across runs) until it is changed.
+ :   Move to the next entry, without adding the current one to the cache.
+     (That's a space character, ASCII 0x20.)
+
+After running a merge, you have the following options:
+
+y:   Open a shell.
+d:   View a diff.
+N:   Move to the next entry.
+
+There is also a batch mode: when \$YES and \$MAY_COMMIT are defined to '1' i
+the environment, this script will iterate the "Approved:" section, and merge
+and commit each entry therein.  If only \$YES is defined, the script will
+merge every nomination (including unapproved and vetoed ones), and complain
+to stderr if it notices any conflicts.  These mode are normally used by the
+'svn-role' cron job and/or buildbot, not by human users.
 
 The 'svn' binary defined by the environment variable \$SVN, or otherwise the
 'svn' found in \$PATH, will be used to manage the working copy.
 EOF
 }
 
+sub digest_string {
+  Digest->new("MD5")->add(@_)->hexdigest
+}
+
 sub prompt {
-  local $\; # disable 'perl -l' effects
-  print "$_[0] "; shift;
+  print $_[0]; shift;
   my %args = @_;
+  my $getchar = sub {
+    ReadMode 'cbreak';
+    my $answer = (ReadKey 0);
+    ReadMode 'normal';
+    print $answer;
+    return $answer;
+  };
 
   die "$0: called prompt() in non-interactive mode!" if $YES;
-  ReadMode 'cbreak';
-  my $answer = (ReadKey 0);
-  ReadMode 'restore';
-  print $answer, "\n";
+  my $answer = $getchar->();
+  $answer .= $getchar->() if exists $args{extra} and $answer =~ $args{extra};
+  say "" unless $args{dontprint};
   return $args{verbose}
          ? $answer
          : ($answer =~ /^y/i) ? 1 : 0;
 }
 
+
 sub merge {
   my %entry = @_;
+  $MERGED_SOMETHING++;
 
   my ($logmsg_fh, $logmsg_filename) = tempfile();
   my ($mergeargs, $pattern);
@@ -90,26 +167,26 @@ sub merge {
     $pattern = sprintf '\V\(%s branch(es)?\|branches\/%s\|Branch\(es\)\?: \*\n\? \*%s\)', $entry{branch}, $entry{branch}, $entry{branch};
     if ($SVNvsn >= 1_008_000) {
       $mergeargs = "$BRANCHES/$entry{branch}";
-      print $logmsg_fh "Merge the $entry{header}:";
+      say $logmsg_fh "Merge the $entry{header}:";
     } else {
       $mergeargs = "--reintegrate $BRANCHES/$entry{branch}";
-      print $logmsg_fh "Reintegrate the $entry{header}:";
+      say $logmsg_fh "Reintegrate the $entry{header}:";
     }
-    print $logmsg_fh "";
+    say $logmsg_fh "";
   } elsif (@{$entry{revisions}}) {
     $pattern = '^ [*] \V' . 'r' . $entry{revisions}->[0];
     $mergeargs = join " ", (map { "-c$_" } @{$entry{revisions}}), '^/subversion/trunk';
     if (@{$entry{revisions}} > 1) {
-      print $logmsg_fh "Merge the $entry{header} from trunk:";
-      print $logmsg_fh "";
+      say $logmsg_fh "Merge the $entry{header} from trunk:";
+      say $logmsg_fh "";
     } else {
-      print $logmsg_fh "Merge r$entry{revisions}->[0] from trunk:";
-      print $logmsg_fh "";
+      say $logmsg_fh "Merge r$entry{revisions}->[0] from trunk:";
+      say $logmsg_fh "";
     }
   } else {
     die "Don't know how to call $entry{header}";
   }
-  print $logmsg_fh $_ for @{$entry{entry}};
+  say $logmsg_fh $_ for @{$entry{entry}};
   close $logmsg_fh or die "Can't close $logmsg_filename: $!";
 
   my $reintegrated_word = ($SVNvsn >= 1_008_000) ? "merged" : "reintegrated";
@@ -120,9 +197,11 @@ if $DEBUG; then
   set -x
 fi
 $SVN diff > $backupfile
-cp STATUS STATUS.$$
+if ! $MAY_COMMIT ; then
+  cp STATUS STATUS.$$
+fi
 $SVNq revert -R .
-if $MAY_COMMIT ; then
+if ! $MAY_COMMIT ; then
   mv STATUS.$$ STATUS
 fi
 $SVNq up
@@ -140,7 +219,7 @@ fi
 if $MAY_COMMIT; then
   $VIM -e -s -n -N -i NONE -u NONE -c '/$pattern/normal! dap' -c wq $STATUS
   $SVNq commit -F $logmsg_filename
-else
+elif test 1 -ne $YES; then
   echo "Would have committed:"
   echo '[[['
   $SVN status -q
@@ -157,16 +236,22 @@ if $MAY_COMMIT; then
   if [ -n "\$YES" ]; then sleep 15; fi
   $SVNq rm $BRANCHES/$entry{branch} -m "Remove the '$entry{branch}' branch, $reintegrated_word in r\$reinteg_rev."
   if [ -n "\$YES" ]; then sleep 1; fi
-else
-  echo "Removing $reintegrated_word '$entry{branch}' branch"
+elif test 1 -ne $YES; then
+  echo "Would remove $reintegrated_word '$entry{branch}' branch"
 fi
 EOF
 
   open SHELL, '|-', qw#/bin/sh# or die "$! (in '$entry{header}')";
   print SHELL $script;
   close SHELL or warn "$0: sh($?): $! (in '$entry{header}')";
+  $ERRORS{$entry{id}} = "sh($?): $!" if $?;
+
+  if (-z $backupfile) {
+    unlink $backupfile;
+  } else {
+    warn "Local mods saved to '$backupfile'\n";
+  }
 
-  unlink $backupfile if -z $backupfile;
   unlink $logmsg_filename unless $? or $!;
 }
 
@@ -180,7 +265,9 @@ sub sanitize_branch {
 
 # TODO: may need to parse other headers too?
 sub parse_entry {
+  my $raw = shift;
   my @lines = @_;
+  my $depends;
   my (@revisions, @logsummary, $branch, @votes);
   # @lines = @_;
 
@@ -209,6 +296,10 @@ sub parse_entry {
   unshift @votes, pop until $_[-1] =~ /^\s*Votes:/ or not defined $_[-1];
   pop;
 
+  # depends
+  # TODO: parse the value of this.
+  $depends = grep /^Depends:/, @_;
+
   # branch
   while (@_) {
     shift and next unless $_[0] =~ s/^\s*Branch(es)?:\s*//;
@@ -216,9 +307,11 @@ sub parse_entry {
   }
 
   # Compute a header.
-  my $header;
+  my ($header, $id);
   $header = "r$revisions[0] group" if @revisions;
-  $header = "$branch branch" if $branch;
+  $id = "r$revisions[0]"           if @revisions;
+  $header = "$branch branch"       if $branch;
+  $id = $branch                    if $branch;
   warn "No header for [@lines]" unless $header;
 
   return (
@@ -226,49 +319,338 @@ sub parse_entry {
     logsummary => [@logsummary],
     branch => $branch,
     header => $header,
+    depends => $depends,
+    id => $id,
     votes => [@votes],
     entry => [@lines],
+    raw => $raw,
+    digest => digest_string($raw),
   );
 }
 
+sub edit_string {
+  # Edits $_[0] in an editor.
+  # $_[1] is used in error messages.
+  die "$0: called edit_string() in non-interactive mode!" if $YES;
+  my $string = shift;
+  my $name = shift;
+  my %args = @_;
+  my $trailing_eol = $args{trailing_eol};
+  my ($fh, $fn) = tempfile;
+  print $fh $string;
+  $fh->flush or die $!;
+  system("$EDITOR -- $fn") == 0
+    or warn "\$EDITOR failed editing $name: $! ($?); "
+           ."edit results ($fn) ignored.";
+  my $rv = `cat $fn`;
+  $rv =~ s/\n*\z// and $rv .= ("\n" x $trailing_eol) if defined $trailing_eol;
+  $rv;
+}
+
+sub vote {
+  my ($state, $approved, $votes) = @_;
+  # TODO: use votesarray instead of votescheck
+  my (%approvedcheck, %votescheck);
+  my $raw_approved = "";
+  my @votesarray;
+  return unless %$approved or %$votes;
+
+  my $had_empty_line;
+
+  $. = 0;
+  open STATUS, "<", $STATUS;
+  open VOTES, ">", "$STATUS.$$.tmp";
+  while (<STATUS>) {
+    $had_empty_line = /\n\n\z/;
+    my $key = digest_string $_;
+
+    $approvedcheck{$key}++ if exists $approved->{$key};
+    $votescheck{$key}++ if exists $votes->{$key};
+
+    unless (exists $votes->{$key} or exists $approved->{$key}) {
+      print VOTES;
+      next;
+    }
+
+    unless (exists $votes->{$key}) {
+      push @votesarray, {
+        entry => $approved->{$key},
+        approval => 1,
+        digest => $key,
+      };
+      $raw_approved .= $_;
+      next;
+    }
+
+    # We have a vote, and potentially an approval.
+
+    my ($vote, $entry) = @{$votes->{$key}};
+    push @votesarray, {
+      entry => $entry,
+      vote => $vote,
+      approval => (exists $approved->{$key}),
+      digest => $key,
+    };
+
+    if ($vote eq 'edit') {
+      local $_ = $entry->{raw};
+      $votesarray[-1]->{digest} = digest_string $_;
+      (exists $approved->{$key}) ? ($raw_approved .= $_) : (print VOTES);
+      next;
+    }
+    
+    s/^(\s*\Q$vote\E:.*)/"$1, $AVAILID"/me
+    or s/(.*\w.*?\n)/"$1     $vote: $AVAILID\n"/se;
+    $_ = edit_string $_, $entry->{header}, trailing_eol => 2
+        if $vote ne '+1';
+    $votesarray[-1]->{digest} = digest_string $_;
+    (exists $approved->{$key}) ? ($raw_approved .= $_) : (print VOTES);
+  }
+  close STATUS;
+  print VOTES "\n" if $raw_approved and !$had_empty_line;
+  print VOTES $raw_approved;
+  close VOTES;
+  warn "Some vote chunks weren't found: ",
+    join ',',
+    map $votes->{$_}->[1]->{id},
+    grep { !$votescheck{$_} } keys %$votes
+    if scalar(keys %$votes) != scalar(keys %votescheck);
+  warn "Some approval chunks weren't found: ",
+    join ',',
+    map $approved->{$_}->{id},
+    grep { !$approvedcheck{$_} } keys %$approved
+    if scalar(keys %$approved) != scalar(keys %approvedcheck);
+  prompt "Press the 'any' key to continue...\n", dontprint => 1
+    if scalar(keys %$approved) != scalar(keys %approvedcheck) 
+    or scalar(keys %$votes) != scalar(keys %votescheck);
+  move "$STATUS.$$.tmp", $STATUS;
+
+  my $logmsg = do {
+    my @sentences = map {
+       my $words_vote = ", approving" x $_->{approval};
+       my $words_edit = " and approve" x $_->{approval};
+       exists $_->{vote}
+       ? (
+         ( $_->{vote} eq 'edit'
+           ? "Edit$words_edit the $_->{entry}->{id} entry"
+           : "Vote $_->{vote} on the $_->{entry}->{header}$words_vote"
+         )
+         . "."
+         )
+      : # exists only in $approved
+        "Approve the $_->{entry}->{header}."
+      } @votesarray;
+    (@sentences == 1)
+    ? $sentences[0]
+    : "* STATUS:\n" . join "", map "  $_\n", @sentences;
+  };
+
+  system "$SVN diff -- $STATUS";
+  printf "[[[\n%s%s]]]\n", $logmsg, ("\n" x ($logmsg !~ /\n\z/));
+  if (prompt "Commit these votes? ") {
+    my ($logmsg_fh, $logmsg_filename) = tempfile();
+    print $logmsg_fh $logmsg;
+    close $logmsg_fh;
+    warn("Tempfile name '$logmsg_filename' not shell-safe; "
+         ."refraining from commit.\n") and return
+        unless $logmsg_filename =~ /^([A-Z0-9._-]|\x2f)+$/i;
+    system("$SVN commit -F $logmsg_filename -- $STATUS") == 0
+        or warn("Committing the votes failed($?): $!") and return;
+    unlink $logmsg_filename;
+
+    $state->{$_->{digest}}++ for @votesarray;
+  }
+}
+
+sub revert {
+  copy $STATUS, "$STATUS.$$.tmp";
+  system "$SVN revert -q $STATUS";
+  system "$SVN revert -R ./" . ($YES && $MAY_COMMIT ne 'true'
+                             ? " -q" : "");
+  move "$STATUS.$$.tmp", $STATUS;
+}
+
+sub maybe_revert {
+  # This is both a SIGINT handler, and the tail end of main() in normal runs.
+  # @_ is 'INT' in the former case and () in the latter.
+  delete $SIG{INT} unless @_;
+  revert if !$YES and $MERGED_SOMETHING and prompt 'Revert? ';
+  (@_ ? exit : return);
+}
+
+sub warning_summary {
+  return unless %ERRORS;
+
+  warn "Warning summary\n";
+  warn "===============\n";
+  warn "\n";
+  for my $header (keys %ERRORS) {
+    warn "$header: $ERRORS{$header}\n";
+  }
+}
+
+sub read_state {
+  # die "$0: called read_state() in non-interactive mode!" if $YES;
+
+  open my $fh, '<', $STATEFILE or do {
+    return {} if $!{ENOENT};
+    die "Can't read statefile: $!";
+  };
+
+  my %rv;
+  while (<$fh>) {
+    chomp;
+    $rv{$_}++;
+  }
+  return \%rv;
+}
+
+sub write_state {
+  my $state = shift;
+  open STATE, '>', $STATEFILE or warn("Can't write state: $!"), return;
+  say STATE for keys %$state;
+  close STATE;
+}
+
+sub exit_stage_left {
+  my $state = shift;
+  maybe_revert;
+  warning_summary if $YES;
+  vote $state, @_;
+  write_state $state;
+  exit scalar keys %ERRORS;
+}
+
 sub handle_entry {
   my $in_approved = shift;
-  my %entry = parse_entry @_;
+  my $approved = shift;
+  my $votes = shift;
+  my $state = shift;
+  my $raw = shift;
+  my %entry = parse_entry $raw, @_;
   my @vetoes = grep { /^  -1:/ } @{$entry{votes}};
 
   if ($YES) {
-    merge %entry if $in_approved and not @vetoes;
+    # Run a merge if:
+    unless (@vetoes) {
+      if ($MAY_COMMIT eq 'true' and $in_approved) {
+        # svn-role mode
+        merge %entry;
+      } elsif ($MAY_COMMIT ne 'true') {
+        # Scan-for-conflicts mode
+        merge %entry;
+
+        my $output = `$SVN status`;
+        my (@conflicts) = ($output =~ m#^(?:C...|.C..|...C)...\s(.*)#mg);
+        if (@conflicts and !$entry{depends}) {
+          $ERRORS{$entry{id}} //= "Conflicts merging the $entry{header}: "
+                                  . (join ', ', map m#.*/(.*)#, @conflicts);
+          say STDERR "Conflicts merging the $entry{header}!";
+          say STDERR "";
+          say STDERR $output;
+          system "$SVN diff -- @conflicts";
+        } elsif (!@conflicts and $entry{depends}) {
+          # Not a warning since svn-role may commit the dependency without
+          # also committing the dependent in hte same pass.
+          print "No conflicts merging $entry{id}, but conflicts were "
+              ."expected ('Depends:' header set)\n";
+        } elsif (@conflicts) {
+          say "Conflicts found merging $entry{id}, as expected.";
+        }
+        revert;
+      }
+    }
+  } elsif ($state->{$entry{digest}}) {
+    print "\n\n";
+    say "Skipping the $entry{header} (remove $STATEFILE to reset):";
+    say $entry{logsummary}->[0], ('[...]' x (0 < $#{$entry{logsummary}}));
   } else {
-    print "";
-    print "\n>>> The $entry{header}:";
-    print join ", ", map { "r$_" } @{$entry{revisions}};
-    print "$BRANCHES/$entry{branch}" if $entry{branch};
-    print "";
-    print for @{$entry{logsummary}};
-    print "";
-    print for @{$entry{votes}};
-    print "";
-    print "Vetoes found!" if @vetoes;
-
-    if (prompt 'Go ahead?') {
-      merge %entry;
-      MAYBE_DIFF: while (1) { 
-        given (prompt "Shall I open a subshell? [ydN]", verbose => 1) {
-          when (/^y/i) {
-            system($ENV{SHELL} // "/bin/sh") == 0
-              or warn "Creating an interactive subshell failed ($?): $!"
-          }
-          when (/^d/) {
-            system($SVN, 'diff') == 0
-              or warn "diff failed ($?): $!";
-            next;
+    # This loop is just a hack because 'goto' panics.  The goto should be where
+    # the "next PROMPT;" is; there's a "last;" at the end of the loop body.
+    PROMPT: while (1) {
+    say "";
+    say "\n>>> The $entry{header}:";
+    say join ", ", map { "r$_" } @{$entry{revisions}} if @{$entry{revisions}};
+    say "$BRANCHES/$entry{branch}" if $entry{branch};
+    say "";
+    say for @{$entry{logsummary}};
+    say "";
+    say for @{$entry{votes}};
+    say "";
+    say "Vetoes found!" if @vetoes;
+
+    # See above for why the while(1).
+    QUESTION: while (1) {
+    my $key = $entry{digest};
+    given (prompt 'Run a merge? [y,l,±1,±0,q,e,a, ,N] ',
+                   verbose => 1, extra => qr/[+-]/) {
+      when (/^y/i) {
+        merge %entry;
+        while (1) { 
+          given (prompt "Shall I open a subshell? [ydN] ", verbose => 1) {
+            when (/^y/i) {
+              system($SHELL) == 0
+                or warn "Creating an interactive subshell failed ($?): $!"
+            }
+            when (/^d/) {
+              system("$SVN diff | $PAGER") == 0
+                or warn "diff failed ($?): $!";
+              next;
+            }
           }
+          revert;
+          next PROMPT;
         }
-      last;
+        # NOTREACHED
+      }
+      when (/^l/i) {
+        if ($entry{branch}) {
+            system "$SVN log --stop-on-copy -v -r 0:HEAD -- "
+                   ."$BRANCHES/$entry{branch} "
+                   ."| $PAGER";
+        } elsif (@{$entry{revisions}}) {
+            system "$SVN log ".(join ' ', map { "-r$_" } @{$entry{revisions}})
+                   ." -- ^/subversion | $PAGER";
+        } else {
+            die "Assertion failed: entry has neither branch nor revisions:\n",
+                '[[[', (join ';;', %entry), ']]]';
+        }
+        next PROMPT;
+      }
+      when (/^q/i) {
+        exit_stage_left $state, $approved, $votes;
+      }
+      when (/^a/i) {
+        $approved->{$key} = \%entry;
+        next PROMPT;
+      }
+      when (/^([+-][01])\s*$/i) {
+        $votes->{$key} = [$1, \%entry];
+        say "Your '$1' vote has been recorded." if $VERBOSE;
+        last PROMPT;
+      }
+      when (/^e/i) {
+        my $original = $entry{raw};
+        $entry{raw} = edit_string $entry{raw}, $entry{header},
+                        trailing_eol => 2;
+        $votes->{$key} = ['edit', \%entry] # marker for the 2nd pass
+            if $original ne $entry{raw};
+        last PROMPT;
+      }
+      when (/^N/i) {
+        $state->{$entry{digest}}++;
+        last PROMPT;
+      }
+      when (/^\x20/) {
+        last PROMPT; # Fall off the end of the given/when block.
+      }
+      default {
+        say "Please use one of the options in brackets (q to quit)!";
+        next QUESTION;
       }
-      # Don't revert.  The next merge() call will do that anyway, or maybe the
-      # user did in his interactive shell.
     }
+    last; } # QUESTION
+    last; } # PROMPT
   }
 
   # TODO: merge() changes ./STATUS, which we're reading below, but
@@ -277,43 +659,51 @@ sub handle_entry {
   1;
 }
 
-sub maybe_revert {
-  # This is both a SIGINT handler, and the tail end of main() in normal runs.
-  system $SVN, qw/revert -R ./ if !$YES and prompt 'Revert? ';
-  exit if @_;
-}
-
+
 sub main {
+  my %approved;
+  my %votes;
+  my $state = read_state;
+
   usage, exit 0 if @ARGV;
 
   open STATUS, "<", $STATUS or (usage, exit 1);
 
   # Because we use the ':normal' command in Vim...
-  die "A vim with the +ex_extra feature is required"
-      if `${VIM} --version` !~ /[+]ex_extra/;
+  die "A vim with the +ex_extra feature is required for \$MAY_COMMIT mode"
+      if $MAY_COMMIT eq 'true' and `${VIM} --version` !~ /[+]ex_extra/;
 
   # ### TODO: need to run 'revert' here
   # ### TODO: both here and in merge(), unlink files that previous merges added
   # When running from cron, there shouldn't be local mods.  (For interactive
   # usage, we preserve local mods to STATUS.)
-  die "Local mods to STATUS file $STATUS" if $YES and `$SVN status -q $STATUS`;
+  system("$SVN info $STATUS >/dev/null") == 0
+    or die "$0: svn error; point \$SVN to an appropriate binary";
+
+  if (`$SVN status -q $STATUS`) {
+    die  "Local mods to STATUS file $STATUS" if $YES;
+    warn "Local mods to STATUS file $STATUS";
+    system "$SVN diff -- $STATUS";
+    prompt "Press the 'any' key to continue...\n", dontprint => 1;
+  }
 
   # Skip most of the file
+  $/ = ""; # paragraph mode
   while (<STATUS>) {
     last if /^Status of \d+\.\d+/;
   }
-  $/ = ""; # paragraph mode
 
-  $SIG{INT} = \&maybe_revert;
+  $SIG{INT} = \&maybe_revert unless $YES;
 
   my $in_approved = 0;
   while (<STATUS>) {
+    my $lines = $_;
     my @lines = split /\n/;
 
     given ($lines[0]) {
       # Section header
       when (/^[A-Z].*:$/i) {
-        print "\n\n=== $lines[0]" unless $YES;
+        say "\n\n=== $lines[0]" unless $YES;
         $in_approved = $lines[0] =~ /^Approved changes/;
       }
       # Comment
@@ -328,15 +718,15 @@ sub main {
       when (/^ \*/) {
         warn "Too many bullets in $lines[0]" and next
           if grep /^ \*/, @lines[1..$#lines];
-        handle_entry $in_approved, @lines;
+        handle_entry $in_approved, \%approved, \%votes, $state, $lines, @lines;
       }
       default {
-        warn "Unknown entry '$lines[0]' at line $.\n";
+        warn "Unknown entry '$lines[0]'";
       }
     }
   }
 
-  maybe_revert;
+  exit_stage_left $state, \%approved, \%votes;
 }
 
 &main

Modified: subversion/branches/fsfs-format7/win-tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/fsfs-format7/win-tests.py?rev=1507012&r1=1507011&r2=1507012&view=diff
==============================================================================
--- subversion/branches/fsfs-format7/win-tests.py (original)
+++ subversion/branches/fsfs-format7/win-tests.py Thu Jul 25 15:29:49 2013
@@ -84,6 +84,8 @@ def _usage_exit():
   print("  --disable-bulk-updates : Disable bulk updates on HTTP server")
   print("  --ssl-cert             : Path to SSL server certificate to trust.")
   print("  --javahl               : Run the javahl tests instead of the normal tests")
+  print("  --swig=language        : Run the swig perl/python/ruby tests instead of")
+  print("                           the normal tests")
   print("  --list                 : print test doc strings only")
   print("  --milestone-filter=RE  : RE is a regular expression pattern that (when")
   print("                           used with --list) limits the tests listed to")
@@ -108,29 +110,24 @@ CMDLINE_TEST_SCRIPT_NATIVE_PATH = CMDLIN
 sys.path.insert(0, os.path.join('build', 'generator'))
 sys.path.insert(1, 'build')
 
-import gen_win
+import gen_win_dependencies
+import gen_base
 version_header = os.path.join('subversion', 'include', 'svn_version.h')
 cp = configparser.ConfigParser()
 cp.read('gen-make.opts')
-gen_obj = gen_win.GeneratorBase('build.conf', version_header,
-                                cp.items('options'))
+gen_obj = gen_win_dependencies.GenDependenciesBase('build.conf', version_header,
+                                                   cp.items('options'))
 all_tests = gen_obj.test_progs + gen_obj.bdb_test_progs \
           + gen_obj.scripts + gen_obj.bdb_scripts
 client_tests = [x for x in all_tests if x.startswith(CMDLINE_TEST_SCRIPT_PATH)]
 
-svn_dlls = []
-for section in gen_obj.sections.values():
-  if section.options.get("msvc-export"):
-    dll_basename = section.name + "-" + str(gen_obj.version) + ".dll"
-    svn_dlls.append(os.path.join("subversion", section.name, dll_basename))
-
 opts, args = my_getopt(sys.argv[1:], 'hrdvqct:pu:f:',
                        ['release', 'debug', 'verbose', 'quiet', 'cleanup',
                         'test=', 'url=', 'svnserve-args=', 'fs-type=', 'asp.net-hack',
                         'httpd-dir=', 'httpd-port=', 'httpd-daemon',
                         'httpd-server', 'http-short-circuit', 'httpd-no-log',
                         'disable-http-v2', 'disable-bulk-updates', 'help',
-                        'fsfs-packing', 'fsfs-sharding=', 'javahl',
+                        'fsfs-packing', 'fsfs-sharding=', 'javahl', 'swig=',
                         'list', 'enable-sasl', 'bin=', 'parallel',
                         'config-file=', 'server-minor-version=', 'log-level=',
                         'log-to-stdout', 'mode-filter=', 'milestone-filter=',
@@ -156,6 +153,7 @@ http_bulk_updates = True
 list_tests = None
 milestone_filter = None
 test_javahl = None
+test_swig = None
 enable_sasl = None
 svn_bin = None
 parallel = None
@@ -216,6 +214,11 @@ for opt, val in opts:
     fsfs_packing = 1
   elif opt == '--javahl':
     test_javahl = 1
+  elif opt == '--swig':
+    if val not in ['perl', 'python', 'ruby']:
+      sys.stderr.write('Running \'%s\' swig tests not supported (yet).\n' 
+                        % (val,))
+    test_swig = val
   elif opt == '--list':
     list_tests = 1
   elif opt == '--milestone-filter':
@@ -289,12 +292,18 @@ def create_target_dir(dirname):
       print("mkdir: %s" % tgt_dir)
     os.makedirs(tgt_dir)
 
-def copy_changed_file(src, tgt):
+def copy_changed_file(src, tgt=None, to_dir=None, cleanup=True):
   if not os.path.isfile(src):
     print('Could not find ' + src)
     sys.exit(1)
-  if os.path.isdir(tgt):
-    tgt = os.path.join(tgt, os.path.basename(src))
+
+  if to_dir and not tgt:
+    tgt = os.path.join(to_dir, os.path.basename(src))
+  elif not tgt or (tgt and to_dir):
+    raise RuntimeError("Using 'tgt' *or* 'to_dir' is required" % (tgt,))
+  elif tgt and os.path.isdir(tgt):
+    raise RuntimeError("'%s' is a directory. Use to_dir=" % (tgt,))
+
   if os.path.exists(tgt):
     assert os.path.isfile(tgt)
     if filecmp.cmp(src, tgt):
@@ -306,57 +315,35 @@ def copy_changed_file(src, tgt):
     print("copy: %s" % src)
     print("  to: %s" % tgt)
   shutil.copy(src, tgt)
-  return 1
 
-def copy_execs(baton, dirname, names):
-  copied_execs = baton
-  for name in names:
-    if not name.endswith('.exe'):
-      continue
-    src = os.path.join(dirname, name)
-    tgt = os.path.join(abs_builddir, dirname, name)
-    create_target_dir(dirname)
-    if copy_changed_file(src, tgt):
-      copied_execs.append(tgt)
+  if cleanup:
+    copied_execs.append(tgt)
 
 def locate_libs():
   "Move DLLs to a known location and set env vars"
 
-  dlls = []
-
-  # look for APR 1.x dll's and use those if found
-  apr_test_path = os.path.join(gen_obj.apr_path, objdir, 'libapr-1.dll')
-  if os.path.exists(apr_test_path):
-    suffix = "-1"
-  else:
-    suffix = ""
-
-  if cp.has_option('options', '--with-static-apr'):
-    dlls.append(os.path.join(gen_obj.apr_path, objdir,
-                             'libapr%s.dll' % (suffix)))
-    dlls.append(os.path.join(gen_obj.apr_util_path, objdir,
-                             'libaprutil%s.dll' % (suffix)))
-
-  if gen_obj.libintl_path is not None:
-    dlls.append(os.path.join(gen_obj.libintl_path, 'bin', 'intl3_svn.dll'))
+  debug = (objdir == 'Debug')
+  
+  for lib in gen_obj._libraries.values():
+
+    if debug:
+      name, dir = lib.debug_dll_name, lib.debug_dll_dir
+    else:
+      name, dir = lib.dll_name, lib.dll_dir
+      
+    if name and dir:
+      src = os.path.join(dir, name)
+      if os.path.exists(src):
+        copy_changed_file(src, to_dir=abs_builddir, cleanup=False)
 
-  if gen_obj.bdb_lib is not None:
-    partial_path = os.path.join(gen_obj.bdb_path, 'bin', gen_obj.bdb_lib)
-    if objdir == 'Debug':
-      dlls.append(partial_path + 'd.dll')
-    else:
-      dlls.append(partial_path + '.dll')
-
-  if gen_obj.sasl_path is not None:
-    dlls.append(os.path.join(gen_obj.sasl_path, 'lib', 'libsasl.dll'))
-
-  for dll in dlls:
-    copy_changed_file(dll, abs_objdir)
 
   # Copy the Subversion library DLLs
-  if not cp.has_option('options', '--disable-shared'):
-    for svn_dll in svn_dlls:
-      copy_changed_file(os.path.join(abs_objdir, svn_dll), abs_objdir)
+  for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL):
+    if isinstance(i, gen_base.TargetLib) and i.msvc_export:
+      src = os.path.join(abs_objdir, i.filename)
+      if os.path.isfile(src):
+        copy_changed_file(src, to_dir=abs_builddir,
+                          cleanup=False)
 
   # Copy the Apache modules
   if run_httpd and cp.has_option('options', '--with-httpd'):
@@ -367,11 +354,11 @@ def locate_libs():
     mod_dontdothat_path = os.path.join(abs_objdir, 'tools', 'server-side',
                                         'mod_dontdothat', 'mod_dontdothat.so')
 
-    copy_changed_file(mod_dav_svn_path, abs_objdir)
-    copy_changed_file(mod_authz_svn_path, abs_objdir)
-    copy_changed_file(mod_dontdothat_path, abs_objdir)
+    copy_changed_file(mod_dav_svn_path, to_dir=abs_builddir, cleanup=False)
+    copy_changed_file(mod_authz_svn_path, to_dir=abs_builddir, cleanup=False)
+    copy_changed_file(mod_dontdothat_path, to_dir=abs_builddir, cleanup=False)
 
-  os.environ['PATH'] = abs_objdir + os.pathsep + os.environ['PATH']
+  os.environ['PATH'] = abs_builddir + os.pathsep + os.environ['PATH']
 
 def fix_case(path):
     path = os.path.normpath(path)
@@ -688,21 +675,17 @@ class Httpd:
     print('Httpd.stop_daemon not implemented')
 
 # Move the binaries to the test directory
+create_target_dir(abs_builddir)
 locate_libs()
 if create_dirs:
-  old_cwd = os.getcwd()
-  try:
-    os.chdir(abs_objdir)
-    baton = copied_execs
-    for dirpath, dirs, files in os.walk('subversion'):
-      copy_execs(baton, dirpath, files)
-    for dirpath, dirs, files in os.walk('tools/server-side'):
-      copy_execs(baton, dirpath, files)
-  except:
-    os.chdir(old_cwd)
-    raise
-  else:
-    os.chdir(old_cwd)
+  for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL):
+    if isinstance(i, gen_base.TargetExe):
+      src = os.path.join(abs_objdir, i.filename)
+
+      if os.path.isfile(src):
+        dst = os.path.join(abs_builddir, i.filename)
+        create_target_dir(os.path.dirname(dst))
+        copy_changed_file(src, dst)
 
 # Create the base directory for Python tests
 create_target_dir(CMDLINE_TEST_SCRIPT_NATIVE_PATH)
@@ -760,7 +743,7 @@ else:
   print('Testing %s configuration on %s' % (objdir, repo_loc))
 sys.path.insert(0, os.path.join(abs_srcdir, 'build'))
 
-if not test_javahl:
+if not test_javahl and not test_swig:
   import run_tests
   if log_to_stdout:
     log_file = None
@@ -788,7 +771,7 @@ if not test_javahl:
     raise
   else:
     os.chdir(old_cwd)
-else:
+elif test_javahl:
   failed = False
   args = (
           'java.exe',
@@ -826,6 +809,148 @@ else:
   if (r != 0):
     print('[Test runner reported failure]')
     failed = True
+elif test_swig == 'perl':
+  failed = False
+  swig_dir = os.path.join(abs_builddir, 'swig')
+  swig_pl_dir = os.path.join(swig_dir, 'p5lib')
+  swig_pl_svn = os.path.join(swig_pl_dir, 'SVN')
+  swig_pl_auto_svn = os.path.join(swig_pl_dir, 'auto', 'SVN')
+
+  create_target_dir(swig_pl_svn)
+
+  for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL):
+    if isinstance(i, gen_base.TargetSWIG) and i.lang == 'perl':
+      mod_dir = os.path.join(swig_pl_auto_svn, '_' + i.name[5:].capitalize())
+      create_target_dir(mod_dir)
+      copy_changed_file(os.path.join(abs_objdir, i.filename), to_dir=mod_dir)
+
+    elif isinstance(i, gen_base.TargetSWIGLib) and i.lang == 'perl':
+      copy_changed_file(os.path.join(abs_objdir, i.filename),
+                        to_dir=abs_builddir)
+
+  pm_src = os.path.join(abs_srcdir, 'subversion', 'bindings', 'swig', 'perl',
+                        'native')
+
+  tests = []
+
+  for root, dirs, files in os.walk(pm_src):
+    for name in files:
+      if name.endswith('.pm'):
+        fn = os.path.join(root, name)
+        copy_changed_file(fn, to_dir=swig_pl_svn)
+      elif name.endswith('.t'):
+        tests.append(os.path.relpath(os.path.join(root, name), pm_src))
+
+  perl5lib = swig_pl_dir
+  if 'PERL5LIB' in os.environ:
+    perl5lib += os.pathsep + os.environ['PERL5LIB']
+
+  perl_exe = 'perl.exe'
+
+  print('-- Running Swig Perl tests --')
+  old_cwd = os.getcwd()
+  try:
+    os.chdir(pm_src)
+
+    os.environ['PERL5LIB'] = perl5lib
+    os.environ["SVN_DBG_NO_ABORT_ON_ERROR_LEAK"] = 'YES'
+
+    r = subprocess.call([
+              perl_exe,
+              '-MExtUtils::Command::MM',
+              '-e', 'test_harness()'
+              ] + tests)
+  finally:
+    os.chdir(old_cwd)
+
+  if (r != 0):
+    print('[Test runner reported failure]')
+    failed = True
+
+elif test_swig == 'python':
+  failed = False
+  swig_dir = os.path.join(abs_builddir, 'swig')
+  swig_py_dir = os.path.join(swig_dir, 'pylib')
+  swig_py_libsvn = os.path.join(swig_py_dir, 'libsvn')
+  swig_py_svn = os.path.join(swig_py_dir, 'svn')
+
+  create_target_dir(swig_py_libsvn)
+  create_target_dir(swig_py_svn)
+
+  for i in gen_obj.graph.get_all_sources(gen_base.DT_INSTALL):
+    if (isinstance(i, gen_base.TargetSWIG)
+        or isinstance(i, gen_base.TargetSWIGLib)) and i.lang == 'python':
+
+      src = os.path.join(abs_objdir, i.filename)
+      copy_changed_file(src, to_dir=swig_py_libsvn)
+
+  py_src = os.path.join(abs_srcdir, 'subversion', 'bindings', 'swig', 'python')
+
+  for py_file in os.listdir(py_src):
+    if py_file.endswith('.py'):
+      copy_changed_file(os.path.join(py_src, py_file),
+                        to_dir=swig_py_libsvn)
+
+  py_src_svn = os.path.join(py_src, 'svn')
+  for py_file in os.listdir(py_src_svn):
+    if py_file.endswith('.py'):
+      copy_changed_file(os.path.join(py_src_svn, py_file),
+                        to_dir=swig_py_svn)
+
+  print('-- Running Swig Python tests --')
+
+  pythonpath = swig_py_dir
+  if 'PYTHONPATH' in os.environ:
+    pythonpath += os.pathsep + os.environ['PYTHONPATH']
+
+  python_exe = 'python.exe'
+  old_cwd = os.getcwd()
+  try:
+    os.environ['PYTHONPATH'] = pythonpath
+
+    r = subprocess.call([
+              python_exe,
+              os.path.join(py_src, 'tests', 'run_all.py')
+              ])
+  finally:
+    os.chdir(old_cwd)
+
+    if (r != 0):
+      print('[Test runner reported failure]')
+      failed = True
+
+elif test_swig == 'ruby':
+  failed = False
+
+  if 'ruby' not in gen_obj._libraries:
+    print('Ruby not found. Skipping Ruby tests')
+  else:
+    ruby_lib = gen_obj._libraries['ruby']
+
+    ruby_exe = 'ruby.exe'
+    ruby_subdir = os.path.join('subversion', 'bindings', 'swig', 'ruby')
+    ruby_args = [
+        '-I', os.path.join(abs_srcdir, ruby_subdir),
+        os.path.join(abs_srcdir, ruby_subdir, 'test', 'run-test.rb'),
+        '--verbose'
+      ]
+
+    print('-- Running Swig Ruby tests --')
+    old_cwd = os.getcwd()
+    try:
+      os.chdir(ruby_subdir)
+
+      os.environ["BUILD_TYPE"] = objdir
+      os.environ["SVN_DBG_NO_ABORT_ON_ERROR_LEAK"] = 'YES'
+      r = subprocess.call([ruby_exe] + ruby_args)
+    finally:
+      os.chdir(old_cwd)
+
+    sys.stdout.flush()
+    sys.stderr.flush()
+    if (r != 0):
+      print('[Test runner reported failure]')
+      failed = True
 
 # Stop service daemon, if any
 if daemon: