Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(186)

Side by Side Diff: android_webview/tools/webview_licenses.py

Issue 622493004: [Android WebView] Rewrite copyrights scanner in Python (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 #!/usr/bin/python 1 #!/usr/bin/python
2 # Copyright (c) 2012 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
11 be used to generate an Android NOTICE file for the third-party code. 11 be used to generate an Android NOTICE file for the third-party code.
12 12
13 It makes use of src/tools/licenses.py and the README.chromium files on which 13 It makes use of src/tools/licenses.py and the README.chromium files on which
14 it depends. It also makes use of a data file, third_party_files_whitelist.txt, 14 it depends. It also makes use of a data file, third_party_files_whitelist.txt,
15 which whitelists indicidual files which contain third-party code but which 15 which whitelists indicidual files which contain third-party code but which
16 aren't in a third-party directory with a README.chromium file. 16 aren't in a third-party directory with a README.chromium file.
17 """ 17 """
18 18
19 import glob 19 import glob
20 import imp 20 import imp
21 import multiprocessing 21 import multiprocessing
22 import optparse 22 import optparse
23 import os 23 import os
24 import re 24 import re
25 import subprocess
26 import sys 25 import sys
27 import textwrap 26 import textwrap
28 27
29 28
30 REPOSITORY_ROOT = os.path.abspath(os.path.join( 29 REPOSITORY_ROOT = os.path.abspath(os.path.join(
31 os.path.dirname(__file__), '..', '..')) 30 os.path.dirname(__file__), '..', '..'))
32 31
33 # Import third_party/PRESUBMIT.py via imp to avoid importing a random 32 # Import third_party/PRESUBMIT.py via imp to avoid importing a random
34 # PRESUBMIT.py from $PATH, also make sure we don't generate a .pyc file. 33 # PRESUBMIT.py from $PATH, also make sure we don't generate a .pyc file.
35 sys.dont_write_bytecode = True 34 sys.dont_write_bytecode = True
36 third_party = \ 35 third_party = \
37 imp.load_source('PRESUBMIT', \ 36 imp.load_source('PRESUBMIT', \
38 os.path.join(REPOSITORY_ROOT, 'third_party', 'PRESUBMIT.py')) 37 os.path.join(REPOSITORY_ROOT, 'third_party', 'PRESUBMIT.py'))
39 38
40 sys.path.append(os.path.join(REPOSITORY_ROOT, 'tools')) 39 sys.path.append(os.path.join(REPOSITORY_ROOT, 'tools'))
41 import licenses 40 import licenses
42 41
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.re = re 47 self.re = re
48 48
49 def GetIncompatibleDirectories(): 49 def GetIncompatibleDirectories():
50 """Gets a list of third-party directories which use licenses incompatible 50 """Gets a list of third-party directories which use licenses incompatible
51 with Android. This is used by the snapshot tool. 51 with Android. This is used by the snapshot tool.
52 Returns: 52 Returns:
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
90 exclude = exclude_dirname 90 exclude = exclude_dirname
91 known_incompatible.append(os.path.normpath(os.path.join(path, exclude))) 91 known_incompatible.append(os.path.normpath(os.path.join(path, exclude)))
92 known_incompatible = frozenset(known_incompatible) 92 known_incompatible = frozenset(known_incompatible)
93 return incompatible_directories.difference(known_incompatible) 93 return incompatible_directories.difference(known_incompatible)
94 94
95 95
96 class ScanResult(object): 96 class ScanResult(object):
97 Ok, Warnings, Errors = range(3) 97 Ok, Warnings, Errors = range(3)
98 98
99 # Needs to be a top-level function for multiprocessing 99 # Needs to be a top-level function for multiprocessing
100 def _FindCopyrights(files_to_scan): 100 def _FindCopyrightViolations(files_to_scan_as_string):
101 args = [os.path.join('android_webview', 'tools', 'find_copyrights.pl')] 101 return copyright_scanner.FindCopyrightViolations(
102 p = subprocess.Popen( 102 REPOSITORY_ROOT, files_to_scan_as_string)
103 args=args, cwd=REPOSITORY_ROOT,
104 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
105 lines = p.communicate(files_to_scan)[0].splitlines()
106 103
107 offending_files = [] 104 def _ShardList(l, shard_len):
108 allowed_copyrights = '^(?:\*No copyright\*' \ 105 return [l[i:i + shard_len] for i in range(0, len(l), shard_len)]
109 '|20[0-9][0-9](?:-20[0-9][0-9])? The Chromium Authors\. ' \
110 'All rights reserved.*)$'
111 allowed_copyrights_re = re.compile(allowed_copyrights)
112 for l in lines:
113 entries = l.split('\t')
114 if entries[1] == "GENERATED FILE":
115 continue
116 copyrights = entries[1].split(' / ')
117 for c in copyrights:
118 if c and not allowed_copyrights_re.match(c):
119 offending_files.append(os.path.normpath(entries[0]))
120 break
121 return offending_files
122
123 def _ShardString(s, delimiter, shard_len):
124 result = []
125 index = 0
126 last_pos = 0
127 for m in re.finditer(delimiter, s):
128 index += 1
129 if index % shard_len == 0:
130 result.append(s[last_pos:m.end()])
131 last_pos = m.end()
132 if not index % shard_len == 0:
133 result.append(s[last_pos:])
134 return result
135 106
136 def _CheckLicenseHeaders(excluded_dirs_list, whitelisted_files): 107 def _CheckLicenseHeaders(excluded_dirs_list, whitelisted_files):
137 """Checks that all files which are not in a listed third-party directory, 108 """Checks that all files which are not in a listed third-party directory,
138 and which do not use the standard Chromium license, are whitelisted. 109 and which do not use the standard Chromium license, are whitelisted.
139 Args: 110 Args:
140 excluded_dirs_list: The list of directories to exclude from scanning. 111 excluded_dirs_list: The list of directories to exclude from scanning.
141 whitelisted_files: The whitelist of files. 112 whitelisted_files: The whitelist of files.
142 Returns: 113 Returns:
143 ScanResult.Ok if all files with non-standard license headers are whitelisted 114 ScanResult.Ok if all files with non-standard license headers are whitelisted
144 and the whitelist contains no stale entries; 115 and the whitelist contains no stale entries;
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 excluded_dirs_list.append('tools/histograms') 149 excluded_dirs_list.append('tools/histograms')
179 # Swarming tools, doesn't exist in the snapshot 150 # Swarming tools, doesn't exist in the snapshot
180 excluded_dirs_list.append('tools/swarming_client') 151 excluded_dirs_list.append('tools/swarming_client')
181 # Arm sysroot tools, doesn't exist in the snapshot 152 # Arm sysroot tools, doesn't exist in the snapshot
182 excluded_dirs_list.append('arm-sysroot') 153 excluded_dirs_list.append('arm-sysroot')
183 # Data is not part of open source chromium, but are included on some bots. 154 # Data is not part of open source chromium, but are included on some bots.
184 excluded_dirs_list.append('data') 155 excluded_dirs_list.append('data')
185 # This is not part of open source chromium, but are included on some bots. 156 # This is not part of open source chromium, but are included on some bots.
186 excluded_dirs_list.append('skia/tools/clusterfuzz-data') 157 excluded_dirs_list.append('skia/tools/clusterfuzz-data')
187 158
188 args = [os.path.join('android_webview', 'tools', 'find_files.pl'), 159 files_to_scan = copyright_scanner.FindFiles(
189 '.' 160 REPOSITORY_ROOT, ['.'], excluded_dirs_list)
190 ] + excluded_dirs_list 161 sharded_files_to_scan = _ShardList(files_to_scan, 2000)
191 p = subprocess.Popen(args=args, cwd=REPOSITORY_ROOT, stdout=subprocess.PIPE)
192 files_to_scan = p.communicate()[0]
193
194 sharded_files_to_scan = _ShardString(files_to_scan, '\n', 2000)
195 pool = multiprocessing.Pool() 162 pool = multiprocessing.Pool()
196 offending_files_chunks = pool.map_async( 163 offending_files_chunks = pool.map_async(
197 _FindCopyrights, sharded_files_to_scan).get(999999) 164 _FindCopyrightViolations, sharded_files_to_scan).get(999999)
198 pool.close() 165 pool.close()
199 pool.join() 166 pool.join()
200 # Flatten out the result 167 # Flatten out the result
201 offending_files = \ 168 offending_files = \
202 [item for sublist in offending_files_chunks for item in sublist] 169 [item for sublist in offending_files_chunks for item in sublist]
203 170
204 unknown = set(offending_files) - set(whitelisted_files) 171 unknown = set(offending_files) - set(whitelisted_files)
205 if unknown: 172 if unknown:
206 print 'The following files contain a third-party license but are not in ' \ 173 print 'The following files contain a third-party license but are not in ' \
207 'a listed third-party directory and are not whitelisted. You must ' \ 174 'a listed third-party directory and are not whitelisted. You must ' \
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after
340 def main(): 307 def main():
341 class FormatterWithNewLines(optparse.IndentedHelpFormatter): 308 class FormatterWithNewLines(optparse.IndentedHelpFormatter):
342 def format_description(self, description): 309 def format_description(self, description):
343 paras = description.split('\n') 310 paras = description.split('\n')
344 formatted_paras = [textwrap.fill(para, self.width) for para in paras] 311 formatted_paras = [textwrap.fill(para, self.width) for para in paras]
345 return '\n'.join(formatted_paras) + '\n' 312 return '\n'.join(formatted_paras) + '\n'
346 313
347 parser = optparse.OptionParser(formatter=FormatterWithNewLines(), 314 parser = optparse.OptionParser(formatter=FormatterWithNewLines(),
348 usage='%prog [options]') 315 usage='%prog [options]')
349 parser.description = (__doc__ + 316 parser.description = (__doc__ +
350 '\nCommands:\n' \ 317 '\nCommands:\n'
351 ' scan Check licenses.\n' \ 318 ' scan Check licenses.\n'
352 ' notice Generate Android NOTICE file on stdout.\n' \ 319 ' notice Generate Android NOTICE file on stdout.\n'
353 ' incompatible_directories Scan for incompatibly' 320 ' incompatible_directories Scan for incompatibly'
354 ' licensed directories.\n' 321 ' licensed directories.\n'
355 ' all_incompatible_directories Scan for incompatibly' 322 ' all_incompatible_directories Scan for incompatibly'
356 ' licensed directories (even those in' 323 ' licensed directories (even those in'
357 ' known_issues.py).\n') 324 ' known_issues.py).\n'
325 ' display_copyrights Display autorship on the files'
326 ' using names provided via stdin.\n')
358 (_, args) = parser.parse_args() 327 (_, args) = parser.parse_args()
359 if len(args) != 1: 328 if len(args) != 1:
360 parser.print_help() 329 parser.print_help()
361 return ScanResult.Errors 330 return ScanResult.Errors
362 331
363 if args[0] == 'scan': 332 if args[0] == 'scan':
364 scan_result = _Scan() 333 scan_result = _Scan()
365 if scan_result == ScanResult.Ok: 334 if scan_result == ScanResult.Ok:
366 print 'OK!' 335 print 'OK!'
367 return scan_result 336 return scan_result
368 elif args[0] == 'notice': 337 elif args[0] == 'notice':
369 print GenerateNoticeFile() 338 print GenerateNoticeFile()
370 return ScanResult.Ok 339 return ScanResult.Ok
371 elif args[0] == 'incompatible_directories': 340 elif args[0] == 'incompatible_directories':
372 return _ProcessIncompatibleResult(GetUnknownIncompatibleDirectories()) 341 return _ProcessIncompatibleResult(GetUnknownIncompatibleDirectories())
373 elif args[0] == 'all_incompatible_directories': 342 elif args[0] == 'all_incompatible_directories':
374 return _ProcessIncompatibleResult(GetIncompatibleDirectories()) 343 return _ProcessIncompatibleResult(GetIncompatibleDirectories())
344 elif args[0] == 'display_copyrights':
345 files = sys.stdin.read().splitlines()
346 for f, c in zip(files, copyright_scanner.FindCopyrights('.', files)):
347 print f, '\t', ' / '.join(sorted(c))
348 return ScanResult.Ok
375 parser.print_help() 349 parser.print_help()
376 return ScanResult.Errors 350 return ScanResult.Errors
377 351
378 if __name__ == '__main__': 352 if __name__ == '__main__':
379 sys.exit(main()) 353 sys.exit(main())
OLDNEW
« android_webview/tools/copyright_scanner.py ('K') | « android_webview/tools/tests/non-generated-05 ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698