blob: fa34207e215e855ea3973decfe3fba6489f0b798 [file] [log] [blame]
#!/usr/bin/env python3
"""Refresh the BUILD file of SPDX license_kinds with new ones from licenses.json.
Usage:
wget https://raw.githubusercontent.com/spdx/license-list-data/main/json/licenses.json
LC_ALL="en_US.UTF-8" admin/refresh_spdx/add_licenses.py
git diff
git commit
"""
import json
import os
from string import Template
import re
import sys
def load_json(path: str):
"""Loads a JSON file and returns the data.
Args:
path: (str) a file path.
Returns:
(dict) the parsed data.
"""
with open(path, 'r') as fp:
ret = json.load(fp)
return ret
def parse_build_file(text: str):
"""Parse a BUILD file of license declaration.
Parses a build file and returns all the text that
is not licenese_kind declarations and a map of
license kind names to the text which declared them
license_kind(
name = "0BSD",
conditions = [],
url = "https://spdx.org/licenses/0BSD.html",
)
Returns:
preamble: str
targets: map<str, str>
"""
target_start = re.compile(r'^([a-zA-Z0-9_]+)\(') # ) to fix autoindent
name_match = re.compile(r'^ *name *= *"([^"]*)"')
targets = {}
preamble = []
collecting_rule = None
for line in text.split('\n'):
if collecting_rule:
collecting_rule.append(line)
if line.startswith(')'):
assert cur_name
targets[cur_name] = '\n'.join(collecting_rule)
# print(cur_name, "=====", targets[cur_name])
collecting_rule = None
cur_name = None
else:
m = name_match.match(line)
if m:
cur_name = m.group(1)
continue
# not collecting a rule
m = target_start.match(line)
if m:
cur_rule = m.group(1)
if cur_rule == 'license_kind':
collecting_rule = [line]
continue
if line or preamble[-1]:
preamble.append(line)
return '\n'.join(preamble), targets
# Sample JSON formate license declaration.
"""
{
"licenseListVersion": "3.8-57-gca4f142",
"licenses": [
{
"reference": "./0BSD.html",
"isDeprecatedLicenseId": false,
"detailsUrl": "http://spdx.org/licenses/0BSD.json",
"referenceNumber": "232",
"name": "BSD Zero Clause License",
"licenseId": "0BSD",
"seeAlso": [
"http://landley.net/toybox/license.html"
],
"isOsiApproved": true
},
"""
def insert_new(already_declared, licenses):
template = Template((
'license_kind(\n'
' name = "${licenseId}",\n'
' conditions = [],\n'
' url = "https://spdx.org/licenses/${licenseId}.html",\n'
')'))
for license in licenses:
id = license['licenseId']
old = already_declared.get(id)
if not old:
print('Adding:', id)
already_declared[id] = template.substitute(license)
def update_spdx_build():
"""Update //licenses/spdx/BUILD with new license kinds."""
license_json = load_json('licenses.json')
# Find workspace root
build_path = 'licenses/spdx/BUILD'
found_spdx_build = False
for i in range(5): # Simple regression failure limiter
if os.access('WORKSPACE', os.R_OK) and os.access(build_path, os.R_OK):
found_spdx_build = True
break
os.chdir('..')
if not found_spdx_build:
print('Could not find', build_path)
return 1
with open(build_path, 'r') as fp:
current_file_content = fp.read()
preamble, kinds = parse_build_file(current_file_content)
insert_new(kinds, license_json['licenses'])
with open(build_path, 'w') as fp:
fp.write(preamble)
for license in sorted(kinds.keys(), key=lambda x: x.lower()):
fp.write('\n')
fp.write(kinds[license])
fp.write('\n')
def main():
lc_all = os.environ.get('LC_ALL')
if lc_all != 'en_US.UTF-8':
print('Your environment settings will reorder the file badly.')
print('Please rerun as:')
print(' LC_ALL="en_US.UTF-8"', ' '.join(sys.argv))
sys.exit(1)
update_spdx_build()
if __name__ == '__main__':
main()