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