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):
13 p
= subprocess
.Popen(cmd
, cwd
=cwd
,
14 stdout
=subprocess
.PIPE
,
15 stderr
=subprocess
.PIPE
)
16 (p_stdout
, p_stderr
) = p
.communicate()
19 output_api
.PresubmitError(err_string
,
24 def RunUnittests(input_api
, output_api
):
25 # Run some Generator unittests if the generator source was changed.
27 files
= input_api
.LocalPaths()
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.',
39 os
.path
.join(ppapi_dir
, 'generators')))
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()
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']:
57 # Only examine the ppapi directory.
58 if name_parts
[0] != 'ppapi':
61 # Only examine public plugin facing directories.
62 if name_parts
[1] not in ['api', 'c', 'cpp', 'utility']:
65 # Only examine public stable interfaces.
66 if name_parts
[2] in ['dev', 'private', 'trusted']:
68 if name_parts
[2] == 'extensions' and name_parts
[3] == 'dev':
71 filepath
= os
.path
.join('..', filename
)
72 if RE_TODO
.search(open(filepath
, 'rb').read()):
76 return [output_api
.PresubmitError(
77 'TODOs found in stable public PPAPI files:',
78 long_text
='\n'.join(todo
))]
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()
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']:
95 # Only examine the public plugin facing ppapi/cpp directory.
96 if name_parts
[0:2] != ['ppapi', 'cpp']:
99 # Only examine public stable and trusted interfaces.
100 if name_parts
[2] in ['dev', 'private']:
103 filepath
= os
.path
.join('..', filename
)
104 if RE_UNVERSIONED_PPB
.search(open(filepath
, 'rb').read()):
105 todo
.append(filename
)
108 return [output_api
.PresubmitError(
109 'Unversioned PPB interface references found in PPAPI C++ wrappers:',
110 long_text
='\n'.join(todo
))]
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.
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:
127 if name_parts
[0] != 'ppapi':
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'):
134 nacl_sdk_files
.append(filename
)
136 if not nacl_sdk_files
:
139 verify_ppapi_py
= os
.path
.join(input_api
.change
.RepositoryRoot(),
140 'native_client_sdk', 'src', 'build_tools',
142 cmd
= [sys
.executable
, verify_ppapi_py
] + nacl_sdk_files
143 return RunCmdAndCheck(cmd
,
144 'PPAPI Interface modified without updating NaCl SDK.',
147 def CheckChange(input_api
, output_api
):
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()
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
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.
189 for filename
in missing
:
190 lines
= input_api
.RightHandSideLines(lambda f
: f
.LocalPath() == filename
)
192 if line
[2].strip() == '[generate_thunk]':
193 new_thunk_files
.append(filename
)
194 for filename
in new_thunk_files
:
195 missing
.remove(filename
)
199 output_api
.PresubmitPromptWarning(
200 'Missing PPAPI header, no change or skipped generation?',
201 long_text
='\n '.join(missing
)))
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
217 if line
[2].split()[:2] == ['#define', 'PPAPI_RELEASE']:
219 output_api
.PresubmitPromptOrNotify(
220 'PPAPI_RELEASE has changed', long_text
=line
[2]))
221 releaseChanged
= True
226 if 'trusted' in name_parts
:
227 missing_priv
.append(' ppapi/c/%s.h' % filename
)
230 if 'private' in name_parts
:
231 missing_priv
.append(' ppapi/c/%s.h' % filename
)
234 if 'dev' in name_parts
:
235 missing_dev
.append(' ppapi/c/%s.h' % filename
)
238 missing_stable
.append(' ppapi/c/%s.h' % filename
)
242 output_api
.PresubmitPromptWarning(
243 'Missing PPAPI IDL for private interface, please generate IDL:',
244 long_text
='\n'.join(missing_priv
)))
248 output_api
.PresubmitPromptWarning(
249 'Missing PPAPI IDL for DEV, required before moving to stable:',
250 long_text
='\n'.join(missing_dev
)))
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
256 if generators_changed
:
258 output_api
.PresubmitPromptWarning(
259 'Missing PPAPI IDL for stable interface (due to change in ' +
261 long_text
='\n'.join(missing_stable
)))
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.',
281 os
.path
.join(ppapi_dir
, 'generators'))
283 results
.extend(cmd_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
)