1#!/usr/bin/env python3 2"""Refresh the BUILD file of SPDX license_kinds with new ones from licenses.json. 3 4Usage: 5 wget https://github.com/spdx/license-list-data/raw/master/json/licenses.json 6 LC_ALL="en_US.UTF-8" admin/refresh_spdx/add_licenses.py 7 git diff 8 git commit 9""" 10 11import json 12import os 13from string import Template 14import re 15import sys 16 17def load_json(path: str): 18 """Loads a JSON file and returns the data. 19 20 Args: 21 path: (str) a file path. 22 Returns: 23 (dict) the parsed data. 24 """ 25 with open(path, 'r') as fp: 26 ret = json.load(fp) 27 return ret 28 29 30def parse_build_file(text: str): 31 """Parse a BUILD file of license declaration. 32 33 Parses a build file and returns all the text that 34 is not licenese_kind declarations and a map of 35 license kind names to the text which declared them 36 37 license_kind( 38 name = "0BSD", 39 conditions = [], 40 url = "https://spdx.org/licenses/0BSD.html", 41 ) 42 43 Returns: 44 preamble: str 45 targets: map<str, str> 46 """ 47 48 target_start = re.compile(r'^([a-zA-Z0-9_]+)\(') # ) to fix autoindent 49 name_match = re.compile(r'^ *name *= *"([^"]*)"') 50 51 targets = {} 52 preamble = [] 53 collecting_rule = None 54 55 for line in text.split('\n'): 56 if collecting_rule: 57 collecting_rule.append(line) 58 if line.startswith(')'): 59 assert cur_name 60 targets[cur_name] = '\n'.join(collecting_rule) 61 # print(cur_name, "=====", targets[cur_name]) 62 collecting_rule = None 63 cur_name = None 64 else: 65 m = name_match.match(line) 66 if m: 67 cur_name = m.group(1) 68 continue 69 70 # not collecting a rule 71 m = target_start.match(line) 72 if m: 73 cur_rule = m.group(1) 74 if cur_rule == 'license_kind': 75 collecting_rule = [line] 76 continue 77 78 if line or preamble[-1]: 79 preamble.append(line) 80 81 return '\n'.join(preamble), targets 82 83 84# Sample JSON formate license declaration. 85""" 86{ 87 "licenseListVersion": "3.8-57-gca4f142", 88 "licenses": [ 89 { 90 "reference": "./0BSD.html", 91 "isDeprecatedLicenseId": false, 92 "detailsUrl": "http://spdx.org/licenses/0BSD.json", 93 "referenceNumber": "232", 94 "name": "BSD Zero Clause License", 95 "licenseId": "0BSD", 96 "seeAlso": [ 97 "http://landley.net/toybox/license.html" 98 ], 99 "isOsiApproved": true 100 }, 101""" 102 103 104def insert_new(already_declared, licenses): 105 template = Template(( 106 'license_kind(\n' 107 ' name = "${licenseId}",\n' 108 ' conditions = [],\n' 109 ' url = "https://spdx.org/licenses/${licenseId}.html",\n' 110 ')')) 111 112 for license in licenses: 113 id = license['licenseId'] 114 old = already_declared.get(id) 115 if not old: 116 print('Adding:', id) 117 already_declared[id] = template.substitute(license) 118 119 120def update_spdx_build(): 121 """Update //licenses/spdx/BUILD with new license kinds.""" 122 123 license_json = load_json('licenses.json') 124 125 # Find workspace root 126 build_path = 'licenses/spdx/BUILD' 127 found_spdx_build = False 128 for i in range(5): # Simple regression failure limiter 129 if os.access('WORKSPACE', os.R_OK) and os.access(build_path, os.R_OK): 130 found_spdx_build = True 131 break 132 os.chdir('..') 133 if not found_spdx_build: 134 print('Could not find', build_path) 135 return 1 136 137 with open(build_path, 'r') as fp: 138 current_file_content = fp.read() 139 140 preamble, kinds = parse_build_file(current_file_content) 141 insert_new(kinds, license_json['licenses']) 142 with open(build_path, 'w') as fp: 143 fp.write(preamble) 144 for license in sorted(kinds.keys(), key=lambda x: x.lower()): 145 fp.write('\n') 146 fp.write(kinds[license]) 147 fp.write('\n') 148 149 150def main(): 151 lc_all = os.environ.get('LC_ALL') 152 if lc_all != 'en_US.UTF-8': 153 print('Your environment settings will reorder the file badly.') 154 print('Please rerun as:') 155 print(' LC_ALL="en_US.UTF-8"', ' '.join(sys.argv)) 156 sys.exit(1) 157 158 update_spdx_build() 159 160 161if __name__ == '__main__': 162 main() 163