Roll src/third_party/WebKit 75a2fa9:2546356 (svn 202272:202273)
[chromium-blink-merge.git] / ppapi / PRESUBMIT.py
blob78457c43c5e95bc29d415066ead9a98f09fc79fd
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, warning=False):
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 if warning:
19 results.append(output_api.PresubmitPromptWarning(
20 '%s\n\n%s' % (err_string, p_stderr)))
21 else:
22 results.append(
23 output_api.PresubmitError(err_string,
24 long_text=p_stderr))
25 return results
28 def RunUnittests(input_api, output_api):
29 # Run some Generator unittests if the generator source was changed.
30 results = []
31 files = input_api.LocalPaths()
32 generator_files = []
33 for filename in files:
34 name_parts = filename.split(os.sep)
35 if name_parts[0:2] == ['ppapi', 'generators']:
36 generator_files.append(filename)
37 if generator_files != []:
38 cmd = [ sys.executable, 'idl_tests.py']
39 ppapi_dir = input_api.PresubmitLocalPath()
40 results.extend(RunCmdAndCheck(cmd,
41 'PPAPI IDL unittests failed.',
42 output_api,
43 os.path.join(ppapi_dir, 'generators')))
44 return results
47 # Verify that the files do not contain a 'TODO' in them.
48 RE_TODO = re.compile(r'\WTODO\W', flags=re.I)
49 def CheckTODO(input_api, output_api):
50 live_files = input_api.AffectedFiles(include_deletes=False)
51 files = [f.LocalPath() for f in live_files]
52 todo = []
54 for filename in files:
55 name, ext = os.path.splitext(filename)
56 name_parts = name.split(os.sep)
58 # Only check normal build sources.
59 if ext not in ['.h', '.idl']:
60 continue
62 # Only examine the ppapi directory.
63 if name_parts[0] != 'ppapi':
64 continue
66 # Only examine public plugin facing directories.
67 if name_parts[1] not in ['api', 'c', 'cpp', 'utility']:
68 continue
70 # Only examine public stable interfaces.
71 if name_parts[2] in ['dev', 'private', 'trusted']:
72 continue
74 filepath = os.path.join('..', filename)
75 if RE_TODO.search(open(filepath, 'rb').read()):
76 todo.append(filename)
78 if todo:
79 return [output_api.PresubmitError(
80 'TODOs found in stable public PPAPI files:',
81 long_text='\n'.join(todo))]
82 return []
84 # Verify that no CPP wrappers use un-versioned PPB interface name macros.
85 RE_UNVERSIONED_PPB = re.compile(r'\bPPB_\w+_INTERFACE\b')
86 def CheckUnversionedPPB(input_api, output_api):
87 live_files = input_api.AffectedFiles(include_deletes=False)
88 files = [f.LocalPath() for f in live_files]
89 todo = []
91 for filename in files:
92 name, ext = os.path.splitext(filename)
93 name_parts = name.split(os.sep)
95 # Only check C++ sources.
96 if ext not in ['.cc']:
97 continue
99 # Only examine the public plugin facing ppapi/cpp directory.
100 if name_parts[0:2] != ['ppapi', 'cpp']:
101 continue
103 # Only examine public stable and trusted interfaces.
104 if name_parts[2] in ['dev', 'private']:
105 continue
107 filepath = os.path.join('..', filename)
108 if RE_UNVERSIONED_PPB.search(open(filepath, 'rb').read()):
109 todo.append(filename)
111 if todo:
112 return [output_api.PresubmitError(
113 'Unversioned PPB interface references found in PPAPI C++ wrappers:',
114 long_text='\n'.join(todo))]
115 return []
117 # Verify that changes to ppapi headers/sources are also made to NaCl SDK.
118 def CheckUpdatedNaClSDK(input_api, output_api):
119 files = input_api.LocalPaths()
121 # PPAPI files the Native Client SDK cares about.
122 nacl_sdk_files = []
124 for filename in files:
125 name, ext = os.path.splitext(filename)
126 name_parts = name.split(os.sep)
128 if len(name_parts) <= 2:
129 continue
131 if name_parts[0] != 'ppapi':
132 continue
134 if ((name_parts[1] == 'c' and ext == '.h') or
135 (name_parts[1] in ('cpp', 'utility') and ext in ('.h', '.cc'))):
136 if name_parts[2] in ('documentation', 'trusted'):
137 continue
138 nacl_sdk_files.append(filename)
140 if not nacl_sdk_files:
141 return []
143 verify_ppapi_py = os.path.join(input_api.change.RepositoryRoot(),
144 'native_client_sdk', 'src', 'build_tools',
145 'verify_ppapi.py')
146 cmd = [sys.executable, verify_ppapi_py] + nacl_sdk_files
147 return RunCmdAndCheck(cmd,
148 'PPAPI Interface modified without updating NaCl SDK.\n'
149 '(note that some dev interfaces should not be added '
150 'the NaCl SDK; when in doubt, ask a ppapi OWNER.\n'
151 'To ignore a file, add it to IGNORED_FILES in '
152 'native_client_sdk/src/build_tools/verify_ppapi.py)',
153 output_api,
154 warning=True)
156 # Verify that changes to ppapi/thunk/interfaces_* files have a corresponding
157 # change to tools/metrics/histograms/histograms.xml for UMA tracking.
158 def CheckHistogramXml(input_api, output_api):
159 # We can't use input_api.LocalPaths() here because we need to know about
160 # changes outside of ppapi/. See tools/depot_tools/presubmit_support.py for
161 # details on input_api.
162 files = input_api.change.AffectedFiles()
164 INTERFACE_FILES = ('ppapi/thunk/interfaces_legacy.h',
165 'ppapi/thunk/interfaces_ppb_private_flash.h',
166 'ppapi/thunk/interfaces_ppb_private.h',
167 'ppapi/thunk/interfaces_ppb_private_no_permissions.h',
168 'ppapi/thunk/interfaces_ppb_public_dev_channel.h',
169 'ppapi/thunk/interfaces_ppb_public_dev.h',
170 'ppapi/thunk/interfaces_ppb_public_stable.h')
171 HISTOGRAM_XML_FILE = 'tools/metrics/histograms/histograms.xml'
172 interface_changes = []
173 has_histogram_xml_change = False
174 for filename in files:
175 path = filename.LocalPath()
176 if path in INTERFACE_FILES:
177 interface_changes.append(path)
178 if path == HISTOGRAM_XML_FILE:
179 has_histogram_xml_change = True
181 if interface_changes and not has_histogram_xml_change:
182 return [output_api.PresubmitNotifyResult(
183 'Missing change to tools/metrics/histograms/histograms.xml.\n' +
184 'Run pepper_hash_for_uma to make get values for new interfaces.\n' +
185 'Interface changes:\n' + '\n'.join(interface_changes))]
186 return []
188 def CheckChange(input_api, output_api):
189 results = []
191 results.extend(RunUnittests(input_api, output_api))
193 results.extend(CheckTODO(input_api, output_api))
195 results.extend(CheckUnversionedPPB(input_api, output_api))
197 results.extend(CheckUpdatedNaClSDK(input_api, output_api))
199 results.extend(CheckHistogramXml(input_api, output_api))
201 # Verify all modified *.idl have a matching *.h
202 files = input_api.LocalPaths()
203 h_files = []
204 idl_files = []
205 generators_changed = False
207 # These are autogenerated by the command buffer generator, they don't go
208 # through idl.
209 whitelist = ['ppb_opengles2', 'ppb_opengles2ext_dev']
211 # The PDF interface is hand-written.
212 whitelist += ['ppb_pdf', 'ppp_pdf']
214 # Find all relevant .h and .idl files.
215 for filename in files:
216 name, ext = os.path.splitext(filename)
217 name_parts = name.split(os.sep)
218 if name_parts[-1] in whitelist:
219 continue
220 if name_parts[0:2] == ['ppapi', 'c'] and ext == '.h':
221 h_files.append('/'.join(name_parts[2:]))
222 elif name_parts[0:2] == ['ppapi', 'api'] and ext == '.idl':
223 idl_files.append('/'.join(name_parts[2:]))
224 elif name_parts[0:2] == ['ppapi', 'generators']:
225 generators_changed = True
227 # Generate a list of all appropriate *.h and *.idl changes in this CL.
228 both = h_files + idl_files
230 # If there aren't any, we are done checking.
231 if not both: return results
233 missing = []
234 for filename in idl_files:
235 if filename not in set(h_files):
236 missing.append('ppapi/api/%s.idl' % filename)
238 # An IDL change that includes [generate_thunk] doesn't need to have
239 # an update to the corresponding .h file.
240 new_thunk_files = []
241 for filename in missing:
242 lines = input_api.RightHandSideLines(lambda f: f.LocalPath() == filename)
243 for line in lines:
244 if line[2].strip() == '[generate_thunk]':
245 new_thunk_files.append(filename)
246 for filename in new_thunk_files:
247 missing.remove(filename)
249 if missing:
250 results.append(
251 output_api.PresubmitPromptWarning(
252 'Missing PPAPI header, no change or skipped generation?',
253 long_text='\n '.join(missing)))
255 missing_dev = []
256 missing_stable = []
257 missing_priv = []
258 for filename in h_files:
259 if filename not in set(idl_files):
260 name_parts = filename.split(os.sep)
262 if name_parts[-1] == 'pp_macros':
263 # The C header generator adds a PPAPI_RELEASE macro based on all the
264 # IDL files, so pp_macros.h may change while its IDL does not.
265 lines = input_api.RightHandSideLines(
266 lambda f: f.LocalPath() == 'ppapi/c/%s.h' % filename)
267 releaseChanged = False
268 for line in lines:
269 if line[2].split()[:2] == ['#define', 'PPAPI_RELEASE']:
270 results.append(
271 output_api.PresubmitPromptOrNotify(
272 'PPAPI_RELEASE has changed', long_text=line[2]))
273 releaseChanged = True
274 break
275 if releaseChanged:
276 continue
278 if 'trusted' in name_parts:
279 missing_priv.append(' ppapi/c/%s.h' % filename)
280 continue
282 if 'private' in name_parts:
283 missing_priv.append(' ppapi/c/%s.h' % filename)
284 continue
286 if 'dev' in name_parts:
287 missing_dev.append(' ppapi/c/%s.h' % filename)
288 continue
290 missing_stable.append(' ppapi/c/%s.h' % filename)
292 if missing_priv:
293 results.append(
294 output_api.PresubmitPromptWarning(
295 'Missing PPAPI IDL for private interface, please generate IDL:',
296 long_text='\n'.join(missing_priv)))
298 if missing_dev:
299 results.append(
300 output_api.PresubmitPromptWarning(
301 'Missing PPAPI IDL for DEV, required before moving to stable:',
302 long_text='\n'.join(missing_dev)))
304 if missing_stable:
305 # It might be okay that the header changed without a corresponding IDL
306 # change. E.g., comment indenting may have been changed. Treat this as a
307 # warning.
308 if generators_changed:
309 results.append(
310 output_api.PresubmitPromptWarning(
311 'Missing PPAPI IDL for stable interface (due to change in ' +
312 'generators?):',
313 long_text='\n'.join(missing_stable)))
314 else:
315 results.append(
316 output_api.PresubmitError(
317 'Missing PPAPI IDL for stable interface:',
318 long_text='\n'.join(missing_stable)))
320 # Verify all *.h files match *.idl definitions, use:
321 # --test to prevent output to disk
322 # --diff to generate a unified diff
323 # --out to pick which files to examine (only the ones in the CL)
324 ppapi_dir = input_api.PresubmitLocalPath()
325 cmd = [sys.executable, 'generator.py',
326 '--wnone', '--diff', '--test','--cgen', '--range=start,end']
328 # Only generate output for IDL files references (as *.h or *.idl) in this CL
329 cmd.append('--out=' + ','.join([name + '.idl' for name in both]))
330 cmd_results = RunCmdAndCheck(cmd,
331 'PPAPI IDL Diff detected: Run the generator.',
332 output_api,
333 os.path.join(ppapi_dir, 'generators'))
334 if cmd_results:
335 results.extend(cmd_results)
337 return results
340 def CheckChangeOnUpload(input_api, output_api):
341 return CheckChange(input_api, output_api)
344 def CheckChangeOnCommit(input_api, output_api):
345 return CheckChange(input_api, output_api)