| #!/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 bisect |
| import re |
| import subprocess |
| import sys |
| |
| line_pattern = re.compile( |
| r'^// ###sourceLocation\(file:\s*"([^"]+)",\s*line:\s*([0-9]+)\s*\)') |
| |
| |
| def _make_line_map(filename, stream=None): |
| """ |
| >>> from StringIO import StringIO |
| >>> _make_line_map('box', |
| ... StringIO('''// ###sourceLocation(file: "foo.bar", line: 3) |
| ... line 2 |
| ... line 3 |
| ... line 4 |
| ... // ###sourceLocation(file: "baz.txt", line: 20) |
| ... 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(1), int(m.group(2)))) |
| 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( |
| '^(?P<file>' + '|'.join(re.escape(s) for s in sources) + |
| '):(?P<line>[0-9]+):(?P<column>[0-9]+):(?P<tail>.*)') |
| |
| assertion_pattern = re.compile( |
| '^(?P<head>.*( file | at |#[0-9]+: |[[]))(?P<file>' + |
| '|'.join(re.escape(s) for s in sources) + |
| ')(?P<middle>, line |:)(?P<line>[0-9]+)(?P<tail>.*)') |
| |
| 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('file'), |
| int(m.group('line'))) |
| 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('file'), |
| int(m.group('line'))) |
| line = '%s%s%s%s%s\n' % ( |
| m.group('head'), file, m.group('middle'), line_num, |
| m.group('tail')) |
| sys.stdout.write(line) |
| |
| sys.exit(command.wait()) |
| |
| if __name__ == '__main__': |
| run() |