# HG changeset patch # User wbell # Date 1272116085 0 # Node ID d9f06a314db58a43a096c5b558e21da4407c5ac9 # Parent db5f7388535d81f88f371c351ee1c4a408308baa Merge [830] from trunk. diff --git a/bitten/build/api.py b/bitten/build/api.py --- a/bitten/build/api.py +++ b/bitten/build/api.py @@ -110,7 +110,7 @@ raise BuildError('Error executing %s: %s %s' % (args, e.__class__.__name__, str(e))) - log.debug('Executing %s, (pid = %s)', args, p.pid) + log.debug('Executing %s, (pid = %s, timeout = %s)', args, p.pid, timeout) if self.input: if isinstance(self.input, basestring): @@ -130,7 +130,8 @@ while True: if limit and limit < time.time(): - if hasattr(subprocess, 'kill'): # Python 2.6+ + if hasattr(p, 'kill'): # Python 2.6+ + log.debug('Killing process.') p.kill() raise TimeoutError('Command %s timed out' % self.executable) if p.poll() != None and self.returncode == None: diff --git a/bitten/build/pythontools.py b/bitten/build/pythontools.py --- a/bitten/build/pythontools.py +++ b/bitten/build/pythontools.py @@ -43,7 +43,8 @@ return python_path return sys.executable -def distutils(ctxt, file_='setup.py', command='build', options=None): +def distutils(ctxt, file_='setup.py', command='build', + options=None, timeout=None): """Execute a ``distutils`` command. :param ctxt: the build context @@ -51,6 +52,8 @@ :param file\_: name of the file defining the distutils setup :param command: the setup command to execute :param options: additional options to pass to the command + :param timeout: the number of seconds before the external process should + be aborted (has same constraints as CommandLine) """ if options: if isinstance(options, basestring): @@ -58,12 +61,15 @@ else: options = [] + if timeout: + timeout = int(timeout) + cmdline = CommandLine(_python_path(ctxt), [ctxt.resolve(file_), command] + options, cwd=ctxt.basedir) log_elem = xmlio.Fragment() error_logged = False - for out, err in cmdline.execute(): + for out, err in cmdline.execute(timeout): if out is not None: log.info(out) log_elem.append(xmlio.Element('message', level='info')[out]) @@ -84,7 +90,8 @@ if not error_logged and cmdline.returncode != 0: ctxt.error('distutils failed (%s)' % cmdline.returncode) -def exec_(ctxt, file_=None, module=None, function=None, output=None, args=None): +def exec_(ctxt, file_=None, module=None, function=None, + output=None, args=None, timeout=None): """Execute a Python script. Either the `file_` or the `module` parameter must be provided. If @@ -99,6 +106,8 @@ :param function: name of the Python function to run :param output: name of the file to which output should be written :param args: extra arguments to pass to the script + :param timeout: the number of seconds before the external process should + be aborted (has same constraints as CommandLine) """ assert file_ or module, 'Either "file" or "module" attribute required' if function: @@ -124,7 +133,8 @@ from bitten.build import shtools returncode = shtools.execute(ctxt, executable=_python_path(ctxt), - file_=file_, output=output, args=args) + file_=file_, output=output, args=args, + timeout=timeout) if returncode != 0: ctxt.error('Executing %s failed (error code %s)' % \ (file_ or function or module, returncode)) diff --git a/bitten/build/shtools.py b/bitten/build/shtools.py --- a/bitten/build/shtools.py +++ b/bitten/build/shtools.py @@ -22,7 +22,8 @@ __docformat__ = 'restructuredtext en' -def exec_(ctxt, executable=None, file_=None, output=None, args=None, dir_=None): +def exec_(ctxt, executable=None, file_=None, output=None, args=None, + dir_=None, timeout=None): """Execute a program or shell script. :param ctxt: the build context @@ -33,12 +34,15 @@ :param output: name of the file to which the output of the script should be written :param args: command-line arguments to pass to the script + :param timeout: the number of seconds before the external process should + be aborted (has same constraints as CommandLine) """ assert executable or file_, \ 'Either "executable" or "file" attribute required' returncode = execute(ctxt, executable=executable, file_=file_, - output=output, args=args, dir_=dir_) + output=output, args=args, dir_=dir_, + timeout=timeout) if returncode != 0: ctxt.error('Executing %s failed (error code %s)' % (executable or file_, returncode)) @@ -69,7 +73,7 @@ % (executable or file_, returncode)) def execute(ctxt, executable=None, file_=None, input_=None, output=None, - args=None, dir_=None, filter_=None): + args=None, dir_=None, filter_=None, timeout=None): """Generic external program execution. This function is not itself bound to a recipe command, but rather used from @@ -86,6 +90,8 @@ written :param args: command-line arguments to pass to the script :param filter\_: function to filter out messages from the executable stdout + :param timeout: the number of seconds before the external process should + be aborted (has same constraints as CommandLine) """ if args: if isinstance(args, basestring): @@ -136,11 +142,14 @@ if not filter_: filter_=lambda s: s + if timeout: + timeout = int(timeout) + try: cmdline = CommandLine(executable, args, input=input_file, cwd=dir_, shell=shell) log_elem = xmlio.Fragment() - for out, err in cmdline.execute(): + for out, err in cmdline.execute(timeout=timeout): if out is not None: log.info(out) info = filter_(out) diff --git a/bitten/recipe.py b/bitten/recipe.py --- a/bitten/recipe.py +++ b/bitten/recipe.py @@ -25,7 +25,7 @@ from sets import Set as set from pkg_resources import WorkingSet -from bitten.build import BuildError +from bitten.build import BuildError, TimeoutError from bitten.build.config import Configuration from bitten.util import xmlio @@ -226,7 +226,7 @@ for child in self._elem: try: ctxt.run(self, child.namespace, child.name, child.attr) - except (BuildError, InvalidRecipeError), e: + except (BuildError, InvalidRecipeError, TimeoutError), e: ctxt.error(e) if time.time() < last_finish + 1: # Add a delay to make sure steps appear in correct order diff --git a/doc/commands.txt b/doc/commands.txt --- a/doc/commands.txt +++ b/doc/commands.txt @@ -105,6 +105,10 @@ +----------------+-----------------------------------------------------------+ | ``args`` | Any arguments to pass to the executable or script | +----------------+-----------------------------------------------------------+ +| `timeout` | Limits the runtime of this command to the specified | +| | number of seconds, after which it will be terminated. | ++----------------+-----------------------------------------------------------+ + Either ``executable`` or ``file`` must be specified. @@ -651,6 +655,10 @@ | ``output`` | Path to a file where any output by the script should be | | | recorded. | +----------------+-----------------------------------------------------------+ +| ``timeout`` | Limits the runtime of this command to the specified | +| | number of seconds, after which it will be terminated. | ++----------------+-----------------------------------------------------------+ + Either `file` or `module` must be specified. @@ -692,6 +700,10 @@ | `options` | Additional options to pass to the command, separated by | | | spaces | +----------------+-----------------------------------------------------------+ +| `timeout` | Limits the runtime of this command to the specified | +| | number of seconds, after which it will be terminated. | ++----------------+-----------------------------------------------------------+ + Examples --------