diff --git a/README.md b/README.md index 440ec7b762e4aedade0bc975906bdf12a32969be..005c7bce6a39441ada137539df5bb8906c01de7f 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,10 @@ Main function: │ │ ├── main.c # command line implement │ │ └── syscap_tool.c # codec implement │ └── test -│ └── syscap_tool_test.c # syscap_tool test codec implement +│ | └── syscap_tool_test.c # syscap_tool test codec implement +| |—— tools +| │ │ +| │ └── syscap_check.py # syscap一致性检查脚本 ``` ### API @@ -79,6 +82,42 @@ SysCap tools usually integrate to IDE, APP store and bundle tools. Follow instru -o outpath, --input outpath : output path ``` +## Syscap consistency check tool + +### Functions and dependencies + +The tool provides the following functions: + +1. Collect syscap fields of all components (or specified components), compare them with arraySyscap in developtools/ syscap_codec/include/syscap_define.h, and output the check results. If they are inconsistent, output the cause of the discrepancy. +2. Collect the syscap fields of all components and compare them with the @syscap property set in *.d.ts in the Interface/sdk-js/api directory. If the check results are inconsistent, output the cause of the inconsistency. +3. Compare syscap attributes in *.d.ts in all interface/ sdK-JS/API directories with arraySyscap in developtools/syscap_codec/include/syscap_define.h. If they are inconsistent, output the cause of the inconsistency. + +### How to use it + +This tool is written in Python language and needs to be executed using the Python interpreter. + +requirements: + +```txt +prettytable==3.3.0 +``` + +usage: + +```shell +# check syscap field in all components for consistency with arraySyscap in syscap_define.h +python3 syscap_check.py -p path_of_openarmony -t component_codec + +# check that the SYSCAP field in bundle.json of the specified part is consistent with arraySyscap in syscap_define.h. Note: --bundles is valid only if --check_target is component_codec +python3 syscap_check.py -p path_of_openarmony -t component_codec -b path_of_component1/bundle.json path_of_component2/bundle.json + +# check the consistency of the syscap field of all components with the "@syscap" property set in *.d.ts +python3 syscap_check.py -p path_of_openarmony -t component_sdk + +# check the "@syscap" attribute set in *.d.ts for consistency with arraSyscap in syscap_define.h +python3 syscap_check.py -p path_of_openarmony -t sdk_codec +``` + ### Release Note -v1.0.0 2022-3-8 first release, SysCap codec supported for Windows/Linux/Mac host. \ No newline at end of file +v1.0.0 2022-3-8 first release, SysCap codec supported for Windows/Linux/Mac host. diff --git a/README_ZH.md b/README_ZH.md index 3a7e5c3e022c699c1eb7dab8ee58cc3c6a56707d..9e8e72eb5668ec9d7f56cdd1e10293ecc642e0d6 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -1,4 +1,5 @@ # 系统能力编解码工具 + 系统能力(SystemCapability, 本文中使用SysCap缩写)编解码工具应用场景如下: 应用开发时,IDE会根据应用配置的SysCap和API版本生成描述RPCID(Required Product Compatibility ID)的json文件,并调用编解码工具syscap_tool将该json文件编码成RPCID。另一方面,IDE拿到开发者导入PCID(Product Compatibility ID),使用该工具解码出设备的SysCap集合。该工具仅供IDE使用,对用户不可见。 @@ -43,6 +44,9 @@ │ │ │ │ └── syscap_codec_test.h │ │ │ └── syscap_codec_test.cpp │ │ └── syscap_tool_test.c # syscap_tool功能测试代码实现 +| |—— tools +| │ │ +| │ └── syscap_check.py # syscap一致性检查脚本 ``` ## API @@ -113,6 +117,61 @@ syscap_tool -Pdsi pcid.txt -o path/ ``` **说明:** -o 选项指定输出目录,缺省为当前目录。 +## syscap一致性检查工具 + +### 功能及依赖 + +本工具主要提供如下功能: + +1. 收集所有部件(或指定部件)的syscap字段并与developtools/sysap_codec/include/syscap_define.h中的arraySyscap比较,输出检查结果,若不一致,输出不一致的原因 +2. 收集所有部件的syscap字段并与interface/sdk-js/api目录下的*.d.ts中的“@syscap”属性集合比较,输出检查结果,若不一致,输出不一致的原因 +3. 收集所有interface/sdk-js/api目录下的*.d.ts中的syscap属性与developtools/syscap_codec/include/syscap_define.h中的arraySyscap比较,若不一致,输出不一致的原因 + +### 使用方法 + +本工具使用python语言编写,需使用python解释器进行执行。 + +requirements: + +```txt +prettytable==3.3.0 +``` + +使用python3 syscap-check.py -h或python3 syscap-check.py --help查看用法: + +```shell +usage: syscap_check.py [-h] [-p PROJECT_PATH] -t {component_codec,component_sdk,sdk_codec} + [-b [BUNDLES [BUNDLES ...]]] + +optional arguments: + -h, --help show this help message and exit + -p PROJECT_PATH, --project_path PROJECT_PATH + root path of project. default: ./ + -t {component_codec,component_sdk,sdk_codec}, --check_target {component_codec,component_sdk,sdk_codec} + the target to be compared + -b [BUNDLES [BUNDLES ...]], --bundles [BUNDLES [BUNDLES ...]] + this option will take effect only when the check_target is component_codec. allow multiple + json file. default: all bundle.json file +``` + +使用示例: + +```shell +# 检查所有部件的syscap字段与syscap_define.h中的arraySyscap一致性情况 +python3 syscap_check.py -p path_of_openarmony -t component_codec + +# 检查指定的部件的bundle.json中的syscap字段与syscap_define.h中的arraySyscap一致性情况,注意,只要当--check_target为component_codec时,--bundles才生效 +python3 syscap_check.py -p path_of_openarmony -t component_codec -b path_of_component1/bundle.json path_of_component2/bundle.json + +# 检查所有部件的syscap字段与*.d.ts中的“@syscap”属性集合的一致性情况 +python3 syscap_check.py -p path_of_openarmony -t component_sdk + +# 检查*.d.ts中的“@syscap"属性集合与syscap_define.h中的arraSyscap的一致性情况 +python3 syscap_check.py -p path_of_openarmony -t sdk_codec +``` + + + ## Release Note v1.1.0 2022-6-17 添加转字符串格式以及比较功能。 -v1.0.0 2022-3-8 首版本,提供Windows/Linux/Mac平台的系统能力编解码。 \ No newline at end of file +v1.0.0 2022-3-8 首版本,提供Windows/Linux/Mac平台的系统能力编解码。 diff --git a/tools/requirements.txt b/tools/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..fbdaf5cc87b9d3bfb48d046b5272d4af19078545 --- /dev/null +++ b/tools/requirements.txt @@ -0,0 +1 @@ +prettytable==3.3.0 diff --git a/tools/syscap_check.py b/tools/syscap_check.py new file mode 100755 index 0000000000000000000000000000000000000000..ffd7eaf2531a67807823879f5744db31bf9ab505 --- /dev/null +++ b/tools/syscap_check.py @@ -0,0 +1,296 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright (c) 2022 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import os +import json +import re +import argparse +from prettytable import PrettyTable, ALL + +table = PrettyTable() +table.hrules = ALL + + +def get_args(): + parser = argparse.ArgumentParser(add_help=True) + parser.add_argument( + "-p", + "--project_path", + default=r"./", + type=str, + help="root path of project. default: ./", + ) + parser.add_argument( + "-t", + "--check_target", + type=str, + choices=["component_codec", "component_sdk", "sdk_codec"], + required=True, + help="the target to be compared", + ) + parser.add_argument( + "-b", + "--bundles", + nargs="*", + type=str, + help="this option will take effect only when the check_target is component_codec. allow multiple json file. default: all bundle.json file", + ) + args = parser.parse_args() + return args + + +def list_to_multiline(l): + return str(l).lstrip("[").rstrip("]").replace(", ", "\n") + + +def read_value_from_json(filepath, key_hierarchy, result_dict): + """ + :param result_dict: result_dict + :param key_hierarchy: key_hierarchy list + :param filepath: fullpath of file + :return: result_dict, {filepath:value_list} + """ + if os.path.exists(filepath) is False: + print('error: file "{}" not exists.'.format(filepath)) + return result_dict + if os.path.isfile(filepath) is False: + print('error: "{}" is not a file.') + return result_dict + with open(filepath, "r") as f: + data = json.load(f) + for key in key_hierarchy: + try: + data = data[key] + except KeyError: + print( + 'warning: can\'t find the key:"{}" in file "{}"'.format( + key, filepath + ) + ) + return result_dict + data = [x for x in data if len(x) != 0 and x.isspace() == False] + if len(data) != 0: + result_dict[filepath] = data + return result_dict + + +def collect_syscap_from_codec(filepath): + arraySyscap_set = set() + pattern = r'{"(.*)"' + with open(filepath, "r") as f: + for line in f: + syscap = re.search(pattern, line.strip()) + if syscap is not None: + arraySyscap_set.add(syscap.group(0).lstrip("{").strip('"')) + return arraySyscap_set + + +def collect_syscap_from_component(project_path, bundles=None): + result_dict = dict() + key_heirarchy = ["component", "syscap"] + if bundles is None: + subsystem_list = [ + x + for x in os.listdir(project_path) + if os.path.isdir(os.path.join(project_path, x)) and x != "out" + ] + for ss in subsystem_list: + output = os.popen( + "find {} -name bundle.json".format(os.path.join(project_path, ss)) + ) + for line in output: + try: + read_value_from_json(line.strip(), key_heirarchy, result_dict) + except Exception as e: + print(e.with_traceback()) + else: + for b in bundles: + try: + result_dict = read_value_from_json(b, key_heirarchy, result_dict) + except Exception as e: + print(e.with_traceback()) + result_set = set() + for v in result_dict.values(): + result_set.update(v) + return result_set, result_dict + + +def collect_syscap_from_sdk(project_path): + full_path = os.path.join(project_path, "interface", "sdk-js", "api") + ts_list = [ + os.path.join(full_path, x) for x in os.listdir(full_path) if x.endswith(".d.ts") + ] + syscap_dict = dict() + pattern = r"\* *@syscap +(.*)" + syscap_set = set() + for ts in ts_list: + with open(ts, "r") as f: + sub_set = set() + for line in f: + syscap = re.search(pattern, line) + if syscap is not None: + ss = syscap.group(0).strip().lstrip("\* @syscap ").strip() + sub_set.add(ss) + syscap_dict[ts] = sub_set + for v in syscap_dict.values(): + syscap_set.update(v) + return syscap_set, syscap_dict + + +def find_files_containes_value(value_set, file_values_dict): + value_files_dict = dict() + for v in value_set: + filename_set = set() + for file in file_values_dict.keys(): + if v in file_values_dict[file]: + filename_set.add(file) + if 0 != len(filename_set): + value_files_dict[v] = filename_set + return value_files_dict + + +def check_component_and_codec(project_path, bundles=None): + if bundles is not None and len(bundles) > 0: + component_syscap_set, component_syscap_dict = collect_syscap_from_component( + project_path, bundles + ) + else: + component_syscap_set, component_syscap_dict = collect_syscap_from_component( + project_path + ) + arraySyscap_set = collect_syscap_from_codec( + os.path.join( + project_path, "developtools", "syscap_codec", "include", "syscap_define.h" + ) + ) + component_diff_array = component_syscap_set.difference(arraySyscap_set) + value_files_dict = find_files_containes_value( + component_diff_array, component_syscap_dict + ) + array_diff_component = arraySyscap_set.difference(component_syscap_set) + if 0 == len(component_diff_array) and 0 == len(array_diff_component): + table.clear() + table.field_names = ["Component and Codec are Consistent"] + print(table) + return + if 0 != len(component_diff_array): + table.field_names = ["Syscap Only in Component", "Files"] + for syscap, files in value_files_dict.items(): + table.add_row([syscap, list_to_multiline(sorted(files))]) + elif 0 == len(component_diff_array): + table.field_names = ["All Syscap in Component have been Covered by Codec"] + print("\n") + print(table) + table.clear() + if 0 != len(array_diff_component): + table.field_names = ["SysCap Only in Codec"] + table.add_row([list_to_multiline(sorted(list(array_diff_component)))]) + elif 0 == len(array_diff_component): + table.field_names = ["All SysCap in Codec have been Covered by Component"] + print("\n") + print(table) + + +def check_component_and_sdk(project_path): + component_syscap_set, component_syscap_dict = collect_syscap_from_component( + project_path + ) + ts_syscap_set, ts_syscap_dict = collect_syscap_from_sdk(project_path) + ts_diff_component = ts_syscap_set.difference(component_syscap_set) + value_ts_dict = find_files_containes_value(ts_diff_component, ts_syscap_dict) + component_diff_ts = component_syscap_set.difference(ts_syscap_set) + value_component_dict = find_files_containes_value( + component_diff_ts, component_syscap_dict + ) + if 0 == len(ts_diff_component) and 0 == len(component_diff_ts): + table.clear() + table.field_names = ["SDK and Component are Consistent"] + print(table) + return + table.clear() + if 0 != len(component_diff_ts): + table.field_names = ["SysCap Only in Component", "Files"] + for syscap, files in value_component_dict.items(): + table.add_row([syscap, list_to_multiline(sorted(list(files)))]) + elif 0 == len(component_diff_ts): + table.field_names = ["SysCap in Component have been Covered by SDK"] + print("\n") + print(table) + table.clear() + if 0 != len(ts_diff_component): + table.field_names = ["SysCap Only in SDK", "Files"] + for syscap, files in value_ts_dict.items(): + table.add_row([syscap, list_to_multiline(sorted(list(files)))]) + elif 0 == len(ts_diff_component): + table.field_names = ["All SysCap in SDK have been Covered by Component"] + print("\n") + print(table) + + +def check_sdk_and_codec(project_path): + ts_syscap_set, ts_syscap_dict = collect_syscap_from_sdk(project_path) + arraySyscap_set = collect_syscap_from_codec( + os.path.join( + project_path, "developtools", "syscap_codec", "include", "syscap_define.h" + ) + ) + ts_diff_array = ts_syscap_set.difference(arraySyscap_set) + value_ts_dict = find_files_containes_value(ts_diff_array, ts_syscap_dict) + array_diff_ts = arraySyscap_set.difference(ts_syscap_set) + if 0 == len(ts_diff_array) and 0 == len(array_diff_ts): + table.clear() + table.field_names = ["SDK and Codec are Consistent"] + print(table) + return + table.clear() + if 0 != len(ts_diff_array): + table.field_names = ["SysCap Only in SDK", "Files"] + for syscap, files in value_ts_dict.items(): + table.add_row([syscap, list_to_multiline(sorted(list(files)))]) + elif 0 == len(ts_diff_array): + table.field_names = ["SysCap in SDK have been Covered by Codec"] + print("\n") + print(table) + table.clear() + if 0 != len(array_diff_ts): + table.field_names = ["SysCap Only in Codec"] + table.add_row([list_to_multiline(sorted(list(array_diff_ts)))]) + elif 0 == len(array_diff_ts): + table.field_names = ["SysCap in Codec have been Covered by SDK"] + print("\n") + print(table) + + +def main(): + args = get_args() + project_path = args.project_path + check_target = args.check_target + bundles = args.bundles + if "component_codec" == check_target: + if bundles is None: + check_component_and_codec(project_path) + else: + if 0 == len(bundles): + print(r"error: '--bundles' parameter is specified, but has no value") + else: + check_component_and_codec(project_path, bundles) + elif "component_sdk" == check_target: + check_component_and_sdk(project_path) + elif "sdk_codec" == check_target: + check_sdk_and_codec(project_path) + + +if __name__ == "__main__": + main()