OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright 2014 The Chromium Authors. All rights reserved. | 2 # Copyright 2014 The Chromium Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 """Checks third-party licenses for the purposes of the Android WebView build. | 6 """Checks third-party licenses for the purposes of the Android WebView build. |
7 | 7 |
8 The Android tree includes a snapshot of Chromium in order to power the system | 8 The Android tree includes a snapshot of Chromium in order to power the system |
9 WebView. This tool checks that all code uses open-source licenses compatible | 9 WebView. This tool checks that all code uses open-source licenses compatible |
10 with Android, and that we meet the requirements of those licenses. It can also | 10 with Android, and that we meet the requirements of those licenses. It can also |
(...skipping 26 matching lines...) Expand all Loading... |
37 os.path.join(REPOSITORY_ROOT, 'third_party', 'PRESUBMIT.py')) | 37 os.path.join(REPOSITORY_ROOT, 'third_party', 'PRESUBMIT.py')) |
38 | 38 |
39 sys.path.append(os.path.join(REPOSITORY_ROOT, 'tools')) | 39 sys.path.append(os.path.join(REPOSITORY_ROOT, 'tools')) |
40 import licenses | 40 import licenses |
41 | 41 |
42 import copyright_scanner | 42 import copyright_scanner |
43 import known_issues | 43 import known_issues |
44 | 44 |
45 class InputApi(object): | 45 class InputApi(object): |
46 def __init__(self): | 46 def __init__(self): |
| 47 self.os_path = os.path |
| 48 self.os_walk = os.walk |
47 self.re = re | 49 self.re = re |
| 50 self.ReadFile = _ReadFile |
48 | 51 |
49 def GetIncompatibleDirectories(): | 52 def GetIncompatibleDirectories(): |
50 """Gets a list of third-party directories which use licenses incompatible | 53 """Gets a list of third-party directories which use licenses incompatible |
51 with Android. This is used by the snapshot tool. | 54 with Android. This is used by the snapshot tool. |
52 Returns: | 55 Returns: |
53 A list of directories. | 56 A list of directories. |
54 """ | 57 """ |
55 | 58 |
56 result = [] | 59 result = [] |
57 for directory in _FindThirdPartyDirs(): | 60 for directory in _FindThirdPartyDirs(): |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
92 known_incompatible = frozenset(known_incompatible) | 95 known_incompatible = frozenset(known_incompatible) |
93 return incompatible_directories.difference(known_incompatible) | 96 return incompatible_directories.difference(known_incompatible) |
94 | 97 |
95 | 98 |
96 class ScanResult(object): | 99 class ScanResult(object): |
97 Ok, Warnings, Errors = range(3) | 100 Ok, Warnings, Errors = range(3) |
98 | 101 |
99 # Needs to be a top-level function for multiprocessing | 102 # Needs to be a top-level function for multiprocessing |
100 def _FindCopyrightViolations(files_to_scan_as_string): | 103 def _FindCopyrightViolations(files_to_scan_as_string): |
101 return copyright_scanner.FindCopyrightViolations( | 104 return copyright_scanner.FindCopyrightViolations( |
102 REPOSITORY_ROOT, files_to_scan_as_string) | 105 InputApi(), REPOSITORY_ROOT, files_to_scan_as_string) |
103 | 106 |
104 def _ShardList(l, shard_len): | 107 def _ShardList(l, shard_len): |
105 return [l[i:i + shard_len] for i in range(0, len(l), shard_len)] | 108 return [l[i:i + shard_len] for i in range(0, len(l), shard_len)] |
106 | 109 |
107 def _CheckLicenseHeaders(excluded_dirs_list, whitelisted_files): | 110 def _CheckLicenseHeaders(excluded_dirs_list, whitelisted_files): |
108 """Checks that all files which are not in a listed third-party directory, | 111 """Checks that all files which are not in a listed third-party directory, |
109 and which do not use the standard Chromium license, are whitelisted. | 112 and which do not use the standard Chromium license, are whitelisted. |
110 Args: | 113 Args: |
111 excluded_dirs_list: The list of directories to exclude from scanning. | 114 excluded_dirs_list: The list of directories to exclude from scanning. |
112 whitelisted_files: The whitelist of files. | 115 whitelisted_files: The whitelist of files. |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
150 # Swarming tools, doesn't exist in the snapshot | 153 # Swarming tools, doesn't exist in the snapshot |
151 excluded_dirs_list.append('tools/swarming_client') | 154 excluded_dirs_list.append('tools/swarming_client') |
152 # Arm sysroot tools, doesn't exist in the snapshot | 155 # Arm sysroot tools, doesn't exist in the snapshot |
153 excluded_dirs_list.append('arm-sysroot') | 156 excluded_dirs_list.append('arm-sysroot') |
154 # Data is not part of open source chromium, but are included on some bots. | 157 # Data is not part of open source chromium, but are included on some bots. |
155 excluded_dirs_list.append('data') | 158 excluded_dirs_list.append('data') |
156 # This is not part of open source chromium, but are included on some bots. | 159 # This is not part of open source chromium, but are included on some bots. |
157 excluded_dirs_list.append('skia/tools/clusterfuzz-data') | 160 excluded_dirs_list.append('skia/tools/clusterfuzz-data') |
158 | 161 |
159 files_to_scan = copyright_scanner.FindFiles( | 162 files_to_scan = copyright_scanner.FindFiles( |
160 REPOSITORY_ROOT, ['.'], excluded_dirs_list) | 163 InputApi(), REPOSITORY_ROOT, ['.'], excluded_dirs_list) |
161 sharded_files_to_scan = _ShardList(files_to_scan, 2000) | 164 sharded_files_to_scan = _ShardList(files_to_scan, 2000) |
162 pool = multiprocessing.Pool() | 165 pool = multiprocessing.Pool() |
163 offending_files_chunks = pool.map_async( | 166 offending_files_chunks = pool.map_async( |
164 _FindCopyrightViolations, sharded_files_to_scan).get(999999) | 167 _FindCopyrightViolations, sharded_files_to_scan).get(999999) |
165 pool.close() | 168 pool.close() |
166 pool.join() | 169 pool.join() |
167 # Flatten out the result | 170 # Flatten out the result |
168 offending_files = \ | 171 offending_files = \ |
169 [item for sublist in offending_files_chunks for item in sublist] | 172 [item for sublist in offending_files_chunks for item in sublist] |
170 | 173 |
(...skipping 15 matching lines...) Expand all Loading... |
186 '\n'.join(sorted(missing)) | 189 '\n'.join(sorted(missing)) |
187 | 190 |
188 if unknown: | 191 if unknown: |
189 return ScanResult.Errors | 192 return ScanResult.Errors |
190 elif stale or missing: | 193 elif stale or missing: |
191 return ScanResult.Warnings | 194 return ScanResult.Warnings |
192 else: | 195 else: |
193 return ScanResult.Ok | 196 return ScanResult.Ok |
194 | 197 |
195 | 198 |
196 def _ReadFile(path): | 199 def _ReadFile(full_path, mode='rU'): |
| 200 """Reads a file from disk. This emulates presubmit InputApi.ReadFile func. |
| 201 Args: |
| 202 full_path: The path of the file to read. |
| 203 Returns: |
| 204 The contents of the file as a string. |
| 205 """ |
| 206 |
| 207 with open(full_path, mode) as f: |
| 208 return f.read() |
| 209 |
| 210 |
| 211 def _ReadLocalFile(path, mode='rb'): |
197 """Reads a file from disk. | 212 """Reads a file from disk. |
198 Args: | 213 Args: |
199 path: The path of the file to read, relative to the root of the repository. | 214 path: The path of the file to read, relative to the root of the repository. |
200 Returns: | 215 Returns: |
201 The contents of the file as a string. | 216 The contents of the file as a string. |
202 """ | 217 """ |
203 | 218 |
204 with open(os.path.join(REPOSITORY_ROOT, path), 'rb') as f: | 219 return _ReadFile(os.path.join(REPOSITORY_ROOT, path), mode) |
205 return f.read() | |
206 | 220 |
207 | 221 |
208 def _FindThirdPartyDirs(): | 222 def _FindThirdPartyDirs(): |
209 """Gets the list of third-party directories. | 223 """Gets the list of third-party directories. |
210 Returns: | 224 Returns: |
211 The list of third-party directories. | 225 The list of third-party directories. |
212 """ | 226 """ |
213 | 227 |
214 # Please don't add here paths that have problems with license files, | 228 # Please don't add here paths that have problems with license files, |
215 # as they will end up included in Android WebView snapshot. | 229 # as they will end up included in Android WebView snapshot. |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
255 all_licenses_valid = True | 269 all_licenses_valid = True |
256 for path in sorted(third_party_dirs): | 270 for path in sorted(third_party_dirs): |
257 try: | 271 try: |
258 licenses.ParseDir(path, REPOSITORY_ROOT) | 272 licenses.ParseDir(path, REPOSITORY_ROOT) |
259 except licenses.LicenseError, e: | 273 except licenses.LicenseError, e: |
260 if not (path in known_issues.KNOWN_ISSUES): | 274 if not (path in known_issues.KNOWN_ISSUES): |
261 print 'Got LicenseError "%s" while scanning %s' % (e, path) | 275 print 'Got LicenseError "%s" while scanning %s' % (e, path) |
262 all_licenses_valid = False | 276 all_licenses_valid = False |
263 | 277 |
264 # Second, check for non-standard license text. | 278 # Second, check for non-standard license text. |
265 files_data = _ReadFile(os.path.join('android_webview', 'tools', | 279 files_data = _ReadLocalFile(os.path.join('android_webview', 'tools', |
266 'third_party_files_whitelist.txt')) | 280 'third_party_files_whitelist.txt')) |
267 whitelisted_files = [] | 281 whitelisted_files = [] |
268 for line in files_data.splitlines(): | 282 for line in files_data.splitlines(): |
269 match = re.match(r'([^#\s]+)', line) | 283 match = re.match(r'([^#\s]+)', line) |
270 if match: | 284 if match: |
271 whitelisted_files.append(match.group(1)) | 285 whitelisted_files.append(match.group(1)) |
272 licenses_check = _CheckLicenseHeaders(third_party_dirs, whitelisted_files) | 286 licenses_check = _CheckLicenseHeaders(third_party_dirs, whitelisted_files) |
273 | 287 |
274 return licenses_check if all_licenses_valid else ScanResult.Errors | 288 return licenses_check if all_licenses_valid else ScanResult.Errors |
275 | 289 |
276 | 290 |
277 def GenerateNoticeFile(): | 291 def GenerateNoticeFile(): |
278 """Generates the contents of an Android NOTICE file for the third-party code. | 292 """Generates the contents of an Android NOTICE file for the third-party code. |
279 This is used by the snapshot tool. | 293 This is used by the snapshot tool. |
280 Returns: | 294 Returns: |
281 The contents of the NOTICE file. | 295 The contents of the NOTICE file. |
282 """ | 296 """ |
283 | 297 |
284 third_party_dirs = _FindThirdPartyDirs() | 298 third_party_dirs = _FindThirdPartyDirs() |
285 | 299 |
286 # Don't forget Chromium's LICENSE file | 300 # Don't forget Chromium's LICENSE file |
287 content = [_ReadFile('LICENSE')] | 301 content = [_ReadLocalFile('LICENSE')] |
288 | 302 |
289 # We provide attribution for all third-party directories. | 303 # We provide attribution for all third-party directories. |
290 # TODO(steveblock): Limit this to only code used by the WebView binary. | 304 # TODO(steveblock): Limit this to only code used by the WebView binary. |
291 for directory in sorted(third_party_dirs): | 305 for directory in sorted(third_party_dirs): |
292 metadata = licenses.ParseDir(directory, REPOSITORY_ROOT, | 306 metadata = licenses.ParseDir(directory, REPOSITORY_ROOT, |
293 require_license_file=False) | 307 require_license_file=False) |
294 license_file = metadata['License File'] | 308 license_file = metadata['License File'] |
295 if license_file and license_file != licenses.NOT_SHIPPED: | 309 if license_file and license_file != licenses.NOT_SHIPPED: |
296 content.append(_ReadFile(license_file)) | 310 content.append(_ReadLocalFile(license_file)) |
297 | 311 |
298 return '\n'.join(content) | 312 return '\n'.join(content) |
299 | 313 |
300 | 314 |
301 def _ProcessIncompatibleResult(incompatible_directories): | 315 def _ProcessIncompatibleResult(incompatible_directories): |
302 if incompatible_directories: | 316 if incompatible_directories: |
303 print ("Incompatibly licensed directories found:\n" + | 317 print ("Incompatibly licensed directories found:\n" + |
304 "\n".join(sorted(incompatible_directories))) | 318 "\n".join(sorted(incompatible_directories))) |
305 return ScanResult.Errors | 319 return ScanResult.Errors |
306 return ScanResult.Ok | 320 return ScanResult.Ok |
(...skipping 30 matching lines...) Expand all Loading... |
337 return scan_result | 351 return scan_result |
338 elif args[0] == 'notice': | 352 elif args[0] == 'notice': |
339 print GenerateNoticeFile() | 353 print GenerateNoticeFile() |
340 return ScanResult.Ok | 354 return ScanResult.Ok |
341 elif args[0] == 'incompatible_directories': | 355 elif args[0] == 'incompatible_directories': |
342 return _ProcessIncompatibleResult(GetUnknownIncompatibleDirectories()) | 356 return _ProcessIncompatibleResult(GetUnknownIncompatibleDirectories()) |
343 elif args[0] == 'all_incompatible_directories': | 357 elif args[0] == 'all_incompatible_directories': |
344 return _ProcessIncompatibleResult(GetIncompatibleDirectories()) | 358 return _ProcessIncompatibleResult(GetIncompatibleDirectories()) |
345 elif args[0] == 'display_copyrights': | 359 elif args[0] == 'display_copyrights': |
346 files = sys.stdin.read().splitlines() | 360 files = sys.stdin.read().splitlines() |
347 for f, c in zip(files, copyright_scanner.FindCopyrights('.', files)): | 361 for f, c in \ |
| 362 zip(files, copyright_scanner.FindCopyrights(InputApi(), '.', files)): |
348 print f, '\t', ' / '.join(sorted(c)) | 363 print f, '\t', ' / '.join(sorted(c)) |
349 return ScanResult.Ok | 364 return ScanResult.Ok |
350 parser.print_help() | 365 parser.print_help() |
351 return ScanResult.Errors | 366 return ScanResult.Errors |
352 | 367 |
353 if __name__ == '__main__': | 368 if __name__ == '__main__': |
354 sys.exit(main()) | 369 sys.exit(main()) |
OLD | NEW |