| #!/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() |