# HG changeset patch # User cmlenz # Date 1120134534 0 # Node ID 234600bf0d49d95be12e4f3cc79d798f61675f79 # Parent 6ffa99b442bfd6b158055020c0ec9831964765ba Fixes for Windows compatibility: * implementation of {{{bitten.util.cmdline}}} for windows, based on temporary files * the recipe tools now close the files they opened so that they can be properly deleted after the build completed * Wrapper batch files for client and server diff --git a/bitten/build/pythontools.py b/bitten/build/pythontools.py --- a/bitten/build/pythontools.py +++ b/bitten/build/pythontools.py @@ -32,7 +32,7 @@ if err: print '[distutils] %s' % err if cmdline.returncode != 0: - raise BuildError, "Executing distutils failed (%s)" % cmdline.returncode + raise BuildError, 'Executing distutils failed (%s)' % cmdline.returncode def pylint(ctxt, file=None): """Extract data from a `pylint` run written to a file.""" @@ -41,18 +41,21 @@ r'\[(?P[A-Z])(?:, (?P[\w\.]+))?\] ' r'(?P.*)$') - fd = open(ctxt.resolve(file), 'r') try: - for line in fd: - match = _msg_re.search(line) - if match: - filename = match.group('file') - if filename.startswith(ctxt.basedir): - filename = filename[len(ctxt.basedir) + 1:] - lineno = int(match.group('line')) - # TODO: emit to build master - finally: - fd.close() + fd = open(ctxt.resolve(file), 'r') + try: + for line in fd: + match = _msg_re.search(line) + if match: + filename = match.group('file') + if filename.startswith(ctxt.basedir): + filename = filename[len(ctxt.basedir) + 1:] + lineno = int(match.group('line')) + # TODO: emit to build master + finally: + fd.close() + except IOError, e: + raise BuildError, 'Error opening pylint results file (%s)' % e def trace(ctxt, summary=None, coverdir=None, include=None, exclude=None): """Extract data from a `trace.py` run.""" @@ -63,18 +66,21 @@ """Extract data from a unittest results file in XML format.""" assert file, 'Missing required attribute "file"' - fd = open(ctxt.resolve(file), 'r') try: - from xml.dom import minidom - root = minidom.parse(fd).documentElement - assert root.tagName == 'unittest-results' - for test in root.getElementsByTagName('test'): - filename = test.getAttribute('file') - if filename.startswith(ctxt.basedir): - filename = filename[len(ctxt.basedir) + 1:] - duration = float(test.getAttribute('duration')) - name = test.getAttribute('name') - status = test.getAttribute('status') - # TODO: emit to build master - finally: - fd.close() + fd = open(ctxt.resolve(file), 'r') + try: + from xml.dom import minidom + root = minidom.parse(fd).documentElement + assert root.tagName == 'unittest-results' + for test in root.getElementsByTagName('test'): + filename = test.getAttribute('file') + if filename.startswith(ctxt.basedir): + filename = filename[len(ctxt.basedir) + 1:] + duration = float(test.getAttribute('duration')) + name = test.getAttribute('name') + status = test.getAttribute('status') + # TODO: emit to build master + finally: + fd.close() + except IOError, e: + raise BuildError, 'Error opening unittest results file (%s)' % e diff --git a/bitten/recipe.py b/bitten/recipe.py --- a/bitten/recipe.py +++ b/bitten/recipe.py @@ -36,7 +36,7 @@ self.basedir = basedir def resolve(self, *path): - return os.path.join(self.basedir, *path) + return os.path.normpath(os.path.join(self.basedir, *path)) class Step(object): diff --git a/bitten/slave.py b/bitten/slave.py --- a/bitten/slave.py +++ b/bitten/slave.py @@ -21,6 +21,7 @@ import logging import os import platform +import sys import tempfile import time @@ -64,7 +65,6 @@ raise beep.TerminateSession, 'Registration failed!' logging.info('Registration successful') - system, node, release, version, machine, processor = platform.uname() system, release, version = platform.system_alias(system, release, version) diff --git a/bitten/util/cmdline.py b/bitten/util/cmdline.py --- a/bitten/util/cmdline.py +++ b/bitten/util/cmdline.py @@ -31,7 +31,6 @@ class Commandline(object): """Simple helper for executing subprocesses.""" # TODO: Use 'subprocess' module if available (Python >= 2.4) - # TODO: Windows implementation based on temporary files def __init__(self, executable, args, cwd=None): """Initialize the Commandline object. @@ -49,16 +48,37 @@ self.returncode = None if os.name == 'nt': # windows + def execute(self, timeout=None): - raise NotImplementedError + args = [self.executable] + self.arguments + for idx, arg in enumerate(args): + if arg.find(' ') >= 0: + args[idx] = '"%s"' % arg + logging.debug('Executing %s', args) + + import tempfile + out_name = tempfile.mktemp() + err_name = tempfile.mktemp() + cmd = "( %s ) > %s 2> %s" % (' '.join(args), out_name, err_name) + self.returncode = os.system(cmd) >> 8 + + out_file = file(out_name, 'r') + err_file = file(err_name, 'r') + out_lines = out_file.readlines() + err_lines = err_file.readlines() + out_file.close() + err_file.close() + for out_line, err_line in self._combine(out_lines, err_lines): + yield out_line, err_line else: # posix + def execute(self, timeout=None): import fcntl, popen2, select if self.cwd: os.chdir(self.cwd) - logging.debug('Executing %s', self) + logging.debug('Executing %s', [self.executable] + self.arguments) pipe = popen2.Popen3([self.executable] + self.arguments, capturestderr=True) pipe.tochild.close() @@ -99,38 +119,38 @@ time.sleep(.1) self.returncode = pipe.wait() - def _combine(self, *iterables): - iterables = [iter(iterable) for iterable in iterables] - size = len(iterables) - while [iterable for iterable in iterables if iterable is not None]: - to_yield = [None] * size - for idx, iterable in enumerate(iterables): - if iterable is None: - continue - try: - to_yield[idx] = iterable.next() - except StopIteration: - iterables[idx] = None - yield tuple(to_yield) + def _combine(self, *iterables): + iterables = [iter(iterable) for iterable in iterables] + size = len(iterables) + while [iterable for iterable in iterables if iterable is not None]: + to_yield = [None] * size + for idx, iterable in enumerate(iterables): + if iterable is None: + continue + try: + to_yield[idx] = iterable.next() + except StopIteration: + iterables[idx] = None + yield tuple(to_yield) - def _extract_lines(self, data): - extracted = [] - def _endswith_linesep(string): - for linesep in ('\n', '\r\n', '\r'): - if string.endswith(linesep): - return True - buf = ''.join(data) - lines = buf.splitlines(True) - if len(lines) > 1: - extracted += lines[:-1] - if _endswith_linesep(lines[-1]): - extracted.append(lines[-1]) - buf = '' - else: - buf = lines[-1] - elif _endswith_linesep(buf): - extracted.append(buf) + def _extract_lines(self, data): + extracted = [] + def _endswith_linesep(string): + for linesep in ('\n', '\r\n', '\r'): + if string.endswith(linesep): + return True + buf = ''.join(data) + lines = buf.splitlines(True) + if len(lines) > 1: + extracted += lines[:-1] + if _endswith_linesep(lines[-1]): + extracted.append(lines[-1]) buf = '' - data[:] = [buf] + else: + buf = lines[-1] + elif _endswith_linesep(buf): + extracted.append(buf) + buf = '' + data[:] = [buf] - return [line.rstrip() for line in extracted] + return [line.rstrip() for line in extracted] diff --git a/scripts/bitten.bat b/scripts/bitten.bat new file mode 100644 --- /dev/null +++ b/scripts/bitten.bat @@ -0,0 +1,2 @@ +@echo off +python -c "import sys; from bitten import slave; sys.argv[0] = r'%0'; slave.main()" %* diff --git a/scripts/bittend.bat b/scripts/bittend.bat new file mode 100644 --- /dev/null +++ b/scripts/bittend.bat @@ -0,0 +1,2 @@ +@echo off +python -c "import sys; from bitten import master; sys.argv[0] = r'%0'; master.main()" %* diff --git a/setup.py b/setup.py --- a/setup.py +++ b/setup.py @@ -20,12 +20,16 @@ # Author: Christopher Lenz from distutils.core import setup +from distutils import util from bitten import __version__ as VERSION from bitten.util.testrunner import unittest +scripts = ['scripts/bitten', 'scripts/bittend'] +if util.get_platform()[:3] == 'win': + scripts = [script + '.bat' for script in scripts] + setup(name='bitten', version=VERSION, packages=['bitten', 'bitten.build', 'bitten.trac_ext', 'bitten.util'], - scripts=['scripts/bitten', 'scripts/bittend'], - author="Christopher Lenz", author_email="cmlenz@gmx.de", + scripts=scripts, author="Christopher Lenz", author_email="cmlenz@gmx.de", url="http://bitten.cmlenz.net/", cmdclass={'unittest': unittest})