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

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: Add license header to the manual test script 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
« no previous file with comments | « android_webview/tools/tests/non-generated-05 ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 19 matching lines...) Expand all
227 194
228 195
229 def _ReadFile(path): 196 def _ReadFile(path):
230 """Reads a file from disk. 197 """Reads a file from disk.
231 Args: 198 Args:
232 path: The path of the file to read, relative to the root of the repository. 199 path: The path of the file to read, relative to the root of the repository.
233 Returns: 200 Returns:
234 The contents of the file as a string. 201 The contents of the file as a string.
235 """ 202 """
236 203
237 return open(os.path.join(REPOSITORY_ROOT, path), 'rb').read() 204 with open(os.path.join(REPOSITORY_ROOT, path), 'rb') as f:
205 return f.read()
238 206
239 207
240 def _FindThirdPartyDirs(): 208 def _FindThirdPartyDirs():
241 """Gets the list of third-party directories. 209 """Gets the list of third-party directories.
242 Returns: 210 Returns:
243 The list of third-party directories. 211 The list of third-party directories.
244 """ 212 """
245 213
246 # Please don't add here paths that have problems with license files, 214 # Please don't add here paths that have problems with license files,
247 # as they will end up included in Android WebView snapshot. 215 # as they will end up included in Android WebView snapshot.
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after
340 def main(): 308 def main():
341 class FormatterWithNewLines(optparse.IndentedHelpFormatter): 309 class FormatterWithNewLines(optparse.IndentedHelpFormatter):
342 def format_description(self, description): 310 def format_description(self, description):
343 paras = description.split('\n') 311 paras = description.split('\n')
344 formatted_paras = [textwrap.fill(para, self.width) for para in paras] 312 formatted_paras = [textwrap.fill(para, self.width) for para in paras]
345 return '\n'.join(formatted_paras) + '\n' 313 return '\n'.join(formatted_paras) + '\n'
346 314
347 parser = optparse.OptionParser(formatter=FormatterWithNewLines(), 315 parser = optparse.OptionParser(formatter=FormatterWithNewLines(),
348 usage='%prog [options]') 316 usage='%prog [options]')
349 parser.description = (__doc__ + 317 parser.description = (__doc__ +
350 '\nCommands:\n' \ 318 '\nCommands:\n'
351 ' scan Check licenses.\n' \ 319 ' scan Check licenses.\n'
352 ' notice Generate Android NOTICE file on stdout.\n' \ 320 ' notice Generate Android NOTICE file on stdout.\n'
353 ' incompatible_directories Scan for incompatibly' 321 ' incompatible_directories Scan for incompatibly'
354 ' licensed directories.\n' 322 ' licensed directories.\n'
355 ' all_incompatible_directories Scan for incompatibly' 323 ' all_incompatible_directories Scan for incompatibly'
356 ' licensed directories (even those in' 324 ' licensed directories (even those in'
357 ' known_issues.py).\n') 325 ' known_issues.py).\n'
326 ' display_copyrights Display autorship on the files'
327 ' using names provided via stdin.\n')
358 (_, args) = parser.parse_args() 328 (_, args) = parser.parse_args()
359 if len(args) != 1: 329 if len(args) != 1:
360 parser.print_help() 330 parser.print_help()
361 return ScanResult.Errors 331 return ScanResult.Errors
362 332
363 if args[0] == 'scan': 333 if args[0] == 'scan':
364 scan_result = _Scan() 334 scan_result = _Scan()
365 if scan_result == ScanResult.Ok: 335 if scan_result == ScanResult.Ok:
366 print 'OK!' 336 print 'OK!'
367 return scan_result 337 return scan_result
368 elif args[0] == 'notice': 338 elif args[0] == 'notice':
369 print GenerateNoticeFile() 339 print GenerateNoticeFile()
370 return ScanResult.Ok 340 return ScanResult.Ok
371 elif args[0] == 'incompatible_directories': 341 elif args[0] == 'incompatible_directories':
372 return _ProcessIncompatibleResult(GetUnknownIncompatibleDirectories()) 342 return _ProcessIncompatibleResult(GetUnknownIncompatibleDirectories())
373 elif args[0] == 'all_incompatible_directories': 343 elif args[0] == 'all_incompatible_directories':
374 return _ProcessIncompatibleResult(GetIncompatibleDirectories()) 344 return _ProcessIncompatibleResult(GetIncompatibleDirectories())
345 elif args[0] == 'display_copyrights':
346 files = sys.stdin.read().splitlines()
347 for f, c in zip(files, copyright_scanner.FindCopyrights('.', files)):
348 print f, '\t', ' / '.join(sorted(c))
349 return ScanResult.Ok
375 parser.print_help() 350 parser.print_help()
376 return ScanResult.Errors 351 return ScanResult.Errors
377 352
378 if __name__ == '__main__': 353 if __name__ == '__main__':
379 sys.exit(main()) 354 sys.exit(main())
OLDNEW
« no previous file with comments | « 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