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