1#!/usr/bin/env python3 2#coding: utf-8 3 4""" 5Copyright (c) 2021-2022 Huawei Device Co., Ltd. 6Licensed under the Apache License, Version 2.0 (the "License"); 7you may not use this file except in compliance with the License. 8You may obtain a copy of the License at 9 10 http://www.apache.org/licenses/LICENSE-2.0 11 12Unless required by applicable law or agreed to in writing, software 13distributed under the License is distributed on an "AS IS" BASIS, 14WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15See the License for the specific language governing permissions and 16limitations under the License. 17 18Description: run script 19 expect_output will get run result, 20 expect_sub_output will catch pivotal sub output, 21 expect_file will get print string 22""" 23 24import argparse 25import os 26import subprocess 27import sys 28import time 29 30 31def get_env_path_from_rsp(script_file: str) -> list: 32 """get env path from response file recursively.""" 33 rsp_file = "{0}{1}".format(script_file, ".rsp") 34 if not os.path.exists(rsp_file): 35 print( 36 "File \"{}\" does not exist!\n" \ 37 "This indicates that its related shared_library is not compiled by this project, but there is an " \ 38 "executable or shared_library depend on its related shared_library!".format(rsp_file)) 39 sys.exit(1) 40 41 rsp_info_list = [] 42 with open(rsp_file, "r") as fi: 43 rsp_info_str = fi.read() 44 rsp_info_list = rsp_info_str.split(" ") 45 46 env_path_list = [] 47 for element in rsp_info_list: 48 if element.endswith(".so") or element.endswith(".dll"): 49 env_path_list.extend(get_env_path_from_rsp(element)) 50 env_path_list.append(os.path.dirname(element)) 51 return env_path_list 52 53 54def get_command_and_env_path(args: object) -> [str, str]: 55 """get command and environment path from args for running excutable.""" 56 env_path_list = list(set(get_env_path_from_rsp(args.script_file))) 57 env_path_list.append(args.clang_lib_path) 58 env_path = ":".join(env_path_list) 59 if args.qemu_binary_path: 60 if not os.path.exists(args.qemu_binary_path): 61 print("Have you set up environment for running executables with qemu?\n" \ 62 "If not, get set-up steps from https://gitee.com/ark_standalone_build/docs ," \ 63 " append your build command of ark.py with option \"--clean-continue\"," \ 64 " and execute the appended command after setting up the environment.\n" \ 65 "If yes, the environment settings for qemu on your host machine may be different from what the link" \ 66 " above shows, it is suggested to match your local environment settings with what the link shows.") 67 sys.exit(1) 68 cmd = \ 69 "{}".format(args.qemu_binary_path) + \ 70 " -L {}".format(args.qemu_ld_prefix) + \ 71 " -E LD_LIBRARY_PATH={}".format(env_path) + \ 72 " {}".format(args.script_file) 73 else: 74 cmd = "{}".format(args.script_file) 75 cmd += " {}".format(args.script_options) if args.script_options else "" 76 cmd += " {}".format(args.script_args) if args.script_args else "" 77 return [cmd, env_path] 78 79 80def parse_args() -> object: 81 """parse arguments.""" 82 parser = argparse.ArgumentParser() 83 parser.add_argument('--script-file', help='execute script file') 84 parser.add_argument('--script-options', help='execute script options') 85 parser.add_argument('--script-args', help='args of script') 86 parser.add_argument('--expect-output', help='expect output') 87 parser.add_argument('--expect-sub-output', help='expect sub output') 88 parser.add_argument('--expect-file', help='expect file') 89 parser.add_argument('--env-path', help='LD_LIBRARY_PATH env') 90 parser.add_argument('--timeout-limit', help='timeout limit') 91 parser.add_argument('--clang-lib-path', help='part for LD_LIBRARY_PATH, it is not in .rsp file') 92 parser.add_argument('--qemu-binary-path', help='path to qemu binary, run executable with qemu if assigned') 93 parser.add_argument('--qemu-ld-prefix', help='elf interpreter prefix') 94 args = parser.parse_args() 95 return args 96 97 98def process_open(args: object) -> [str, object]: 99 """get command and open subprocess.""" 100 if args.env_path: 101 # use the given env-path 102 cmd = args.script_file 103 cmd += " {}".format(args.script_options) if args.script_options else "" 104 cmd += " {}".format(args.script_args) if args.script_args else "" 105 # process for running executable directly 106 subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 107 env={'LD_LIBRARY_PATH': str(args.env_path)}) 108 else: 109 # get env-path from response file recursively 110 [cmd, env_path] = get_command_and_env_path(args) 111 if args.qemu_binary_path: 112 # process for running executable with qemu 113 subp = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 114 else: 115 # process for running executable directly 116 subp = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 117 env={'LD_LIBRARY_PATH': str(env_path)}) 118 return [cmd, subp] 119 120 121def judge_output(args: object): 122 """run executable and judge is success or not.""" 123 start_time = time.time() 124 [cmd, subp] = process_open(args) 125 timeout_limit = int(args.timeout_limit) if args.timeout_limit else 1200 # units: s 126 127 try: 128 out, err = subp.communicate(timeout=timeout_limit) 129 except subprocess.TimeoutExpired: 130 raise RuntimeError('Run [', cmd, '] timeout, timeout_limit = ', timeout_limit, 's') 131 132 out_str = out.decode('UTF-8', errors="ignore") 133 err_str = err.decode('UTF-8', errors="ignore") 134 returncode = str(subp.returncode) 135 if args.expect_output: 136 if returncode != args.expect_output: 137 print(">>>>> ret <<<<<") 138 print(returncode) 139 print(">>>>> out <<<<<") 140 print(out_str) 141 print(">>>>> err <<<<<") 142 print(err_str) 143 print(">>>>> Expect return: [" + args.expect_output \ 144 + "]\n>>>>> But got: [" + returncode + "]") 145 raise RuntimeError("Run [" + cmd + "] failed!") 146 elif args.expect_sub_output: 147 out_str = out.decode('UTF-8', errors="ignore") 148 if out_str.find(args.expect_sub_output) == -1 or returncode != "0": 149 print(">>>>> ret <<<<<") 150 print(returncode) 151 print(">>>>> err <<<<<") 152 print(err_str) 153 print(">>>>> Expect contain: [" + args.expect_sub_output \ 154 + "]\n>>>>> But got: [" + out_str + "]") 155 raise RuntimeError("Run [" + cmd + "] failed!") 156 elif args.expect_file: 157 with open(args.expect_file, mode='r') as file: 158 # skip license header 159 expect_output = ''.join(file.readlines()[13:]) 160 file.close() 161 out_str = out.decode('UTF-8', errors="ignore") 162 result_cmp = compare_line_by_line(expect_output, out_str) 163 if result_cmp or returncode != "0": 164 print(">>>>> ret <<<<<") 165 print(returncode) 166 print(">>>>> err <<<<<") 167 print(err_str) 168 print(">>>>> Expect {} lines: [{}]\n>>>>> But got {} lines: [{}]".format( 169 expect_output.count('\n'), expect_output, out_str.count('\n'), out_str 170 )) 171 raise RuntimeError("Run [" + cmd + "] failed!") 172 else: 173 raise RuntimeError("Run [" + cmd + "] with no expect !") 174 175 print("Run [" + cmd + "] success!") 176 print("used: %.5f seconds" % (time.time() - start_time)) 177 178def compare_line_by_line(expect_output:str, got_output:str): 179 expect_output_list = expect_output.split("\n") 180 got_output_list = got_output.split("\n") 181 for index, (expect_line, got_line) in enumerate(zip(expect_output_list, got_output_list)): 182 if expect_line == got_line: 183 continue 184 error_msg = "" 185 186 if "__INT_MORE_PREV__" in expect_line: 187 prev_got_value = reverse_find_first_not_trace_line(got_output_list, index-1) 188 if got_line.isdigit() and prev_got_value.isdigit() and int(prev_got_value) < int(got_line): 189 continue 190 error_msg = "Got integer result is not more than previous integer result" 191 192 if "__INT__" in expect_line: 193 if got_line.isdigit(): 194 continue 195 error_msg = "Got not integer" 196 197 print(">>>>> diff <<<<<") 198 if error_msg: 199 print(error_msg) 200 print("Difference in line {}:\nExcepted: [{}]\nBut got: [{}]".format(index+1, expect_line, got_line)) 201 return True 202 return False 203 204def reverse_find_first_not_trace_line(output_list: list, init_index: int) -> str: 205 for i in range(init_index, -1, -1): 206 if "[trace]" not in output_list[i]: 207 return output_list[i] 208 return "" 209 210if __name__ == '__main__': 211 input_args = parse_args() 212 judge_output(input_args) 213