161 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			161 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
| #!/usr/bin/env python
 | |
| # -*- coding: utf-8 -*-
 | |
| # Copyright (c) 2021 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 sys
 | |
| import fnmatch
 | |
| import argparse
 | |
| import json
 | |
| 
 | |
| 
 | |
| def is_combine_jars(contents):
 | |
|     if contents.get('deps_info').get('combine_target'):
 | |
|         return True
 | |
|     else:
 | |
|         return False
 | |
| 
 | |
| 
 | |
| def get_sources_file(build_config):
 | |
|     if not fnmatch.fnmatch(build_config, "*java*.build_config"):
 | |
|         return None
 | |
|     with open(build_config, 'r') as config_f:
 | |
|         contents = json.load(config_f)
 | |
|         if is_combine_jars(contents):
 | |
|             return None
 | |
|         return contents.get('deps_info').get('java_sources_file')
 | |
| 
 | |
| 
 | |
| def read_file(sources_file):
 | |
|     contents = []
 | |
|     if not os.path.exists(sources_file):
 | |
|         return []
 | |
|     with open(sources_file, 'r') as this:
 | |
|         contents = [line.strip() for line in this.readlines()]
 | |
|     return contents
 | |
| 
 | |
| 
 | |
| def get_subsystem_paths(file, root_dir):
 | |
|     paths = {
 | |
|         'common': 'build',
 | |
|         'thirdparty': 'third_party',
 | |
|         'test': 'test',
 | |
|         'mcl': 'mcl'
 | |
|     }
 | |
|     with open(file, 'r') as jfile:
 | |
|         data = json.load(jfile)
 | |
|         for key in data.keys():
 | |
|             path = data.get(key).get('path')
 | |
|             if os.path.exists(os.path.join(root_dir, path)):
 | |
|                 paths[key] = path
 | |
| 
 | |
|     return paths
 | |
| 
 | |
| 
 | |
| def overlap_rate_key(element):
 | |
|     return element['overlap_rate']
 | |
| 
 | |
| 
 | |
| def compute_overlap_rate_by_subsystem(options, paths, program_language):
 | |
|     objs = []
 | |
|     if program_language == 'c':
 | |
|         pattern = '*.o'
 | |
|     if program_language == 'java':
 | |
|         pattern = '*.build_config'
 | |
|     for root, _, files in os.walk(options.build_out_dir):
 | |
|         for file in fnmatch.filter(files, pattern):
 | |
|             if program_language == 'c':
 | |
|                 splits = os.path.join(root, file).split('/obj/')
 | |
|                 obj = ''.join(splits[1:])
 | |
|                 if obj == '':
 | |
|                     continue
 | |
|                 if obj.find('/gen/') != -1:
 | |
|                     splits = obj.split('/gen/')
 | |
|                     obj = ''.join(splits[1:])
 | |
|                 objs.append(obj)
 | |
|             if program_language == 'java':
 | |
|                 sources_file = get_sources_file(os.path.join(root, file))
 | |
|                 if not sources_file:
 | |
|                     continue
 | |
|                 for java_file in read_file(
 | |
|                         os.path.join(options.build_out_dir, sources_file)):
 | |
|                     if fnmatch.fnmatch(java_file, "*/generated_java/*"):
 | |
|                         continue
 | |
|                     objs.append(java_file)
 | |
| 
 | |
|     total_builds = len(objs)
 | |
|     total_files = len(set(objs))
 | |
|     if total_builds == 0 or total_files == 0:
 | |
|         return
 | |
| 
 | |
|     statistics = []
 | |
|     for subsystem in sorted(paths.keys()):
 | |
|         path = paths.get(subsystem)
 | |
|         if program_language == 'c':
 | |
|             pattern = '{}*'.format(path)
 | |
|         if program_language == 'java':
 | |
|             pattern = '../../{}*'.format(path)
 | |
| 
 | |
|         sub_objs = []
 | |
|         for obj in fnmatch.filter(objs, pattern):
 | |
|             sub_objs.append(obj)
 | |
|         builds = len(sub_objs)
 | |
|         files = len(set(sub_objs))
 | |
|         if files == 0:
 | |
|             continue
 | |
|         overlap_rate = float(builds) / float(files)
 | |
|         sub_stat = {
 | |
|             "builds": builds,
 | |
|             "builds_percentage": 100 * float(builds) / float(total_builds),
 | |
|             "files": files,
 | |
|             "files_percentage": 100 * float(files) / float(total_files),
 | |
|             "overlap_rate": overlap_rate,
 | |
|             "subsystem": subsystem,
 | |
|         }
 | |
|         statistics.append(sub_stat)
 | |
|     print('{} targets overlap rate statistics'.format(program_language))
 | |
|     print('{:16}\t{:8}\t{:5}\t{:8}\t{:5}\t{:4}'.format(
 | |
|         'subsystem', 'files NO.', 'percentage', 'builds NO.', 'percentage',
 | |
|         'overlap rate'))
 | |
| 
 | |
|     for item in sorted(statistics, key=overlap_rate_key, reverse=True):
 | |
|         print('{:16}\t{:8}\t{:2.1f}%\t{:8}\t{:2.1f}%\t{:.2f}'.format(
 | |
|             item.get('subsystem'), item.get('files'),
 | |
|             item.get('files_percentage'), item.get('builds'),
 | |
|             item.get('builds_percentage'), item.get('overlap_rate')))
 | |
|     print('\n{} overall build overlap rate: {:.2f}\n\n'.format(
 | |
|         program_language,
 | |
|         float(total_builds) / float(total_files)))
 | |
| 
 | |
| 
 | |
| def main():
 | |
|     parser = argparse.ArgumentParser()
 | |
|     parser.add_argument('--build-out-dir', help='base directory to analyze.')
 | |
|     parser.add_argument('--root-source-dir', help='source root directory.')
 | |
|     parser.add_argument(
 | |
|         '--subsystem-config-file', help='path to subsystem java targets.')
 | |
|     parser.add_argument(
 | |
|         '--subsystem-config-overlay-file', help='path to subsystem overlay java targets.')
 | |
| 
 | |
|     options = parser.parse_args()
 | |
| 
 | |
|     paths = get_subsystem_paths(options.subsystem_config_file,
 | |
|                                 os.path.realpath(options.root_source_dir))
 | |
|     compute_overlap_rate_by_subsystem(options, paths, 'c')
 | |
|     compute_overlap_rate_by_subsystem(options, paths, 'java')
 | |
| 
 | |
| 
 | |
| if __name__ == '__main__':
 | |
|     sys.exit(main())
 |