[email protected] | e4f373e | 2012-01-10 23:18:24 | [diff] [blame] | 1 | # Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 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 |
[email protected] | c7db6e9 | 2012-03-14 06:58:14 | [diff] [blame] | 6 | import re |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 7 | import sys |
| 8 | import subprocess |
| 9 | |
[email protected] | c7db6e9 | 2012-03-14 06:58:14 | [diff] [blame] | 10 | |
[email protected] | bfe7574 | 2014-07-11 16:43:57 | [diff] [blame] | 11 | def RunCmdAndCheck(cmd, err_string, output_api, cwd=None, warning=False): |
[email protected] | 2978339a7 | 2011-11-30 17:59:14 | [diff] [blame] | 12 | results = [] |
[email protected] | e4f373e | 2012-01-10 23:18:24 | [diff] [blame] | 13 | p = subprocess.Popen(cmd, cwd=cwd, |
[email protected] | 2978339a7 | 2011-11-30 17:59:14 | [diff] [blame] | 14 | stdout=subprocess.PIPE, |
| 15 | stderr=subprocess.PIPE) |
| 16 | (p_stdout, p_stderr) = p.communicate() |
| 17 | if p.returncode: |
[email protected] | bfe7574 | 2014-07-11 16:43:57 | [diff] [blame] | 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)) |
[email protected] | 2978339a7 | 2011-11-30 17:59:14 | [diff] [blame] | 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 != []: |
[email protected] | 6faeb20 | 2012-12-06 15:43:05 | [diff] [blame] | 38 | cmd = [ sys.executable, 'idl_tests.py'] |
[email protected] | 2978339a7 | 2011-11-30 17:59:14 | [diff] [blame] | 39 | ppapi_dir = input_api.PresubmitLocalPath() |
| 40 | results.extend(RunCmdAndCheck(cmd, |
[email protected] | 6faeb20 | 2012-12-06 15:43:05 | [diff] [blame] | 41 | 'PPAPI IDL unittests failed.', |
[email protected] | e4f373e | 2012-01-10 23:18:24 | [diff] [blame] | 42 | output_api, |
| 43 | os.path.join(ppapi_dir, 'generators'))) |
[email protected] | 2978339a7 | 2011-11-30 17:59:14 | [diff] [blame] | 44 | return results |
| 45 | |
| 46 | |
[email protected] | c7db6e9 | 2012-03-14 06:58:14 | [diff] [blame] | 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): |
[email protected] | 4d60dcd8 | 2013-10-08 21:33:51 | [diff] [blame] | 50 | live_files = input_api.AffectedFiles(include_deletes=False) |
| 51 | files = [f.LocalPath() for f in live_files] |
[email protected] | c7db6e9 | 2012-03-14 06:58:14 | [diff] [blame] | 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. |
[email protected] | 620446f7 | 2012-12-12 22:21:03 | [diff] [blame] | 59 | if ext not in ['.h', '.idl']: |
[email protected] | c7db6e9 | 2012-03-14 06:58:14 | [diff] [blame] | 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 | |
[email protected] | 1594f57 | 2012-06-01 18:33:14 | [diff] [blame] | 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): |
[email protected] | 4d60dcd8 | 2013-10-08 21:33:51 | [diff] [blame] | 87 | live_files = input_api.AffectedFiles(include_deletes=False) |
| 88 | files = [f.LocalPath() for f in live_files] |
[email protected] | 1594f57 | 2012-06-01 18:33:14 | [diff] [blame] | 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 [] |
[email protected] | c7db6e9 | 2012-03-14 06:58:14 | [diff] [blame] | 116 | |
[email protected] | 183dea0 | 2013-07-17 19:15:19 | [diff] [blame] | 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, |
[email protected] | bfe7574 | 2014-07-11 16:43:57 | [diff] [blame] | 148 | 'PPAPI Interface modified without updating NaCl SDK.\n' |
| 149 | '(note that some dev interfaces should not be added ' |
[email protected] | 72412cf8 | 2014-08-15 00:37:03 | [diff] [blame] | 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)', |
[email protected] | bfe7574 | 2014-07-11 16:43:57 | [diff] [blame] | 153 | output_api, |
| 154 | warning=True) |
[email protected] | 183dea0 | 2013-07-17 19:15:19 | [diff] [blame] | 155 | |
[email protected] | ea44183 | 2014-02-05 15:34:21 | [diff] [blame] | 156 | # Verify that changes to ppapi/thunk/interfaces_* files have a corresponding |
Steven Holte | d79dbde0 | 2017-05-04 00:05:48 | [diff] [blame] | 157 | # change to tools/metrics/histograms/enums.xml for UMA tracking. |
[email protected] | ea44183 | 2014-02-05 15:34:21 | [diff] [blame] | 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') |
Steven Holte | d79dbde0 | 2017-05-04 00:05:48 | [diff] [blame] | 171 | HISTOGRAM_XML_FILE = 'tools/metrics/histograms/enums.xml' |
[email protected] | ea44183 | 2014-02-05 15:34:21 | [diff] [blame] | 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: |
[email protected] | 29ab1d9 | 2014-04-03 15:34:41 | [diff] [blame] | 182 | return [output_api.PresubmitNotifyResult( |
Steven Holte | d79dbde0 | 2017-05-04 00:05:48 | [diff] [blame] | 183 | 'Missing change to tools/metrics/histograms/enums.xml.\n' + |
[email protected] | ea44183 | 2014-02-05 15:34:21 | [diff] [blame] | 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 | |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 188 | def CheckChange(input_api, output_api): |
| 189 | results = [] |
| 190 | |
[email protected] | 2978339a7 | 2011-11-30 17:59:14 | [diff] [blame] | 191 | results.extend(RunUnittests(input_api, output_api)) |
| 192 | |
[email protected] | c7db6e9 | 2012-03-14 06:58:14 | [diff] [blame] | 193 | results.extend(CheckTODO(input_api, output_api)) |
[email protected] | 1594f57 | 2012-06-01 18:33:14 | [diff] [blame] | 194 | |
| 195 | results.extend(CheckUnversionedPPB(input_api, output_api)) |
| 196 | |
[email protected] | 183dea0 | 2013-07-17 19:15:19 | [diff] [blame] | 197 | results.extend(CheckUpdatedNaClSDK(input_api, output_api)) |
| 198 | |
[email protected] | ea44183 | 2014-02-05 15:34:21 | [diff] [blame] | 199 | results.extend(CheckHistogramXml(input_api, output_api)) |
| 200 | |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 201 | # Verify all modified *.idl have a matching *.h |
| 202 | files = input_api.LocalPaths() |
| 203 | h_files = [] |
| 204 | idl_files = [] |
[email protected] | 60902e7 | 2013-09-10 02:31:51 | [diff] [blame] | 205 | generators_changed = False |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 206 | |
[email protected] | 3992b9e | 2014-01-23 01:55:45 | [diff] [blame] | 207 | # These are autogenerated by the command buffer generator, they don't go |
| 208 | # through idl. |
| 209 | whitelist = ['ppb_opengles2', 'ppb_opengles2ext_dev'] |
| 210 | |
[email protected] | 2c0b8c9 | 2014-01-31 05:33:26 | [diff] [blame] | 211 | # The PDF interface is hand-written. |
[email protected] | ec15840 | 2014-02-06 11:40:25 | [diff] [blame] | 212 | whitelist += ['ppb_pdf', 'ppp_pdf'] |
[email protected] | 2c0b8c9 | 2014-01-31 05:33:26 | [diff] [blame] | 213 | |
[email protected] | c7db6e9 | 2012-03-14 06:58:14 | [diff] [blame] | 214 | # Find all relevant .h and .idl files. |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 215 | for filename in files: |
| 216 | name, ext = os.path.splitext(filename) |
| 217 | name_parts = name.split(os.sep) |
[email protected] | 3992b9e | 2014-01-23 01:55:45 | [diff] [blame] | 218 | if name_parts[-1] in whitelist: |
| 219 | continue |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 220 | if name_parts[0:2] == ['ppapi', 'c'] and ext == '.h': |
| 221 | h_files.append('/'.join(name_parts[2:])) |
[email protected] | 60902e7 | 2013-09-10 02:31:51 | [diff] [blame] | 222 | elif name_parts[0:2] == ['ppapi', 'api'] and ext == '.idl': |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 223 | idl_files.append('/'.join(name_parts[2:])) |
[email protected] | 60902e7 | 2013-09-10 02:31:51 | [diff] [blame] | 224 | elif name_parts[0:2] == ['ppapi', 'generators']: |
| 225 | generators_changed = True |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 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): |
[email protected] | 8c311f0 | 2012-11-17 16:01:32 | [diff] [blame] | 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) |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 248 | |
| 249 | if missing: |
| 250 | results.append( |
[email protected] | c7db6e9 | 2012-03-14 06:58:14 | [diff] [blame] | 251 | output_api.PresubmitPromptWarning( |
| 252 | 'Missing PPAPI header, no change or skipped generation?', |
[email protected] | 8c311f0 | 2012-11-17 16:01:32 | [diff] [blame] | 253 | long_text='\n '.join(missing))) |
[email protected] | c7db6e9 | 2012-03-14 06:58:14 | [diff] [blame] | 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 | |
[email protected] | e12f4cc | 2013-02-21 03:24:34 | [diff] [blame] | 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( |
[email protected] | f7051d5 | 2013-04-02 18:31:42 | [diff] [blame] | 271 | output_api.PresubmitPromptOrNotify( |
[email protected] | e12f4cc | 2013-02-21 03:24:34 | [diff] [blame] | 272 | 'PPAPI_RELEASE has changed', long_text=line[2])) |
| 273 | releaseChanged = True |
| 274 | break |
| 275 | if releaseChanged: |
| 276 | continue |
| 277 | |
[email protected] | c7db6e9 | 2012-03-14 06:58:14 | [diff] [blame] | 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: |
[email protected] | 60902e7 | 2013-09-10 02:31:51 | [diff] [blame] | 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))) |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 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() |
[email protected] | c7db6e9 | 2012-03-14 06:58:14 | [diff] [blame] | 325 | cmd = [sys.executable, 'generator.py', |
| 326 | '--wnone', '--diff', '--test','--cgen', '--range=start,end'] |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 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])) |
[email protected] | c7db6e9 | 2012-03-14 06:58:14 | [diff] [blame] | 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 | |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 337 | return results |
| 338 | |
[email protected] | c7db6e9 | 2012-03-14 06:58:14 | [diff] [blame] | 339 | |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 340 | def CheckChangeOnUpload(input_api, output_api): |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 341 | return CheckChange(input_api, output_api) |
| 342 | |
[email protected] | c7db6e9 | 2012-03-14 06:58:14 | [diff] [blame] | 343 | |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 344 | def CheckChangeOnCommit(input_api, output_api): |
[email protected] | 3937578 | 2011-09-14 16:55:09 | [diff] [blame] | 345 | return CheckChange(input_api, output_api) |