Chromium Code Reviews| Index: build/fix_gn_headers.py |
| diff --git a/build/fix_gn_headers.py b/build/fix_gn_headers.py |
| new file mode 100755 |
| index 0000000000000000000000000000000000000000..14e8ff061badc7e2332e6698abe014c35154a6da |
| --- /dev/null |
| +++ b/build/fix_gn_headers.py |
| @@ -0,0 +1,122 @@ |
| +#!/usr/bin/env python |
| +# Copyright 2017 The Chromium Authors. All rights reserved. |
| +# Use of this source code is governed by a BSD-style license that can be |
| +# found in the LICENSE file. |
| + |
| +"""Fix header files missing in GN. |
| + |
| +This script takes the missing header files from check_gn_headers.py, and |
| +try to fix them by adding them to the GN files. |
| +Manual cleaning up is likely required afterwards. |
| +""" |
| + |
| +import argparse |
| +import os |
| +import re |
| +import subprocess |
| +import sys |
| + |
| + |
| +def AddHeadersNextToCC(headers, skip_ambiguous=True): |
| + """Add header files next to the corresponding .cc files in GN files. |
| + |
| + When skip_ambiguous is True, skip if multiple .cc files are found. |
| + Returns unhandled headers. |
| + |
| + Manual cleaning up is likely required, especially if not skip_ambiguous. |
| + """ |
| + edits = {} |
| + unhandled = [] |
| + for filename in headers: |
| + filename = filename.strip() |
| + if not (filename.endswith('.h') or filename.endswith('.hh')): |
|
Nico
2017/03/28 14:37:55
Do we have any headers ending in .hh?
wychen
2017/03/28 17:52:05
Yes, we do, and some of them are part of the GN mi
|
| + continue |
| + basename = os.path.basename(filename) |
| + print filename |
| + cc = r'\b' + os.path.splitext(basename)[0] + r'\.(cc|cpp|mm)\b' |
| + p = subprocess.Popen( |
| + ['git', 'grep', '-En', cc + '"', '--', '*.gn', '*.gni'], |
| + stdout=subprocess.PIPE) |
| + out, _ = p.communicate() |
| + if p.returncode != 0 or not out: |
| + unhandled.append(filename) |
| + continue |
| + |
| + if skip_ambiguous and len(out.splitlines()) > 1: |
| + print '\n[WARNING] Ambiguous matching for', filename |
| + print out |
| + continue |
| + |
| + for gnline in out.splitlines(): |
| + gnfile, linenr, contents = gnline.split(':') |
| + linenr = int(linenr) |
| + new = re.sub(cc, basename, contents) |
| + lines = open(gnfile).read().splitlines() |
| + if lines[linenr] == new: |
| + continue |
| + if lines[linenr - 2] == new: |
|
Nico
2017/03/28 14:37:55
why this?
wychen
2017/03/28 17:52:05
This and the line above is for deduplication.
line
|
| + continue |
| + print ' ', gnfile, linenr, new |
| + edits.setdefault(gnfile, {})[linenr] = new |
| + |
| + for gnfile in edits: |
| + lines = open(gnfile).read().splitlines() |
| + for l in reversed(sorted(edits[gnfile].keys())): |
|
Nico
2017/03/28 14:37:55
Ah, that was the bug! I had assumed the keys were
wychen
2017/03/28 17:52:05
Done.
|
| + lines.insert(l, edits[gnfile][l]) |
| + open(gnfile, 'w').write('\n'.join(lines) + '\n') |
| + |
| + return unhandled |
| + |
| + |
| +def AddHeadersToSources(headers, skip_ambiguous=True): |
| + """Add header files to the sources list in the closest GN file. |
|
Nico
2017/03/28 14:37:55
"to the _first_ sources list"
Mention somehow tha
wychen
2017/03/28 17:52:05
Done.
|
| + |
| + When skip_ambiguous is True, skip if multiple sources arrays are found. |
| + |
| + "git cl format" afterwards is required. Manually cleaning up duplicated items |
| + is likely required. |
| + """ |
| + for filename in headers: |
| + filename = filename.strip() |
| + print filename |
| + dirname = os.path.dirname(filename) |
| + while not os.path.exists(os.path.join(dirname, 'BUILD.gn')): |
| + dirname = os.path.dirname(dirname) |
| + rel = filename[len(dirname) + 1:] |
| + gnfile = os.path.join(dirname, 'BUILD.gn') |
| + |
| + lines = open(gnfile).read().splitlines() |
| + matched = [i for i, l in enumerate(lines) if ' sources = [' in l] |
| + if skip_ambiguous and len(matched) > 1: |
| + print '[WARNING] Multiple sources in', gnfile |
| + continue |
| + |
| + if len(matched) < 1: |
| + continue |
| + print ' ', gnfile, rel |
| + index = matched[0] |
| + lines.insert(index + 1, '"%s",' % rel) |
| + open(gnfile, 'w').write('\n'.join(lines) + '\n') |
| + |
| + |
| +def main(): |
| + parser = argparse.ArgumentParser() |
| + parser.add_argument('--input', required=True, |
| + help="missing headers, output of check_gn_headers.py") |
|
Nico
2017/03/28 14:37:55
nit: I'd make this one a positional argument
wychen
2017/03/28 17:52:05
Done.
|
| + parser.add_argument('--prefix', |
| + help="only handle path name with this prefix") |
| + parser.add_argument('args', nargs=argparse.REMAINDER) |
| + |
| + args, _extras = parser.parse_known_args() |
| + |
| + headers = open(args.input).readlines() |
| + |
| + if args.prefix: |
| + headers = [i for i in headers if i.startswith(args.prefix)] |
| + |
| + unhandled = AddHeadersNextToCC(headers) |
| + AddHeadersToSources(unhandled) |
| + |
| + |
| +if __name__ == '__main__': |
| + sys.exit(main()) |