blob: e11bcacbbfee52b3832eaebc69803d32eeae53b1 [file] [log] [blame]
dchengc711a7fd2015-12-26 02:18:511#!/usr/bin/env python
Daniel Chengb82e8ec42015-12-18 01:15:092# 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 Cheng51c55302017-05-04 00:39:1619# python apply_fixits.py -p <build_directory> < generated-fixits
Daniel Chengb82e8ec42015-12-18 01:15:0920
21import argparse
22import collections
23import fileinput
24import os
25import re
26import 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
35FixIt = collections.namedtuple(
36 'FixIt', ('start_line', 'start_col', 'end_line', 'end_col', 'text'))
37
38
39def main():
40 parser = argparse.ArgumentParser()
41 parser.add_argument(
Daniel Cheng51c55302017-05-04 00:39:1642 '-p',
43 required=True,
Daniel Chengb82e8ec42015-12-18 01:15:0944 help='path to the build directory to complete relative paths in fixits')
45 args = parser.parse_args()
46
47 fixits = collections.defaultdict(list)
dcheng955d90dc2015-12-31 02:34:5148 for line in fileinput.input(['-']):
Daniel Chengb82e8ec42015-12-18 01:15:0949 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 Cheng51c55302017-05-04 00:39:1663 with open(os.path.join(args.p, k), 'rb+') as f:
Daniel Chengb82e8ec42015-12-18 01:15:0964 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
84if __name__ == '__main__':
85 sys.exit(main())