#!/usr/bin/env python #convert line numbers in error messages according to "line directive" comments import os import sys import re import bisect import subprocess line_pattern = re.compile( r'^// ###line ([0-9]+) "([^"]+)"\s*') def _make_line_map(filename, stream=None): """ >>> from StringIO import StringIO >>> _make_line_map('box', ... StringIO('''// ###line 3 "foo.bar" ... line 2 ... line 3 ... line 4 ... // ###line 20 "baz.txt" ... line 6 ... line 7 ... ''')) [(0, 'box', 1), (1, 'foo.bar', 3), (5, 'baz.txt', 20)] """ result = [(0, filename, 1)] input = stream or open(filename) for i, l in enumerate(input.readlines()): m = line_pattern.match(l) if m: result.append((i+1, m.group(2), int(m.group(1)))) return result _line_maps = {} def fline_map(filename): map = _line_maps.get(filename) if map is None: map = _make_line_map(filename) _line_maps[filename] = map return map def map_line(filename, line_num): assert(line_num > 0) map = fline_map(filename) index = bisect.bisect_left(map, (line_num,'',0)) base = map[index - 1] return base[1], base[2] + (line_num - base[0]-1) def run(): if len(sys.argv) <= 1: import doctest doctest.testmod() else: dashes = sys.argv.index('--') sources = sys.argv[1:dashes] command = subprocess.Popen( sys.argv[dashes + 1:], stderr = subprocess.STDOUT, stdout = subprocess.PIPE ) error_pattern = re.compile( '^(' + '|'.join(re.escape(s) for s in sources) + '):([0-9]+):([0-9]+):(.*)') assertion_pattern = re.compile( '^(.*)(: file| at|#[0-9]+:) (' + '|'.join(re.escape(s) for s in sources) + ')(, line |:)([0-9]+)(.*)') while True: line = command.stdout.readline() if line == '': break l = line.rstrip('\n') m = error_pattern.match(l) if m: file, line_num = map_line(m.group(1), int(m.group(2))) line = '%s:%s:%s:%s\n' % (file, line_num, int(m.group(3)), m.group(4)) else: m = assertion_pattern.match(l) if m: file, line_num = map_line(m.group(3), int(m.group(5))) line = '%s%s %s%s%s%s\n' % (m.group(1), m.group(2), file, m.group(4), line_num, m.group(6)) sys.stdout.write(line) sys.exit(command.wait()) if __name__ == '__main__': run()