# HG changeset patch # User cmlenz # Date 1124570115 0 # Node ID affd91b4c6fb7764862c26acc6a153e521812695 # Parent 0221d7cdf59a3fbcbdc4031ec2bae22958227f44 Add a `` recipe command so that things like Pylint can be executed without using a Makefile. diff --git a/Makefile b/Makefile deleted file mode 100644 --- a/Makefile +++ /dev/null @@ -1,9 +0,0 @@ -PYLINT_MSGS = C0101,E0201,E0213,W0103,W0704,R0921,R0923 -PYTHONPATH = . - -all: pylint - -pylint: - PYTHONPATH=$(PYTHONPATH) pylint --parseable=yes --include-ids=yes \ - --disable-msg=$(PYLINT_MSGS) --ignore=tests \ - bitten > build/pylint-results.txt diff --git a/bitten/build/ctools.py b/bitten/build/ctools.py --- a/bitten/build/ctools.py +++ b/bitten/build/ctools.py @@ -26,11 +26,11 @@ log = logging.getLogger('bitten.build.ctools') -def make(ctxt, target=None, file=None, jobs=None, keep_going=False): +def make(ctxt, target=None, file_=None, jobs=None, keep_going=False): """Execute a Makefile target.""" args = ['--directory', ctxt.basedir] - if file: - args += ['--file', ctxt.resolve(file)] + if file_: + args += ['--file', ctxt.resolve(file_)] if jobs: args += ['--jobs', int(jobs)] if keep_going: @@ -40,13 +40,15 @@ log_elem = xmlio.Fragment() cmdline = Commandline('make', args) - for out, err in cmdline.execute(timeout=100.0): - if out: + for out, err in cmdline.execute(): + if out is not None: log.info(out) - xmlio.SubElement(log_elem, 'message', level='info')[out] - if err: + if out: + xmlio.SubElement(log_elem, 'message', level='info')[out] + if err is not None: log.error(err) - xmlio.SubElement(log_elem, 'message', level='error')[err] + if err: + xmlio.SubElement(log_elem, 'message', level='error')[err] ctxt.log(log_elem) if cmdline.returncode != 0: ctxt.error('make failed (%s)' % cmdline.returncode) diff --git a/bitten/build/pythontools.py b/bitten/build/pythontools.py --- a/bitten/build/pythontools.py +++ b/bitten/build/pythontools.py @@ -21,8 +21,9 @@ import logging import os import re +import shlex -from bitten.build import BuildError +from bitten.recipe import Recipe from bitten.util import xmlio from bitten.util.cmdline import Commandline @@ -32,11 +33,12 @@ """Execute a `distutils` command.""" cmdline = Commandline('python', ['setup.py', command], cwd=ctxt.basedir) log_elem = xmlio.Fragment() - for out, err in cmdline.execute(timeout=100.0): - if out: - log.info(out) + for out, err in cmdline.execute(): + if out is not None: + if out: + log.info(out) xmlio.SubElement(log_elem, 'message', level='info')[out] - if err: + if err is not None: level = 'error' if err.startswith('warning: '): err = err[9:] @@ -44,14 +46,67 @@ log.warning(err) else: log.error(err) - xmlio.SubElement(log_elem, 'message', level=level)[err] + if err: + xmlio.SubElement(log_elem, 'message', level=level)[err] ctxt.log(log_elem) if cmdline.returncode != 0: ctxt.error('distutils failed (%s)' % cmdline.returncode) -def pylint(ctxt, file=None): +def exec_(ctxt, file_=None, module=None, output=None, args=None): + """Execute a python script.""" + assert file_ or module, 'Either "file" or "module" attribute required' + + if module: + # Script specified as module name, need to resolve that to a file + mod = __import__(module, globals(), locals(), []) + components = module.split('.') + for comp in components[1:]: + mod = getattr(mod, comp) + file_ = mod.__file__ + + if args: + args = shlex.split(args) + else: + args = [] + + output_file = None + if output: + output = ctxt.resolve(output) + output_file = file(output, 'w') + + try: + cmdline = Commandline('python', [file_] + args, cwd=ctxt.basedir) + log_elem = xmlio.Fragment() + for out, err in cmdline.execute(): + if out is not None: + log.info(out) + if output_file: + output_file.write(out + os.linesep) + if out: + xmlio.SubElement(log_elem, 'message', level='info')[out] + if err is not None: + level = 'error' + if err.startswith('warning: '): + err = err[9:] + level = 'warning' + log.warning(err) + else: + log.error(err) + if output_file: + output_file.write(err + os.linesep) + if err: + xmlio.SubElement(log_elem, 'message', level=level)[err] + ctxt.log(log_elem) + finally: + if output_file: + output_file.close() + + if cmdline.returncode != 0: + ctxt.error('distutils failed (%s)' % cmdline.returncode) + +def pylint(ctxt, file_=None): """Extract data from a `pylint` run written to a file.""" - assert file, 'Missing required attribute "file"' + assert file_, 'Missing required attribute "file"' msg_re = re.compile(r'^(?P.+):(?P\d+): ' r'\[(?P[A-Z]\d*)(?:, (?P[\w\.]+))?\] ' r'(?P.*)$') @@ -59,8 +114,7 @@ problems = xmlio.Element('problems') try: - fd = open(ctxt.resolve(file), 'r') - print 'Reading Pylint results file', fd.name + fd = open(ctxt.resolve(file_), 'r') try: for line in fd: match = msg_re.search(line) @@ -131,13 +185,12 @@ except IOError, e: log.warning('Error opening coverage summary file (%s)', e) - -def unittest(ctxt, file=None): +def unittest(ctxt, file_=None): """Extract data from a unittest results file in XML format.""" - assert file, 'Missing required attribute "file"' + assert file_, 'Missing required attribute "file"' try: - fd = open(ctxt.resolve(file), 'r') + fd = open(ctxt.resolve(file_), 'r') try: results = xmlio.Fragment() for child in xmlio.parse(fd).children(): diff --git a/bitten/recipe.py b/bitten/recipe.py --- a/bitten/recipe.py +++ b/bitten/recipe.py @@ -18,6 +18,7 @@ # # Author: Christopher Lenz +import keyword import logging import os import time @@ -98,25 +99,32 @@ if errors: if self.onerror == 'fail': raise BuildError, 'Build step %s failed' % self.id - log.warning('Ignoring error in step %s (%s)', self.id, e) + log.warning('Ignoring errors in step %s (%s)', self.id, + ', '.join([error[1] for error in errors])) def _args(self, elem): - return dict([(name.replace('-', '_'), value) for name, value + return dict([(self._translate_name(name), value) for name, value in elem.attr.items()]) def _function(self, elem): if not elem.namespace.startswith('bitten:'): # Ignore elements in foreign namespaces return None - func_name = elem.name.replace('-', '_') + func_name = self._translate_name(elem.name) try: module = __import__(elem.namespace[7:], globals(), locals(), func_name) - func = getattr(module, elem.name) + func = getattr(module, func_name) return func except (ImportError, AttributeError), e: raise InvalidRecipeError, 'Cannot load "%s" (%s)' % (elem.name, e) + def _translate_name(self, name): + name = name.replace('-', '_') + if keyword.iskeyword(name) or name in __builtins__: + name = name + '_' + return name + class Recipe(object): """A build recipe. diff --git a/recipe.xml b/recipe.xml --- a/recipe.xml +++ b/recipe.xml @@ -19,7 +19,11 @@ - +