| 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 |
| (...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 144 "URL": "http://www.strongtalk.org/", | 144 "URL": "http://www.strongtalk.org/", |
| 145 "License File": "/v8/LICENSE.strongtalk", | 145 "License File": "/v8/LICENSE.strongtalk", |
| 146 }, | 146 }, |
| 147 } | 147 } |
| 148 | 148 |
| 149 class LicenseError(Exception): | 149 class LicenseError(Exception): |
| 150 """We raise this exception when a directory's licensing info isn't | 150 """We raise this exception when a directory's licensing info isn't |
| 151 fully filled out.""" | 151 fully filled out.""" |
| 152 pass | 152 pass |
| 153 | 153 |
| 154 def AbsolutePath(path, filename): |
| 155 """Convert a path in README.chromium to be absolute based on the source |
| 156 root.""" |
| 157 if filename.startswith('/'): |
| 158 # Absolute-looking paths are relative to the source root |
| 159 # (which is the directory we're run from). |
| 160 absolute_path = os.path.join(os.getcwd(), filename[1:]) |
| 161 else: |
| 162 absolute_path = os.path.join(path, filename) |
| 163 if os.path.exists(absolute_path): |
| 164 return absolute_path |
| 165 return None |
| 154 | 166 |
| 155 def ParseDir(path): | 167 def ParseDir(path): |
| 156 """Examine a third_party/foo component and extract its metadata.""" | 168 """Examine a third_party/foo component and extract its metadata.""" |
| 157 | 169 |
| 158 # Parse metadata fields out of README.chromium. | 170 # Parse metadata fields out of README.chromium. |
| 159 # We examine "LICENSE" for the license file by default. | 171 # We examine "LICENSE" for the license file by default. |
| 160 metadata = { | 172 metadata = { |
| 161 "License File": "LICENSE", # Relative path to license text. | 173 "License File": "LICENSE", # Relative path to license text. |
| 162 "Name": None, # Short name (for header on about:credits). | 174 "Name": None, # Short name (for header on about:credits). |
| 163 "URL": None, # Project home page. | 175 "URL": None, # Project home page. |
| 164 } | 176 } |
| 165 | 177 |
| 178 # Relative path to a file containing some html we're required to place in |
| 179 # about:credits. |
| 180 optional_keys = ["Required Text"] |
| 181 |
| 166 if path in SPECIAL_CASES: | 182 if path in SPECIAL_CASES: |
| 167 metadata.update(SPECIAL_CASES[path]) | 183 metadata.update(SPECIAL_CASES[path]) |
| 168 else: | 184 else: |
| 169 # Try to find README.chromium. | 185 # Try to find README.chromium. |
| 170 readme_path = os.path.join(path, 'README.chromium') | 186 readme_path = os.path.join(path, 'README.chromium') |
| 171 if not os.path.exists(readme_path): | 187 if not os.path.exists(readme_path): |
| 172 raise LicenseError("missing README.chromium") | 188 raise LicenseError("missing README.chromium") |
| 173 | 189 |
| 174 for line in open(readme_path): | 190 for line in open(readme_path): |
| 175 line = line.strip() | 191 line = line.strip() |
| 176 if not line: | 192 if not line: |
| 177 break | 193 break |
| 178 for key in metadata.keys(): | 194 for key in metadata.keys() + optional_keys: |
| 179 field = key + ": " | 195 field = key + ": " |
| 180 if line.startswith(field): | 196 if line.startswith(field): |
| 181 metadata[key] = line[len(field):] | 197 metadata[key] = line[len(field):] |
| 182 | 198 |
| 183 # Check that all expected metadata is present. | 199 # Check that all expected metadata is present. |
| 184 for key, value in metadata.iteritems(): | 200 for key, value in metadata.iteritems(): |
| 185 if not value: | 201 if not value: |
| 186 raise LicenseError("couldn't find '" + key + "' line " | 202 raise LicenseError("couldn't find '" + key + "' line " |
| 187 "in README.chromium or licences.py " | 203 "in README.chromium or licences.py " |
| 188 "SPECIAL_CASES") | 204 "SPECIAL_CASES") |
| 189 | 205 |
| 190 # Check that the license file exists. | 206 # Check that the license file exists. |
| 191 for filename in (metadata["License File"], "COPYING"): | 207 for filename in (metadata["License File"], "COPYING"): |
| 192 if filename.startswith('/'): | 208 license_path = AbsolutePath(path, filename) |
| 193 # Absolute-looking paths are relative to the source root | 209 if license_path is not None: |
| 194 # (which is the directory we're run from). | |
| 195 license_path = os.path.join(os.getcwd(), filename[1:]) | |
| 196 else: | |
| 197 license_path = os.path.join(path, filename) | |
| 198 if os.path.exists(license_path): | |
| 199 metadata["License File"] = license_path | 210 metadata["License File"] = license_path |
| 200 break | 211 break |
| 201 license_path = None | |
| 202 | 212 |
| 203 if not license_path: | 213 if not license_path: |
| 204 raise LicenseError("License file not found. " | 214 raise LicenseError("License file not found. " |
| 205 "Either add a file named LICENSE, " | 215 "Either add a file named LICENSE, " |
| 206 "import upstream's COPYING if available, " | 216 "import upstream's COPYING if available, " |
| 207 "or add a 'License File:' line to README.chromium " | 217 "or add a 'License File:' line to README.chromium " |
| 208 "with the appropriate path.") | 218 "with the appropriate path.") |
| 209 | 219 |
| 220 if "Required Text" in metadata: |
| 221 required_path = AbsolutePath(path, metadata["Required Text"]) |
| 222 if required_path is not None: |
| 223 metadata["Required Text"] = required_path |
| 224 else: |
| 225 raise LicenseError("Required text file listed but not found.") |
| 226 |
| 210 return metadata | 227 return metadata |
| 211 | 228 |
| 212 | 229 |
| 213 def FindThirdPartyDirs(): | 230 def FindThirdPartyDirs(): |
| 214 """Find all third_party directories underneath the current directory.""" | 231 """Find all third_party directories underneath the current directory.""" |
| 215 third_party_dirs = [] | 232 third_party_dirs = [] |
| 216 for path, dirs, files in os.walk('.'): | 233 for path, dirs, files in os.walk('.'): |
| 217 path = path[len('./'):] # Pretty up the path. | 234 path = path[len('./'):] # Pretty up the path. |
| 218 | 235 |
| 219 if path in PRUNE_PATHS: | 236 if path in PRUNE_PATHS: |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 262 return len(errors) == 0 | 279 return len(errors) == 0 |
| 263 | 280 |
| 264 | 281 |
| 265 def GenerateCredits(): | 282 def GenerateCredits(): |
| 266 """Generate about:credits, dumping the result to stdout.""" | 283 """Generate about:credits, dumping the result to stdout.""" |
| 267 | 284 |
| 268 def EvaluateTemplate(template, env, escape=True): | 285 def EvaluateTemplate(template, env, escape=True): |
| 269 """Expand a template with variables like {{foo}} using a | 286 """Expand a template with variables like {{foo}} using a |
| 270 dictionary of expansions.""" | 287 dictionary of expansions.""" |
| 271 for key, val in env.items(): | 288 for key, val in env.items(): |
| 272 if escape: | 289 if escape and not key.endswith("_unescaped"): |
| 273 val = cgi.escape(val) | 290 val = cgi.escape(val) |
| 274 template = template.replace('{{%s}}' % key, val) | 291 template = template.replace('{{%s}}' % key, val) |
| 275 return template | 292 return template |
| 276 | 293 |
| 277 third_party_dirs = FindThirdPartyDirs() | 294 third_party_dirs = FindThirdPartyDirs() |
| 278 | 295 |
| 279 entry_template = open('chrome/browser/resources/about_credits_entry.tmpl', | 296 entry_template = open('chrome/browser/resources/about_credits_entry.tmpl', |
| 280 'rb').read() | 297 'rb').read() |
| 281 entries = [] | 298 entries = [] |
| 282 for path in sorted(third_party_dirs): | 299 for path in sorted(third_party_dirs): |
| 283 try: | 300 try: |
| 284 metadata = ParseDir(path) | 301 metadata = ParseDir(path) |
| 285 except LicenseError: | 302 except LicenseError: |
| 286 print >>sys.stderr, ("WARNING: licensing info for " + path + | 303 print >>sys.stderr, ("WARNING: licensing info for " + path + |
| 287 " is incomplete, skipping.") | 304 " is incomplete, skipping.") |
| 288 continue | 305 continue |
| 289 env = { | 306 env = { |
| 290 'name': metadata['Name'], | 307 'name': metadata['Name'], |
| 291 'url': metadata['URL'], | 308 'url': metadata['URL'], |
| 292 'license': open(metadata['License File'], 'rb').read(), | 309 'license': open(metadata['License File'], 'rb').read(), |
| 310 'license_unescaped': '', |
| 293 } | 311 } |
| 312 if 'Required Text' in metadata: |
| 313 required_text = open(metadata['Required Text'], 'rb').read() |
| 314 env["license_unescaped"] = required_text |
| 294 entries.append(EvaluateTemplate(entry_template, env)) | 315 entries.append(EvaluateTemplate(entry_template, env)) |
| 295 | 316 |
| 296 file_template = open('chrome/browser/resources/about_credits.tmpl', | 317 file_template = open('chrome/browser/resources/about_credits.tmpl', |
| 297 'rb').read() | 318 'rb').read() |
| 298 print "<!-- Generated by licenses.py; do not edit. -->" | 319 print "<!-- Generated by licenses.py; do not edit. -->" |
| 299 print EvaluateTemplate(file_template, {'entries': '\n'.join(entries)}, | 320 print EvaluateTemplate(file_template, {'entries': '\n'.join(entries)}, |
| 300 escape=False) | 321 escape=False) |
| 301 | 322 |
| 302 | 323 |
| 303 def main(): | 324 def main(): |
| 304 command = 'help' | 325 command = 'help' |
| 305 if len(sys.argv) > 1: | 326 if len(sys.argv) > 1: |
| 306 command = sys.argv[1] | 327 command = sys.argv[1] |
| 307 | 328 |
| 308 if command == 'scan': | 329 if command == 'scan': |
| 309 if not ScanThirdPartyDirs(): | 330 if not ScanThirdPartyDirs(): |
| 310 return 1 | 331 return 1 |
| 311 elif command == 'credits': | 332 elif command == 'credits': |
| 312 if not GenerateCredits(): | 333 if not GenerateCredits(): |
| 313 return 1 | 334 return 1 |
| 314 else: | 335 else: |
| 315 print __doc__ | 336 print __doc__ |
| 316 return 1 | 337 return 1 |
| 317 | 338 |
| 318 | 339 |
| 319 if __name__ == '__main__': | 340 if __name__ == '__main__': |
| 320 sys.exit(main()) | 341 sys.exit(main()) |
| OLD | NEW |