1#!/usr/bin/env python3 2# -*- coding: utf-8 -*- 3# Copyright (c) 2022 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 16# This file contains a RomAnalyzer for rom analyzation of standard device. 17 18import argparse 19import json 20import os 21import sys 22import typing 23from copy import deepcopy 24from typing import * 25 26from pkgs.basic_tool import BasicTool 27from pkgs.gn_common_tool import GnCommonTool 28from pkgs.simple_excel_writer import SimpleExcelWriter 29 30debug = bool(sys.gettrace()) 31 32NOTFOUND = "NOTFOUND" 33 34 35class RomAnalyzer: 36 @classmethod 37 def __collect_product_info(cls, system_module_info_json: Text, 38 project_path: Text) -> Dict[Text, Dict[Text, Text]]: 39 """ 40 根据system_module_info.json生成target字典 41 """ 42 with open(system_module_info_json, 'r', encoding='utf-8') as f: 43 product_list = json.loads(f.read()) 44 project_path = BasicTool.get_abs_path(project_path) 45 product_info_dict: Dict[Text, Dict[Text, Text]] = dict() 46 for unit in product_list: 47 dest: List = unit.get("dest") 48 if dest is None: 49 print("warning: keyword 'dest' not found in {}".format( 50 system_module_info_json)) 51 continue 52 label: Text = unit.get("label") 53 gn_path = component_name = subsystem_name = None 54 if label: 55 gn_path = os.path.join(project_path, label.split(':')[ 56 0].lstrip('/'), "BUILD.gn") 57 component_name = unit.get("part_name") 58 subsystem_name = unit.get("subsystem_name") 59 if (not component_name) or (not subsystem_name): 60 cn, sn = GnCommonTool.find_part_subsystem( 61 gn_path, project_path) 62 component_name = cn if not component_name else component_name 63 subsystem_name = sn if not subsystem_name else subsystem_name 64 else: 65 print("warning: keyword 'label' not found in {}".format(unit)) 66 for target in dest: 67 product_info_dict[target] = { 68 "component_name": component_name, 69 "subsystem_name": subsystem_name, 70 "gn_path": gn_path, 71 } 72 return product_info_dict 73 74 @classmethod 75 def __save_result_as_excel(cls, result_dict: dict, output_name: str): 76 header = ["subsystem_name", "component_name", 77 "output_file", "size(Byte)"] 78 tmp_dict = deepcopy(result_dict) 79 excel_writer = SimpleExcelWriter("rom") 80 excel_writer.set_sheet_header(headers=header) 81 subsystem_start_row = 1 82 subsystem_end_row = 0 83 subsystem_col = 0 84 component_start_row = 1 85 component_end_row = 0 86 component_col = 1 87 88 for subsystem_name in tmp_dict.keys(): 89 subsystem_dict = tmp_dict.get(subsystem_name) 90 subsystem_size = subsystem_dict.get("size") 91 subsystem_file_count = subsystem_dict.get("file_count") 92 del subsystem_dict["file_count"] 93 del subsystem_dict["size"] 94 subsystem_end_row += subsystem_file_count 95 96 for component_name in subsystem_dict.keys(): 97 component_dict: Dict[str, int] = subsystem_dict.get( 98 component_name) 99 component_size = component_dict.get("size") 100 component_file_count = component_dict.get("file_count") 101 del component_dict["file_count"] 102 del component_dict["size"] 103 component_end_row += component_file_count 104 105 for file_name, size in component_dict.items(): 106 excel_writer.append_line( 107 [subsystem_name, component_name, file_name, size]) 108 excel_writer.write_merge(component_start_row, component_col, component_end_row, component_col, 109 component_name) 110 component_start_row = component_end_row + 1 111 excel_writer.write_merge(subsystem_start_row, subsystem_col, subsystem_end_row, subsystem_col, 112 subsystem_name) 113 subsystem_start_row = subsystem_end_row + 1 114 excel_writer.save(output_name + ".xls") 115 116 @classmethod 117 def __put(cls, unit: typing.Dict[Text, Any], result_dict: typing.Dict[Text, Dict]): 118 """ 119 subsystem_name:{ 120 component_name: { 121 file_name: file_size 122 } 123 } 124 """ 125 component_name = NOTFOUND if unit.get( 126 "component_name") is None else unit.get("component_name") 127 subsystem_name = NOTFOUND if unit.get( 128 "subsystem_name") is None else unit.get("subsystem_name") 129 size = unit.get("size") 130 relative_filepath = unit.get("relative_filepath") 131 if result_dict.get(subsystem_name) is None: # 子系统 132 result_dict[subsystem_name] = dict() 133 result_dict[subsystem_name]["size"] = 0 134 result_dict[subsystem_name]["file_count"] = 0 135 136 if result_dict.get(subsystem_name).get(component_name) is None: # 部件 137 result_dict[subsystem_name][component_name] = dict() 138 result_dict[subsystem_name][component_name]["size"] = 0 139 result_dict[subsystem_name][component_name]["file_count"] = 0 140 result_dict[subsystem_name]["size"] += size 141 result_dict[subsystem_name]["file_count"] += 1 142 result_dict[subsystem_name][component_name]["size"] += size 143 result_dict[subsystem_name][component_name]["file_count"] += 1 144 result_dict[subsystem_name][component_name][relative_filepath] = size 145 146 @classmethod 147 def analysis(cls, system_module_info_json: Text, product_dirs: List[str], 148 project_path: Text, product_name: Text, output_file: Text, output_execel: bool): 149 """ 150 system_module_info_json: json文件 151 product_dirs:要处理的产物的路径列表如["vendor", "system/"] 152 project_path: 项目根路径 153 product_name: eg,rk3568 154 output_file: basename of output file 155 """ 156 project_path = BasicTool.get_abs_path(project_path) 157 phone_dir = os.path.join( 158 project_path, "out", product_name, "packages", "phone") 159 product_dirs = [os.path.join(phone_dir, d) for d in product_dirs] 160 product_info_dict = cls.__collect_product_info( 161 system_module_info_json, project_path) # 所有产物信息 162 result_dict: Dict[Text:Dict] = dict() 163 for d in product_dirs: 164 file_list: List[Text] = BasicTool.find_all_files(d) 165 for f in file_list: 166 size = os.path.getsize(f) 167 relative_filepath = f.replace(phone_dir, "").lstrip(os.sep) 168 unit: Dict[Text, Any] = product_info_dict.get( 169 relative_filepath) 170 if unit is None: 171 unit = dict() 172 unit["size"] = size 173 unit["relative_filepath"] = relative_filepath 174 cls.__put(unit, result_dict) 175 output_dir, _ = os.path.split(output_file) 176 if len(output_dir) != 0: 177 os.makedirs(output_dir, exist_ok=True) 178 with open(output_file + ".json", 'w', encoding='utf-8') as f: 179 f.write(json.dumps(result_dict, indent=4)) 180 if output_execel: 181 cls.__save_result_as_excel(result_dict, output_file) 182 183 184def get_args(): 185 VERSION = 2.0 186 parser = argparse.ArgumentParser( 187 description=f"analyze rom size of component.\n") 188 parser.add_argument("-v", "-version", action="version", 189 version=f"version {VERSION}") 190 parser.add_argument("-p", "--project_path", type=str, required=True, 191 help="root path of openharmony. eg: -p ~/openharmony") 192 parser.add_argument("-j", "--module_info_json", required=True, type=str, 193 help="path of out/{product_name}/packages/phone/system_module_info.json") 194 parser.add_argument("-n", "--product_name", required=True, 195 type=str, help="product name. eg: -n rk3568") 196 parser.add_argument("-d", "--product_dir", required=True, action="append", 197 help="subdirectories of out/{product_name}/packages/phone to be counted." 198 "eg: -d system -d vendor") 199 parser.add_argument("-o", "--output_file", type=str, default="rom_analysis_result", 200 help="basename of output file, default: rom_analysis_result. eg: demo/rom_analysis_result") 201 parser.add_argument("-e", "--excel", type=bool, default=False, 202 help="if output result as excel, default: False. eg: -e True") 203 args = parser.parse_args() 204 return args 205 206 207if __name__ == '__main__': 208 args = get_args() 209 module_info_json = args.module_info_json 210 project_path = args.project_path 211 product_name = args.product_name 212 product_dirs = args.product_dir 213 output_file = args.output_file 214 output_excel = args.excel 215 RomAnalyzer.analysis(module_info_json, product_dirs, 216 project_path, product_name, output_file, output_excel) 217