46 lines
2.2 KiB
Python
Executable File
46 lines
2.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Collect license notices for the locked ARM Rust workspace and vendored C code."""
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
from pathlib import Path
|
|
import subprocess
|
|
|
|
project = Path(__file__).resolve().parents[1]
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('output', type=Path)
|
|
args = parser.parse_args()
|
|
metadata = json.loads(subprocess.check_output([
|
|
'cargo', 'metadata', '--locked', '--offline', '--format-version', '1',
|
|
'--filter-platform', 'aarch64-unknown-linux-musl',
|
|
], cwd=project))
|
|
resolved = {node['id'] for node in metadata['resolve']['nodes']}
|
|
notices = ['FDS/OS Rust workspace third-party notices\n',
|
|
'Includes build dependencies and vendored native-code license files.\n',
|
|
'This inventory does not imply every dependency is linked into every executable.\n']
|
|
count = 0
|
|
for package in sorted(metadata['packages'], key=lambda p: (p['name'], p['version'])):
|
|
if not package['source'] or package['id'] not in resolved:
|
|
continue
|
|
root = Path(package['manifest_path']).parent
|
|
files = {path for path in root.rglob('*') if path.is_file()
|
|
and path.name.upper().startswith(('LICENSE', 'COPYING', 'NOTICE'))}
|
|
if package['license_file']:
|
|
files.add(root / package['license_file'])
|
|
if not files:
|
|
parser.error(f'No license notice found for {package["name"]} {package["version"]}')
|
|
notices.append(f'\n=== {package["name"]} {package["version"]} ===\n'
|
|
f'Declared license: {package["license"] or "see license file"}\n'
|
|
f'Source: {package["source"]}\n')
|
|
for path in sorted(files):
|
|
if path.is_symlink() or not path.resolve().is_relative_to(root.resolve()):
|
|
parser.error(f'License notice leaves crate source: {package["name"]}')
|
|
data = path.read_bytes()
|
|
notices.append(f'\n--- {path.relative_to(root)} ---\n'
|
|
f'SHA-256: {hashlib.sha256(data).hexdigest()}\n\n')
|
|
notices.append(data.decode('utf-8') + '\n')
|
|
count += 1
|
|
with args.output.open('x') as stream:
|
|
stream.write(''.join(notices))
|
|
print(f'PASS: license notices for {count} resolved third-party crates: {args.output}')
|