1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3# Copyright (c) 2021 Huawei Device Co., Ltd. 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15 16import sys 17import argparse 18import os 19import shutil 20import stat 21 22sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir)) 23from scripts.util.file_utils import read_json_file, write_json_file # noqa: E402 24from scripts.util import build_utils # noqa: E402 25 26README_FILE_NAME = 'README.OpenSource' 27LICENSE_CANDIDATES = [ 28 'LICENSE', 29 'License', 30 'NOTICE', 31 'Notice', 32 'COPYRIGHT', 33 'Copyright', 34 'COPYING', 35 'Copying' 36] 37 38 39def is_top_dir(current_dir: str): 40 return os.path.exists(os.path.join(current_dir, '.gn')) 41 42 43def find_other_files(license_file_path): 44 other_files = [] 45 if os.path.isfile(license_file_path): 46 license_dir = os.path.dirname(license_file_path) 47 license_file = os.path.basename(license_file_path) 48 for file in LICENSE_CANDIDATES: 49 license_path = os.path.join(license_dir, file) 50 if os.path.isfile(license_path) and file != license_file and \ 51 license_path not in other_files: 52 other_files.append(license_path) 53 elif os.path.isdir(license_file_path): 54 for file in ['COPYRIGHT', 'Copyright', 'COPYING', 'Copying']: 55 license_file = os.path.join(license_file_path, file) 56 if os.path.isfile(license_file): 57 other_files.append(license_file) 58 return other_files 59 60 61def find_file_recursively(current_dir: str, target_files: list): 62 if is_top_dir(current_dir): 63 return None 64 for file in target_files: 65 candidate = os.path.join(current_dir, file) 66 if os.path.isfile(candidate): 67 return candidate 68 return find_file_recursively(os.path.dirname(current_dir), target_files) 69 70 71def find_license(current_dir: str): 72 return find_file_recursively(current_dir, LICENSE_CANDIDATES) 73 74 75def find_opensource(current_dir: str): 76 return find_file_recursively(current_dir, [README_FILE_NAME]) 77 78 79def get_license_from_readme(readme_path: str): 80 contents = read_json_file(readme_path) 81 if contents is None: 82 raise Exception("Error: failed to read {}.".format(readme_path)) 83 84 if len(contents) <= 1: 85 notice_file = contents[0].get('License File').strip() 86 notice_name = contents[0].get('Name').strip() 87 notice_version = contents[0].get('Version Number').strip() 88 if notice_file is None: 89 raise Exception("Error: value of notice file is empty in {}.".format( 90 readme_path)) 91 if notice_name is None: 92 raise Exception("Error: Name of notice file is empty in {}.".format( 93 readme_path)) 94 if notice_version is None: 95 raise Exception("Error: Version Number of notice file is empty in {}.".format( 96 readme_path)) 97 98 return os.path.join(os.path.dirname(readme_path), notice_file), notice_name, notice_version 99 else: 100 notice_files = [] 101 notice_names = [] 102 notice_versions = [] 103 for content in contents: 104 notice_files.append(content.get('License File').strip()) 105 notice_names.append(content.get('Name').strip()) 106 notice_versions.append(content.get('Version Number').strip()) 107 108 if notice_files is None: 109 raise Exception("Error: value of notice file is empty in {}.".format( 110 readme_path)) 111 if notice_names is None: 112 raise Exception("Error: Name of notice file is empty in {}.".format( 113 readme_path)) 114 if notice_versions is None: 115 raise Exception("Error: Version Number of notice file is empty in {}.".format( 116 readme_path)) 117 118 return [os.path.join(os.path.dirname(readme_path), file) for file in notice_files], \ 119 notice_names, notice_versions 120 121 122def add_path_to_module_notice(module_notice_info, module_notice_info_list, options): 123 if isinstance(module_notice_info.get('Software', None), list): 124 softwares = module_notice_info.get('Software') 125 versions = module_notice_info.get('Version') 126 for software, version in zip(softwares, versions): 127 module_notice_info_list.append({'Software': software, 'Version': version}) 128 module_notice_info_list[-1]['Path'] = "/{}".format(options.module_source_dir[5:]) 129 else: 130 if module_notice_info.get('Software', None): 131 module_notice_info['Path'] = "/{}".format(options.module_source_dir[5:]) 132 module_notice_info_list.append(module_notice_info) 133 if module_notice_info_list: 134 for module_notice_info in module_notice_info_list: 135 module_path = module_notice_info.get("Path", None) 136 if module_path is not None: 137 module_notice_info["Path"] = module_path.replace("../", "") 138 139 140def do_collect_notice_files(options, depfiles: str): 141 module_notice_info_list = [] 142 module_notice_info = {} 143 notice_file = options.license_file 144 other_files = [] 145 if notice_file: 146 opensource_file = find_opensource(os.path.abspath(options.module_source_dir)) 147 if opensource_file is not None and os.path.exists(opensource_file): 148 other_files.extend(find_other_files(opensource_file)) 149 notice_file_info = get_license_from_readme(opensource_file) 150 module_notice_info['Software'] = notice_file_info[1] 151 module_notice_info['Version'] = notice_file_info[2] 152 else: 153 module_notice_info['Software'] = "" 154 module_notice_info['Version'] = "" 155 if notice_file is None: 156 readme_path = os.path.join(options.module_source_dir, 157 README_FILE_NAME) 158 if not os.path.exists(readme_path): 159 readme_path = find_opensource(os.path.abspath(options.module_source_dir)) 160 other_files.extend(find_other_files(options.module_source_dir)) 161 if readme_path is not None: 162 depfiles.append(readme_path) 163 notice_file_info = get_license_from_readme(readme_path) 164 notice_file = notice_file_info[0] 165 if isinstance(notice_file, list): 166 notice_file = ",".join(notice_file) 167 module_notice_info['Software'] = notice_file_info[1] 168 module_notice_info['Version'] = notice_file_info[2] 169 170 if notice_file is None: 171 notice_file = find_license(options.module_source_dir) 172 opensource_file = find_opensource(os.path.abspath(options.module_source_dir)) 173 if opensource_file is not None and os.path.exists(opensource_file): 174 other_files.extend(find_other_files(opensource_file)) 175 notice_file_info = get_license_from_readme(opensource_file) 176 module_notice_info['Software'] = notice_file_info[1] 177 module_notice_info['Version'] = notice_file_info[2] 178 else: 179 module_notice_info['Software'] = "" 180 module_notice_info['Version'] = "" 181 182 add_path_to_module_notice(module_notice_info, module_notice_info_list, options) 183 184 if notice_file: 185 if other_files: 186 notice_file = f"{notice_file},{','.join(other_files)}" 187 for output in options.output: 188 notice_info_json = '{}.json'.format(output) 189 os.makedirs(os.path.dirname(output), exist_ok=True) 190 os.makedirs(os.path.dirname(notice_info_json), exist_ok=True) 191 notice_files = [file for file in notice_file.split(",") if file] 192 193 write_file_content(notice_files, options, output, notice_info_json, module_notice_info_list, depfiles) 194 195 196def write_file_content(notice_files, options, output, notice_info_json, module_notice_info_list, depfiles): 197 for notice_file in notice_files: 198 notice_file = notice_file.strip() 199 if not os.path.exists(notice_file): 200 notice_file = os.path.join(options.module_source_dir, notice_file) 201 if os.path.exists(notice_file): 202 if not os.path.exists(output): 203 build_utils.touch(output) 204 write_notice_to_output(notice_file, output) 205 write_json_file(notice_info_json, module_notice_info_list) 206 else: 207 build_utils.touch(output) 208 build_utils.touch(notice_info_json) 209 depfiles.append(notice_file) 210 211 212def write_notice_to_output(notice_file, output): 213 with os.fdopen(os.open(notice_file, os.O_RDWR | os.O_CREAT, stat.S_IWUSR | stat.S_IRUSR), 214 'r', encoding='utf-8', errors='ignore') as notice_data_flow: 215 license_content = notice_data_flow.read() 216 with os.fdopen(os.open(output, os.O_RDWR | os.O_CREAT, stat.S_IWUSR | stat.S_IRUSR), 217 'r', encoding='utf-8', errors='ignore') as output_data_flow: 218 output_file_content = output_data_flow.read() 219 if license_content not in output_file_content: 220 with os.fdopen(os.open(output, os.O_RDWR | os.O_CREAT, stat.S_IWUSR | stat.S_IRUSR), 221 'a', encoding='utf-8') as testfwk_info_file: 222 testfwk_info_file.write(f"{license_content}\n") 223 testfwk_info_file.close() 224 225 226def main(args): 227 args = build_utils.expand_file_args(args) 228 229 parser = argparse.ArgumentParser() 230 build_utils.add_depfile_option(parser) 231 232 parser.add_argument('--license-file', required=False) 233 parser.add_argument('--output', action='append', required=False) 234 parser.add_argument('--sources', action='append', required=False) 235 parser.add_argument('--sdk-install-info-file', required=False) 236 parser.add_argument('--label', required=False) 237 parser.add_argument('--sdk-notice-dir', required=False) 238 parser.add_argument('--module-source-dir', 239 help='source directory of this module', 240 required=True) 241 242 options = parser.parse_args() 243 depfiles = [] 244 245 if options.sdk_install_info_file: 246 install_dir = '' 247 sdk_install_info = read_json_file(options.sdk_install_info_file) 248 for item in sdk_install_info: 249 if options.label == item.get('label'): 250 install_dir = item.get('install_dir') 251 break 252 if options.sources and install_dir: 253 for src in options.sources: 254 extend_output = os.path.join(options.sdk_notice_dir, install_dir, 255 '{}.{}'.format(os.path.basename(src), 'txt')) 256 options.output.append(extend_output) 257 258 do_collect_notice_files(options, depfiles) 259 if options.license_file: 260 depfiles.append(options.license_file) 261 build_utils.write_depfile(options.depfile, options.output[0], depfiles) 262 263 264if __name__ == '__main__': 265 sys.exit(main(sys.argv[1:])) 266 267