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

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

Issue 10816041: Add a tool to check license compatibility with Android (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebased Created 8 years, 4 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 | Annotate | Revision Log
« no previous file with comments | « android_webview/tools/third_party_files_whitelist.txt ('k') | chrome/version.h.in » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #!/usr/bin/python
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
4 # found in the LICENSE file.
5
6 """Checks third-party licenses for the purposes of the Android WebView build.
7
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
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.
12
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,
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.
17 """
18
19 import optparse
20 import os
21 import re
22 import subprocess
23 import sys
24 import textwrap
25
26
27 REPOSITORY_ROOT = os.path.abspath(os.path.join(
28 os.path.dirname(__file__), '..', '..'))
29
30 sys.path.append(os.path.join(REPOSITORY_ROOT, 'tools'))
31 import licenses
32
33
34 def _CheckLicenseHeaders(directory_list, whitelisted_files):
35 """Checks that all files which are not in a listed third-party directory,
36 and which do not use the standard Chromium license, are listed.
Nico 2012/08/09 15:02:29 s/are listed/are whitelisted/?
Steve Block 2012/08/09 17:24:59 Done.
37 Args:
38 directory_list: The list of directories.
39 whitelisted_files: The list of files.
40 Returns:
41 True if all files with non-standard license headers are listed and the
42 file list contains no stale entries, otherwise false.
43 """
44
45 # Matches one of ...
46 # - '[Cc]opyright', but not when followed by
47 # ' (c) 20[0-9][0-9] The Chromium Authors.', with optional date range
48 # - '([Cc]) (19|20)[0-9][0-9]', but not when preceeded by the word copyright,
49 # as this is handled above
50 regex = '[Cc]opyright(?!( \(c\))? 20[0-9][0-9](-20[01][0189])? ' \
Nico 2012/08/09 15:02:29 Why not 20[0-9][0-9] for the second year in a rang
Steve Block 2012/08/09 17:24:59 Done.
51 'The Chromium Authors\. All rights reserved\.)' \
52 '|' \
53 '(?<!(pyright |opyright))\([Cc]\) (19|20)[0-9][0-9]'
54
55 args = ['grep',
56 '-rPlI',
57 '--exclude-dir', 'third_party',
58 '--exclude-dir', 'out',
59 '--exclude-dir', '.git',
60 regex,
61 '.']
Nico 2012/08/09 15:02:29 Do you set / check the cwd anywhere?
Steve Block 2012/08/09 17:24:59 I set the cwd on the line below. But it turns out
62 p = subprocess.Popen(args=args, cwd=REPOSITORY_ROOT, stdout=subprocess.PIPE)
63 files = p.communicate()[0].splitlines()
64
65 # Exclude files under listed directories and some known offendors.
Nico 2012/08/09 15:02:29 "offenders" I think
Steve Block 2012/08/09 17:24:59 Done.
66 offending_files = []
67 for x in files:
68 x = os.path.normpath(x)
69 is_in_listed_directory = False
70 for y in directory_list:
71 if x.startswith(y):
72 is_in_listed_directory = True
73 break
74 if (not is_in_listed_directory
75 # Ignore these tools.
76 and not x.startswith('android_webview/tools/')
77 # This is a build intermediate directory.
78 and not x.startswith('chrome/app/theme/google_chrome/')
79 # This is a test output directory.
80 and not x.startswith('data/page_cycler/')
81 # 'Copyright' appears in strings.
82 and not x.startswith('chrome/app/resources/')):
Nico 2012/08/09 15:02:29 nit: Could you instead say directory_list = dir
Steve Block 2012/08/09 17:24:59 Done.
83 offending_files += [x]
Nico 2012/08/09 15:02:29 .append
Steve Block 2012/08/09 17:24:59 Done.
84
85 result = True
Nico 2012/08/09 15:02:29 s/result/all_files_valid/
Steve Block 2012/08/09 17:24:59 Done.
86 unknown = set(offending_files) - set(whitelisted_files)
87 if unknown:
88 print 'The following files contain a third-party license but are not in ' \
89 'a listed third-party directory and are not whitelisted. You must ' \
90 'add the following files to the whitelist.\n%s' % \
91 '\n'.join(sorted(unknown))
92 result = False
93
94 stale = set(whitelisted_files) - set(offending_files)
95 if stale:
96 print 'The following files are whitelisted unnecessarily. You must ' \
97 ' remove the following files from the whitelist list.\n%s' % \
Nico 2012/08/09 15:02:29 s/whitelist list/whitelist/
Steve Block 2012/08/09 17:24:59 Done.
98 '\n'.join(sorted(stale))
99 result = False
100
101 return result
102
103
104 def _ReadFile(path):
105 """Reads a file from disk.
106 Args:
107 path: The path of the file to read, relative to the root of the repository.
108 Returns:
109 The contents of the file as a string.
110 """
111
112 with file(os.path.join(REPOSITORY_ROOT, path), 'r') as f:
113 lines = f.read()
Nico 2012/08/09 15:02:29 fyi: if you write return open(...path...).read(
Steve Block 2012/08/09 17:24:59 Done.
114 return lines
115
116
117 def _Scan():
118 """Checks that license meta-data is present for all third-party code.
119 Returns:
120 Whether the check succeeded.
121 """
122
123 third_party_dirs = _FindThirdPartyDirs()
124
125 # First, check designated third-party directories using src/tools/licenses.py.
126 result = True
Nico 2012/08/09 15:02:29 s/result/all_licenses_valid/
Steve Block 2012/08/09 17:24:59 Done.
127 for path in sorted(third_party_dirs):
128 try:
129 licenses.ParseDir(path)
130 except licenses.LicenseError, e:
131 print 'Got LicenseError "%s" while scanning %s' % (e, path)
132 result = False
133
134 # Second, check for non-standard license text.
135 files_data = _ReadFile(os.path.join('android_webview', 'tools',
136 'third_party_files_whitelist.txt'))
137 whitelisted_files = []
138 for line in files_data.splitlines():
139 match = re.match(r'([^#\s]*)', line)
140 if match and not len(match.group(1)) == 0:
Nico 2012/08/09 15:02:29 if you say + instead of *, you don't need the seco
Steve Block 2012/08/09 17:24:59 Done.
141 whitelisted_files += [match.group(1)]
Nico 2012/08/09 15:02:29 append
Steve Block 2012/08/09 17:24:59 Done.
142 return _CheckLicenseHeaders(third_party_dirs, whitelisted_files) and result
143
144
145 def _FindThirdPartyDirs():
Nico 2012/08/09 15:02:29 Move above _Scan
Steve Block 2012/08/09 17:24:59 Done.
146 """Gets the list of third-party directories.
147 Returns:
148 The list of third-party directories.
149 """
150
151 prune_paths = [
152 # Placeholder directory, no third-party code.
153 os.path.join('third_party', 'adobe'),
154 # Apache 2.0 license. See
155 # https://code.google.com/p/chromium/issues/detail?id=140478.
156 os.path.join('third_party', 'bidichecker'),
157 ]
158 return licenses.FindThirdPartyDirs(prune_paths)
159
160
161 def _GenerateNoticeFile(print_warnings):
162 """Generates the contents of an Android NOTICE file for the third-party code.
163 Args:
164 print_warnings: Whether to print warnings.
165 Returns:
166 The contents of the NOTICE file.
167 """
168
169 third_party_dirs = _FindThirdPartyDirs()
170
171 # Don't forget Chromium's LICENSE file
172 content = [_ReadFile('LICENSE')]
173
174 # We provide attribution for all third-party directories.
175 # TODO(steveblock): Limit this to only code used by the WebView binary.
176 for directory in third_party_dirs:
177 content += [_ReadFile(licenses.ParseDir(directory)['License File'])]
178
179 return '\n'.join(content)
180
181
182 def main():
183 class IndentedHelpFormatterWithNL(optparse.IndentedHelpFormatter):
184 def format_description(self, description):
185 if not description: return ""
186 desc_width = self.width - self.current_indent
187 indent = " "*self.current_indent
188 bits = description.split('\n')
189 formatted_bits = [
190 textwrap.fill(bit,
191 desc_width,
192 initial_indent=indent,
193 subsequent_indent=indent)
194 for bit in bits]
195 result = '\n'.join(formatted_bits) + '\n'
196 return result
Nico 2012/08/09 15:02:29 Do you need this? If so, adjust coding style (it k
Steve Block 2012/08/09 17:24:59 I found the snippet on a forum. I meant to clean i
197
198 parser = optparse.OptionParser(formatter=IndentedHelpFormatterWithNL(),
199 usage='%prog [options]')
200 parser.description = (__doc__ +
201 '\nCommands:\n' \
202 ' scan Check licenses.\n' \
203 ' notice Generate Android NOTICE file on stdout')
204 (options, args) = parser.parse_args()
205 if len(args) != 1:
206 parser.print_help()
207 return 1
208
209 if args[0] == 'scan':
210 if _Scan():
211 print 'OK!'
212 return 0
213 else:
214 return 1
215 elif args[0] == 'notice':
216 print _GenerateNoticeFile(print_warnings=False)
217 return 0
218
219 parser.print_help()
220 return 1
221
222 if __name__ == '__main__':
223 sys.exit(main())
OLDNEW
« no previous file with comments | « android_webview/tools/third_party_files_whitelist.txt ('k') | chrome/version.h.in » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698