1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3# Copyright (c) 2023 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 argparse 17import copy 18import os 19import shutil 20import subprocess 21import sys 22 23sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) 24from scripts.util.file_utils import read_json_file # noqa: E402 25 26target_data_dict = dict() 27 28 29def get_value_from_file(file: str, target: list, key: str, must: bool=True): 30 target_name = target[0] 31 target_out_dir = target[1] 32 file_path = os.path.join(target_out_dir, target_name + "_" + file + ".json") 33 34 global target_data_dict 35 target_data = target_data_dict.get(file_path) 36 if target_data is not None: 37 return target_data.get(key) 38 39 if not os.path.exists(file_path): 40 if must: 41 raise Exception("File " + file_path + " not exists!") 42 else: 43 print(file_path + " not exists!") 44 return "Unknown" 45 46 target_data = read_json_file(file_path) 47 target_data_dict[file_path] = target_data 48 return target_data.get(key) 49 50 51def get_valid_deps(module_deps: list, finish_list: list): 52 already_check = [] 53 valid_list = [] 54 while len(module_deps) > 0: 55 module_deps_copy = copy.deepcopy(module_deps) 56 module_deps = [] 57 for module_dep in module_deps_copy: 58 target_name = module_dep.get("target_name") 59 target_out_dir = module_dep.get("target_out_dir") 60 61 element = (target_name, target_out_dir) 62 if already_check.count(element) > 0: 63 continue 64 already_check.append(element) 65 66 target_type = get_value_from_file("deps_data", element, "type", False) 67 if target_type == "Unknown": 68 continue 69 elif target_type == "shared_library" or target_type == "etc": 70 if finish_list.count(element) > 0: 71 continue 72 valid_list.append(element) 73 else: 74 deps = get_value_from_file("deps_data", element, "module_deps_info", False) 75 for dep in deps: 76 name = dep.get("target_name") 77 out_dir = dep.get("target_out_dir") 78 dep_tup = (name, out_dir) 79 module_deps.append(dep) 80 return valid_list 81 82 83def check_debug_info(check_file: str, readelf: str): 84 out = subprocess.Popen([readelf, "-S", check_file], shell=False, stdout=subprocess.PIPE) 85 infos = out.stdout.read().splitlines() 86 for info in infos: 87 info_str = info.decode() 88 pos = info_str.find(".debug_info") 89 if pos >= 0: 90 return True 91 return False 92 93 94def do_check(target_out_dir: str, target_name: str, stripped_dir: str, readelf: str, 95 abidiff: str, abidw: str, abi_dumps_path: str): 96 element = (target_name, target_out_dir) 97 prebuilt = get_value_from_file("deps_data", element, "prebuilt") 98 if prebuilt: 99 check_file = get_value_from_file("deps_data", element, "source_path") 100 if not os.path.exists(check_file): 101 raise Exception("File " + check_file + " not exists!") 102 has_debug_info = check_debug_info(check_file, readelf) 103 if not has_debug_info: 104 raise Exception("Prebuilt target should be with debug info!") 105 else: 106 source = get_value_from_file("module_info", element, "source") 107 check_file = os.path.join(stripped_dir, source) 108 if not os.path.exists(check_file): 109 raise Exception("File " + check_file + " not exists!") 110 111 out_file = os.path.join(target_out_dir, target_name + "_abi_info.dump") 112 ret = subprocess.call([abidw, "--out-file", out_file, check_file]) 113 if ret != 0: 114 raise Exception("Execute abidw failed! Return value: " + str(ret)) 115 116 toolchain = get_value_from_file("deps_data", element, "toolchain") 117 toolchain_name = toolchain.split(':')[-1] 118 119 base_name = os.path.basename(out_file) 120 base_file = os.path.join(abi_dumps_path, toolchain_name, base_name) 121 if not os.path.exists(base_file): 122 raise Exception("File " + base_file + " not exists!") 123 ret = subprocess.call([abidiff, out_file, base_file]) 124 if ret != 0: 125 raise Exception("ABI info in " + out_file + " and " + base_file + " are different!") 126 127 128def get_copy_source_path(element: list): 129 prebuilt = get_value_from_file("deps_data", element, "prebuilt") 130 if prebuilt: 131 source = get_value_from_file("deps_data", element, "output_path") 132 else: 133 source = get_value_from_file("module_info", element, "source") 134 return (element, source) 135 136 137def traverse_and_check(check_list: list, readelf: str, abidiff: str, abidw: str, abi_dumps_path: str): 138 copy_list = [] 139 finish_list = [] 140 loop_count = 0 141 while len(check_list) > 0: 142 check_list_copy = copy.deepcopy(check_list) 143 check_list = [] 144 for element in check_list_copy: 145 if finish_list.count(element) > 0: 146 continue 147 finish_list.append(element) 148 149 target_name = element[0] 150 target_out_dir = element[1] 151 target_type = get_value_from_file("deps_data", element, "type") 152 if target_type == "etc": 153 copy_list.append(copy.deepcopy(get_copy_source_path(element))) 154 continue 155 156 stable = get_value_from_file("deps_data", element, "stable") 157 if not stable: 158 if loop_count == 0: 159 raise Exception("Target '{}' is not stable! Check config in gn".format(target_name)) 160 else: 161 copy_list.append(copy.deepcopy(get_copy_source_path(element))) 162 module_deps = get_value_from_file("deps_data", element, "module_deps_info") 163 check_list.extend(get_valid_deps(module_deps, finish_list)) 164 else: 165 stripped_dir = "" 166 if target_type == "shared_library": 167 stripped_dir = "lib.unstripped" 168 elif target_type == "executable": 169 stripped_dir = "exe.unstripped" 170 else: 171 raise Exception("Invalid target type: '{}'".format(target_type)) 172 173 do_check(target_out_dir, target_name, stripped_dir, readelf, abidiff, abidw, abi_dumps_path) 174 if loop_count == 0: 175 copy_list.append(copy.deepcopy(get_copy_source_path(element))) 176 177 module_deps = get_value_from_file("deps_data", element, "module_deps_info") 178 check_list.extend(get_valid_deps(module_deps, finish_list)) 179 loop_count += 1 180 return copy_list 181 182 183def get_copy_output_path(element: list, parent_output: str): 184 output_path = parent_output 185 target_type = get_value_from_file("deps_data", element, "type") 186 if target_type == "etc": 187 output_path = os.path.join(parent_output, "etc") 188 elif target_type == "executable": 189 output_path = os.path.join(parent_output, "bin") 190 elif target_type == "shared_library": 191 output_path = os.path.join(parent_output, get_value_from_file("module_info", element, "type")) 192 return output_path 193 194 195def main(): 196 parser = argparse.ArgumentParser() 197 parser.add_argument('--clang-readelf', required=True) 198 parser.add_argument('--target-out-dir', required=True) 199 parser.add_argument('--check-datas-file', required=True) 200 parser.add_argument('--abidiff-target-name', required=True) 201 parser.add_argument('--abidiff-target-out-dir', required=True) 202 parser.add_argument('--abidw-target-name', required=True) 203 parser.add_argument('--abidw-target-out-dir', required=True) 204 parser.add_argument('--abi-dumps-path', required=True) 205 args = parser.parse_args() 206 207 if not os.path.exists(args.check_datas_file): 208 raise Exception("File " + args.check_datas_file + " not exists!") 209 210 abidiff_element = (args.abidiff_target_name, args.abidiff_target_out_dir) 211 abidiff_bin = get_value_from_file("module_info", abidiff_element, "source") 212 abidw_element = (args.abidw_target_name, args.abidw_target_out_dir) 213 abidw_bin = get_value_from_file("module_info", abidw_element, "source") 214 215 parent_output = os.path.join(args.target_out_dir, "module_package", "img_input") 216 if not os.path.exists(parent_output): 217 os.makedirs(parent_output, exist_ok=True) 218 219 check_list = [] 220 check_datas = read_json_file(args.check_datas_file) 221 for check_data in check_datas: 222 element = (check_data.get("target_name"), check_data.get("target_out_dir")) 223 check_list.append(element) 224 225 copy_list = traverse_and_check(check_list, args.clang_readelf, abidiff_bin, abidw_bin, args.abi_dumps_path) 226 for copy_element in copy_list: 227 print("copy_list: '{}'".format(str(copy_element))) 228 output = get_copy_output_path(copy_element[0], parent_output) 229 if not os.path.exists(output): 230 os.makedirs(output, exist_ok=True) 231 if isinstance(copy_element[1], list): 232 for file in copy_element[1]: 233 shutil.copy(file, output) 234 else: 235 shutil.copy(copy_element[1], output) 236 os.remove(args.check_datas_file) 237 238 239if __name__ == '__main__': 240 sys.exit(main()) 241