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/10/15 10:52:18 UTC
svn commit: r1532250 [37/37] - in /subversion/branches/cache-server: ./
build/ build/ac-macros/ build/generator/ build/generator/swig/
build/generator/templates/ contrib/client-side/emacs/ contrib/hook-scripts/
contrib/server-side/fsfsfixer/ contrib/se...
Modified: subversion/branches/cache-server/tools/dist/release.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/dist/release.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/dist/release.py (original)
+++ subversion/branches/cache-server/tools/dist/release.py Tue Oct 15 08:52:06 2013
@@ -66,12 +66,33 @@ except ImportError:
import ezt
+try:
+ subprocess.check_output
+except AttributeError:
+ def check_output(cmd):
+ proc = subprocess.Popen(['svn', 'list', dist_dev_url],
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE)
+ (stdout, stderr) = proc.communicate()
+ rc = proc.wait()
+ if rc or stderr:
+ logging.error('%r failed with stderr %r', cmd, stderr)
+ raise subprocess.CalledProcessError(rc, cmd)
+ return stdout
+ subprocess.check_output = check_output
+ del check_output
+
# Our required / recommended release tool versions by release branch
tool_versions = {
'trunk' : {
- 'autoconf' : '2.68',
- 'libtool' : '2.4',
- 'swig' : '2.0.4',
+ 'autoconf' : '2.69',
+ 'libtool' : '2.4.2',
+ 'swig' : '2.0.9',
+ },
+ '1.8' : {
+ 'autoconf' : '2.69',
+ 'libtool' : '2.4.2',
+ 'swig' : '2.0.9',
},
'1.7' : {
'autoconf' : '2.68',
@@ -99,7 +120,7 @@ extns = ['zip', 'tar.gz', 'tar.bz2']
# Utility functions
class Version(object):
- regex = re.compile('(\d+).(\d+).(\d+)(?:-(?:(rc|alpha|beta)(\d+)))?')
+ regex = re.compile(r'(\d+).(\d+).(\d+)(?:-(?:(rc|alpha|beta)(\d+)))?')
def __init__(self, ver_str):
# Special case the 'trunk-nightly' version
@@ -155,7 +176,7 @@ class Version(object):
else:
return self.pre_num < that.pre_num
- def __str(self):
+ def __str__(self):
if self.pre:
if self.pre == 'nightly':
return 'nightly'
@@ -168,11 +189,7 @@ class Version(object):
def __repr__(self):
- return "Version('%s')" % self.__str()
-
- def __str__(self):
- return self.__str()
-
+ return "Version(%s)" % repr(str(self))
def get_prefix(base_dir):
return os.path.join(base_dir, 'prefix')
@@ -183,6 +200,13 @@ def get_tempdir(base_dir):
def get_deploydir(base_dir):
return os.path.join(base_dir, 'deploy')
+def get_target(args):
+ "Return the location of the artifacts"
+ if args.target:
+ return args.target
+ else:
+ return get_deploydir(args.base_dir)
+
def get_tmpldir():
return os.path.join(os.path.abspath(sys.path[0]), 'templates')
@@ -194,8 +218,7 @@ def get_tmplfile(filename):
return urllib2.urlopen(repos + '/trunk/tools/dist/templates/' + filename)
def get_nullfile():
- # This is certainly not cross platform
- return open('/dev/null', 'w')
+ return open(os.path.devnull, 'w')
def run_script(verbose, script):
if verbose:
@@ -371,12 +394,7 @@ def compare_changes(repos, branch, revis
mergeinfo_cmd = ['svn', 'mergeinfo', '--show-revs=eligible',
repos + '/trunk/CHANGES',
repos + '/' + branch + '/' + 'CHANGES']
- proc = subprocess.Popen(mergeinfo_cmd, stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- rc = proc.wait()
- if stderr:
- raise RuntimeError('svn mergeinfo failed: %s' % stderr)
+ stdout = subprocess.check_output(mergeinfo_cmd)
if stdout:
# Treat this as a warning since we are now putting entries for future
# minor releases in CHANGES on trunk.
@@ -463,15 +481,11 @@ def sign_candidates(args):
def sign_file(filename):
asc_file = open(filename + '.asc', 'a')
logging.info("Signing %s" % filename)
- proc = subprocess.Popen(['gpg', '-ba', '-o', '-', filename],
- stdout=asc_file)
- proc.wait()
+ proc = subprocess.check_call(['gpg', '-ba', '-o', '-', filename],
+ stdout=asc_file)
asc_file.close()
- if args.target:
- target = args.target
- else:
- target = get_deploydir(args.base_dir)
+ target = get_target(args)
for e in extns:
filename = os.path.join(target, 'subversion-%s.%s' % (args.version, e))
@@ -488,17 +502,17 @@ def sign_candidates(args):
def post_candidates(args):
'Post candidate artifacts to the dist development directory.'
+ target = get_target(args)
+
logging.info('Importing tarballs to %s' % dist_dev_url)
svn_cmd = ['svn', 'import', '-m',
'Add %s candidate release artifacts' % args.version.base,
'--auto-props', '--config-option',
'config:auto-props:*.asc=svn:eol-style=native;svn:mime-type=text/plain',
- get_deploydir(args.base_dir), dist_dev_url]
+ target, dist_dev_url]
if (args.username):
svn_cmd += ['--username', args.username]
- proc = subprocess.Popen(svn_cmd)
- (stdout, stderr) = proc.communicate()
- proc.wait()
+ subprocess.check_call(svn_cmd)
#----------------------------------------------------------------------
# Create tag
@@ -513,6 +527,7 @@ def create_tag(args):
else:
branch = secure_repos + '/branches/%d.%d.x' % (args.version.major,
args.version.minor)
+ target = get_target(args)
tag = secure_repos + '/tags/' + str(args.version)
@@ -521,13 +536,63 @@ def create_tag(args):
if (args.username):
svnmucc_cmd += ['--username', args.username]
svnmucc_cmd += ['cp', str(args.revnum), branch, tag]
- svnmucc_cmd += ['put', os.path.join(get_deploydir(args.base_dir),
- 'svn_version.h.dist'),
+ svnmucc_cmd += ['put', os.path.join(target, 'svn_version.h.dist' + '-' +
+ str(args.version)),
tag + '/subversion/include/svn_version.h']
# don't redirect stdout/stderr since svnmucc might ask for a password
- proc = subprocess.Popen(svnmucc_cmd)
- proc.wait()
+ subprocess.check_call(svnmucc_cmd)
+
+ if not args.version.is_prerelease():
+ logging.info('Bumping revisions on the branch')
+ def replace_in_place(fd, startofline, flat, spare):
+ """In file object FD, replace FLAT with SPARE in the first line
+ starting with STARTOFLINE."""
+
+ fd.seek(0, os.SEEK_SET)
+ lines = fd.readlines()
+ for i, line in enumerate(lines):
+ if line.startswith(startofline):
+ lines[i] = line.replace(flat, spare)
+ break
+ else:
+ raise RuntimeError('Definition of %r not found' % startofline)
+
+ fd.seek(0, os.SEEK_SET)
+ fd.writelines(lines)
+ fd.truncate() # for current callers, new value is never shorter.
+
+ new_version = Version('%d.%d.%d' %
+ (args.version.major, args.version.minor,
+ args.version.patch + 1))
+
+ def file_object_for(relpath):
+ fd = tempfile.NamedTemporaryFile()
+ url = branch + '/' + relpath
+ fd.url = url
+ subprocess.check_call(['svn', 'cat', '%s@%d' % (url, args.revnum)],
+ stdout=fd)
+ return fd
+
+ svn_version_h = file_object_for('subversion/include/svn_version.h')
+ replace_in_place(svn_version_h, '#define SVN_VER_PATCH ',
+ str(args.version.patch), str(new_version.patch))
+
+ STATUS = file_object_for('STATUS')
+ replace_in_place(STATUS, 'Status of ',
+ str(args.version), str(new_version))
+
+ svn_version_h.seek(0, os.SEEK_SET)
+ STATUS.seek(0, os.SEEK_SET)
+ subprocess.check_call(['svnmucc', '-r', str(args.revnum),
+ '-m', 'Post-release housekeeping: '
+ 'bump the %s branch to %s.'
+ % (branch.split('/')[-1], str(new_version)),
+ 'put', svn_version_h.name, svn_version_h.url,
+ 'put', STATUS.name, STATUS.url,
+ ])
+ del svn_version_h
+ del STATUS
#----------------------------------------------------------------------
# Clean dist
@@ -535,13 +600,7 @@ def create_tag(args):
def clean_dist(args):
'Clean the distribution directory of all but the most recent artifacts.'
- proc = subprocess.Popen(['svn', 'list', dist_release_url],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- proc.wait()
- if stderr:
- raise RuntimeError(stderr)
+ stdout = subprocess.check_output(['svn', 'list', dist_release_url])
filenames = stdout.split('\n')
tar_gz_archives = []
@@ -570,8 +629,7 @@ def clean_dist(args):
svnmucc_cmd += ['rm', dist_release_url + '/' + filename]
# don't redirect stdout/stderr since svnmucc might ask for a password
- proc = subprocess.Popen(svnmucc_cmd)
- proc.wait()
+ subprocess.check_call(svnmucc_cmd)
#----------------------------------------------------------------------
# Move to dist
@@ -579,13 +637,7 @@ def clean_dist(args):
def move_to_dist(args):
'Move candidate artifacts to the distribution directory.'
- proc = subprocess.Popen(['svn', 'list', dist_dev_url],
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE)
- (stdout, stderr) = proc.communicate()
- proc.wait()
- if stderr:
- raise RuntimeError(stderr)
+ stdout = subprocess.check_output(['svn', 'list', dist_dev_url])
filenames = []
for entry in stdout.split('\n'):
@@ -603,8 +655,7 @@ def move_to_dist(args):
# don't redirect stdout/stderr since svnmucc might ask for a password
logging.info('Moving release artifacts to %s' % dist_release_url)
- proc = subprocess.Popen(svnmucc_cmd)
- proc.wait()
+ subprocess.check_call(svnmucc_cmd)
#----------------------------------------------------------------------
# Write announcements
@@ -631,10 +682,7 @@ def write_news(args):
def get_sha1info(args, replace=False):
'Return a list of sha1 info for the release'
- if args.target:
- target = args.target
- else:
- target = get_deploydir(args.base_dir)
+ target = get_target(args)
sha1s = glob.glob(os.path.join(target, 'subversion*-%s*.sha1' % args.version))
@@ -708,10 +756,7 @@ def get_siginfo(args, quiet=False):
import _gnupg as gnupg
gpg = gnupg.GPG()
- if args.target:
- target = args.target
- else:
- target = get_deploydir(args.base_dir)
+ target = get_target(args)
good_sigs = {}
fingerprints = {}
@@ -842,6 +887,9 @@ def main():
help='''The release label, such as '1.7.0-alpha1'.''')
subparser.add_argument('--username',
help='''Username for ''' + dist_repos + '''.''')
+ subparser.add_argument('--target',
+ help='''The full path to the directory containing
+ release artifacts.''')
# Setup the parser for the create-tag subcommand
subparser = subparsers.add_parser('create-tag',
@@ -855,6 +903,9 @@ def main():
help='''The branch to base the release on.''')
subparser.add_argument('--username',
help='''Username for ''' + secure_repos + '''.''')
+ subparser.add_argument('--target',
+ help='''The full path to the directory containing
+ release artifacts.''')
# The clean-dist subcommand
subparser = subparsers.add_parser('clean-dist',
Modified: subversion/branches/cache-server/tools/dist/templates/download.ezt
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/dist/templates/download.ezt?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/dist/templates/download.ezt (original)
+++ subversion/branches/cache-server/tools/dist/templates/download.ezt Tue Oct 15 08:52:06 2013
@@ -1,4 +1,4 @@
-<p style="font-size: 150%; text-align: center;">Subversion [version]</p>
+<p style="font-size: 150%; text-align: center;">Apache Subversion [version]</p>
<table class="centered">
<tr>
<th>File</th>
Modified: subversion/branches/cache-server/tools/examples/blame.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/examples/blame.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/examples/blame.py (original)
+++ subversion/branches/cache-server/tools/examples/blame.py Tue Oct 15 08:52:06 2013
@@ -91,9 +91,9 @@ def blame(path, filename, rev=None):
# print ''.join(diffresult)
# print annotresult
for x in range(len(annotresult.keys())):
- sys.stdout.write("Line %d (rev %d):%s" % (x,
- annotresult[x][0],
- annotresult[x][1]))
+ sys.stdout.write("Line %d (r%d):%s" % (x,
+ annotresult[x][0],
+ annotresult[x][1]))
def usage():
print("USAGE: blame.py [-r REV] repos-path file")
Modified: subversion/branches/cache-server/tools/hook-scripts/mailer/mailer.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/hook-scripts/mailer/mailer.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/hook-scripts/mailer/mailer.py (original)
+++ subversion/branches/cache-server/tools/hook-scripts/mailer/mailer.py Tue Oct 15 08:52:06 2013
@@ -236,16 +236,30 @@ class MailedOutput(OutputBase):
and self.reply_to[2] == ']':
self.reply_to = self.reply_to[3:]
+ def _rfc2047_encode(self, hdr):
+ # Return the result of splitting HDR into tokens (on space
+ # characters), encoding (per RFC2047) each token as necessary, and
+ # slapping 'em back to together again.
+ from email.Header import Header
+
+ def _maybe_encode_header(hdr_token):
+ try:
+ hdr_token.encode('ascii')
+ return hdr_token
+ except UnicodeError:
+ return Header(hdr_token, 'utf-8').encode()
+
+ return ' '.join(map(_maybe_encode_header, hdr.split()))
+
def mail_headers(self, group, params):
from email import Utils
- subject = self.make_subject(group, params)
- try:
- subject.encode('ascii')
- except UnicodeError:
- from email.Header import Header
- subject = Header(subject, 'utf-8').encode()
- hdrs = 'From: %s\n' \
- 'To: %s\n' \
+
+ subject = self._rfc2047_encode(self.make_subject(group, params))
+ from_hdr = self._rfc2047_encode(self.from_addr)
+ to_hdr = self._rfc2047_encode(', '.join(self.to_addrs))
+
+ hdrs = 'From: %s\n' \
+ 'To: %s\n' \
'Subject: %s\n' \
'Date: %s\n' \
'Message-ID: %s\n' \
@@ -256,7 +270,7 @@ class MailedOutput(OutputBase):
'X-Svn-Commit-Author: %s\n' \
'X-Svn-Commit-Revision: %d\n' \
'X-Svn-Commit-Repository: %s\n' \
- % (self.from_addr, ', '.join(self.to_addrs), subject,
+ % (from_hdr, to_hdr, subject,
Utils.formatdate(), Utils.make_msgid(), group,
self.repos.author or 'no_author', self.repos.rev,
os.path.basename(self.repos.repos_dir))
Modified: subversion/branches/cache-server/tools/server-side/fsfs-stats.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/server-side/fsfs-stats.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/server-side/fsfs-stats.c (original)
+++ subversion/branches/cache-server/tools/server-side/fsfs-stats.c Tue Oct 15 08:52:06 2013
@@ -28,6 +28,7 @@
#include <apr_file_io.h>
#include <apr_poll.h>
+#include "svn_private_config.h"
#include "svn_pools.h"
#include "svn_diff.h"
#include "svn_io.h"
@@ -270,6 +271,12 @@ typedef struct fs_fs_t
/* history of sizes of changed nodes */
histogram_t node_size_histogram;
+ /* history of representation sizes */
+ histogram_t added_rep_size_histogram;
+
+ /* history of sizes of changed nodes */
+ histogram_t added_node_size_histogram;
+
/* history of unused representations */
histogram_t unused_rep_histogram;
@@ -486,6 +493,7 @@ add_to_histogram(histogram_t *histogram,
/* Update data aggregators in FS with this representation of type KIND, on-
* disk REP_SIZE and expanded node size EXPANDED_SIZE for PATH in REVSION.
+ * PLAIN_ADDED indicates whether the node has a deltification predecessor.
*/
static void
add_change(fs_fs_t *fs,
@@ -493,7 +501,8 @@ add_change(fs_fs_t *fs,
apr_int64_t expanded_size,
svn_revnum_t revision,
const char *path,
- rep_kind_t kind)
+ rep_kind_t kind,
+ svn_boolean_t plain_added)
{
/* identify largest reps */
if (rep_size >= fs->largest_changes->min_size)
@@ -522,6 +531,12 @@ add_change(fs_fs_t *fs,
add_to_histogram(&fs->rep_size_histogram, rep_size);
add_to_histogram(&fs->node_size_histogram, expanded_size);
+ if (plain_added)
+ {
+ add_to_histogram(&fs->added_rep_size_histogram, rep_size);
+ add_to_histogram(&fs->added_node_size_histogram, expanded_size);
+ }
+
/* specific histograms by type */
switch (kind)
{
@@ -1240,7 +1255,7 @@ parse_dir(fs_fs_t *fs,
next = current ? strchr(++current, '\n') : NULL;
if (next == NULL)
return svn_error_createf(SVN_ERR_FS_CORRUPT, NULL,
- _("Corrupt directory representation in rev %ld at offset %ld"),
+ _("Corrupt directory representation in r%ld at offset %ld"),
representation->revision,
(long)representation->offset);
@@ -1288,6 +1303,7 @@ read_noderev(fs_fs_t *fs,
representation_t *props = NULL;
apr_size_t start_offset = offset;
svn_boolean_t is_dir = FALSE;
+ svn_boolean_t has_predecessor = FALSE;
const char *path = "???";
scratch_pool = svn_pool_create(scratch_pool);
@@ -1348,15 +1364,17 @@ read_noderev(fs_fs_t *fs,
}
else if (key_matches(&key, "cpath"))
path = value.data;
+ else if (key_matches(&key, "pred"))
+ has_predecessor = TRUE;
}
/* record largest changes */
if (text && text->ref_count == 1)
add_change(fs, (apr_int64_t)text->size, (apr_int64_t)text->expanded_size,
- text->revision, path, text->kind);
+ text->revision, path, text->kind, !has_predecessor);
if (props && props->ref_count == 1)
add_change(fs, (apr_int64_t)props->size, (apr_int64_t)props->expanded_size,
- props->revision, path, props->kind);
+ props->revision, path, props->kind, !has_predecessor);
/* if this is a directory and has not been processed, yet, read and
* process it recursively */
@@ -1573,7 +1591,9 @@ read_revisions(fs_fs_t **fs,
svn_cache__get_global_membuffer_cache(),
NULL, NULL,
sizeof(window_cache_key_t),
- "", FALSE, pool));
+ "",
+ SVN_CACHE__MEMBUFFER_DEFAULT_PRIORITY,
+ FALSE, pool));
/* read all packed revs */
for ( revision = start_revision
@@ -2043,6 +2063,7 @@ print_stats(fs_fs_t *fs,
printf(_("%20s bytes in %12s representations total\n"
"%20s bytes in %12s directory representations\n"
"%20s bytes in %12s file representations\n"
+ "%20s bytes in %12s representations of added file nodes\n"
"%20s bytes in %12s directory property representations\n"
"%20s bytes in %12s file property representations\n"
"%20s bytes in header & footer overhead\n"),
@@ -2052,6 +2073,8 @@ print_stats(fs_fs_t *fs,
svn__i64toa_sep(dir_rep_stats.total.count, ',', pool),
svn__i64toa_sep(file_rep_stats.total.packed_size, ',', pool),
svn__i64toa_sep(file_rep_stats.total.count, ',', pool),
+ svn__i64toa_sep(fs->added_rep_size_histogram.total.sum, ',', pool),
+ svn__i64toa_sep(fs->added_rep_size_histogram.total.count, ',', pool),
svn__i64toa_sep(dir_prop_rep_stats.total.packed_size, ',', pool),
svn__i64toa_sep(dir_prop_rep_stats.total.count, ',', pool),
svn__i64toa_sep(file_prop_rep_stats.total.packed_size, ',', pool),
@@ -2169,13 +2192,13 @@ int main(int argc, const char *argv[])
svn_err = read_revisions(&fs, repo_path, start_revision, memsize, pool);
printf("\n");
- print_stats(fs, pool);
-
if (svn_err)
{
svn_handle_error2(svn_err, stdout, FALSE, ERROR_TAG);
return 2;
}
+ print_stats(fs, pool);
+
return 0;
}
Modified: subversion/branches/cache-server/tools/server-side/svn-populate-node-origins-index.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/server-side/svn-populate-node-origins-index.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/server-side/svn-populate-node-origins-index.c (original)
+++ subversion/branches/cache-server/tools/server-side/svn-populate-node-origins-index.c Tue Oct 15 08:52:06 2013
@@ -77,7 +77,8 @@ index_revision_adds(int *count, svn_fs_t
*count = 0;
SVN_ERR(svn_fs_revision_root(&root, fs, revision, pool));
- SVN_ERR(svn_fs_paths_changed2(&changes, root, pool));
+ SVN_ERR(svn_fs_paths_changed3(&changes, root,
+ svn_move_behavior_explicit_moves, pool));
/* No paths changed in this revision? Nothing to do. */
if (apr_hash_count(changes) == 0)
@@ -94,7 +95,9 @@ index_revision_adds(int *count, svn_fs_t
apr_hash_this(hi, &path, NULL, &val);
change = val;
if ((change->change_kind == svn_fs_path_change_add)
- || (change->change_kind == svn_fs_path_change_replace))
+ || (change->change_kind == svn_fs_path_change_replace)
+ || (change->change_kind == svn_fs_path_change_move)
+ || (change->change_kind == svn_fs_path_change_movereplace))
{
if (! (change->copyfrom_path
&& SVN_IS_VALID_REVNUM(change->copyfrom_rev)))
Modified: subversion/branches/cache-server/tools/server-side/svn-rep-sharing-stats.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/server-side/svn-rep-sharing-stats.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/server-side/svn-rep-sharing-stats.c (original)
+++ subversion/branches/cache-server/tools/server-side/svn-rep-sharing-stats.c Tue Oct 15 08:52:06 2013
@@ -33,6 +33,7 @@
#include "../../subversion/libsvn_fs_fs/fs_fs.h"
/* for svn_fs_fs__id_* (used in assertions only) */
#include "../../subversion/libsvn_fs_fs/id.h"
+#include "../../subversion/libsvn_fs_fs/cached_data.h"
#include "private/svn_cmdline_private.h"
@@ -95,7 +96,8 @@ check_lib_versions(void)
};
SVN_VERSION_DEFINE(my_version);
- return svn_error_trace(svn_ver_check_list(&my_version, checklist));
+ return svn_error_trace(svn_ver_check_list2(&my_version, checklist,
+ svn_ver_equal));
}
@@ -182,7 +184,8 @@ struct key_t
/* What we need to know about a rep. */
struct value_t
{
- svn_checksum_t *sha1_checksum;
+ svn_checksum_t checksum;
+ unsigned char sha1_digest[APR_SHA1_DIGESTSIZE];
apr_uint64_t refcount;
};
@@ -200,7 +203,7 @@ static svn_error_t *record(apr_hash_t *r
* exist or doesn't have the checksum we are after. (The latter case
* often corresponds to node_rev->kind == svn_node_dir.)
*/
- if (records == NULL || rep == NULL || rep->sha1_checksum == NULL)
+ if (records == NULL || rep == NULL || !rep->has_sha1)
return SVN_NO_ERROR;
/* Construct the key.
@@ -215,17 +218,19 @@ static svn_error_t *record(apr_hash_t *r
if ((value = apr_hash_get(records, key, sizeof(*key))))
{
/* Paranoia. */
- SVN_ERR_ASSERT(value->sha1_checksum != NULL);
- SVN_ERR_ASSERT(svn_checksum_match(value->sha1_checksum,
- rep->sha1_checksum));
+ SVN_ERR_ASSERT(memcmp(value->sha1_digest,
+ rep->sha1_digest,
+ sizeof(value->sha1_digest)));
/* Real work. */
value->refcount++;
}
else
{
value = apr_palloc(result_pool, sizeof(*value));
- value->sha1_checksum = svn_checksum_dup(rep->sha1_checksum, result_pool);
+ value->checksum.digest = value->sha1_digest;
+ value->checksum.kind = svn_checksum_sha1;
value->refcount = 1;
+ memcpy(value->sha1_digest, rep->sha1_digest, sizeof(value->sha1_digest));
}
/* Store them. */
@@ -264,7 +269,9 @@ process_one_revision(svn_fs_t *fs,
/* Get the changed paths. */
SVN_ERR(svn_fs_revision_root(&rev_root, fs, revnum, scratch_pool));
- SVN_ERR(svn_fs_paths_changed2(&paths_changed, rev_root, scratch_pool));
+ SVN_ERR(svn_fs_paths_changed3(&paths_changed, rev_root,
+ svn_move_behavior_explicit_moves,
+ scratch_pool));
/* Iterate them. */
/* ### use iterpool? */
@@ -340,7 +347,7 @@ pretty_print(const char *name,
SVN_ERR(svn_cmdline_printf(scratch_pool, "%s %" APR_UINT64_T_FMT " %s\n",
name, value->refcount,
svn_checksum_to_cstring_display(
- value->sha1_checksum,
+ &value->checksum,
scratch_pool)));
}
Modified: subversion/branches/cache-server/tools/server-side/svnauthz.c
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/server-side/svnauthz.c?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/server-side/svnauthz.c (original)
+++ subversion/branches/cache-server/tools/server-side/svnauthz.c Tue Oct 15 08:52:06 2013
@@ -59,7 +59,9 @@ static const apr_getopt_option_t options
{"repository", svnauthz__repos, 1, ("repository authz name")},
{"transaction", 't', 1, ("transaction id")},
{"is", svnauthz__is, 1,
- ("instead of outputing, tests if the access is ARG\n"
+ ("instead of outputting, test if the access is\n"
+ " "
+ "exactly ARG\n"
" "
"ARG can be one of the following values:\n"
" "
@@ -67,10 +69,12 @@ static const apr_getopt_option_t options
" "
" r read-only access\n"
" "
- " no no access\n")
+ " no no access")
},
- {"groups-file", svnauthz__groups_file, 1, ("path to the global groups file")},
- {"recursive", 'R', 0, ("recursive access to path")},
+ {"groups-file", svnauthz__groups_file, 1,
+ ("use the groups from file ARG")},
+ {"recursive", 'R', 0,
+ ("determine recursive access to PATH")},
{0, 0, 0, 0}
};
@@ -129,27 +133,32 @@ static const svn_opt_subcommand_desc2_t
{'t'} },
{"accessof", subcommand_accessof, {0} /* no aliases */,
("Print or test the permissions set by an authz file.\n"
- "usage: 1. svnauthz accessof [--username USER] [--groups-file GROUPS_FILE] TARGET\n"
- " 2. svnauthz accessof [--username USER] [--groups-file GROUPS_FILE] \\\n"
- " -t TXN REPOS_PATH FILE_PATH\n\n"
- " 1. Prints the access of USER based on TARGET.\n"
+ "usage: 1. svnauthz accessof TARGET\n"
+ " 2. svnauthz accessof -t TXN REPOS_PATH FILE_PATH\n"
+ "\n"
+ " 1. Prints the access of USER to PATH based on authorization file at TARGET.\n"
" TARGET can be a path to a file or an absolute file:// URL to an authz\n"
- " file in a repository, but cannot be a repository relative URL (^/).\n\n"
- " 2. Prints the access of USER based on authz file at FILE_PATH in the\n"
- " transaction TXN in the repository at REPOS_PATH.\n\n"
- " If the --username argument is omitted then access of an anonymous user\n"
- " will be printed. If --path argument is omitted prints if any access\n"
- " to the repo is allowed. If --groups-file is specified, the groups from\n"
- " GROUPS_FILE will be used.\n\n"
+ " file in a repository, but cannot be a repository relative URL (^/).\n"
+ "\n"
+ " 2. Prints the access of USER to PATH based on authz file at FILE_PATH in the\n"
+ " transaction TXN in the repository at REPOS_PATH.\n"
+ "\n"
+ " USER is the argument to the --username option; if that option is not\n"
+ " provided, then access of an anonymous user will be printed or tested.\n"
+ "\n"
+ " PATH is the argument to the --path option; if that option is not provided,\n"
+ " the maximal access to any path in the repository will be considered.\n"
+ "\n"
"Outputs one of the following:\n"
" rw write access (which also implies read)\n"
" r read access\n"
- " no no access\n\n"
+ " no no access\n"
+ "\n"
"Returns:\n"
- " 0 when syntax is OK and --is argument (if any) matches.\n"
+ " 0 when syntax is OK and '--is' argument (if any) matches.\n"
" 1 when syntax is invalid.\n"
" 2 operational error\n"
- " 3 when --is argument doesn't match\n"
+ " 3 when '--is' argument doesn't match\n"
),
{'t', svnauthz__username, svnauthz__path, svnauthz__repos, svnauthz__is,
svnauthz__groups_file, 'R'} },
Modified: subversion/branches/cache-server/tools/server-side/svnpubsub/commit-hook.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/server-side/svnpubsub/commit-hook.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/server-side/svnpubsub/commit-hook.py (original)
+++ subversion/branches/cache-server/tools/server-side/svnpubsub/commit-hook.py Tue Oct 15 08:52:06 2013
@@ -19,8 +19,10 @@
SVNLOOK="/usr/local/svn-install/current/bin/svnlook"
#SVNLOOK="/usr/local/bin/svnlook"
+HOST="127.0.0.1"
+PORT=2069
+
import sys
-import subprocess
try:
import simplejson as json
except ImportError:
@@ -28,35 +30,32 @@ except ImportError:
import urllib2
-HOST="127.0.0.1"
-PORT=2069
-
-def svncmd(cmd):
- return subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE)
+import svnpubsub.util
-def svncmd_uuid(repo):
- cmd = "%s uuid %s" % (SVNLOOK, repo)
- p = svncmd(cmd)
- return p.stdout.read().strip()
-
-def svncmd_info(repo, revision):
- cmd = "%s info -r %s %s" % (SVNLOOK, revision, repo)
- p = svncmd(cmd)
- data = p.stdout.read().split("\n")
+def svnlook(cmd, **kwargs):
+ args = [SVNLOOK] + cmd
+ return svnpubsub.util.check_output(args, **kwargs)
+
+def svnlook_uuid(repo):
+ cmd = ["uuid", "--", repo]
+ return svnlook(cmd).strip()
+
+def svnlook_info(repo, revision):
+ cmd = ["info", "-r", revision, "--", repo]
+ data = svnlook(cmd, universal_newlines=True).split("\n")
#print data
return {'author': data[0].strip(),
'date': data[1].strip(),
'log': "\n".join(data[3:]).strip()}
-def svncmd_changed(repo, revision):
- cmd = "%s changed -r %s %s" % (SVNLOOK, revision, repo)
- p = svncmd(cmd)
+def svnlook_changed(repo, revision):
+ cmd = ["changed", "-r", revision, "--", repo]
+ lines = svnlook(cmd, universal_newlines=True).split("\n")
changed = {}
- while True:
- line = p.stdout.readline()
- if not line:
- break
+ for line in lines:
line = line.strip()
+ if not line:
+ continue
(flags, filename) = (line[0:3], line[4:])
changed[filename] = {'flags': flags}
return changed
@@ -71,23 +70,23 @@ def do_put(body):
def main(repo, revision):
revision = revision.lstrip('r')
- i = svncmd_info(repo, revision)
+ i = svnlook_info(repo, revision)
data = {'type': 'svn',
'format': 1,
'id': int(revision),
'changed': {},
- 'repository': svncmd_uuid(repo),
+ 'repository': svnlook_uuid(repo),
'committer': i['author'],
'log': i['log'],
'date': i['date'],
}
- data['changed'].update(svncmd_changed(repo, revision))
+ data['changed'].update(svnlook_changed(repo, revision))
body = json.dumps(data)
do_put(body)
if __name__ == "__main__":
- if len(sys.argv) != 3:
- print "invalid args"
- sys.exit(0)
+ if len(sys.argv) not in (3, 4):
+ sys.stderr.write("invalid args\n")
+ sys.exit(1)
- main(sys.argv[1], sys.argv[2])
+ main(*sys.argv[1:3])
Modified: subversion/branches/cache-server/tools/server-side/svnpubsub/daemonize.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/server-side/svnpubsub/daemonize.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/server-side/svnpubsub/daemonize.py (original)
+++ subversion/branches/cache-server/tools/server-side/svnpubsub/daemonize.py Tue Oct 15 08:52:06 2013
@@ -24,6 +24,7 @@ import os
import signal
import sys
import time
+import multiprocessing # requires Python 2.6
# possible return values from Daemon.daemonize()
@@ -50,11 +51,11 @@ class Daemon(object):
def daemonize_exit(self):
try:
result = self.daemonize()
- except (ChildFailed, DaemonFailed) as e:
+ except (ChildFailed, DaemonFailed), e:
# duplicate the exit code
sys.exit(e.code)
except (ChildTerminatedAbnormally, ChildForkFailed,
- DaemonTerminatedAbnormally, DaemonForkFailed) as e:
+ DaemonTerminatedAbnormally, DaemonForkFailed), e:
sys.stderr.write('ERROR: %s\n' % e)
sys.exit(1)
except ChildResumedIncorrectly:
@@ -71,29 +72,41 @@ class Daemon(object):
# in original process. daemon is up and running. we're done.
def daemonize(self):
- # fork off a child that can detach itself from this process.
- try:
- pid = os.fork()
- except OSError as e:
- raise ChildForkFailed(e.errno, e.strerror)
-
- if pid > 0:
- # we're in the parent. let's wait for the child to finish setting
- # things up -- on our exit, we want to ensure the child is accepting
- # connections.
- cpid, status = os.waitpid(pid, 0)
- assert pid == cpid
- if os.WIFEXITED(status):
- code = os.WEXITSTATUS(status)
- if code:
- raise ChildFailed(code)
- return DAEMON_RUNNING
-
- # the child did not exit cleanly.
- raise ChildTerminatedAbnormally(status)
+ ### review error situations. map to backwards compat. ??
+ ### be mindful of daemonize_exit().
+ ### we should try and raise ChildFailed / ChildTerminatedAbnormally.
+ ### ref: older revisions. OR: remove exceptions.
+
+ child_is_ready = multiprocessing.Event()
+ child_completed = multiprocessing.Event()
+
+ p = multiprocessing.Process(target=self._first_child,
+ args=(child_is_ready, child_completed))
+ p.start()
+
+ # Wait for the child to finish setting things up (in case we need
+ # to communicate with it). It will only exit when ready.
+ ### use a timeout here! (parameterized, of course)
+ p.join()
+
+ ### need to propagate errors, to adjust the return codes
+ if child_completed.is_set():
+ ### what was the exit status?
+ return DAEMON_COMPLETE
+ if child_is_ready.is_set():
+ return DAEMON_RUNNING
+
+ ### how did we get here?! the immediate child should not exit without
+ ### signalling ready/complete. some kind of error.
+ return DAEMON_STARTED
+ def _first_child(self, child_is_ready, child_completed):
# we're in the child.
+ ### NOTE: the original design was a bit bunk. Exceptions raised from
+ ### this point are within the child processes. We need to signal the
+ ### errors to the parent in other ways.
+
# decouple from the parent process
os.chdir('/')
os.umask(0)
@@ -102,56 +115,86 @@ class Daemon(object):
# remember this pid so the second child can signal it.
thispid = os.getpid()
- # register a signal handler so the SIGUSR1 doesn't stop the process.
- # this object will also record whether if got signalled.
- daemon_accepting = SignalCatcher(signal.SIGUSR1)
-
- # if the daemon process exits before sending SIGUSR1, then we need to see
- # the problem. trap SIGCHLD with a SignalCatcher.
+ # if the daemon process exits before signalling readiness, then we
+ # need to see the problem. trap SIGCHLD with a SignalCatcher.
daemon_exit = SignalCatcher(signal.SIGCHLD)
# perform the second fork
try:
pid = os.fork()
- except OSError as e:
+ except OSError, e:
+ ### this won't make it to the parent process
raise DaemonForkFailed(e.errno, e.strerror)
if pid > 0:
# in the parent.
- # we want to wait for the daemon to signal that it has created and
- # bound the socket, and is (thus) ready for connections. if the
- # daemon improperly exits before serving, we'll see SIGCHLD and the
- # .pause will return.
- ### we should add a timeout to this. allow an optional parameter to
- ### specify the timeout, in case it takes a long time to start up.
- signal.pause()
+
+ # Wait for the child to be ready for operation.
+ while True:
+ # The readiness event will invariably be signalled early/first.
+ # If it *doesn't* get signalled because the child has prematurely
+ # exited, then we will pause 10ms before noticing the exit. The
+ # pause is acceptable since that is aberrant/unexpected behavior.
+ ### is there a way to break this wait() on a signal such as SIGCHLD?
+ ### parameterize this wait, in case the app knows children may
+ ### fail quickly?
+ if child_is_ready.wait(timeout=0.010):
+ # The child signalled readiness. Yay!
+ break
+ if daemon_exit.signalled:
+ # Whoops. The child exited without signalling :-(
+ break
+ # Python 2.6 compat: .wait() may exit when set, but return None
+ if child_is_ready.is_set():
+ break
+ # A simple timeout. The child is taking a while to prepare. Go
+ # back and wait for readiness.
if daemon_exit.signalled:
+ # Tell the parent that the child has exited.
+ ### we need to communicate the exit status, if possible.
+ child_completed.set()
+
# reap the daemon process, getting its exit code. bubble it up.
cpid, status = os.waitpid(pid, 0)
assert pid == cpid
if os.WIFEXITED(status):
code = os.WEXITSTATUS(status)
if code:
+ ### this won't make it to the parent process
raise DaemonFailed(code)
+ ### this return value is ignored
return DAEMON_NOT_RUNNING
# the daemon did not exit cleanly.
+ ### this won't make it to the parent process
raise DaemonTerminatedAbnormally(status)
- if daemon_accepting.signalled:
- # the daemon is up and running, so save the pid and return success.
- if self.pidfile:
- open(self.pidfile, 'w').write('%d\n' % pid)
- return DAEMON_STARTED
+ # child_is_ready got asserted. the daemon is up and running, so
+ # save the pid and return success.
+ if self.pidfile:
+ # Be wary of symlink attacks
+ try:
+ os.remove(self.pidfile)
+ except OSError:
+ pass
+ fd = os.open(self.pidfile, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0444)
+ os.write(fd, '%d\n' % pid)
+ os.close(fd)
+ ### this return value is ignored
+ return DAEMON_STARTED
+
+ ### old code. what to do with this? throw ChildResumedIncorrectly
+ ### or just toss this and the exception.
# some other signal popped us out of the pause. the daemon might not
# be running.
+ ### this won't make it to the parent process
raise ChildResumedIncorrectly()
- # we're a deamon now. get rid of the final remnants of the parent.
- # start by restoring default signal handlers
+ # we're a daemon now. get rid of the final remnants of the parent:
+ # restore the signal handlers and switch std* to the proper files.
signal.signal(signal.SIGUSR1, signal.SIG_DFL)
signal.signal(signal.SIGCHLD, signal.SIG_DFL)
sys.stdout.flush()
@@ -169,30 +212,31 @@ class Daemon(object):
so.close()
se.close()
- # TEST: don't release the parent immediately. the whole parent stack
- # should pause along with this sleep.
+ ### TEST: don't release the parent immediately. the whole parent stack
+ ### should pause along with this sleep.
#time.sleep(10)
# everything is set up. call the initialization function.
self.setup()
- # sleep for one second before signalling. we want to make sure the
- # parent has called signal.pause()
- ### we should think of a better wait around the race condition.
- time.sleep(1)
+ ### TEST: exit before signalling.
+ #sys.exit(0)
+ #sys.exit(1)
- # okay. the daemon is ready. signal the parent to tell it we're set.
- os.kill(thispid, signal.SIGUSR1)
+ # the child is now ready for parent/anyone to communicate with it.
+ child_is_ready.set()
# start the daemon now.
self.run()
# The daemon is shutting down, so toss the pidfile.
- try:
- os.remove(self.pidfile)
- except OSError:
- pass
+ if self.pidfile:
+ try:
+ os.remove(self.pidfile)
+ except OSError:
+ pass
+ ### this return value is ignored
return DAEMON_COMPLETE
def setup(self):
@@ -202,6 +246,34 @@ class Daemon(object):
raise NotImplementedError
+class _Detacher(Daemon):
+ def __init__(self, target, logfile='/dev/null', pidfile=None,
+ args=(), kwargs={}):
+ Daemon.__init__(self, logfile, pidfile)
+ self.target = target
+ self.args = args
+ self.kwargs = kwargs
+
+ def setup(self):
+ pass
+
+ def run(self):
+ self.target(*self.args, **self.kwargs)
+
+
+def run_detached(target, *args, **kwargs):
+ """Simple function to run TARGET as a detached daemon.
+
+ The additional arguments/keywords will be passed along. This function
+ does not return -- sys.exit() will be called as appropriate.
+
+ (capture SystemExit if logging/reporting is necessary)
+ ### if needed, a variant of this func could be written to not exit
+ """
+ d = _Detacher(target, args=args, kwargs=kwargs)
+ d.daemonize_exit()
+
+
class SignalCatcher(object):
def __init__(self, signum):
self.signalled = False
Modified: subversion/branches/cache-server/tools/server-side/svnpubsub/svnpubsub/client.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/server-side/svnpubsub/svnpubsub/client.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/server-side/svnpubsub/svnpubsub/client.py (original)
+++ subversion/branches/cache-server/tools/server-side/svnpubsub/svnpubsub/client.py Tue Oct 15 08:52:06 2013
@@ -62,7 +62,8 @@ class SvnpubsubClientException(Exception
class Client(asynchat.async_chat):
- def __init__(self, url, commit_callback, event_callback):
+ def __init__(self, url, commit_callback, event_callback,
+ metadata_callback = None):
asynchat.async_chat.__init__(self)
self.last_activity = time.time()
@@ -82,7 +83,8 @@ class Client(asynchat.async_chat):
self.event_callback = event_callback
- self.parser = JSONRecordHandler(commit_callback, event_callback)
+ self.parser = JSONRecordHandler(commit_callback, event_callback,
+ metadata_callback)
# Wait for the end of headers. Then we start parsing JSON.
self.set_terminator(b'\r\n\r\n')
@@ -126,36 +128,50 @@ class Client(asynchat.async_chat):
self.ibuffer.append(data)
+class Notification(object):
+ def __init__(self, data):
+ self.__dict__.update(data)
+
+class Commit(Notification):
+ KIND = 'COMMIT'
+
+class Metadata(Notification):
+ KIND = 'METADATA'
+
+
class JSONRecordHandler:
- def __init__(self, commit_callback, event_callback):
+ def __init__(self, commit_callback, event_callback, metadata_callback):
self.commit_callback = commit_callback
self.event_callback = event_callback
+ self.metadata_callback = metadata_callback
+
+ EXPECTED_VERSION = 1
def feed(self, record):
obj = json.loads(record)
if 'svnpubsub' in obj:
actual_version = obj['svnpubsub'].get('version')
- EXPECTED_VERSION = 1
- if actual_version != EXPECTED_VERSION:
- raise SvnpubsubClientException("Unknown svnpubsub format: %r != %d"
- % (actual_format, expected_format))
+ if actual_version != self.EXPECTED_VERSION:
+ raise SvnpubsubClientException(
+ "Unknown svnpubsub format: %r != %d"
+ % (actual_version, self.EXPECTED_VERSION))
self.event_callback('version', obj['svnpubsub']['version'])
elif 'commit' in obj:
commit = Commit(obj['commit'])
self.commit_callback(commit)
elif 'stillalive' in obj:
self.event_callback('ping', obj['stillalive'])
-
-
-class Commit(object):
- def __init__(self, commit):
- self.__dict__.update(commit)
+ elif 'metadata' in obj and self.metadata_callback:
+ metadata = Metadata(obj['metadata'])
+ self.metadata_callback(metadata)
class MultiClient(object):
- def __init__(self, urls, commit_callback, event_callback):
+ def __init__(self, urls, commit_callback, event_callback,
+ metadata_callback = None):
self.commit_callback = commit_callback
self.event_callback = event_callback
+ self.metadata_callback = metadata_callback
# No target time, as no work to do
self.target_time = 0
@@ -185,9 +201,15 @@ class MultiClient(object):
def _add_channel(self, url):
# Simply instantiating the client will install it into the global map
# for processing in the main event loop.
- Client(url,
- functools.partial(self.commit_callback, url),
- functools.partial(self._reconnect, url))
+ if self.metadata_callback:
+ Client(url,
+ functools.partial(self.commit_callback, url),
+ functools.partial(self._reconnect, url),
+ functools.partial(self.metadata_callback, url))
+ else:
+ Client(url,
+ functools.partial(self.commit_callback, url),
+ functools.partial(self._reconnect, url))
def _check_stale(self):
now = time.time()
Modified: subversion/branches/cache-server/tools/server-side/svnpubsub/svnpubsub/server.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/server-side/svnpubsub/svnpubsub/server.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/server-side/svnpubsub/svnpubsub/server.py (original)
+++ subversion/branches/cache-server/tools/server-side/svnpubsub/svnpubsub/server.py Tue Oct 15 08:52:06 2013
@@ -25,20 +25,27 @@
# Instead of using a complicated XMPP/AMPQ/JMS/super messaging service,
# we have simple HTTP GETs and PUTs to get data in and out.
#
-# Currently supports both XML and JSON serialization.
+# Currently supports JSON serialization.
#
# Example Sub clients:
-# curl -sN http://127.0.0.1:2069/commits
-# curl -sN http://127.0.0.1:2069/commits/svn/*
-# curl -sN http://127.0.0.1:2069/commits/svn
-# curl -sN http://127.0.0.1:2069/commits/*/13f79535-47bb-0310-9956-ffa450edef68
-# curl -sN http://127.0.0.1:2069/commits/svn/13f79535-47bb-0310-9956-ffa450edef68
+# curl -sN http://127.0.0.1:2069/commits
+# curl -sN 'http://127.0.0.1:2069/commits/svn/*'
+# curl -sN http://127.0.0.1:2069/commits/svn
+# curl -sN 'http://127.0.0.1:2069/commits/*/13f79535-47bb-0310-9956-ffa450edef68'
+# curl -sN http://127.0.0.1:2069/commits/svn/13f79535-47bb-0310-9956-ffa450edef68
#
-# URL is built into 2 parts:
-# /commits/${optional_type}/${optional_repository}
+# curl -sN http://127.0.0.1:2069/metadata
+# curl -sN 'http://127.0.0.1:2069/metadata/svn/*'
+# curl -sN http://127.0.0.1:2069/metadata/svn
+# curl -sN 'http://127.0.0.1:2069/metadata/*/13f79535-47bb-0310-9956-ffa450edef68'
+# curl -sN http://127.0.0.1:2069/metadata/svn/13f79535-47bb-0310-9956-ffa450edef68
#
-# If the type is included in the URL, you will only get commits of that type.
-# The type can be * and then you will receive commits of any type.
+# URLs are constructed from 3 parts:
+# /${notification}/${optional_type}/${optional_repository}
+#
+# Notifications can be sent for commits or metadata (e.g., revprop) changes.
+# If the type is included in the URL, you will only get notifications of that type.
+# The type can be * and then you will receive notifications of any type.
#
# If the repository is included in the URL, you will only receive
# messages about that repository. The repository can be * and then you
@@ -71,7 +78,7 @@ from twisted.python import log
import time
-class Commit:
+class Notification(object):
def __init__(self, r):
self.__dict__.update(r)
if not self.check_value('repository'):
@@ -86,7 +93,16 @@ class Commit:
def check_value(self, k):
return hasattr(self, k) and self.__dict__[k]
- def render_commit(self):
+ def render(self):
+ raise NotImplementedError
+
+ def render_log(self):
+ raise NotImplementedError
+
+class Commit(Notification):
+ KIND = 'COMMIT'
+
+ def render(self):
obj = {'commit': {}}
obj['commit'].update(self.__dict__)
return json.dumps(obj)
@@ -96,20 +112,32 @@ class Commit:
paths_changed = " %d paths changed" % len(self.changed)
except:
paths_changed = ""
- return "%s:%s repo '%s' id '%s'%s" % (self.type,
- self.format,
- self.repository,
- self.id,
- paths_changed)
+ return "commit %s:%s repo '%s' id '%s'%s" % (
+ self.type, self.format, self.repository, self.id,
+ paths_changed)
+
+class Metadata(Notification):
+ KIND = 'METADATA'
+
+ def render(self):
+ obj = {'metadata': {}}
+ obj['metadata'].update(self.__dict__)
+ return json.dumps(obj)
+
+ def render_log(self):
+ return "metadata %s:%s repo '%s' id '%s' revprop '%s'" % (
+ self.type, self.format, self.repository, self.id,
+ self.revprop['name'])
HEARTBEAT_TIME = 15
class Client(object):
- def __init__(self, pubsub, r, type, repository):
+ def __init__(self, pubsub, r, kind, type, repository):
self.pubsub = pubsub
r.notifyFinish().addErrback(self.finished)
self.r = r
+ self.kind = kind
self.type = type
self.repository = repository
self.alive = True
@@ -123,11 +151,14 @@ class Client(object):
except ValueError:
pass
- def interested_in(self, commit):
- if self.type and self.type != commit.type:
+ def interested_in(self, notification):
+ if self.kind != notification.KIND:
return False
- if self.repository and self.repository != commit.repository:
+ if self.type and self.type != notification.type:
+ return False
+
+ if self.repository and self.repository != notification.repository:
return False
return True
@@ -152,7 +183,8 @@ class Client(object):
self.r.write(str(input))
def write_start(self):
- self.r.setHeader('content-type', 'application/json')
+ self.r.setHeader('X-SVNPubSub-Version', '1')
+ self.r.setHeader('content-type', 'application/vnd.apache.vc-notify+json')
self.write('{"svnpubsub": {"version": 1}}\n\0')
def write_heartbeat(self):
@@ -163,6 +195,13 @@ class SvnPubSub(resource.Resource):
isLeaf = True
clients = []
+ __notification_uri_map = {'commits': Commit.KIND,
+ 'metadata': Metadata.KIND}
+
+ def __init__(self, notification_class):
+ resource.Resource.__init__(self)
+ self.__notification_class = notification_class
+
def cc(self):
return len(self.clients)
@@ -182,6 +221,11 @@ class SvnPubSub(resource.Resource):
request.setResponseCode(400)
return "Invalid path\n"
+ kind = self.__notification_uri_map.get(uri[1], None)
+ if kind is None:
+ request.setResponseCode(400)
+ return "Invalid path\n"
+
if uri_len >= 3:
type = uri[2]
@@ -194,17 +238,18 @@ class SvnPubSub(resource.Resource):
if repository == '*':
repository = None
- c = Client(self, request, type, repository)
+ c = Client(self, request, kind, type, repository)
self.clients.append(c)
c.start()
return twisted.web.server.NOT_DONE_YET
- def notifyAll(self, commit):
- data = commit.render_commit()
+ def notifyAll(self, notification):
+ data = notification.render()
- log.msg("COMMIT: %s (%d clients)" % (commit.render_log(), self.cc()))
+ log.msg("%s: %s (%d clients)"
+ % (notification.KIND, notification.render_log(), self.cc()))
for client in self.clients:
- if client.interested_in(commit):
+ if client.interested_in(notification):
client.write_data(data)
def render_PUT(self, request):
@@ -217,19 +262,23 @@ class SvnPubSub(resource.Resource):
#import pdb;pdb.set_trace()
#print "input: %s" % (input)
try:
- c = json.loads(input)
- commit = Commit(c)
+ data = json.loads(input)
+ notification = self.__notification_class(data)
except ValueError as e:
request.setResponseCode(400)
- log.msg("COMMIT: failed due to: %s" % str(e))
- return str(e)
- self.notifyAll(commit)
+ errstr = str(e)
+ log.msg("%s: failed due to: %s" % (notification.KIND, errstr))
+ return errstr
+ self.notifyAll(notification)
return "Ok"
+
def svnpubsub_server():
root = resource.Resource()
- s = SvnPubSub()
- root.putChild("commits", s)
+ c = SvnPubSub(Commit)
+ m = SvnPubSub(Metadata)
+ root.putChild('commits', c)
+ root.putChild('metadata', m)
return server.Site(root)
if __name__ == "__main__":
Modified: subversion/branches/cache-server/tools/server-side/svnpubsub/svnwcsub.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/server-side/svnpubsub/svnwcsub.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/server-side/svnpubsub/svnwcsub.py (original)
+++ subversion/branches/cache-server/tools/server-side/svnpubsub/svnwcsub.py Tue Oct 15 08:52:06 2013
@@ -69,27 +69,22 @@ except ImportError:
import daemonize
import svnpubsub.client
-
-# check_output() is only available in Python 2.7. Allow us to run with
-# earlier versions
-try:
- check_output = subprocess.check_output
-except AttributeError:
- def check_output(args, env): # note: we only use these two args
- pipe = subprocess.Popen(args, stdout=subprocess.PIPE, env=env)
- output, _ = pipe.communicate()
- if pipe.returncode:
- raise subprocess.CalledProcessError(pipe.returncode, args)
- return output
+import svnpubsub.util
assert hasattr(subprocess, 'check_call')
def check_call(*args, **kwds):
- """Wrapper around subprocess.check_call() that logs stderr upon failure."""
+ """Wrapper around subprocess.check_call() that logs stderr upon failure,
+ with an optional list of exit codes to consider non-failure."""
assert 'stderr' not in kwds
+ if '__okayexits' in kwds:
+ __okayexits = kwds['__okayexits']
+ del kwds['__okayexits']
+ else:
+ __okayexits = set([0]) # EXIT_SUCCESS
kwds.update(stderr=subprocess.PIPE)
pipe = subprocess.Popen(*args, **kwds)
output, errput = pipe.communicate()
- if pipe.returncode:
+ if pipe.returncode not in __okayexits:
cmd = args[0] if len(args) else kwds.get('args', '(no command)')
# TODO: log stdout too?
logging.error('Command failed: returncode=%d command=%r stderr=%r',
@@ -103,7 +98,7 @@ def check_call(*args, **kwds):
def svn_info(svnbin, env, path):
"Run 'svn info' on the target path, returning a dict of info data."
args = [svnbin, "info", "--non-interactive", "--", path]
- output = check_output(args, env=env).strip()
+ output = svnpubsub.util.check_output(args, env=env).strip()
info = { }
for line in output.split('\n'):
idx = line.index(':')
@@ -303,6 +298,21 @@ class BackgroundWorker(threading.Thread)
logging.info("updating: %s", wc.path)
+ ## Run the hook
+ HEAD = svn_info(self.svnbin, self.env, wc.url)['Revision']
+ if self.hook:
+ hook_mode = ['pre-update', 'pre-boot'][boot]
+ logging.info('running hook: %s at %s',
+ wc.path, hook_mode)
+ args = [self.hook, hook_mode, wc.path, HEAD, wc.url]
+ rc = check_call(args, env=self.env, __okayexits=[0, 1])
+ if rc == 1:
+ # TODO: log stderr
+ logging.warn('hook denied update of %s at %s',
+ wc.path, hook_mode)
+ return
+ del rc
+
### we need to move some of these args into the config. these are
### still specific to the ASF setup.
args = [self.svnbin, 'switch',
@@ -313,12 +323,13 @@ class BackgroundWorker(threading.Thread)
'--config-option',
'config:miscellany:use-commit-times=on',
'--',
- wc.url,
+ wc.url + '@' + HEAD,
wc.path]
check_call(args, env=self.env)
### check the loglevel before running 'svn info'?
info = svn_info(self.svnbin, self.env, wc.path)
+ assert info['Revision'] == HEAD
logging.info("updated: %s now at r%s", wc.path, info['Revision'])
## Run the hook
@@ -465,7 +476,15 @@ def handle_options(options):
# Otherwise, we should write this (foreground) PID into the file.
if options.pidfile and not options.daemon:
pid = os.getpid()
- open(options.pidfile, 'w').write('%s\n' % pid)
+ # Be wary of symlink attacks
+ try:
+ os.remove(options.pidfile)
+ except OSError:
+ pass
+ fd = os.open(options.pidfile, os.O_WRONLY | os.O_CREAT | os.O_EXCL,
+ 0444)
+ os.write(fd, '%d\n' % pid)
+ os.close(fd)
logging.info('pid %d written to %s', pid, options.pidfile)
if options.gid:
@@ -525,7 +544,8 @@ def main(args):
# We manage the logfile ourselves (along with possible rotation). The
# daemon process can just drop stdout/stderr into /dev/null.
- d = Daemon('/dev/null', options.pidfile, options.umask, bdec)
+ d = Daemon('/dev/null', os.path.abspath(options.pidfile),
+ options.umask, bdec)
if options.daemon:
# Daemonize the process and call sys.exit() with appropriate code
d.daemonize_exit()
Modified: subversion/branches/cache-server/tools/server-side/svnpubsub/watcher.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/tools/server-side/svnpubsub/watcher.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/tools/server-side/svnpubsub/watcher.py (original)
+++ subversion/branches/cache-server/tools/server-side/svnpubsub/watcher.py Tue Oct 15 08:52:06 2013
@@ -35,6 +35,9 @@ def _commit(url, commit):
print('COMMIT: from %s' % url)
pprint.pprint(vars(commit), indent=2)
+def _metadata(url, metadata):
+ print('METADATA: from %s' % url)
+ pprint.pprint(vars(metadata), indent=2)
def _event(url, event_name, event_arg):
if event_arg:
@@ -44,7 +47,7 @@ def _event(url, event_name, event_arg):
def main(urls):
- mc = svnpubsub.client.MultiClient(urls, _commit, _event)
+ mc = svnpubsub.client.MultiClient(urls, _commit, _event, _metadata)
mc.run_forever()
Modified: subversion/branches/cache-server/win-tests.py
URL: http://svn.apache.org/viewvc/subversion/branches/cache-server/win-tests.py?rev=1532250&r1=1532249&r2=1532250&view=diff
==============================================================================
--- subversion/branches/cache-server/win-tests.py (original)
+++ subversion/branches/cache-server/win-tests.py Tue Oct 15 08:52:06 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'))
-
- 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'))
+ 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)
- 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)
@@ -396,7 +383,7 @@ class Svnserve:
self.path = os.path.join(abs_objdir,
'subversion', 'svnserve', self.name)
self.root = os.path.join(abs_builddir, CMDLINE_TEST_SCRIPT_NATIVE_PATH)
- self.proc_handle = None
+ self.proc = None
def __del__(self):
"Stop svnserve when the object is deleted"
@@ -414,26 +401,18 @@ class Svnserve:
else:
args = [self.name] + self.args
print('Starting %s %s' % (self.kind, self.name))
- try:
- import win32process
- import win32con
- args = ' '.join([self._quote(x) for x in args])
- self.proc_handle = (
- win32process.CreateProcess(self._quote(self.path), args,
- None, None, 0,
- win32con.CREATE_NEW_CONSOLE,
- None, None, win32process.STARTUPINFO()))[0]
- except ImportError:
- os.spawnv(os.P_NOWAIT, self.path, args)
+
+ self.proc = subprocess.Popen([self.path] + args[1:])
def stop(self):
- if self.proc_handle is not None:
+ if self.proc is not None:
try:
- import win32process
print('Stopping %s' % self.name)
- win32process.TerminateProcess(self.proc_handle, 0)
+ self.proc.poll();
+ if self.proc.returncode is None:
+ self.proc.kill();
return
- except ImportError:
+ except AttributeError:
pass
print('Svnserve.stop not implemented')
@@ -456,7 +435,7 @@ class Httpd:
self.bulkupdates_option = 'off'
self.service = service
- self.proc_handle = None
+ self.proc = None
self.path = os.path.join(self.httpd_dir, 'bin', self.name)
if short_circuit:
@@ -600,6 +579,12 @@ class Httpd:
def _create_dontdothat_file(self):
"Create empty mime.types file"
+ # If the tests have not previously been run or were cleaned
+ # up, then 'svn-test-work' does not exist yet.
+ parent_dir = os.path.dirname(self.dontdothat_file)
+ if not os.path.exists(parent_dir):
+ os.makedirs(parent_dir)
+
fp = open(self.dontdothat_file, 'w')
fp.write('[recursive-actions]\n')
fp.write('/ = deny\n')
@@ -610,7 +595,7 @@ class Httpd:
return 'LoadModule ' + name + " " + self._quote(full_path) + '\n'
def _svn_module(self, name, path):
- full_path = os.path.join(self.abs_objdir, path)
+ full_path = os.path.join(self.abs_builddir, path)
return 'LoadModule ' + name + ' ' + self._quote(full_path) + '\n'
def _svn_repo(self, name):
@@ -674,46 +659,33 @@ class Httpd:
"Start HTTPD as daemon"
print('Starting httpd as daemon')
print(self.httpd_args)
- try:
- import win32process
- import win32con
- args = ' '.join([self._quote(x) for x in self.httpd_args])
- self.proc_handle = (
- win32process.CreateProcess(self._quote(self.path), args,
- None, None, 0,
- win32con.CREATE_NEW_CONSOLE,
- None, None, win32process.STARTUPINFO()))[0]
- except ImportError:
- os.spawnv(os.P_NOWAIT, self.path, self.httpd_args)
+ self.proc = subprocess.Popen([self.path] + self.httpd_args[1:])
def _stop_daemon(self):
"Stop the HTTPD daemon"
- if self.proc_handle is not None:
+ if self.proc is not None:
try:
- import win32process
print('Stopping %s' % self.name)
- win32process.TerminateProcess(self.proc_handle, 0)
+ self.proc.poll();
+ if self.proc.returncode is None:
+ self.proc.kill();
return
- except ImportError:
+ except AttributeError:
pass
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)
@@ -771,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
@@ -799,7 +771,7 @@ if not test_javahl:
raise
else:
os.chdir(old_cwd)
-else:
+elif test_javahl:
failed = False
args = (
'java.exe',
@@ -837,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
+ sys.exit(1)
+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: