#!/usr/bin/env python # # Transform compiler messages into gnu format so that Geany can locate # the files automatically. Inside Geany, use this error regex: # # ^([^:]+?):([0-9]+):.+ # # You can change the "PATTERNS" constant below, to your needs. # # import os import sys import re import functools from subprocess import Popen, PIPE from threading import Thread try: from Queue import Queue, Empty except ImportError: from queue import Queue, Empty # python 3.x ON_POSIX = 'posix' in sys.builtin_module_names # # Add new languages here with their own regular expressions here. # # "pat" is the pattern for parsing. All other fields are regex group # numbers. You can also use constant strings instead of numbers (e.g. # when a field is not available) # PATTERNS = { "python": { 'pat': re.compile( r"""\s*File\s+"([^"]+)",\s+line\s+(\d+),\s+in\s+([^\s].*)"""), 'fpath': 0, 'lineno': 1, 'level': 'E', 'message': 'Python error', } } def enqueue_output(out, queue): for line in iter(out.readline, b''): queue.put(line) out.close() def transform(args, shell, outproc, errproc): """Process a command's output. @param args: Arguments to start the program. @param shell: Set True to execute it with the shell. @param outproc: Callback to process a line of stderr. @param errproc: Callback to process a line of stderr. @return: Exit code of the application. """ proc = Popen(args, shell=shell, stdout=PIPE, stderr=PIPE, bufsize=1, close_fds=ON_POSIX) qo = Queue() to = Thread(target=enqueue_output, args=(proc.stdout, qo)) to.daemon = True to.start() qe = Queue() te = Thread(target=enqueue_output, args=(proc.stderr, qe)) te.daemon = True te.start() def proc_output(final=False): while True: try: line = qo.get_nowait() outproc(line) except Empty: pass try: line = qe.get_nowait() errproc(line) except Empty: pass if not final: break if qo.empty() and qe.empty(): break while True: terminated = proc.poll() is not None if terminated: break proc_output() proc_output(True) return proc.returncode def _get_pitem(res, props, propname): if propname in props: ret = props[propname] if isinstance(ret, int): return res[ret] else: return ret else: return "" def procline(fout, s): global PATTERNS fout.write(s) fout.flush() for key, props in PATTERNS.iteritems(): sre = props["pat"].match(s) if sre: res = sre.groups() fout.write(u'"%s":%s:%s:%s\n' % ( _get_pitem(res, props, "fpath"), _get_pitem(res, props, "lineno"), _get_pitem(res, props, "level"), _get_pitem(res, props, "message"), )) fout.flush() raise SystemExit(transform(sys.argv[1:], False, functools.partial(procline, sys.stdout), functools.partial(procline, sys.stderr), ))