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