[CleanUp] Use base::STLSetDifference() in place of std::set_difference()
[chromium-blink-merge.git] / ppapi / PRESUBMIT.py
blob7d6a400bd4d50b638b19cc7a469ed9a8d1106160
1 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 import os
6 import re
7 import sys
8 import subprocess
11 def RunCmdAndCheck(cmd, err_string, output_api, cwd=None):
12 results = []
13 p = subprocess.Popen(cmd, cwd=cwd,
14 stdout=subprocess.PIPE,
15 stderr=subprocess.PIPE)
16 (p_stdout, p_stderr) = p.communicate()
17 if p.returncode:
18 results.append(
19 output_api.PresubmitError(err_string,
20 long_text=p_stderr))
21 return results
24 def RunUnittests(input_api, output_api):
25 # Run some Generator unittests if the generator source was changed.
26 results = []
27 files = input_api.LocalPaths()
28 generator_files = []
29 for filename in files:
30 name_parts = filename.split(os.sep)
31 if name_parts[0:2] == ['ppapi', 'generators']:
32 generator_files.append(filename)
33 if generator_files != []:
34 cmd = [ sys.executable, 'idl_tests.py']
35 ppapi_dir = input_api.PresubmitLocalPath()
36 results.extend(RunCmdAndCheck(cmd,
37 'PPAPI IDL unittests failed.',
38 output_api,
39 os.path.join(ppapi_dir, 'generators')))
40 return results
43 # Verify that the files do not contain a 'TODO' in them.
44 RE_TODO = re.compile(r'\WTODO\W', flags=re.I)
45 def CheckTODO(input_api, output_api):
46 files = input_api.LocalPaths()
47 todo = []
49 for filename in files:
50 name, ext = os.path.splitext(filename)
51 name_parts = name.split(os.sep)
53 # Only check normal build sources.
54 if ext not in ['.h', '.idl']:
55 continue
57 # Only examine the ppapi directory.
58 if name_parts[0] != 'ppapi':
59 continue
61 # Only examine public plugin facing directories.
62 if name_parts[1] not in ['api', 'c', 'cpp', 'utility']:
63 continue
65 # Only examine public stable interfaces.
66 if name_parts[2] in ['dev', 'private', 'trusted']:
67 continue
68 if name_parts[2] == 'extensions' and name_parts[3] == 'dev':
69 continue
71 filepath = os.path.join('..', filename)
72 if RE_TODO.search(open(filepath, 'rb').read()):
73 todo.append(filename)
75 if todo:
76 return [output_api.PresubmitError(
77 'TODOs found in stable public PPAPI files:',
78 long_text='\n'.join(todo))]
79 return []
81 # Verify that no CPP wrappers use un-versioned PPB interface name macros.
82 RE_UNVERSIONED_PPB = re.compile(r'\bPPB_\w+_INTERFACE\b')
83 def CheckUnversionedPPB(input_api, output_api):
84 files = input_api.LocalPaths()
85 todo = []
87 for filename in files:
88 name, ext = os.path.splitext(filename)
89 name_parts = name.split(os.sep)
91 # Only check C++ sources.
92 if ext not in ['.cc']:
93 continue
95 # Only examine the public plugin facing ppapi/cpp directory.
96 if name_parts[0:2] != ['ppapi', 'cpp']:
97 continue
99 # Only examine public stable and trusted interfaces.
100 if name_parts[2] in ['dev', 'private']:
101 continue
103 filepath = os.path.join('..', filename)
104 if RE_UNVERSIONED_PPB.search(open(filepath, 'rb').read()):
105 todo.append(filename)
107 if todo:
108 return [output_api.PresubmitError(
109 'Unversioned PPB interface references found in PPAPI C++ wrappers:',
110 long_text='\n'.join(todo))]
111 return []
113 # Verify that changes to ppapi headers/sources are also made to NaCl SDK.
114 def CheckUpdatedNaClSDK(input_api, output_api):
115 files = input_api.LocalPaths()
117 # PPAPI files the Native Client SDK cares about.
118 nacl_sdk_files = []
120 for filename in files:
121 name, ext = os.path.splitext(filename)
122 name_parts = name.split(os.sep)
124 if len(name_parts) <= 2:
125 continue
127 if name_parts[0] != 'ppapi':
128 continue
130 if ((name_parts[1] == 'c' and ext == '.h') or
131 (name_parts[1] in ('cpp', 'utility') and ext in ('.h', '.cc'))):
132 if name_parts[2] in ('documentation', 'trusted'):
133 continue
134 nacl_sdk_files.append(filename)
136 if not nacl_sdk_files:
137 return []
139 verify_ppapi_py = os.path.join(input_api.change.RepositoryRoot(),
140 'native_client_sdk', 'src', 'build_tools',
141 'verify_ppapi.py')
142 cmd = [sys.executable, verify_ppapi_py] + nacl_sdk_files
143 return RunCmdAndCheck(cmd,
144 'PPAPI Interface modified without updating NaCl SDK.',
145 output_api)
147 def CheckChange(input_api, output_api):
148 results = []
150 results.extend(RunUnittests(input_api, output_api))
152 results.extend(CheckTODO(input_api, output_api))
154 results.extend(CheckUnversionedPPB(input_api, output_api))
156 results.extend(CheckUpdatedNaClSDK(input_api, output_api))
158 # Verify all modified *.idl have a matching *.h
159 files = input_api.LocalPaths()
160 h_files = []
161 idl_files = []
162 generators_changed = False
164 # Find all relevant .h and .idl files.
165 for filename in files:
166 name, ext = os.path.splitext(filename)
167 name_parts = name.split(os.sep)
168 if name_parts[0:2] == ['ppapi', 'c'] and ext == '.h':
169 h_files.append('/'.join(name_parts[2:]))
170 elif name_parts[0:2] == ['ppapi', 'api'] and ext == '.idl':
171 idl_files.append('/'.join(name_parts[2:]))
172 elif name_parts[0:2] == ['ppapi', 'generators']:
173 generators_changed = True
175 # Generate a list of all appropriate *.h and *.idl changes in this CL.
176 both = h_files + idl_files
178 # If there aren't any, we are done checking.
179 if not both: return results
181 missing = []
182 for filename in idl_files:
183 if filename not in set(h_files):
184 missing.append('ppapi/api/%s.idl' % filename)
186 # An IDL change that includes [generate_thunk] doesn't need to have
187 # an update to the corresponding .h file.
188 new_thunk_files = []
189 for filename in missing:
190 lines = input_api.RightHandSideLines(lambda f: f.LocalPath() == filename)
191 for line in lines:
192 if line[2].strip() == '[generate_thunk]':
193 new_thunk_files.append(filename)
194 for filename in new_thunk_files:
195 missing.remove(filename)
197 if missing:
198 results.append(
199 output_api.PresubmitPromptWarning(
200 'Missing PPAPI header, no change or skipped generation?',
201 long_text='\n '.join(missing)))
203 missing_dev = []
204 missing_stable = []
205 missing_priv = []
206 for filename in h_files:
207 if filename not in set(idl_files):
208 name_parts = filename.split(os.sep)
210 if name_parts[-1] == 'pp_macros':
211 # The C header generator adds a PPAPI_RELEASE macro based on all the
212 # IDL files, so pp_macros.h may change while its IDL does not.
213 lines = input_api.RightHandSideLines(
214 lambda f: f.LocalPath() == 'ppapi/c/%s.h' % filename)
215 releaseChanged = False
216 for line in lines:
217 if line[2].split()[:2] == ['#define', 'PPAPI_RELEASE']:
218 results.append(
219 output_api.PresubmitPromptOrNotify(
220 'PPAPI_RELEASE has changed', long_text=line[2]))
221 releaseChanged = True
222 break
223 if releaseChanged:
224 continue
226 if 'trusted' in name_parts:
227 missing_priv.append(' ppapi/c/%s.h' % filename)
228 continue
230 if 'private' in name_parts:
231 missing_priv.append(' ppapi/c/%s.h' % filename)
232 continue
234 if 'dev' in name_parts:
235 missing_dev.append(' ppapi/c/%s.h' % filename)
236 continue
238 missing_stable.append(' ppapi/c/%s.h' % filename)
240 if missing_priv:
241 results.append(
242 output_api.PresubmitPromptWarning(
243 'Missing PPAPI IDL for private interface, please generate IDL:',
244 long_text='\n'.join(missing_priv)))
246 if missing_dev:
247 results.append(
248 output_api.PresubmitPromptWarning(
249 'Missing PPAPI IDL for DEV, required before moving to stable:',
250 long_text='\n'.join(missing_dev)))
252 if missing_stable:
253 # It might be okay that the header changed without a corresponding IDL
254 # change. E.g., comment indenting may have been changed. Treat this as a
255 # warning.
256 if generators_changed:
257 results.append(
258 output_api.PresubmitPromptWarning(
259 'Missing PPAPI IDL for stable interface (due to change in ' +
260 'generators?):',
261 long_text='\n'.join(missing_stable)))
262 else:
263 results.append(
264 output_api.PresubmitError(
265 'Missing PPAPI IDL for stable interface:',
266 long_text='\n'.join(missing_stable)))
268 # Verify all *.h files match *.idl definitions, use:
269 # --test to prevent output to disk
270 # --diff to generate a unified diff
271 # --out to pick which files to examine (only the ones in the CL)
272 ppapi_dir = input_api.PresubmitLocalPath()
273 cmd = [sys.executable, 'generator.py',
274 '--wnone', '--diff', '--test','--cgen', '--range=start,end']
276 # Only generate output for IDL files references (as *.h or *.idl) in this CL
277 cmd.append('--out=' + ','.join([name + '.idl' for name in both]))
278 cmd_results = RunCmdAndCheck(cmd,
279 'PPAPI IDL Diff detected: Run the generator.',
280 output_api,
281 os.path.join(ppapi_dir, 'generators'))
282 if cmd_results:
283 results.extend(cmd_results)
285 return results
288 def CheckChangeOnUpload(input_api, output_api):
289 return CheckChange(input_api, output_api)
292 def CheckChangeOnCommit(input_api, output_api):
293 return CheckChange(input_api, output_api)