Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(499)

Side by Side Diff: build/check_gn_headers.py

Issue 2846473002: (reland) Optimize check_gn_headers.py for speed (Closed)
Patch Set: use multiprocessing Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | build/check_gn_headers_unittest.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #!/usr/bin/env python 1 #!/usr/bin/env python
2 # Copyright 2017 The Chromium Authors. All rights reserved. 2 # Copyright 2017 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be 3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file. 4 # found in the LICENSE file.
5 5
6 """Find header files missing in GN. 6 """Find header files missing in GN.
7 7
8 This script gets all the header files from ninja_deps, which is from the true 8 This script gets all the header files from ninja_deps, which is from the true
9 dependency generated by the compiler, and report if they don't exist in GN. 9 dependency generated by the compiler, and report if they don't exist in GN.
10 """ 10 """
11 11
12 import argparse 12 import argparse
13 import json 13 import json
14 import os 14 import os
15 import re 15 import re
16 import subprocess 16 import subprocess
17 import sys 17 import sys
18 from multiprocessing import Process, Queue
18 19
19 20
20 def GetHeadersFromNinja(out_dir): 21 def GetHeadersFromNinja(out_dir, q):
21 """Return all the header files from ninja_deps""" 22 """Return all the header files from ninja_deps"""
22 ninja_out = subprocess.check_output(['ninja', '-C', out_dir, '-t', 'deps']) 23
23 return ParseNinjaDepsOutput(ninja_out) 24 def NinjaSource():
25 cmd = ['ninja', '-C', out_dir, '-t', 'deps']
26 # A negative bufsize means to use the system default, which usually
27 # means fully buffered.
28 popen = subprocess.Popen(cmd, stdout=subprocess.PIPE, bufsize=-1)
29 for line in iter(popen.stdout.readline, ''):
30 yield line.rstrip()
31
32 popen.stdout.close()
33 return_code = popen.wait()
34 if return_code:
35 raise subprocess.CalledProcessError(return_code, cmd)
36
37 ninja_out = NinjaSource()
38 q.put(ParseNinjaDepsOutput(ninja_out))
24 39
25 40
26 def ParseNinjaDepsOutput(ninja_out): 41 def ParseNinjaDepsOutput(ninja_out):
27 """Parse ninja output and get the header files""" 42 """Parse ninja output and get the header files"""
28 all_headers = set() 43 all_headers = set()
29 44
30 prefix = '..' + os.sep + '..' + os.sep 45 prefix = '..' + os.sep + '..' + os.sep
31 46
32 is_valid = False 47 is_valid = False
33 for line in ninja_out.split('\n'): 48 for line in ninja_out:
34 if line.startswith(' '): 49 if line.startswith(' '):
35 if not is_valid: 50 if not is_valid:
36 continue 51 continue
37 if line.endswith('.h') or line.endswith('.hh'): 52 if line.endswith('.h') or line.endswith('.hh'):
38 f = line.strip() 53 f = line.strip()
39 if f.startswith(prefix): 54 if f.startswith(prefix):
40 f = f[6:] # Remove the '../../' prefix 55 f = f[6:] # Remove the '../../' prefix
41 # build/ only contains build-specific files like build_config.h 56 # build/ only contains build-specific files like build_config.h
42 # and buildflag.h, and system header files, so they should be 57 # and buildflag.h, and system header files, so they should be
43 # skipped. 58 # skipped.
44 if not f.startswith('build'): 59 if not f.startswith('build'):
45 all_headers.add(f) 60 all_headers.add(f)
46 else: 61 else:
47 is_valid = line.endswith('(VALID)') 62 is_valid = line.endswith('(VALID)')
48 63
49 return all_headers 64 return all_headers
50 65
51 66
52 def GetHeadersFromGN(out_dir): 67 def GetHeadersFromGN(out_dir, q):
53 """Return all the header files from GN""" 68 """Return all the header files from GN"""
54 subprocess.check_call(['gn', 'gen', out_dir, '--ide=json', '-q']) 69 subprocess.check_call(['gn', 'gen', out_dir, '--ide=json', '-q'])
55 gn_json = json.load(open(os.path.join(out_dir, 'project.json'))) 70 gn_json = json.load(open(os.path.join(out_dir, 'project.json')))
56 return ParseGNProjectJSON(gn_json) 71 q.put(ParseGNProjectJSON(gn_json))
57 72
58 73
59 def ParseGNProjectJSON(gn): 74 def ParseGNProjectJSON(gn):
60 """Parse GN output and get the header files""" 75 """Parse GN output and get the header files"""
61 all_headers = set() 76 all_headers = set()
62 77
63 for _target, properties in gn['targets'].iteritems(): 78 for _target, properties in gn['targets'].iteritems():
64 for f in properties.get('sources', []): 79 for f in properties.get('sources', []):
65 if f.endswith('.h') or f.endswith('.hh'): 80 if f.endswith('.h') or f.endswith('.hh'):
66 if f.startswith('//'): 81 if f.startswith('//'):
67 f = f[2:] # Strip the '//' prefix. 82 f = f[2:] # Strip the '//' prefix.
68 all_headers.add(f) 83 all_headers.add(f)
69 84
70 return all_headers 85 return all_headers
71 86
72 87
73 def GetDepsPrefixes(): 88 def GetDepsPrefixes(q):
74 """Return all the folders controlled by DEPS file""" 89 """Return all the folders controlled by DEPS file"""
75 gclient_out = subprocess.check_output( 90 gclient_out = subprocess.check_output(
76 ['gclient', 'recurse', '--no-progress', '-j1', 91 ['gclient', 'recurse', '--no-progress', '-j1',
77 'python', '-c', 'import os;print os.environ["GCLIENT_DEP_PATH"]']) 92 'python', '-c', 'import os;print os.environ["GCLIENT_DEP_PATH"]'])
78 prefixes = set() 93 prefixes = set()
79 for i in gclient_out.split('\n'): 94 for i in gclient_out.split('\n'):
80 if i.startswith('src/'): 95 if i.startswith('src/'):
81 i = i[4:] 96 i = i[4:]
82 prefixes.add(i) 97 prefixes.add(i)
83 return prefixes 98 q.put(prefixes)
84 99
85 100
86 def ParseWhiteList(whitelist): 101 def ParseWhiteList(whitelist):
87 out = set() 102 out = set()
88 for line in whitelist.split('\n'): 103 for line in whitelist.split('\n'):
89 line = re.sub(r'#.*', '', line).strip() 104 line = re.sub(r'#.*', '', line).strip()
90 if line: 105 if line:
91 out.add(line) 106 out.add(line)
92 return out 107 return out
93 108
94 109
95 def main(): 110 def main():
96 parser = argparse.ArgumentParser() 111 parser = argparse.ArgumentParser()
97 parser.add_argument('--out-dir', default='out/Release') 112 parser.add_argument('--out-dir', default='out/Release')
98 parser.add_argument('--json') 113 parser.add_argument('--json')
99 parser.add_argument('--whitelist') 114 parser.add_argument('--whitelist')
100 parser.add_argument('args', nargs=argparse.REMAINDER) 115 parser.add_argument('args', nargs=argparse.REMAINDER)
101 116
102 args, _extras = parser.parse_known_args() 117 args, _extras = parser.parse_known_args()
103 118
104 d = GetHeadersFromNinja(args.out_dir) 119 d_q = Queue()
105 gn = GetHeadersFromGN(args.out_dir) 120 d_p = Process(target=GetHeadersFromNinja, args=(args.out_dir, d_q,))
121 d_p.start()
122
123 gn_q = Queue()
124 gn_p = Process(target=GetHeadersFromGN, args=(args.out_dir, gn_q,))
125 gn_p.start()
126
127 deps_q = Queue()
128 deps_p = Process(target=GetDepsPrefixes, args=(deps_q,))
129 deps_p.start()
130
131 d = d_q.get()
132 gn = gn_q.get()
106 missing = d - gn 133 missing = d - gn
107 134
108 deps = GetDepsPrefixes() 135 deps = deps_q.get()
109 missing = {m for m in missing if not any(m.startswith(d) for d in deps)} 136 missing = {m for m in missing if not any(m.startswith(d) for d in deps)}
110 137
138 d_p.join()
139 gn_p.join()
140 deps_p.join()
141
111 if args.whitelist: 142 if args.whitelist:
112 whitelist = ParseWhiteList(open(args.whitelist).read()) 143 whitelist = ParseWhiteList(open(args.whitelist).read())
113 missing -= whitelist 144 missing -= whitelist
114 145
115 missing = sorted(missing) 146 missing = sorted(missing)
116 147
117 if args.json: 148 if args.json:
118 with open(args.json, 'w') as f: 149 with open(args.json, 'w') as f:
119 json.dump(missing, f) 150 json.dump(missing, f)
120 151
121 if len(missing) == 0: 152 if len(missing) == 0:
122 return 0 153 return 0
123 154
124 print 'The following files should be included in gn files:' 155 print 'The following files should be included in gn files:'
125 for i in missing: 156 for i in missing:
126 print i 157 print i
127 return 1 158 return 1
128 159
129 160
130 if __name__ == '__main__': 161 if __name__ == '__main__':
131 sys.exit(main()) 162 sys.exit(main())
OLDNEW
« no previous file with comments | « no previous file | build/check_gn_headers_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698