mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
It needed to be renamed to allow it to be published on crates.io, as there's already a parameterized_test crate there.
184 lines
6.2 KiB
Python
184 lines
6.2 KiB
Python
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# This script expects cargo-attribution
|
|
# <https://github.com/ameknite/cargo-attribution> to be installed.
|
|
|
|
import argparse
|
|
import datetime
|
|
import json
|
|
import os
|
|
import re
|
|
import shutil
|
|
import subprocess
|
|
import tomllib
|
|
|
|
def process_licenses(source_dir, destination_dir):
|
|
# CC0, Unlicense and Zlib don't need licenses to be included.
|
|
# MPL doesn't need the license included with binary distributions, and
|
|
# allows distribution under a compatible license.
|
|
# cargo attribution's GPL-3.0 download doesn't contain the license text.
|
|
# The ISC license is skipped because its dependency gets filtered out, same
|
|
# with the exceptions.
|
|
skip_entries = [
|
|
'exceptions',
|
|
'CC0-1.0',
|
|
'GPL-3.0',
|
|
'ISC',
|
|
'MPL-2.0',
|
|
'Unlicense',
|
|
'Zlib'
|
|
]
|
|
|
|
for entry in os.scandir(source_dir):
|
|
if entry.name not in skip_entries:
|
|
destination_path = os.path.join(destination_dir, entry.name)
|
|
if entry.is_dir():
|
|
shutil.copytree(entry.path, destination_path, dirs_exist_ok=True)
|
|
else:
|
|
shutil.copy2(entry.path, destination_path)
|
|
|
|
def recurse_dependencies(packages, package, dependency_names):
|
|
for dependency in package['dependencies']:
|
|
name = dependency['name']
|
|
if name in dependency_names:
|
|
continue
|
|
|
|
dependency_names.add(name)
|
|
for package in packages:
|
|
if package['name'] == name:
|
|
recurse_dependencies(packages, package, dependency_names)
|
|
|
|
def get_target_dependency_names(target_package_name):
|
|
result = subprocess.run(
|
|
[
|
|
'cargo',
|
|
'metadata',
|
|
'--manifest-path',
|
|
cargo_toml_path,
|
|
'--filter-platform',
|
|
'x86_64-pc-windows-msvc',
|
|
'--format-version',
|
|
'1'
|
|
],
|
|
capture_output=True,
|
|
text=True,
|
|
check=True
|
|
)
|
|
|
|
json_output = json.loads(result.stdout[:-1])
|
|
packages = json_output['packages']
|
|
|
|
target_dependency_names = set()
|
|
for package in packages:
|
|
if package['name'] == 'libloot-cpp':
|
|
recurse_dependencies(packages, package, target_dependency_names)
|
|
|
|
return target_dependency_names
|
|
|
|
if __name__ == "__main__":
|
|
target_package_name = 'libloot-cpp'
|
|
cargo_toml_path = os.path.join('..', 'cpp', 'Cargo.toml')
|
|
attribution_dir = os.path.join('build', 'attribution')
|
|
output_dir = os.path.join('api', 'copyright')
|
|
|
|
subprocess.run(
|
|
[
|
|
'cargo',
|
|
'attribution',
|
|
'--manifest-path',
|
|
cargo_toml_path,
|
|
'--output-dir',
|
|
attribution_dir,
|
|
'--filter-platform',
|
|
'x86_64-pc-windows-msvc',
|
|
'--only-normal-dependencies'
|
|
],
|
|
check=True
|
|
)
|
|
|
|
process_licenses(os.path.join(attribution_dir, 'licenses'), os.path.join(output_dir, 'licenses'))
|
|
|
|
target_dependency_names = get_target_dependency_names(target_package_name)
|
|
|
|
dependencies_toml_path = os.path.join(attribution_dir, 'dependencies.toml')
|
|
with open(dependencies_toml_path, 'rb') as f:
|
|
data = tomllib.load(f)
|
|
dependencies = data['dependencies']
|
|
|
|
# esplugin's and libloadorder's notices are nonsense, but they're my
|
|
# libraries so I give myself permission to skip providing the notices.
|
|
skip_dependencies = [
|
|
'libloot',
|
|
'libloot-ffi-errors',
|
|
'libloot-cpp',
|
|
'libloot-nodejs',
|
|
'libloot-python',
|
|
'array-parameterized-test',
|
|
'esplugin',
|
|
'libloadorder'
|
|
]
|
|
|
|
notices_rst = f'.. This file was generated by scripts/licenses.py at {datetime.datetime.now().isoformat()}.\n\n'
|
|
|
|
heading = 'Dependency Copyright Notices'
|
|
notices_rst += heading
|
|
notices_rst += '\n'
|
|
notices_rst += '=' * len(heading)
|
|
notices_rst += '\n\n'
|
|
|
|
for dependency in dependencies:
|
|
if dependency['name'] not in target_dependency_names:
|
|
continue
|
|
|
|
if dependency['name'] in skip_dependencies:
|
|
continue
|
|
|
|
if 'license' not in dependency:
|
|
print(f'Found dependency with no license: {dependency['name']}')
|
|
exit(1)
|
|
|
|
if 'ISC' in dependency['license']:
|
|
print(f'Found dependency that may require the ISC license: {dependency['name']}')
|
|
exit(1)
|
|
|
|
if 'exception' in dependency['license']:
|
|
print(f'Found dependency that may require an exception: {dependency['name']}')
|
|
exit(1)
|
|
|
|
if 'CC0-1.0' in dependency['license']:
|
|
print(f'Skipping {dependency['name']} because it uses the CC0-1.0 license: {dependency['license']}')
|
|
continue
|
|
|
|
if 'Unlicense' in dependency['license']:
|
|
print(f'Skipping {dependency['name']} because it uses the Unlicense license: {dependency['license']}')
|
|
continue
|
|
|
|
if 'Zlib' in dependency['license']:
|
|
print(f'Skipping {dependency['name']} because it uses the Zlib license: {dependency['license']}')
|
|
continue
|
|
|
|
if 'notices' not in dependency:
|
|
print(f'Skipping dependency with no notices: {dependency['name']}')
|
|
continue
|
|
|
|
if 'repository' not in dependency:
|
|
print(f'Found dependency with no repository: {dependency['name']}')
|
|
exit(1)
|
|
|
|
if dependency['name'] == 'hashlink':
|
|
# The notice for hashlink is mangled so correcting it here.
|
|
dependency['notices'] = ['Copyright (c) 2015 The Rust Project Developers']
|
|
|
|
link_rst = f'`{dependency['name']} v{dependency['version']} <{dependency['repository']}>`_'
|
|
underline_rst = '-' * len(link_rst)
|
|
dep_notices_rst = '\n '.join(dependency['notices'])
|
|
|
|
section_rst = f'{link_rst}\n{underline_rst}\n\n::\n\n {dep_notices_rst}\n\n'
|
|
|
|
notices_rst += section_rst
|
|
|
|
notices_rst_path = os.path.join(output_dir, 'dependency-notices.rst')
|
|
with open(notices_rst_path, 'w', encoding="utf-8") as outfile:
|
|
outfile.write(notices_rst)
|