| #!/usr/bin/env python |
| # line-directive.py - Transform line numbers in error messages -*- python -*- |
| # |
| # This source file is part of the Swift.org open source project |
| # |
| # Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors |
| # Licensed under Apache License v2.0 with Runtime Library Exception |
| # |
| # See http://swift.org/LICENSE.txt for license information |
| # See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| # |
| # ---------------------------------------------------------------------------- |
| # |
| # Converts line numbers in error messages according to "line directive" |
| # comments. |
| # |
| # ---------------------------------------------------------------------------- |
| |
| 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, |
| universal_newlines = True |
| ) |
| |
| 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() |