dcheng | c711a7fd | 2015-12-26 02:18:51 | [diff] [blame] | 1 | #!/usr/bin/env python |
Daniel Cheng | b82e8ec4 | 2015-12-18 01:15:09 | [diff] [blame] | 2 | # Copyright 2015 The Chromium Authors. All rights reserved. |
| 3 | # Use of this source code is governed by a BSD-style license that can be |
| 4 | # found in the LICENSE file. |
| 5 | # |
| 6 | # Script to apply fixits generated by clang. This is to work around the fact |
| 7 | # that clang's -Xclang -fixit-recompile flag, which automatically applies fixits |
| 8 | # and recompiles, doesn't work well with parallel invocations of clang. |
| 9 | # |
| 10 | # Usage: |
| 11 | # 1. Enable parseable fixits and disable warnings as errors. Instructions for |
| 12 | # doing this vary based on the build environment, but for GN, warnings as |
| 13 | # errors can be disabled by setting treat_warnings_as_errors = false |
| 14 | # Enabling parseable fixits requires editing build/config/compiler/BUILD.gn |
| 15 | # and adding `-fdiagnostics-parseable-fixits` to cflags. |
| 16 | # 2. Build everything and capture the output: |
| 17 | # ninja -C <build_directory> &> generated-fixits |
| 18 | # 3. Apply the fixits with this script: |
Daniel Cheng | 51c5530 | 2017-05-04 00:39:16 | [diff] [blame] | 19 | # python apply_fixits.py -p <build_directory> < generated-fixits |
Daniel Cheng | b82e8ec4 | 2015-12-18 01:15:09 | [diff] [blame] | 20 | |
| 21 | import argparse |
| 22 | import collections |
| 23 | import fileinput |
| 24 | import os |
| 25 | import re |
| 26 | import sys |
| 27 | |
| 28 | # fix-it:"../../base/threading/sequenced_worker_pool.h":{341:3-341:11}:"" |
| 29 | # Note that the file path is relative to the build directory. |
| 30 | _FIXIT_RE = re.compile(r'^fix-it:"(?P<file>.+?)":' |
| 31 | r'{(?P<start_line>\d+?):(?P<start_col>\d+?)-' |
| 32 | r'(?P<end_line>\d+?):(?P<end_col>\d+?)}:' |
| 33 | r'"(?P<text>.*?)"$') |
| 34 | |
| 35 | FixIt = collections.namedtuple( |
| 36 | 'FixIt', ('start_line', 'start_col', 'end_line', 'end_col', 'text')) |
| 37 | |
| 38 | |
| 39 | def main(): |
| 40 | parser = argparse.ArgumentParser() |
| 41 | parser.add_argument( |
Daniel Cheng | 51c5530 | 2017-05-04 00:39:16 | [diff] [blame] | 42 | '-p', |
| 43 | required=True, |
Daniel Cheng | b82e8ec4 | 2015-12-18 01:15:09 | [diff] [blame] | 44 | help='path to the build directory to complete relative paths in fixits') |
| 45 | args = parser.parse_args() |
| 46 | |
| 47 | fixits = collections.defaultdict(list) |
dcheng | 955d90dc | 2015-12-31 02:34:51 | [diff] [blame] | 48 | for line in fileinput.input(['-']): |
Daniel Cheng | b82e8ec4 | 2015-12-18 01:15:09 | [diff] [blame] | 49 | if not line.startswith('fix-it:'): |
| 50 | continue |
| 51 | m = _FIXIT_RE.match(line) |
| 52 | if not m: |
| 53 | continue |
| 54 | # The negative line numbers are a cheap hack so we can sort things in line |
| 55 | # order but reverse column order. Applying the fixits in reverse order makes |
| 56 | # things simpler, since offsets won't have to be adjusted as the text is |
| 57 | # changed. |
| 58 | fixits[m.group('file')].append(FixIt( |
| 59 | int(m.group('start_line')), -int(m.group('start_col')), int(m.group( |
| 60 | 'end_line')), -int(m.group('end_col')), m.group('text'))) |
| 61 | for k, v in fixits.iteritems(): |
| 62 | v.sort() |
Daniel Cheng | 51c5530 | 2017-05-04 00:39:16 | [diff] [blame] | 63 | with open(os.path.join(args.p, k), 'rb+') as f: |
Daniel Cheng | b82e8ec4 | 2015-12-18 01:15:09 | [diff] [blame] | 64 | lines = f.readlines() |
| 65 | last_fixit = None |
| 66 | for fixit in v: |
| 67 | if fixit.start_line != fixit.end_line: |
| 68 | print 'error: multiline fixits not supported! file: %s, fixit: %s' % ( |
| 69 | k, fixit) |
| 70 | sys.exit(1) |
| 71 | if fixit == last_fixit: |
| 72 | continue |
| 73 | last_fixit = fixit |
| 74 | # The line/column numbers emitted in fixit hints start at 1, so offset |
| 75 | # is appropriately. |
| 76 | line = lines[fixit.start_line - 1] |
| 77 | lines[fixit.start_line - 1] = (line[:-fixit.start_col - 1] + fixit.text |
| 78 | + line[-fixit.end_col - 1:]) |
| 79 | f.seek(0) |
| 80 | f.truncate() |
| 81 | f.writelines(lines) |
| 82 | |
| 83 | |
| 84 | if __name__ == '__main__': |
| 85 | sys.exit(main()) |