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.
11 def RunCmdAndCheck(cmd
, err_string
, output_api
, cwd
=None, warning
=False):
13 p
= subprocess
.Popen(cmd
, cwd
=cwd
,
14 stdout
=subprocess
.PIPE
,
15 stderr
=subprocess
.PIPE
)
16 (p_stdout
, p_stderr
) = p
.communicate()
19 results
.append(output_api
.PresubmitPromptWarning(
20 '%s\n\n%s' % (err_string
, p_stderr
)))
23 output_api
.PresubmitError(err_string
,
28 def RunUnittests(input_api
, output_api
):
29 # Run some Generator unittests if the generator source was changed.
31 files
= input_api
.LocalPaths()
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.',
43 os
.path
.join(ppapi_dir
, 'generators')))
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
]
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']:
62 # Only examine the ppapi directory.
63 if name_parts
[0] != 'ppapi':
66 # Only examine public plugin facing directories.
67 if name_parts
[1] not in ['api', 'c', 'cpp', 'utility']:
70 # Only examine public stable interfaces.
71 if name_parts
[2] in ['dev', 'private', 'trusted']:
74 filepath
= os
.path
.join('..', filename
)
75 if RE_TODO
.search(open(filepath
, 'rb').read()):
79 return [output_api
.PresubmitError(
80 'TODOs found in stable public PPAPI files:',
81 long_text
='\n'.join(todo
))]
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
]
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']:
99 # Only examine the public plugin facing ppapi/cpp directory.
100 if name_parts
[0:2] != ['ppapi', 'cpp']:
103 # Only examine public stable and trusted interfaces.
104 if name_parts
[2] in ['dev', 'private']:
107 filepath
= os
.path
.join('..', filename
)
108 if RE_UNVERSIONED_PPB
.search(open(filepath
, 'rb').read()):
109 todo
.append(filename
)
112 return [output_api
.PresubmitError(
113 'Unversioned PPB interface references found in PPAPI C++ wrappers:',
114 long_text
='\n'.join(todo
))]
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.
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:
131 if name_parts
[0] != 'ppapi':
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'):
138 nacl_sdk_files
.append(filename
)
140 if not nacl_sdk_files
:
143 verify_ppapi_py
= os
.path
.join(input_api
.change
.RepositoryRoot(),
144 'native_client_sdk', 'src', 'build_tools',
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.)',
154 # Verify that changes to ppapi/thunk/interfaces_* files have a corresponding
155 # change to tools/metrics/histograms/histograms.xml for UMA tracking.
156 def CheckHistogramXml(input_api
, output_api
):
157 # We can't use input_api.LocalPaths() here because we need to know about
158 # changes outside of ppapi/. See tools/depot_tools/presubmit_support.py for
159 # details on input_api.
160 files
= input_api
.change
.AffectedFiles()
162 INTERFACE_FILES
= ('ppapi/thunk/interfaces_legacy.h',
163 'ppapi/thunk/interfaces_ppb_private_flash.h',
164 'ppapi/thunk/interfaces_ppb_private.h',
165 'ppapi/thunk/interfaces_ppb_private_no_permissions.h',
166 'ppapi/thunk/interfaces_ppb_public_dev_channel.h',
167 'ppapi/thunk/interfaces_ppb_public_dev.h',
168 'ppapi/thunk/interfaces_ppb_public_stable.h')
169 HISTOGRAM_XML_FILE
= 'tools/metrics/histograms/histograms.xml'
170 interface_changes
= []
171 has_histogram_xml_change
= False
172 for filename
in files
:
173 path
= filename
.LocalPath()
174 if path
in INTERFACE_FILES
:
175 interface_changes
.append(path
)
176 if path
== HISTOGRAM_XML_FILE
:
177 has_histogram_xml_change
= True
179 if interface_changes
and not has_histogram_xml_change
:
180 return [output_api
.PresubmitNotifyResult(
181 'Missing change to tools/metrics/histograms/histograms.xml.\n' +
182 'Run pepper_hash_for_uma to make get values for new interfaces.\n' +
183 'Interface changes:\n' + '\n'.join(interface_changes
))]
186 def CheckChange(input_api
, output_api
):
189 results
.extend(RunUnittests(input_api
, output_api
))
191 results
.extend(CheckTODO(input_api
, output_api
))
193 results
.extend(CheckUnversionedPPB(input_api
, output_api
))
195 results
.extend(CheckUpdatedNaClSDK(input_api
, output_api
))
197 results
.extend(CheckHistogramXml(input_api
, output_api
))
199 # Verify all modified *.idl have a matching *.h
200 files
= input_api
.LocalPaths()
203 generators_changed
= False
205 # These are autogenerated by the command buffer generator, they don't go
207 whitelist
= ['ppb_opengles2', 'ppb_opengles2ext_dev']
209 # The PDF interface is hand-written.
210 whitelist
+= ['ppb_pdf', 'ppp_pdf']
212 # Find all relevant .h and .idl files.
213 for filename
in files
:
214 name
, ext
= os
.path
.splitext(filename
)
215 name_parts
= name
.split(os
.sep
)
216 if name_parts
[-1] in whitelist
:
218 if name_parts
[0:2] == ['ppapi', 'c'] and ext
== '.h':
219 h_files
.append('/'.join(name_parts
[2:]))
220 elif name_parts
[0:2] == ['ppapi', 'api'] and ext
== '.idl':
221 idl_files
.append('/'.join(name_parts
[2:]))
222 elif name_parts
[0:2] == ['ppapi', 'generators']:
223 generators_changed
= True
225 # Generate a list of all appropriate *.h and *.idl changes in this CL.
226 both
= h_files
+ idl_files
228 # If there aren't any, we are done checking.
229 if not both
: return results
232 for filename
in idl_files
:
233 if filename
not in set(h_files
):
234 missing
.append('ppapi/api/%s.idl' % filename
)
236 # An IDL change that includes [generate_thunk] doesn't need to have
237 # an update to the corresponding .h file.
239 for filename
in missing
:
240 lines
= input_api
.RightHandSideLines(lambda f
: f
.LocalPath() == filename
)
242 if line
[2].strip() == '[generate_thunk]':
243 new_thunk_files
.append(filename
)
244 for filename
in new_thunk_files
:
245 missing
.remove(filename
)
249 output_api
.PresubmitPromptWarning(
250 'Missing PPAPI header, no change or skipped generation?',
251 long_text
='\n '.join(missing
)))
256 for filename
in h_files
:
257 if filename
not in set(idl_files
):
258 name_parts
= filename
.split(os
.sep
)
260 if name_parts
[-1] == 'pp_macros':
261 # The C header generator adds a PPAPI_RELEASE macro based on all the
262 # IDL files, so pp_macros.h may change while its IDL does not.
263 lines
= input_api
.RightHandSideLines(
264 lambda f
: f
.LocalPath() == 'ppapi/c/%s.h' % filename
)
265 releaseChanged
= False
267 if line
[2].split()[:2] == ['#define', 'PPAPI_RELEASE']:
269 output_api
.PresubmitPromptOrNotify(
270 'PPAPI_RELEASE has changed', long_text
=line
[2]))
271 releaseChanged
= True
276 if 'trusted' in name_parts
:
277 missing_priv
.append(' ppapi/c/%s.h' % filename
)
280 if 'private' in name_parts
:
281 missing_priv
.append(' ppapi/c/%s.h' % filename
)
284 if 'dev' in name_parts
:
285 missing_dev
.append(' ppapi/c/%s.h' % filename
)
288 missing_stable
.append(' ppapi/c/%s.h' % filename
)
292 output_api
.PresubmitPromptWarning(
293 'Missing PPAPI IDL for private interface, please generate IDL:',
294 long_text
='\n'.join(missing_priv
)))
298 output_api
.PresubmitPromptWarning(
299 'Missing PPAPI IDL for DEV, required before moving to stable:',
300 long_text
='\n'.join(missing_dev
)))
303 # It might be okay that the header changed without a corresponding IDL
304 # change. E.g., comment indenting may have been changed. Treat this as a
306 if generators_changed
:
308 output_api
.PresubmitPromptWarning(
309 'Missing PPAPI IDL for stable interface (due to change in ' +
311 long_text
='\n'.join(missing_stable
)))
314 output_api
.PresubmitError(
315 'Missing PPAPI IDL for stable interface:',
316 long_text
='\n'.join(missing_stable
)))
318 # Verify all *.h files match *.idl definitions, use:
319 # --test to prevent output to disk
320 # --diff to generate a unified diff
321 # --out to pick which files to examine (only the ones in the CL)
322 ppapi_dir
= input_api
.PresubmitLocalPath()
323 cmd
= [sys
.executable
, 'generator.py',
324 '--wnone', '--diff', '--test','--cgen', '--range=start,end']
326 # Only generate output for IDL files references (as *.h or *.idl) in this CL
327 cmd
.append('--out=' + ','.join([name
+ '.idl' for name
in both
]))
328 cmd_results
= RunCmdAndCheck(cmd
,
329 'PPAPI IDL Diff detected: Run the generator.',
331 os
.path
.join(ppapi_dir
, 'generators'))
333 results
.extend(cmd_results
)
338 def CheckChangeOnUpload(input_api
, output_api
):
339 return CheckChange(input_api
, output_api
)
342 def CheckChangeOnCommit(input_api
, output_api
):
343 return CheckChange(input_api
, output_api
)