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 os 17import sys 18import fnmatch 19import argparse 20import json 21 22 23def is_combine_jars(contents: dict): 24 if contents.get('deps_info').get('combine_target'): 25 return True 26 else: 27 return False 28 29 30def get_sources_file(build_config: str): 31 if not fnmatch.fnmatch(build_config, "*java*.build_config"): 32 return None 33 with open(build_config, 'r') as config_f: 34 contents = json.load(config_f) 35 if is_combine_jars(contents): 36 return None 37 return contents.get('deps_info').get('java_sources_file') 38 39 40def read_file(sources_file: str): 41 contents = [] 42 if not os.path.exists(sources_file): 43 return [] 44 with open(sources_file, 'r') as this: 45 contents = [line.strip() for line in this.readlines()] 46 return contents 47 48 49def get_subsystem_paths(file: str, root_dir: str): 50 paths = { 51 'common': 'build', 52 'thirdparty': 'third_party', 53 'test': 'test', 54 'mcl': 'mcl' 55 } 56 with open(file, 'r') as jfile: 57 data = json.load(jfile) 58 for key in data.keys(): 59 path = data.get(key).get('path') 60 if os.path.exists(os.path.join(root_dir, path)): 61 paths[key] = path 62 63 return paths 64 65 66def overlap_rate_key(element): 67 return element['overlap_rate'] 68 69 70def compute_overlap_rate_by_subsystem(options, paths: str, program_language: str): 71 objs = [] 72 if program_language == 'c': 73 pattern = '*.o' 74 if program_language == 'java': 75 pattern = '*.build_config' 76 for root, _, files in os.walk(options.build_out_dir): 77 for file in fnmatch.filter(files, pattern): 78 if program_language == 'c': 79 splits = os.path.join(root, file).split('/obj/') 80 obj = ''.join(splits[1:]) 81 if obj == '': 82 continue 83 if obj.find('/gen/') != -1: 84 splits = obj.split('/gen/') 85 obj = ''.join(splits[1:]) 86 objs.append(obj) 87 if program_language == 'java': 88 sources_file = get_sources_file(os.path.join(root, file)) 89 if not sources_file: 90 continue 91 for java_file in read_file( 92 os.path.join(options.build_out_dir, sources_file)): 93 if fnmatch.fnmatch(java_file, "*/generated_java/*"): 94 continue 95 objs.append(java_file) 96 97 total_builds = len(objs) 98 total_files = len(set(objs)) 99 if total_builds == 0 or total_files == 0: 100 return 101 102 statistics = [] 103 for subsystem in sorted(paths.keys()): 104 path = paths.get(subsystem) 105 if program_language == 'c': 106 pattern = '{}*'.format(path) 107 if program_language == 'java': 108 pattern = '../../{}*'.format(path) 109 110 sub_objs = [] 111 for obj in fnmatch.filter(objs, pattern): 112 sub_objs.append(obj) 113 builds = len(sub_objs) 114 files = len(set(sub_objs)) 115 if files == 0: 116 continue 117 overlap_rate = float(builds) / float(files) 118 sub_stat = { 119 "builds": builds, 120 "builds_percentage": 100 * float(builds) / float(total_builds), 121 "files": files, 122 "files_percentage": 100 * float(files) / float(total_files), 123 "overlap_rate": overlap_rate, 124 "subsystem": subsystem, 125 } 126 statistics.append(sub_stat) 127 print('{} targets overlap rate statistics'.format(program_language)) 128 print('{:16}\t{:8}\t{:5}\t{:8}\t{:5}\t{:4}'.format( 129 'subsystem', 'files NO.', 'percentage', 'builds NO.', 'percentage', 130 'overlap rate')) 131 132 for item in sorted(statistics, key=overlap_rate_key, reverse=True): 133 print('{:16}\t{:8}\t{:2.1f}%\t{:8}\t{:2.1f}%\t{:.2f}'.format( 134 item.get('subsystem'), item.get('files'), 135 item.get('files_percentage'), item.get('builds'), 136 item.get('builds_percentage'), item.get('overlap_rate'))) 137 print('\n{} overall build overlap rate: {:.2f}\n\n'.format( 138 program_language, 139 float(total_builds) / float(total_files))) 140 141 142def main(): 143 parser = argparse.ArgumentParser() 144 parser.add_argument('--build-out-dir', help='base directory to analyze.') 145 parser.add_argument('--root-source-dir', help='source root directory.') 146 parser.add_argument( 147 '--subsystem-config-file', help='path to subsystem java targets.') 148 parser.add_argument( 149 '--subsystem-config-overlay-file', help='path to subsystem overlay java targets.') 150 151 options = parser.parse_args() 152 153 paths = get_subsystem_paths(options.subsystem_config_file, 154 os.path.realpath(options.root_source_dir)) 155 compute_overlap_rate_by_subsystem(options, paths, 'c') 156 compute_overlap_rate_by_subsystem(options, paths, 'java') 157 158 159if __name__ == '__main__': 160 sys.exit(main()) 161