107 lines
3.7 KiB
Python
107 lines
3.7 KiB
Python
# Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import re
|
|
import stat
|
|
import sys
|
|
|
|
parser = argparse.ArgumentParser(
|
|
description='Takes a directory containing license files, and produces a manifest with associated assets that can be loaded at runtime in Flutter.',
|
|
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
|
)
|
|
parser.add_argument('-i', '--input', default='licenses', type=str, metavar='INPUT_PATH', help='The path containing directories with license files')
|
|
parser.add_argument('-o', '--output', default='assets/licenses', type=str, metavar='OUTPUT_PATH', help='The path to which to write the manifest and text assets')
|
|
|
|
args = parser.parse_args(sys.argv[1:])
|
|
|
|
notices_directory = args.input
|
|
output_directory = args.output
|
|
text_directory = os.path.join(output_directory, 'text')
|
|
manifest_path = os.path.join(output_directory, 'manifest.json')
|
|
|
|
if not os.path.exists(notices_directory):
|
|
print(f'Invalid input path "{notices_directory}"')
|
|
sys.exit(1)
|
|
|
|
# Check that we're not about to overwrite something unrelated
|
|
if os.path.exists(text_directory) and not os.path.exists(manifest_path):
|
|
response = input(
|
|
f'Output path "{output_directory}" exists, but does not contain a manifest file.\n'
|
|
f'All files in the the path "{text_directory}" will be deleted and re-generated.\n'
|
|
'Are you sure you want to continue? (Y/N) ')
|
|
|
|
if response.lower() != 'y':
|
|
print('Aborting')
|
|
sys.exit(1)
|
|
|
|
os.makedirs(output_directory, exist_ok=True)
|
|
os.makedirs(text_directory, exist_ok=True)
|
|
|
|
license_pattern = re.compile(
|
|
r'=*\n'
|
|
r'Software Name: ([^\n]*)\n'
|
|
r'Version: ([^\n]*)\n'
|
|
r'URL: .*\n'
|
|
r'===+\n'
|
|
r'(.*)',
|
|
|
|
flags=re.DOTALL
|
|
)
|
|
|
|
# Clear existing license text in the output directory since we intend to replace them all
|
|
for license_name in os.listdir(text_directory):
|
|
license_path = os.path.join(text_directory, license_name)
|
|
os.chmod(license_path, stat.S_IWRITE) # Clear read-only flag so we can delete files tracked by Perforce
|
|
os.remove(license_path)
|
|
|
|
manifest = {}
|
|
seen_hashes = set()
|
|
|
|
for input_directory_item in sorted(os.listdir(notices_directory), key=str.lower):
|
|
# Find the license file
|
|
input_directory_path = os.path.join(notices_directory, input_directory_item)
|
|
|
|
# Skip any paths that don't point to a directory
|
|
if os.path.isdir(input_directory_path) is False:
|
|
continue
|
|
|
|
license_path = None
|
|
|
|
for file_name in os.listdir(input_directory_path):
|
|
if file_name.lower().endswith('.license'):
|
|
license_path = os.path.join(input_directory_path, file_name)
|
|
break
|
|
|
|
if not license_path:
|
|
print(f'No license found for directory {input_directory_path}')
|
|
sys.exit(1)
|
|
|
|
# Parse the license file
|
|
with open(license_path, 'r', encoding='utf-8') as license_file:
|
|
match = license_pattern.search(license_file.read())
|
|
if not match:
|
|
print(f'Invalid license format for file {license_path}')
|
|
sys.exit(1)
|
|
|
|
name, version, license_text = match.groups()
|
|
|
|
# Make a hash for each license so we only store one copy of each
|
|
license_hash = hashlib.md5(license_text.encode()).hexdigest()
|
|
|
|
if license_hash not in seen_hashes:
|
|
with open(os.path.join(text_directory, license_hash), 'w', encoding='utf-8') as license_text_file:
|
|
license_text_file.write(license_text)
|
|
|
|
manifest[name] = {
|
|
'version': version,
|
|
'file': license_hash
|
|
}
|
|
|
|
# Write manifest
|
|
with open(manifest_path, 'w') as manifest_file:
|
|
manifest_file.write(json.dumps(manifest))
|
|
|
|
print(f'Asset generation complete. If this is a Perforce repo, you should reconcile the "{output_directory}" directory.') |