OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env 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 """Utility for checking and processing licensing information in third_party | 6 """Utility for checking and processing licensing information in third_party |
7 directories. | 7 directories. |
8 | 8 |
9 Usage: licenses.py <command> | 9 Usage: licenses.py <command> |
10 | 10 |
11 Commands: | 11 Commands: |
12 scan scan third_party directories, verifying that we have licensing info | 12 scan scan third_party directories, verifying that we have licensing info |
13 credits generate about:credits on stdout | 13 credits generate about:credits on stdout |
14 | 14 |
15 (You can also import this as a module.) | 15 (You can also import this as a module.) |
16 """ | 16 """ |
17 | 17 |
18 import cgi | 18 import cgi |
19 import os | 19 import os |
20 import sys | 20 import sys |
21 | 21 |
22 # Paths from the root of the tree to directories to skip. | 22 # Paths from the root of the tree to directories to skip. |
23 PRUNE_PATHS = set([ | 23 PRUNE_PATHS = set([ |
24 # Same module occurs in both the top-level third_party and others. | 24 # Same module occurs in crypto/third_party/nss and net/third_party/nss, so |
25 os.path.join('base','third_party','icu'), | 25 # skip this one. |
26 | |
27 # Assume for now that breakpad has their licensing in order. | |
28 os.path.join('breakpad'), | |
29 | |
30 # Assume for now that native client has their licensing in order. | |
31 os.path.join('native_client'), | |
32 | |
33 # Same module occurs in chrome/ and in net/, so skip one of them. | |
34 os.path.join('net','third_party','mozilla_security_manager'), | |
35 | |
36 # Same module occurs in base/, net/, and src/ so skip all but one of them. | |
37 os.path.join('third_party','nss'), | 26 os.path.join('third_party','nss'), |
38 os.path.join('net','third_party','nss'), | |
39 | 27 |
40 # Placeholder directory only, not third-party code. | 28 # Placeholder directory only, not third-party code. |
41 os.path.join('third_party','adobe'), | 29 os.path.join('third_party','adobe'), |
42 | 30 |
43 # Same license as Chromium. | |
44 os.path.join('third_party','lss'), | |
45 | |
46 # Only binaries, used during development. | 31 # Only binaries, used during development. |
47 os.path.join('third_party','valgrind'), | 32 os.path.join('third_party','valgrind'), |
48 | 33 |
49 # Directories that are the same as those in base/third_party. | |
50 os.path.join('v8','src','third_party','valgrind'), | |
51 | |
52 # Used for development and test, not in the shipping product. | 34 # Used for development and test, not in the shipping product. |
53 os.path.join('third_party','bidichecker'), | 35 os.path.join('third_party','bidichecker'), |
54 os.path.join('third_party','cygwin'), | 36 os.path.join('third_party','cygwin'), |
55 os.path.join('third_party','gold'), | 37 os.path.join('third_party','gold'), |
56 os.path.join('third_party','lighttpd'), | 38 os.path.join('third_party','lighttpd'), |
57 os.path.join('third_party','mingw-w64'), | 39 os.path.join('third_party','mingw-w64'), |
58 os.path.join('third_party','pefile'), | 40 os.path.join('third_party','pefile'), |
59 os.path.join('third_party','python_26'), | 41 os.path.join('third_party','python_26'), |
60 | 42 |
61 # Stuff pulled in from chrome-internal for official builds/tools. | 43 # Stuff pulled in from chrome-internal for official builds/tools. |
62 os.path.join('third_party', 'clear_cache'), | 44 os.path.join('third_party', 'clear_cache'), |
63 os.path.join('third_party', 'gnu'), | 45 os.path.join('third_party', 'gnu'), |
64 os.path.join('third_party', 'googlemac'), | 46 os.path.join('third_party', 'googlemac'), |
65 os.path.join('third_party', 'pcre'), | 47 os.path.join('third_party', 'pcre'), |
66 os.path.join('third_party', 'psutils'), | 48 os.path.join('third_party', 'psutils'), |
67 os.path.join('third_party', 'sawbuck'), | 49 os.path.join('third_party', 'sawbuck'), |
68 | 50 |
69 # Redistribution does not require attribution in documentation. | 51 # Redistribution does not require attribution in documentation. |
70 os.path.join('third_party','directxsdk'), | 52 os.path.join('third_party','directxsdk'), |
71 os.path.join('third_party','platformsdk_win2008_6_1'), | 53 os.path.join('third_party','platformsdk_win2008_6_1'), |
72 os.path.join('third_party','platformsdk_win7'), | 54 os.path.join('third_party','platformsdk_win7'), |
73 | |
74 # Harfbuzz-ng is not currently shipping in any product: | |
75 os.path.join('third_party','harfbuzz-ng'), | |
76 ]) | 55 ]) |
77 | 56 |
78 # Directories we don't scan through. | 57 # Directories we don't scan through. |
79 PRUNE_DIRS = ('.svn', '.git', # VCS metadata | 58 PRUNE_DIRS = ('.svn', '.git', # VCS metadata |
80 'out', 'Debug', 'Release', # build files | 59 'out', 'Debug', 'Release', # build files |
81 'layout_tests') # lots of subdirs | 60 'layout_tests') # lots of subdirs |
82 | 61 |
83 ADDITIONAL_PATHS = ( | 62 ADDITIONAL_PATHS = ( |
| 63 os.path.join('breakpad'), |
| 64 os.path.join('chrome', 'common', 'extensions', 'docs', 'examples'), |
| 65 os.path.join('chrome', 'test', 'chromeos', 'autotest'), |
| 66 os.path.join('chrome', 'test', 'data'), |
84 os.path.join('googleurl'), | 67 os.path.join('googleurl'), |
| 68 os.path.join('native_client'), |
85 os.path.join('native_client_sdk'), | 69 os.path.join('native_client_sdk'), |
| 70 os.path.join('net', 'tools', 'spdyshark'), |
86 os.path.join('ppapi'), | 71 os.path.join('ppapi'), |
| 72 os.path.join('sandbox', 'linux', 'seccomp-legacy'), |
| 73 os.path.join('sdch', 'open-vcdiff'), |
| 74 os.path.join('testing', 'gmock'), |
| 75 os.path.join('testing', 'gtest'), |
87 # The directory with the word list for Chinese and Japanese segmentation | 76 # The directory with the word list for Chinese and Japanese segmentation |
88 # with different license terms than ICU. | 77 # with different license terms than ICU. |
89 os.path.join('third_party','icu','source','data','brkitr'), | 78 os.path.join('third_party','icu','source','data','brkitr'), |
| 79 os.path.join('tools', 'grit'), |
| 80 os.path.join('tools', 'gyp'), |
| 81 os.path.join('tools', 'page_cycler', 'acid3'), |
| 82 os.path.join('v8'), |
90 # Fake directory so we can include the strongtalk license. | 83 # Fake directory so we can include the strongtalk license. |
91 os.path.join('v8', 'strongtalk'), | 84 os.path.join('v8', 'strongtalk'), |
92 ) | 85 ) |
93 | 86 |
94 | 87 |
95 # Directories where we check out directly from upstream, and therefore | 88 # Directories where we check out directly from upstream, and therefore |
96 # can't provide a README.chromium. Please prefer a README.chromium | 89 # can't provide a README.chromium. Please prefer a README.chromium |
97 # wherever possible. | 90 # wherever possible. |
98 SPECIAL_CASES = { | 91 SPECIAL_CASES = { |
99 'googleurl': { | 92 os.path.join('googleurl'): { |
100 "Name": "google-url", | 93 "Name": "google-url", |
101 "URL": "http://code.google.com/p/google-url/", | 94 "URL": "http://code.google.com/p/google-url/", |
102 "License": "BSD and MPL 1.1/GPL 2.0/LGPL 2.1", | 95 "License": "BSD and MPL 1.1/GPL 2.0/LGPL 2.1", |
103 "License File": "LICENSE.txt", | 96 "License File": "LICENSE.txt", |
104 }, | 97 }, |
| 98 os.path.join('native_client'): { |
| 99 "Name": "native client", |
| 100 "URL": "http://code.google.com/p/nativeclient", |
| 101 "License": "BSD", |
| 102 }, |
| 103 os.path.join('sandbox', 'linux', 'seccomp-legacy'): { |
| 104 "Name": "seccompsandbox", |
| 105 "URL": "http://code.google.com/p/seccompsandbox", |
| 106 "License": "BSD", |
| 107 }, |
| 108 os.path.join('sdch', 'open-vcdiff'): { |
| 109 "Name": "open-vcdiff", |
| 110 "URL": "http://code.google.com/p/open-vcdiff", |
| 111 "License": "Apache 2.0, MIT, GPL v2 and custom licenses", |
| 112 }, |
| 113 os.path.join('testing', 'gmock'): { |
| 114 "Name": "gmock", |
| 115 "URL": "http://code.google.com/p/googlemock", |
| 116 "License": "BSD", |
| 117 }, |
| 118 os.path.join('testing', 'gtest'): { |
| 119 "Name": "gtest", |
| 120 "URL": "http://code.google.com/p/googletest", |
| 121 "License": "BSD", |
| 122 }, |
105 os.path.join('third_party', 'angle'): { | 123 os.path.join('third_party', 'angle'): { |
106 "Name": "Almost Native Graphics Layer Engine", | 124 "Name": "Almost Native Graphics Layer Engine", |
107 "URL": "http://code.google.com/p/angleproject/", | 125 "URL": "http://code.google.com/p/angleproject/", |
108 "License": "BSD", | 126 "License": "BSD", |
109 }, | 127 }, |
110 os.path.join('third_party', 'cros_system_api'): { | 128 os.path.join('third_party', 'cros_system_api'): { |
111 "Name": "Chromium OS system API", | 129 "Name": "Chromium OS system API", |
112 "URL": "http://www.chromium.org/chromium-os", | 130 "URL": "http://www.chromium.org/chromium-os", |
113 "License": "BSD", | 131 "License": "BSD", |
114 # Absolute path here is resolved as relative to the source root. | 132 # Absolute path here is resolved as relative to the source root. |
115 "License File": "/LICENSE.chromium_os", | 133 "License File": "/LICENSE.chromium_os", |
116 }, | 134 }, |
117 os.path.join('third_party', 'GTM'): { | 135 os.path.join('third_party', 'GTM'): { |
118 "Name": "Google Toolbox for Mac", | 136 "Name": "Google Toolbox for Mac", |
119 "URL": "http://code.google.com/p/google-toolbox-for-mac/", | 137 "URL": "http://code.google.com/p/google-toolbox-for-mac/", |
120 "License": "Apache 2.0", | 138 "License": "Apache 2.0", |
121 "License File": "COPYING", | 139 "License File": "COPYING", |
122 }, | 140 }, |
123 os.path.join('third_party', 'lss'): { | 141 os.path.join('third_party', 'lss'): { |
124 "Name": "linux-syscall-support", | 142 "Name": "linux-syscall-support", |
125 "URL": "http://code.google.com/p/lss/", | 143 "URL": "http://code.google.com/p/lss/", |
| 144 "License": "BSD", |
| 145 "License File": "/LICENSE", |
126 }, | 146 }, |
127 os.path.join('third_party', 'ots'): { | 147 os.path.join('third_party', 'ots'): { |
128 "Name": "OTS (OpenType Sanitizer)", | 148 "Name": "OTS (OpenType Sanitizer)", |
129 "URL": "http://code.google.com/p/ots/", | 149 "URL": "http://code.google.com/p/ots/", |
130 "License": "BSD", | 150 "License": "BSD", |
131 }, | 151 }, |
132 os.path.join('third_party', 'pdfsqueeze'): { | 152 os.path.join('third_party', 'pdfsqueeze'): { |
133 "Name": "pdfsqueeze", | 153 "Name": "pdfsqueeze", |
134 "URL": "http://code.google.com/p/pdfsqueeze/", | 154 "URL": "http://code.google.com/p/pdfsqueeze/", |
135 "License": "Apache 2.0", | 155 "License": "Apache 2.0", |
(...skipping 23 matching lines...) Expand all Loading... |
159 "URL": "http://webkit.org/", | 179 "URL": "http://webkit.org/", |
160 "License": "BSD and GPL v2", | 180 "License": "BSD and GPL v2", |
161 # Absolute path here is resolved as relative to the source root. | 181 # Absolute path here is resolved as relative to the source root. |
162 "License File": "/webkit/LICENSE", | 182 "License File": "/webkit/LICENSE", |
163 }, | 183 }, |
164 os.path.join('third_party', 'webpagereplay'): { | 184 os.path.join('third_party', 'webpagereplay'): { |
165 "Name": "webpagereplay", | 185 "Name": "webpagereplay", |
166 "URL": "http://code.google.com/p/web-page-replay", | 186 "URL": "http://code.google.com/p/web-page-replay", |
167 "License": "Apache 2.0", | 187 "License": "Apache 2.0", |
168 }, | 188 }, |
| 189 os.path.join('tools', 'grit'): { |
| 190 "Name": "grit", |
| 191 "URL": "http://code.google.com/p/grit-i18n", |
| 192 "License": "BSD", |
| 193 }, |
| 194 os.path.join('tools', 'gyp'): { |
| 195 "Name": "gyp", |
| 196 "URL": "http://code.google.com/p/gyp", |
| 197 "License": "BSD", |
| 198 }, |
| 199 os.path.join('v8'): { |
| 200 "Name": "gyp", |
| 201 "URL": "http://code.google.com/p/v8", |
| 202 "License": "BSD", |
| 203 }, |
169 os.path.join('v8', 'strongtalk'): { | 204 os.path.join('v8', 'strongtalk'): { |
170 "Name": "Strongtalk", | 205 "Name": "Strongtalk", |
171 "URL": "http://www.strongtalk.org/", | 206 "URL": "http://www.strongtalk.org/", |
172 "License": "BSD", | 207 "License": "BSD", |
173 # Absolute path here is resolved as relative to the source root. | 208 # Absolute path here is resolved as relative to the source root. |
174 "License File": "/v8/LICENSE.strongtalk", | 209 "License File": "/v8/LICENSE.strongtalk", |
175 }, | 210 }, |
176 } | 211 } |
177 | 212 |
| 213 # Special value for 'License File' field used to indicate that the license file |
| 214 # should not be used in about:credits. |
| 215 NOT_SHIPPED = "NOT_SHIPPED" |
| 216 |
| 217 |
178 class LicenseError(Exception): | 218 class LicenseError(Exception): |
179 """We raise this exception when a directory's licensing info isn't | 219 """We raise this exception when a directory's licensing info isn't |
180 fully filled out.""" | 220 fully filled out.""" |
181 pass | 221 pass |
182 | 222 |
183 def AbsolutePath(path, filename): | 223 def AbsolutePath(path, filename): |
184 """Convert a path in README.chromium to be absolute based on the source | 224 """Convert a path in README.chromium to be absolute based on the source |
185 root.""" | 225 root.""" |
186 if filename.startswith('/'): | 226 if filename.startswith('/'): |
187 # Absolute-looking paths are relative to the source root | 227 # Absolute-looking paths are relative to the source root |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
227 if line.startswith(field): | 267 if line.startswith(field): |
228 metadata[key] = line[len(field):] | 268 metadata[key] = line[len(field):] |
229 | 269 |
230 # Check that all expected metadata is present. | 270 # Check that all expected metadata is present. |
231 for key, value in metadata.iteritems(): | 271 for key, value in metadata.iteritems(): |
232 if not value: | 272 if not value: |
233 raise LicenseError("couldn't find '" + key + "' line " | 273 raise LicenseError("couldn't find '" + key + "' line " |
234 "in README.chromium or licences.py " | 274 "in README.chromium or licences.py " |
235 "SPECIAL_CASES") | 275 "SPECIAL_CASES") |
236 | 276 |
237 # Check that the license file exists. | 277 # Special-case modules that aren't in the shipping product, so don't need |
238 for filename in (metadata["License File"], "COPYING"): | 278 # their license in about:credits. |
239 license_path = AbsolutePath(path, filename) | 279 if metadata["License File"] != NOT_SHIPPED: |
240 if license_path is not None: | 280 # Check that the license file exists. |
241 metadata["License File"] = license_path | 281 for filename in (metadata["License File"], "COPYING"): |
242 break | 282 license_path = AbsolutePath(path, filename) |
| 283 if license_path is not None: |
| 284 metadata["License File"] = license_path |
| 285 break |
243 | 286 |
244 if not license_path: | 287 if not license_path: |
245 raise LicenseError("License file not found. " | 288 raise LicenseError("License file not found. " |
246 "Either add a file named LICENSE, " | 289 "Either add a file named LICENSE, " |
247 "import upstream's COPYING if available, " | 290 "import upstream's COPYING if available, " |
248 "or add a 'License File:' line to README.chromium " | 291 "or add a 'License File:' line to " |
249 "with the appropriate path.") | 292 "README.chromium with the appropriate path.") |
250 | 293 |
251 if "Required Text" in metadata: | 294 if "Required Text" in metadata: |
252 required_path = AbsolutePath(path, metadata["Required Text"]) | 295 required_path = AbsolutePath(path, metadata["Required Text"]) |
253 if required_path is not None: | 296 if required_path is not None: |
254 metadata["Required Text"] = required_path | 297 metadata["Required Text"] = required_path |
255 else: | 298 else: |
256 raise LicenseError("Required text file listed but not found.") | 299 raise LicenseError("Required text file listed but not found.") |
257 | 300 |
258 return metadata | 301 return metadata |
259 | 302 |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
343 entry_template = open('chrome/browser/resources/about_credits_entry.tmpl', | 386 entry_template = open('chrome/browser/resources/about_credits_entry.tmpl', |
344 'rb').read() | 387 'rb').read() |
345 entries = [] | 388 entries = [] |
346 for path in sorted(third_party_dirs): | 389 for path in sorted(third_party_dirs): |
347 try: | 390 try: |
348 metadata = ParseDir(path) | 391 metadata = ParseDir(path) |
349 except LicenseError: | 392 except LicenseError: |
350 print >>sys.stderr, ("WARNING: licensing info for " + path + | 393 print >>sys.stderr, ("WARNING: licensing info for " + path + |
351 " is incomplete, skipping.") | 394 " is incomplete, skipping.") |
352 continue | 395 continue |
| 396 if metadata['License File'] == NOT_SHIPPED: |
| 397 print >>sys.stderr, ("Path " + path + " marked as " + NOT_SHIPPED + |
| 398 ", skipping.") |
| 399 continue |
353 env = { | 400 env = { |
354 'name': metadata['Name'], | 401 'name': metadata['Name'], |
355 'url': metadata['URL'], | 402 'url': metadata['URL'], |
356 'license': open(metadata['License File'], 'rb').read(), | 403 'license': open(metadata['License File'], 'rb').read(), |
357 'license_unescaped': '', | 404 'license_unescaped': '', |
358 } | 405 } |
359 if 'Required Text' in metadata: | 406 if 'Required Text' in metadata: |
360 required_text = open(metadata['Required Text'], 'rb').read() | 407 required_text = open(metadata['Required Text'], 'rb').read() |
361 env["license_unescaped"] = required_text | 408 env["license_unescaped"] = required_text |
362 entries.append(EvaluateTemplate(entry_template, env)) | 409 entries.append(EvaluateTemplate(entry_template, env)) |
(...skipping 16 matching lines...) Expand all Loading... |
379 elif command == 'credits': | 426 elif command == 'credits': |
380 if not GenerateCredits(): | 427 if not GenerateCredits(): |
381 return 1 | 428 return 1 |
382 else: | 429 else: |
383 print __doc__ | 430 print __doc__ |
384 return 1 | 431 return 1 |
385 | 432 |
386 | 433 |
387 if __name__ == '__main__': | 434 if __name__ == '__main__': |
388 sys.exit(main()) | 435 sys.exit(main()) |
OLD | NEW |