15f9996aaSopenharmony_ci#!/usr/bin/env python
25f9996aaSopenharmony_ci# -*- coding: utf-8 -*-
35f9996aaSopenharmony_ci# Copyright (c) 2022 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 json
185f9996aaSopenharmony_ciimport sys
195f9996aaSopenharmony_ci
205f9996aaSopenharmony_cisys.path.append(os.path.dirname(os.path.abspath(__file__)))
215f9996aaSopenharmony_cifrom sort_sa_by_bootphase import SARearrangement
225f9996aaSopenharmony_ciimport sa_info_config_errors as json_err  # noqa E402
235f9996aaSopenharmony_ci
245f9996aaSopenharmony_ci
255f9996aaSopenharmony_ciclass JsonSAInfoMerger(object):
265f9996aaSopenharmony_ci    class SAInfoCollector(object):
275f9996aaSopenharmony_ci        """
285f9996aaSopenharmony_ci        Class for collecting sa info pieces shared with same process name
295f9996aaSopenharmony_ci        """
305f9996aaSopenharmony_ci        def __init__(self, process_name, wdir):
315f9996aaSopenharmony_ci            self.process_name = process_name
325f9996aaSopenharmony_ci            self.systemabilities = []
335f9996aaSopenharmony_ci            self.wdir = wdir
345f9996aaSopenharmony_ci
355f9996aaSopenharmony_ci        @property
365f9996aaSopenharmony_ci        def output_filename(self):
375f9996aaSopenharmony_ci            basename = self.process_name + '.json'
385f9996aaSopenharmony_ci            return os.path.join(self.wdir, basename)
395f9996aaSopenharmony_ci
405f9996aaSopenharmony_ci        def add_systemability_info(self, systemability):
415f9996aaSopenharmony_ci            self.systemabilities += systemability
425f9996aaSopenharmony_ci
435f9996aaSopenharmony_ci        def merge_sa_info(self):
445f9996aaSopenharmony_ci            """
455f9996aaSopenharmony_ci            Write all pieces of sa info shared with same process to a new file
465f9996aaSopenharmony_ci            """
475f9996aaSopenharmony_ci            xml_lines = {}
485f9996aaSopenharmony_ci            xml_lines['process'] = self.process_name
495f9996aaSopenharmony_ci            xml_lines['systemability'] = self.systemabilities
505f9996aaSopenharmony_ci            if not os.path.exists(self.wdir):
515f9996aaSopenharmony_ci                os.mkdir(self.wdir)
525f9996aaSopenharmony_ci            with os.fdopen(os.open(self.output_filename, os.O_RDWR | os.O_CREAT, 0o640), 'w') as json_files:
535f9996aaSopenharmony_ci                json.dump(xml_lines, json_files, indent=4, ensure_ascii=False)
545f9996aaSopenharmony_ci
555f9996aaSopenharmony_ci    def __init__(self):
565f9996aaSopenharmony_ci        self.process_sas_dict = {}
575f9996aaSopenharmony_ci        self.output_filelist = []
585f9996aaSopenharmony_ci
595f9996aaSopenharmony_ci    def add_to_output_filelist(self, infile: str):
605f9996aaSopenharmony_ci        self.output_filelist.append(os.path.join(self.output_dir, infile))
615f9996aaSopenharmony_ci
625f9996aaSopenharmony_ci    def merge(self, sa_info_filelist, output_dir):
635f9996aaSopenharmony_ci        return self.__merge(sa_info_filelist, output_dir)
645f9996aaSopenharmony_ci
655f9996aaSopenharmony_ci    def parse_json_file(self, file: str):
665f9996aaSopenharmony_ci        with open(file, 'r') as json_files:
675f9996aaSopenharmony_ci            data = json.load(json_files)
685f9996aaSopenharmony_ci            _format = 'one and only one {} tag is expected, actually {} is found'
695f9996aaSopenharmony_ci        # check process tag
705f9996aaSopenharmony_ci        if 'process' not in data or data['process'] == '':
715f9996aaSopenharmony_ci            raise json_err.BadFormatJsonError('provide a valid value for process', file)
725f9996aaSopenharmony_ci        process_name = data['process']
735f9996aaSopenharmony_ci        if self.process_sas_dict.get(process_name) is None:
745f9996aaSopenharmony_ci            # create a new collector if a new process tag is found
755f9996aaSopenharmony_ci            sa_info_collector = self.SAInfoCollector(process_name, self.temp_dir)
765f9996aaSopenharmony_ci            self.process_sas_dict[process_name] = sa_info_collector
775f9996aaSopenharmony_ci            self.add_to_output_filelist(process_name + '.json')
785f9996aaSopenharmony_ci        else:
795f9996aaSopenharmony_ci            sa_info_collector = self.process_sas_dict.get(process_name)
805f9996aaSopenharmony_ci        # check systemability tag
815f9996aaSopenharmony_ci        if 'systemability' not in data or data['systemability'] == '':
825f9996aaSopenharmony_ci            raise json_err.BadFormatJsonError('provide a valid value for systemability', file)
835f9996aaSopenharmony_ci        sys_count = len(data['systemability'])
845f9996aaSopenharmony_ci        if sys_count != 1:
855f9996aaSopenharmony_ci            raise json_err.BadFormatJsonError(_format.format('systemabiltiy', sys_count), file)
865f9996aaSopenharmony_ci        sys_value = data['systemability']
875f9996aaSopenharmony_ci        if 'name' not in sys_value[0] or 'libpath' not in sys_value[0]:
885f9996aaSopenharmony_ci            raise json_err.BadFormatJsonError('systemability must have name and libpath', file)
895f9996aaSopenharmony_ci        sa_info_collector.add_systemability_info(sys_value)
905f9996aaSopenharmony_ci
915f9996aaSopenharmony_ci    def __merge(self, sa_info_filelist: list, path_merges: str):
925f9996aaSopenharmony_ci        """
935f9996aaSopenharmony_ci        merge the json files of sa_info_filelist
945f9996aaSopenharmony_ci        :param sa_info_filelist : input_files
955f9996aaSopenharmony_ci        :param path_merges : merges_path
965f9996aaSopenharmony_ci        """
975f9996aaSopenharmony_ci        self.temp_dir = path_merges
985f9996aaSopenharmony_ci        self.output_dir = path_merges
995f9996aaSopenharmony_ci        for file in sa_info_filelist:
1005f9996aaSopenharmony_ci            self.parse_json_file(file)
1015f9996aaSopenharmony_ci        global_ordered_systemability_names = []
1025f9996aaSopenharmony_ci        global_systemability_deps_dict = {}
1035f9996aaSopenharmony_ci        # merge systemability info for each process
1045f9996aaSopenharmony_ci        for process, collector in self.process_sas_dict.items():
1055f9996aaSopenharmony_ci            rearragement = SARearrangement()
1065f9996aaSopenharmony_ci            collector.merge_sa_info()
1075f9996aaSopenharmony_ci            merged_file = collector.output_filename
1085f9996aaSopenharmony_ci            rearragement.sort(merged_file, merged_file)
1095f9996aaSopenharmony_ci            # get deps info for later detecting globally circular
1105f9996aaSopenharmony_ci            deps_info = rearragement.get_deps_info()
1115f9996aaSopenharmony_ci            global_ordered_systemability_names += deps_info[0]
1125f9996aaSopenharmony_ci            global_systemability_deps_dict.update(deps_info[1])
1135f9996aaSopenharmony_ci        # detect possible cross-process circular dependency
1145f9996aaSopenharmony_ci        try:
1155f9996aaSopenharmony_ci            SARearrangement.detect_invalid_dependency_globally(
1165f9996aaSopenharmony_ci                global_ordered_systemability_names,
1175f9996aaSopenharmony_ci                global_systemability_deps_dict)
1185f9996aaSopenharmony_ci        except json_err.CircularDependencyError as error:
1195f9996aaSopenharmony_ci            for _file in self.output_filelist:
1205f9996aaSopenharmony_ci                try:
1215f9996aaSopenharmony_ci                    os.remove(_file)
1225f9996aaSopenharmony_ci                except OSError:
1235f9996aaSopenharmony_ci                    pass
1245f9996aaSopenharmony_ci            raise json_err.CrossProcessCircularDependencyError(error)
1255f9996aaSopenharmony_ci        # finally return an output filelist
1265f9996aaSopenharmony_ci        return self.output_filelist
127