1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3# Copyright (c) 2022 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 json 18import sys 19 20sys.path.append(os.path.dirname(os.path.abspath(__file__))) 21from sort_sa_by_bootphase import SARearrangement 22import sa_info_config_errors as json_err # noqa E402 23 24 25class JsonSAInfoMerger(object): 26 class SAInfoCollector(object): 27 """ 28 Class for collecting sa info pieces shared with same process name 29 """ 30 def __init__(self, process_name, wdir): 31 self.process_name = process_name 32 self.systemabilities = [] 33 self.wdir = wdir 34 35 @property 36 def output_filename(self): 37 basename = self.process_name + '.json' 38 return os.path.join(self.wdir, basename) 39 40 def add_systemability_info(self, systemability): 41 self.systemabilities += systemability 42 43 def merge_sa_info(self): 44 """ 45 Write all pieces of sa info shared with same process to a new file 46 """ 47 xml_lines = {} 48 xml_lines['process'] = self.process_name 49 xml_lines['systemability'] = self.systemabilities 50 if not os.path.exists(self.wdir): 51 os.mkdir(self.wdir) 52 file_node = os.open(self.output_filename, os.O_RDWR | os.O_CREAT, 0o640) 53 with os.fdopen(file_node, 'w') as json_files: 54 json.dump(xml_lines, json_files, indent=4, ensure_ascii=False) 55 56 def __init__(self): 57 self.process_sas_dict = {} 58 self.output_filelist = [] 59 60 def __add_to_output_filelist(self, infile: str): 61 self.output_filelist.append(os.path.join(self.output_dir, infile)) 62 63 def __parse_json_file(self, file: str): 64 with open(file, 'r') as json_files: 65 data = json.load(json_files) 66 _format = 'one and only one {} tag is expected, actually {} is found' 67 # check process tag 68 if 'process' not in data or data['process'] == '': 69 raise json_err.BadFormatJsonError('provide a valid value for process', file) 70 process_name = data['process'] 71 if self.process_sas_dict.get(process_name) is None: 72 # create a new collector if a new process tag is found 73 sa_info_collector = self.SAInfoCollector(process_name, self.temp_dir) 74 self.process_sas_dict[process_name] = sa_info_collector 75 self.__add_to_output_filelist(process_name + '.json') 76 else: 77 sa_info_collector = self.process_sas_dict.get(process_name) 78 # check systemability tag 79 if 'systemability' not in data or data['systemability'] == '': 80 raise json_err.BadFormatJsonError('provide a valid value for systemability', file) 81 sys_count = len(data['systemability']) 82 if sys_count != 1: 83 raise json_err.BadFormatJsonError(_format.format('systemabiltiy', sys_count), file) 84 sys_value = data['systemability'] 85 if 'name' not in sys_value[0] or 'libpath' not in sys_value[0]: 86 raise json_err.BadFormatJsonError('systemability must have name and libpath', file) 87 sa_info_collector.add_systemability_info(sys_value) 88 89 def __merge(self, sa_info_filelist: list, path_merges: str): 90 """ 91 merge the json files of sa_info_filelist 92 :param sa_info_filelist : input_files 93 :param path_merges : merges_path 94 """ 95 self.temp_dir = path_merges 96 self.output_dir = path_merges 97 for file in sa_info_filelist: 98 self.__parse_json_file(file) 99 global_ordered_systemability_names = [] 100 global_systemability_deps_dict = {} 101 # merge systemability info for each process 102 for process, collector in self.process_sas_dict.items(): 103 rearragement = SARearrangement() 104 collector.merge_sa_info() 105 merged_file = collector.output_filename 106 rearragement.sort(merged_file, merged_file) 107 # get deps info for later detecting globally circular 108 deps_info = rearragement.get_deps_info() 109 global_ordered_systemability_names += deps_info[0] 110 global_systemability_deps_dict.update(deps_info[1]) 111 # detect possible cross-process circular dependency 112 try: 113 SARearrangement.detect_invalid_dependency_globally( 114 global_ordered_systemability_names, 115 global_systemability_deps_dict) 116 except json_err.CircularDependencyError as error: 117 for _file in self.output_filelist: 118 try: 119 os.remove(_file) 120 except OSError: 121 pass 122 raise json_err.CrossProcessCircularDependencyError(error) 123 # finally return an output filelist 124 return self.output_filelist 125 126 def merge(self, sa_info_filelist, output_dir): 127 return self.__merge(sa_info_filelist, output_dir) 128