1*635a8641SAndroid Build Coastguard Worker#!/usr/bin/env python 2*635a8641SAndroid Build Coastguard Worker# Copyright 2017 The Chromium Authors. All rights reserved. 3*635a8641SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*635a8641SAndroid Build Coastguard Worker# found in the LICENSE file. 5*635a8641SAndroid Build Coastguard Worker 6*635a8641SAndroid Build Coastguard Worker"""Fix header files missing in GN. 7*635a8641SAndroid Build Coastguard Worker 8*635a8641SAndroid Build Coastguard WorkerThis script takes the missing header files from check_gn_headers.py, and 9*635a8641SAndroid Build Coastguard Workertry to fix them by adding them to the GN files. 10*635a8641SAndroid Build Coastguard WorkerManual cleaning up is likely required afterwards. 11*635a8641SAndroid Build Coastguard Worker""" 12*635a8641SAndroid Build Coastguard Worker 13*635a8641SAndroid Build Coastguard Workerimport argparse 14*635a8641SAndroid Build Coastguard Workerimport os 15*635a8641SAndroid Build Coastguard Workerimport re 16*635a8641SAndroid Build Coastguard Workerimport subprocess 17*635a8641SAndroid Build Coastguard Workerimport sys 18*635a8641SAndroid Build Coastguard Worker 19*635a8641SAndroid Build Coastguard Worker 20*635a8641SAndroid Build Coastguard Workerdef GitGrep(pattern): 21*635a8641SAndroid Build Coastguard Worker p = subprocess.Popen( 22*635a8641SAndroid Build Coastguard Worker ['git', 'grep', '-En', pattern, '--', '*.gn', '*.gni'], 23*635a8641SAndroid Build Coastguard Worker stdout=subprocess.PIPE) 24*635a8641SAndroid Build Coastguard Worker out, _ = p.communicate() 25*635a8641SAndroid Build Coastguard Worker return out, p.returncode 26*635a8641SAndroid Build Coastguard Worker 27*635a8641SAndroid Build Coastguard Worker 28*635a8641SAndroid Build Coastguard Workerdef ValidMatches(basename, cc, grep_lines): 29*635a8641SAndroid Build Coastguard Worker """Filter out 'git grep' matches with header files already.""" 30*635a8641SAndroid Build Coastguard Worker matches = [] 31*635a8641SAndroid Build Coastguard Worker for line in grep_lines: 32*635a8641SAndroid Build Coastguard Worker gnfile, linenr, contents = line.split(':') 33*635a8641SAndroid Build Coastguard Worker linenr = int(linenr) 34*635a8641SAndroid Build Coastguard Worker new = re.sub(cc, basename, contents) 35*635a8641SAndroid Build Coastguard Worker lines = open(gnfile).read().splitlines() 36*635a8641SAndroid Build Coastguard Worker assert contents in lines[linenr - 1] 37*635a8641SAndroid Build Coastguard Worker # Skip if it's already there. It could be before or after the match. 38*635a8641SAndroid Build Coastguard Worker if lines[linenr] == new: 39*635a8641SAndroid Build Coastguard Worker continue 40*635a8641SAndroid Build Coastguard Worker if lines[linenr - 2] == new: 41*635a8641SAndroid Build Coastguard Worker continue 42*635a8641SAndroid Build Coastguard Worker print(' ', gnfile, linenr, new) 43*635a8641SAndroid Build Coastguard Worker matches.append((gnfile, linenr, new)) 44*635a8641SAndroid Build Coastguard Worker return matches 45*635a8641SAndroid Build Coastguard Worker 46*635a8641SAndroid Build Coastguard Worker 47*635a8641SAndroid Build Coastguard Workerdef AddHeadersNextToCC(headers, skip_ambiguous=True): 48*635a8641SAndroid Build Coastguard Worker """Add header files next to the corresponding .cc files in GN files. 49*635a8641SAndroid Build Coastguard Worker 50*635a8641SAndroid Build Coastguard Worker When skip_ambiguous is True, skip if multiple .cc files are found. 51*635a8641SAndroid Build Coastguard Worker Returns unhandled headers. 52*635a8641SAndroid Build Coastguard Worker 53*635a8641SAndroid Build Coastguard Worker Manual cleaning up is likely required, especially if not skip_ambiguous. 54*635a8641SAndroid Build Coastguard Worker """ 55*635a8641SAndroid Build Coastguard Worker edits = {} 56*635a8641SAndroid Build Coastguard Worker unhandled = [] 57*635a8641SAndroid Build Coastguard Worker for filename in headers: 58*635a8641SAndroid Build Coastguard Worker filename = filename.strip() 59*635a8641SAndroid Build Coastguard Worker if not (filename.endswith('.h') or filename.endswith('.hh')): 60*635a8641SAndroid Build Coastguard Worker continue 61*635a8641SAndroid Build Coastguard Worker basename = os.path.basename(filename) 62*635a8641SAndroid Build Coastguard Worker print(filename) 63*635a8641SAndroid Build Coastguard Worker cc = r'\b' + os.path.splitext(basename)[0] + r'\.(cc|cpp|mm)\b' 64*635a8641SAndroid Build Coastguard Worker out, returncode = GitGrep('(/|")' + cc + '"') 65*635a8641SAndroid Build Coastguard Worker if returncode != 0 or not out: 66*635a8641SAndroid Build Coastguard Worker unhandled.append(filename) 67*635a8641SAndroid Build Coastguard Worker continue 68*635a8641SAndroid Build Coastguard Worker 69*635a8641SAndroid Build Coastguard Worker matches = ValidMatches(basename, cc, out.splitlines()) 70*635a8641SAndroid Build Coastguard Worker 71*635a8641SAndroid Build Coastguard Worker if len(matches) == 0: 72*635a8641SAndroid Build Coastguard Worker continue 73*635a8641SAndroid Build Coastguard Worker if len(matches) > 1: 74*635a8641SAndroid Build Coastguard Worker print('\n[WARNING] Ambiguous matching for', filename) 75*635a8641SAndroid Build Coastguard Worker for i in enumerate(matches, 1): 76*635a8641SAndroid Build Coastguard Worker print('%d: %s' % (i[0], i[1])) 77*635a8641SAndroid Build Coastguard Worker print 78*635a8641SAndroid Build Coastguard Worker if skip_ambiguous: 79*635a8641SAndroid Build Coastguard Worker continue 80*635a8641SAndroid Build Coastguard Worker 81*635a8641SAndroid Build Coastguard Worker picked = raw_input('Pick the matches ("2,3" for multiple): ') 82*635a8641SAndroid Build Coastguard Worker try: 83*635a8641SAndroid Build Coastguard Worker matches = [matches[int(i) - 1] for i in picked.split(',')] 84*635a8641SAndroid Build Coastguard Worker except (ValueError, IndexError): 85*635a8641SAndroid Build Coastguard Worker continue 86*635a8641SAndroid Build Coastguard Worker 87*635a8641SAndroid Build Coastguard Worker for match in matches: 88*635a8641SAndroid Build Coastguard Worker gnfile, linenr, new = match 89*635a8641SAndroid Build Coastguard Worker print(' ', gnfile, linenr, new) 90*635a8641SAndroid Build Coastguard Worker edits.setdefault(gnfile, {})[linenr] = new 91*635a8641SAndroid Build Coastguard Worker 92*635a8641SAndroid Build Coastguard Worker for gnfile in edits: 93*635a8641SAndroid Build Coastguard Worker lines = open(gnfile).read().splitlines() 94*635a8641SAndroid Build Coastguard Worker for l in sorted(edits[gnfile].keys(), reverse=True): 95*635a8641SAndroid Build Coastguard Worker lines.insert(l, edits[gnfile][l]) 96*635a8641SAndroid Build Coastguard Worker open(gnfile, 'w').write('\n'.join(lines) + '\n') 97*635a8641SAndroid Build Coastguard Worker 98*635a8641SAndroid Build Coastguard Worker return unhandled 99*635a8641SAndroid Build Coastguard Worker 100*635a8641SAndroid Build Coastguard Worker 101*635a8641SAndroid Build Coastguard Workerdef AddHeadersToSources(headers, skip_ambiguous=True): 102*635a8641SAndroid Build Coastguard Worker """Add header files to the sources list in the first GN file. 103*635a8641SAndroid Build Coastguard Worker 104*635a8641SAndroid Build Coastguard Worker The target GN file is the first one up the parent directories. 105*635a8641SAndroid Build Coastguard Worker This usually does the wrong thing for _test files if the test and the main 106*635a8641SAndroid Build Coastguard Worker target are in the same .gn file. 107*635a8641SAndroid Build Coastguard Worker When skip_ambiguous is True, skip if multiple sources arrays are found. 108*635a8641SAndroid Build Coastguard Worker 109*635a8641SAndroid Build Coastguard Worker "git cl format" afterwards is required. Manually cleaning up duplicated items 110*635a8641SAndroid Build Coastguard Worker is likely required. 111*635a8641SAndroid Build Coastguard Worker """ 112*635a8641SAndroid Build Coastguard Worker for filename in headers: 113*635a8641SAndroid Build Coastguard Worker filename = filename.strip() 114*635a8641SAndroid Build Coastguard Worker print(filename) 115*635a8641SAndroid Build Coastguard Worker dirname = os.path.dirname(filename) 116*635a8641SAndroid Build Coastguard Worker while not os.path.exists(os.path.join(dirname, 'BUILD.gn')): 117*635a8641SAndroid Build Coastguard Worker dirname = os.path.dirname(dirname) 118*635a8641SAndroid Build Coastguard Worker rel = filename[len(dirname) + 1:] 119*635a8641SAndroid Build Coastguard Worker gnfile = os.path.join(dirname, 'BUILD.gn') 120*635a8641SAndroid Build Coastguard Worker 121*635a8641SAndroid Build Coastguard Worker lines = open(gnfile).read().splitlines() 122*635a8641SAndroid Build Coastguard Worker matched = [i for i, l in enumerate(lines) if ' sources = [' in l] 123*635a8641SAndroid Build Coastguard Worker if skip_ambiguous and len(matched) > 1: 124*635a8641SAndroid Build Coastguard Worker print('[WARNING] Multiple sources in', gnfile) 125*635a8641SAndroid Build Coastguard Worker continue 126*635a8641SAndroid Build Coastguard Worker 127*635a8641SAndroid Build Coastguard Worker if len(matched) < 1: 128*635a8641SAndroid Build Coastguard Worker continue 129*635a8641SAndroid Build Coastguard Worker print(' ', gnfile, rel) 130*635a8641SAndroid Build Coastguard Worker index = matched[0] 131*635a8641SAndroid Build Coastguard Worker lines.insert(index + 1, '"%s",' % rel) 132*635a8641SAndroid Build Coastguard Worker open(gnfile, 'w').write('\n'.join(lines) + '\n') 133*635a8641SAndroid Build Coastguard Worker 134*635a8641SAndroid Build Coastguard Worker 135*635a8641SAndroid Build Coastguard Workerdef RemoveHeader(headers, skip_ambiguous=True): 136*635a8641SAndroid Build Coastguard Worker """Remove non-existing headers in GN files. 137*635a8641SAndroid Build Coastguard Worker 138*635a8641SAndroid Build Coastguard Worker When skip_ambiguous is True, skip if multiple matches are found. 139*635a8641SAndroid Build Coastguard Worker """ 140*635a8641SAndroid Build Coastguard Worker edits = {} 141*635a8641SAndroid Build Coastguard Worker unhandled = [] 142*635a8641SAndroid Build Coastguard Worker for filename in headers: 143*635a8641SAndroid Build Coastguard Worker filename = filename.strip() 144*635a8641SAndroid Build Coastguard Worker if not (filename.endswith('.h') or filename.endswith('.hh')): 145*635a8641SAndroid Build Coastguard Worker continue 146*635a8641SAndroid Build Coastguard Worker basename = os.path.basename(filename) 147*635a8641SAndroid Build Coastguard Worker print(filename) 148*635a8641SAndroid Build Coastguard Worker out, returncode = GitGrep('(/|")' + basename + '"') 149*635a8641SAndroid Build Coastguard Worker if returncode != 0 or not out: 150*635a8641SAndroid Build Coastguard Worker unhandled.append(filename) 151*635a8641SAndroid Build Coastguard Worker print(' Not found') 152*635a8641SAndroid Build Coastguard Worker continue 153*635a8641SAndroid Build Coastguard Worker 154*635a8641SAndroid Build Coastguard Worker grep_lines = out.splitlines() 155*635a8641SAndroid Build Coastguard Worker matches = [] 156*635a8641SAndroid Build Coastguard Worker for line in grep_lines: 157*635a8641SAndroid Build Coastguard Worker gnfile, linenr, contents = line.split(':') 158*635a8641SAndroid Build Coastguard Worker print(' ', gnfile, linenr, contents) 159*635a8641SAndroid Build Coastguard Worker linenr = int(linenr) 160*635a8641SAndroid Build Coastguard Worker lines = open(gnfile).read().splitlines() 161*635a8641SAndroid Build Coastguard Worker assert contents in lines[linenr - 1] 162*635a8641SAndroid Build Coastguard Worker matches.append((gnfile, linenr, contents)) 163*635a8641SAndroid Build Coastguard Worker 164*635a8641SAndroid Build Coastguard Worker if len(matches) == 0: 165*635a8641SAndroid Build Coastguard Worker continue 166*635a8641SAndroid Build Coastguard Worker if len(matches) > 1: 167*635a8641SAndroid Build Coastguard Worker print('\n[WARNING] Ambiguous matching for', filename) 168*635a8641SAndroid Build Coastguard Worker for i in enumerate(matches, 1): 169*635a8641SAndroid Build Coastguard Worker print('%d: %s' % (i[0], i[1])) 170*635a8641SAndroid Build Coastguard Worker print 171*635a8641SAndroid Build Coastguard Worker if skip_ambiguous: 172*635a8641SAndroid Build Coastguard Worker continue 173*635a8641SAndroid Build Coastguard Worker 174*635a8641SAndroid Build Coastguard Worker picked = raw_input('Pick the matches ("2,3" for multiple): ') 175*635a8641SAndroid Build Coastguard Worker try: 176*635a8641SAndroid Build Coastguard Worker matches = [matches[int(i) - 1] for i in picked.split(',')] 177*635a8641SAndroid Build Coastguard Worker except (ValueError, IndexError): 178*635a8641SAndroid Build Coastguard Worker continue 179*635a8641SAndroid Build Coastguard Worker 180*635a8641SAndroid Build Coastguard Worker for match in matches: 181*635a8641SAndroid Build Coastguard Worker gnfile, linenr, contents = match 182*635a8641SAndroid Build Coastguard Worker print(' ', gnfile, linenr, contents) 183*635a8641SAndroid Build Coastguard Worker edits.setdefault(gnfile, set()).add(linenr) 184*635a8641SAndroid Build Coastguard Worker 185*635a8641SAndroid Build Coastguard Worker for gnfile in edits: 186*635a8641SAndroid Build Coastguard Worker lines = open(gnfile).read().splitlines() 187*635a8641SAndroid Build Coastguard Worker for l in sorted(edits[gnfile], reverse=True): 188*635a8641SAndroid Build Coastguard Worker lines.pop(l - 1) 189*635a8641SAndroid Build Coastguard Worker open(gnfile, 'w').write('\n'.join(lines) + '\n') 190*635a8641SAndroid Build Coastguard Worker 191*635a8641SAndroid Build Coastguard Worker return unhandled 192*635a8641SAndroid Build Coastguard Worker 193*635a8641SAndroid Build Coastguard Worker 194*635a8641SAndroid Build Coastguard Workerdef main(): 195*635a8641SAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 196*635a8641SAndroid Build Coastguard Worker parser.add_argument('input_file', help="missing or non-existing headers, " 197*635a8641SAndroid Build Coastguard Worker "output of check_gn_headers.py") 198*635a8641SAndroid Build Coastguard Worker parser.add_argument('--prefix', 199*635a8641SAndroid Build Coastguard Worker help="only handle path name with this prefix") 200*635a8641SAndroid Build Coastguard Worker parser.add_argument('--remove', action='store_true', 201*635a8641SAndroid Build Coastguard Worker help="treat input_file as non-existing headers") 202*635a8641SAndroid Build Coastguard Worker 203*635a8641SAndroid Build Coastguard Worker args, _extras = parser.parse_known_args() 204*635a8641SAndroid Build Coastguard Worker 205*635a8641SAndroid Build Coastguard Worker headers = open(args.input_file).readlines() 206*635a8641SAndroid Build Coastguard Worker 207*635a8641SAndroid Build Coastguard Worker if args.prefix: 208*635a8641SAndroid Build Coastguard Worker headers = [i for i in headers if i.startswith(args.prefix)] 209*635a8641SAndroid Build Coastguard Worker 210*635a8641SAndroid Build Coastguard Worker if args.remove: 211*635a8641SAndroid Build Coastguard Worker RemoveHeader(headers, False) 212*635a8641SAndroid Build Coastguard Worker else: 213*635a8641SAndroid Build Coastguard Worker unhandled = AddHeadersNextToCC(headers) 214*635a8641SAndroid Build Coastguard Worker AddHeadersToSources(unhandled) 215*635a8641SAndroid Build Coastguard Worker 216*635a8641SAndroid Build Coastguard Worker 217*635a8641SAndroid Build Coastguard Workerif __name__ == '__main__': 218*635a8641SAndroid Build Coastguard Worker sys.exit(main()) 219