• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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