1bae44755Sopenharmony_ci#!/usr/bin/env python3 2bae44755Sopenharmony_ci# -*- coding: utf-8 -*- 3bae44755Sopenharmony_ci# Copyright (c) 2022 Huawei Device Co., Ltd. 4bae44755Sopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); 5bae44755Sopenharmony_ci# you may not use this file except in compliance with the License. 6bae44755Sopenharmony_ci# You may obtain a copy of the License at 7bae44755Sopenharmony_ci# 8bae44755Sopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 9bae44755Sopenharmony_ci# 10bae44755Sopenharmony_ci# Unless required by applicable law or agreed to in writing, software 11bae44755Sopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, 12bae44755Sopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bae44755Sopenharmony_ci# See the License for the specific language governing permissions and 14bae44755Sopenharmony_ci# limitations under the License. 15bae44755Sopenharmony_ci 16bae44755Sopenharmony_ciimport os 17bae44755Sopenharmony_ciimport json 18bae44755Sopenharmony_ciimport re 19bae44755Sopenharmony_ciimport argparse 20bae44755Sopenharmony_cifrom prettytable import PrettyTable, ALL 21bae44755Sopenharmony_ci 22bae44755Sopenharmony_ci 23bae44755Sopenharmony_cidef get_args(): 24bae44755Sopenharmony_ci parser = argparse.ArgumentParser(add_help=True) 25bae44755Sopenharmony_ci parser.add_argument( 26bae44755Sopenharmony_ci "-p", 27bae44755Sopenharmony_ci "--project_path", 28bae44755Sopenharmony_ci default=r"./", 29bae44755Sopenharmony_ci type=str, 30bae44755Sopenharmony_ci help="root path of project. default: ./", 31bae44755Sopenharmony_ci ) 32bae44755Sopenharmony_ci parser.add_argument( 33bae44755Sopenharmony_ci "-t", 34bae44755Sopenharmony_ci "--check_target", 35bae44755Sopenharmony_ci type=str, 36bae44755Sopenharmony_ci choices=["component_codec", "component_sdk", "sdk_codec"], 37bae44755Sopenharmony_ci required=True, 38bae44755Sopenharmony_ci help="the target to be compared", 39bae44755Sopenharmony_ci ) 40bae44755Sopenharmony_ci parser.add_argument( 41bae44755Sopenharmony_ci "-b", 42bae44755Sopenharmony_ci "--bundles", 43bae44755Sopenharmony_ci nargs="*", 44bae44755Sopenharmony_ci type=str, 45bae44755Sopenharmony_ci help="option take effect only when the check_target is component_codec. allow multiple json file. " 46bae44755Sopenharmony_ci "default: all bundle.json file", 47bae44755Sopenharmony_ci ) 48bae44755Sopenharmony_ci args = parser.parse_args() 49bae44755Sopenharmony_ci return args 50bae44755Sopenharmony_ci 51bae44755Sopenharmony_ci 52bae44755Sopenharmony_cidef list_to_multiline(target_list: list): 53bae44755Sopenharmony_ci return str(target_list).lstrip("[").rstrip("]").replace(", ", "\n") 54bae44755Sopenharmony_ci 55bae44755Sopenharmony_ci 56bae44755Sopenharmony_cidef convert_set_to_sorted_list(target_set) -> list: 57bae44755Sopenharmony_ci return sorted(list(target_set)) 58bae44755Sopenharmony_ci 59bae44755Sopenharmony_ci 60bae44755Sopenharmony_cidef add_dict_as_table_row(f_table: PrettyTable, d_dict: dict, out_converter=list_to_multiline) -> None: 61bae44755Sopenharmony_ci s_keys = convert_set_to_sorted_list(d_dict.keys()) 62bae44755Sopenharmony_ci for i, k in enumerate(s_keys): 63bae44755Sopenharmony_ci f_table.add_row([i + 1, k, out_converter(convert_set_to_sorted_list(d_dict.get(k)))]) 64bae44755Sopenharmony_ci 65bae44755Sopenharmony_ci 66bae44755Sopenharmony_cidef bundle_syscap_post_handler(syscap: str) -> str: 67bae44755Sopenharmony_ci return syscap.split('=')[0].strip() 68bae44755Sopenharmony_ci 69bae44755Sopenharmony_ci 70bae44755Sopenharmony_cidef read_value_from_json(filepath: str, 71bae44755Sopenharmony_ci key_hierarchy: tuple, 72bae44755Sopenharmony_ci result_dict: dict, 73bae44755Sopenharmony_ci post_handler=None 74bae44755Sopenharmony_ci ) -> None: 75bae44755Sopenharmony_ci if os.path.exists(filepath) is False: 76bae44755Sopenharmony_ci print('error: file "{}" not exist.'.format(filepath)) 77bae44755Sopenharmony_ci return 78bae44755Sopenharmony_ci if not os.path.isfile(filepath): 79bae44755Sopenharmony_ci print('error: "{}" is not a file.') 80bae44755Sopenharmony_ci return 81bae44755Sopenharmony_ci with open(filepath, 'r', encoding='utf-8') as f: 82bae44755Sopenharmony_ci data = json.load(f) 83bae44755Sopenharmony_ci for key in key_hierarchy: 84bae44755Sopenharmony_ci try: 85bae44755Sopenharmony_ci data = data[key] 86bae44755Sopenharmony_ci except KeyError: 87bae44755Sopenharmony_ci return 88bae44755Sopenharmony_ci finally: 89bae44755Sopenharmony_ci pass 90bae44755Sopenharmony_ci data = [post_handler(x) for x in data if len(x) != 0 and not x.isspace()] 91bae44755Sopenharmony_ci if len(data) != 0: 92bae44755Sopenharmony_ci result_dict[filepath] = data 93bae44755Sopenharmony_ci 94bae44755Sopenharmony_ci 95bae44755Sopenharmony_cidef collect_syscap_from_codec(filepath: str, pattern: str = r'{"(.*)"') -> tuple: 96bae44755Sopenharmony_ci """ 97bae44755Sopenharmony_ci 从syscap_define.h收集syscap 98bae44755Sopenharmony_ci :param filepath: 文件的路径 99bae44755Sopenharmony_ci :param pattern: 匹配syscap的规则 100bae44755Sopenharmony_ci :return: syscap_define.h中声明的syscap集合 101bae44755Sopenharmony_ci """ 102bae44755Sopenharmony_ci array_syscap_set = set() 103bae44755Sopenharmony_ci array_syscap_dict = dict() 104bae44755Sopenharmony_ci ptrn = re.compile(pattern) 105bae44755Sopenharmony_ci with open(filepath, "r") as f: 106bae44755Sopenharmony_ci content = f.read() 107bae44755Sopenharmony_ci array_syscap_set.update(re.findall(ptrn, content)) 108bae44755Sopenharmony_ci array_syscap_dict[filepath] = list() 109bae44755Sopenharmony_ci for v in array_syscap_set: 110bae44755Sopenharmony_ci array_syscap_dict.get(filepath).append(v) 111bae44755Sopenharmony_ci return array_syscap_set, array_syscap_dict 112bae44755Sopenharmony_ci 113bae44755Sopenharmony_ci 114bae44755Sopenharmony_cidef collect_syscap_from_component(project_path: str, 115bae44755Sopenharmony_ci black_dirs: tuple, 116bae44755Sopenharmony_ci key_heirarchy: tuple, 117bae44755Sopenharmony_ci bundles: list = None) -> tuple: 118bae44755Sopenharmony_ci """ 119bae44755Sopenharmony_ci 从部件的bundle.json中收集syscap 120bae44755Sopenharmony_ci :param project_path: 项目根路径 121bae44755Sopenharmony_ci :param black_dirs: 根路径下的一级目录的黑名单,不扫描这些bundle.json下面的内容 122bae44755Sopenharmony_ci :param key_heirarchy: bundle.json中syscap所在的key的元组 123bae44755Sopenharmony_ci :param bundles: 指定的bundle.json 124bae44755Sopenharmony_ci :return: syscap_set,文件名和其syscap list组成的dict 125bae44755Sopenharmony_ci """ 126bae44755Sopenharmony_ci result_dict = dict() 127bae44755Sopenharmony_ci if bundles is None: 128bae44755Sopenharmony_ci search_dir_list = [ 129bae44755Sopenharmony_ci os.path.join(project_path, x) 130bae44755Sopenharmony_ci for x in os.listdir(project_path) 131bae44755Sopenharmony_ci if os.path.isdir(os.path.join(project_path, x)) and x not in black_dirs 132bae44755Sopenharmony_ci ] 133bae44755Sopenharmony_ci for folder in search_dir_list: 134bae44755Sopenharmony_ci output = os.popen("find {} -name bundle.json".format(folder)) 135bae44755Sopenharmony_ci for line in output: 136bae44755Sopenharmony_ci line = line.strip() 137bae44755Sopenharmony_ci read_value_from_json(line, key_heirarchy, result_dict, post_handler=bundle_syscap_post_handler) 138bae44755Sopenharmony_ci output.close() 139bae44755Sopenharmony_ci else: 140bae44755Sopenharmony_ci for bundle in bundles: 141bae44755Sopenharmony_ci read_value_from_json(bundle, key_heirarchy, result_dict, post_handler=bundle_syscap_post_handler) 142bae44755Sopenharmony_ci result_set = set() 143bae44755Sopenharmony_ci for v in result_dict.values(): 144bae44755Sopenharmony_ci result_set.update(v) 145bae44755Sopenharmony_ci return result_set, result_dict 146bae44755Sopenharmony_ci 147bae44755Sopenharmony_ci 148bae44755Sopenharmony_cidef sdk_syscap_post_handler(syscap: str) -> str: 149bae44755Sopenharmony_ci return syscap.strip().lstrip("*").lstrip().lstrip("*@syscap").strip() 150bae44755Sopenharmony_ci 151bae44755Sopenharmony_ci 152bae44755Sopenharmony_cidef collect_syscap_from_sdk(ts_path: str, 153bae44755Sopenharmony_ci pattern: str = r"\* *@syscap +((?i)SystemCapability\..*)", 154bae44755Sopenharmony_ci post_handler=None 155bae44755Sopenharmony_ci ) -> tuple: 156bae44755Sopenharmony_ci """ 157bae44755Sopenharmony_ci 从sdk下的*.d.ts中收集syscap 158bae44755Sopenharmony_ci :param ts_path: ts文件的所在路径 159bae44755Sopenharmony_ci :param pattern: 匹配syscap的pattern 160bae44755Sopenharmony_ci :param post_handler: 对匹配到的syscap进行后置处理 161bae44755Sopenharmony_ci :return: syscap_set,和由文件名、syscap list组成的dict 162bae44755Sopenharmony_ci """ 163bae44755Sopenharmony_ci ts_list = list() 164bae44755Sopenharmony_ci for item in os.listdir(ts_path): 165bae44755Sopenharmony_ci file_path = os.path.join(ts_path, item) 166bae44755Sopenharmony_ci if file_path.endswith(".d.ts") and os.path.isfile(file_path): 167bae44755Sopenharmony_ci ts_list.append(file_path) 168bae44755Sopenharmony_ci syscap_dict = dict() 169bae44755Sopenharmony_ci ptrn = re.compile(pattern) 170bae44755Sopenharmony_ci for ts in ts_list: 171bae44755Sopenharmony_ci with open(ts, 'r') as f: 172bae44755Sopenharmony_ci content = f.read() 173bae44755Sopenharmony_ci sub_syscap_list = re.findall(ptrn, content) 174bae44755Sopenharmony_ci sub_syscap_set = set() 175bae44755Sopenharmony_ci for syscap in sub_syscap_list: 176bae44755Sopenharmony_ci sub_syscap_set.add(post_handler(syscap)) 177bae44755Sopenharmony_ci syscap_dict[ts] = sub_syscap_set 178bae44755Sopenharmony_ci syscap_set = set() 179bae44755Sopenharmony_ci for v in syscap_dict.values(): 180bae44755Sopenharmony_ci syscap_set.update(v) 181bae44755Sopenharmony_ci return syscap_set, syscap_dict 182bae44755Sopenharmony_ci 183bae44755Sopenharmony_ci 184bae44755Sopenharmony_cidef find_files_containes_value(value_set: set, file_values_dict: dict) -> dict: 185bae44755Sopenharmony_ci """ 186bae44755Sopenharmony_ci 查看包含指定值的文件 187bae44755Sopenharmony_ci :param value_set: 指定值 188bae44755Sopenharmony_ci :param file_values_dict: 文件和值组成的dict 189bae44755Sopenharmony_ci :return: 值和文件组成的dict 190bae44755Sopenharmony_ci """ 191bae44755Sopenharmony_ci value_files_dict = dict() 192bae44755Sopenharmony_ci for v in value_set: 193bae44755Sopenharmony_ci filename_set = set() 194bae44755Sopenharmony_ci for file in file_values_dict.keys(): 195bae44755Sopenharmony_ci if v in file_values_dict[file]: 196bae44755Sopenharmony_ci filename_set.add(file) 197bae44755Sopenharmony_ci if 0 != len(filename_set): 198bae44755Sopenharmony_ci value_files_dict[v] = filename_set 199bae44755Sopenharmony_ci return value_files_dict 200bae44755Sopenharmony_ci 201bae44755Sopenharmony_ci 202bae44755Sopenharmony_cidef mutual_diff(a_set: set, b_set: set) -> tuple: 203bae44755Sopenharmony_ci return a_set.difference(b_set), b_set.difference(a_set) 204bae44755Sopenharmony_ci 205bae44755Sopenharmony_ci 206bae44755Sopenharmony_cidef print_inconsistent(diff_set: set, a_name: str, b_name: str, table: PrettyTable, 207bae44755Sopenharmony_ci value_file_dict: dict) -> None: 208bae44755Sopenharmony_ci table.clear() 209bae44755Sopenharmony_ci if len(diff_set) != 0: 210bae44755Sopenharmony_ci table.field_names = ["index", "SysCap only in {}".format(a_name), "Files"] 211bae44755Sopenharmony_ci add_dict_as_table_row(table, value_file_dict) 212bae44755Sopenharmony_ci elif len(diff_set) == 0: 213bae44755Sopenharmony_ci table.field_names = ["All SysCap in {} have been Covered by {}".format(a_name, b_name)] 214bae44755Sopenharmony_ci print(table) 215bae44755Sopenharmony_ci print() 216bae44755Sopenharmony_ci 217bae44755Sopenharmony_ci 218bae44755Sopenharmony_cidef print_consistent(a_diff_b_set: set, b_diff_a_set: set, table: PrettyTable, a_name: str, b_name: str) -> bool: 219bae44755Sopenharmony_ci if len(a_diff_b_set) == 0 and len(b_diff_a_set) == 0: 220bae44755Sopenharmony_ci table.field_names = ["{} and {} are Consistent".format(a_name, b_name)] 221bae44755Sopenharmony_ci print(table) 222bae44755Sopenharmony_ci print() 223bae44755Sopenharmony_ci return True 224bae44755Sopenharmony_ci return False 225bae44755Sopenharmony_ci 226bae44755Sopenharmony_ci 227bae44755Sopenharmony_cidef print_check_result(a_diff_b_set: set, 228bae44755Sopenharmony_ci b_diff_a_set: set, 229bae44755Sopenharmony_ci a_name: str, 230bae44755Sopenharmony_ci b_name: str, 231bae44755Sopenharmony_ci a_value_file_dict: dict, 232bae44755Sopenharmony_ci b_value_file_dict: dict, 233bae44755Sopenharmony_ci ) -> None: 234bae44755Sopenharmony_ci f_table = PrettyTable() 235bae44755Sopenharmony_ci f_table.hrules = ALL 236bae44755Sopenharmony_ci consistent_flag = print_consistent(a_diff_b_set, b_diff_a_set, f_table, a_name, b_name) 237bae44755Sopenharmony_ci if not consistent_flag: 238bae44755Sopenharmony_ci print_inconsistent(a_diff_b_set, a_name, b_name, f_table, a_value_file_dict) 239bae44755Sopenharmony_ci print_inconsistent(b_diff_a_set, b_name, a_name, f_table, b_value_file_dict) 240bae44755Sopenharmony_ci 241bae44755Sopenharmony_ci 242bae44755Sopenharmony_cidef check_component_and_codec(project_path, syscap_define_path: str, component_black_dirs: tuple, 243bae44755Sopenharmony_ci bundle_key_heirarchy: tuple, bundles=None): 244bae44755Sopenharmony_ci component_syscap_set, component_syscap_dict = collect_syscap_from_component( 245bae44755Sopenharmony_ci project_path, black_dirs=component_black_dirs, key_heirarchy=bundle_key_heirarchy, bundles=bundles 246bae44755Sopenharmony_ci ) 247bae44755Sopenharmony_ci array_syscap_set, array_syscap_dict = collect_syscap_from_codec(syscap_define_path) 248bae44755Sopenharmony_ci component_diff_array, array_diff_component = mutual_diff(component_syscap_set, array_syscap_set) 249bae44755Sopenharmony_ci value_component_dict = find_files_containes_value( 250bae44755Sopenharmony_ci component_diff_array, component_syscap_dict 251bae44755Sopenharmony_ci ) 252bae44755Sopenharmony_ci value_h_dict = find_files_containes_value(array_diff_component, array_syscap_dict) 253bae44755Sopenharmony_ci print_check_result(component_diff_array, array_diff_component, a_name="Component", b_name="Codec", 254bae44755Sopenharmony_ci a_value_file_dict=value_component_dict, b_value_file_dict=value_h_dict) 255bae44755Sopenharmony_ci 256bae44755Sopenharmony_ci 257bae44755Sopenharmony_cidef check_component_and_sdk(project_path, component_black_dirs: tuple, component_key_heirarchy: tuple, sdk_path: str): 258bae44755Sopenharmony_ci component_syscap_set, component_syscap_dict = collect_syscap_from_component( 259bae44755Sopenharmony_ci project_path, black_dirs=component_black_dirs, key_heirarchy=component_key_heirarchy 260bae44755Sopenharmony_ci ) 261bae44755Sopenharmony_ci ts_syscap_set, ts_syscap_dict = collect_syscap_from_sdk(ts_path=sdk_path, 262bae44755Sopenharmony_ci post_handler=sdk_syscap_post_handler) 263bae44755Sopenharmony_ci component_diff_ts, ts_diff_component = mutual_diff(component_syscap_set, ts_syscap_set) 264bae44755Sopenharmony_ci value_ts_dict = find_files_containes_value(ts_diff_component, ts_syscap_dict) 265bae44755Sopenharmony_ci value_component_dict = find_files_containes_value( 266bae44755Sopenharmony_ci component_diff_ts, component_syscap_dict 267bae44755Sopenharmony_ci ) 268bae44755Sopenharmony_ci print_check_result(component_diff_ts, ts_diff_component, a_name="Component", b_name="SDK", 269bae44755Sopenharmony_ci a_value_file_dict=value_component_dict, b_value_file_dict=value_ts_dict) 270bae44755Sopenharmony_ci 271bae44755Sopenharmony_ci 272bae44755Sopenharmony_cidef check_sdk_and_codec(syscap_define_path: str, sdk_path: str) -> None: 273bae44755Sopenharmony_ci ts_syscap_set, ts_syscap_dict = collect_syscap_from_sdk(ts_path=sdk_path, 274bae44755Sopenharmony_ci post_handler=sdk_syscap_post_handler) 275bae44755Sopenharmony_ci array_syscap_set, array_syscap_dict = collect_syscap_from_codec(syscap_define_path) 276bae44755Sopenharmony_ci ts_diff_array, array_diff_ts = mutual_diff(ts_syscap_set, array_syscap_set) 277bae44755Sopenharmony_ci value_ts_dict = find_files_containes_value(ts_diff_array, ts_syscap_dict) 278bae44755Sopenharmony_ci value_h_dict = find_files_containes_value(array_diff_ts, array_syscap_dict) 279bae44755Sopenharmony_ci print_check_result(ts_diff_array, array_diff_ts, a_name="SDK", b_name="Codec", a_value_file_dict=value_ts_dict, 280bae44755Sopenharmony_ci b_value_file_dict=value_h_dict) 281bae44755Sopenharmony_ci 282bae44755Sopenharmony_ci 283bae44755Sopenharmony_cidef main(): 284bae44755Sopenharmony_ci args = get_args() 285bae44755Sopenharmony_ci project_path = args.project_path 286bae44755Sopenharmony_ci check_target = args.check_target 287bae44755Sopenharmony_ci bundles = args.bundles 288bae44755Sopenharmony_ci syscap_define_path = os.path.join(project_path, "developtools", "syscap_codec", "include", "syscap_define.h") 289bae44755Sopenharmony_ci ts_path = os.path.join(project_path, "interface", "sdk-js", "api") 290bae44755Sopenharmony_ci component_black_dirs = ("out",) 291bae44755Sopenharmony_ci bundle_syscap_heirarchy = ("component", "syscap") 292bae44755Sopenharmony_ci if (bundles is not None or (bundles is not None and len(bundles) != 0)) and check_target != "component_codec": 293bae44755Sopenharmony_ci print("error: --bundles could only be used with -t component_codec") 294bae44755Sopenharmony_ci return 295bae44755Sopenharmony_ci if "component_sdk" == check_target: 296bae44755Sopenharmony_ci check_component_and_sdk(project_path, component_black_dirs, bundle_syscap_heirarchy, sdk_path=ts_path) 297bae44755Sopenharmony_ci elif "sdk_codec" == check_target: 298bae44755Sopenharmony_ci check_sdk_and_codec(syscap_define_path=syscap_define_path, sdk_path=ts_path) 299bae44755Sopenharmony_ci elif "component_codec" == check_target: 300bae44755Sopenharmony_ci if bundles is not None and len(bundles) == 0: 301bae44755Sopenharmony_ci print(r"error: '--bundles' parameter is specified, but has no value") 302bae44755Sopenharmony_ci else: 303bae44755Sopenharmony_ci check_component_and_codec(project_path, syscap_define_path, component_black_dirs=component_black_dirs, 304bae44755Sopenharmony_ci bundle_key_heirarchy=bundle_syscap_heirarchy, bundles=bundles) 305bae44755Sopenharmony_ci 306bae44755Sopenharmony_ci 307bae44755Sopenharmony_ciif __name__ == "__main__": 308bae44755Sopenharmony_ci main() 309bae44755Sopenharmony_ci 310