Refactor TranslateManager::Observer to not assume that TranslateManager is a singleton
[chromium-blink-merge.git] / ppapi / PRESUBMIT.py
blob4a71f49574c105c16638ff3ce34a6f681119555c
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 live_files = input_api.AffectedFiles(include_deletes=False)
47 files = [f.LocalPath() for f in live_files]
48 todo = []
50 for filename in files:
51 name, ext = os.path.splitext(filename)
52 name_parts = name.split(os.sep)
54 # Only check normal build sources.
55 if ext not in ['.h', '.idl']:
56 continue
58 # Only examine the ppapi directory.
59 if name_parts[0] != 'ppapi':
60 continue
62 # Only examine public plugin facing directories.
63 if name_parts[1] not in ['api', 'c', 'cpp', 'utility']:
64 continue
66 # Only examine public stable interfaces.
67 if name_parts[2] in ['dev', 'private', 'trusted']:
68 continue
69 if name_parts[2] == 'extensions' and name_parts[3] == 'dev':
70 continue
72 filepath = os.path.join('..', filename)
73 if RE_TODO.search(open(filepath, 'rb').read()):
74 todo.append(filename)
76 if todo:
77 return [output_api.PresubmitError(
78 'TODOs found in stable public PPAPI files:',
79 long_text='\n'.join(todo))]
80 return []
82 # Verify that no CPP wrappers use un-versioned PPB interface name macros.
83 RE_UNVERSIONED_PPB = re.compile(r'\bPPB_\w+_INTERFACE\b')
84 def CheckUnversionedPPB(input_api, output_api):
85 live_files = input_api.AffectedFiles(include_deletes=False)
86 files = [f.LocalPath() for f in live_files]
87 todo = []
89 for filename in files:
90 name, ext = os.path.splitext(filename)
91 name_parts = name.split(os.sep)
93 # Only check C++ sources.
94 if ext not in ['.cc']:
95 continue
97 # Only examine the public plugin facing ppapi/cpp directory.
98 if name_parts[0:2] != ['ppapi', 'cpp']:
99 continue
101 # Only examine public stable and trusted interfaces.
102 if name_parts[2] in ['dev', 'private']:
103 continue
105 filepath = os.path.join('..', filename)
106 if RE_UNVERSIONED_PPB.search(open(filepath, 'rb').read()):
107 todo.append(filename)
109 if todo:
110 return [output_api.PresubmitError(
111 'Unversioned PPB interface references found in PPAPI C++ wrappers:',
112 long_text='\n'.join(todo))]
113 return []
115 # Verify that changes to ppapi headers/sources are also made to NaCl SDK.
116 def CheckUpdatedNaClSDK(input_api, output_api):
117 files = input_api.LocalPaths()
119 # PPAPI files the Native Client SDK cares about.
120 nacl_sdk_files = []
122 for filename in files:
123 name, ext = os.path.splitext(filename)
124 name_parts = name.split(os.sep)
126 if len(name_parts) <= 2:
127 continue
129 if name_parts[0] != 'ppapi':
130 continue
132 if ((name_parts[1] == 'c' and ext == '.h') or
133 (name_parts[1] in ('cpp', 'utility') and ext in ('.h', '.cc'))):
134 if name_parts[2] in ('documentation', 'trusted'):
135 continue
136 nacl_sdk_files.append(filename)
138 if not nacl_sdk_files:
139 return []
141 verify_ppapi_py = os.path.join(input_api.change.RepositoryRoot(),
142 'native_client_sdk', 'src', 'build_tools',
143 'verify_ppapi.py')
144 cmd = [sys.executable, verify_ppapi_py] + nacl_sdk_files
145 return RunCmdAndCheck(cmd,
146 'PPAPI Interface modified without updating NaCl SDK.',
147 output_api)
149 # Verify that changes to ppapi/thunk/interfaces_* files have a corresponding
150 # change to tools/metrics/histograms/histograms.xml for UMA tracking.
151 def CheckHistogramXml(input_api, output_api):
152 # We can't use input_api.LocalPaths() here because we need to know about
153 # changes outside of ppapi/. See tools/depot_tools/presubmit_support.py for
154 # details on input_api.
155 files = input_api.change.AffectedFiles()
157 INTERFACE_FILES = ('ppapi/thunk/interfaces_legacy.h',
158 'ppapi/thunk/interfaces_ppb_private_flash.h',
159 'ppapi/thunk/interfaces_ppb_private.h',
160 'ppapi/thunk/interfaces_ppb_private_no_permissions.h',
161 'ppapi/thunk/interfaces_ppb_public_dev_channel.h',
162 'ppapi/thunk/interfaces_ppb_public_dev.h',
163 'ppapi/thunk/interfaces_ppb_public_stable.h')
164 HISTOGRAM_XML_FILE = 'tools/metrics/histograms/histograms.xml'
165 interface_changes = []
166 has_histogram_xml_change = False
167 for filename in files:
168 path = filename.LocalPath()
169 if path in INTERFACE_FILES:
170 interface_changes.append(path)
171 if path == HISTOGRAM_XML_FILE:
172 has_histogram_xml_change = True
174 if interface_changes and not has_histogram_xml_change:
175 return [output_api.PresubmitPromptWarning(
176 'Missing change to tools/metrics/histograms/histograms.xml.\n' +
177 'Run pepper_hash_for_uma to make get values for new interfaces.\n' +
178 'Interface changes:\n' + '\n'.join(interface_changes))]
179 return []
181 def CheckChange(input_api, output_api):
182 results = []
184 results.extend(RunUnittests(input_api, output_api))
186 results.extend(CheckTODO(input_api, output_api))
188 results.extend(CheckUnversionedPPB(input_api, output_api))
190 results.extend(CheckUpdatedNaClSDK(input_api, output_api))
192 results.extend(CheckHistogramXml(input_api, output_api))
194 # Verify all modified *.idl have a matching *.h
195 files = input_api.LocalPaths()
196 h_files = []
197 idl_files = []
198 generators_changed = False
200 # These are autogenerated by the command buffer generator, they don't go
201 # through idl.
202 whitelist = ['ppb_opengles2', 'ppb_opengles2ext_dev']
204 # The PDF interface is hand-written.
205 whitelist += ['ppb_pdf', 'ppp_pdf']
207 # Find all relevant .h and .idl files.
208 for filename in files:
209 name, ext = os.path.splitext(filename)
210 name_parts = name.split(os.sep)
211 if name_parts[-1] in whitelist:
212 continue
213 if name_parts[0:2] == ['ppapi', 'c'] and ext == '.h':
214 h_files.append('/'.join(name_parts[2:]))
215 elif name_parts[0:2] == ['ppapi', 'api'] and ext == '.idl':
216 idl_files.append('/'.join(name_parts[2:]))
217 elif name_parts[0:2] == ['ppapi', 'generators']:
218 generators_changed = True
220 # Generate a list of all appropriate *.h and *.idl changes in this CL.
221 both = h_files + idl_files
223 # If there aren't any, we are done checking.
224 if not both: return results
226 missing = []
227 for filename in idl_files:
228 if filename not in set(h_files):
229 missing.append('ppapi/api/%s.idl' % filename)
231 # An IDL change that includes [generate_thunk] doesn't need to have
232 # an update to the corresponding .h file.
233 new_thunk_files = []
234 for filename in missing:
235 lines = input_api.RightHandSideLines(lambda f: f.LocalPath() == filename)
236 for line in lines:
237 if line[2].strip() == '[generate_thunk]':
238 new_thunk_files.append(filename)
239 for filename in new_thunk_files:
240 missing.remove(filename)
242 if missing:
243 results.append(
244 output_api.PresubmitPromptWarning(
245 'Missing PPAPI header, no change or skipped generation?',
246 long_text='\n '.join(missing)))
248 missing_dev = []
249 missing_stable = []
250 missing_priv = []
251 for filename in h_files:
252 if filename not in set(idl_files):
253 name_parts = filename.split(os.sep)
255 if name_parts[-1] == 'pp_macros':
256 # The C header generator adds a PPAPI_RELEASE macro based on all the
257 # IDL files, so pp_macros.h may change while its IDL does not.
258 lines = input_api.RightHandSideLines(
259 lambda f: f.LocalPath() == 'ppapi/c/%s.h' % filename)
260 releaseChanged = False
261 for line in lines:
262 if line[2].split()[:2] == ['#define', 'PPAPI_RELEASE']:
263 results.append(
264 output_api.PresubmitPromptOrNotify(
265 'PPAPI_RELEASE has changed', long_text=line[2]))
266 releaseChanged = True
267 break
268 if releaseChanged:
269 continue
271 if 'trusted' in name_parts:
272 missing_priv.append(' ppapi/c/%s.h' % filename)
273 continue
275 if 'private' in name_parts:
276 missing_priv.append(' ppapi/c/%s.h' % filename)
277 continue
279 if 'dev' in name_parts:
280 missing_dev.append(' ppapi/c/%s.h' % filename)
281 continue
283 missing_stable.append(' ppapi/c/%s.h' % filename)
285 if missing_priv:
286 results.append(
287 output_api.PresubmitPromptWarning(
288 'Missing PPAPI IDL for private interface, please generate IDL:',
289 long_text='\n'.join(missing_priv)))
291 if missing_dev:
292 results.append(
293 output_api.PresubmitPromptWarning(
294 'Missing PPAPI IDL for DEV, required before moving to stable:',
295 long_text='\n'.join(missing_dev)))
297 if missing_stable:
298 # It might be okay that the header changed without a corresponding IDL
299 # change. E.g., comment indenting may have been changed. Treat this as a
300 # warning.
301 if generators_changed:
302 results.append(
303 output_api.PresubmitPromptWarning(
304 'Missing PPAPI IDL for stable interface (due to change in ' +
305 'generators?):',
306 long_text='\n'.join(missing_stable)))
307 else:
308 results.append(
309 output_api.PresubmitError(
310 'Missing PPAPI IDL for stable interface:',
311 long_text='\n'.join(missing_stable)))
313 # Verify all *.h files match *.idl definitions, use:
314 # --test to prevent output to disk
315 # --diff to generate a unified diff
316 # --out to pick which files to examine (only the ones in the CL)
317 ppapi_dir = input_api.PresubmitLocalPath()
318 cmd = [sys.executable, 'generator.py',
319 '--wnone', '--diff', '--test','--cgen', '--range=start,end']
321 # Only generate output for IDL files references (as *.h or *.idl) in this CL
322 cmd.append('--out=' + ','.join([name + '.idl' for name in both]))
323 cmd_results = RunCmdAndCheck(cmd,
324 'PPAPI IDL Diff detected: Run the generator.',
325 output_api,
326 os.path.join(ppapi_dir, 'generators'))
327 if cmd_results:
328 results.extend(cmd_results)
330 return results
333 def CheckChangeOnUpload(input_api, output_api):
334 return CheckChange(input_api, output_api)
337 def CheckChangeOnCommit(input_api, output_api):
338 return CheckChange(input_api, output_api)