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 |
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
67 if metadata.get('License Android Compatible', 'no') == 'yes': | 67 if metadata.get('License Android Compatible', 'no') == 'yes': |
68 continue | 68 continue |
69 license = re.split(' [Ll]icenses?$', metadata['License'])[0] | 69 license = re.split(' [Ll]icenses?$', metadata['License'])[0] |
70 tokens = [x.strip() for x in re.split(' and |,', license) if len(x) > 0] | 70 tokens = [x.strip() for x in re.split(' and |,', license) if len(x) > 0] |
71 for token in tokens: | 71 for token in tokens: |
72 if not re.match(regex, token, re.IGNORECASE): | 72 if not re.match(regex, token, re.IGNORECASE): |
73 result.append(directory) | 73 result.append(directory) |
74 break | 74 break |
75 return result | 75 return result |
76 | 76 |
| 77 class ScanResult(object): |
| 78 Ok, Warnings, Errors = range(3) |
77 | 79 |
78 def _CheckLicenseHeaders(excluded_dirs_list, whitelisted_files): | 80 def _CheckLicenseHeaders(excluded_dirs_list, whitelisted_files): |
79 """Checks that all files which are not in a listed third-party directory, | 81 """Checks that all files which are not in a listed third-party directory, |
80 and which do not use the standard Chromium license, are whitelisted. | 82 and which do not use the standard Chromium license, are whitelisted. |
81 Args: | 83 Args: |
82 excluded_dirs_list: The list of directories to exclude from scanning. | 84 excluded_dirs_list: The list of directories to exclude from scanning. |
83 whitelisted_files: The whitelist of files. | 85 whitelisted_files: The whitelist of files. |
84 Returns: | 86 Returns: |
85 True if all files with non-standard license headers are whitelisted and the | 87 ScanResult.Ok if all files with non-standard license headers are whitelisted |
86 whitelist contains no stale entries, otherwise false. | 88 and the whitelist contains no stale entries; |
| 89 ScanResult.Warnings if there are stale entries; |
| 90 ScanResult.Errors if new non-whitelisted entries found. |
87 """ | 91 """ |
88 | 92 |
89 excluded_dirs_list = [d for d in excluded_dirs_list if not 'third_party' in d] | 93 excluded_dirs_list = [d for d in excluded_dirs_list if not 'third_party' in d] |
90 # Using a commond pattern for third-partyies makes the ignore regexp shorter | 94 # Using a commond pattern for third-partyies makes the ignore regexp shorter |
91 excluded_dirs_list.append('third_party') | 95 excluded_dirs_list.append('third_party') |
92 # VCS dirs | 96 # VCS dirs |
93 excluded_dirs_list.append('.git') | 97 excluded_dirs_list.append('.git') |
94 excluded_dirs_list.append('.svn') | 98 excluded_dirs_list.append('.svn') |
95 # Build output | 99 # Build output |
96 excluded_dirs_list.append('out/Debug') | 100 excluded_dirs_list.append('out/Debug') |
(...skipping 25 matching lines...) Expand all Loading... |
122 for l in lines: | 126 for l in lines: |
123 entries = l.split('\t') | 127 entries = l.split('\t') |
124 if entries[1] == "GENERATED FILE": | 128 if entries[1] == "GENERATED FILE": |
125 continue | 129 continue |
126 copyrights = entries[1].split(' / ') | 130 copyrights = entries[1].split(' / ') |
127 for c in copyrights: | 131 for c in copyrights: |
128 if c and not allowed_copyrights_re.match(c): | 132 if c and not allowed_copyrights_re.match(c): |
129 offending_files.append(os.path.normpath(entries[0])) | 133 offending_files.append(os.path.normpath(entries[0])) |
130 break | 134 break |
131 | 135 |
132 all_files_valid = True | |
133 unknown = set(offending_files) - set(whitelisted_files) | 136 unknown = set(offending_files) - set(whitelisted_files) |
134 if unknown: | 137 if unknown: |
135 print 'The following files contain a third-party license but are not in ' \ | 138 print 'The following files contain a third-party license but are not in ' \ |
136 'a listed third-party directory and are not whitelisted. You must ' \ | 139 'a listed third-party directory and are not whitelisted. You must ' \ |
137 'add the following files to the whitelist.\n%s' % \ | 140 'add the following files to the whitelist.\n%s' % \ |
138 '\n'.join(sorted(unknown)) | 141 '\n'.join(sorted(unknown)) |
139 all_files_valid = False | |
140 | 142 |
141 stale = set(whitelisted_files) - set(offending_files) | 143 stale = set(whitelisted_files) - set(offending_files) |
142 if stale: | 144 if stale: |
143 print 'The following files are whitelisted unnecessarily. You must ' \ | 145 print 'The following files are whitelisted unnecessarily. You must ' \ |
144 ' remove the following files from the whitelist.\n%s' % \ | 146 ' remove the following files from the whitelist.\n%s' % \ |
145 '\n'.join(sorted(stale)) | 147 '\n'.join(sorted(stale)) |
146 all_files_valid = False | |
147 | 148 |
148 return all_files_valid | 149 if unknown: |
| 150 return ScanResult.Errors |
| 151 elif stale: |
| 152 return ScanResult.Warnings |
| 153 else: |
| 154 return ScanResult.Ok |
149 | 155 |
150 | 156 |
151 def _ReadFile(path): | 157 def _ReadFile(path): |
152 """Reads a file from disk. | 158 """Reads a file from disk. |
153 Args: | 159 Args: |
154 path: The path of the file to read, relative to the root of the repository. | 160 path: The path of the file to read, relative to the root of the repository. |
155 Returns: | 161 Returns: |
156 The contents of the file as a string. | 162 The contents of the file as a string. |
157 """ | 163 """ |
158 | 164 |
(...skipping 20 matching lines...) Expand all Loading... |
179 # The llvm-build doesn't exist for non-clang builder | 185 # The llvm-build doesn't exist for non-clang builder |
180 os.path.join('third_party', 'llvm-build'), | 186 os.path.join('third_party', 'llvm-build'), |
181 # Binaries doesn't apply to android | 187 # Binaries doesn't apply to android |
182 os.path.join('third_party', 'widevine'), | 188 os.path.join('third_party', 'widevine'), |
183 ] | 189 ] |
184 third_party_dirs = licenses.FindThirdPartyDirs(prune_paths, REPOSITORY_ROOT) | 190 third_party_dirs = licenses.FindThirdPartyDirs(prune_paths, REPOSITORY_ROOT) |
185 return licenses.FilterDirsWithFiles(third_party_dirs, REPOSITORY_ROOT) | 191 return licenses.FilterDirsWithFiles(third_party_dirs, REPOSITORY_ROOT) |
186 | 192 |
187 | 193 |
188 def _Scan(): | 194 def _Scan(): |
189 """Checks that license meta-data is present for all third-party code. | 195 """Checks that license meta-data is present for all third-party code and |
| 196 that all non third-party code doesn't contain external copyrighted code. |
190 Returns: | 197 Returns: |
191 Whether the check succeeded. | 198 ScanResult.Ok if everything is in order; |
| 199 ScanResult.Warnings if there are non-fatal problems (e.g. stale whitelist |
| 200 entries) |
| 201 ScanResult.Errors otherwise. |
192 """ | 202 """ |
193 | 203 |
194 third_party_dirs = _FindThirdPartyDirs() | 204 third_party_dirs = _FindThirdPartyDirs() |
195 | 205 |
196 # First, check designated third-party directories using src/tools/licenses.py. | 206 # First, check designated third-party directories using src/tools/licenses.py. |
197 all_licenses_valid = True | 207 all_licenses_valid = True |
198 for path in sorted(third_party_dirs): | 208 for path in sorted(third_party_dirs): |
199 try: | 209 try: |
200 licenses.ParseDir(path, REPOSITORY_ROOT) | 210 licenses.ParseDir(path, REPOSITORY_ROOT) |
201 except licenses.LicenseError, e: | 211 except licenses.LicenseError, e: |
202 if not (path in known_issues.KNOWN_ISSUES): | 212 if not (path in known_issues.KNOWN_ISSUES): |
203 print 'Got LicenseError "%s" while scanning %s' % (e, path) | 213 print 'Got LicenseError "%s" while scanning %s' % (e, path) |
204 all_licenses_valid = False | 214 all_licenses_valid = False |
205 | 215 |
206 # Second, check for non-standard license text. | 216 # Second, check for non-standard license text. |
207 files_data = _ReadFile(os.path.join('android_webview', 'tools', | 217 files_data = _ReadFile(os.path.join('android_webview', 'tools', |
208 'third_party_files_whitelist.txt')) | 218 'third_party_files_whitelist.txt')) |
209 whitelisted_files = [] | 219 whitelisted_files = [] |
210 for line in files_data.splitlines(): | 220 for line in files_data.splitlines(): |
211 match = re.match(r'([^#\s]+)', line) | 221 match = re.match(r'([^#\s]+)', line) |
212 if match: | 222 if match: |
213 whitelisted_files.append(match.group(1)) | 223 whitelisted_files.append(match.group(1)) |
214 return _CheckLicenseHeaders(third_party_dirs, whitelisted_files) \ | 224 licenses_check = _CheckLicenseHeaders(third_party_dirs, whitelisted_files) |
215 and all_licenses_valid | 225 |
| 226 return licenses_check if all_licenses_valid else ScanResult.Errors |
216 | 227 |
217 | 228 |
218 def GenerateNoticeFile(): | 229 def GenerateNoticeFile(): |
219 """Generates the contents of an Android NOTICE file for the third-party code. | 230 """Generates the contents of an Android NOTICE file for the third-party code. |
220 This is used by the snapshot tool. | 231 This is used by the snapshot tool. |
221 Returns: | 232 Returns: |
222 The contents of the NOTICE file. | 233 The contents of the NOTICE file. |
223 """ | 234 """ |
224 | 235 |
225 third_party_dirs = _FindThirdPartyDirs() | 236 third_party_dirs = _FindThirdPartyDirs() |
(...skipping 22 matching lines...) Expand all Loading... |
248 | 259 |
249 parser = optparse.OptionParser(formatter=FormatterWithNewLines(), | 260 parser = optparse.OptionParser(formatter=FormatterWithNewLines(), |
250 usage='%prog [options]') | 261 usage='%prog [options]') |
251 parser.description = (__doc__ + | 262 parser.description = (__doc__ + |
252 '\nCommands:\n' \ | 263 '\nCommands:\n' \ |
253 ' scan Check licenses.\n' \ | 264 ' scan Check licenses.\n' \ |
254 ' notice Generate Android NOTICE file on stdout') | 265 ' notice Generate Android NOTICE file on stdout') |
255 (options, args) = parser.parse_args() | 266 (options, args) = parser.parse_args() |
256 if len(args) != 1: | 267 if len(args) != 1: |
257 parser.print_help() | 268 parser.print_help() |
258 return 1 | 269 return ScanResult.Errors |
259 | 270 |
260 if args[0] == 'scan': | 271 if args[0] == 'scan': |
261 if _Scan(): | 272 scan_result = _Scan() |
| 273 if scan_result == ScanResult.Ok: |
262 print 'OK!' | 274 print 'OK!' |
263 return 0 | 275 return scan_result |
264 else: | |
265 return 1 | |
266 elif args[0] == 'notice': | 276 elif args[0] == 'notice': |
267 print GenerateNoticeFile() | 277 print GenerateNoticeFile() |
268 return 0 | 278 return ScanResult.Ok |
269 | 279 |
270 parser.print_help() | 280 parser.print_help() |
271 return 1 | 281 return ScanResult.Errors |
272 | 282 |
273 if __name__ == '__main__': | 283 if __name__ == '__main__': |
274 sys.exit(main()) | 284 sys.exit(main()) |
OLD | NEW |