OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2012 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 optparse | 22 import optparse |
22 import os | 23 import os |
23 import re | 24 import re |
24 import subprocess | 25 import subprocess |
25 import sys | 26 import sys |
26 import textwrap | 27 import textwrap |
27 | 28 |
28 | 29 |
29 REPOSITORY_ROOT = os.path.abspath(os.path.join( | 30 REPOSITORY_ROOT = os.path.abspath(os.path.join( |
30 os.path.dirname(__file__), '..', '..')) | 31 os.path.dirname(__file__), '..', '..')) |
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
88 ' skipping.' % exclude) | 89 ' skipping.' % exclude) |
89 exclude = exclude_dirname | 90 exclude = exclude_dirname |
90 known_incompatible.append(os.path.normpath(os.path.join(path, exclude))) | 91 known_incompatible.append(os.path.normpath(os.path.join(path, exclude))) |
91 known_incompatible = frozenset(known_incompatible) | 92 known_incompatible = frozenset(known_incompatible) |
92 return incompatible_directories.difference(known_incompatible) | 93 return incompatible_directories.difference(known_incompatible) |
93 | 94 |
94 | 95 |
95 class ScanResult(object): | 96 class ScanResult(object): |
96 Ok, Warnings, Errors = range(3) | 97 Ok, Warnings, Errors = range(3) |
97 | 98 |
| 99 # Needs to be a top-level function for multiprocessing |
| 100 def _FindCopyrights(files_to_scan): |
| 101 args = [os.path.join('android_webview', 'tools', 'find_copyrights.pl')] |
| 102 p = subprocess.Popen( |
| 103 args=args, cwd=REPOSITORY_ROOT, |
| 104 stdin=subprocess.PIPE, stdout=subprocess.PIPE) |
| 105 lines = p.communicate(files_to_scan)[0].splitlines() |
| 106 |
| 107 offending_files = [] |
| 108 allowed_copyrights = '^(?:\*No copyright\*' \ |
| 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 |
98 def _CheckLicenseHeaders(excluded_dirs_list, whitelisted_files): | 136 def _CheckLicenseHeaders(excluded_dirs_list, whitelisted_files): |
99 """Checks that all files which are not in a listed third-party directory, | 137 """Checks that all files which are not in a listed third-party directory, |
100 and which do not use the standard Chromium license, are whitelisted. | 138 and which do not use the standard Chromium license, are whitelisted. |
101 Args: | 139 Args: |
102 excluded_dirs_list: The list of directories to exclude from scanning. | 140 excluded_dirs_list: The list of directories to exclude from scanning. |
103 whitelisted_files: The whitelist of files. | 141 whitelisted_files: The whitelist of files. |
104 Returns: | 142 Returns: |
105 ScanResult.Ok if all files with non-standard license headers are whitelisted | 143 ScanResult.Ok if all files with non-standard license headers are whitelisted |
106 and the whitelist contains no stale entries; | 144 and the whitelist contains no stale entries; |
107 ScanResult.Warnings if there are stale entries; | 145 ScanResult.Warnings if there are stale entries; |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
140 excluded_dirs_list.append('tools/histograms') | 178 excluded_dirs_list.append('tools/histograms') |
141 # Swarming tools, doesn't exist in the snapshot | 179 # Swarming tools, doesn't exist in the snapshot |
142 excluded_dirs_list.append('tools/swarming_client') | 180 excluded_dirs_list.append('tools/swarming_client') |
143 # Arm sysroot tools, doesn't exist in the snapshot | 181 # Arm sysroot tools, doesn't exist in the snapshot |
144 excluded_dirs_list.append('arm-sysroot') | 182 excluded_dirs_list.append('arm-sysroot') |
145 # Data is not part of open source chromium, but are included on some bots. | 183 # Data is not part of open source chromium, but are included on some bots. |
146 excluded_dirs_list.append('data') | 184 excluded_dirs_list.append('data') |
147 # This is not part of open source chromium, but are included on some bots. | 185 # This is not part of open source chromium, but are included on some bots. |
148 excluded_dirs_list.append('skia/tools/clusterfuzz-data') | 186 excluded_dirs_list.append('skia/tools/clusterfuzz-data') |
149 | 187 |
150 args = ['android_webview/tools/find_copyrights.pl', | 188 args = [os.path.join('android_webview', 'tools', 'find_files.pl'), |
151 '.' | 189 '.' |
152 ] + excluded_dirs_list | 190 ] + excluded_dirs_list |
153 p = subprocess.Popen(args=args, cwd=REPOSITORY_ROOT, stdout=subprocess.PIPE) | 191 p = subprocess.Popen(args=args, cwd=REPOSITORY_ROOT, stdout=subprocess.PIPE) |
154 lines = p.communicate()[0].splitlines() | 192 files_to_scan = p.communicate()[0] |
155 | 193 |
156 offending_files = [] | 194 sharded_files_to_scan = _ShardString(files_to_scan, '\n', 2000) |
157 allowed_copyrights = '^(?:\*No copyright\*' \ | 195 pool = multiprocessing.Pool() |
158 '|20[0-9][0-9](?:-20[0-9][0-9])? The Chromium Authors\. ' \ | 196 offending_files_chunks = pool.map_async( |
159 'All rights reserved.*)$' | 197 _FindCopyrights, sharded_files_to_scan).get(999999) |
160 allowed_copyrights_re = re.compile(allowed_copyrights) | 198 pool.close() |
161 for l in lines: | 199 pool.join() |
162 entries = l.split('\t') | 200 # Flatten out the result |
163 if entries[1] == "GENERATED FILE": | 201 offending_files = \ |
164 continue | 202 [item for sublist in offending_files_chunks for item in sublist] |
165 copyrights = entries[1].split(' / ') | |
166 for c in copyrights: | |
167 if c and not allowed_copyrights_re.match(c): | |
168 offending_files.append(os.path.normpath(entries[0])) | |
169 break | |
170 | 203 |
171 unknown = set(offending_files) - set(whitelisted_files) | 204 unknown = set(offending_files) - set(whitelisted_files) |
172 if unknown: | 205 if unknown: |
173 print 'The following files contain a third-party license but are not in ' \ | 206 print 'The following files contain a third-party license but are not in ' \ |
174 'a listed third-party directory and are not whitelisted. You must ' \ | 207 'a listed third-party directory and are not whitelisted. You must ' \ |
175 'add the following files to the whitelist.\n%s' % \ | 208 'add the following files to the whitelist.\n%s' % \ |
176 '\n'.join(sorted(unknown)) | 209 '\n'.join(sorted(unknown)) |
177 | 210 |
178 stale = set(whitelisted_files) - set(offending_files) | 211 stale = set(whitelisted_files) - set(offending_files) |
179 if stale: | 212 if stale: |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
337 return ScanResult.Ok | 370 return ScanResult.Ok |
338 elif args[0] == 'incompatible_directories': | 371 elif args[0] == 'incompatible_directories': |
339 return _ProcessIncompatibleResult(GetUnknownIncompatibleDirectories()) | 372 return _ProcessIncompatibleResult(GetUnknownIncompatibleDirectories()) |
340 elif args[0] == 'all_incompatible_directories': | 373 elif args[0] == 'all_incompatible_directories': |
341 return _ProcessIncompatibleResult(GetIncompatibleDirectories()) | 374 return _ProcessIncompatibleResult(GetIncompatibleDirectories()) |
342 parser.print_help() | 375 parser.print_help() |
343 return ScanResult.Errors | 376 return ScanResult.Errors |
344 | 377 |
345 if __name__ == '__main__': | 378 if __name__ == '__main__': |
346 sys.exit(main()) | 379 sys.exit(main()) |
OLD | NEW |