Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / ppapi / PRESUBMIT.py
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.
4
5 import os
6 import re
7 import sys
8 import subprocess
9
10
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
26
27
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
45
46
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 = []
53
54   for filename in files:
55     name, ext = os.path.splitext(filename)
56     name_parts = name.split(os.sep)
57
58     # Only check normal build sources.
59     if ext not in ['.h', '.idl']:
60       continue
61
62     # Only examine the ppapi directory.
63     if name_parts[0] != 'ppapi':
64       continue
65
66     # Only examine public plugin facing directories.
67     if name_parts[1] not in ['api', 'c', 'cpp', 'utility']:
68       continue
69
70     # Only examine public stable interfaces.
71     if name_parts[2] in ['dev', 'private', 'trusted']:
72       continue
73
74     filepath = os.path.join('..', filename)
75     if RE_TODO.search(open(filepath, 'rb').read()):
76       todo.append(filename)
77
78   if todo:
79     return [output_api.PresubmitError(
80         'TODOs found in stable public PPAPI files:',
81         long_text='\n'.join(todo))]
82   return []
83
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 = []
90
91   for filename in files:
92     name, ext = os.path.splitext(filename)
93     name_parts = name.split(os.sep)
94
95     # Only check C++ sources.
96     if ext not in ['.cc']:
97       continue
98
99     # Only examine the public plugin facing ppapi/cpp directory.
100     if name_parts[0:2] != ['ppapi', 'cpp']:
101       continue
102
103     # Only examine public stable and trusted interfaces.
104     if name_parts[2] in ['dev', 'private']:
105       continue
106
107     filepath = os.path.join('..', filename)
108     if RE_UNVERSIONED_PPB.search(open(filepath, 'rb').read()):
109       todo.append(filename)
110
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 []
116
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()
120
121   # PPAPI files the Native Client SDK cares about.
122   nacl_sdk_files = []
123
124   for filename in files:
125     name, ext = os.path.splitext(filename)
126     name_parts = name.split(os.sep)
127
128     if len(name_parts) <= 2:
129       continue
130
131     if name_parts[0] != 'ppapi':
132       continue
133
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)
139
140   if not nacl_sdk_files:
141     return []
142
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)
155
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()
163
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
180
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 []
187
188 def CheckChange(input_api, output_api):
189   results = []
190
191   results.extend(RunUnittests(input_api, output_api))
192
193   results.extend(CheckTODO(input_api, output_api))
194
195   results.extend(CheckUnversionedPPB(input_api, output_api))
196
197   results.extend(CheckUpdatedNaClSDK(input_api, output_api))
198
199   results.extend(CheckHistogramXml(input_api, output_api))
200
201   # Verify all modified *.idl have a matching *.h
202   files = input_api.LocalPaths()
203   h_files = []
204   idl_files = []
205   generators_changed = False
206
207   # These are autogenerated by the command buffer generator, they don't go
208   # through idl.
209   whitelist = ['ppb_opengles2', 'ppb_opengles2ext_dev']
210
211   # The PDF interface is hand-written.
212   whitelist += ['ppb_pdf', 'ppp_pdf']
213
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
226
227   # Generate a list of all appropriate *.h and *.idl changes in this CL.
228   both = h_files + idl_files
229
230   # If there aren't any, we are done checking.
231   if not both: return results
232
233   missing = []
234   for filename in idl_files:
235     if filename not in set(h_files):
236       missing.append('ppapi/api/%s.idl' % filename)
237
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)
248
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)))
254
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)
261
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
277
278       if 'trusted' in name_parts:
279         missing_priv.append('  ppapi/c/%s.h' % filename)
280         continue
281
282       if 'private' in name_parts:
283         missing_priv.append('  ppapi/c/%s.h' % filename)
284         continue
285
286       if 'dev' in name_parts:
287         missing_dev.append('  ppapi/c/%s.h' % filename)
288         continue
289
290       missing_stable.append('  ppapi/c/%s.h' % filename)
291
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)))
297
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)))
303
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)))
319
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']
327
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)
336
337   return results
338
339
340 def CheckChangeOnUpload(input_api, output_api):
341   return CheckChange(input_api, output_api)
342
343
344 def CheckChangeOnCommit(input_api, output_api):
345   return CheckChange(input_api, output_api)