| #!/usr/bin/env python3 |
| # |
| # Copyright 2017 The Fuchsia Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| # A script to fetch missing LICENSE files when building a vendored repo |
| |
| # Should be run from top-level of third_party/rust_crates repository. |
| |
| import argparse |
| import json |
| import os |
| import re |
| import sys |
| import urllib.request |
| |
| repo_re = re.compile('\s*repository\s*=\s*"(.*)"\s*$') |
| |
| |
| def die(reason): |
| raise Exception(reason) |
| |
| |
| def get_repo_path(subdir): |
| for line in open(os.path.join(subdir, 'Cargo.toml')): |
| m = repo_re.match(line) |
| if m: |
| return m.group(1) |
| |
| |
| def find_github_blob_path(path): |
| s = path.split('/') |
| if s[2] == 'github.com': |
| s[2] = 'raw.githubusercontent.com' |
| # github redirects "github.com/$USER/$PROJECT.git" to |
| # "github.com/$USER/$PROJECT". |
| if s[4].endswith('.git'): |
| s[4] = s[4][:len('.git')] |
| else: |
| die('don\'t know raw content path for ' + path) |
| if s[-1] == '': |
| del s[-1] |
| if len(s) >= 6 and s[5] == 'tree': |
| del s[5] |
| else: |
| s.append('HEAD') |
| return '/'.join(s) |
| |
| |
| def fetch_license(subdir): |
| repo_path = get_repo_path(subdir) |
| if repo_path is None: |
| die('can\'t find repo path for ' + subdir) |
| base_path = find_github_blob_path(repo_path) |
| first = True |
| with open(os.path.join(subdir, 'LICENSE'), 'wb') as license_out: |
| # 'LICENCE' is a British English spelling variant used in https://github.com/ebarnard/rust-plist |
| for filename in ('LICENSE', 'LICENSE-APACHE', 'LICENSE-MIT', 'COPYING', |
| 'LICENCE', 'LICENSE.md'): |
| url = '/'.join((base_path, filename)) |
| try: |
| with urllib.request.urlopen(url) as resp: |
| if first: |
| first = False |
| else: |
| license_out.write(bytearray('=' * 40 + '\n', 'utf-8')) |
| license_out.write(bytearray(url + ':\n\n', 'utf-8')) |
| license_out.write(resp.read()) |
| except urllib.error.HTTPError as err: |
| if err.code != 404: |
| raise |
| if first: |
| die('no licenses found under ' + repo_path) |
| |
| |
| def check_licenses(directory, verify=False): |
| success = True |
| os.chdir(directory) |
| for subdir in sorted(os.listdir(os.getcwd())): |
| # TODO(pylaligand): remove this temporary hack when a new version of |
| # the crate is published. |
| if subdir.startswith('fuchsia-zircon-sys'): |
| print('IGNORED %s' % subdir) |
| continue |
| if subdir.startswith('.') or not os.path.isdir(subdir): |
| continue |
| license_files = [ |
| file for file in os.listdir(subdir) if file.startswith('LICENSE') or |
| file.startswith('LICENCE') or file.startswith('license') |
| ] |
| if license_files: |
| print('OK %s' % subdir) |
| continue |
| if verify: |
| print('MISSING %s' % subdir) |
| success = False |
| continue |
| try: |
| fetch_license(subdir) |
| print('FETCH %s' % subdir) |
| except Exception as err: |
| print('ERROR %s (%s)' % (subdir, err)) |
| success = False |
| return success |
| |
| |
| def main(): |
| parser = argparse.ArgumentParser( |
| 'Verifies licenses for third-party Rust crates') |
| parser.add_argument( |
| '--directory', |
| help='Directory containing the crates', |
| default=os.getcwd()) |
| parser.add_argument( |
| '--verify', |
| help='Simply check whether licenses are up-to-date', |
| action='store_true') |
| args = parser.parse_args() |
| if not check_licenses(args.directory, verify=args.verify): |
| sys.exit(1) |
| |
| |
| if __name__ == '__main__': |
| main() |