15f9996aaSopenharmony_ci#!/usr/bin/env python
25f9996aaSopenharmony_ci# -*- coding: utf-8 -*-
35f9996aaSopenharmony_ci# Copyright (c) 2021 Huawei Device Co., Ltd.
45f9996aaSopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License");
55f9996aaSopenharmony_ci# you may not use this file except in compliance with the License.
65f9996aaSopenharmony_ci# You may obtain a copy of the License at
75f9996aaSopenharmony_ci#
85f9996aaSopenharmony_ci#     http://www.apache.org/licenses/LICENSE-2.0
95f9996aaSopenharmony_ci#
105f9996aaSopenharmony_ci# Unless required by applicable law or agreed to in writing, software
115f9996aaSopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS,
125f9996aaSopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135f9996aaSopenharmony_ci# See the License for the specific language governing permissions and
145f9996aaSopenharmony_ci# limitations under the License.
155f9996aaSopenharmony_ci
165f9996aaSopenharmony_ciimport os
175f9996aaSopenharmony_ciimport sys
185f9996aaSopenharmony_ciimport fnmatch
195f9996aaSopenharmony_ciimport argparse
205f9996aaSopenharmony_ciimport json
215f9996aaSopenharmony_ci
225f9996aaSopenharmony_ci
235f9996aaSopenharmony_cidef is_combine_jars(contents: dict):
245f9996aaSopenharmony_ci    if contents.get('deps_info').get('combine_target'):
255f9996aaSopenharmony_ci        return True
265f9996aaSopenharmony_ci    else:
275f9996aaSopenharmony_ci        return False
285f9996aaSopenharmony_ci
295f9996aaSopenharmony_ci
305f9996aaSopenharmony_cidef get_sources_file(build_config: str):
315f9996aaSopenharmony_ci    if not fnmatch.fnmatch(build_config, "*java*.build_config"):
325f9996aaSopenharmony_ci        return None
335f9996aaSopenharmony_ci    with open(build_config, 'r') as config_f:
345f9996aaSopenharmony_ci        contents = json.load(config_f)
355f9996aaSopenharmony_ci        if is_combine_jars(contents):
365f9996aaSopenharmony_ci            return None
375f9996aaSopenharmony_ci        return contents.get('deps_info').get('java_sources_file')
385f9996aaSopenharmony_ci
395f9996aaSopenharmony_ci
405f9996aaSopenharmony_cidef read_file(sources_file: str):
415f9996aaSopenharmony_ci    contents = []
425f9996aaSopenharmony_ci    if not os.path.exists(sources_file):
435f9996aaSopenharmony_ci        return []
445f9996aaSopenharmony_ci    with open(sources_file, 'r') as this:
455f9996aaSopenharmony_ci        contents = [line.strip() for line in this.readlines()]
465f9996aaSopenharmony_ci    return contents
475f9996aaSopenharmony_ci
485f9996aaSopenharmony_ci
495f9996aaSopenharmony_cidef get_subsystem_paths(file: str, root_dir: str):
505f9996aaSopenharmony_ci    paths = {
515f9996aaSopenharmony_ci        'common': 'build',
525f9996aaSopenharmony_ci        'thirdparty': 'third_party',
535f9996aaSopenharmony_ci        'test': 'test',
545f9996aaSopenharmony_ci        'mcl': 'mcl'
555f9996aaSopenharmony_ci    }
565f9996aaSopenharmony_ci    with open(file, 'r') as jfile:
575f9996aaSopenharmony_ci        data = json.load(jfile)
585f9996aaSopenharmony_ci        for key in data.keys():
595f9996aaSopenharmony_ci            path = data.get(key).get('path')
605f9996aaSopenharmony_ci            if os.path.exists(os.path.join(root_dir, path)):
615f9996aaSopenharmony_ci                paths[key] = path
625f9996aaSopenharmony_ci
635f9996aaSopenharmony_ci    return paths
645f9996aaSopenharmony_ci
655f9996aaSopenharmony_ci
665f9996aaSopenharmony_cidef overlap_rate_key(element):
675f9996aaSopenharmony_ci    return element['overlap_rate']
685f9996aaSopenharmony_ci
695f9996aaSopenharmony_ci
705f9996aaSopenharmony_cidef compute_overlap_rate_by_subsystem(options, paths: str, program_language: str):
715f9996aaSopenharmony_ci    objs = []
725f9996aaSopenharmony_ci    if program_language == 'c':
735f9996aaSopenharmony_ci        pattern = '*.o'
745f9996aaSopenharmony_ci    if program_language == 'java':
755f9996aaSopenharmony_ci        pattern = '*.build_config'
765f9996aaSopenharmony_ci    for root, _, files in os.walk(options.build_out_dir):
775f9996aaSopenharmony_ci        for file in fnmatch.filter(files, pattern):
785f9996aaSopenharmony_ci            if program_language == 'c':
795f9996aaSopenharmony_ci                splits = os.path.join(root, file).split('/obj/')
805f9996aaSopenharmony_ci                obj = ''.join(splits[1:])
815f9996aaSopenharmony_ci                if obj == '':
825f9996aaSopenharmony_ci                    continue
835f9996aaSopenharmony_ci                if obj.find('/gen/') != -1:
845f9996aaSopenharmony_ci                    splits = obj.split('/gen/')
855f9996aaSopenharmony_ci                    obj = ''.join(splits[1:])
865f9996aaSopenharmony_ci                objs.append(obj)
875f9996aaSopenharmony_ci            if program_language == 'java':
885f9996aaSopenharmony_ci                sources_file = get_sources_file(os.path.join(root, file))
895f9996aaSopenharmony_ci                if not sources_file:
905f9996aaSopenharmony_ci                    continue
915f9996aaSopenharmony_ci                for java_file in read_file(
925f9996aaSopenharmony_ci                        os.path.join(options.build_out_dir, sources_file)):
935f9996aaSopenharmony_ci                    if fnmatch.fnmatch(java_file, "*/generated_java/*"):
945f9996aaSopenharmony_ci                        continue
955f9996aaSopenharmony_ci                    objs.append(java_file)
965f9996aaSopenharmony_ci
975f9996aaSopenharmony_ci    total_builds = len(objs)
985f9996aaSopenharmony_ci    total_files = len(set(objs))
995f9996aaSopenharmony_ci    if total_builds == 0 or total_files == 0:
1005f9996aaSopenharmony_ci        return
1015f9996aaSopenharmony_ci
1025f9996aaSopenharmony_ci    statistics = []
1035f9996aaSopenharmony_ci    for subsystem in sorted(paths.keys()):
1045f9996aaSopenharmony_ci        path = paths.get(subsystem)
1055f9996aaSopenharmony_ci        if program_language == 'c':
1065f9996aaSopenharmony_ci            pattern = '{}*'.format(path)
1075f9996aaSopenharmony_ci        if program_language == 'java':
1085f9996aaSopenharmony_ci            pattern = '../../{}*'.format(path)
1095f9996aaSopenharmony_ci
1105f9996aaSopenharmony_ci        sub_objs = []
1115f9996aaSopenharmony_ci        for obj in fnmatch.filter(objs, pattern):
1125f9996aaSopenharmony_ci            sub_objs.append(obj)
1135f9996aaSopenharmony_ci        builds = len(sub_objs)
1145f9996aaSopenharmony_ci        files = len(set(sub_objs))
1155f9996aaSopenharmony_ci        if files == 0:
1165f9996aaSopenharmony_ci            continue
1175f9996aaSopenharmony_ci        overlap_rate = float(builds) / float(files)
1185f9996aaSopenharmony_ci        sub_stat = {
1195f9996aaSopenharmony_ci            "builds": builds,
1205f9996aaSopenharmony_ci            "builds_percentage": 100 * float(builds) / float(total_builds),
1215f9996aaSopenharmony_ci            "files": files,
1225f9996aaSopenharmony_ci            "files_percentage": 100 * float(files) / float(total_files),
1235f9996aaSopenharmony_ci            "overlap_rate": overlap_rate,
1245f9996aaSopenharmony_ci            "subsystem": subsystem,
1255f9996aaSopenharmony_ci        }
1265f9996aaSopenharmony_ci        statistics.append(sub_stat)
1275f9996aaSopenharmony_ci    print('{} targets overlap rate statistics'.format(program_language))
1285f9996aaSopenharmony_ci    print('{:16}\t{:8}\t{:5}\t{:8}\t{:5}\t{:4}'.format(
1295f9996aaSopenharmony_ci        'subsystem', 'files NO.', 'percentage', 'builds NO.', 'percentage',
1305f9996aaSopenharmony_ci        'overlap rate'))
1315f9996aaSopenharmony_ci
1325f9996aaSopenharmony_ci    for item in sorted(statistics, key=overlap_rate_key, reverse=True):
1335f9996aaSopenharmony_ci        print('{:16}\t{:8}\t{:2.1f}%\t{:8}\t{:2.1f}%\t{:.2f}'.format(
1345f9996aaSopenharmony_ci            item.get('subsystem'), item.get('files'),
1355f9996aaSopenharmony_ci            item.get('files_percentage'), item.get('builds'),
1365f9996aaSopenharmony_ci            item.get('builds_percentage'), item.get('overlap_rate')))
1375f9996aaSopenharmony_ci    print('\n{} overall build overlap rate: {:.2f}\n\n'.format(
1385f9996aaSopenharmony_ci        program_language,
1395f9996aaSopenharmony_ci        float(total_builds) / float(total_files)))
1405f9996aaSopenharmony_ci
1415f9996aaSopenharmony_ci
1425f9996aaSopenharmony_cidef main():
1435f9996aaSopenharmony_ci    parser = argparse.ArgumentParser()
1445f9996aaSopenharmony_ci    parser.add_argument('--build-out-dir', help='base directory to analyze.')
1455f9996aaSopenharmony_ci    parser.add_argument('--root-source-dir', help='source root directory.')
1465f9996aaSopenharmony_ci    parser.add_argument(
1475f9996aaSopenharmony_ci        '--subsystem-config-file', help='path to subsystem java targets.')
1485f9996aaSopenharmony_ci    parser.add_argument(
1495f9996aaSopenharmony_ci        '--subsystem-config-overlay-file', help='path to subsystem overlay java targets.')
1505f9996aaSopenharmony_ci
1515f9996aaSopenharmony_ci    options = parser.parse_args()
1525f9996aaSopenharmony_ci
1535f9996aaSopenharmony_ci    paths = get_subsystem_paths(options.subsystem_config_file,
1545f9996aaSopenharmony_ci                                os.path.realpath(options.root_source_dir))
1555f9996aaSopenharmony_ci    compute_overlap_rate_by_subsystem(options, paths, 'c')
1565f9996aaSopenharmony_ci    compute_overlap_rate_by_subsystem(options, paths, 'java')
1575f9996aaSopenharmony_ci
1585f9996aaSopenharmony_ci
1595f9996aaSopenharmony_ciif __name__ == '__main__':
1605f9996aaSopenharmony_ci    sys.exit(main())
161